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

This commit is contained in:
2026-07-29 03:26:09 -07:00
parent 8f081ab58a
commit 8c8add3c40
3 changed files with 110 additions and 3 deletions
+92 -2
View File
@@ -62,8 +62,9 @@ internal static class InternalComProbe
+ "because §13.2 retracted a decision on that reading and it deserves confirming"),
];
internal static void Run(bool callThrough)
internal static void Run(bool callThrough, string? sessionName = null)
{
InternalComProbe.sessionName = sessionName;
Console.WriteLine();
Console.WriteLine("internal COM probe (docs/WINDOWS_PORT.md §13.2):");
Console.WriteLine(" wslc.idl declares no coclass, so the question is whether an EXISTING");
@@ -218,14 +219,98 @@ internal static class InternalComProbe
}
// The callee allocated it; nobody else will free it.
if (sessions != IntPtr.Zero) Marshal.FreeCoTaskMem(sessions);
Console.WriteLine(" → enumeration and reattach are both reachable from here.");
if (count == 0)
// Worth saying, because "it returned S_OK" is weaker evidence than it looks: with
// no entries, the WSLCSessionListEntry layout — two DWORDs then two INLINE
// wchar_t buffers — was never actually unmarshalled. ListContainers uses the same
// `size_is(, *Count)` double-pointer shape, so that layout is still unproven.
Console.WriteLine(" (0 entries, so the ENTRY STRUCT layout is still "
+ "untested — run with a session up: --session --keep --internal-call)");
}
catch (Exception e)
{
Console.WriteLine($" ListSessions() threw {e.GetType().Name}: {e.Message}");
return;
}
ProbeSessionHandoff(proxy);
}
/// <summary>
/// The question that decides the SHAPE of D13's internal arm.
///
/// `IWSLCSession` (internal, `EF0661E4-…`) and `IWSLCCompatSession` (SDK-facing,
/// `DD7B2EF9-…`) are different interfaces with different IIDs. `WSLCCompatSessionManager`
/// already proved one object can wear both faces — so if the session `OpenSessionByName`
/// returns ALSO answers a QI for the compat interface, then a re-adopted session can be
/// handed to `Session.FromAbi(ptr)` and driven by the **existing** facade code.
///
/// * **YES** → internal COM is used only to *find* things. Reattach costs one QI, and
/// nothing else in `WslcFacade` changes.
/// * **NO** → every re-adopted handle is internal-only, and start/stop/delete/exec each need
/// a second, hand-marshalled implementation. That is a much bigger arm, and it is the point
/// at which the C++/WinRT shim §13.1 mused about earns its keep.
/// </summary>
private static void ProbeSessionHandoff(IWSLCSessionManager proxy)
{
var name = sessionName;
if (string.IsNullOrEmpty(name))
{
Console.WriteLine(" (no --session-name given; skipping the reattach handoff test)");
return;
}
Console.WriteLine();
Console.WriteLine($" reattach handoff test — OpenSessionByName(\"{name}\"):");
int hr;
IntPtr session;
try
{
hr = proxy.OpenSessionByName(name, out session);
}
catch (Exception e)
{
Console.WriteLine($" threw {e.GetType().Name}: {e.Message}");
return;
}
if (hr < 0)
{
Console.WriteLine($" failed: {Hresult(hr)}");
Console.WriteLine(" (expected if no session of that name is running — create "
+ "one first: --session --keep --internal-call)");
return;
}
Console.WriteLine(" opened — the session half of reattach WORKS");
try
{
var compat = IidCompatSession;
var qi = Marshal.QueryInterface(session, in compat, out var projected);
if (qi >= 0)
{
Console.WriteLine(" QI IWSLCCompatSession: **YES**");
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.");
Marshal.Release(projected);
}
else
{
Console.WriteLine($" QI IWSLCCompatSession: no ({Hresult(qi)})");
Console.WriteLine(" → a re-adopted session is internal-only, so every "
+ "operation on it needs a second hand-marshalled path.");
Console.WriteLine(" → that is the case where a C++/WinRT shim is worth it.");
}
}
finally
{
Marshal.Release(session);
}
}
private static string? sessionName;
private static IEnumerable<string> Wrap(string text)
{
var words = text.Split(' ', StringSplitOptions.RemoveEmptyEntries);
@@ -264,6 +349,11 @@ internal static class InternalComProbe
/// </summary>
private static readonly Guid IidIUnknown = new("00000000-0000-0000-C000-000000000046");
/// `IWSLCCompatSession` from WSLCCompat.idl — the SDK-facing session interface, and the one
/// the C#/WinRT `Session` projection wraps. Distinct from the internal `IWSLCSession`
/// (`EF0661E4-…`); whether one object answers both is the reattach-handoff question.
private static readonly Guid IidCompatSession = new("DD7B2EF9-AA01-4F21-8A3A-29D394CBB579");
private const uint ClsctxAll = 0x17; // INPROC_SERVER|HANDLER|LOCAL_SERVER|REMOTE_SERVER
private const uint CoinitMultithreaded = 0;
private const uint RpcEChangedMode = 0x80010106;
+8 -1
View File
@@ -104,7 +104,14 @@ internal static class Program
// interface can be reached at all, which is what D13's enumeration/reattach/pty arm
// hangs on (docs/WINDOWS_PORT.md §13.2).
if (args.Contains("--internal") || args.Contains("--internal-call"))
InternalComProbe.Run(callThrough: args.Contains("--internal-call"));
InternalComProbe.Run(
callThrough: args.Contains("--internal-call"),
// Shares --session's name so `--session --keep --internal-call` creates a session
// and then re-adopts it through the internal interface in one run — which is the
// only way to test the handoff, and the whole §2.3 reattach story in miniature.
sessionName: args.Contains("--session") || args.Contains("--session-name")
? ArgValue(args, "--session-name") ?? "nucleic-spike"
: null);
Console.WriteLine();
if (componentsMissing is { Count: > 0 })