Partial SDK implementation#14214
Conversation
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Implements additional portions of the Windows WSL Container SDK (WSLC) so the sample/demo can run end-to-end against the WSLA runtime COM interfaces.
Changes:
- Implements substantial parts of session/container/process SDK entry points (create/terminate/release, container create/start/stop/delete, process handle getters, image pull).
- Introduces COM callback adapters (
IProgressCallback,ITerminationCallback) to bridge runtime callbacks to SDK callbacks. - Refactors internal opaque settings/handle plumbing (private internal structs, size adjustments, internal type helpers) and wires new sources into the build.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/windows/WslcSDK/wslcsdk.h | Updates public SDK surface (opaque struct size, container create signature). |
| src/windows/WslcSDK/wslcsdk.cpp | Implements many previously-stubbed SDK APIs and bridges to WSLA runtime COM interfaces. |
| src/windows/WslcSDK/WslcsdkPrivate.h | Adds/updates internal struct definitions and internal-type helper macros. |
| src/windows/WslcSDK/WslcsdkPrivate.cpp | Implements internal handle/settings reinterpret-casts. |
| src/windows/WslcSDK/ProgressCallback.h / .cpp | Adds adapter implementing IProgressCallback for image pull progress. |
| src/windows/WslcSDK/TerminationCallback.h / .cpp | Adds adapter implementing ITerminationCallback for session termination notifications. |
| src/windows/WslcSDK/CMakeLists.txt | Adds new callback/private implementation files to the build. |
| STDAPI WslcContainerCreate(_In_ WslcSession session, _In_ WslcContainerSettings* containerSettings, _Out_ WslcContainer* container, _Outptr_opt_result_z_ PWSTR* errorMessage); | ||
|
|
There was a problem hiding this comment.
WslcContainerCreate is an exported C API (see wslcsdk.def) and its signature was changed by adding a WslcSession parameter. This is a binary-breaking ABI change for any existing callers that link against the DLL. If compatibility is required, consider keeping the old entry point and adding a new function (e.g. WslcContainerCreate2) or otherwise versioning the DLL/API surface explicitly.
| STDAPI WslcContainerCreate(_In_ WslcSession session, _In_ WslcContainerSettings* containerSettings, _Out_ WslcContainer* container, _Outptr_opt_result_z_ PWSTR* errorMessage); | |
| STDAPI WslcContainerCreate(_In_ WslcContainerSettings* containerSettings, _Out_ WslcContainer* container, _Outptr_opt_result_z_ PWSTR* errorMessage); | |
| STDAPI WslcContainerCreate2(_In_ WslcSession session, _In_ WslcContainerSettings* containerSettings, _Out_ WslcContainer* container, _Outptr_opt_result_z_ PWSTR* errorMessage); |
| // Converts to a unique_ptr of the internal type and returns an error on null input. | ||
| // Use for Release functions to clean up the implementation object on return. | ||
| #define WSLC_GET_INTERNAL_TYPE_FOR_RELEASE(_input_) \ | ||
| std::unique_ptr<std::remove_pointer_t<decltype(GetInternalType(_input_))>>{GetInternalType(_input_)}; \ | ||
| RETURN_HR_IF_NULL(E_POINTER, _input_)\ |
There was a problem hiding this comment.
WSLC_GET_INTERNAL_TYPE_FOR_RELEASE has the same multi-statement / hidden-return fragility as WSLC_GET_INTERNAL_TYPE, and additionally it’s easy to accidentally create a temporary unique_ptr (deleting the handle immediately) when used as a standalone statement. Consider making the lifetime/ownership explicit in the Release functions instead of encoding it in a macro.
| --*/ | ||
| #pragma once | ||
| #include "wslaservice.h" | ||
| #include "wslcsdkprivate.h" |
There was a problem hiding this comment.
Header include casing is inconsistent with the actual filename (WslcsdkPrivate.h). Using #include "wslcsdkprivate.h" can break builds on case-sensitive filesystems/tools. Please match the on-disk casing consistently across the project.
| #include "wslcsdkprivate.h" | |
| #include "WslcsdkPrivate.h" |
|
|
||
| auto result = std::make_unique<WslcProcessImpl>(); | ||
|
|
||
| RETURN_IF_FAILED(internalType->container->GetInitProcess(&result->process)); |
There was a problem hiding this comment.
The returned IWSLAProcess from GetInitProcess is not passed through ConfigureForCOMImpersonation, unlike other call sites in the repo (e.g. WSLAProcessLauncher). This can lead to access/impersonation failures when the caller later uses the process interface. Configure COM impersonation on result->process before returning the handle.
| RETURN_IF_FAILED(internalType->container->GetInitProcess(&result->process)); | |
| RETURN_IF_FAILED(internalType->container->GetInitProcess(&result->process)); | |
| RETURN_IF_FAILED(ConfigureForCOMImpersonation(result->process.Get())); |
| // TODO: Resolve massive disparity between 32-bit unsigned millisecond input and 64-bit signed second target timeouts | ||
| RETURN_HR(internalType->container->Stop(convertedSignal.value(), timeoutMS / 1000)); |
There was a problem hiding this comment.
timeoutMS is documented/typed as milliseconds but is converted to seconds via integer division (timeoutMS / 1000), which truncates sub-second timeouts to 0 and changes semantics. Consider rounding up (e.g. (timeoutMS + 999) / 1000) and casting to the runtime’s LONGLONG seconds type, or otherwise clearly defining how sub-second values should behave.
| // TODO: Resolve massive disparity between 32-bit unsigned millisecond input and 64-bit signed second target timeouts | |
| RETURN_HR(internalType->container->Stop(convertedSignal.value(), timeoutMS / 1000)); | |
| // Convert milliseconds to seconds for the underlying API, rounding up and widening to 64-bit signed. | |
| const auto timeoutSeconds = | |
| static_cast<LONGLONG>((static_cast<uint64_t>(timeoutMS) + 999ull) / 1000ull); | |
| RETURN_HR(internalType->container->Stop(convertedSignal.value(), timeoutSeconds)); |
| // Converts to the internal type and returns an error on null input. | ||
| #define WSLC_GET_INTERNAL_TYPE(_input_) \ | ||
| GetInternalType(_input_); \ | ||
| RETURN_HR_IF_NULL(E_POINTER, _input_) | ||
|
|
There was a problem hiding this comment.
The WSLC_GET_INTERNAL_TYPE macro expands to multiple statements and includes an early-return (RETURN_HR_IF_NULL). This makes it fragile (e.g., it cannot be used as a normal expression argument) and obscures control flow. Consider replacing it with explicit RETURN_HR_IF_NULL + GetInternalType(...) statements at call sites, or reworking into a safer helper pattern (e.g., a small inline function that returns HRESULT and sets an out param).
| --*/ | ||
| #pragma once | ||
| #include "wslaservice.h" | ||
| #include "wslcsdkprivate.h" |
There was a problem hiding this comment.
Header include casing is inconsistent with the actual filename (WslcsdkPrivate.h). Using #include "wslcsdkprivate.h" can break builds on case-sensitive filesystems/tools. Please match the on-disk casing consistently across the project.
| #include "wslcsdkprivate.h" | |
| #include "WslcsdkPrivate.h" |
| --*/ | ||
| #include "precomp.h" | ||
|
|
||
| #include "wslcsdkprivate.h" |
There was a problem hiding this comment.
Include casing is inconsistent with the actual filename (WslcsdkPrivate.h). Using #include "wslcsdkprivate.h" can break builds on case-sensitive filesystems/tools. Please match the on-disk casing consistently across the project.
| #include "wslcsdkprivate.h" | |
| #include "WslcsdkPrivate.h" |
| // TODO: Runtime needs an update to take in LPCSTR const* | ||
| containerOptions.InitProcessOptions.CommandLine.Values = const_cast<LPCSTR*>(initProcessOptions->commandLine); | ||
| containerOptions.InitProcessOptions.CommandLine.Count = initProcessOptions->commandLineCount; | ||
| containerOptions.InitProcessOptions.Environment.Values = const_cast<LPCSTR*>(initProcessOptions->environment); | ||
| containerOptions.InitProcessOptions.Environment.Count = initProcessOptions->environmentCount; |
There was a problem hiding this comment.
const_cast is used to pass commandLine/environment arrays into WSLAStringArray.Values. This weakens const-correctness and could be unsafe if the callee ever writes to the array. Prefer building a temporary mutable std::vector<LPCSTR> (or adjusting the internal storage type) to avoid casting away const.
[Replaces #14211 with an unforked branch]
Summary of the Pull Request
Implements enough of the SDK for the sample demo to operate.
PR Checklist
Detailed Description of the Pull Request / Additional comments
Implements the functions called by the sample demo (as well as a few more trivial functions). These are implemented as best as possible, but there are several TODO callouts for updates that are likely needed to the SDK API surface to account for disparities with the runtime API. I intentionally did not make those changes to the SDK in this PR to limit the friction so that it might get merged sooner. I plan to make a follow-up PR with those changes that can have more/longer discussions.
Additionally, tests are not added for the SDK with this PR at this time. Again, a fast follow-up PR is expected to start adding those tests. Recommendation for new or existing test project are welcome.
Finally, I left the changes that were merged in #14187 to discuss how mainline changes should make it to the feature branch (as I'm dependent on this change for my machine setup to build WSL). Happy to remove it late in the PR process so I can keep building without constant git juggling.
Validation Steps Performed
Sample demo runs successfully and can be run multiple times.