Skip to content

Implement WinRT / C# projection for the simple parts of the WSLC SDK API surface#40360

Draft
florelis wants to merge 6 commits intomicrosoft:masterfrom
florelis:csFullApi
Draft

Implement WinRT / C# projection for the simple parts of the WSLC SDK API surface#40360
florelis wants to merge 6 commits intomicrosoft:masterfrom
florelis:csFullApi

Conversation

@florelis
Copy link
Copy Markdown
Member

Summary of the Pull Request

This PR implements most of the C++/WinRT wrapper for the WSLC SDK, which is then projected to C#.

PR Checklist

  • Closes: Link to issue #xxx
  • Communication: I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected
  • Tests: Added/updated if needed and all pass
  • Localization: All end user facing strings can be localized
  • Dev docs: Added/updated if needed
  • Documentation updated: If checked, please file a pull request on our docs repo and link it here: #xxx

Detailed Description of the Pull Request / Additional comments

This builds on top of #40121 and #40212. The relevant changes for this are only the .h and .cpp files.

The implementation for all types follows a similar pattern, but there are some differences between types that are meant to be constructed by the consumer (e.g., SessionSettings) and types that can only be obtained by a function call (e.g., Session).

For types constructed by the consumer:

  • Added constructors to the implementation types that don't have one in the public API
  • Declared private fields to back all the properties
  • All getters and setters use the backing fields directly
  • Setters perform minimal validation (e.g., non-empty strings)
  • String values are not stored as a WinRT hstring but as a native std::(w)string to directly back any string pointers passed to the C API.
  • Properties that in the C API use 0 to represent the default value, here use instead an IReference<> (projected to C# as a nullable type) and nullptr to represent the default.
  • Added a ToStruct() or ToStructPointer() (depending on usage) to get and retrieve the equivalent struct for the C API. This native struct is cached so that we only create it once. To avoid dealing with updating the native object if a property is changed, I blocked all setters if the native struct was already created.

For the types that are only constructed by the API:

  • Added a backing field for pointer to the handle returned by the API
  • Most functions are just forwarding parameters to the handle
  • The destructor takes care of releasing them

For the few properties that are lists (e.g., ContainerPortMapping), I created two backing vectors. One is for the WinRT objects that are used when interacting with the consumer, and the other is for backing the pointers passed to the C API.

Things missing in this PR that I'll add later:

  • Handling the more complex types, like callbacks for progress
  • Tests

Validation Steps Performed

Copilot AI review requested due to automatic review settings April 29, 2026 23:11
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a C++/WinRT wrapper over the existing native WSLC SDK API and wires it into a CsWinRT-generated C# projection, including native WinRT activation factory exports and the CMake/NuGet plumbing needed to build/package the projection.

Changes:

  • Added a WinRT IDL surface (wslcsdk.idl) and C++/WinRT wrapper implementations for a large portion of the WSLC SDK object model.
  • Added a C# projection build that consumes the generated .winmd via CsWinRT plus a module-initializer-based activation hook that routes activation to wslcsdk.dll.
  • Updated build/package/export configuration (CMake, .def, NuGet inputs, and package references) to include CsWinRT and the WinRT wrapper artifacts.

Reviewed changes

Copilot reviewed 52 out of 52 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/windows/wslsettings/CMakeLists.txt Disables auto assembly-info generation for wslsettings C# project.
src/windows/WslcSDK/wslcsdk.def Exports WinRT activation entrypoints from wslcsdk.dll for CsWinRT activation.
src/windows/WslcSDK/wslcsdk.cpp Moves SDK default constants to a shared header.
src/windows/WslcSDK/winrt/wslcsdk.idl Defines the WinRT API surface for Microsoft.WSL.Containers.
src/windows/WslcSDK/winrt/precomp.h Adds a WinRT-specific PCH including all wrapper headers.
src/windows/WslcSDK/winrt/WslcService.h Declares WinRT wrapper for service-level APIs.
src/windows/WslcSDK/winrt/WslcService.cpp Implements service-level WinRT wrapper calls.
src/windows/WslcSDK/winrt/VhdRequirements.h Declares WinRT wrapper for VHD requirements options.
src/windows/WslcSDK/winrt/VhdRequirements.cpp Implements VHD requirements validation + struct materialization.
src/windows/WslcSDK/winrt/TagImageOptions.h Declares WinRT wrapper for tag-image options.
src/windows/WslcSDK/winrt/TagImageOptions.cpp Implements tag-image options validation + struct materialization.
src/windows/WslcSDK/winrt/SessionSettings.h Declares WinRT wrapper for session settings.
src/windows/WslcSDK/winrt/SessionSettings.cpp Implements session settings -> native struct conversion.
src/windows/WslcSDK/winrt/Session.h Declares WinRT wrapper for session handle-based APIs.
src/windows/WslcSDK/winrt/Session.cpp Implements session creation and several synchronous operations.
src/windows/WslcSDK/winrt/ServiceVersion.h Declares WinRT wrapper for service version value object.
src/windows/WslcSDK/winrt/ServiceVersion.cpp Implements service version wrapper.
src/windows/WslcSDK/winrt/PushImageOptions.h Declares WinRT wrapper for push-image options.
src/windows/WslcSDK/winrt/PushImageOptions.cpp Implements push-image options validation + struct materialization.
src/windows/WslcSDK/winrt/PullImageOptions.h Declares WinRT wrapper for pull-image options.
src/windows/WslcSDK/winrt/PullImageOptions.cpp Implements pull-image options struct materialization.
src/windows/WslcSDK/winrt/ProcessSettings.h Declares WinRT wrapper for process settings.
src/windows/WslcSDK/winrt/ProcessSettings.cpp Implements process settings -> native struct conversion for cmd/env/working dir.
src/windows/WslcSDK/winrt/Process.h Declares WinRT wrapper for process handle-based APIs.
src/windows/WslcSDK/winrt/Process.cpp Implements basic process operations (pid/state/exitcode/signal).
src/windows/WslcSDK/winrt/InstallProgress.h Declares WinRT wrapper for install progress payload.
src/windows/WslcSDK/winrt/InstallProgress.cpp Implements install progress wrapper.
src/windows/WslcSDK/winrt/ImageProgress.h Declares WinRT wrapper for image progress payload.
src/windows/WslcSDK/winrt/ImageProgress.cpp Implements image progress wrapper properties.
src/windows/WslcSDK/winrt/ImageInfo.h Declares WinRT wrapper for image info payload.
src/windows/WslcSDK/winrt/ImageInfo.cpp Implements image info wrapper conversions to WinRT types.
src/windows/WslcSDK/winrt/Helpers.h Adds helper templates/macros for bridging WinRT objects to native structs/handles.
src/windows/WslcSDK/winrt/ContainerVolume.h Declares WinRT wrapper for bind-mount volume settings.
src/windows/WslcSDK/winrt/ContainerVolume.cpp Implements bind-mount volume validation + struct materialization.
src/windows/WslcSDK/winrt/ContainerSettings.h Declares WinRT wrapper for container settings.
src/windows/WslcSDK/winrt/ContainerSettings.cpp Implements container settings -> native struct conversion (including vectors).
src/windows/WslcSDK/winrt/ContainerPortMapping.h Declares WinRT wrapper for port mapping settings.
src/windows/WslcSDK/winrt/ContainerPortMapping.cpp Implements port mapping validation + struct materialization.
src/windows/WslcSDK/winrt/ContainerNamedVolume.h Declares WinRT wrapper for named volume settings.
src/windows/WslcSDK/winrt/ContainerNamedVolume.cpp Implements named volume validation + struct materialization.
src/windows/WslcSDK/winrt/Container.h Declares WinRT wrapper for container handle-based APIs.
src/windows/WslcSDK/winrt/Container.cpp Implements container lifecycle + inspect/id/state/init-process forwarding.
src/windows/WslcSDK/winrt/CMakeLists.txt Adds build target for WinRT wrapper static library and IDL->winmd generation.
src/windows/WslcSDK/csharp/WinRTActivation.cs Adds C# module initializer to route WinRT activation through native DLL exports.
src/windows/WslcSDK/csharp/CMakeLists.txt Updates C# build to run CsWinRT on the generated winmd and reference CsWinRT NuGet.
src/windows/WslcSDK/Defaults.h Introduces shared default constants for the native SDK.
src/windows/WslcSDK/CMakeLists.txt Integrates winrt + optional csharp subdirectories; links winrt static lib into native DLL.
packages.config Adds Microsoft.Windows.CsWinRT NuGet dependency.
nuget/Microsoft.WSL.Containers.nuspec.in Adjusts packaged outputs to use ${CMAKE_BUILD_TYPE} paths and include C# DLL.
cmake/FindIDL.cmake Adds add_idl_winrt() helper to generate .winmd and cppwinrt outputs.
cmake/FindCSharp.cmake Removes global GenerateAssemblyInfo override (now set per-target where needed).
CMakeLists.txt Adds CsWinRT NuGet discovery and removes duplicate top-level csharp subdir inclusion.

Comment on lines +18 to +23
constexpr uint32_t s_DefaultCPUCount = 2;
constexpr uint32_t s_DefaultMemoryMB = 2000;
// Maximum value per use with HVSOCKET_CONNECT_TIMEOUT_MAX
constexpr ULONG s_DefaultBootTimeout = 300000;
// Default to 1 GB
constexpr UINT64 s_DefaultStorageSize = 1000 * 1000 * 1000;
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defaults.h uses Windows typedefs (ULONG/UINT64) but only includes . This makes the header non-self-contained and can break compilation if included without windows.h. Consider switching these constants to standard fixed-width types (e.g., uint32_t/uint64_t) or include the header that defines ULONG/UINT64.

Copilot uses AI. Check for mistakes.
Comment thread src/windows/WslcSDK/winrt/ContainerPortMapping.cpp Outdated
Comment thread src/windows/WslcSDK/winrt/ContainerPortMapping.cpp
Comment thread src/windows/WslcSDK/winrt/ProcessSettings.cpp
Comment on lines +16 to +26

// Overrides the WinRT activation to route activation of our types through our native DLL.
[ModuleInitializer]
internal static void Initialize()
{
// Get a pointer to the function in the DLL that creates activation factories.
s_getDllFactory = Marshal.GetDelegateForFunctionPointer<DllGetActivationFactoryFn>(
NativeLibrary.GetExport(
NativeLibrary.Load("wslcsdk.dll", typeof(WinRTActivation).Assembly, DllImportSearchPath.AssemblyDirectory),
"DllGetActivationFactory"));

Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WinRTActivation.Initialize() performs NativeLibrary.Load/GetExport during module initialization without any error handling. If the native DLL/export isn’t present (e.g., mispackaging, wrong RID, or running on an unsupported architecture), the module initializer will throw and prevent the entire assembly from loading. Consider using TryLoad/TryGetExport (or catching and rethrowing a clearer exception) so failures are diagnosable and/or fail gracefully.

Suggested change
// Overrides the WinRT activation to route activation of our types through our native DLL.
[ModuleInitializer]
internal static void Initialize()
{
// Get a pointer to the function in the DLL that creates activation factories.
s_getDllFactory = Marshal.GetDelegateForFunctionPointer<DllGetActivationFactoryFn>(
NativeLibrary.GetExport(
NativeLibrary.Load("wslcsdk.dll", typeof(WinRTActivation).Assembly, DllImportSearchPath.AssemblyDirectory),
"DllGetActivationFactory"));
private static IntPtr s_libraryHandle;
// Overrides the WinRT activation to route activation of our types through our native DLL.
[ModuleInitializer]
internal static void Initialize()
{
// Get a pointer to the function in the DLL that creates activation factories.
if (!NativeLibrary.TryLoad("wslcsdk.dll", typeof(WinRTActivation).Assembly, DllImportSearchPath.AssemblyDirectory, out s_libraryHandle))
{
throw new InvalidOperationException(
"Failed to load native dependency 'wslcsdk.dll' required for WinRT activation. " +
"This usually indicates a packaging, RID, or architecture mismatch.");
}
if (!NativeLibrary.TryGetExport(s_libraryHandle, "DllGetActivationFactory", out var getActivationFactoryExport))
{
throw new InvalidOperationException(
"Failed to locate export 'DllGetActivationFactory' in 'wslcsdk.dll' required for WinRT activation.");
}
s_getDllFactory = Marshal.GetDelegateForFunctionPointer<DllGetActivationFactoryFn>(getActivationFactoryExport);

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +56
// Convert the type name to HSTRING
WindowsCreateString(typeName, (uint)typeName.Length, out var hstring);
try
{
if (s_getDllFactory(hstring, out var factory) < 0)
{
return IntPtr.Zero;
}
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WindowsCreateString/WindowsDeleteString return HRESULTs, but the code ignores their return values. If WindowsCreateString fails, subsequent calls into DllGetActivationFactory will be passed an invalid HSTRING. Consider checking the HRESULT (and throwing/returning failure) and only calling WindowsDeleteString when creation succeeds.

Copilot uses AI. Check for mistakes.
Comment on lines +145 to +148
winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::WSL::Containers::ContainerPortMapping> ContainerSettings::PortMappings()
{
return m_portMappings;
}
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ContainerSettings caches the native WslcContainerSettings the first time ToStructPointer() is called and blocks property setters afterward. However PortMappings/Volumes/NamedVolumes are exposed as mutable IVector instances, so callers can still mutate the collections (e.g., Append/RemoveAt) after the native struct is created, and those changes will be silently ignored. Consider projecting these as read-only views, returning copies, or invalidating/rebuilding the cached native struct when the collections change.

Copilot uses AI. Check for mistakes.
Comment thread src/windows/WslcSDK/winrt/WslcService.cpp
Comment thread src/windows/WslcSDK/winrt/Session.cpp
@benhillis
Copy link
Copy Markdown
Member

The feature/wsl-for-apps branch is now defunct. If your PR is still valid, please retarget the master branch. Reach out to @benhillis for help if needed.

@florelis florelis changed the base branch from feature/wsl-for-apps to master April 30, 2026 21:41
Copilot AI review requested due to automatic review settings April 30, 2026 21:41
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

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.

3 participants