WSLC SDK install API update (#40949)
Updates the WSLC SDK install API to allow more flexibility in the calling pattern, specifically enabling caller-initiated updates/reinstalls to WSL even when WSLC is already present.
JohnMcPMS committed
Jul 28, 2026 at 09:12 UTC
b25c0820a9e9211d8c3212b45ee948120eb02919
17 files changed
+354
-69
src/windows/WslcSDK/winrt/CMakeLists.txt
+2
@@ -9,6 +9,7 @@ set(SOURCES
9
ContainerVolume.cpp
10
ImageInfo.cpp
11
ImageProgress.cpp
12
+ InstallOptions.cpp
13
InstallProgress.cpp
14
Process.cpp
15
ProcessCrashInformation.cpp
@@ -33,6 +34,7 @@ set(HEADERS
34
Helpers.h
35
ImageInfo.h
36
ImageProgress.h
37
+ InstallOptions.h
38
InstallProgress.h
39
Process.h
40
ProcessCrashInformation.h
src/windows/WslcSDK/winrt/InstallOptions.cpp
new
+41
@@ -0,0 +1,41 @@
1
+/*++
2
+
3
+Copyright (c) Microsoft. All rights reserved.
4
+
5
+Module Name:
6
+
7
+ InstallOptions.cpp
8
+
9
+Abstract:
10
+
11
+ This file contains the implementation of the WinRT wrapper for the WSLC SDK InstallOptions class.
12
+
13
+--*/
14
+
15
+#include "precomp.h"
16
+#include "InstallOptions.h"
17
+#include "Microsoft.WSL.Containers.InstallOptions.g.cpp"
18
+
19
+namespace winrt::Microsoft::WSL::Containers::implementation {
20
+
21
+winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::WSL::Containers::Component> InstallOptions::Components()
22
+{
23
+ return m_components;
24
+}
25
+
26
+void InstallOptions::Components(winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::WSL::Containers::Component> value)
27
+{
28
+ m_components = std::move(value);
29
+}
30
+
31
+bool InstallOptions::Repair()
32
+{
33
+ return m_repair;
34
+}
35
+
36
+void InstallOptions::Repair(bool value)
37
+{
38
+ m_repair = value;
39
+}
40
+
41
+} // namespace winrt::Microsoft::WSL::Containers::implementation
src/windows/WslcSDK/winrt/InstallOptions.h
new
+41
@@ -0,0 +1,41 @@
1
+/*++
2
+
3
+Copyright (c) Microsoft. All rights reserved.
4
+
5
+Module Name:
6
+
7
+ InstallOptions.h
8
+
9
+Abstract:
10
+
11
+ This file contains the definition of the WinRT wrapper for the WSLC SDK InstallOptions class.
12
+
13
+--*/
14
+
15
+#pragma once
16
+#include "Microsoft.WSL.Containers.InstallOptions.g.h"
17
+#include "Helpers.h"
18
+
19
+namespace winrt::Microsoft::WSL::Containers::implementation {
20
+struct InstallOptions : InstallOptionsT<InstallOptions>
21
+{
22
+ InstallOptions() = default;
23
+
24
+ winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::WSL::Containers::Component> Components();
25
+ void Components(winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::WSL::Containers::Component> value);
26
+ bool Repair();
27
+ void Repair(bool value);
28
+
29
+private:
30
+ winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::WSL::Containers::Component> m_components = nullptr;
31
+ bool m_repair = false;
32
+};
33
+} // namespace winrt::Microsoft::WSL::Containers::implementation
34
+
35
+namespace winrt::Microsoft::WSL::Containers::factory_implementation {
36
+struct InstallOptions : InstallOptionsT<InstallOptions, implementation::InstallOptions>
37
+{
38
+};
39
+} // namespace winrt::Microsoft::WSL::Containers::factory_implementation
40
+
41
+DEFINE_TYPE_HELPERS(InstallOptions);
src/windows/WslcSDK/winrt/WslcService.cpp
+64
-4
@@ -24,6 +24,60 @@ using namespace winrt::Windows::Foundation::Collections;
24
namespace winrt::Microsoft::WSL::Containers::implementation {
25
26
namespace {
27
+ WslcComponentFlags GetComponentsForInstall(const InstallOptions& options)
28
+ {
29
+ WslcComponentFlags result = WslcComponentFlags::WSLC_COMPONENT_FLAG_NONE;
30
+ bool shouldCheckMissingComponents = true;
31
+
32
+ if (options)
33
+ {
34
+ auto components = options.Components();
35
+ if (components)
36
+ {
37
+ shouldCheckMissingComponents = false;
38
+
39
+ for (const auto& component : components)
40
+ {
41
+ switch (component)
42
+ {
43
+ case Component::VirtualMachinePlatform:
44
+ result |= WslcComponentFlags::WSLC_COMPONENT_FLAG_VIRTUAL_MACHINE_PLATFORM;
45
+ break;
46
+ case Component::WslPackage:
47
+ result |= WslcComponentFlags::WSLC_COMPONENT_FLAG_WSL_PACKAGE;
48
+ break;
49
+ case Component::SdkNeedsUpdate:
50
+ THROW_HR(WSLC_E_SDK_UPDATE_NEEDED);
51
+ default:
52
+ THROW_HR(E_INVALIDARG);
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ if (shouldCheckMissingComponents)
59
+ {
60
+ winrt::check_hresult(WslcGetMissingComponents(&result));
61
+ }
62
+
63
+ return result;
64
+ }
65
+
66
+ WslcInstallOptions GetOptionsForInstall(const InstallOptions& options)
67
+ {
68
+ WslcInstallOptions result = WslcInstallOptions::WSLC_INSTALL_OPTION_NONE;
69
+
70
+ if (options)
71
+ {
72
+ if (options.Repair())
73
+ {
74
+ result |= WslcInstallOptions::WSLC_INSTALL_OPTION_REPAIR;
75
+ }
76
+ }
77
+
78
+ return result;
79
+ }
80
+
81
void CALLBACK InstallProgressCallback(WslcComponentFlags component, uint32_t progressSteps, uint32_t totalSteps, PVOID context) noexcept
82
{
83
try
@@ -64,16 +118,22 @@ winrt::Microsoft::WSL::Containers::ServiceVersion WslcService::GetVersion()
118
return winrt::make<implementation::ServiceVersion>(version.major, version.minor, version.revision);
119
}
120
67
-IAsyncActionWithProgress<winrt::Microsoft::WSL::Containers::InstallProgress> WslcService::InstallWithDependenciesAsync()
121
+IAsyncActionWithProgress<winrt::Microsoft::WSL::Containers::InstallProgress> WslcService::InstallWithDependenciesAsync(
122
+ winrt::Microsoft::WSL::Containers::InstallOptions options)
123
{
124
+ auto components = GetComponentsForInstall(options);
125
+ auto wslcOptions = GetOptionsForInstall(options);
126
+
127
co_await winrt::resume_background();
128
129
auto context = ProgressCallbackHelper<winrt::Microsoft::WSL::Containers::InstallProgress>{co_await winrt::get_progress_token()};
72
- winrt::check_hresult(WslcInstallWithDependencies(InstallProgressCallback, &context));
130
+ winrt::check_hresult(WslcInstallWithDependencies(components, wslcOptions, InstallProgressCallback, &context));
131
}
132
75
-void WslcService::InstallWithDependencies()
133
+void WslcService::InstallWithDependencies(winrt::Microsoft::WSL::Containers::InstallOptions options)
134
{
77
- winrt::check_hresult(WslcInstallWithDependencies(nullptr, nullptr));
135
+ auto components = GetComponentsForInstall(options);
136
+ auto wslcOptions = GetOptionsForInstall(options);
137
+ winrt::check_hresult(WslcInstallWithDependencies(components, wslcOptions, nullptr, nullptr));
138
}
139
} // namespace winrt::Microsoft::WSL::Containers::implementation
src/windows/WslcSDK/winrt/WslcService.h
+3
-2
@@ -23,8 +23,9 @@ struct WslcService
23
24
static winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::WSL::Containers::Component> GetMissingComponents();
25
static winrt::Microsoft::WSL::Containers::ServiceVersion GetVersion();
26
- static void InstallWithDependencies();
27
- static winrt::Windows::Foundation::IAsyncActionWithProgress<winrt::Microsoft::WSL::Containers::InstallProgress> InstallWithDependenciesAsync();
26
+ static void InstallWithDependencies(winrt::Microsoft::WSL::Containers::InstallOptions options);
27
+ static winrt::Windows::Foundation::IAsyncActionWithProgress<winrt::Microsoft::WSL::Containers::InstallProgress> InstallWithDependenciesAsync(
28
+ winrt::Microsoft::WSL::Containers::InstallOptions options);
29
};
30
} // namespace winrt::Microsoft::WSL::Containers::implementation
31
namespace winrt::Microsoft::WSL::Containers::factory_implementation {
src/windows/WslcSDK/winrt/wslcsdk.idl
+10
-2
@@ -249,6 +249,14 @@ namespace Microsoft.WSL.Containers
249
UInt32 Revision { get; };
250
};
251
252
+ runtimeclass InstallOptions
253
+ {
254
+ InstallOptions();
255
+
256
+ IVectorView<Component> Components;
257
+ Boolean Repair;
258
+ };
259
+
260
runtimeclass InstallProgress
261
{
262
Component Component { get; };
@@ -260,8 +268,8 @@ namespace Microsoft.WSL.Containers
268
{
269
static IVectorView<Component> GetMissingComponents();
270
static ServiceVersion GetVersion();
263
- static void InstallWithDependencies();
264
- static Windows.Foundation.IAsyncActionWithProgress<InstallProgress> InstallWithDependenciesAsync();
271
+ static void InstallWithDependencies(InstallOptions options);
272
+ static Windows.Foundation.IAsyncActionWithProgress<InstallProgress> InstallWithDependenciesAsync(InstallOptions options);
273
};
274
275
enum VhdType
src/windows/WslcSDK/wslcsdk.cpp
+25
-13
@@ -1664,33 +1664,43 @@ try
1664
}
1665
CATCH_RETURN();
1666
1667
-STDAPI WslcInstallWithDependencies(_In_opt_ WslcInstallCallback progressCallback, _In_opt_ PVOID context)
1667
+STDAPI WslcInstallWithDependencies(
1668
+ _In_ WslcComponentFlags components, _In_ WslcInstallOptions options, _In_opt_ WslcInstallCallback progressCallback, _In_opt_ PVOID context)
1669
try
1670
{
1671
+ // Reject unknown flag bits.
1672
+ constexpr WslcComponentFlags c_knownComponents =
1673
+ WSLC_COMPONENT_FLAG_VIRTUAL_MACHINE_PLATFORM | WSLC_COMPONENT_FLAG_WSL_PACKAGE | WSLC_COMPONENT_FLAG_SDK_NEEDS_UPDATE;
1674
+ RETURN_HR_IF(E_INVALIDARG, (components & ~c_knownComponents) != WSLC_COMPONENT_FLAG_NONE);
1675
+ constexpr WslcInstallOptions c_knownOptions = WSLC_INSTALL_OPTION_REPAIR;
1676
+ RETURN_HR_IF(E_INVALIDARG, (options & ~c_knownOptions) != WSLC_INSTALL_OPTION_NONE);
1677
+
1678
+ // This API cannot update the SDK that the client is using.
1679
+ RETURN_HR_IF(WSLC_E_SDK_UPDATE_NEEDED, WI_IsFlagSet(components, WSLC_COMPONENT_FLAG_SDK_NEEDS_UPDATE));
1680
+
1681
HRESULT result = S_OK;
1671
- bool needsVirtualMachine = NeedsVirtualMachineServicesInstalled();
1672
- auto runtimeResult = CreateSessionManagerRaw().second;
1682
1674
- if (!needsVirtualMachine && SUCCEEDED(runtimeResult))
1683
+ if (components == WSLC_COMPONENT_FLAG_NONE)
1684
{
1685
return result;
1686
}
1687
1679
- THROW_HR_IF(runtimeResult, runtimeResult != REGDB_E_CLASSNOTREG && runtimeResult != WSLC_E_SDK_UPDATE_NEEDED);
1680
-
1681
- // Installing these components requires elevation.
1688
+ // Installing components requires elevation.
1689
RETURN_HR_IF(
1690
HRESULT_FROM_WIN32(ERROR_ELEVATION_REQUIRED),
1691
!wsl::windows::common::security::IsTokenElevated(GetCurrentThreadEffectiveToken()) &&
1692
!wsl::windows::common::security::IsTokenLocalSystem(nullptr));
1693
1687
- if (needsVirtualMachine)
1694
+ bool isRepair = WI_IsFlagSet(options, WSLC_INSTALL_OPTION_REPAIR);
1695
+
1696
+ if (WI_IsFlagSet(components, WSLC_COMPONENT_FLAG_VIRTUAL_MACHINE_PLATFORM))
1697
{
1698
if (progressCallback)
1699
{
1700
progressCallback(WSLC_COMPONENT_FLAG_VIRTUAL_MACHINE_PLATFORM, 0, 1, context);
1701
}
1702
1703
+ // No difference between install and repair, just let DISM attempt to enable the feature.
1704
auto exitCode = WslInstall::InstallOptionalComponent(WslInstall::c_optionalFeatureNameVmp, false);
1705
if (exitCode == ERROR_SUCCESS_REBOOT_REQUIRED)
1706
{
@@ -1709,7 +1719,7 @@ try
1719
}
1720
}
1721
1712
- if (!SUCCEEDED(runtimeResult))
1722
+ if (WI_IsFlagSet(components, WSLC_COMPONENT_FLAG_WSL_PACKAGE))
1723
{
1724
std::function<void(uint32_t)> callback;
1725
if (progressCallback)
@@ -1719,14 +1729,16 @@ try
1729
};
1730
}
1731
1722
- wsl::windows::common::WindowsUpdateContext wuContext;
1723
- wuContext.RunUpdateFlow(true, callback);
1732
+ using WindowsUpdateContext = wsl::windows::common::WindowsUpdateContext;
1733
+ WindowsUpdateContext wuContext;
1734
+ wuContext.RunUpdateFlow(
1735
+ isRepair ? WindowsUpdateContext::UpdateOptions::ResetProductRegistration : WindowsUpdateContext::UpdateOptions::EnsureProductRegistration,
1736
+ callback);
1737
1725
- // Because we do a forced install here, we expect an update.
1738
if (wuContext.GetUpdateCount() == 0)
1739
{
1740
// During the preview period, the package may not be published yet, so fall back to getting it from GH.
1729
- // When moving to GA, change this to a hard error to indicate a service configuration issue.
1741
+ // When moving to GA, change this to an error like WSL_E_NO_UPDATE_AVAILABLE or similar.
1742
if (callback)
1743
{
1744
callback(0);
src/windows/WslcSDK/wslcsdk.h
+12
-2
@@ -624,8 +624,18 @@ STDAPI WslcGetVersion(_Out_writes_(1) WslcVersion* version);
624
typedef __callback void(CALLBACK* WslcInstallCallback)(
625
_In_ WslcComponentFlags component, _In_ uint32_t progressSteps, _In_ uint32_t totalSteps, _In_opt_ PVOID context);
626
627
+typedef enum WslcInstallOptions
628
+{
629
+ WSLC_INSTALL_OPTION_NONE = 0,
630
+ // Allows components to be reinstalled.
631
+ WSLC_INSTALL_OPTION_REPAIR = 1,
632
+} WslcInstallOptions;
633
+
634
+DEFINE_ENUM_FLAG_OPERATORS(WslcInstallOptions);
635
+
636
// Callbacks will only be made for components that are actively installed by this call.
628
-// That list can be acquired prior to this call with `WslcCanRun`.
629
-STDAPI WslcInstallWithDependencies(_In_opt_ WslcInstallCallback progressCallback, _In_opt_ PVOID context);
637
+// The list of required components can be acquired prior to this call with `WslcGetMissingComponents`.
638
+STDAPI WslcInstallWithDependencies(
639
+ _In_ WslcComponentFlags components, _In_ WslcInstallOptions options, _In_opt_ WslcInstallCallback progressCallback, _In_opt_ PVOID context);
640
641
EXTERN_C_END
src/windows/common/WindowsUpdateIntegration.cpp
+12
-15
@@ -126,18 +126,12 @@ namespace anon {
126
};
127
} // namespace anon
128
129
-WindowsUpdateContext::WindowsUpdateContext() :
130
- WindowsUpdateContext(std::make_unique<anon::DefaultWindowsUpdateClassFactory>(), WslProductIdentifier())
129
+WindowsUpdateContext::WindowsUpdateContext() : WindowsUpdateContext(std::make_unique<anon::DefaultWindowsUpdateClassFactory>())
130
{
131
}
132
134
-WindowsUpdateContext::WindowsUpdateContext(std::wstring product) :
135
- WindowsUpdateContext(std::make_unique<anon::DefaultWindowsUpdateClassFactory>(), std::move(product))
136
-{
137
-}
138
-
139
-WindowsUpdateContext::WindowsUpdateContext(std::unique_ptr<WindowsUpdateClassFactory> factory, std::wstring product) :
140
- m_factory(std::move(factory)), m_product(std::move(product))
133
+WindowsUpdateContext::WindowsUpdateContext(std::unique_ptr<WindowsUpdateClassFactory> factory) :
134
+ m_factory(std::move(factory)), m_product(WslProductIdentifier())
135
{
136
m_session = m_factory->CreateUpdateSession();
137
@@ -158,9 +152,12 @@ std::wstring WindowsUpdateContext::WslProductIdentifier()
152
return STRING_TO_WIDE_STRING(DCAT_PRODUCT_NAME);
153
}
154
161
-void WindowsUpdateContext::EnsureProductRegistryEntry() const
155
+void WindowsUpdateContext::EnsureProductRegistryEntry(bool reset) const
156
{
163
- wsl::windows::common::helpers::RegisterWithDcat(false);
157
+ if (reset || !wsl::windows::common::helpers::VersionRegisteredWithDcat())
158
+ {
159
+ wsl::windows::common::helpers::RegisterWithDcat(false);
160
+ }
161
}
162
163
size_t WindowsUpdateContext::SearchForUpdates()
@@ -336,14 +333,14 @@ void WindowsUpdateContext::InstallUpdates(const std::function<void(uint32_t)>& p
333
THROW_IF_FAILED(installationHResult);
334
}
335
339
-void WindowsUpdateContext::RunUpdateFlow(bool forceInstall, const std::function<void(uint32_t)>& progress)
336
+void WindowsUpdateContext::RunUpdateFlow(UpdateOptions options, const std::function<void(uint32_t)>& progress)
337
{
338
TraceLoggingWriteTagged(
339
*m_activity,
340
"RunUpdateFlow",
341
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
342
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
346
- TraceLoggingBool(forceInstall, "forceInstall"));
343
+ TraceLoggingUInt32(static_cast<std::underlying_type_t<UpdateOptions>>(options), "options"));
344
345
static_assert(
346
DownloadProgressPercent + InstallProgressPercent == 100, "Download and Install progress values must add up to 100.");
@@ -353,9 +350,9 @@ void WindowsUpdateContext::RunUpdateFlow(bool forceInstall, const std::function<
350
progress(0);
351
}
352
356
- if (forceInstall)
353
+ if (options != UpdateOptions::None)
354
{
358
- EnsureProductRegistryEntry();
355
+ EnsureProductRegistryEntry(options == UpdateOptions::ResetProductRegistration);
356
}
357
358
size_t updateCount = SearchForUpdates();
src/windows/common/WindowsUpdateIntegration.h
+13
-9
@@ -31,11 +31,8 @@ struct WindowsUpdateContext
31
// Create a context using the default class factory and WSL product.
32
WindowsUpdateContext();
33
34
- // Create a context using the default class factory.
35
- WindowsUpdateContext(std::wstring product);
36
-
34
// Create a context using the provided class factory.
38
- WindowsUpdateContext(std::unique_ptr<WindowsUpdateClassFactory> factory, std::wstring product);
35
+ WindowsUpdateContext(std::unique_ptr<WindowsUpdateClassFactory> factory);
36
37
NON_COPYABLE(WindowsUpdateContext);
38
DEFAULT_MOVABLE(WindowsUpdateContext);
@@ -45,7 +42,8 @@ struct WindowsUpdateContext
42
43
// Ensures that the product is registered in with the Windows Update system.
44
// This is required to use the system for initial installs.
48
- void EnsureProductRegistryEntry() const;
45
+ // When `reset` is true, always sets the entry to a value that will result in an install.
46
+ void EnsureProductRegistryEntry(bool reset = false) const;
47
48
// Searches for updates for the product.
49
// Returns the number of updates found.
@@ -65,11 +63,17 @@ struct WindowsUpdateContext
63
static constexpr uint32_t DownloadProgressPercent = 70;
64
static constexpr uint32_t InstallProgressPercent = 30;
65
68
- // Performs a complete update flow. This is a convenience method to remove the need to call and coordinate the individual actions.
69
- // When `forceInstall` is true, `EnsureProductRegistryEntry` is called.
70
- // Calls the progress callback, if provided, with the overall update progress estimate.
66
+ enum class UpdateOptions
67
+ {
68
+ None,
69
+ EnsureProductRegistration,
70
+ ResetProductRegistration,
71
+ };
72
+
73
+ // Performs a complete update flow. This is a convenience method to remove the need to call and coordinate the individual
74
+ // actions. Calls the progress callback, if provided, with the overall update progress estimate.
75
// Download and install phases are split according to the values defined above.
72
- void RunUpdateFlow(bool forceInstall = false, const std::function<void(uint32_t)>& progress = {});
76
+ void RunUpdateFlow(UpdateOptions options = UpdateOptions::EnsureProductRegistration, const std::function<void(uint32_t)>& progress = {});
77
78
private:
79
using ActivityType = TraceLoggingActivity<g_hTraceLoggingProvider, MICROSOFT_KEYWORD_MEASURES>;
src/windows/common/helpers.cpp
+17
-1
@@ -38,6 +38,7 @@ using wsl::windows::common::helpers::LaunchWslRelayFlags;
38
39
constexpr auto c_WslSupportInterfaceKey = L"Software\\Classes\\Interface\\{46f3c96d-ffa3-42f0-b052-52f5e7ecbb08}";
40
constexpr auto c_WslSupportInterfaceName = L"IWslSupport";
41
+constexpr auto c_DcatRegistryVersionValueName = L"Version";
42
43
constexpr ULONG c_MaxStorageHwQueues = 4;
44
@@ -746,6 +747,21 @@ bool wsl::windows::common::helpers::TryAttachConsole()
747
return ReopenStdHandles();
748
}
749
750
+std::optional<std::wstring> wsl::windows::common::helpers::VersionRegisteredWithDcat()
751
+{
752
+ try
753
+ {
754
+ auto [dcatKey, result] = wsl::windows::common::registry::OpenKeyNoThrow(HKEY_LOCAL_MACHINE, TEXT(DCAT_REGISTRATION_KEY), KEY_READ);
755
+ if (SUCCEEDED(result))
756
+ {
757
+ return wsl::windows::common::registry::ReadOptionalString(dcatKey.get(), nullptr, c_DcatRegistryVersionValueName);
758
+ }
759
+ }
760
+ CATCH_LOG()
761
+
762
+ return {};
763
+}
764
+
765
void wsl::windows::common::helpers::RegisterWithDcat(_In_ bool IncludeVersionNumber)
766
try
767
{
@@ -760,7 +776,7 @@ try
776
}
777
778
wil::unique_hkey dcatKey = wsl::windows::common::registry::CreateKey(HKEY_LOCAL_MACHINE, TEXT(DCAT_REGISTRATION_KEY), KEY_SET_VALUE);
763
- wsl::windows::common::registry::WriteString(dcatKey.get(), nullptr, L"Version", registeredVersion.c_str());
779
+ wsl::windows::common::registry::WriteString(dcatKey.get(), nullptr, c_DcatRegistryVersionValueName, registeredVersion.c_str());
780
}
781
CATCH_LOG()
782
src/windows/common/helpers.hpp
+2
@@ -203,6 +203,8 @@ void SetHandleInheritable(_In_ HANDLE Handle, _In_ bool Inheritable = true);
203
204
bool TryAttachConsole();
205
206
+std::optional<std::wstring> VersionRegisteredWithDcat();
207
+
208
void RegisterWithDcat(_In_ bool IncludeVersionNumber = true);
209
210
void AppendCommonKernelCommandLine(_Inout_ std::wstring& kernelCmdLine, _In_ int pageReportingOrder, _In_ ULONG64 swiotlbSizeBytes, _In_ ULONG cpuCount);
test/windows/Common.cpp
+3
-2
@@ -2568,7 +2568,7 @@ void ScopedEnvVariable::Clear()
2568
VERIFY_IS_TRUE(SetEnvironmentVariableW(m_name.c_str(), nullptr));
2569
}
2570
2571
-UniqueWebServer::UniqueWebServer(LPCWSTR Endpoint, LPCWSTR Content)
2571
+UniqueWebServer::UniqueWebServer(LPCWSTR Endpoint, LPCWSTR Content, UINT StatusCode)
2572
{
2573
auto cmd = std::format(
2574
LR"(Powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "
@@ -2579,12 +2579,13 @@ $server.Start()
2579
while ($true)
2580
{{
2581
$context = $server.GetContext()
2582
- $context.Response.StatusCode
2582
+ $context.Response.StatusCode = {}
2583
$content = [Text.Encoding]::UTF8.GetBytes('{}')
2584
$context.Response.OutputStream.Write($content , 0, $content.length)
2585
$context.Response.close()
2586
}}")",
2587
Endpoint,
2588
+ StatusCode,
2589
Content);
2590
2591
m_process = LxsstuStartProcess(cmd.data());
test/windows/Common.h
+1
-1
@@ -356,7 +356,7 @@ private:
356
class UniqueWebServer
357
{
358
public:
359
- UniqueWebServer(LPCWSTR Endpoint, LPCWSTR ResponseContent);
359
+ UniqueWebServer(LPCWSTR Endpoint, LPCWSTR ResponseContent, UINT StatusCode = 200);
360
UniqueWebServer(LPCWSTR Endpoint, const std::filesystem::path& path);
361
~UniqueWebServer();
362
UniqueWebServer(const UniqueWebServer&) = delete;
test/windows/WindowsUpdateTests.cpp
+17
-17
@@ -830,7 +830,7 @@ class WindowsUpdateTests
830
TEST_METHOD(SearchForUpdates_NoUpdates)
831
{
832
auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
833
- WindowsUpdateContext ctx(std::move(factory), L"TestProduct");
833
+ WindowsUpdateContext ctx(std::move(factory));
834
835
VERIFY_ARE_EQUAL(0u, ctx.SearchForUpdates());
836
VERIFY_ARE_EQUAL(0u, ctx.GetUpdateCount());
@@ -845,7 +845,7 @@ class WindowsUpdateTests
845
AddMockUpdate(col, VARIANT_FALSE);
846
AddMockUpdate(col, VARIANT_FALSE);
847
848
- WindowsUpdateContext ctx(std::move(factory), L"TestProduct");
848
+ WindowsUpdateContext ctx(std::move(factory));
849
850
VERIFY_ARE_EQUAL(3u, ctx.SearchForUpdates());
851
VERIFY_ARE_EQUAL(3u, ctx.GetUpdateCount());
@@ -858,7 +858,7 @@ class WindowsUpdateTests
858
fp->session->searcher->searchResult->resultCode = OperationResultCode::orcSucceededWithErrors;
859
AddMockUpdate(fp->session->searcher->searchResult->updates.get(), VARIANT_FALSE);
860
861
- WindowsUpdateContext ctx(std::move(factory), L"TestProduct");
861
+ WindowsUpdateContext ctx(std::move(factory));
862
863
// orcSucceededWithErrors must succeed — the update count is still returned.
864
VERIFY_ARE_EQUAL(1u, ctx.SearchForUpdates());
@@ -869,7 +869,7 @@ class WindowsUpdateTests
869
auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
870
factory->session->searcher->searchResult->resultCode = OperationResultCode::orcFailed;
871
872
- WindowsUpdateContext ctx(std::move(factory), L"TestProduct");
872
+ WindowsUpdateContext ctx(std::move(factory));
873
874
VERIFY_ARE_EQUAL(WSLC_E_WU_SEARCH_FAILED, CaptureHResult([&] { ctx.SearchForUpdates(); }));
875
}
@@ -886,7 +886,7 @@ class WindowsUpdateTests
886
AddMockUpdate(col, VARIANT_TRUE);
887
AddMockUpdate(col, VARIANT_TRUE);
888
889
- WindowsUpdateContext ctx(std::move(factory), L"TestProduct");
889
+ WindowsUpdateContext ctx(std::move(factory));
890
ctx.SearchForUpdates();
891
892
std::vector<uint32_t> progressCalls;
@@ -909,7 +909,7 @@ class WindowsUpdateTests
909
AddMockUpdate(col, VARIANT_FALSE); // needs download
910
AddMockUpdate(col, VARIANT_FALSE); // needs download
911
912
- WindowsUpdateContext ctx(std::move(factory), L"TestProduct");
912
+ WindowsUpdateContext ctx(std::move(factory));
913
ctx.SearchForUpdates();
914
ctx.DownloadUpdates();
915
@@ -924,7 +924,7 @@ class WindowsUpdateTests
924
AddMockUpdate(fp->session->searcher->searchResult->updates.get(), VARIANT_FALSE);
925
fp->session->downloader->downloadResult->downloadHResult = E_FAIL;
926
927
- WindowsUpdateContext ctx(std::move(factory), L"TestProduct");
927
+ WindowsUpdateContext ctx(std::move(factory));
928
ctx.SearchForUpdates();
929
930
VERIFY_ARE_EQUAL(E_FAIL, CaptureHResult([&] { ctx.DownloadUpdates(); }));
@@ -940,7 +940,7 @@ class WindowsUpdateTests
940
auto* fp = factory.get();
941
AddMockUpdate(fp->session->searcher->searchResult->updates.get(), VARIANT_TRUE);
942
943
- WindowsUpdateContext ctx(std::move(factory), L"TestProduct");
943
+ WindowsUpdateContext ctx(std::move(factory));
944
ctx.SearchForUpdates();
945
946
// Should not throw.
@@ -957,7 +957,7 @@ class WindowsUpdateTests
957
AddMockUpdate(fp->session->searcher->searchResult->updates.get(), VARIANT_TRUE);
958
fp->session->installer->installResult->installHResult = E_ACCESSDENIED;
959
960
- WindowsUpdateContext ctx(std::move(factory), L"TestProduct");
960
+ WindowsUpdateContext ctx(std::move(factory));
961
ctx.SearchForUpdates();
962
963
VERIFY_ARE_EQUAL(E_ACCESSDENIED, CaptureHResult([&] { ctx.InstallUpdates(); }));
@@ -972,10 +972,10 @@ class WindowsUpdateTests
972
auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
973
auto* fp = factory.get();
974
975
- WindowsUpdateContext ctx(std::move(factory), L"TestProduct");
975
+ WindowsUpdateContext ctx(std::move(factory));
976
977
std::vector<uint32_t> progressCalls;
978
- ctx.RunUpdateFlow(false, [&](uint32_t p) { progressCalls.push_back(p); });
978
+ ctx.RunUpdateFlow(WindowsUpdateContext::UpdateOptions::None, [&](uint32_t p) { progressCalls.push_back(p); });
979
980
// progress(0) at the start, progress(100) because there are no updates.
981
VERIFY_ARE_EQUAL(2u, progressCalls.size());
@@ -993,10 +993,10 @@ class WindowsUpdateTests
993
auto* fp = factory.get();
994
AddMockUpdate(fp->session->searcher->searchResult->updates.get(), VARIANT_FALSE);
995
996
- WindowsUpdateContext ctx(std::move(factory), L"TestProduct");
996
+ WindowsUpdateContext ctx(std::move(factory));
997
998
std::vector<uint32_t> progressCalls;
999
- ctx.RunUpdateFlow(false, [&](uint32_t p) { progressCalls.push_back(p); });
999
+ ctx.RunUpdateFlow(WindowsUpdateContext::UpdateOptions::None, [&](uint32_t p) { progressCalls.push_back(p); });
1000
1001
// progress(0) is emitted at the start.
1002
VERIFY_IS_FALSE(progressCalls.empty());
@@ -1027,9 +1027,9 @@ class WindowsUpdateTests
1027
AddMockUpdate(fp->session->searcher->searchResult->updates.get(), VARIANT_FALSE);
1028
fp->session->downloader->downloadResult->downloadHResult = E_FAIL;
1029
1030
- WindowsUpdateContext ctx(std::move(factory), L"TestProduct");
1030
+ WindowsUpdateContext ctx(std::move(factory));
1031
1032
- VERIFY_ARE_EQUAL(E_FAIL, CaptureHResult([&] { ctx.RunUpdateFlow(); }));
1032
+ VERIFY_ARE_EQUAL(E_FAIL, CaptureHResult([&] { ctx.RunUpdateFlow(WindowsUpdateContext::UpdateOptions::None); }));
1033
1034
// Install should not have been called after download failure.
1035
VERIFY_IS_FALSE(fp->session->installer->beginInstallCalled);
@@ -1041,9 +1041,9 @@ class WindowsUpdateTests
1041
auto factory = std::make_unique<MockWindowsUpdateClassFactory>();
1042
AddMockUpdate(factory->session->searcher->searchResult->updates.get(), VARIANT_TRUE);
1043
1044
- WindowsUpdateContext ctx(std::move(factory), L"TestProduct");
1044
+ WindowsUpdateContext ctx(std::move(factory));
1045
1046
// Should complete without crashing even with no progress callback.
1047
- ctx.RunUpdateFlow();
1047
+ ctx.RunUpdateFlow(WindowsUpdateContext::UpdateOptions::None);
1048
}
1049
};
test/windows/WslcSdkTests.cpp
+54
@@ -1513,6 +1513,60 @@ class WslcSdkTests
1513
VERIFY_ARE_EQUAL(missing, WSLC_COMPONENT_FLAG_NONE);
1514
}
1515
1516
+ WSLC_TEST_METHOD(InstallWithDependencies_NoComponents_Succeeds)
1517
+ {
1518
+ // Passing WSLC_COMPONENT_FLAG_NONE must return S_OK immediately without requiring elevation.
1519
+ VERIFY_SUCCEEDED(WslcInstallWithDependencies(WSLC_COMPONENT_FLAG_NONE, WSLC_INSTALL_OPTION_NONE, nullptr, nullptr));
1520
+ }
1521
+
1522
+ WSLC_TEST_METHOD(InstallWithDependencies_SdkNeedsUpdate_ReturnsError)
1523
+ {
1524
+ // Passing SDK_NEEDS_UPDATE must always return WSLC_E_SDK_UPDATE_NEEDED — the caller must update their SDK.
1525
+ VERIFY_ARE_EQUAL(
1526
+ WSLC_E_SDK_UPDATE_NEEDED,
1527
+ WslcInstallWithDependencies(WSLC_COMPONENT_FLAG_SDK_NEEDS_UPDATE, WSLC_INSTALL_OPTION_NONE, nullptr, nullptr));
1528
+ }
1529
+
1530
+ WSLC_TEST_METHOD(InstallWithDependencies_WslPackage_GhFallback404)
1531
+ {
1532
+ // Without repair semantics, DCAT uses EnsureProductRegistration so it should find no update
1533
+ // (the product is already registered at the current version). The code then falls back to the
1534
+ // GitHub release endpoint. This test intercepts that fallback: the fake API server returns a
1535
+ // release whose asset URL points at a second local server that always responds with HTTP 404,
1536
+ // so the download fails and WslcInstallWithDependencies surfaces an error HRESULT.
1537
+ constexpr auto apiEndpoint = L"http://127.0.0.1:12345/";
1538
+ constexpr auto assetEndpoint = L"http://127.0.0.1:12346/";
1539
+
1540
+ RegistryKeyChange<std::wstring> urlOverride(
1541
+ HKEY_LOCAL_MACHINE,
1542
+ L"Software\\Microsoft\\Windows\\CurrentVersion\\Lxss",
1543
+ wsl::windows::common::wslutil::c_githubUrlOverrideRegistryValue,
1544
+ apiEndpoint);
1545
+
1546
+ // Version 1.0.0 is below the current package version, so without repair=true the version
1547
+ // check in UpdatePackageImpl would short-circuit and return early. Using a sub-2.0 version
1548
+ // here confirms that the repair flag (always set in the GH fallback) is what drives the
1549
+ // download attempt rather than the version being newer than the installed one.
1550
+ constexpr auto GitHubApiResponse =
1551
+ LR"([{
1552
+ \"name\": \"1.0.0\",
1553
+ \"created_at\": \"2023-06-14T16:56:30Z\",
1554
+ \"assets\": [
1555
+ {
1556
+ \"url\": \"http://127.0.0.1:12346/fake.msixbundle\",
1557
+ \"id\": 1,
1558
+ \"name\": \"Microsoft.WSL_1.0.0.0_x64_ARM64.msixbundle\"
1559
+ }
1560
+ ]
1561
+ }])";
1562
+
1563
+ UniqueWebServer apiServer(apiEndpoint, GitHubApiResponse);
1564
+ UniqueWebServer assetServer(assetEndpoint, L"", 404u);
1565
+
1566
+ VERIFY_ARE_EQUAL(
1567
+ HTTP_E_STATUS_NOT_FOUND, WslcInstallWithDependencies(WSLC_COMPONENT_FLAG_WSL_PACKAGE, WSLC_INSTALL_OPTION_NONE, nullptr, nullptr));
1568
+ }
1569
+
1570
// -----------------------------------------------------------------------
1571
// WslcSetProcessSettingsCallbacks tests
1572
// -----------------------------------------------------------------------
test/windows/WslcSdkWinRTTests.cpp
+37
-1
@@ -1164,10 +1164,46 @@ class WslcSdkWinRtTests
1164
1165
WSLC_TEST_METHOD(InstallWithDependencies)
1166
{
1167
- WSLCSDK::WslcService::InstallWithDependenciesAsync().get();
1167
+ // Pass null options to auto-detect and install any missing components (same behavior as the old no-arg call).
1168
+ WSLCSDK::WslcService::InstallWithDependenciesAsync(nullptr).get();
1169
VERIFY_ARE_EQUAL(WSLCSDK::WslcService::GetMissingComponents().Size(), 0u);
1170
}
1171
1172
+ WSLC_TEST_METHOD(InstallOptions_DefaultValues)
1173
+ {
1174
+ // Default-constructed InstallOptions must have null Components and Repair=false.
1175
+ auto options = WSLCSDK::InstallOptions();
1176
+ VERIFY_IS_NULL(options.Components());
1177
+ VERIFY_IS_FALSE(options.Repair());
1178
+ }
1179
+
1180
+ WSLC_TEST_METHOD(InstallOptions_SetRepair)
1181
+ {
1182
+ auto options = WSLCSDK::InstallOptions();
1183
+ options.Repair(true);
1184
+ VERIFY_IS_TRUE(options.Repair());
1185
+ options.Repair(false);
1186
+ VERIFY_IS_FALSE(options.Repair());
1187
+ }
1188
+
1189
+ WSLC_TEST_METHOD(InstallOptions_SetComponents)
1190
+ {
1191
+ auto options = WSLCSDK::InstallOptions();
1192
+ auto components = winrt::single_threaded_vector<WSLCSDK::Component>({WSLCSDK::Component::WslPackage});
1193
+ options.Components(components.GetView());
1194
+ VERIFY_ARE_EQUAL(1u, options.Components().Size());
1195
+ VERIFY_ARE_EQUAL(WSLCSDK::Component::WslPackage, options.Components().GetAt(0));
1196
+ }
1197
+
1198
+ WSLC_TEST_METHOD(InstallWithDependencies_SdkNeedsUpdate_Throws)
1199
+ {
1200
+ // Passing SdkNeedsUpdate in the component list must throw WSLC_E_SDK_UPDATE_NEEDED.
1201
+ auto options = WSLCSDK::InstallOptions();
1202
+ auto components = winrt::single_threaded_vector<WSLCSDK::Component>({WSLCSDK::Component::SdkNeedsUpdate});
1203
+ options.Components(components.GetView());
1204
+ VERIFY_THROWS_HR(WSLCSDK::WslcService::InstallWithDependenciesAsync(options).get(), WSLC_E_SDK_UPDATE_NEEDED);
1205
+ }
1206
+
1207
// -----------------------------------------------------------------------
1208
// Process IO event tests
1209
// -----------------------------------------------------------------------