SDK updates (#40840)
JohnMcPMS committed
Jun 22, 2026 at 13:50 UTC
c8888dc66d17189ca8e66f362de2d2b93a186fc7
15 files changed
+381
-43
src/windows/WslcSDK/CMakeLists.txt
+3
-2
@@ -28,5 +28,6 @@ if (WSL_INCLUDE_SDK_CSHARP)
28
endif()
29
30
add_dependencies(wslcsdk wslserviceidl wslcsdkwinrt)
31
-target_link_libraries(wslcsdk ${COMMON_LINK_LIBRARIES} legacy_stdio_definitions common wslcsdkwinrt)
32
-target_precompile_headers(wslcsdk REUSE_FROM common)
\ No newline at end of file
31
+target_link_libraries(wslcsdk ${COMMON_LINK_LIBRARIES} ${MSI_LINK_LIBRARIES} legacy_stdio_definitions common wslcsdkwinrt delayimp.lib)
32
+set_target_properties(wslcsdk PROPERTIES LINK_FLAGS "/DELAYLOAD:msi.dll /DELAYLOAD:WINTRUST.dll")
33
+target_precompile_headers(wslcsdk REUSE_FROM common)
src/windows/WslcSDK/ProgressCallback.cpp
+23
-9
@@ -25,16 +25,30 @@ WslcImageProgressStatus ConvertStatus(LPCSTR Status)
25
return _status_; \
26
}
27
28
- // TODO: Mapping engine strings to status values seems fragile.
29
- // WSLC is intentionally avoiding this kind of thing for localization of engine strings, which amounts to the same
30
- // thing. If we keep this, a test should be added to explicitly validate that each status is returned properly.
31
- WSLC_STRING_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_PULLING, "Pulling fs layer");
32
- WSLC_STRING_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_WAITING, "Waiting");
33
- WSLC_STRING_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_DOWNLOADING, "Downloading");
34
- WSLC_STRING_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_VERIFYING, "Verifying Checksum");
35
- WSLC_STRING_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_EXTRACTING, "Extracting");
36
- WSLC_STRING_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_COMPLETE, "Pull complete");
28
+#define WSLC_PREFIX_TO_STATUS_MAPPING(_status_, _prefix_) \
29
+ if (std::string_view{Status}.starts_with(_prefix_##sv)) \
30
+ { \
31
+ return _status_; \
32
+ }
33
+
34
+ if (Status)
35
+ {
36
+ // TODO: Mapping engine strings to status values seems fragile.
37
+ // WSLC is intentionally avoiding this kind of thing for localization of engine strings, which amounts to the same
38
+ // thing. If we keep this, a test should be added to explicitly validate that each status is returned properly.
39
+ WSLC_PREFIX_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_PULLING, "Pulling from ");
40
+ WSLC_STRING_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_PULLING, "Pulling fs layer");
41
+ WSLC_STRING_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_WAITING, "Waiting");
42
+ WSLC_STRING_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_DOWNLOADING, "Downloading");
43
+ WSLC_STRING_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_COMPLETE, "Download complete");
44
+ WSLC_STRING_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_VERIFYING, "Verifying Checksum");
45
+ WSLC_PREFIX_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_VERIFYING, "Digest: ");
46
+ WSLC_STRING_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_EXTRACTING, "Extracting");
47
+ WSLC_STRING_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_COMPLETE, "Pull complete");
48
+ WSLC_PREFIX_TO_STATUS_MAPPING(WSLC_IMAGE_PROGRESS_STATUS_COMPLETE, "Status: ");
49
+ }
50
51
+ WSL_LOG_DEBUG("UnknownImageProgressStatus", TraceLoggingString(Status, "status"));
52
return WSLC_IMAGE_PROGRESS_STATUS_UNKNOWN;
53
}
54
} // namespace
src/windows/WslcSDK/winrt/CMakeLists.txt
+4
-2
@@ -10,7 +10,8 @@ set(SOURCES
10
ImageInfo.cpp
11
ImageProgress.cpp
12
InstallProgress.cpp
13
- Process.cpp
13
+ Process.cpp
14
+ ProcessCrashInformation.cpp
15
ProcessSettings.cpp
16
PullImageOptions.cpp
17
PushImageOptions.cpp
@@ -33,7 +34,8 @@ set(HEADERS
34
ImageInfo.h
35
ImageProgress.h
36
InstallProgress.h
36
- Process.h
37
+ Process.h
38
+ ProcessCrashInformation.h
39
ProcessSettings.h
40
PullImageOptions.h
41
PushImageOptions.h
src/windows/WslcSDK/winrt/ProcessCrashInformation.cpp
new
+53
@@ -0,0 +1,53 @@
1
+/*++
2
+
3
+Copyright (c) Microsoft. All rights reserved.
4
+
5
+Module Name:
6
+
7
+ ProcessCrashInformation.cpp
8
+
9
+Abstract:
10
+
11
+ This file contains the implementation of the WinRT wrapper for the WSLC SDK ProcessCrashInformation class.
12
+
13
+--*/
14
+
15
+#include "precomp.h"
16
+#include "ProcessCrashInformation.h"
17
+#include "Microsoft.WSL.Containers.ProcessCrashInformation.g.cpp"
18
+
19
+namespace winrt::Microsoft::WSL::Containers::implementation {
20
+ProcessCrashInformation::ProcessCrashInformation(const WslcSessionCrashDumpInfo* info)
21
+{
22
+ m_dumpPath = info->dumpPath;
23
+ m_processName = winrt::to_hstring(info->processName);
24
+ m_pid = info->pid;
25
+ m_signal = info->signal;
26
+ m_timestamp = winrt::clock::from_time_t(static_cast<time_t>(info->timestamp));
27
+}
28
+
29
+hstring ProcessCrashInformation::DumpPath() const
30
+{
31
+ return m_dumpPath;
32
+}
33
+
34
+hstring ProcessCrashInformation::ProcessName() const
35
+{
36
+ return m_processName;
37
+}
38
+
39
+uint32_t ProcessCrashInformation::Pid() const
40
+{
41
+ return m_pid;
42
+}
43
+
44
+uint32_t ProcessCrashInformation::Signal() const
45
+{
46
+ return m_signal;
47
+}
48
+
49
+winrt::Windows::Foundation::DateTime ProcessCrashInformation::Timestamp() const
50
+{
51
+ return m_timestamp;
52
+}
53
+} // namespace winrt::Microsoft::WSL::Containers::implementation
src/windows/WslcSDK/winrt/ProcessCrashInformation.h
new
+39
@@ -0,0 +1,39 @@
1
+/*++
2
+
3
+Copyright (c) Microsoft. All rights reserved.
4
+
5
+Module Name:
6
+
7
+ ProcessCrashInformation.h
8
+
9
+Abstract:
10
+
11
+ This file contains the definition of the WinRT wrapper for the WSLC SDK ProcessCrashInformation class.
12
+
13
+--*/
14
+
15
+#pragma once
16
+#include "Microsoft.WSL.Containers.ProcessCrashInformation.g.h"
17
+#include "Helpers.h"
18
+
19
+namespace winrt::Microsoft::WSL::Containers::implementation {
20
+struct ProcessCrashInformation : ProcessCrashInformationT<ProcessCrashInformation>
21
+{
22
+ ProcessCrashInformation(const WslcSessionCrashDumpInfo* info);
23
+
24
+ hstring DumpPath() const;
25
+ hstring ProcessName() const;
26
+ uint32_t Pid() const;
27
+ uint32_t Signal() const;
28
+ winrt::Windows::Foundation::DateTime Timestamp() const;
29
+
30
+private:
31
+ hstring m_dumpPath;
32
+ hstring m_processName;
33
+ uint32_t m_pid{};
34
+ uint32_t m_signal{};
35
+ winrt::Windows::Foundation::DateTime m_timestamp{};
36
+};
37
+} // namespace winrt::Microsoft::WSL::Containers::implementation
38
+
39
+DEFINE_TYPE_HELPERS(ProcessCrashInformation);
src/windows/WslcSDK/winrt/Session.cpp
+27
@@ -14,6 +14,7 @@ Abstract:
14
15
#include "precomp.h"
16
#include "Session.h"
17
+#include "ProcessCrashInformation.h"
18
#include "SessionSettings.h"
19
#include "Microsoft.WSL.Containers.Session.g.cpp"
20
@@ -62,6 +63,9 @@ void Session::Start()
63
m_terminationWait.reset(CreateThreadpoolWait(&Session::OnTerminated, this, nullptr));
64
THROW_LAST_ERROR_IF_NULL(m_terminationWait);
65
SetThreadpoolWait(m_terminationWait.get(), m_terminationEvent.get(), nullptr);
66
+
67
+ hr = WslcRegisterSessionCrashDumpCallback(m_session.get(), &Session::OnCrashDump, this, &m_crashDumpSubscription, errorMessage.put());
68
+ THROW_MSG_IF_FAILED(hr, errorMessage);
69
}
70
71
void Session::EnsureStarted() const
@@ -281,6 +285,16 @@ void Session::Terminated(winrt::event_token const& token) noexcept
285
m_terminatedEvent.remove(token);
286
}
287
288
+winrt::event_token Session::ProcessCrashed(winrt::Microsoft::WSL::Containers::ProcessCrashHandler const& handler)
289
+{
290
+ return m_crashDumpEvent.add(handler);
291
+}
292
+
293
+void Session::ProcessCrashed(winrt::event_token const& token) noexcept
294
+{
295
+ m_crashDumpEvent.remove(token);
296
+}
297
+
298
IVectorView<winrt::Microsoft::WSL::Containers::ImageInfo> Session::Images()
299
{
300
EnsureStarted();
@@ -318,4 +332,17 @@ void CALLBACK Session::OnTerminated(PTP_CALLBACK_INSTANCE /* instance */, PVOID
332
CATCH_LOG();
333
}
334
335
+void CALLBACK Session::OnCrashDump(const WslcSessionCrashDumpInfo* info, PVOID context) noexcept
336
+{
337
+ try
338
+ {
339
+ auto session = static_cast<Session*>(context);
340
+
341
+ auto information = winrt::make_self<implementation::ProcessCrashInformation>(info);
342
+
343
+ session->m_crashDumpEvent(*information);
344
+ }
345
+ CATCH_LOG();
346
+}
347
+
348
} // namespace winrt::Microsoft::WSL::Containers::implementation
src/windows/WslcSDK/winrt/Session.h
+6
@@ -38,6 +38,8 @@ struct Session : SessionT<Session>
38
winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::WSL::Containers::ImageInfo> Images();
39
winrt::event_token Terminated(winrt::Microsoft::WSL::Containers::SessionTerminationHandler const& handler);
40
void Terminated(winrt::event_token const& token) noexcept;
41
+ winrt::event_token ProcessCrashed(winrt::Microsoft::WSL::Containers::ProcessCrashHandler const& handler);
42
+ void ProcessCrashed(winrt::event_token const& token) noexcept;
43
44
WslcSession ToHandle();
45
@@ -47,13 +49,17 @@ private:
49
50
// Threadpool callback that raises the Terminated event once the session's termination handle is signaled.
51
static void CALLBACK OnTerminated(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WAIT wait, TP_WAIT_RESULT waitResult) noexcept;
52
+ static void CALLBACK OnCrashDump(const WslcSessionCrashDumpInfo* info, PVOID context) noexcept;
53
54
winrt::event<winrt::Microsoft::WSL::Containers::SessionTerminationHandler> m_terminatedEvent;
55
+ winrt::event<winrt::Microsoft::WSL::Containers::ProcessCrashHandler> m_crashDumpEvent;
56
wil::unique_any<WslcSession, decltype(&WslcReleaseSession), &WslcReleaseSession> m_session{nullptr};
57
58
// Bridges the one-off termination event surfaced by the SDK to the WinRT Terminated event.
59
wil::unique_handle m_terminationEvent;
60
wil::unique_threadpool_wait m_terminationWait;
61
+
62
+ wil::unique_any<WslcCrashDumpSubscription, decltype(&WslcReleaseCrashDumpSubscription), &WslcReleaseCrashDumpSubscription> m_crashDumpSubscription;
63
};
64
} // namespace winrt::Microsoft::WSL::Containers::implementation
65
namespace winrt::Microsoft::WSL::Containers::factory_implementation {
src/windows/WslcSDK/winrt/wslcsdk.idl
+33
-1
@@ -28,7 +28,18 @@ namespace Microsoft.WSL.Containers
28
Crashed = 2,
29
};
30
31
- delegate void SessionTerminationHandler(SessionTerminationReason reason);
31
+ delegate void SessionTerminationHandler(SessionTerminationReason reason);
32
+
33
+ runtimeclass ProcessCrashInformation
34
+ {
35
+ String DumpPath { get; };
36
+ String ProcessName { get; };
37
+ UInt32 Pid { get; };
38
+ UInt32 Signal { get; };
39
+ Windows.Foundation.DateTime Timestamp { get; };
40
+ };
41
+
42
+ delegate void ProcessCrashHandler(ProcessCrashInformation information);
43
44
runtimeclass SessionSettings
45
{
@@ -70,6 +81,7 @@ namespace Microsoft.WSL.Containers
81
IVectorView<ImageInfo> Images { get; };
82
83
event SessionTerminationHandler Terminated;
84
+ event ProcessCrashHandler ProcessCrashed;
85
};
86
87
@@ -238,6 +250,7 @@ namespace Microsoft.WSL.Containers
250
None = 0,
251
VirtualMachinePlatform = 1,
252
WslPackage = 2,
253
+ SdkNeedsUpdate = 4,
254
};
255
256
runtimeclass ServiceVersion
@@ -331,5 +344,24 @@ namespace Microsoft.WSL.Containers
344
Windows.Storage.Streams.IBuffer Sha256 { get; };
345
UInt64 SizeBytes { get; };
346
Windows.Foundation.DateTime CreatedTimestamp { get; };
347
+ };
348
+
349
+ // Ensure wslcsdk.h and wslc.idl are also updated.
350
+ enum Error
351
+ {
352
+ ImageNotFound = 0x80040601,
353
+ ContainerPrefixAmbiguous = 0x80040602,
354
+ ContainerNotFound = 0x80040603,
355
+ VolumeNotFound = 0x80040604,
356
+ ContainerNotRunning = 0x80040605,
357
+ ContainerIsRunning = 0x80040606,
358
+ SessionReserved = 0x80040607,
359
+ InvalidSessionName = 0x80040608,
360
+ NetworkNotFound = 0x80040609,
361
+ WindowsUpdateSearchFailed = 0x8004060A,
362
+ SdkUpdateNeeded = 0x8004060B,
363
+ ContainerDisabled = 0x8004060C,
364
+ RegistryBlockedByPolicy = 0x8004060D,
365
+ VolumeNotAvailable = 0x8004060E,
366
};
367
}
src/windows/WslcSDK/wslcsdk.cpp
+19
-2
@@ -18,6 +18,7 @@ Abstract:
18
#include "Defaults.h"
19
#include "ProgressCallback.h"
20
#include "CrashDumpCallback.h"
21
+#include "install.h"
22
#include "Localization.h"
23
#include "WslInstall.h"
24
#include "wslutil.h"
@@ -1676,10 +1677,10 @@ try
1677
THROW_HR_IF(runtimeResult, runtimeResult != REGDB_E_CLASSNOTREG && runtimeResult != WSLC_E_SDK_UPDATE_NEEDED);
1678
1679
// Installing these components requires elevation.
1679
- auto token = wil::open_current_access_token();
1680
RETURN_HR_IF(
1681
HRESULT_FROM_WIN32(ERROR_ELEVATION_REQUIRED),
1682
- !wsl::windows::common::security::IsTokenElevated(token.get()) && !wsl::windows::common::security::IsTokenLocalSystem(token.get()));
1682
+ !wsl::windows::common::security::IsTokenElevated(GetCurrentThreadEffectiveToken()) &&
1683
+ !wsl::windows::common::security::IsTokenLocalSystem(nullptr));
1684
1685
if (needsVirtualMachine)
1686
{
@@ -1718,6 +1719,22 @@ try
1719
1720
wsl::windows::common::WindowsUpdateContext wuContext;
1721
wuContext.RunUpdateFlow(true, callback);
1722
+
1723
+ // Because we do a forced install here, we expect an update.
1724
+ if (wuContext.GetUpdateCount() == 0)
1725
+ {
1726
+ // During the preview period, the package may not be published yet, so fall back to getting it from GH.
1727
+ // When moving to GA, change this to a hard error to indicate a service configuration issue.
1728
+ if (callback)
1729
+ {
1730
+ callback(0);
1731
+ }
1732
+ wsl::windows::common::install::UpdatePackage(true, false, false);
1733
+ if (callback)
1734
+ {
1735
+ callback(100);
1736
+ }
1737
+ }
1738
}
1739
1740
return result;
src/windows/WslcSDK/wslcsdk.h
+1
@@ -26,6 +26,7 @@ Abstract:
26
EXTERN_C_START
27
28
// WSLC specific error codes
29
+// Ensure wslc.idl and wslcsdk.idl are also updated.
30
#define WSLC_E_BASE (0x0600)
31
#define WSLC_E_IMAGE_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 1) /* 0x80040601 */
32
#define WSLC_E_CONTAINER_PREFIX_AMBIGUOUS MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 2) /* 0x80040602 */
src/windows/common/WindowsUpdateIntegration.cpp
+30
-5
@@ -166,7 +166,12 @@ void WindowsUpdateContext::EnsureProductRegistryEntry() const
166
size_t WindowsUpdateContext::SearchForUpdates()
167
{
168
TraceLoggingWriteTagged(
169
- *m_activity, "SearchForUpdates", TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
169
+ *m_activity,
170
+ "SearchForUpdates",
171
+ TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
172
+ TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
173
+ TraceLoggingWideString(m_product.c_str(), "product"));
174
+
175
THROW_IF_FAILED(m_session->CreateUpdateSearcher(&m_searcher));
176
177
std::wstring queryString = std::format(L"Product='{}'", m_product);
@@ -217,7 +222,17 @@ size_t WindowsUpdateContext::SearchForUpdates()
222
}
223
224
THROW_IF_FAILED(searchResult->get_Updates(&m_updates));
220
- return GetUpdateCount();
225
+ size_t result = GetUpdateCount();
226
+
227
+ TraceLoggingWriteTagged(
228
+ *m_activity,
229
+ "SearchForUpdatesResult",
230
+ TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
231
+ TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
232
+ TraceLoggingInt32(resultCode, "OperationResultCode"),
233
+ TraceLoggingLong(static_cast<LONG>(result), "updateCount"));
234
+
235
+ return result;
236
}
237
238
size_t WindowsUpdateContext::GetUpdateCount() const
@@ -232,8 +247,6 @@ size_t WindowsUpdateContext::GetUpdateCount() const
247
248
void WindowsUpdateContext::DownloadUpdates(const std::function<void(uint32_t)>& progress) const
249
{
235
- TraceLoggingWriteTagged(
236
- *m_activity, "DownloadUpdates", TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
250
// Collect all of the updates that are not currently downloaded
251
wil::com_ptr<IUpdateCollection> toDownload = m_factory->CreateUpdateCollection();
252
@@ -252,6 +265,14 @@ void WindowsUpdateContext::DownloadUpdates(const std::function<void(uint32_t)>&
265
// All updates are already downloaded — nothing to do.
266
LONG toDownloadCount{};
267
THROW_IF_FAILED(toDownload->get_Count(&toDownloadCount));
268
+
269
+ TraceLoggingWriteTagged(
270
+ *m_activity,
271
+ "DownloadUpdates",
272
+ TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
273
+ TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
274
+ TraceLoggingLong(toDownloadCount, "downloadCount"));
275
+
276
if (toDownloadCount == 0)
277
{
278
if (progress)
@@ -318,7 +339,11 @@ void WindowsUpdateContext::InstallUpdates(const std::function<void(uint32_t)>& p
339
void WindowsUpdateContext::RunUpdateFlow(bool forceInstall, const std::function<void(uint32_t)>& progress)
340
{
341
TraceLoggingWriteTagged(
321
- *m_activity, "RunUpdateFlow", TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
342
+ *m_activity,
343
+ "RunUpdateFlow",
344
+ TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
345
+ TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
346
+ TraceLoggingBool(forceInstall, "forceInstall"));
347
348
static_assert(
349
DownloadProgressPercent + InstallProgressPercent == 100, "Download and Install progress values must add up to 100.");
src/windows/common/install.cpp
+39
-20
@@ -77,9 +77,9 @@ bool PromptForKeyPressWithTimeout()
77
return waitResult == std::future_status::ready && future.get();
78
}
79
80
-int UpdatePackageImpl(bool preRelease, bool repair)
80
+int UpdatePackageImpl(bool preRelease, bool repair, bool callerOwnsProcess)
81
{
82
- if (!repair)
82
+ if (!repair && callerOwnsProcess)
83
{
84
PrintMessage(Localization::MessageCheckingForUpdates());
85
}
@@ -88,11 +88,17 @@ int UpdatePackageImpl(bool preRelease, bool repair)
88
89
if (!repair && ParseWslPackageVersion(version) <= wsl::shared::PackageVersion)
90
{
91
- PrintMessage(Localization::MessageUpdateNotNeeded());
91
+ if (callerOwnsProcess)
92
+ {
93
+ PrintMessage(Localization::MessageUpdateNotNeeded());
94
+ }
95
return 0;
96
}
97
95
- PrintMessage(Localization::MessageUpdatingToVersion(version.c_str()));
98
+ if (callerOwnsProcess)
99
+ {
100
+ PrintMessage(Localization::MessageUpdatingToVersion(version.c_str()));
101
+ }
102
103
const bool msiInstall = wsl::shared::string::EndsWith<wchar_t>(release.name, L".msi");
104
const auto downloadPath = DownloadFile(release.url, release.name);
@@ -102,11 +108,14 @@ int UpdatePackageImpl(bool preRelease, bool repair)
108
auto clearLogs =
109
wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&logFile]() { LOG_IF_WIN32_BOOL_FALSE(DeleteFile(logFile.c_str())); });
110
105
- const auto exitCode = UpgradeViaMsi(downloadPath.c_str(), L"", logFile.c_str(), &MsiMessageCallback);
111
+ const auto exitCode = UpgradeViaMsi(downloadPath.c_str(), L"", logFile.c_str(), callerOwnsProcess ? &MsiMessageCallback : nullptr);
112
113
if (exitCode == ERROR_SUCCESS_REBOOT_REQUIRED)
114
{
109
- PrintSystemError(ERROR_SUCCESS_REBOOT_REQUIRED);
115
+ if (callerOwnsProcess)
116
+ {
117
+ PrintSystemError(ERROR_SUCCESS_REBOOT_REQUIRED);
118
+ }
119
}
120
else if (exitCode != 0)
121
{
@@ -131,7 +140,7 @@ int UpdatePackageImpl(bool preRelease, bool repair)
140
141
THROW_IF_FAILED(result.get().ExtendedErrorCode());
142
134
- // Note: If the installation is successful, this process is expected to receive and Ctrl-C and exit
143
+ // Note: If the installation is successful, this process is expected to receive a Ctrl-C and exit
144
}
145
146
return 0;
@@ -333,24 +342,34 @@ void wsl::windows::common::install::MsiMessageCallback(INSTALLMESSAGE type, LPCW
342
}
343
}
344
336
-int wsl::windows::common::install::UpdatePackage(bool PreRelease, bool Repair)
345
+int wsl::windows::common::install::UpdatePackage(bool PreRelease, bool Repair, bool CallerOwnsProcess)
346
{
338
- // Register a console control handler so "^C" is not printed when the app platform terminates the process.
339
- THROW_IF_WIN32_BOOL_FALSE(SetConsoleCtrlHandler(
340
- [](DWORD ctrlType) {
341
- if (ctrlType == CTRL_C_EVENT)
342
- {
343
- ExitProcess(0);
344
- }
345
- return FALSE;
346
- },
347
- TRUE));
347
+ bool clearHandler = false;
348
+ auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {
349
+ if (clearHandler)
350
+ {
351
+ SetConsoleCtrlHandler(nullptr, FALSE);
352
+ }
353
+ });
354
349
- auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [] { SetConsoleCtrlHandler(nullptr, FALSE); });
355
+ if (CallerOwnsProcess)
356
+ {
357
+ // Register a console control handler so "^C" is not printed when the app platform terminates the process.
358
+ THROW_IF_WIN32_BOOL_FALSE(SetConsoleCtrlHandler(
359
+ [](DWORD ctrlType) {
360
+ if (ctrlType == CTRL_C_EVENT)
361
+ {
362
+ ExitProcess(0);
363
+ }
364
+ return FALSE;
365
+ },
366
+ TRUE));
367
+ clearHandler = true;
368
+ }
369
370
try
371
{
353
- return UpdatePackageImpl(PreRelease, Repair);
372
+ return UpdatePackageImpl(PreRelease, Repair, CallerOwnsProcess);
373
}
374
catch (...)
375
{
src/windows/common/install.h
+2
-1
@@ -23,7 +23,8 @@ void MsiMessageCallback(INSTALLMESSAGE type, LPCWSTR message);
23
24
wil::unique_hfile ValidateFileSignature(LPCWSTR Path);
25
26
-int UpdatePackage(bool PreRelease, bool Repair);
26
+// Setting CallerOwnsProcess to false will prevent this function from making process wide changes or printing output.
27
+int UpdatePackage(bool PreRelease, bool Repair, bool CallerOwnsProcess = true);
28
29
UINT UpgradeViaMsi(_In_ LPCWSTR PackageLocation, _In_opt_ LPCWSTR ExtraArgs, _In_opt_ LPCWSTR LogFile, _In_ const std::function<void(INSTALLMESSAGE, LPCWSTR)>& callback);
30
src/windows/service/inc/wslc.idl
+2
-1
@@ -710,7 +710,8 @@ interface IWSLCSessionManager : IUnknown
710
HRESULT OpenSession([in] ULONG Id, [out] IWSLCSession** Session);
711
HRESULT OpenSessionByName([in, unique] LPCWSTR DisplayName, [out] IWSLCSession** Session);
712
}
713
-
713
+
714
+// Ensure wslcsdk.h and wslcsdk.idl are also updated.
715
cpp_quote("#define WSLC_E_BASE (0x0600)")
716
cpp_quote("#define WSLC_E_IMAGE_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 1) /* 0x80040601 */")
717
cpp_quote("#define WSLC_E_CONTAINER_PREFIX_AMBIGUOUS MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 2) /* 0x80040602 */")
test/windows/WslcSdkWinRTTests.cpp
+100
@@ -22,6 +22,7 @@ Abstract:
22
23
#include "winrt/Session.h"
24
#include "winrt/Helpers.h"
25
+#include "winrt/ProcessCrashInformation.h"
26
27
#include <winrt/Microsoft.WSL.Containers.h>
28
#include <winrt/Windows.Foundation.h>
@@ -307,6 +308,105 @@ class WslcSdkWinRtTests
308
VERIFY_ARE_EQUAL(future.get(), WSLCSDK::SessionTerminationReason::Shutdown);
309
}
310
311
+ WSLC_TEST_METHOD(ProcessCrashedEvent)
312
+ {
313
+ // Start a long-running container so we can exec non-PID-1 processes into it.
314
+ // The crashing process must NOT be the container's init process: Linux silently
315
+ // drops kill()-sent signals with default disposition when targeting PID 1 in a
316
+ // PID namespace, so no core dump would be generated if we crash the init process.
317
+ auto initProcSettings = WSLCSDK::ProcessSettings();
318
+ initProcSettings.CmdLine(winrt::single_threaded_vector<winrt::hstring>({L"/bin/sleep", L"99"}));
319
+
320
+ auto containerSettings = WSLCSDK::ContainerSettings(L"debian:latest");
321
+ containerSettings.InitProcess(initProcSettings);
322
+
323
+ auto container = m_defaultSession.CreateContainer(containerSettings);
324
+ auto cleanup = DELETE_CONTAINER_ON_SCOPE_EXIT(container);
325
+ container.Start();
326
+
327
+ // Positive: A crashing exec process must fire the ProcessCrashed event with correctly populated info.
328
+ {
329
+ std::promise<WSLCSDK::ProcessCrashInformation> promise;
330
+
331
+ auto revoker = m_defaultSession.ProcessCrashed(winrt::auto_revoke, [&](WSLCSDK::ProcessCrashInformation info) {
332
+ // Guard against multiple firings from concurrently running tests.
333
+ try
334
+ {
335
+ promise.set_value(info);
336
+ }
337
+ catch (...)
338
+ {
339
+ }
340
+ });
341
+
342
+ auto execSettings = WSLCSDK::ProcessSettings();
343
+ execSettings.CmdLine(winrt::single_threaded_vector<winrt::hstring>({L"/bin/sh", L"-c", L"kill -SEGV $$"}));
344
+
345
+ const auto beforeCrash = winrt::clock::now();
346
+ StartProcessAndWaitForExit(container.CreateProcess(execSettings), 30s);
347
+
348
+ auto future = promise.get_future();
349
+ VERIFY_ARE_EQUAL(future.wait_for(60s), std::future_status::ready);
350
+ const auto afterCrash = winrt::clock::now();
351
+
352
+ auto info = future.get();
353
+
354
+ VERIFY_IS_FALSE(info.DumpPath().empty());
355
+ VERIFY_IS_TRUE(std::filesystem::exists(info.DumpPath().c_str()));
356
+ VERIFY_IS_TRUE(std::wstring_view(info.ProcessName()).find(L"sh") != std::wstring_view::npos);
357
+ VERIFY_IS_GREATER_THAN(info.Pid(), 0u);
358
+ VERIFY_ARE_EQUAL(info.Signal(), 11u); // SIGSEGV = 11
359
+
360
+ // Crash timestamps are second-granularity; allow some slack around the measured window.
361
+ VERIFY_IS_TRUE(info.Timestamp() >= beforeCrash - 1s);
362
+ VERIFY_IS_TRUE(info.Timestamp() <= afterCrash + 1s);
363
+ }
364
+
365
+ // Negative: After revoking the subscription token, the handler must no longer fire.
366
+ {
367
+ std::atomic<int> callCount{0};
368
+ {
369
+ auto revoker =
370
+ m_defaultSession.ProcessCrashed(winrt::auto_revoke, [&](WSLCSDK::ProcessCrashInformation) { ++callCount; });
371
+ // revoker goes out of scope here — handler is unsubscribed before any crash is triggered.
372
+ }
373
+
374
+ auto execSettings = WSLCSDK::ProcessSettings();
375
+ execSettings.CmdLine(winrt::single_threaded_vector<winrt::hstring>({L"/bin/sh", L"-c", L"kill -SEGV $$"}));
376
+
377
+ StartProcessAndWaitForExit(container.CreateProcess(execSettings), 60s);
378
+
379
+ // Allow the event system time to dispatch any pending callbacks.
380
+ Sleep(1000);
381
+ VERIFY_ARE_EQUAL(callCount.load(), 0);
382
+ }
383
+ }
384
+
385
+ WSLC_TEST_METHOD(ProcessCrashInformationProperties)
386
+ {
387
+ // Verify that ProcessCrashInformation correctly maps all fields from WslcSessionCrashDumpInfo.
388
+ constexpr PCWSTR c_dumpPath = L"C:\\test\\dump.dmp";
389
+ constexpr PCSTR c_processName = "test-process";
390
+ constexpr uint32_t c_pid = 42;
391
+ constexpr uint32_t c_signal = 11; // SIGSEGV
392
+ constexpr uint64_t c_timestamp = 1700000000; // 2023-11-14 22:13:20 UTC
393
+
394
+ WslcSessionCrashDumpInfo info{};
395
+ info.dumpPath = c_dumpPath;
396
+ info.processName = c_processName;
397
+ info.pid = c_pid;
398
+ info.signal = c_signal;
399
+ info.timestamp = c_timestamp;
400
+
401
+ auto impl = winrt::make_self<WSLCSDK::implementation::ProcessCrashInformation>(&info);
402
+
403
+ VERIFY_ARE_EQUAL(impl->DumpPath(), winrt::hstring(c_dumpPath));
404
+ VERIFY_ARE_EQUAL(impl->ProcessName(), winrt::to_hstring(c_processName));
405
+ VERIFY_ARE_EQUAL(impl->Pid(), c_pid);
406
+ VERIFY_ARE_EQUAL(impl->Signal(), c_signal);
407
+ VERIFY_ARE_EQUAL(winrt::clock::to_time_t(impl->Timestamp()), static_cast<time_t>(c_timestamp));
408
+ }
409
+
410
// -----------------------------------------------------------------------
411
// Image tests
412
// -----------------------------------------------------------------------