Skip to content

Add C# robot client using CsWinRT#4172

Draft
Copilot wants to merge 7 commits intomasterfrom
copilot/add-csharp-client-to-robot-sample
Draft

Add C# robot client using CsWinRT#4172
Copilot wants to merge 7 commits intomasterfrom
copilot/add-csharp-client-to-robot-sample

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 9, 2026

Adds a C# client for the robot sample using CsWinRT to generate projections from robot.winmd at build time.

client.csproj: targets net9.0-windows10.0.19041.0; references Microsoft.Windows.CsWinRT 2.2.0; feeds robot.winmd via CsWinRTInputs/CsWinRTIncludes=Robotics; sets CsWinRTWindowsMetadata=local so cswinrt.exe skips reading the UAP Platform.xml (only our own Robotics types are projected, no Windows SDK WinRT types are needed).

client.cs: uses CsWinRT-projected types — new Robot() for WinRT activation, robot.Speak("...") with automatic HSTRING marshaling, and MarshalInspectable<Robot>.FromAbi(ptr) to wrap the raw ABI pointer returned by CreateRobotFromHandle. CreateRobotFromHandle is kept as a P/Invoke (plain Win32 export). To obtain the interop handle, Marshal.QueryInterface is called directly on the raw ABI pointer (passing the Guid without a ref modifier, matching the in Guid parameter in .NET 9) to retrieve IRobotInterop*, and Handle() is invoked through the COM vtable via Marshal.GetDelegateForFunctionPointer — bypassing CsWinRT's global ComWrappers, which re-projects all IInspectable* pointers as WinRT types and makes a direct [ComImport] cast invalid (CS0030).

src/lib.rs: Rust test invokes dotnet run and validates output; panic message includes both stdout (where MSBuild writes compiler errors) and stderr.

Copilot AI and others added 2 commits April 9, 2026 17:27
Agent-Logs-Url: https://github.com/microsoft/windows-rs/sessions/e097a3e9-9657-44ce-930d-2a1c49414c75

Co-authored-by: kennykerr <9845234+kennykerr@users.noreply.github.com>
… Robot.FromAbi

Agent-Logs-Url: https://github.com/microsoft/windows-rs/sessions/ca6b84e1-48e0-4ab2-bfe8-ee281d366ce8

Co-authored-by: kennykerr <9845234+kennykerr@users.noreply.github.com>
Copilot AI requested a review from kennykerr April 9, 2026 20:24
@kennykerr
Copy link
Copy Markdown
Collaborator

@copilot client.cs(27,1): error CS8803: Top-level statements must precede namespace and type declarations.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 9, 2026

@copilot client.cs(27,1): error CS8803: Top-level statements must precede namespace and type declarations.

Fixed in ff813e9. In C# top-level-statement files, type declarations must come after all top-level statements. Moved IRobotInterop to the bottom of the file.

@kennykerr
Copy link
Copy Markdown
Collaborator

@copilot now its failing for a different reason on x64

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 9, 2026

@copilot now its failing for a different reason on x64

Fixed in 6a05bc7. The x64 failure was cswinrt.exe failing to read C:\Program Files (x86)\Windows Kits\10\Platforms\UAP\10.0.19041.0\Platform.xml — a UAP SDK component that isn't installed on the runner. Since we're only projecting our own Robotics types (not any Windows SDK WinRT types), adding <CsWinRTWindowsMetadata>local</CsWinRTWindowsMetadata> to client.csproj tells CsWinRT to skip reading Platform.xml entirely.

@kennykerr
Copy link
Copy Markdown
Collaborator

@copilot more issues

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 9, 2026

@copilot more issues

Fixed in 90fd1fc. The root issue is that CsWinRT registers a global ComWrappers that re-projects any IInspectable* back as a WinRT Robot object — so a C# cast to a [ComImport] interface fails at compile time (CS0030) because the projected class doesn't expose it.

The fix removes the invalid cast and instead calls IRobotInterop::Handle() through the raw COM vtable:

  1. Marshal.QueryInterface(handyAbi, ref iid, out IntPtr interopPtr) — QIs the raw ABI pointer directly, bypassing CsWinRT's ComWrappers
  2. Marshal.ReadIntPtr to walk the vtable (slot 3 = Handle(), after QueryInterface/AddRef/Release)
  3. Marshal.GetDelegateForFunctionPointer<HandleFunc> to call it safely without unsafe code

@kennykerr
Copy link
Copy Markdown
Collaborator

@copilot still failing

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 9, 2026

@copilot still failing

Fixed in 088d2e2. The issue was warning CS9191: Marshal.QueryInterface in .NET 9 declares its second parameter as in Guid, so passing ref iid generates a warning that dotnet run writes to stdout. The Rust test then saw the warning as the first output line instead of "Hello from cs land". Fixed by changing ref iid to iid (no modifier — passing a value directly to an in parameter is valid and warning-free).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants