Skip to content

Add a new API to query the earliest supported client version#40067

Draft
OneBlue wants to merge 5 commits intofeature/wsl-for-appsfrom
user/oneblue/client-version-check
Draft

Add a new API to query the earliest supported client version#40067
OneBlue wants to merge 5 commits intofeature/wsl-for-appsfrom
user/oneblue/client-version-check

Conversation

@OneBlue
Copy link
Copy Markdown
Collaborator

@OneBlue OneBlue commented Apr 1, 2026

Summary of the Pull Request

This change adds a new COM API to check the earliest supported client version, and SDK logic to fail with a specific error code if the SDK is too old

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

Validation Steps Performed

Copilot AI review requested due to automatic review settings April 1, 2026 21:08
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 introduces a new WSLC COM API intended to let clients query the minimum (earliest) supported client version, and adds SDK-side gating that fails with a WSLC-specific HRESULT when the SDK is older than what the runtime requires.

Changes:

  • Adds GetMinimumSupportedClientVersion to the session manager COM surface and implements it in the service.
  • Adds a new WSLC-specific HRESULT (WSLC_E_SDK_UPDATED_NEEDED) and SDK logic to enforce a minimum supported client version.
  • Refactors GetVersion implementation in WSLCSessionManager (moves it out of the Impl forwarding path).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

File Description
src/windows/WslcSDK/wslcsdk.h Adds WSLC HRESULT macro definitions for SDK consumers.
src/windows/WslcSDK/wslcsdk.cpp Calls new COM method and throws a WSLC-specific error if SDK version is too old.
src/windows/service/inc/wslc.idl Extends COM interface with a new method and adds an exported HRESULT constant.
src/windows/service/exe/WSLCSessionManager.h / .cpp Implements the new minimum-supported-version query on the service side.

Comment thread src/windows/service/inc/wslc.idl Outdated
Comment thread src/windows/service/inc/wslc.idl
Comment thread src/windows/WslcSDK/wslcsdk.cpp Outdated
Comment thread src/windows/WslcSDK/wslcsdk.h Outdated
Comment on lines +23 to +29
// WSLC specific error codes
#define WSLC_E_BASE (0x0600)
#define WSLC_E_IMAGE_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 1) /* 0x80040601 */
#define WSLC_E_CONTAINER_PREFIX_AMBIGUOUS MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 2) /* 0x80040602 */
#define WSLC_E_CONTAINER_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 3) /* 0x80040603 */
#define WSLC_E_VOLUME_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 4) /* 0x80040604 */
#define WSLC_E_SDK_UPDATED_NEEDED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 5) /* 0x80040605 */
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

WSLC error codes are now defined in this public header, but internal SDK code also includes the MIDL-generated wslc.h (via WslcsdkPrivate.h), which already defines these macros via cpp_quote. This will cause macro redefinition warnings (often /WX) when building the SDK. Consider wrapping these #defines with #ifndef guards or moving the shared HRESULT definitions to a single header included by both.

Suggested change
// WSLC specific error codes
#define WSLC_E_BASE (0x0600)
#define WSLC_E_IMAGE_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 1) /* 0x80040601 */
#define WSLC_E_CONTAINER_PREFIX_AMBIGUOUS MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 2) /* 0x80040602 */
#define WSLC_E_CONTAINER_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 3) /* 0x80040603 */
#define WSLC_E_VOLUME_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 4) /* 0x80040604 */
#define WSLC_E_SDK_UPDATED_NEEDED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 5) /* 0x80040605 */
// WSLC specific error codes
#ifndef WSLC_E_BASE
#define WSLC_E_BASE (0x0600)
#endif
#ifndef WSLC_E_IMAGE_NOT_FOUND
#define WSLC_E_IMAGE_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 1) /* 0x80040601 */
#endif
#ifndef WSLC_E_CONTAINER_PREFIX_AMBIGUOUS
#define WSLC_E_CONTAINER_PREFIX_AMBIGUOUS MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 2) /* 0x80040602 */
#endif
#ifndef WSLC_E_CONTAINER_NOT_FOUND
#define WSLC_E_CONTAINER_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 3) /* 0x80040603 */
#endif
#ifndef WSLC_E_VOLUME_NOT_FOUND
#define WSLC_E_VOLUME_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 4) /* 0x80040604 */
#endif
#ifndef WSLC_E_SDK_UPDATED_NEEDED
#define WSLC_E_SDK_UPDATED_NEEDED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 5) /* 0x80040605 */
#endif

