Implement WinRT / C# projection for the simple parts of the WSLC SDK API surface#40360
Implement WinRT / C# projection for the simple parts of the WSLC SDK API surface#40360florelis wants to merge 6 commits intomicrosoft:masterfrom
Conversation
There was a problem hiding this comment.
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
.winmdvia CsWinRT plus a module-initializer-based activation hook that routes activation towslcsdk.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. |
| 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; |
There was a problem hiding this comment.
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.
|
|
||
| // 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")); | ||
|
|
There was a problem hiding this comment.
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.
| // 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); |
| // 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; | ||
| } |
There was a problem hiding this comment.
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.
| winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::WSL::Containers::ContainerPortMapping> ContainerSettings::PortMappings() | ||
| { | ||
| return m_portMappings; | ||
| } |
There was a problem hiding this comment.
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.
|
The |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
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
Detailed Description of the Pull Request / Additional comments
This builds on top of #40121 and #40212. The relevant changes for this are only the
.hand.cppfiles.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:
implementationtypes that don't have one in the public APIhstringbut as a nativestd::(w)stringto directly back any string pointers passed to the C API.IReference<>(projected to C# as a nullable type) andnullptrto represent the default.ToStruct()orToStructPointer()(depending on usage) to get and retrieve the equivalentstructfor the C API. This nativestructis 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 nativestructwas already created.For the types that are only constructed by the API:
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:
Validation Steps Performed