Merge nucleic/lucid-river-toad-6efj into dev

This commit is contained in:
2026-07-29 03:57:12 -07:00
parent 71157c0d6e
commit f5cd4a3023
3 changed files with 102 additions and 7 deletions
+87 -1
View File
@@ -334,7 +334,7 @@ internal static class InternalComProbe
Console.WriteLine(" → a re-adopted session can be handed to "
+ "Session.FromAbi() and driven by the EXISTING compat facade code.");
Console.WriteLine(" → D13's internal arm shrinks to: find, then hand off.");
ProbeFromAbiOwnership(projected);
ProbeObjectIdentity(session, projected);
Marshal.Release(projected);
}
else
@@ -353,6 +353,92 @@ internal static class InternalComProbe
private static string? sessionName;
/// <summary>
/// Why did `Session.FromAbi` throw `InvalidCastException` on a pointer that had just QI'd
/// successfully to `IWSLCCompatSession`?
///
/// The hypothesis is that there are **three** layers, not two: `wslc.idl` (service-internal
/// COM), `WSLCCompat.idl` (compat COM), and a **WinRT** layer implemented *client-side* in
/// `wslcsdk.dll` — which exports `DllGetActivationFactory`, so its `Session` is an in-process
/// wrapper object that holds compat COM proxies rather than being one. If so, the service's
/// object can never satisfy `FromAbi`, because the WinRT default interface only ever exists
/// on the client-side wrapper, and no API turns a compat pointer into one.
///
/// Decisive test, and it needs no knowledge of the WinRT IID: take the `Session` the SDK
/// itself created, reach through to its underlying native pointer, and compare **COM
/// identity** (QI for IUnknown — the canonical pointer) against the one `OpenSessionByName`
/// returned. Same object → the layers coincide and something else broke FromAbi. Different
/// objects → the wrapper is client-side and the handoff is impossible as designed.
/// </summary>
private static void ProbeObjectIdentity(IntPtr internalSession, IntPtr compatFace)
{
Console.WriteLine();
Console.WriteLine(" object identity — is the WinRT Session the SAME object?");
if (sdkSession is null)
{
Console.WriteLine(" (no SDK-created session in this run; pass --session --keep)");
return;
}
IntPtr sdkPtr;
try
{
// CsWinRT keeps the native pointer on IWinRTObject.NativeObject.ThisPtr. Reached by
// reflection so this spike keeps building even if the projection's shape changes.
var winrt = sdkSession.GetType().GetInterface("WinRT.IWinRTObject");
var native = winrt?.GetProperty("NativeObject")?.GetValue(sdkSession);
var ptr = native?.GetType().GetProperty("ThisPtr")?.GetValue(native);
if (ptr is not IntPtr value || value == IntPtr.Zero)
{
Console.WriteLine(" could not reach IWinRTObject.NativeObject.ThisPtr");
return;
}
sdkPtr = value;
}
catch (Exception e)
{
Console.WriteLine($" reflection failed: {e.GetType().Name}: {e.Message}");
return;
}
// COM identity is defined as the IUnknown pointer, so canonicalise both before comparing.
var iid = IidIUnknown;
if (Marshal.QueryInterface(sdkPtr, in iid, out var sdkIdentity) < 0) return;
if (Marshal.QueryInterface(internalSession, in iid, out var internalIdentity) < 0)
{
Marshal.Release(sdkIdentity);
return;
}
try
{
var same = sdkIdentity == internalIdentity;
Console.WriteLine($" SDK Session IUnknown = 0x{sdkIdentity:X}");
Console.WriteLine($" OpenSessionByName IUnknown = 0x{internalIdentity:X}");
Console.WriteLine($" → {(same ? "SAME object" : "DIFFERENT objects")}");
// The other half: does the SDK's own object even implement the compat COM interface?
var compat = IidCompatSession;
var qi = Marshal.QueryInterface(sdkPtr, in compat, out var sdkCompat);
if (qi >= 0) Marshal.Release(sdkCompat);
Console.WriteLine($" SDK Session QI IWSLCCompatSession: {(qi >= 0 ? "yes" : "no")}");
Console.WriteLine(same
? " → the layers coincide; FromAbi failed for some OTHER reason — investigate."
: " → the WinRT Session is a CLIENT-SIDE WRAPPER (wslcsdk.dll exports\n"
+ " DllGetActivationFactory), so a service-side pointer can never satisfy\n"
+ " FromAbi. The 'find, then hand off' shape does NOT work, and a\n"
+ " re-adopted session must be driven through internal COM directly.");
}
finally
{
Marshal.Release(sdkIdentity);
Marshal.Release(internalIdentity);
}
}
internal static object? sdkSession;
/// <summary>
/// Does <c>Session.FromAbi(ptr)</c> take a reference, or borrow the caller's?
///