Copilot uses AI. Check for mistakes.
Comment thread src/windows/WslcSDK/wslcsdk.h Outdated
#define WSLC_E_CONTAINER_PREFIX_AMBIGUOUS MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 2) /* 0x80040602 */
#define WSLC_E_CONTAINER_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 3) /* 0x80040603 */
#define WSLC_E_VOLUME_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 4) /* 0x80040604 */
#define WSLC_E_SDK_UPDATED_NEEDED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 5) /* 0x80040605 */
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The new HRESULT name WSLC_E_SDK_UPDATED_NEEDED appears to have a typo/awkward tense ("UPDATED" vs "UPDATE"). Since this constant is part of the public API surface (IDL + SDK header), it’s best to correct the name now (e.g., WSLC_E_SDK_UPDATE_NEEDED/WSLC_E_SDK_UPDATE_REQUIRED) before it becomes a compatibility burden.

Suggested change
#define WSLC_E_SDK_UPDATED_NEEDED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 5) /* 0x80040605 */
#define WSLC_E_SDK_UPDATE_NEEDED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 5) /* 0x80040605 */

Copilot uses AI. Check for mistakes.
Comment thread src/windows/service/exe/WSLCSessionManager.cpp
Comment thread src/windows/WslcSDK/wslcsdk.cpp Outdated
decltype(wsl::shared::PackageVersion) requiredClientVersion{
minimumClientVersion.Major, minimumClientVersion.Minor, minimumClientVersion.Revision};

