Merge nucleic/lucid-river-toad-6efj into dev
This commit is contained in:
@@ -128,6 +128,16 @@ service proper (`LxssUserSession`, `LxssUserSessionInBox`).
|
||||
for `IWSLCSessionManager`. One object, two faces. `IWSLCVirtualMachine` was refused by every
|
||||
class, confirming §13.1's retraction against the machine rather than against an IDL.
|
||||
|
||||
`--internal-call` then confirmed the **vtable** matches the IDL: `GetVersion()` (slot 3) returned
|
||||
2.9.4, agreeing with the compat `WslcService.GetVersion()` in the same run — the same service
|
||||
answering through both faces — and `ListSessions()` (slot 6) returned S_OK. So hand-written
|
||||
`ComImport` is sufficient and the C++/WinRT shim §13.1 mused about is not needed.
|
||||
|
||||
It returned **0 sessions**, though, so the entry-struct layout (inline `wchar_t` buffers, the same
|
||||
shape `ListContainers` uses) is still untested. Run `--session --keep --internal-call` to create a
|
||||
session, re-adopt it through the internal interface, and exercise it — that combination is §2.3's
|
||||
reattach story end to end, and it also answers the handoff question below. `wsl --shutdown` after.
|
||||
|
||||
The hypothesis was that one of those objects also implements the internal interface — COM
|
||||
objects routinely expose several — so `--internal` activates each and QIs for the internal IIDs.
|
||||
It **never calls a method**, so a vtable mismatch cannot crash it, and QI alone answers the
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 })
|
||||
|
||||
Reference in New Issue
Block a user