diff --git a/spikes/WslcApiDump/InternalComProbe.cs b/spikes/WslcApiDump/InternalComProbe.cs index 0420402..4ce480b 100644 --- a/spikes/WslcApiDump/InternalComProbe.cs +++ b/spikes/WslcApiDump/InternalComProbe.cs @@ -82,11 +82,35 @@ internal static class InternalComProbe Console.WriteLine($" {name} {{{clsid}}}"); Console.WriteLine($" ({source})"); - var iunknown = typeof(object).GUID; // IID_IUnknown - var hr = CoCreateInstance(in clsid, IntPtr.Zero, ClsctxAll, in iunknown, out var unknown); + var hr = CoCreateInstance(in clsid, IntPtr.Zero, ClsctxAll, in IidIUnknown, out var unknown); if (hr < 0) { Console.WriteLine($" activation failed: {Hresult(hr)}"); + if ((uint)hr == ENoInterface) + // Every COM object implements IUnknown, so this combination is impossible + // unless the IID being passed is not IID_IUnknown. Say so loudly: the first + // version of this probe did exactly that and reported a confident false + // negative that read like a real finding about WSL. + Console.WriteLine(" !! E_NOINTERFACE on an IUnknown activation is IMPOSSIBLE — " + + "every COM object implements IUnknown. This probe is passing a wrong IID; " + + "treat the whole run as void and fix it, do NOT record a finding."); + // A class factory is allowed to refuse IUnknown-first activation, so a failure + // here is not yet an answer: ask for each internal interface directly before + // concluding anything. + foreach (var (interfaceName, iid, _) in Interfaces) + { + var direct = iid; + var dhr = CoCreateInstance(in clsid, IntPtr.Zero, ClsctxAll, in direct, out var instance); + Console.WriteLine($" direct activation as {interfaceName}: " + + (dhr >= 0 ? "**YES**" : Hresult(dhr))); + if (dhr < 0) continue; + if (interfaceName == "IWSLCSessionManager") + { + bound = true; + if (callThrough) CallThrough(instance); + } + Marshal.Release(instance); + } Console.WriteLine(); continue; } @@ -227,9 +251,20 @@ internal static class InternalComProbe // MARK: - Interop + /// + /// `IID_IUnknown`, spelled out. **Do not** reach for `typeof(object).GUID` here — that is the + /// CLR's type GUID for `System.Object`, not `IID_IUnknown`, and activating with it asks every + /// class factory for an interface nothing implements. The failure is maximally misleading: + /// each registered class answers `E_NOINTERFACE` *at activation*, which reads exactly like + /// "this class does not expose the interface you wanted" and is really "you asked for + /// gibberish". That mistake produced a false negative on this very probe. + /// + private static readonly Guid IidIUnknown = new("00000000-0000-0000-C000-000000000046"); + private const uint ClsctxAll = 0x17; // INPROC_SERVER|HANDLER|LOCAL_SERVER|REMOTE_SERVER private const uint CoinitMultithreaded = 0; private const uint RpcEChangedMode = 0x80010106; + private const uint ENoInterface = 0x80004002; [DllImport("ole32.dll")] private static extern int CoInitializeEx(IntPtr reserved, uint coInit);