THROW_HR_IF_MSG(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This throw would go into CreateSessionManager and instead we would return the HR so that CanRun succeeds with a missing component rather than failing. Really with this API I think the SDK stuff would be refactored in several ways.

@benhillis
Copy link
Copy Markdown
Member

I spoke with @OneBlue, I think we should consider COM interface versioning a bit more. The problem with this solution is it required a checked-in version, and versions are controlled by git tags, so there is a chicken and egg problem.

@JohnMcPMS
Copy link
Copy Markdown
Member

I spoke with @OneBlue, I think we should consider COM interface versioning a bit more. The problem with this solution is it required a checked-in version, and versions are controlled by git tags, so there is a chicken and egg problem.

His original idea for a IsClientVersionCompatible moves the problem to deciding about already released versions and allows for the same binary choice but removes the ability to know to what version one must upgrade. Given that this upgrade choice is a development time one for the SDK consumer, it seems acceptable.

No matter how we do this, we would only want to break older SDK usage if it were a security issue. We can do that anyway, just less gracefully, without anything new (ex. making all ISession1 functions fail; must use ISession2 instead). The question becomes if it is worth building up the pre-check if it is extremely rare to happen and then no clients actually bother. A very specific HRESULT and documentation is probably good enough without the extra work from everyone.

@benhillis
Copy link
Copy Markdown
Member

Hey @OneBlue 👋 — Following up on this draft PR. CI is green, but there are 3 unresolved review threads:

  • \#ifndef\ guards needed for macro redefinitions
  • Typo: \WSLC_E_SDK_UPDATED_NEEDED\ → should be \UPDATE\
  • SDK refactoring discussion from @JohnMcPMS about \CreateSessionManager\ error handling

Is this still being actively developed? Let us know if the design direction has been settled.

@OneBlue
Copy link
Copy Markdown
Collaborator Author

OneBlue commented Apr 22, 2026

I spoke with @OneBlue, I think we should consider COM interface versioning a bit more. The problem with this solution is it required a checked-in version, and versions are controlled by git tags, so there is a chicken and egg problem.

Did a bit of research on this, I think we should do both COM interface versioning and still have a method to allow clients to check for compatibility.

COM versioning will allow us to add more methods in new interfaces without breaking ABI, and if one day we decide to stop supporting old client, we'll have an "escape" to do that without causing the clients to fail with confusing errors.

Now that we have the 2.8.0 tag in the feature branch, the chicken-egg issue is also gone, we can just default to 2.8.0 as the lowest version.

How does that sound @benhillis ?

@JohnMcPMS
Copy link
Copy Markdown
Member

I would still propose that we swap who is responsible for determining version support from the current PR. We still have a similar "predict the release version number" problem if we return the minimum supported version (when it is the running version). If the runtime API is IsClientVersionSupported([in] WslcVersion* version, [out]BOOL* isSupported), then the runtime code can simply use a relation to previously released version numbers rather than needing to know the future number (ex. > 2.7.2).

Regardless of the API shape, any developer caught by surprise at the release of a new WSL version that makes their SDK version unsupported will need to make and deliver a new build. I'm sure that we will have documented this and it will be discoverable quickly.

Copilot AI review requested due to automatic review settings April 23, 2026 21:45
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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.

Comments suppressed due to low confidence (1)

src/windows/WslcSDK/wslcsdk.cpp:1576

  • This treats any non-success (including WSLC_E_SDK_UPDATE_NEEDED) as requiring the Windows Update flow. If the SDK is too old for the installed runtime, running the installer path won’t fix the issue and may prompt for elevation unnecessarily. Consider special-casing WSLC_E_SDK_UPDATE_NEEDED to return that error immediately instead of invoking Windows Update.
    if (!SUCCEEDED(runtimeResult))
    {
        std::function<void(uint32_t)> callback;
        if (progressCallback)
        {

return result;
}

THROW_HR_IF(runtimeResult, runtimeResult != REGDB_E_CLASSNOTREG && runtimeResult != WSLC_E_SDK_UPDATE_NEEDED);
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

WslcInstallWithDependencies allows WSLC_E_SDK_UPDATE_NEEDED to proceed into the installation path. That error means the SDK is too old for the installed runtime, so attempting to install/repair WSL via Windows Update won’t resolve it and may trigger unnecessary elevation prompts. Consider returning WSLC_E_SDK_UPDATE_NEEDED early instead.

Suggested change
THROW_HR_IF(runtimeResult, runtimeResult != REGDB_E_CLASSNOTREG && runtimeResult != WSLC_E_SDK_UPDATE_NEEDED);
THROW_HR_IF(runtimeResult, runtimeResult != REGDB_E_CLASSNOTREG && runtimeResult != WSLC_E_SDK_UPDATE_NEEDED);
RETURN_HR_IF(WSLC_E_SDK_UPDATE_NEEDED, runtimeResult == WSLC_E_SDK_UPDATE_NEEDED);

Copilot uses AI. Check for mistakes.
Comment thread test/windows/InstallerTests.cpp
Comment thread src/windows/service/inc/wslc.idl
Comment on lines 424 to +428
HRESULT WSLCSessionManager::GetVersion(_Out_ WSLCVersion* Version)
{
return CallImpl(&WSLCSessionManagerImpl::GetVersion, Version);
Version->Major = WSL_PACKAGE_VERSION_MAJOR;
Version->Minor = WSL_PACKAGE_VERSION_MINOR;
Version->Revision = WSL_PACKAGE_VERSION_REVISION;
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

GetVersion dereferences Version without validating it. Add an explicit null check (returning E_POINTER) before writing to the out-parameter to avoid an AV if a caller passes nullptr.

Copilot uses AI. Check for mistakes.
}

HRESULT WSLCSessionManager::GetMinimumSupportedClientVersion(_Out_ WSLCVersion* Version)
{
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

GetMinimumSupportedClientVersion writes to Version without validating it. Add an explicit null check (E_POINTER) to match the rest of the COM surface’s parameter validation and avoid crashing on nullptr.

Suggested change
{
{
RETURN_HR_IF(E_POINTER, Version == nullptr);

Copilot uses AI. Check for mistakes.
Comment thread test/windows/WSLCTests.cpp Outdated
Comment thread src/windows/WslcSDK/wslcsdk.cpp
Comment thread src/windows/WslcSDK/wslcsdk.h
constexpr std::tuple<uint32_t, uint32_t, uint32_t> c_minClientVersion{2, 9, 0};

// If the current version is below the minimum version, return the current version for convenience.
// TODO: Remove once 2.9.0 is published.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Presumably the removal here would be replaced with a 2.8.* as appropriate to the actual release version of WSLC.

While this pattern can work, I still think it better that we have the code where we control the update make the actual decision on whether the client should be supported. Returning the version to the unchanging client code means we couldn't do things like "2.9.0 introduced a security issue that was fixed in 2.9.2, but 2.8.0 didn't have it and should be allowed to continue on".

// The WSL runtime package, at an appropriate version to provide support for WSLC.
WSLC_COMPONENT_FLAG_WSL_PACKAGE = 2,
// Set if the WSLC SDK itself needs to be updated.
WSLC_COMPONENT_FLAG_SDK_NEEDS_UPDATE = 4,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Adding this makes me want to change the API name back to WslcCanRun and change the name of the enum/parameter to be more aligned with "reasons that we cannot run".

@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.

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.

4 participants