wslc service com api support for emit warning message as optional com callback (#40558)
yao-msft committed
Jun 3, 2026 at 11:21 UTC
bd4e436239ead160713dd3e861b95a3158bf8838
27 files changed
+784
-253
localization/strings/en-US/Resources.resw
+34
@@ -3113,4 +3113,38 @@ On first run, creates the file with all settings commented out at their defaults
3113
<value>Warning: Settings file at {} is empty or has invalid structure. Expected a YAML mapping.</value>
3114
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
3115
</data>
3116
+ <data name="MessageWslcFailedToRecoverContainer" xml:space="preserve">
3117
+ <value>Failed to recover container '{}'</value>
3118
+ <comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
3119
+ </data>
3120
+ <data name="MessageWslcFailedToRecoverNetwork" xml:space="preserve">
3121
+ <value>Failed to recover network '{}'</value>
3122
+ <comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
3123
+ </data>
3124
+ <data name="MessageWslcFailedToRecoverVolume" xml:space="preserve">
3125
+ <value>Failed to recover volume '{}'</value>
3126
+ <comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
3127
+ </data>
3128
+ <data name="MessageWslcSwapInitFailed" xml:space="preserve">
3129
+ <value>Failed to initialize swap</value>
3130
+ </data>
3131
+ <data name="MessageWslcContainerTimestampRecoveryFailed" xml:space="preserve">
3132
+ <value>Failed to restore timestamp for container '{}'</value>
3133
+ <comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
3134
+ </data>
3135
+ <data name="MessageWslcImportProgressParseFailed" xml:space="preserve">
3136
+ <value>Failed to parse image import progress</value>
3137
+ </data>
3138
+ <data name="MessageWslcVolumeUnmountFailed" xml:space="preserve">
3139
+ <value>Failed to unmount volume '{}': {}</value>
3140
+ <comment>{FixedPlaceholder="{}"}{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
3141
+ </data>
3142
+ <data name="MessageWslcContainerStopAfterPluginRejectionFailed" xml:space="preserve">
3143
+ <value>Failed to stop container '{}' after plugin rejection</value>
3144
+ <comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
3145
+ </data>
3146
+ <data name="MessageWslcVolumeReleaseFailed" xml:space="preserve">
3147
+ <value>Failed to release host resources for volume '{}'</value>
3148
+ <comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
3149
+ </data>
3150
</root>
msipackage/package.wix.in
+8
@@ -322,6 +322,14 @@
322
</RegistryKey>
323
</RegistryKey>
324
325
+ <!-- IWarningCallback-->
326
+ <RegistryKey Root="HKCR" Key="Interface\{8153ED5D-8ABB-408B-ADBE-C0F3B13E07C3}">
327
+ <RegistryValue Value="IWarningCallback" Type="string" />
328
+ <RegistryKey Key="ProxyStubClsid32">
329
+ <RegistryValue Value="{4EA0C6DD-E9FF-48E7-994E-13A31D10DC60}" Type="string" />
330
+ </RegistryKey>
331
+ </RegistryKey>
332
+
333
<!-- IWSLCSession-->
334
<RegistryKey Root="HKCR" Key="Interface\{EF0661E4-6364-40EA-B433-E2FDF11F3519}">
335
<RegistryValue Value="IWSLCSession" Type="string" />
src/windows/WslcSDK/wslcsdk.cpp
+9
-7
@@ -444,7 +444,8 @@ try
444
WI_SetFlag(runtimeSettings.FeatureFlags, WslcFeatureFlagsVirtioFs);
445
WI_SetFlag(runtimeSettings.FeatureFlags, WslcFeatureFlagsDnsTunneling);
446
447
- if (SUCCEEDED(errorInfoWrapper.CaptureResult(sessionManager->CreateSession(&runtimeSettings, WSLCSessionFlagsNone, &result->session))))
447
+ if (SUCCEEDED(errorInfoWrapper.CaptureResult(
448
+ sessionManager->CreateSession(&runtimeSettings, WSLCSessionFlagsNone, nullptr, &result->session))))
449
{
450
wsl::windows::common::security::ConfigureForCOMImpersonation(result->session.get());
451
*session = reinterpret_cast<WslcSession>(result.release());
@@ -831,7 +832,7 @@ try
832
// containerOptions.StopSignal;
833
// containerOptions.ShmSize;
834
834
- if (SUCCEEDED(errorInfoWrapper.CaptureResult(internalSession->session->CreateContainer(&containerOptions, &result->container))))
835
+ if (SUCCEEDED(errorInfoWrapper.CaptureResult(internalSession->session->CreateContainer(&containerOptions, nullptr, &result->container))))
836
{
837
wsl::windows::common::security::ConfigureForCOMImpersonation(result->container.get());
838
@@ -859,7 +860,7 @@ try
860
// TODO: Consider if we should just override flags when callbacks were provided instead.
861
RETURN_HR_IF(E_INVALIDARG, WI_IsFlagClear(flags, WSLC_CONTAINER_START_FLAG_ATTACH) && hasIOCallback);
862
862
- if (SUCCEEDED(errorInfoWrapper.CaptureResult(internalType->container->Start(ConvertFlags(flags), nullptr))))
863
+ if (SUCCEEDED(errorInfoWrapper.CaptureResult(internalType->container->Start(ConvertFlags(flags), nullptr, nullptr))))
864
{
865
if (hasIOCallback)
866
{
@@ -1331,7 +1332,7 @@ try
1332
1333
auto progressCallback = ProgressCallback::CreateIf(options);
1334
1334
- return errorInfoWrapper.CaptureResult(internalType->session->PullImage(options->uri, options->registryAuth, progressCallback.get()));
1335
+ return errorInfoWrapper.CaptureResult(internalType->session->PullImage(options->uri, options->registryAuth, progressCallback.get(), nullptr));
1336
}
1337
CATCH_RETURN();
1338
@@ -1341,7 +1342,7 @@ static HRESULT WslcImportSessionImageImpl(
1342
auto progressCallback = ProgressCallback::CreateIf(options);
1343
1344
return errorInfoWrapper.CaptureResult(internalSession->session->ImportImage(
1344
- ToCOMInputHandle(imageFile.Handle()), imageName, progressCallback.get(), imageFile.Length()));
1345
+ ToCOMInputHandle(imageFile.Handle()), imageName, progressCallback.get(), imageFile.Length(), nullptr));
1346
}
1347
1348
STDAPI WslcImportSessionImage(
@@ -1379,7 +1380,7 @@ static HRESULT WslcLoadSessionImageImpl(
1380
auto progressCallback = ProgressCallback::CreateIf(options);
1381
1382
return errorInfoWrapper.CaptureResult(
1382
- internalSession->session->LoadImage(ToCOMInputHandle(imageFile.Handle()), progressCallback.get(), imageFile.Length()));
1383
+ internalSession->session->LoadImage(ToCOMInputHandle(imageFile.Handle()), progressCallback.get(), imageFile.Length(), nullptr));
1384
}
1385
1386
STDAPI WslcLoadSessionImage(
@@ -1458,7 +1459,8 @@ try
1459
1460
auto progressCallback = ProgressCallback::CreateIf(options);
1461
1461
- return errorInfoWrapper.CaptureResult(internalType->session->PushImage(options->image, options->registryAuth, progressCallback.get()));
1462
+ return errorInfoWrapper.CaptureResult(
1463
+ internalType->session->PushImage(options->image, options->registryAuth, progressCallback.get(), nullptr));
1464
}
1465
CATCH_RETURN();
1466
src/windows/common/WSLCContainerLauncher.cpp
+10
-9
@@ -250,20 +250,21 @@ void wsl::windows::common::WSLCContainerLauncher::AddAdditionalNetwork(const std
250
m_additionalNetworks.push_back(Name);
251
}
252
253
-std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::LaunchNoThrow(IWSLCSession& Session, WSLCContainerStartFlags Flags)
253
+std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::LaunchNoThrow(
254
+ IWSLCSession& Session, WSLCContainerStartFlags Flags, IWarningCallback* WarningCallback)
255
{
255
- auto [result, container] = CreateNoThrow(Session);
256
+ auto [result, container] = CreateNoThrow(Session, WarningCallback);
257
if (FAILED(result))
258
{
259
return std::make_pair(result, std::optional<RunningWSLCContainer>{});
260
}
261
261
- result = container.value().Get().Start(Flags, nullptr);
262
+ result = container.value().Get().Start(Flags, nullptr, WarningCallback);
263
264
return std::make_pair(result, std::move(container));
265
}
266
266
-std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::CreateNoThrow(IWSLCSession& Session)
267
+std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::CreateNoThrow(IWSLCSession& Session, IWarningCallback* WarningCallback)
268
{
269
WSLCContainerOptions options{};
270
options.Image = m_image.c_str();
@@ -373,7 +374,7 @@ std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::C
374
375
// TODO: Support volumes, ports, flags, container networking mode, etc.
376
wil::com_ptr<IWSLCContainer> container;
376
- auto result = Session.CreateContainer(&options, &container);
377
+ auto result = Session.CreateContainer(&options, WarningCallback, &container);
378
if (FAILED(result))
379
{
380
return std::pair<HRESULT, std::optional<RunningWSLCContainer>>(result, std::optional<RunningWSLCContainer>{});
@@ -382,17 +383,17 @@ std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::C
383
return std::make_pair(S_OK, std::move(RunningWSLCContainer{std::move(container), m_flags}));
384
}
385
385
-RunningWSLCContainer WSLCContainerLauncher::Create(IWSLCSession& Session)
386
+RunningWSLCContainer WSLCContainerLauncher::Create(IWSLCSession& Session, IWarningCallback* WarningCallback)
387
{
387
- auto [result, container] = CreateNoThrow(Session);
388
+ auto [result, container] = CreateNoThrow(Session, WarningCallback);
389
THROW_IF_FAILED(result);
390
391
return std::move(container.value());
392
}
393
393
-RunningWSLCContainer WSLCContainerLauncher::Launch(IWSLCSession& Session, WSLCContainerStartFlags Flags)
394
+RunningWSLCContainer WSLCContainerLauncher::Launch(IWSLCSession& Session, WSLCContainerStartFlags Flags, IWarningCallback* WarningCallback)
395
{
395
- auto [result, container] = LaunchNoThrow(Session, Flags);
396
+ auto [result, container] = LaunchNoThrow(Session, Flags, WarningCallback);
397
THROW_IF_FAILED(result);
398
399
return std::move(container.value());
src/windows/common/WSLCContainerLauncher.h
+5
-4
@@ -64,11 +64,12 @@ public:
64
void AddTmpfs(const std::string& ContainerPath, const std::string& Options);
65
void AddAdditionalNetwork(const std::string& Name);
66
67
- std::pair<HRESULT, std::optional<RunningWSLCContainer>> CreateNoThrow(IWSLCSession& Session);
68
- RunningWSLCContainer Create(IWSLCSession& Session);
67
+ std::pair<HRESULT, std::optional<RunningWSLCContainer>> CreateNoThrow(IWSLCSession& Session, IWarningCallback* WarningCallback = nullptr);
68
+ RunningWSLCContainer Create(IWSLCSession& Session, IWarningCallback* WarningCallback = nullptr);
69
70
- RunningWSLCContainer Launch(IWSLCSession& Session, WSLCContainerStartFlags Flags = WSLCContainerStartFlagsAttach);
71
- std::pair<HRESULT, std::optional<RunningWSLCContainer>> LaunchNoThrow(IWSLCSession& Session, WSLCContainerStartFlags Flags = WSLCContainerStartFlagsAttach);
70
+ RunningWSLCContainer Launch(IWSLCSession& Session, WSLCContainerStartFlags Flags = WSLCContainerStartFlagsAttach, IWarningCallback* WarningCallback = nullptr);
71
+ std::pair<HRESULT, std::optional<RunningWSLCContainer>> LaunchNoThrow(
72
+ IWSLCSession& Session, WSLCContainerStartFlags Flags = WSLCContainerStartFlagsAttach, IWarningCallback* WarningCallback = nullptr);
73
74
void SetName(std::string&& Name);
75
void SetEntrypoint(std::vector<std::string>&& entrypoint);
src/windows/inc/docker_schema.h
+2
-1
@@ -38,9 +38,10 @@ struct ErrorResponse
38
struct ImageLoadResult
39
{
40
std::optional<std::string> stream;
41
+ std::optional<std::string> status;
42
std::optional<ErrorResponse> errorDetail;
43
43
- NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ImageLoadResult, stream, errorDetail);
44
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ImageLoadResult, stream, status, errorDetail);
45
};
46
47
struct EmptyRequest
src/windows/service/exe/WSLCSessionManager.cpp
+12
-9
@@ -164,7 +164,8 @@ try
164
}
165
CATCH_LOG()
166
167
-void WSLCSessionManagerImpl::CreateSession(const WSLCSessionSettings* Settings, WSLCSessionFlags Flags, IWSLCSession** WslcSession)
167
+void WSLCSessionManagerImpl::CreateSession(
168
+ _In_ const WSLCSessionSettings* Settings, _In_ WSLCSessionFlags Flags, _In_opt_ IWarningCallback* WarningCallback, _Out_ IWSLCSession** WslcSession)
169
{
170
auto tokenInfo = GetCallingProcessTokenInfo();
171
const auto callerToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);
@@ -269,10 +270,10 @@ void WSLCSessionManagerImpl::CreateSession(const WSLCSessionSettings* Settings,
270
auto factory = wslutil::CreateComServerAsUser<IWSLCSessionFactory>(__uuidof(WSLCSessionFactory), userToken.get());
271
AddSessionProcessToJobObject(factory.get());
272
272
- auto sessionSettings = CreateSessionSettings(sessionId, creatorPid, Settings, resolvedDisplayName.c_str());
273
+ const auto sessionSettings = CreateSessionSettings(sessionId, creatorPid, Settings, resolvedDisplayName.c_str());
274
wil::com_ptr<IWSLCSession> session;
275
wil::com_ptr<IWSLCSessionReference> serviceRef;
275
- THROW_IF_FAILED(factory->CreateSession(&sessionSettings, vm.Get(), notifier.Get(), &session, &serviceRef));
276
+ THROW_IF_FAILED(factory->CreateSession(&sessionSettings, vm.Get(), notifier.Get(), WarningCallback, &session, &serviceRef));
277
278
// Track the session via its service ref, along with metadata and security info.
279
m_sessions.push_back(SessionEntry{
@@ -402,14 +403,15 @@ void WSLCSessionManagerImpl::ListSessions(_Out_ WSLCSessionListEntry** Sessions,
403
*SessionsCount = static_cast<ULONG>(sessionInfo.size());
404
}
405
405
-void WSLCSessionManagerImpl::EnterSession(_In_ LPCWSTR DisplayName, _In_ LPCWSTR StoragePath, IWSLCSession** WslcSession)
406
+void WSLCSessionManagerImpl::EnterSession(
407
+ _In_ LPCWSTR DisplayName, _In_ LPCWSTR StoragePath, _In_opt_ IWarningCallback* WarningCallback, _Out_ IWSLCSession** WslcSession)
408
{
409
THROW_HR_IF(E_POINTER, DisplayName == nullptr || StoragePath == nullptr);
410
THROW_HR_IF(E_INVALIDARG, DisplayName[0] == L'\0' || StoragePath[0] == L'\0');
411
412
const auto callerToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);
413
auto sessionSettings = SessionSettings::Custom(callerToken.get(), DisplayName, StoragePath, WSLCSessionStorageFlagsNoCreate);
412
- CreateSession(&sessionSettings.Settings, WSLCSessionFlagsNone, WslcSession);
414
+ CreateSession(&sessionSettings.Settings, WSLCSessionFlagsNone, WarningCallback, WslcSession);
415
}
416
417
WSLCSessionInitSettings WSLCSessionManagerImpl::CreateSessionSettings(
@@ -551,20 +553,21 @@ try
553
}
554
CATCH_RETURN();
555
554
-HRESULT WSLCSessionManager::CreateSession(const WSLCSessionSettings* WslcSessionSettings, WSLCSessionFlags Flags, IWSLCSession** WslcSession)
556
+HRESULT WSLCSessionManager::CreateSession(
557
+ const WSLCSessionSettings* WslcSessionSettings, WSLCSessionFlags Flags, IWarningCallback* WarningCallback, IWSLCSession** WslcSession)
558
try
559
{
560
COMServiceExecutionContext context;
561
559
- return CallImpl(&WSLCSessionManagerImpl::CreateSession, WslcSessionSettings, Flags, WslcSession);
562
+ return CallImpl(&WSLCSessionManagerImpl::CreateSession, WslcSessionSettings, Flags, WarningCallback, WslcSession);
563
}
564
CATCH_RETURN();
565
563
-HRESULT WSLCSessionManager::EnterSession(_In_ LPCWSTR DisplayName, _In_ LPCWSTR StoragePath, IWSLCSession** WslcSession)
566
+HRESULT WSLCSessionManager::EnterSession(_In_ LPCWSTR DisplayName, _In_ LPCWSTR StoragePath, IWarningCallback* WarningCallback, IWSLCSession** WslcSession)
567
{
568
COMServiceExecutionContext context;
569
567
- return CallImpl(&WSLCSessionManagerImpl::EnterSession, DisplayName, StoragePath, WslcSession);
570
+ return CallImpl(&WSLCSessionManagerImpl::EnterSession, DisplayName, StoragePath, WarningCallback, WslcSession);
571
}
572
573
HRESULT WSLCSessionManager::ListSessions(_Out_ WSLCSessionListEntry** Sessions, _Out_ ULONG* SessionsCount)
src/windows/service/exe/WSLCSessionManager.h
+9
-4
@@ -79,8 +79,12 @@ public:
79
WSLCSessionManagerImpl();
80
~WSLCSessionManagerImpl();
81
82
- void CreateSession(const WSLCSessionSettings* WslcSessionSettings, WSLCSessionFlags Flags, IWSLCSession** WslcSession);
83
- void EnterSession(_In_ LPCWSTR DisplayName, _In_ LPCWSTR StoragePath, IWSLCSession** WslcSession);
82
+ void CreateSession(
83
+ _In_ const WSLCSessionSettings* WslcSessionSettings,
84
+ _In_ WSLCSessionFlags Flags,
85
+ _In_opt_ IWarningCallback* WarningCallback,
86
+ _Out_ IWSLCSession** WslcSession);
87
+ void EnterSession(_In_ LPCWSTR DisplayName, _In_ LPCWSTR StoragePath, _In_opt_ IWarningCallback* WarningCallback, _Out_ IWSLCSession** WslcSession);
88
void ListSessions(_Out_ WSLCSessionListEntry** Sessions, _Out_ ULONG* SessionsCount);
89
void OpenSession(_In_ ULONG Id, _Out_ IWSLCSession** Session);
90
void OpenSessionByName(_In_ LPCWSTR DisplayName, _Out_ IWSLCSession** Session);
@@ -198,8 +202,9 @@ public:
202
203
IFACEMETHOD(GetVersion)(_Out_ WSLCVersion* Version) override;
204
IFACEMETHOD(IsClientVersionSupported)(_In_ const WSLCVersion* ClientVersion, _Out_ BOOL* IsSupported) override;
201
- IFACEMETHOD(CreateSession)(const WSLCSessionSettings* WslcSessionSettings, WSLCSessionFlags Flags, IWSLCSession** WslcSession) override;
202
- IFACEMETHOD(EnterSession)(_In_ LPCWSTR DisplayName, _In_ LPCWSTR StoragePath, IWSLCSession** WslcSession) override;
205
+ IFACEMETHOD(CreateSession)(
206
+ const WSLCSessionSettings* WslcSessionSettings, WSLCSessionFlags Flags, IWarningCallback* WarningCallback, IWSLCSession** WslcSession) override;
207
+ IFACEMETHOD(EnterSession)(_In_ LPCWSTR DisplayName, _In_ LPCWSTR StoragePath, IWarningCallback* WarningCallback, IWSLCSession** WslcSession) override;
208
IFACEMETHOD(ListSessions)(_Out_ WSLCSessionListEntry** Sessions, _Out_ ULONG* SessionsCount) override;
209
IFACEMETHOD(OpenSession)(_In_ ULONG Id, _Out_ IWSLCSession** Session) override;
210
IFACEMETHOD(OpenSessionByName)(_In_ LPCWSTR DisplayName, _Out_ IWSLCSession** Session) override;
src/windows/service/inc/wslc.idl
+23
-11
@@ -117,6 +117,16 @@ interface IProgressCallback : IUnknown
117
HRESULT OnProgress(LPCSTR Status, LPCSTR Id, ULONGLONG Current, ULONGLONG Total);
118
};
119
120
+[
121
+ uuid(8153ED5D-8ABB-408B-ADBE-C0F3B13E07C3),
122
+ pointer_default(unique),
123
+ object
124
+]
125
+interface IWarningCallback : IUnknown
126
+{
127
+ HRESULT OnWarning([in, string] LPCWSTR Message);
128
+};
129
+
130
[
131
uuid(F3E6D5B2-1D40-4E8B-9C39-7A45D1C0F8A2),
132
pointer_default(unique),
@@ -556,7 +566,7 @@ interface IWSLCContainer : IUnknown
566
{
567
HRESULT Attach([in, unique] LPCSTR DetachKeys, [out] WSLCHandle* StdIn, [out] WSLCHandle* StdOut, [out] WSLCHandle* StdErr);
568
HRESULT Stop([in] WSLCSignal Signal, [in] LONG TimeoutSeconds);
559
- HRESULT Start([in] WSLCContainerStartFlags Flags, [in, unique] LPCSTR DetachKeys);
569
+ HRESULT Start([in] WSLCContainerStartFlags Flags, [in, unique] LPCSTR DetachKeys, [in, unique] IWarningCallback* WarningCallback);
570
HRESULT Delete([in] WSLCDeleteFlags Flags);
571
HRESULT Export([in] WSLCHandle TarHandle);
572
HRESULT GetState([out] WSLCContainerState* State);
@@ -724,10 +734,10 @@ interface IWSLCSession : IUnknown
734
HRESULT GetState([out] WSLCSessionState* State);
735
736
// Image management.
727
- HRESULT PullImage([in] LPCSTR Image, [in, unique] LPCSTR RegistryAuthenticationInformation, [in, unique] IProgressCallback* ProgressCallback);
737
+ HRESULT PullImage([in] LPCSTR Image, [in, unique] LPCSTR RegistryAuthenticationInformation, [in, unique] IProgressCallback* ProgressCallback, [in, unique] IWarningCallback* WarningCallback);
738
HRESULT BuildImage([in] const WSLCBuildImageOptions* Options, [in, unique] IProgressCallback* ProgressCallback, [in, unique, system_handle(sh_event)] HANDLE CancelEvent);
729
- HRESULT LoadImage([in] WSLCHandle ImageHandle, [in, unique] IProgressCallback* ProgressCallback, [in] ULONGLONG ContentLength);
730
- HRESULT ImportImage([in] WSLCHandle ImageHandle, [in] LPCSTR ImageName, [in, unique] IProgressCallback* ProgressCallback, [in] ULONGLONG ContentLength);
739
+ HRESULT LoadImage([in] WSLCHandle ImageHandle, [in, unique] IProgressCallback* ProgressCallback, [in] ULONGLONG ContentLength, [in, unique] IWarningCallback* WarningCallback);
740
+ HRESULT ImportImage([in] WSLCHandle ImageHandle, [in] LPCSTR ImageName, [in, unique] IProgressCallback* ProgressCallback, [in] ULONGLONG ContentLength, [in, unique] IWarningCallback* WarningCallback);
741
HRESULT SaveImage([in] WSLCHandle OutputHandle, [in] LPCSTR ImageNameOrID, [in, unique] IProgressCallback * ProgressCallback, [in, unique, system_handle(sh_event)] HANDLE CancelEvent);
742
HRESULT ListImages([in, unique] const WSLCListImagesOptions* Options, [out, size_is(, *Count)] WSLCImageInformation** Images, [out] ULONG* Count);
743
HRESULT DeleteImage([in] const WSLCDeleteImageOptions* Options, [out, size_is(, *Count)] WSLCDeletedImageInformation** DeletedImages, [out] ULONG* Count);
@@ -736,7 +746,7 @@ interface IWSLCSession : IUnknown
746
HRESULT PruneImages([in, unique, size_is(FiltersCount)] const WSLCFilter* Filters, [in] ULONG FiltersCount, [out, size_is(, *DeletedImagesCount)] WSLCDeletedImageInformation** DeletedImages, [out] ULONG* DeletedImagesCount, [out] ULONGLONG* SpaceReclaimed);
747
748
// Container management.
739
- HRESULT CreateContainer([in] const WSLCContainerOptions* Options, [out] IWSLCContainer** Container);
749
+ HRESULT CreateContainer([in] const WSLCContainerOptions* Options, [in, unique] IWarningCallback* WarningCallback, [out] IWSLCContainer** Container);
750
HRESULT OpenContainer([in, ref] LPCSTR Id, [out] IWSLCContainer** Container);
751
HRESULT ListContainers([in, unique] const WSLCListContainersOptions* Options,[out, size_is(, *Count)] WSLCContainerEntry** Containers,[out] ULONG* Count, [out, size_is(, *PortsCount)] WSLCContainerPortMapping** Ports, [out] ULONG* PortsCount);
752
HRESULT PruneContainers([in, unique, size_is(FiltersCount)] const WSLCFilter* Filters, [in] ULONG FiltersCount, [out] WSLCPruneContainersResults* Result);
@@ -766,7 +776,8 @@ interface IWSLCSession : IUnknown
776
HRESULT Initialize(
777
[in] const WSLCSessionInitSettings* Settings,
778
[in] IWSLCVirtualMachine* Vm,
769
- [in] IWSLCPluginNotifier* PluginNotifier);
779
+ [in] IWSLCPluginNotifier* PluginNotifier,
780
+ [in, unique] IWarningCallback* WarningCallback);
781
782
// Volume management.
783
HRESULT CreateVolume([in] const WSLCVolumeOptions* Options, [out] WSLCVolumeInformation* VolumeInfo);
@@ -775,11 +786,11 @@ interface IWSLCSession : IUnknown
786
HRESULT InspectVolume([in] LPCSTR Name, [out] LPSTR* Output);
787
788
HRESULT Authenticate([in] LPCSTR ServerAddress, [in] LPCSTR Username, [in] LPCSTR Password, [out] LPSTR* IdentityToken);
778
- HRESULT PushImage([in] LPCSTR Image, [in] LPCSTR RegistryAuthenticationInformation, [in, unique] IProgressCallback* ProgressCallback);
779
- HRESULT PruneVolumes([in, unique, size_is(FiltersCount)] const WSLCFilter* Filters, [in] ULONG FiltersCount, [out, size_is(, *VolumesCount)] WSLCVolumeName** Volumes, [out] ULONG* VolumesCount, [out] ULONGLONG* SpaceReclaimed);
789
+ HRESULT PushImage([in] LPCSTR Image, [in] LPCSTR RegistryAuthenticationInformation, [in, unique] IProgressCallback* ProgressCallback, [in, unique] IWarningCallback* WarningCallback);
790
+ HRESULT PruneVolumes([in, unique, size_is(FiltersCount)] const WSLCFilter* Filters, [in] ULONG FiltersCount, [in, unique] IWarningCallback* WarningCallback, [out, size_is(, *VolumesCount)] WSLCVolumeName** Volumes, [out] ULONG* VolumesCount, [out] ULONGLONG* SpaceReclaimed);
791
792
// Network management.
782
- HRESULT CreateNetwork([in] const WSLCNetworkOptions* Options);
793
+ HRESULT CreateNetwork([in] const WSLCNetworkOptions* Options, [in, unique] IWarningCallback* WarningCallback);
794
HRESULT DeleteNetwork([in] LPCSTR Name);
795
HRESULT ListNetworks([out, size_is(, *Count)] WSLCNetworkInformation** Networks, [out] ULONG* Count);
796
HRESULT InspectNetwork([in] LPCSTR Name, [out] LPSTR* Output);
@@ -821,6 +832,7 @@ interface IWSLCSessionFactory : IUnknown
832
[in] const WSLCSessionInitSettings* Settings,
833
[in] IWSLCVirtualMachine* Vm,
834
[in] IWSLCPluginNotifier* PluginNotifier,
835
+ [in, unique] IWarningCallback* WarningCallback,
836
[out] IWSLCSession** Session,
837
[out] IWSLCSessionReference** ServiceRef);
838
@@ -857,8 +869,8 @@ interface IWSLCSessionManager : IUnknown
869
HRESULT IsClientVersionSupported([in] const WSLCVersion* ClientVersion, [out] BOOL* IsSupported);
870
871
// Session management.
860
- HRESULT CreateSession([in, unique] const WSLCSessionSettings* Settings, WSLCSessionFlags Flags, [out] IWSLCSession** Session);
861
- HRESULT EnterSession([in, ref] LPCWSTR DisplayName, [in, ref] LPCWSTR StoragePath, [out] IWSLCSession** Session);
872
+ HRESULT CreateSession([in, unique] const WSLCSessionSettings* Settings, WSLCSessionFlags Flags, [in, unique] IWarningCallback* WarningCallback, [out] IWSLCSession** Session);
873
+ HRESULT EnterSession([in, ref] LPCWSTR DisplayName, [in, ref] LPCWSTR StoragePath, [in, unique] IWarningCallback* WarningCallback, [out] IWSLCSession** Session);
874
HRESULT ListSessions([out, size_is(, *SessionsCount)] WSLCSessionListEntry** Sessions, [out] ULONG* SessionsCount);
875
HRESULT OpenSession([in] ULONG Id, [out] IWSLCSession** Session);
876
HRESULT OpenSessionByName([in, unique] LPCWSTR DisplayName, [out] IWSLCSession** Session);
src/windows/wslc/services/ContainerService.cpp
+12
-7
@@ -17,6 +17,7 @@ Abstract:
17
#include "ConsoleService.h"
18
#include "ImageService.h"
19
#include "ImageProgressCallback.h"
20
+#include "WarningCallback.h"
21
#include <wslutil.h>
22
#include <WSLCProcessLauncher.h>
23
#include <CommandLine.h>
@@ -38,7 +39,8 @@ static void SetContainerArguments(WSLCProcessOptions& options, std::vector<const
39
options.CommandLine = {.Values = argsStorage.data(), .Count = static_cast<ULONG>(argsStorage.size())};
40
}
41
41
-static wsl::windows::common::RunningWSLCContainer CreateInternal(Session& session, const std::string& image, const ContainerOptions& options)
42
+static wsl::windows::common::RunningWSLCContainer CreateInternal(
43
+ Session& session, const std::string& image, const ContainerOptions& options, IWarningCallback* warningCallback = nullptr)
44
{
45
auto processFlags = WSLCProcessFlagsNone;
46
WI_SetFlagIf(processFlags, WSLCProcessFlagsStdin, options.Interactive);
@@ -160,7 +162,7 @@ static wsl::windows::common::RunningWSLCContainer CreateInternal(Session& sessio
162
containerLauncher.AddLabel(key, value);
163
}
164
163
- auto [result, runningContainer] = containerLauncher.CreateNoThrow(*session.Get());
165
+ auto [result, runningContainer] = containerLauncher.CreateNoThrow(*session.Get(), warningCallback);
166
if (result == WSLC_E_IMAGE_NOT_FOUND)
167
{
168
{
@@ -170,7 +172,7 @@ static wsl::windows::common::RunningWSLCContainer CreateInternal(Session& sessio
172
ImageService imageService;
173
imageService.Pull(session, image, &callback);
174
}
173
- return containerLauncher.Create(*session.Get());
175
+ return containerLauncher.Create(*session.Get(), warningCallback);
176
}
177
178
THROW_IF_FAILED(result);
@@ -346,9 +348,10 @@ int ContainerService::Run(Session& session, const std::string& image, ContainerO
348
// container isn't created when the caller-requested path can't be written. The file is
349
// removed automatically if we don't reach Commit() below.
350
CidFile cidFile(runOptions.CidFile);
351
+ auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
352
353
// Create the container
351
- auto runningContainer = CreateInternal(session, image, runOptions);
354
+ auto runningContainer = CreateInternal(session, image, runOptions, warningCallback.Get());
355
auto& container = runningContainer.Get();
356
357
WSLCContainerId containerId{};
@@ -357,7 +360,7 @@ int ContainerService::Run(Session& session, const std::string& image, ContainerO
360
// Start the created container
361
WSLCContainerStartFlags startFlags{};
362
WI_SetFlagIf(startFlags, WSLCContainerStartFlagsAttach, !runOptions.Detach);
360
- THROW_IF_FAILED(container.Start(startFlags, nullptr)); // TODO: Error message, detach keys
363
+ THROW_IF_FAILED(container.Start(startFlags, nullptr, warningCallback.Get())); // TODO: Error message, detach keys
364
365
// Disable auto-delete only after successful start
366
runningContainer.SetDeleteOnClose(false);
@@ -377,7 +380,8 @@ int ContainerService::Run(Session& session, const std::string& image, ContainerO
380
CreateContainerResult ContainerService::Create(Session& session, const std::string& image, ContainerOptions runOptions)
381
{
382
CidFile cidFile(runOptions.CidFile);
380
- auto runningContainer = CreateInternal(session, image, runOptions);
383
+ auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
384
+ auto runningContainer = CreateInternal(session, image, runOptions, warningCallback.Get());
385
runningContainer.SetDeleteOnClose(false);
386
auto& container = runningContainer.Get();
387
WSLCContainerId id{};
@@ -391,7 +395,8 @@ int ContainerService::Start(Session& session, const std::string& id, bool attach
395
wil::com_ptr<IWSLCContainer> container;
396
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));
397
WSLCContainerStartFlags flags = attach ? WSLCContainerStartFlagsAttach : WSLCContainerStartFlagsNone;
394
- THROW_IF_FAILED_EXCEPT(container->Start(flags, nullptr), WSLC_E_CONTAINER_IS_RUNNING);
398
+ auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
399
+ THROW_IF_FAILED_EXCEPT(container->Start(flags, nullptr, warningCallback.Get()), WSLC_E_CONTAINER_IS_RUNNING);
400
401
if (!attach)
402
{
src/windows/wslc/services/ImageService.cpp
+13
-4
@@ -14,6 +14,7 @@ Abstract:
14
#include "ImageService.h"
15
#include "RegistryService.h"
16
#include "SessionService.h"
17
+#include "WarningCallback.h"
18
#include <wslutil.h>
19
#include <HandleConsoleProgressBar.h>
20
#include <relay.hpp>
@@ -227,14 +228,20 @@ std::vector<ImageInformation> ImageService::List(
228
void ImageService::Load(wsl::windows::wslc::models::Session& session, const std::wstring& input)
229
{
230
auto source = OpenImageInput(input);
230
- THROW_IF_FAILED(session.Get()->LoadImage(ToCOMInputHandle(source.Handle.Get()), nullptr, source.ContentLength));
231
+ auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
232
+ THROW_IF_FAILED(session.Get()->LoadImage(ToCOMInputHandle(source.Handle.Get()), nullptr, source.ContentLength, warningCallback.Get()));
233
}
234
235
void ImageService::Import(wsl::windows::wslc::models::Session& session, const std::wstring& input, const std::string& imageName)
236
{
237
auto source = OpenImageInput(input);
238
+ auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
239
THROW_IF_FAILED(session.Get()->ImportImage(
237
- ToCOMInputHandle(source.Handle.Get()), imageName.empty() ? nullptr : imageName.c_str(), nullptr, source.ContentLength));
240
+ ToCOMInputHandle(source.Handle.Get()),
241
+ imageName.empty() ? nullptr : imageName.c_str(),
242
+ nullptr,
243
+ source.ContentLength,
244
+ warningCallback.Get()));
245
}
246
247
void ImageService::Delete(wsl::windows::wslc::models::Session& session, const std::string& image, bool force, bool noPrune)
@@ -260,7 +267,8 @@ void ImageService::Pull(wsl::windows::wslc::models::Session& session, const std:
267
{
268
auto server = GetServerFromImage(image);
269
auto auth = RegistryService::Get(server);
263
- THROW_IF_FAILED(session.Get()->PullImage(image.c_str(), auth.c_str(), callback));
270
+ auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
271
+ THROW_IF_FAILED(session.Get()->PullImage(image.c_str(), auth.c_str(), callback, warningCallback.Get()));
272
}
273
274
void ImageService::Tag(wsl::windows::wslc::models::Session& session, const std::string& sourceImage, const std::string& targetImage)
@@ -291,7 +299,8 @@ void ImageService::Push(wsl::windows::wslc::models::Session& session, const std:
299
{
300
auto server = GetServerFromImage(image);
301
auto auth = RegistryService::Get(server);
294
- THROW_IF_FAILED(session.Get()->PushImage(image.c_str(), auth.c_str(), callback));
302
+ auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
303
+ THROW_IF_FAILED(session.Get()->PushImage(image.c_str(), auth.c_str(), callback, warningCallback.Get()));
304
}
305
306
void ImageService::Save(wsl::windows::wslc::models::Session& session, const std::string& image, const std::wstring& output, HANDLE cancelEvent)
src/windows/wslc/services/SessionService.cpp
+5
-2
@@ -15,6 +15,7 @@ Abstract:
15
#include "precomp.h"
16
#include "SessionService.h"
17
#include "ConsoleService.h"
18
+#include "WarningCallback.h"
19
#include <wslc.h>
20
#include <WSLCProcessLauncher.h>
21
@@ -113,7 +114,8 @@ Session SessionService::CreateDefaultSession()
114
115
// Null Settings = default session with server-determined name and settings.
116
wil::com_ptr<IWSLCSession> session;
116
- THROW_IF_FAILED(sessionManager->CreateSession(nullptr, WSLCSessionFlagsNone, &session));
117
+ auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
118
+ THROW_IF_FAILED(sessionManager->CreateSession(nullptr, WSLCSessionFlagsNone, warningCallback.Get(), &session));
119
wsl::windows::common::security::ConfigureForCOMImpersonation(session.get());
120
return Session(std::move(session));
121
}
@@ -128,7 +130,8 @@ int SessionService::Enter(const std::wstring& storagePath, const std::wstring& d
130
wsl::windows::common::security::ConfigureForCOMImpersonation(sessionManager.get());
131
132
wil::com_ptr<IWSLCSession> session;
131
- THROW_IF_FAILED(sessionManager->EnterSession(displayName.c_str(), storagePath.c_str(), &session));
133
+ auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
134
+ THROW_IF_FAILED(sessionManager->EnterSession(displayName.c_str(), storagePath.c_str(), warningCallback.Get(), &session));
135
wsl::windows::common::security::ConfigureForCOMImpersonation(session.get());
136
wsl::windows::common::wslutil::PrintMessage(Localization::MessageWslcCreatedSession(displayName), stderr);
137
src/windows/wslc/services/WarningCallback.h
new
+24
@@ -0,0 +1,24 @@
1
+// Copyright (C) Microsoft Corporation. All rights reserved.
2
+
3
+#pragma once
4
+
5
+#include <wslc.h>
6
+#include <wslutil.h>
7
+
8
+namespace wsl::windows::wslc::services {
9
+
10
+class DECLSPEC_UUID("A7E3F8B2-4D19-4C6A-9E5B-8F2A1D3C7E90") WarningCallback
11
+ : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IWarningCallback, IFastRundown>
12
+{
13
+public:
14
+ HRESULT OnWarning(LPCWSTR Message) override
15
+ {
16
+ WI_ASSERT(Message);
17
+ // The message already includes the "wsl: " prefix and a trailing newline (added by
18
+ // EmitUserWarning), so write it directly. This matches how wsl.exe surfaces its warnings.
19
+ fputws(Message, stderr);
20
+ return S_OK;
21
+ }
22
+};
23
+
24
+} // namespace wsl::windows::wslc::services
src/windows/wslcsession/WSLCContainer.cpp
+47
-22
@@ -19,6 +19,7 @@ Abstract:
19
20
#include "precomp.h"
21
#include "WSLCContainer.h"
22
+#include "WSLCExecutionContext.h"
23
#include "WSLCProcess.h"
24
#include "WSLCProcessIO.h"
25
#include "WSLCVolumes.h"
@@ -44,6 +45,7 @@ using wsl::windows::service::wslc::WSLCContainer;
45
using wsl::windows::service::wslc::WSLCContainerImpl;
46
using wsl::windows::service::wslc::WSLCContainerMetadata;
47
using wsl::windows::service::wslc::WSLCContainerMetadataV1;
48
+using wsl::windows::service::wslc::WSLCExecutionContext;
49
using wsl::windows::service::wslc::WSLCPortMapping;
50
using wsl::windows::service::wslc::WSLCSession;
51
using wsl::windows::service::wslc::WSLCVirtualMachine;
@@ -282,10 +284,17 @@ void UnmountVolumes(std::vector<WSLCVolumeMount>& volumes, WSLCVirtualMachine& p
284
{
285
if (volume.Mounted)
286
{
285
- if (SUCCEEDED(LOG_IF_FAILED(parentVM.UnmountWindowsFolder(volume.ParentVMPath.c_str()))))
287
+ auto result = parentVM.UnmountWindowsFolder(volume.ParentVMPath.c_str());
288
+ if (SUCCEEDED(result))
289
{
290
volume.Mounted = false;
291
}
292
+ else
293
+ {
294
+ LOG_HR(result);
295
+ EMIT_USER_WARNING(wsl::shared::Localization::MessageWslcVolumeUnmountFailed(
296
+ volume.HostPath, wsl::windows::common::wslutil::GetErrorString(result)));
297
+ }
298
}
299
}
300
}
@@ -500,7 +509,7 @@ WSLCContainerImpl::WSLCContainerImpl(
509
m_mountedVolumes(std::move(volumes)),
510
m_mappedPorts(std::move(ports)),
511
m_labels(std::move(labels)),
503
- m_comWrapper(wil::MakeOrThrow<WSLCContainer>(this, std::move(onDeleted))),
512
+ m_comWrapper(wil::MakeOrThrow<WSLCContainer>(this, wslcSession, std::move(onDeleted))),
513
m_dockerClient(DockerClient),
514
m_eventTracker(EventTracker),
515
m_ioRelay(Relay),
@@ -760,7 +769,12 @@ void WSLCContainerImpl::Start(WSLCContainerStartFlags Flags, LPCSTR DetachKeys)
769
{
770
m_dockerClient.StopContainer(m_id.c_str(), {}, {});
771
}
763
- CATCH_LOG();
772
+ catch (...)
773
+ {
774
+ LOG_CAUGHT_EXCEPTION();
775
+ EMIT_USER_WARNING(wsl::shared::Localization::MessageWslcContainerStopAfterPluginRejectionFailed(
776
+ wsl::shared::string::MultiByteToWide(m_id)));
777
+ }
778
779
if (comError.has_value() && comError->Message)
780
{
@@ -1582,6 +1596,12 @@ std::unique_ptr<WSLCContainerImpl> WSLCContainerImpl::Create(
1596
// Send the request to docker.
1597
auto result = DockerClient.CreateContainer(request, containerName);
1598
1599
+ // Surface any warnings returned by Docker (e.g., deprecated features, configuration issues).
1600
+ for (const auto& warning : result.Warnings)
1601
+ {
1602
+ EMIT_USER_WARNING(wsl::shared::string::MultiByteToWide(warning));
1603
+ }
1604
+
1605
// Clean up the Docker container if anything below fails.
1606
// N.B. The container ID is captured by value since it is moved into the WSLCContainerImpl constructor below.
1607
auto deleteOnFailure = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&DockerClient, containerId = result.Id]() {
@@ -1753,7 +1773,12 @@ std::unique_ptr<WSLCContainerImpl> WSLCContainerImpl::Open(
1773
container->m_stateChangedAt = ParseDockerTimestamp(timestamp);
1774
}
1775
}
1756
- CATCH_LOG();
1776
+ catch (...)
1777
+ {
1778
+ LOG_CAUGHT_EXCEPTION();
1779
+ EMIT_USER_WARNING(wsl::shared::Localization::MessageWslcContainerTimestampRecoveryFailed(
1780
+ wsl::shared::string::MultiByteToWide(dockerContainer.Id)));
1781
+ }
1782
1783
return container;
1784
}
@@ -2020,14 +2045,14 @@ __requires_lock_held(m_lock) void WSLCContainerImpl::Transition(WSLCContainerSta
2045
m_stateChangedAt = stateChangedAt.value_or(static_cast<std::uint64_t>(std::time(nullptr)));
2046
}
2047
2023
-WSLCContainer::WSLCContainer(WSLCContainerImpl* impl, std::function<void(const WSLCContainerImpl*)>&& OnDeleted) :
2024
- COMImplClass<WSLCContainerImpl>(impl), m_onDeleted(std::move(OnDeleted))
2048
+WSLCContainer::WSLCContainer(WSLCContainerImpl* impl, WSLCSession& session, std::function<void(const WSLCContainerImpl*)>&& OnDeleted) :
2049
+ COMImplClass<WSLCContainerImpl>(impl), m_session(session), m_onDeleted(std::move(OnDeleted))
2050
{
2051
}
2052
2053
HRESULT WSLCContainer::Attach(LPCSTR DetachKeys, WSLCHandle* Stdin, WSLCHandle* Stdout, WSLCHandle* Stderr)
2054
{
2030
- COMServiceExecutionContext context;
2055
+ WSLCExecutionContext context(&m_session);
2056
2057
*Stdin = {};
2058
*Stdout = {};
@@ -2038,7 +2063,7 @@ HRESULT WSLCContainer::Attach(LPCSTR DetachKeys, WSLCHandle* Stdin, WSLCHandle*
2063
2064
HRESULT WSLCContainer::GetState(WSLCContainerState* Result)
2065
{
2041
- COMServiceExecutionContext context;
2066
+ WSLCExecutionContext context(&m_session);
2067
RETURN_HR_IF_NULL(E_POINTER, Result);
2068
2069
*Result = WslcContainerStateInvalid;
@@ -2065,7 +2090,7 @@ HRESULT WSLCContainer::GetState(WSLCContainerState* Result)
2090
2091
HRESULT WSLCContainer::GetInitProcess(IWSLCProcess** Process)
2092
{
2068
- COMServiceExecutionContext context;
2093
+ WSLCExecutionContext context(&m_session);
2094
2095
*Process = nullptr;
2096
@@ -2091,7 +2116,7 @@ HRESULT WSLCContainer::GetInitProcess(IWSLCProcess** Process)
2116
2117
HRESULT WSLCContainer::Exec(const WSLCProcessOptions* Options, LPCSTR DetachKeys, IWSLCProcess** Process)
2118
{
2094
- COMServiceExecutionContext context;
2119
+ WSLCExecutionContext context(&m_session);
2120
2121
*Process = nullptr;
2122
return CallImpl(&WSLCContainerImpl::Exec, Options, DetachKeys, Process);
@@ -2099,22 +2124,22 @@ HRESULT WSLCContainer::Exec(const WSLCProcessOptions* Options, LPCSTR DetachKeys
2124
2125
HRESULT WSLCContainer::Stop(_In_ WSLCSignal Signal, _In_ LONG TimeoutSeconds)
2126
{
2102
- COMServiceExecutionContext context;
2127
+ WSLCExecutionContext context(&m_session);
2128
2129
return CallImpl(&WSLCContainerImpl::Stop, Signal, TimeoutSeconds, false);
2130
}
2131
2132
HRESULT WSLCContainer::Kill(_In_ WSLCSignal Signal)
2133
{
2109
- COMServiceExecutionContext context;
2134
+ WSLCExecutionContext context(&m_session);
2135
2136
return CallImpl(&WSLCContainerImpl::Stop, Signal, {}, true);
2137
}
2138
2114
-HRESULT WSLCContainer::Start(WSLCContainerStartFlags Flags, LPCSTR DetachKeys)
2139
+HRESULT WSLCContainer::Start(WSLCContainerStartFlags Flags, LPCSTR DetachKeys, IWarningCallback* WarningCallback)
2140
try
2141
{
2117
- COMServiceExecutionContext context;
2142
+ WSLCExecutionContext context(&m_session, WarningCallback);
2143
2144
THROW_HR_IF_MSG(E_INVALIDARG, WI_IsAnyFlagSet(Flags, ~WSLCContainerStartFlagsValid), "Invalid flags: 0x%x", Flags);
2145
@@ -2124,7 +2149,7 @@ CATCH_RETURN();
2149
2150
HRESULT WSLCContainer::Inspect(LPSTR* Output)
2151
{
2127
- COMServiceExecutionContext context;
2152
+ WSLCExecutionContext context(&m_session);
2153
2154
*Output = nullptr;
2155
@@ -2134,7 +2159,7 @@ HRESULT WSLCContainer::Inspect(LPSTR* Output)
2159
HRESULT WSLCContainer::Stats(LPSTR* Output)
2160
try
2161
{
2137
- COMServiceExecutionContext context;
2162
+ WSLCExecutionContext context(&m_session);
2163
2164
RETURN_HR_IF(E_POINTER, Output == nullptr);
2165
@@ -2146,7 +2171,7 @@ CATCH_RETURN();
2171
HRESULT WSLCContainer::Delete(WSLCDeleteFlags Flags)
2172
try
2173
{
2149
- COMServiceExecutionContext context;
2174
+ WSLCExecutionContext context(&m_session);
2175
2176
THROW_HR_IF_MSG(E_INVALIDARG, WI_IsAnyFlagSet(Flags, ~WSLCDeleteFlagsValid), "Invalid flags: 0x%x", Flags);
2177
@@ -2177,7 +2202,7 @@ CATCH_LOG();
2202
2203
HRESULT WSLCContainer::Export(WSLCHandle TarHandle)
2204
{
2180
- COMServiceExecutionContext context;
2205
+ WSLCExecutionContext context(&m_session);
2206
2207
return CallImpl(&WSLCContainerImpl::Export, TarHandle);
2208
}
@@ -2185,7 +2210,7 @@ HRESULT WSLCContainer::Export(WSLCHandle TarHandle)
2210
HRESULT WSLCContainer::Logs(WSLCLogsFlags Flags, WSLCHandle* Stdout, WSLCHandle* Stderr, ULONGLONG Since, ULONGLONG Until, ULONGLONG Tail)
2211
try
2212
{
2188
- COMServiceExecutionContext context;
2213
+ WSLCExecutionContext context(&m_session);
2214
RETURN_HR_IF(E_POINTER, Stdout == nullptr || Stderr == nullptr);
2215
2216
THROW_HR_IF_MSG(E_INVALIDARG, WI_IsAnyFlagSet(Flags, ~WSLCLogsFlagsValid), "Invalid flags: 0x%x", Flags);
@@ -2200,7 +2225,7 @@ CATCH_RETURN();
2225
HRESULT WSLCContainer::GetId(WSLCContainerId Id)
2226
try
2227
{
2203
- COMServiceExecutionContext context;
2228
+ WSLCExecutionContext context(&m_session);
2229
2230
const auto hr = wil::ResultFromException([&] {
2231
auto [lock, impl] = LockImpl();
@@ -2225,7 +2250,7 @@ CATCH_RETURN();
2250
HRESULT WSLCContainer::GetName(LPSTR* Name)
2251
try
2252
{
2228
- COMServiceExecutionContext context;
2253
+ WSLCExecutionContext context(&m_session);
2254
2255
RETURN_HR_IF_NULL(E_POINTER, Name);
2256
*Name = nullptr;
@@ -2288,7 +2313,7 @@ void WSLCContainerImpl::GetLabels(WSLCLabelInformation** Labels, ULONG* Count) c
2313
HRESULT WSLCContainer::GetLabels(WSLCLabelInformation** Labels, ULONG* Count)
2314
try
2315
{
2291
- COMServiceExecutionContext context;
2316
+ WSLCExecutionContext context(&m_session);
2317
2318
RETURN_HR_IF(E_POINTER, Labels == nullptr || Count == nullptr);
2319
src/windows/wslcsession/WSLCContainer.h
+3
-2
@@ -220,7 +220,7 @@ class DECLSPEC_UUID("B1F1C4E3-C225-4CAE-AD8A-34C004DE1AE4") WSLCContainer
220
{
221
222
public:
223
- WSLCContainer(WSLCContainerImpl* impl, std::function<void(const WSLCContainerImpl*)>&& OnDeleted);
223
+ WSLCContainer(WSLCContainerImpl* impl, WSLCSession& session, std::function<void(const WSLCContainerImpl*)>&& OnDeleted);
224
225
IFACEMETHOD(Attach)(_In_opt_ LPCSTR DetachKeys, _Out_ WSLCHandle* Stdin, _Out_ WSLCHandle* Stdout, _Out_ WSLCHandle* Stderr) override;
226
IFACEMETHOD(Stop)(_In_ WSLCSignal Signal, _In_ LONG TimeoutSeconds) override;
@@ -230,7 +230,7 @@ public:
230
IFACEMETHOD(GetState)(_Out_ WSLCContainerState* State) override;
231
IFACEMETHOD(GetInitProcess)(_Out_ IWSLCProcess** process) override;
232
IFACEMETHOD(Exec)(_In_ const WSLCProcessOptions* Options, _In_opt_ LPCSTR DetachKeys, _Out_ IWSLCProcess** Process) override;
233
- IFACEMETHOD(Start)(WSLCContainerStartFlags Flags, _In_opt_ LPCSTR DetachKeys) override;
233
+ IFACEMETHOD(Start)(WSLCContainerStartFlags Flags, _In_opt_ LPCSTR DetachKeys, _In_opt_ IWarningCallback* WarningCallback) override;
234
IFACEMETHOD(Inspect)(_Out_ LPSTR* Output) override;
235
IFACEMETHOD(Logs)(_In_ WSLCLogsFlags Flags, _Out_ WSLCHandle* Stdout, _Out_ WSLCHandle* Stderr, _In_ ULONGLONG Since, _In_ ULONGLONG Until, _In_ ULONGLONG Tail) override;
236
IFACEMETHOD(GetId)(_Out_ WSLCContainerId Id) override;
@@ -245,6 +245,7 @@ public:
245
void CacheState(const std::string& id, const std::string& name, WSLCContainerState state, const Microsoft::WRL::ComPtr<IWSLCProcess>& initProcess) noexcept;
246
247
private:
248
+ WSLCSession& m_session;
249
std::function<void(const WSLCContainerImpl*)> m_onDeleted;
250
251
// Cached read-only properties populated by CacheState() so they remain
src/windows/wslcsession/WSLCExecutionContext.h
new
+55
@@ -0,0 +1,55 @@
1
+// Copyright (C) Microsoft Corporation. All rights reserved.
2
+
3
+#pragma once
4
+
5
+#include "ExecutionContext.h"
6
+#include "WSLCSession.h"
7
+
8
+namespace wsl::windows::service::wslc {
9
+
10
+// Extends COMServiceExecutionContext with a WSLCSession pointer for lazy COM callback
11
+// registration when warnings are emitted. This enables EMIT_USER_WARNING to stream
12
+// warnings back to the CLI via IWarningCallback, with proper cancellation support
13
+// during session termination via RegisterUserCOMCallback/CoCancelCall.
14
+class WSLCExecutionContext : public wsl::windows::common::COMServiceExecutionContext
15
+{
16
+public:
17
+ NON_COPYABLE(WSLCExecutionContext);
18
+ NON_MOVABLE(WSLCExecutionContext);
19
+
20
+ WSLCExecutionContext(WSLCSession* session, IWarningCallback* warningCallback = nullptr) :
21
+ m_session(session), m_warningCallback(warningCallback)
22
+ {
23
+ }
24
+
25
+ ~WSLCExecutionContext() override = default;
26
+
27
+protected:
28
+ bool CollectUserWarning(const std::wstring& warning) override
29
+ {
30
+ if (m_warningCallback != nullptr)
31
+ {
32
+ std::unique_ptr<UserCOMCallback> comCallback;
33
+ if (m_session != nullptr)
34
+ {
35
+ comCallback = std::make_unique<UserCOMCallback>(m_session->RegisterUserCOMCallback());
36
+ }
37
+
38
+ auto hr = m_warningCallback->OnWarning(warning.c_str());
39
+ if (SUCCEEDED(hr) || hr == RPC_E_CALL_CANCELED || hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))
40
+ {
41
+ return true;
42
+ }
43
+
44
+ LOG_HR(hr);
45
+ }
46
+
47
+ return COMServiceExecutionContext::CollectUserWarning(warning);
48
+ }
49
+
50
+private:
51
+ WSLCSession* m_session = nullptr;
52
+ IWarningCallback* m_warningCallback = nullptr;
53
+};
54
+
55
+} // namespace wsl::windows::service::wslc
src/windows/wslcsession/WSLCSession.cpp
+95
-48
@@ -14,6 +14,7 @@ Abstract:
14
15
#include "precomp.h"
16
#include "WSLCSession.h"
17
+#include "WSLCExecutionContext.h"
18
#include "WSLCContainer.h"
19
#include "WSLCNetworkMetadata.h"
20
#include "ContainerNameGenerator.h"
@@ -26,6 +27,7 @@ using io::MultiHandleWait;
27
using wsl::shared::Localization;
28
using wsl::windows::service::wslc::UserCOMCallback;
29
using wsl::windows::service::wslc::UserHandle;
30
+using wsl::windows::service::wslc::WSLCExecutionContext;
31
using wsl::windows::service::wslc::WSLCSession;
32
using wsl::windows::service::wslc::WSLCVirtualMachine;
33
@@ -257,12 +259,17 @@ try
259
}
260
CATCH_RETURN();
261
260
-HRESULT WSLCSession::Initialize(_In_ const WSLCSessionInitSettings* Settings, _In_ IWSLCVirtualMachine* Vm, _In_ IWSLCPluginNotifier* PluginNotifier)
262
+HRESULT WSLCSession::Initialize(
263
+ _In_ const WSLCSessionInitSettings* Settings, _In_ IWSLCVirtualMachine* Vm, _In_ IWSLCPluginNotifier* PluginNotifier, _In_opt_ IWarningCallback* WarningCallback)
264
try
265
{
266
RETURN_HR_IF(E_POINTER, Settings == nullptr || Vm == nullptr);
267
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED), m_virtualMachine.has_value());
268
269
+ // Set up a warning context for the duration of initialization so that non-fatal
270
+ // failures (e.g., container/volume/network recovery) are streamed to the CLI.
271
+ WSLCExecutionContext warningContext(this, WarningCallback);
272
+
273
// N.B. No locking is required because Initialize() is always called before the session is returned to the caller.
274
m_id = Settings->SessionId;
275
m_displayName = Settings->DisplayName ? Settings->DisplayName : L"";
@@ -419,7 +426,11 @@ void WSLCSession::ConfigureStorage(const WSLCSessionInitSettings& Settings, PSID
426
ServiceProcessLauncher launcher("/bin/sh", {"/bin/sh", "-c", cmd});
427
launcher.Launch(*m_virtualMachine);
428
}
422
- CATCH_LOG()
429
+ catch (...)
430
+ {
431
+ LOG_CAUGHT_EXCEPTION();
432
+ EMIT_USER_WARNING(Localization::MessageWslcSwapInitFailed());
433
+ }
434
}
435
436
deleteVhdOnFailure.release();
@@ -590,6 +601,7 @@ void WSLCSession::StreamImageOperation(DockerHTTPClient::HTTPRequestContext& req
601
OperationName,
602
reportedError->c_str(),
603
parsed.errorDetail->message.c_str());
604
+ EMIT_USER_WARNING(wsl::shared::string::MultiByteToWide(*reportedError));
605
}
606
607
reportedError = parsed.errorDetail->message;
@@ -660,10 +672,10 @@ try
672
}
673
CATCH_LOG()
674
663
-HRESULT WSLCSession::PullImage(LPCSTR Image, LPCSTR RegistryAuthenticationInformation, IProgressCallback* ProgressCallback)
675
+HRESULT WSLCSession::PullImage(LPCSTR Image, LPCSTR RegistryAuthenticationInformation, IProgressCallback* ProgressCallback, IWarningCallback* WarningCallback)
676
try
677
{
666
- COMServiceExecutionContext context;
678
+ WSLCExecutionContext context(this, WarningCallback);
679
680
RETURN_HR_IF_NULL(E_POINTER, Image);
681
@@ -697,7 +709,7 @@ CATCH_RETURN();
709
HRESULT WSLCSession::BuildImage(const WSLCBuildImageOptions* Options, IProgressCallback* ProgressCallback, HANDLE CancelEvent)
710
try
711
{
700
- COMServiceExecutionContext context;
712
+ WSLCExecutionContext context(this);
713
714
RETURN_HR_IF_NULL(E_POINTER, Options);
715
RETURN_HR_IF_NULL(E_POINTER, Options->ContextPath);
@@ -1030,12 +1042,12 @@ try
1042
}
1043
CATCH_RETURN();
1044
1033
-HRESULT WSLCSession::LoadImage(const WSLCHandle ImageHandle, IProgressCallback* ProgressCallback, ULONGLONG ContentSize)
1045
+HRESULT WSLCSession::LoadImage(const WSLCHandle ImageHandle, IProgressCallback* ProgressCallback, ULONGLONG ContentSize, IWarningCallback* WarningCallback)
1046
try
1047
{
1048
UNREFERENCED_PARAMETER(ProgressCallback);
1049
1038
- COMServiceExecutionContext context;
1050
+ WSLCExecutionContext context(this, WarningCallback);
1051
1052
auto lock = m_lock.lock_shared();
1053
@@ -1049,12 +1061,12 @@ try
1061
}
1062
CATCH_RETURN();
1063
1052
-HRESULT WSLCSession::ImportImage(const WSLCHandle ImageHandle, LPCSTR ImageName, IProgressCallback* ProgressCallback, ULONGLONG ContentSize)
1064
+HRESULT WSLCSession::ImportImage(const WSLCHandle ImageHandle, LPCSTR ImageName, IProgressCallback* ProgressCallback, ULONGLONG ContentSize, IWarningCallback* WarningCallback)
1065
try
1066
{
1067
UNREFERENCED_PARAMETER(ProgressCallback);
1068
1057
- COMServiceExecutionContext context;
1069
+ WSLCExecutionContext context(this, WarningCallback);
1070
1071
RETURN_HR_IF_NULL(E_POINTER, ImageName);
1072
RETURN_HR_IF(E_INVALIDARG, strlen(ImageName) > WSLC_MAX_IMAGE_NAME_LENGTH);
@@ -1122,6 +1134,7 @@ void WSLCSession::ImportImageImpl(DockerHTTPClient::HTTPRequestContext& Request,
1134
"Overriding previous error message '%hs' with new message '%hs'",
1135
errorMessage->c_str(),
1136
parsed.errorDetail->message.c_str());
1137
+ EMIT_USER_WARNING(wsl::shared::string::MultiByteToWide(*errorMessage));
1138
}
1139
1140
errorMessage = std::move(parsed.errorDetail->message);
@@ -1130,9 +1143,14 @@ void WSLCSession::ImportImageImpl(DockerHTTPClient::HTTPRequestContext& Request,
1143
{
1144
WSL_LOG("ImageImportProgress", TraceLoggingValue(parsed.stream->c_str(), "Content"));
1145
}
1146
+ else if (parsed.status.has_value())
1147
+ {
1148
+ WSL_LOG("ImageImportProgress", TraceLoggingValue(parsed.status->c_str(), "Status"));
1149
+ }
1150
else
1151
{
1152
LOG_HR_MSG(E_UNEXPECTED, "Failed to parse import progress: %.*hs", static_cast<int>(buffer.size()), buffer.data());
1153
+ EMIT_USER_WARNING(Localization::MessageWslcImportProgressParseFailed());
1154
}
1155
};
1156
@@ -1168,7 +1186,7 @@ try
1186
{
1187
UNREFERENCED_PARAMETER(ProgressCallback);
1188
1171
- COMServiceExecutionContext context;
1189
+ WSLCExecutionContext context(this);
1190
1191
RETURN_HR_IF_NULL(E_POINTER, ImageNameOrID);
1192
RETURN_HR_IF(E_INVALIDARG, strlen(ImageNameOrID) > WSLC_MAX_IMAGE_NAME_LENGTH);
@@ -1224,7 +1242,7 @@ void WSLCSession::SaveImageImpl(std::pair<uint32_t, wil::unique_socket>& SocketC
1242
HRESULT WSLCSession::ListImages(const WSLCListImagesOptions* Options, WSLCImageInformation** Images, ULONG* Count)
1243
try
1244
{
1227
- COMServiceExecutionContext context;
1245
+ WSLCExecutionContext context(this);
1246
1247
RETURN_HR_IF_NULL(E_POINTER, Images);
1248
RETURN_HR_IF_NULL(E_POINTER, Count);
@@ -1343,7 +1361,7 @@ CATCH_RETURN();
1361
HRESULT WSLCSession::DeleteImage(const WSLCDeleteImageOptions* Options, WSLCDeletedImageInformation** DeletedImages, ULONG* Count)
1362
try
1363
{
1346
- COMServiceExecutionContext context;
1364
+ WSLCExecutionContext context(this);
1365
1366
RETURN_HR_IF_NULL(E_POINTER, Options);
1367
RETURN_HR_IF_NULL(E_POINTER, Options->Image);
@@ -1424,7 +1442,7 @@ CATCH_RETURN();
1442
HRESULT WSLCSession::TagImage(const WSLCTagImageOptions* Options)
1443
try
1444
{
1427
- COMServiceExecutionContext context;
1445
+ WSLCExecutionContext context(this);
1446
1447
RETURN_HR_IF_NULL(E_POINTER, Options);
1448
RETURN_HR_IF_NULL(E_POINTER, Options->Image);
@@ -1459,10 +1477,10 @@ try
1477
}
1478
CATCH_RETURN();
1479
1462
-HRESULT WSLCSession::PushImage(LPCSTR Image, LPCSTR RegistryAuthenticationInformation, IProgressCallback* ProgressCallback)
1480
+HRESULT WSLCSession::PushImage(LPCSTR Image, LPCSTR RegistryAuthenticationInformation, IProgressCallback* ProgressCallback, IWarningCallback* WarningCallback)
1481
try
1482
{
1465
- COMServiceExecutionContext context;
1483
+ WSLCExecutionContext context(this, WarningCallback);
1484
1485
RETURN_HR_IF_NULL(E_POINTER, Image);
1486
RETURN_HR_IF_NULL(E_POINTER, RegistryAuthenticationInformation);
@@ -1483,7 +1501,7 @@ CATCH_RETURN();
1501
HRESULT WSLCSession::InspectImage(_In_ LPCSTR ImageNameOrId, _Out_ LPSTR* Output)
1502
try
1503
{
1486
- COMServiceExecutionContext context;
1504
+ WSLCExecutionContext context(this);
1505
1506
RETURN_HR_IF_NULL(E_POINTER, ImageNameOrId);
1507
RETURN_HR_IF(E_INVALIDARG, strlen(ImageNameOrId) > WSLC_MAX_IMAGE_NAME_LENGTH);
@@ -1530,7 +1548,7 @@ std::string WSLCSession::InspectImageLockHeld(const std::string& NameOrId)
1548
HRESULT WSLCSession::Authenticate(_In_ LPCSTR ServerAddress, _In_ LPCSTR Username, _In_ LPCSTR Password, _Out_ LPSTR* IdentityToken)
1549
try
1550
{
1533
- COMServiceExecutionContext context;
1551
+ WSLCExecutionContext context(this);
1552
1553
RETURN_HR_IF_NULL(E_POINTER, ServerAddress);
1554
RETURN_HR_IF_NULL(E_POINTER, Username);
@@ -1560,7 +1578,7 @@ HRESULT WSLCSession::PruneImages(
1578
const WSLCFilter* Filters, ULONG FiltersCount, WSLCDeletedImageInformation** DeletedImages, ULONG* DeletedImagesCount, ULONGLONG* SpaceReclaimed)
1579
try
1580
{
1563
- COMServiceExecutionContext context;
1581
+ WSLCExecutionContext context(this);
1582
1583
RETURN_HR_IF_NULL(E_POINTER, DeletedImages);
1584
RETURN_HR_IF_NULL(E_POINTER, DeletedImagesCount);
@@ -1614,10 +1632,10 @@ try
1632
}
1633
CATCH_RETURN();
1634
1617
-HRESULT WSLCSession::CreateContainer(const WSLCContainerOptions* containerOptions, IWSLCContainer** Container)
1635
+HRESULT WSLCSession::CreateContainer(const WSLCContainerOptions* containerOptions, IWarningCallback* WarningCallback, IWSLCContainer** Container)
1636
try
1637
{
1620
- COMServiceExecutionContext context;
1638
+ WSLCExecutionContext context(this, WarningCallback);
1639
1640
RETURN_HR_IF_NULL(E_POINTER, containerOptions);
1641
@@ -1711,7 +1729,7 @@ CATCH_RETURN();
1729
HRESULT WSLCSession::OpenContainer(LPCSTR Id, IWSLCContainer** Container)
1730
try
1731
{
1714
- COMServiceExecutionContext context;
1732
+ WSLCExecutionContext context(this);
1733
1734
ValidateName(Id, WSLC_MAX_CONTAINER_NAME_LENGTH);
1735
@@ -1759,7 +1777,7 @@ HRESULT WSLCSession::ListContainers(
1777
const WSLCListContainersOptions* Options, WSLCContainerEntry** Containers, ULONG* Count, WSLCContainerPortMapping** Ports, ULONG* PortsCount)
1778
try
1779
{
1762
- COMServiceExecutionContext context;
1780
+ WSLCExecutionContext context(this);
1781
1782
RETURN_HR_IF_NULL(E_POINTER, Containers);
1783
RETURN_HR_IF_NULL(E_POINTER, Count);
@@ -1861,7 +1879,7 @@ CATCH_RETURN();
1879
HRESULT WSLCSession::PruneContainers(_In_opt_ const WSLCFilter* Filters, _In_ ULONG FiltersCount, _Out_ WSLCPruneContainersResults* Result)
1880
try
1881
{
1864
- COMServiceExecutionContext context;
1882
+ WSLCExecutionContext context(this);
1883
1884
RETURN_HR_IF_NULL(E_POINTER, Result);
1885
ZeroMemory(Result, sizeof(*Result));
@@ -1926,7 +1944,7 @@ CATCH_RETURN();
1944
HRESULT WSLCSession::CreateRootNamespaceProcess(LPCSTR Executable, const WSLCProcessOptions* Options, IWSLCProcess** Process, int* Errno)
1945
try
1946
{
1929
- COMServiceExecutionContext context;
1947
+ WSLCExecutionContext context(this);
1948
1949
if (Errno != nullptr)
1950
{
@@ -1955,7 +1973,7 @@ void WSLCSession::Ext4Format(const std::string& Device)
1973
HRESULT WSLCSession::FormatVirtualDisk(LPCWSTR Path)
1974
try
1975
{
1958
- COMServiceExecutionContext context;
1976
+ WSLCExecutionContext context(this);
1977
1978
THROW_HR_WITH_USER_ERROR_IF(E_INVALIDARG, Localization::MessagePathNotAbsolute(Path), !std::filesystem::path(Path).is_absolute());
1979
@@ -1978,7 +1996,7 @@ CATCH_RETURN();
1996
HRESULT WSLCSession::CreateVolume(const WSLCVolumeOptions* Options, WSLCVolumeInformation* VolumeInfo)
1997
try
1998
{
1981
- COMServiceExecutionContext context;
1999
+ WSLCExecutionContext context(this);
2000
2001
RETURN_HR_IF_NULL(E_POINTER, Options);
2002
RETURN_HR_IF_NULL(E_POINTER, VolumeInfo);
@@ -2003,7 +2021,7 @@ CATCH_RETURN();
2021
HRESULT WSLCSession::DeleteVolume(LPCSTR Name)
2022
try
2023
{
2006
- COMServiceExecutionContext context;
2024
+ WSLCExecutionContext context(this);
2025
2026
RETURN_HR_IF_NULL(E_POINTER, Name);
2027
@@ -2018,7 +2036,7 @@ CATCH_RETURN();
2036
HRESULT WSLCSession::ListVolumes(const WSLCFilter* Filters, ULONG FiltersCount, WSLCVolumeInformation** Volumes, ULONG* Count)
2037
try
2038
{
2021
- COMServiceExecutionContext context;
2039
+ WSLCExecutionContext context(this);
2040
2041
RETURN_HR_IF_NULL(E_POINTER, Volumes);
2042
RETURN_HR_IF_NULL(E_POINTER, Count);
@@ -2050,7 +2068,7 @@ CATCH_RETURN();
2068
HRESULT WSLCSession::InspectVolume(LPCSTR Name, LPSTR* Output)
2069
try
2070
{
2053
- COMServiceExecutionContext context;
2071
+ WSLCExecutionContext context(this);
2072
2073
RETURN_HR_IF_NULL(E_POINTER, Name);
2074
RETURN_HR_IF_NULL(E_POINTER, Output);
@@ -2070,10 +2088,11 @@ try
2088
}
2089
CATCH_RETURN();
2090
2073
-HRESULT WSLCSession::PruneVolumes(const WSLCFilter* Filters, ULONG FiltersCount, WSLCVolumeName** Volumes, ULONG* VolumesCount, ULONGLONG* SpaceReclaimed)
2091
+HRESULT WSLCSession::PruneVolumes(
2092
+ const WSLCFilter* Filters, ULONG FiltersCount, IWarningCallback* WarningCallback, WSLCVolumeName** Volumes, ULONG* VolumesCount, ULONGLONG* SpaceReclaimed)
2093
try
2094
{
2076
- COMServiceExecutionContext context;
2095
+ WSLCExecutionContext context(this, WarningCallback);
2096
2097
RETURN_HR_IF_NULL(E_POINTER, Volumes);
2098
RETURN_HR_IF_NULL(E_POINTER, VolumesCount);
@@ -2144,10 +2163,10 @@ int WSLCSession::StopProcess(ServiceRunningProcess& Process, DWORD TerminateTime
2163
}
2164
// Network management.
2165
2147
-HRESULT WSLCSession::CreateNetwork(const WSLCNetworkOptions* Options)
2166
+HRESULT WSLCSession::CreateNetwork(const WSLCNetworkOptions* Options, IWarningCallback* WarningCallback)
2167
try
2168
{
2150
- COMServiceExecutionContext context;
2169
+ WSLCExecutionContext context(this, WarningCallback);
2170
2171
RETURN_HR_IF_NULL(E_POINTER, Options);
2172
RETURN_HR_IF_NULL(E_POINTER, Options->Name);
@@ -2198,9 +2217,10 @@ try
2217
ipam.Config.emplace().push_back(std::move(ipamConfig));
2218
}
2219
2220
+ docker_schema::CreateNetworkResponse createResult;
2221
try
2222
{
2203
- m_dockerClient->CreateNetwork(request);
2223
+ createResult = m_dockerClient->CreateNetwork(request);
2224
}
2225
catch (const DockerHTTPException& e)
2226
{
@@ -2209,6 +2229,11 @@ try
2229
THROW_DOCKER_USER_ERROR_MSG(e, "Failed to create network '%hs'", name.c_str());
2230
}
2231
2232
+ if (!createResult.Warning.empty())
2233
+ {
2234
+ EMIT_USER_WARNING(wsl::shared::string::MultiByteToWide(createResult.Warning));
2235
+ }
2236
+
2237
auto removeNetworkCleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [this, &name]() { m_dockerClient->RemoveNetwork(name); });
2238
2239
// Inspect the newly created network to cache full properties (IPAM, Scope, etc.)
@@ -2253,7 +2278,7 @@ CATCH_RETURN();
2278
HRESULT WSLCSession::DeleteNetwork(LPCSTR Name)
2279
try
2280
{
2256
- COMServiceExecutionContext context;
2281
+ WSLCExecutionContext context(this);
2282
2283
RETURN_HR_IF_NULL(E_POINTER, Name);
2284
std::string name = Name;
@@ -2291,7 +2316,7 @@ CATCH_RETURN();
2316
HRESULT WSLCSession::ListNetworks(WSLCNetworkInformation** Networks, ULONG* Count)
2317
try
2318
{
2294
- COMServiceExecutionContext context;
2319
+ WSLCExecutionContext context(this);
2320
2321
RETURN_HR_IF_NULL(E_POINTER, Networks);
2322
RETURN_HR_IF_NULL(E_POINTER, Count);
@@ -2328,7 +2353,7 @@ CATCH_RETURN();
2353
HRESULT WSLCSession::InspectNetwork(LPCSTR Name, LPSTR* Output)
2354
try
2355
{
2331
- COMServiceExecutionContext context;
2356
+ WSLCExecutionContext context(this);
2357
2358
RETURN_HR_IF_NULL(E_POINTER, Name);
2359
RETURN_HR_IF_NULL(E_POINTER, Output);
@@ -2528,7 +2553,7 @@ CATCH_RETURN();
2553
HRESULT WSLCSession::MountWindowsFolder(LPCWSTR WindowsPath, LPCSTR LinuxPath, BOOL ReadOnly)
2554
try
2555
{
2531
- COMServiceExecutionContext context;
2556
+ WSLCExecutionContext context(this);
2557
2558
auto lock = m_lock.lock_shared();
2559
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_virtualMachine);
@@ -2540,7 +2565,7 @@ CATCH_RETURN();
2565
HRESULT WSLCSession::UnmountWindowsFolder(LPCSTR LinuxPath)
2566
try
2567
{
2543
- COMServiceExecutionContext context;
2568
+ WSLCExecutionContext context(this);
2569
2570
auto lock = m_lock.lock_shared();
2571
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_virtualMachine);
@@ -2552,7 +2577,7 @@ CATCH_RETURN();
2577
HRESULT WSLCSession::MapVmPort(int Family, unsigned short WindowsPort, unsigned short LinuxPort)
2578
try
2579
{
2555
- COMServiceExecutionContext context;
2580
+ WSLCExecutionContext context(this);
2581
2582
auto lock = m_lock.lock_shared();
2583
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_virtualMachine);
@@ -2598,7 +2623,7 @@ CATCH_RETURN();
2623
HRESULT WSLCSession::UnmapVmPort(int Family, unsigned short WindowsPort, unsigned short LinuxPort)
2624
try
2625
{
2601
- COMServiceExecutionContext context;
2626
+ WSLCExecutionContext context(this);
2627
2628
auto lock = m_lock.lock_shared();
2629
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_virtualMachine);
@@ -2705,8 +2730,18 @@ UserCOMCallback WSLCSession::RegisterUserCOMCallback()
2730
2731
THROW_IF_FAILED(CoEnableCallCancellation(nullptr));
2732
2708
- auto [_, inserted] = m_userCOMCallbackThreads.insert(GetCurrentThreadId());
2709
- WI_VERIFY(inserted);
2733
+ auto threadId = GetCurrentThreadId();
2734
+ auto it = m_userCOMCallbackThreads.find(threadId);
2735
+ WI_VERIFY(it == m_userCOMCallbackThreads.end() || it->second > 0);
2736
+
2737
+ if (it == m_userCOMCallbackThreads.end())
2738
+ {
2739
+ m_userCOMCallbackThreads.insert({threadId, 1});
2740
+ }
2741
+ else
2742
+ {
2743
+ it->second++;
2744
+ }
2745
2746
return UserCOMCallback{*this};
2747
}
@@ -2716,14 +2751,21 @@ void WSLCSession::UnregisterUserCOMCallback(DWORD ThreadId)
2751
std::lock_guard lock(m_userCOMCallbacksLock);
2752
2753
auto it = m_userCOMCallbackThreads.find(ThreadId);
2719
- WI_VERIFY(it != m_userCOMCallbackThreads.end());
2754
+ WI_VERIFY(it != m_userCOMCallbackThreads.end() && it->second > 0);
2755
2721
- m_userCOMCallbackThreads.erase(it);
2756
+ if (it->second > 1)
2757
+ {
2758
+ it->second--;
2759
+ }
2760
+ else
2761
+ {
2762
+ m_userCOMCallbackThreads.erase(it);
2763
+ }
2764
}
2765
2766
void WSLCSession::CancelUserCOMCallbacks()
2767
{
2726
- for (auto threadId : m_userCOMCallbackThreads)
2768
+ for (auto threadId : std::views::keys(m_userCOMCallbackThreads))
2769
{
2770
LOG_IF_FAILED(CoCancelCall(threadId, 0));
2771
}
@@ -2771,8 +2813,9 @@ void WSLCSession::RecoverExistingContainers()
2813
}
2814
catch (...)
2815
{
2774
- // Log but don't fail the session startup if a single container fails to recover.
2816
LOG_CAUGHT_EXCEPTION_MSG("Failed to recover container: %hs", dockerContainer.Id.c_str());
2817
+ EMIT_USER_WARNING(
2818
+ Localization::MessageWslcFailedToRecoverContainer(wsl::shared::string::MultiByteToWide(dockerContainer.Id)));
2819
}
2820
}
2821
@@ -2821,7 +2864,11 @@ void WSLCSession::RecoverExistingNetworks()
2864
auto [_, inserted] = m_networks.insert({network.Name, std::move(entry)});
2865
WI_VERIFY(inserted);
2866
}
2824
- CATCH_LOG_MSG("Failed to recover network: %hs", network.Name.c_str());
2867
+ catch (...)
2868
+ {
2869
+ LOG_CAUGHT_EXCEPTION_MSG("Failed to recover network: %hs", network.Name.c_str());
2870
+ EMIT_USER_WARNING(Localization::MessageWslcFailedToRecoverNetwork(wsl::shared::string::MultiByteToWide(network.Name)));
2871
+ }
2872
}
2873
2874
WSL_LOG(
src/windows/wslcsession/WSLCSession.h
+26
-8
@@ -85,21 +85,38 @@ public:
85
86
// IWSLCSession - initialization methods
87
IFACEMETHOD(GetProcessHandle)(_Out_ HANDLE* ProcessHandle) override;
88
- IFACEMETHOD(Initialize)(_In_ const WSLCSessionInitSettings* Settings, _In_ IWSLCVirtualMachine* Vm, _In_ IWSLCPluginNotifier* PluginNotifier) override;
88
+ IFACEMETHOD(Initialize)(
89
+ _In_ const WSLCSessionInitSettings* Settings,
90
+ _In_ IWSLCVirtualMachine* Vm,
91
+ _In_ IWSLCPluginNotifier* PluginNotifier,
92
+ _In_opt_ IWarningCallback* WarningCallback) override;
93
94
IFACEMETHOD(GetId)(_Out_ ULONG* Id) override;
95
IFACEMETHOD(GetState)(_Out_ WSLCSessionState* State) override;
96
97
// Image management.
94
- IFACEMETHOD(PullImage)(_In_ LPCSTR Image, _In_opt_ LPCSTR RegistryAuthenticationInformation, _In_opt_ IProgressCallback* ProgressCallback) override;
98
+ IFACEMETHOD(PullImage)(
99
+ _In_ LPCSTR Image,
100
+ _In_opt_ LPCSTR RegistryAuthenticationInformation,
101
+ _In_opt_ IProgressCallback* ProgressCallback,
102
+ _In_opt_ IWarningCallback* WarningCallback) override;
103
IFACEMETHOD(BuildImage)(_In_ const WSLCBuildImageOptions* Options, _In_opt_ IProgressCallback* ProgressCallback, _In_opt_ HANDLE CancelEvent) override;
96
- IFACEMETHOD(LoadImage)(_In_ const WSLCHandle ImageHandle, _In_ IProgressCallback* ProgressCallback, _In_ ULONGLONG ContentLength) override;
97
- IFACEMETHOD(ImportImage)(_In_ const WSLCHandle ImageHandle, _In_ LPCSTR ImageName, _In_ IProgressCallback* ProgressCallback, _In_ ULONGLONG ContentLength) override;
104
+ IFACEMETHOD(LoadImage)(_In_ const WSLCHandle ImageHandle, _In_ IProgressCallback* ProgressCallback, _In_ ULONGLONG ContentLength, _In_opt_ IWarningCallback* WarningCallback) override;
105
+ IFACEMETHOD(ImportImage)(
106
+ _In_ const WSLCHandle ImageHandle,
107
+ _In_ LPCSTR ImageName,
108
+ _In_ IProgressCallback* ProgressCallback,
109
+ _In_ ULONGLONG ContentLength,
110
+ _In_opt_ IWarningCallback* WarningCallback) override;
111
IFACEMETHOD(SaveImage)(_In_ WSLCHandle OutputHandle, _In_ LPCSTR ImageNameOrID, _In_ IProgressCallback* ProgressCallback, _In_opt_ HANDLE CancelEvent) override;
112
IFACEMETHOD(ListImages)(_In_opt_ const WSLCListImagesOptions* Options, _Out_ WSLCImageInformation** Images, _Out_ ULONG* Count) override;
113
IFACEMETHOD(DeleteImage)(_In_ const WSLCDeleteImageOptions* Options, _Out_ WSLCDeletedImageInformation** DeletedImages, _Out_ ULONG* Count) override;
114
IFACEMETHOD(TagImage)(_In_ const WSLCTagImageOptions* Options) override;
102
- IFACEMETHOD(PushImage)(_In_ LPCSTR Image, _In_ LPCSTR RegistryAuthenticationInformation, _In_opt_ IProgressCallback* ProgressCallback) override;
115
+ IFACEMETHOD(PushImage)(
116
+ _In_ LPCSTR Image,
117
+ _In_ LPCSTR RegistryAuthenticationInformation,
118
+ _In_opt_ IProgressCallback* ProgressCallback,
119
+ _In_opt_ IWarningCallback* WarningCallback) override;
120
IFACEMETHOD(InspectImage)(_In_ LPCSTR ImageNameOrId, _Out_ LPSTR* Output) override;
121
IFACEMETHOD(Authenticate)(_In_ LPCSTR ServerAddress, _In_ LPCSTR Username, _In_ LPCSTR Password, _Out_ LPSTR* IdentityToken) override;
122
IFACEMETHOD(PruneImages)(
@@ -110,7 +127,7 @@ public:
127
_Out_ ULONGLONG* SpaceReclaimed) override;
128
129
// Container management.
113
- IFACEMETHOD(CreateContainer)(_In_ const WSLCContainerOptions* Options, _Out_ IWSLCContainer** Container) override;
130
+ IFACEMETHOD(CreateContainer)(_In_ const WSLCContainerOptions* Options, _In_opt_ IWarningCallback* WarningCallback, _Out_ IWSLCContainer** Container) override;
131
IFACEMETHOD(OpenContainer)(_In_ LPCSTR Id, _In_ IWSLCContainer** Container) override;
132
IFACEMETHOD(ListContainers)(
133
_In_opt_ const WSLCListContainersOptions* Options,
@@ -137,12 +154,13 @@ public:
154
IFACEMETHOD(PruneVolumes)
155
(_In_reads_opt_(FiltersCount) const WSLCFilter* Filters,
156
_In_ ULONG FiltersCount,
157
+ _In_opt_ IWarningCallback* WarningCallback,
158
_Out_ WSLCVolumeName** Volumes,
159
_Out_ ULONG* VolumesCount,
160
_Out_ ULONGLONG* SpaceReclaimed) override;
161
162
// Network management.
145
- IFACEMETHOD(CreateNetwork)(_In_ const WSLCNetworkOptions* Options) override;
163
+ IFACEMETHOD(CreateNetwork)(_In_ const WSLCNetworkOptions* Options, _In_opt_ IWarningCallback* WarningCallback) override;
164
IFACEMETHOD(DeleteNetwork)(_In_ LPCSTR Name) override;
165
IFACEMETHOD(ListNetworks)(_Out_ WSLCNetworkInformation** Networks, _Out_ ULONG* Count) override;
166
IFACEMETHOD(InspectNetwork)(_In_ LPCSTR Name, _Out_ LPSTR* Output) override;
@@ -247,7 +265,7 @@ private:
265
266
// Threads currently inside an outgoing COM callback (e.g. IProgressCallback::OnProgress).
267
std::recursive_mutex m_userCOMCallbacksLock;
250
- __guarded_by(m_userCOMCallbacksLock) std::set<DWORD> m_userCOMCallbackThreads;
268
+ __guarded_by(m_userCOMCallbacksLock) std::map<DWORD, int> m_userCOMCallbackThreads;
269
270
// Used for testing only.
271
std::mutex m_allocatedPortsLock;
src/windows/wslcsession/WSLCSessionFactory.cpp
+2
-1
@@ -33,6 +33,7 @@ HRESULT wslc::WSLCSessionFactory::CreateSession(
33
_In_ const WSLCSessionInitSettings* Settings,
34
_In_ IWSLCVirtualMachine* Vm,
35
_In_ IWSLCPluginNotifier* PluginNotifier,
36
+ _In_opt_ IWarningCallback* WarningCallback,
37
_Out_ IWSLCSession** Session,
38
_Out_ IWSLCSessionReference** ServiceRef)
39
try
@@ -48,7 +49,7 @@ try
49
session->SetDestructionCallback(std::move(m_destructionCallback));
50
51
// Initialize the session with the VM.
51
- RETURN_IF_FAILED(session->Initialize(Settings, Vm, PluginNotifier));
52
+ RETURN_IF_FAILED(session->Initialize(Settings, Vm, PluginNotifier, WarningCallback));
53
54
// Create the service session ref. It extracts metadata and a weak reference from the session.
55
auto serviceRef = Microsoft::WRL::Make<wslc::WSLCSessionReference>(session.Get());
src/windows/wslcsession/WSLCSessionFactory.h
+1
@@ -47,6 +47,7 @@ public:
47
(_In_ const WSLCSessionInitSettings* Settings,
48
_In_ IWSLCVirtualMachine* Vm,
49
_In_ IWSLCPluginNotifier* PluginNotifier,
50
+ _In_opt_ IWarningCallback* WarningCallback,
51
_Out_ IWSLCSession** Session,
52
_Out_ IWSLCSessionReference** ServiceRef) override;
53
src/windows/wslcsession/WSLCVolumes.cpp
+11
-2
@@ -41,7 +41,12 @@ WSLCVolumes::WSLCVolumes(
41
{
42
OpenVolumeExclusiveLockHeld(volume);
43
}
44
- CATCH_LOG_MSG("Failed to recover volume: %hs", volume.Name.c_str());
44
+ catch (...)
45
+ {
46
+ LOG_CAUGHT_EXCEPTION_MSG("Failed to recover volume: %hs", volume.Name.c_str());
47
+ EMIT_USER_WARNING(
48
+ wsl::shared::Localization::MessageWslcFailedToRecoverVolume(wsl::shared::string::MultiByteToWide(volume.Name)));
49
+ }
50
}
51
}
52
@@ -240,7 +245,11 @@ WSLCVolumes::PruneVolumesResult WSLCVolumes::PruneVolumes(const std::map<std::st
245
{
246
it->second->OnDeleted();
247
}
243
- CATCH_LOG_MSG("Failed to release host resources for pruned volume: %hs", name.c_str());
248
+ catch (...)
249
+ {
250
+ LOG_CAUGHT_EXCEPTION_MSG("Failed to release host resources for pruned volume: %hs", name.c_str());
251
+ EMIT_USER_WARNING(wsl::shared::Localization::MessageWslcVolumeReleaseFailed(wsl::shared::string::MultiByteToWide(name)));
252
+ }
253
254
m_volumes.erase(it);
255
m_expectedEvents.emplace_back(name, VolumeEvent::Destroy);
test/windows/Common.cpp
+1
-1
@@ -2914,7 +2914,7 @@ void LoadTestImage(IWSLCSession& session, std::string_view imageName)
2914
LARGE_INTEGER fileSize{};
2915
THROW_LAST_ERROR_IF(!GetFileSizeEx(imageFile.get(), &fileSize));
2916
2917
- THROW_IF_FAILED(session.LoadImage(wsl::windows::common::wslutil::ToCOMInputHandle(imageFile.get()), nullptr, fileSize.QuadPart));
2917
+ THROW_IF_FAILED(session.LoadImage(wsl::windows::common::wslutil::ToCOMInputHandle(imageFile.get()), nullptr, fileSize.QuadPart, nullptr));
2918
}
2919
2920
void ExpectHttpResponse(LPCWSTR Url, std::optional<int> expectedCode, bool retry)
test/windows/PluginTests.cpp
+4
-4
@@ -617,7 +617,7 @@ class PluginTests
617
618
auto manager = OpenWslcSessionManager();
619
wil::com_ptr<IWSLCSession> session;
620
- VERIFY_SUCCEEDED(manager->CreateSession(&settings, WSLCSessionFlagsNone, &session));
620
+ VERIFY_SUCCEEDED(manager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, &session));
621
wsl::windows::common::security::ConfigureForCOMImpersonation(session.get());
622
623
WSLCSessionState state{};
@@ -704,7 +704,7 @@ class PluginTests
704
VERIFY_SUCCEEDED(session->TagImage(&tagOptions));
705
706
auto emptyAuth = wsl::windows::common::wslutil::BuildRegistryAuthHeader("", "");
707
- VERIFY_SUCCEEDED(session->PushImage(registryImage.c_str(), emptyAuth.c_str(), nullptr));
707
+ VERIFY_SUCCEEDED(session->PushImage(registryImage.c_str(), emptyAuth.c_str(), nullptr, nullptr));
708
709
// Delete the local tagged copy so PullImage actually downloads it.
710
WSLCDeleteImageOptions deleteOpts{.Image = registryImage.c_str(), .Flags = WSLCDeleteImageFlagsNone};
@@ -712,7 +712,7 @@ class PluginTests
712
VERIFY_SUCCEEDED(session->DeleteImage(&deleteOpts, deletedImages.addressof(), deletedImages.size_address<ULONG>()));
713
714
// Pull the image back — this should trigger the ImageCreated plugin callback.
715
- VERIFY_SUCCEEDED(session->PullImage(registryImage.c_str(), nullptr, nullptr));
715
+ VERIFY_SUCCEEDED(session->PullImage(registryImage.c_str(), nullptr, nullptr, nullptr));
716
}
717
718
constexpr auto ExpectedOutput =
@@ -739,7 +739,7 @@ class PluginTests
739
740
auto manager = OpenWslcSessionManager();
741
wil::com_ptr<IWSLCSession> session;
742
- const auto hr = manager->CreateSession(&settings, WSLCSessionFlagsNone, &session);
742
+ const auto hr = manager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, &session);
743
ValidateCOMErrorMessageContains(L"A fatal error was returned by plugin 'TestPlugin'");
744
VERIFY_ARE_EQUAL(hr, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
745
test/windows/WSLCTests.cpp
+255
-102
@@ -153,7 +153,7 @@ class WSLCTests
153
154
wil::com_ptr<IWSLCSession> session;
155
156
- VERIFY_SUCCEEDED(sessionManager->CreateSession(&sessionSettings, Flags, &session));
156
+ VERIFY_SUCCEEDED(sessionManager->CreateSession(&sessionSettings, Flags, nullptr, &session));
157
wsl::windows::common::security::ConfigureForCOMImpersonation(session.get());
158
159
WSLCSessionState state{};
@@ -214,7 +214,7 @@ class WSLCTests
214
auto cleanup = wil::scope_exit_log(
215
WI_DIAGNOSTICS_INFO, [&]() { LOG_IF_FAILED(DeleteImageNoThrow(registryImage, WSLCDeleteImageFlagsNone).first); });
216
217
- VERIFY_SUCCEEDED(m_defaultSession->PushImage(registryImage.c_str(), registryAuth.c_str(), nullptr));
217
+ VERIFY_SUCCEEDED(m_defaultSession->PushImage(registryImage.c_str(), registryAuth.c_str(), nullptr, nullptr));
218
219
return registryImage;
220
}
@@ -416,7 +416,7 @@ class WSLCTests
416
{
417
auto settings = GetDefaultSessionSettings(nullptr);
418
wil::com_ptr<IWSLCSession> session;
419
- VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, &session), WSLC_E_INVALID_SESSION_NAME);
419
+ VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, &session), WSLC_E_INVALID_SESSION_NAME);
420
}
421
422
// Reject DisplayName at exact boundary (no room for null terminator).
@@ -424,7 +424,7 @@ class WSLCTests
424
std::wstring boundaryName(std::size(WSLCSessionListEntry{}.DisplayName), L'x');
425
auto settings = GetDefaultSessionSettings(boundaryName.c_str());
426
wil::com_ptr<IWSLCSession> session;
427
- VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, &session), WSLC_E_INVALID_SESSION_NAME);
427
+ VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, &session), WSLC_E_INVALID_SESSION_NAME);
428
}
429
430
// Reject too long DisplayName.
@@ -432,7 +432,7 @@ class WSLCTests
432
std::wstring longName(std::size(WSLCSessionListEntry{}.DisplayName) + 1, L'x');
433
auto settings = GetDefaultSessionSettings(longName.c_str());
434
wil::com_ptr<IWSLCSession> session;
435
- VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, &session), WSLC_E_INVALID_SESSION_NAME);
435
+ VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, &session), WSLC_E_INVALID_SESSION_NAME);
436
}
437
438
// Validate that creating a session on a non-existing storage fails if WSLCSessionStorageFlagsNoCreate is set.
@@ -441,7 +441,7 @@ class WSLCTests
441
settings.StoragePath = L"C:\\does-not-exist";
442
settings.StorageFlags = WSLCSessionStorageFlagsNoCreate;
443
wil::com_ptr<IWSLCSession> session;
444
- VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, &session), HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND));
444
+ VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, &session), HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND));
445
}
446
447
// Reject invalid storage flags.
@@ -449,7 +449,7 @@ class WSLCTests
449
auto settings = GetDefaultSessionSettings(L"invalid-storage-flags");
450
settings.StorageFlags = static_cast<WSLCSessionStorageFlags>(0x2);
451
wil::com_ptr<IWSLCSession> session;
452
- VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, &session), E_INVALIDARG);
452
+ VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, &session), E_INVALIDARG);
453
}
454
}
455
@@ -592,7 +592,7 @@ class WSLCTests
592
auto image = PushImageToRegistry("hello-world:latest", registryAddress, BuildRegistryAuthHeader("", ""));
593
ExpectImagePresent(*m_defaultSession, image.c_str(), false);
594
595
- VERIFY_SUCCEEDED(m_defaultSession->PullImage(image.c_str(), nullptr, nullptr));
595
+ VERIFY_SUCCEEDED(m_defaultSession->PullImage(image.c_str(), nullptr, nullptr, nullptr));
596
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(DeleteImageNoThrow(image, WSLCDeleteImageFlagsForce).first); });
597
598
// Verify that the image is in the list of images.
@@ -611,7 +611,7 @@ class WSLCTests
611
L"pull access denied for does-not, repository does not exist or may require 'docker login': denied: requested "
612
L"access to the resource is denied";
613
614
- VERIFY_ARE_EQUAL(m_defaultSession->PullImage("does-not:exist", nullptr, nullptr), WSLC_E_IMAGE_NOT_FOUND);
614
+ VERIFY_ARE_EQUAL(m_defaultSession->PullImage("does-not:exist", nullptr, nullptr, nullptr), WSLC_E_IMAGE_NOT_FOUND);
615
ValidateCOMErrorMessage(expectedError.c_str());
616
}
617
@@ -623,7 +623,7 @@ class WSLCTests
623
ResetTestSession(); // Reopen the test session since the session was terminated.
624
});
625
626
- VERIFY_ARE_EQUAL(m_defaultSession->PullImage("hello-world:linux", nullptr, nullptr), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
626
+ VERIFY_ARE_EQUAL(m_defaultSession->PullImage("hello-world:linux", nullptr, nullptr, nullptr), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
627
}
628
}
629
@@ -638,7 +638,7 @@ class WSLCTests
638
auto registryImage = PushImageToRegistry(sourceImage, registryAddress, auth);
639
ExpectImagePresent(*m_defaultSession, registryImage.c_str(), false);
640
641
- VERIFY_SUCCEEDED(m_defaultSession->PullImage(registryImage.c_str(), nullptr, nullptr));
641
+ VERIFY_SUCCEEDED(m_defaultSession->PullImage(registryImage.c_str(), nullptr, nullptr, nullptr));
642
643
auto cleanup =
644
wil::scope_exit([&]() { LOG_IF_FAILED(DeleteImageNoThrow(registryImage, WSLCDeleteImageFlagsForce).first); });
@@ -656,7 +656,7 @@ class WSLCTests
656
SKIP_TEST_UNSTABLE();
657
658
auto validatePull = [&](const std::string& Image, const std::optional<std::string>& ExpectedTag = {}) {
659
- VERIFY_SUCCEEDED(m_defaultSession->PullImage(Image.c_str(), nullptr, nullptr));
659
+ VERIFY_SUCCEEDED(m_defaultSession->PullImage(Image.c_str(), nullptr, nullptr, nullptr));
660
661
auto cleanup = wil::scope_exit(
662
[&]() { LOG_IF_FAILED(DeleteImageNoThrow(ExpectedTag.value_or(Image), WSLCDeleteImageFlagsForce).first); });
@@ -705,7 +705,7 @@ class WSLCTests
705
settings.MemoryMb = 1024;
706
auto session = CreateSession(settings);
707
708
- VERIFY_ARE_EQUAL(session->PullImage("pytorch/pytorch", nullptr, nullptr), E_FAIL);
708
+ VERIFY_ARE_EQUAL(session->PullImage("pytorch/pytorch", nullptr, nullptr, nullptr), E_FAIL);
709
710
ValidateCOMErrorMessageContains(L"no space left on device");
711
}
@@ -717,13 +717,13 @@ class WSLCTests
717
718
// Validate that pushing a non-existent image fails.
719
{
720
- VERIFY_ARE_EQUAL(m_defaultSession->PushImage("does-not-exist:latest", emptyAuth.c_str(), nullptr), E_FAIL);
720
+ VERIFY_ARE_EQUAL(m_defaultSession->PushImage("does-not-exist:latest", emptyAuth.c_str(), nullptr, nullptr), E_FAIL);
721
ValidateCOMErrorMessage(L"An image does not exist locally with the tag: does-not-exist");
722
}
723
724
// Validate passing empty auth string returns an appropriate error.
725
{
726
- VERIFY_ARE_EQUAL(m_defaultSession->PushImage("does-not-exist:latest", "", nullptr), E_INVALIDARG);
726
+ VERIFY_ARE_EQUAL(m_defaultSession->PushImage("does-not-exist:latest", "", nullptr, nullptr), E_INVALIDARG);
727
}
728
729
// Validate that PushImage() returns the appropriate error if the session is terminated.
@@ -731,7 +731,7 @@ class WSLCTests
731
VERIFY_SUCCEEDED(m_defaultSession->Terminate());
732
auto cleanup = wil::scope_exit([&]() { ResetTestSession(); });
733
734
- VERIFY_ARE_EQUAL(m_defaultSession->PushImage("hello-world:latest", emptyAuth.c_str(), nullptr), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
734
+ VERIFY_ARE_EQUAL(m_defaultSession->PushImage("hello-world:latest", emptyAuth.c_str(), nullptr, nullptr), HRESULT_FROM_WIN32(ERROR_INVALID_STATE));
735
}
736
}
737
@@ -753,11 +753,11 @@ class WSLCTests
753
auto image = PushImageToRegistry("hello-world:latest", registryAddress, xRegistryAuth);
754
755
// Pulling without credentials should fail.
756
- VERIFY_ARE_EQUAL(m_defaultSession->PullImage(image.c_str(), nullptr, nullptr), E_FAIL);
756
+ VERIFY_ARE_EQUAL(m_defaultSession->PullImage(image.c_str(), nullptr, nullptr, nullptr), E_FAIL);
757
ValidateCOMErrorMessageContains(L"no basic auth credentials");
758
759
// Pulling with credentials should succeed.
760
- VERIFY_SUCCEEDED(m_defaultSession->PullImage(image.c_str(), xRegistryAuth.c_str(), nullptr));
760
+ VERIFY_SUCCEEDED(m_defaultSession->PullImage(image.c_str(), xRegistryAuth.c_str(), nullptr, nullptr));
761
ExpectImagePresent(*m_defaultSession, image.c_str());
762
}
763
@@ -1116,7 +1116,7 @@ class WSLCTests
1116
LARGE_INTEGER fileSize{};
1117
VERIFY_IS_TRUE(GetFileSizeEx(imageTarFileHandle.get(), &fileSize));
1118
1119
- VERIFY_SUCCEEDED(m_defaultSession->LoadImage(ToCOMInputHandle(imageTarFileHandle.get()), nullptr, fileSize.QuadPart));
1119
+ VERIFY_SUCCEEDED(m_defaultSession->LoadImage(ToCOMInputHandle(imageTarFileHandle.get()), nullptr, fileSize.QuadPart, nullptr));
1120
1121
// Verify that the image is in the list of images.
1122
ExpectImagePresent(*m_defaultSession, "hello-world:latest");
@@ -1137,7 +1137,8 @@ class WSLCTests
1137
auto currentExecutableHandle = wil::open_file(wil::GetModuleFileNameW<std::wstring>().c_str());
1138
VERIFY_IS_TRUE(GetFileSizeEx(currentExecutableHandle.get(), &fileSize));
1139
1140
- VERIFY_ARE_EQUAL(m_defaultSession->LoadImage(ToCOMInputHandle(currentExecutableHandle.get()), nullptr, fileSize.QuadPart), E_FAIL);
1140
+ VERIFY_ARE_EQUAL(
1141
+ m_defaultSession->LoadImage(ToCOMInputHandle(currentExecutableHandle.get()), nullptr, fileSize.QuadPart, nullptr), E_FAIL);
1142
1143
ValidateCOMErrorMessage(L"archive/tar: invalid tar header");
1144
}
@@ -1150,7 +1151,7 @@ class WSLCTests
1151
1152
std::promise<HRESULT> loadResult;
1153
std::thread operationThread([&]() {
1153
- loadResult.set_value(m_defaultSession->LoadImage(ToCOMInputHandle(pipeRead.get()), nullptr, 1024 * 1024));
1154
+ loadResult.set_value(m_defaultSession->LoadImage(ToCOMInputHandle(pipeRead.get()), nullptr, 1024 * 1024, nullptr));
1155
});
1156
1157
auto threadCleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { operationThread.join(); });
@@ -1174,7 +1175,7 @@ class WSLCTests
1175
std::promise<HRESULT> terminateResult;
1176
wil::unique_event testCompleted{wil::EventOptions::ManualReset};
1177
std::thread operationThread([&]() {
1177
- terminateResult.set_value(m_defaultSession->LoadImage(ToCOMInputHandle(pipeRead.get()), nullptr, 1024 * 1024));
1178
+ terminateResult.set_value(m_defaultSession->LoadImage(ToCOMInputHandle(pipeRead.get()), nullptr, 1024 * 1024, nullptr));
1179
WI_ASSERT(testCompleted.is_signaled());
1180
});
1181
@@ -1211,7 +1212,7 @@ class WSLCTests
1212
VERIFY_IS_TRUE(GetFileSizeEx(imageTarFileHandle.get(), &fileSize));
1213
1214
VERIFY_SUCCEEDED(m_defaultSession->ImportImage(
1214
- ToCOMInputHandle(imageTarFileHandle.get()), "my-hello-world:test", nullptr, fileSize.QuadPart));
1215
+ ToCOMInputHandle(imageTarFileHandle.get()), "my-hello-world:test", nullptr, fileSize.QuadPart, nullptr));
1216
1217
ExpectImagePresent(*m_defaultSession, "my-hello-world:test");
1218
@@ -1229,7 +1230,7 @@ class WSLCTests
1230
// Validate that ImportImage fails if no tag is passed
1231
{
1232
VERIFY_ARE_EQUAL(
1232
- m_defaultSession->ImportImage(ToCOMInputHandle(imageTarFileHandle.get()), "my-hello-world", nullptr, fileSize.QuadPart),
1233
+ m_defaultSession->ImportImage(ToCOMInputHandle(imageTarFileHandle.get()), "my-hello-world", nullptr, fileSize.QuadPart, nullptr),
1234
E_INVALIDARG);
1235
}
1236
@@ -1241,7 +1242,7 @@ class WSLCTests
1242
1243
VERIFY_ARE_EQUAL(
1244
m_defaultSession->ImportImage(
1244
- ToCOMInputHandle(currentExecutableHandle.get()), "invalid-image:test", nullptr, fileSize.QuadPart),
1245
+ ToCOMInputHandle(currentExecutableHandle.get()), "invalid-image:test", nullptr, fileSize.QuadPart, nullptr),
1246
E_FAIL);
1247
1248
ValidateCOMErrorMessage(L"archive/tar: invalid tar header");
@@ -1255,7 +1256,8 @@ class WSLCTests
1256
1257
std::promise<HRESULT> importResult;
1258
std::thread operationThread([&]() {
1258
- importResult.set_value(m_defaultSession->ImportImage(ToCOMInputHandle(pipeRead.get()), "broken-read:eof", nullptr, 1024 * 1024));
1259
+ importResult.set_value(
1260
+ m_defaultSession->ImportImage(ToCOMInputHandle(pipeRead.get()), "broken-read:eof", nullptr, 1024 * 1024, nullptr));
1261
});
1262
1263
auto threadCleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { operationThread.join(); });
@@ -1279,8 +1281,8 @@ class WSLCTests
1281
std::promise<HRESULT> terminateResult;
1282
wil::unique_event testCompleted{wil::EventOptions::ManualReset};
1283
std::thread operationThread([&]() {
1282
- terminateResult.set_value(
1283
- m_defaultSession->ImportImage(ToCOMInputHandle(pipeRead.get()), "session-terminate:test", nullptr, 1024 * 1024));
1284
+ terminateResult.set_value(m_defaultSession->ImportImage(
1285
+ ToCOMInputHandle(pipeRead.get()), "session-terminate:test", nullptr, 1024 * 1024, nullptr));
1286
WI_ASSERT(testCompleted.is_signaled());
1287
});
1288
@@ -2057,7 +2059,7 @@ class WSLCTests
2059
auto volumeCleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
2060
wil::unique_cotaskmem_array_ptr<WSLCVolumeName> deleted;
2061
ULONGLONG spaceReclaimed = 0;
2060
- LOG_IF_FAILED(m_defaultSession->PruneVolumes(nullptr, 0, deleted.addressof(), deleted.size_address<ULONG>(), &spaceReclaimed));
2062
+ LOG_IF_FAILED(m_defaultSession->PruneVolumes(nullptr, 0, nullptr, deleted.addressof(), deleted.size_address<ULONG>(), &spaceReclaimed));
2063
});
2064
2065
VERIFY_ARE_EQUAL(ListVolumes(anonymousVolumeFilters).size(), 1u);
@@ -2411,7 +2413,7 @@ class WSLCTests
2413
LARGE_INTEGER fileSize{};
2414
VERIFY_IS_TRUE(GetFileSizeEx(imageTarFileHandle.get(), &fileSize));
2415
// Load the image from a saved tar
2414
- VERIFY_SUCCEEDED(m_defaultSession->LoadImage(ToCOMInputHandle(imageTarFileHandle.get()), nullptr, fileSize.QuadPart));
2416
+ VERIFY_SUCCEEDED(m_defaultSession->LoadImage(ToCOMInputHandle(imageTarFileHandle.get()), nullptr, fileSize.QuadPart, nullptr));
2417
// Verify that the image is in the list of images.
2418
ExpectImagePresent(*m_defaultSession, "hello-world:latest");
2419
WSLCContainerLauncher launcher("hello-world:latest", "wslc-hello-world-container");
@@ -2446,7 +2448,7 @@ class WSLCTests
2448
LARGE_INTEGER fileSize{};
2449
VERIFY_IS_TRUE(GetFileSizeEx(imageTarFileHandle.get(), &fileSize));
2450
// Load the image from a saved tar
2449
- VERIFY_SUCCEEDED(m_defaultSession->LoadImage(ToCOMInputHandle(imageTarFileHandle.get()), nullptr, fileSize.QuadPart));
2451
+ VERIFY_SUCCEEDED(m_defaultSession->LoadImage(ToCOMInputHandle(imageTarFileHandle.get()), nullptr, fileSize.QuadPart, nullptr));
2452
// Verify that the image is in the list of images.
2453
ExpectImagePresent(*m_defaultSession, "hello-world:latest");
2454
WSLCContainerLauncher launcher("hello-world:latest", "wslc-hello-world-container");
@@ -2504,7 +2506,7 @@ class WSLCTests
2506
2507
wil::unique_event testCompleted{wil::EventOptions::ManualReset};
2508
std::thread operationThread([&]() {
2507
- result.set_value(m_defaultSession->ImportImage(ToCOMInputHandle(pipeRead.get()), "dummy:latest", nullptr, 1024 * 1024));
2509
+ result.set_value(m_defaultSession->ImportImage(ToCOMInputHandle(pipeRead.get()), "dummy:latest", nullptr, 1024 * 1024, nullptr));
2510
2511
WI_ASSERT(testCompleted.is_signaled()); // Sanity check.
2512
});
@@ -2551,7 +2553,7 @@ class WSLCTests
2553
VERIFY_IS_FALSE(INVALID_HANDLE_VALUE == imageTarFileHandle.get());
2554
LARGE_INTEGER fileSize{};
2555
VERIFY_IS_TRUE(GetFileSizeEx(imageTarFileHandle.get(), &fileSize));
2554
- VERIFY_SUCCEEDED(m_defaultSession->LoadImage(ToCOMInputHandle(imageTarFileHandle.get()), nullptr, fileSize.QuadPart));
2556
+ VERIFY_SUCCEEDED(m_defaultSession->LoadImage(ToCOMInputHandle(imageTarFileHandle.get()), nullptr, fileSize.QuadPart, nullptr));
2557
// Verify that the image is in the list of images.
2558
ExpectImagePresent(*m_defaultSession, "hello-world:latest");
2559
WSLCContainerLauncher launcher("hello-world:latest", "wslc-hello-world-container");
@@ -2584,7 +2586,7 @@ class WSLCTests
2586
});
2587
2588
VERIFY_SUCCEEDED(m_defaultSession->ImportImage(
2587
- ToCOMInputHandle(containerTarFileHandle.get()), "test-imported-container:latest", nullptr, fileSize.QuadPart));
2589
+ ToCOMInputHandle(containerTarFileHandle.get()), "test-imported-container:latest", nullptr, fileSize.QuadPart, nullptr));
2590
2591
// Verify that the image is in the list of images.
2592
ExpectImagePresent(*m_defaultSession, "test-imported-container:latest");
@@ -4415,7 +4417,7 @@ class WSLCTests
4417
wil::unique_cotaskmem_array_ptr<WSLCVolumeName> deleted;
4418
ULONGLONG spaceReclaimed = 0;
4419
VERIFY_SUCCEEDED(m_defaultSession->PruneVolumes(
4418
- filtersPtr, filtersCount, deleted.addressof(), deleted.size_address<ULONG>(), &spaceReclaimed));
4420
+ filtersPtr, filtersCount, nullptr, deleted.addressof(), deleted.size_address<ULONG>(), &spaceReclaimed));
4421
4422
std::vector<std::string> names;
4423
for (const auto& n : deleted)
@@ -4555,7 +4557,8 @@ class WSLCTests
4557
4558
VERIFY_ARE_EQUAL(
4559
E_POINTER,
4558
- m_defaultSession->PruneVolumes(filters, ARRAYSIZE(filters), deleted.addressof(), deleted.size_address<ULONG>(), &spaceReclaimed));
4560
+ m_defaultSession->PruneVolumes(
4561
+ filters, ARRAYSIZE(filters), nullptr, deleted.addressof(), deleted.size_address<ULONG>(), &spaceReclaimed));
4562
}
4563
4564
// Filter with null Value rejected.
@@ -4567,7 +4570,8 @@ class WSLCTests
4570
4571
VERIFY_ARE_EQUAL(
4572
E_POINTER,
4570
- m_defaultSession->PruneVolumes(filters, ARRAYSIZE(filters), deleted.addressof(), deleted.size_address<ULONG>(), &spaceReclaimed));
4573
+ m_defaultSession->PruneVolumes(
4574
+ filters, ARRAYSIZE(filters), nullptr, deleted.addressof(), deleted.size_address<ULONG>(), &spaceReclaimed));
4575
}
4576
}
4577
@@ -4587,7 +4591,7 @@ class WSLCTests
4591
options.Driver = "bridge";
4592
options.DriverOpts = nullptr;
4593
options.DriverOptsCount = 0;
4590
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options));
4594
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options, nullptr));
4595
4596
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
4597
@@ -4599,7 +4603,7 @@ class WSLCTests
4603
VERIFY_IS_TRUE(strlen(networks[0].Id) > 0);
4604
4605
// Duplicate name should fail.
4602
- VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), m_defaultSession->CreateNetwork(&options));
4606
+ VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), m_defaultSession->CreateNetwork(&options, nullptr));
4607
4608
cleanup.release();
4609
VERIFY_SUCCEEDED(m_defaultSession->DeleteNetwork(networkName.c_str()));
@@ -4628,7 +4632,7 @@ class WSLCTests
4632
4633
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
4634
4631
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options));
4635
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options, nullptr));
4636
4637
wil::unique_cotaskmem_array_ptr<WSLCNetworkInformation> networks;
4638
VERIFY_SUCCEEDED(m_defaultSession->ListNetworks(networks.addressof(), networks.size_address<ULONG>()));
@@ -4652,7 +4656,7 @@ class WSLCTests
4656
4657
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
4658
4655
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options));
4659
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options, nullptr));
4660
4661
wil::unique_cotaskmem_array_ptr<WSLCNetworkInformation> networks;
4662
VERIFY_SUCCEEDED(m_defaultSession->ListNetworks(networks.addressof(), networks.size_address<ULONG>()));
@@ -4681,7 +4685,7 @@ class WSLCTests
4685
4686
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
4687
4684
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options));
4688
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options, nullptr));
4689
4690
wil::unique_cotaskmem_array_ptr<WSLCNetworkInformation> networks;
4691
VERIFY_SUCCEEDED(m_defaultSession->ListNetworks(networks.addressof(), networks.size_address<ULONG>()));
@@ -4699,7 +4703,7 @@ class WSLCTests
4703
for (const char* driver : {"overlay", "Bridge", ""})
4704
{
4705
options.Driver = driver;
4702
- VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options));
4706
+ VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options, nullptr));
4707
ValidateCOMErrorMessageContains(L"Unsupported network driver:");
4708
}
4709
}
@@ -4712,15 +4716,15 @@ class WSLCTests
4716
options.DriverOptsCount = 0;
4717
4718
options.Name = "bridge";
4715
- VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options));
4719
+ VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options, nullptr));
4720
ValidateCOMErrorMessageContains(L"bridge");
4721
4722
options.Name = "host";
4719
- VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options));
4723
+ VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options, nullptr));
4724
ValidateCOMErrorMessageContains(L"host");
4725
4726
options.Name = "none";
4723
- VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options));
4727
+ VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options, nullptr));
4728
ValidateCOMErrorMessageContains(L"none");
4729
}
4730
@@ -4732,7 +4736,7 @@ class WSLCTests
4736
options.DriverOpts = nullptr;
4737
options.DriverOptsCount = 0;
4738
4735
- VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options));
4739
+ VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options, nullptr));
4740
ValidateCOMErrorMessageContains(L"invalid name!");
4741
}
4742
@@ -4750,7 +4754,7 @@ class WSLCTests
4754
options.DriverOpts = opts;
4755
options.DriverOptsCount = ARRAYSIZE(opts);
4756
4753
- VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options));
4757
+ VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options, nullptr));
4758
ValidateCOMErrorMessageContains(L"invalid subnet");
4759
4760
wil::unique_cotaskmem_ansistring output;
@@ -4771,7 +4775,7 @@ class WSLCTests
4775
options.DriverOpts = opts;
4776
options.DriverOptsCount = ARRAYSIZE(opts);
4777
4774
- VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options));
4778
+ VERIFY_ARE_EQUAL(E_INVALIDARG, m_defaultSession->CreateNetwork(&options, nullptr));
4779
ValidateCOMErrorMessageContains(L"invalid gateway");
4780
4781
wil::unique_cotaskmem_ansistring output;
@@ -4794,7 +4798,7 @@ class WSLCTests
4798
4799
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
4800
4797
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options));
4801
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options, nullptr));
4802
4803
wil::unique_cotaskmem_ansistring output;
4804
VERIFY_SUCCEEDED(m_defaultSession->InspectNetwork(networkName.c_str(), &output));
@@ -4818,7 +4822,7 @@ class WSLCTests
4822
options.Driver = "bridge";
4823
options.DriverOpts = nullptr;
4824
options.DriverOptsCount = 0;
4821
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options));
4825
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options, nullptr));
4826
4827
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
4828
@@ -4854,7 +4858,7 @@ class WSLCTests
4858
optionsA.Driver = "bridge";
4859
optionsA.DriverOpts = nullptr;
4860
optionsA.DriverOptsCount = 0;
4857
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&optionsA));
4861
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&optionsA, nullptr));
4862
4863
WSLCDriverOption subnetOpt[] = {{"Subnet", "172.29.0.0/16"}};
4864
WSLCNetworkOptions optionsB{};
@@ -4862,7 +4866,7 @@ class WSLCTests
4866
optionsB.Driver = "bridge";
4867
optionsB.DriverOpts = subnetOpt;
4868
optionsB.DriverOptsCount = ARRAYSIZE(subnetOpt);
4865
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&optionsB));
4869
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&optionsB, nullptr));
4870
4871
WSLCDriverOption internalOpt[] = {{"Internal", "true"}};
4872
WSLCNetworkOptions optionsC{};
@@ -4870,7 +4874,7 @@ class WSLCTests
4874
optionsC.Driver = "bridge";
4875
optionsC.DriverOpts = internalOpt;
4876
optionsC.DriverOptsCount = ARRAYSIZE(internalOpt);
4873
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&optionsC));
4877
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&optionsC, nullptr));
4878
4879
wil::unique_cotaskmem_array_ptr<WSLCNetworkInformation> networks;
4880
VERIFY_SUCCEEDED(m_defaultSession->ListNetworks(networks.addressof(), networks.size_address<ULONG>()));
@@ -4894,7 +4898,7 @@ class WSLCTests
4898
options.Driver = "bridge";
4899
options.DriverOpts = nullptr;
4900
options.DriverOptsCount = 0;
4897
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options));
4901
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options, nullptr));
4902
4903
wil::unique_cotaskmem_ansistring output;
4904
VERIFY_SUCCEEDED(m_defaultSession->InspectNetwork(networkName.c_str(), &output));
@@ -4922,7 +4926,7 @@ class WSLCTests
4926
options.Driver = "bridge";
4927
options.DriverOpts = subnetOpt;
4928
options.DriverOptsCount = ARRAYSIZE(subnetOpt);
4925
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options));
4929
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&options, nullptr));
4930
4931
wil::unique_cotaskmem_ansistring output;
4932
VERIFY_SUCCEEDED(m_defaultSession->InspectNetwork(networkName.c_str(), &output));
@@ -5285,7 +5289,7 @@ class WSLCTests
5289
options.InitProcessOptions.CommandLine = {.Values = nullptr, .Count = 0};
5290
5291
wil::com_ptr<IWSLCContainer> container;
5288
- auto hr = m_defaultSession->CreateContainer(&options, &container);
5292
+ auto hr = m_defaultSession->CreateContainer(&options, nullptr, &container);
5293
VERIFY_ARE_EQUAL(hr, E_INVALIDARG);
5294
}
5295
@@ -5297,7 +5301,7 @@ class WSLCTests
5301
options.InitProcessOptions.CommandLine = {.Values = nullptr, .Count = 0};
5302
5303
wil::com_ptr<IWSLCContainer> container;
5300
- VERIFY_SUCCEEDED(m_defaultSession->CreateContainer(&options, &container));
5304
+ VERIFY_SUCCEEDED(m_defaultSession->CreateContainer(&options, nullptr, &container));
5305
VERIFY_SUCCEEDED(container->Delete(WSLCDeleteFlagsNone));
5306
}
5307
}
@@ -5313,14 +5317,14 @@ class WSLCTests
5317
5318
{
5319
// Validate that the container can be restarted.
5316
- VERIFY_ARE_EQUAL(container.Get().Start(WSLCContainerStartFlagsAttach, nullptr), S_OK);
5320
+ VERIFY_ARE_EQUAL(container.Get().Start(WSLCContainerStartFlagsAttach, nullptr, nullptr), S_OK);
5321
auto restartedProcess = container.GetInitProcess();
5322
ValidateProcessOutput(restartedProcess, {{1, "OK\n"}});
5323
}
5324
5325
{
5326
// Validate that the container can be restarted without the attach flag.
5323
- VERIFY_ARE_EQUAL(container.Get().Start(WSLCContainerStartFlagsNone, nullptr), S_OK);
5327
+ VERIFY_ARE_EQUAL(container.Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr), S_OK);
5328
auto restartedProcess = container.GetInitProcess();
5329
VERIFY_ARE_EQUAL(restartedProcess.Wait(), 0);
5330
@@ -5342,21 +5346,21 @@ class WSLCTests
5346
VERIFY_SUCCEEDED(container.Get().Stop(WSLCSignalSIGKILL, 0));
5347
VERIFY_ARE_EQUAL(container.State(), WslcContainerStateExited);
5348
5345
- VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr));
5349
+ VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr));
5350
VERIFY_ARE_EQUAL(container.State(), WslcContainerStateRunning);
5351
5352
auto initProcess = container.GetInitProcess();
5353
initProcess.Get().Signal(WSLCSignalSIGKILL);
5354
VERIFY_ARE_EQUAL(initProcess.Wait(), WSLCSignalSIGKILL + 128);
5355
5352
- VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr));
5356
+ VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr));
5357
VERIFY_ARE_EQUAL(container.State(), WslcContainerStateRunning);
5358
5359
VERIFY_SUCCEEDED(container.Get().Stop(WSLCSignalSIGKILL, 0));
5360
VERIFY_SUCCEEDED(container.Get().Delete(WSLCDeleteFlagsNone));
5361
5362
// Validate that deleted containers can't be started.
5359
- VERIFY_ARE_EQUAL(container.Get().Start(WSLCContainerStartFlagsNone, nullptr), RPC_E_DISCONNECTED);
5363
+ VERIFY_ARE_EQUAL(container.Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr), RPC_E_DISCONNECTED);
5364
}
5365
5366
// Validate restart behavior for a container with the autorm flag set
@@ -5369,14 +5373,14 @@ class WSLCTests
5373
VERIFY_SUCCEEDED(container.Get().Stop(WSLCSignalSIGKILL, 0));
5374
5375
// Validate that deleted containers can't be started.
5372
- VERIFY_ARE_EQUAL(container.Get().Start(WSLCContainerStartFlagsNone, nullptr), RPC_E_DISCONNECTED);
5376
+ VERIFY_ARE_EQUAL(container.Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr), RPC_E_DISCONNECTED);
5377
}
5378
5379
// Validate that invalid start flags are rejected.
5380
{
5381
WSLCContainerLauncher launcher("debian:latest", "test-stop-start-invalid-flags", {"echo", "OK"});
5382
auto container = launcher.Create(*m_defaultSession);
5379
- VERIFY_ARE_EQUAL(container.Get().Start(static_cast<WSLCContainerStartFlags>(0x2), nullptr), E_INVALIDARG);
5383
+ VERIFY_ARE_EQUAL(container.Get().Start(static_cast<WSLCContainerStartFlags>(0x2), nullptr, nullptr), E_INVALIDARG);
5384
}
5385
}
5386
@@ -5578,7 +5582,7 @@ class WSLCTests
5582
ValidateCOMErrorMessage(std::format(L"Container '{}' is not running.", id));
5583
5584
// Verify that the container is in running state.
5581
- VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr));
5585
+ VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr));
5586
VERIFY_ARE_EQUAL(container.State(), WslcContainerStateRunning);
5587
5588
VERIFY_SUCCEEDED(container.Get().Stop(WSLCSignalSIGTERM, 0));
@@ -5604,7 +5608,7 @@ class WSLCTests
5608
VERIFY_ARE_EQUAL(container.Get().Kill(WSLCSignalNone), WSLC_E_CONTAINER_NOT_RUNNING);
5609
ValidateCOMErrorMessage(std::format(L"Container '{}' is not running.", id));
5610
5607
- VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr));
5611
+ VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr));
5612
VERIFY_ARE_EQUAL(container.State(), WslcContainerStateRunning);
5613
VERIFY_SUCCEEDED(container.Get().Kill(WSLCSignalNone));
5614
@@ -5627,7 +5631,7 @@ class WSLCTests
5631
5632
auto container = launcher.Create(*m_defaultSession);
5633
5630
- VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr));
5634
+ VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr));
5635
VERIFY_ARE_EQUAL(container.State(), WslcContainerStateRunning);
5636
VERIFY_SUCCEEDED(container.Get().Kill(WSLCSignalSIGTERM));
5637
@@ -5707,11 +5711,11 @@ class WSLCTests
5711
VERIFY_SUCCEEDED(result);
5712
5713
VERIFY_ARE_EQUAL(container->State(), WslcContainerStateCreated);
5710
- VERIFY_SUCCEEDED(container->Get().Start(WSLCContainerStartFlagsNone, nullptr));
5714
+ VERIFY_SUCCEEDED(container->Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr));
5715
5716
// Verify that Start() can't be called again on a running container.
5717
auto id = container->Id();
5714
- VERIFY_ARE_EQUAL(container->Get().Start(WSLCContainerStartFlagsNone, nullptr), WSLC_E_CONTAINER_IS_RUNNING);
5718
+ VERIFY_ARE_EQUAL(container->Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr), WSLC_E_CONTAINER_IS_RUNNING);
5719
ValidateCOMErrorMessage(std::format(L"Container '{}' is running.", id));
5720
5721
VERIFY_ARE_EQUAL(container->State(), WslcContainerStateRunning);
@@ -6000,7 +6004,7 @@ class WSLCTests
6004
networkOptions.DriverOpts = opts;
6005
networkOptions.DriverOptsCount = ARRAYSIZE(opts);
6006
6003
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions));
6007
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions, nullptr));
6008
6009
auto networkCleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
6010
@@ -6035,7 +6039,7 @@ class WSLCTests
6039
options.ContainerNetwork.NetworksCount = 1;
6040
6041
wil::com_ptr<IWSLCContainer> container;
6038
- auto hr = m_defaultSession->CreateContainer(&options, &container);
6042
+ auto hr = m_defaultSession->CreateContainer(&options, nullptr, &container);
6043
VERIFY_ARE_EQUAL(E_INVALIDARG, hr);
6044
ValidateCOMErrorMessageContains(L"Network name");
6045
}
@@ -6051,7 +6055,7 @@ class WSLCTests
6055
WSLCNetworkOptions networkOptions{};
6056
networkOptions.Name = networkName.c_str();
6057
networkOptions.Driver = "bridge";
6054
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions));
6058
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions, nullptr));
6059
auto networkCleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
6060
6061
LPCSTR args[] = {"sleep", "99999"};
@@ -6070,7 +6074,7 @@ class WSLCTests
6074
options.ContainerNetwork.NetworksCount = 1;
6075
6076
wil::com_ptr<IWSLCContainer> container;
6073
- auto hr = m_defaultSession->CreateContainer(&options, &container);
6077
+ auto hr = m_defaultSession->CreateContainer(&options, nullptr, &container);
6078
VERIFY_ARE_EQUAL(E_NOTIMPL, hr);
6079
ValidateCOMErrorMessage(L"Endpoint settings are not yet supported (network 'custom-net-settings').");
6080
}
@@ -6101,7 +6105,7 @@ class WSLCTests
6105
networkOptions.DriverOpts = opts;
6106
networkOptions.DriverOptsCount = ARRAYSIZE(opts);
6107
6104
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions));
6108
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions, nullptr));
6109
6110
auto networkCleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
6111
@@ -6132,7 +6136,7 @@ class WSLCTests
6136
networkOptions.DriverOpts = opts;
6137
networkOptions.DriverOptsCount = ARRAYSIZE(opts);
6138
6135
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions));
6139
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions, nullptr));
6140
6141
auto networkCleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
6142
@@ -6158,7 +6162,7 @@ class WSLCTests
6162
networkOptions.DriverOpts = opts;
6163
networkOptions.DriverOptsCount = ARRAYSIZE(opts);
6164
6161
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions));
6165
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions, nullptr));
6166
6167
auto networkCleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
6168
@@ -6188,7 +6192,7 @@ class WSLCTests
6192
networkOptions.DriverOpts = opts;
6193
networkOptions.DriverOptsCount = ARRAYSIZE(opts);
6194
6191
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions));
6195
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&networkOptions, nullptr));
6196
6197
auto networkCleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(networkName.c_str())); });
6198
@@ -6207,7 +6211,7 @@ class WSLCTests
6211
auto recoveredContainer = OpenContainer(m_defaultSession.get(), containerName);
6212
6213
VERIFY_ARE_EQUAL(recoveredContainer.State(), WslcContainerStateCreated);
6210
- VERIFY_SUCCEEDED(recoveredContainer.Get().Start(WSLCContainerStartFlagsAttach, nullptr));
6214
+ VERIFY_SUCCEEDED(recoveredContainer.Get().Start(WSLCContainerStartFlagsAttach, nullptr, nullptr));
6215
VERIFY_ARE_EQUAL(recoveredContainer.State(), WslcContainerStateRunning);
6216
6217
VERIFY_ARE_EQUAL(recoveredContainer.Inspect().HostConfig.NetworkMode, networkName);
@@ -6227,7 +6231,7 @@ class WSLCTests
6231
primaryNetOpts.Driver = "bridge";
6232
primaryNetOpts.DriverOpts = primaryOpts;
6233
primaryNetOpts.DriverOptsCount = ARRAYSIZE(primaryOpts);
6230
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&primaryNetOpts));
6234
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&primaryNetOpts, nullptr));
6235
auto primaryCleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(primaryNetwork.c_str())); });
6236
6237
WSLCDriverOption additionalOpts[] = {{"Subnet", "172.41.0.0/16"}};
@@ -6236,7 +6240,7 @@ class WSLCTests
6240
additionalNetOpts.Driver = "bridge";
6241
additionalNetOpts.DriverOpts = additionalOpts;
6242
additionalNetOpts.DriverOptsCount = ARRAYSIZE(additionalOpts);
6239
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&additionalNetOpts));
6243
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&additionalNetOpts, nullptr));
6244
auto additionalCleanup =
6245
wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(additionalNetwork.c_str())); });
6246
@@ -6266,7 +6270,7 @@ class WSLCTests
6270
netOpts.Driver = "bridge";
6271
netOpts.DriverOpts = opts;
6272
netOpts.DriverOptsCount = ARRAYSIZE(opts);
6269
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&netOpts));
6273
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&netOpts, nullptr));
6274
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(primaryNetwork.c_str())); });
6275
6276
WSLCContainerLauncher launcher("debian:latest", "test-multi-net-dup-reject", {"sleep", "99999"}, {}, std::string(primaryNetwork));
@@ -6292,7 +6296,7 @@ class WSLCTests
6296
netOpts.Driver = "bridge";
6297
netOpts.DriverOpts = opts;
6298
netOpts.DriverOptsCount = ARRAYSIZE(opts);
6295
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&netOpts));
6299
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&netOpts, nullptr));
6300
auto cleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(primaryNetwork.c_str())); });
6301
6302
WSLCContainerLauncher launcher("debian:latest", "test-multi-net-notfound-reject", {"sleep", "99999"}, {}, std::string(primaryNetwork));
@@ -6366,7 +6370,7 @@ class WSLCTests
6370
primaryNetOpts.Driver = "bridge";
6371
primaryNetOpts.DriverOpts = primaryOpts;
6372
primaryNetOpts.DriverOptsCount = ARRAYSIZE(primaryOpts);
6369
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&primaryNetOpts));
6373
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&primaryNetOpts, nullptr));
6374
auto primaryCleanup = wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(primaryNetwork.c_str())); });
6375
6376
WSLCDriverOption additionalOpts[] = {{"Subnet", "172.51.0.0/16"}};
@@ -6375,7 +6379,7 @@ class WSLCTests
6379
additionalNetOpts.Driver = "bridge";
6380
additionalNetOpts.DriverOpts = additionalOpts;
6381
additionalNetOpts.DriverOptsCount = ARRAYSIZE(additionalOpts);
6378
- VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&additionalNetOpts));
6382
+ VERIFY_SUCCEEDED(m_defaultSession->CreateNetwork(&additionalNetOpts, nullptr));
6383
auto additionalCleanup =
6384
wil::scope_exit([&]() { LOG_IF_FAILED(m_defaultSession->DeleteNetwork(additionalNetwork.c_str())); });
6385
@@ -7056,7 +7060,7 @@ class WSLCTests
7060
options.ContainerNetwork.NetworkMode = containerNetworkType.c_str();
7061
7062
wil::com_ptr<IWSLCContainer> container;
7059
- VERIFY_ARE_EQUAL(session.CreateContainer(&options, &container), E_INVALIDARG);
7063
+ VERIFY_ARE_EQUAL(session.CreateContainer(&options, nullptr, &container), E_INVALIDARG);
7064
}
7065
7066
// TODO: Update once UDP is supported.
@@ -7381,7 +7385,7 @@ class WSLCTests
7385
container.SetDeleteOnClose(false);
7386
7387
auto openedContainer = OpenContainer(m_defaultSession.get(), "test-volumes-8");
7384
- VERIFY_SUCCEEDED(openedContainer.Get().Start(WSLCContainerStartFlagsAttach, nullptr));
7388
+ VERIFY_SUCCEEDED(openedContainer.Get().Start(WSLCContainerStartFlagsAttach, nullptr, nullptr));
7389
validateInspect(openedContainer);
7390
7391
ValidateContainerOutput(openedContainer, {{1, "OK"}});
@@ -7889,7 +7893,7 @@ class WSLCTests
7893
container.SetDeleteOnClose(false);
7894
7895
VERIFY_ARE_EQUAL(container.State(), WslcContainerStateCreated);
7892
- VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsAttach, nullptr));
7896
+ VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsAttach, nullptr, nullptr));
7897
7898
auto initProcess = container.GetInitProcess();
7899
WaitForOutput(initProcess.GetStdHandle(1), "Serving HTTP on 0.0.0.0 port 8000");
@@ -8015,7 +8019,7 @@ class WSLCTests
8019
auto settings = GetDefaultSessionSettings(L"session-1");
8020
8021
wil::com_ptr<IWSLCSession> session;
8018
- VERIFY_ARE_EQUAL(manager->CreateSession(&settings, WSLCSessionFlagsPersistent, &session), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS));
8022
+ VERIFY_ARE_EQUAL(manager->CreateSession(&settings, WSLCSessionFlagsPersistent, nullptr, &session), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS));
8023
8024
VERIFY_SUCCEEDED(session1Copy->Terminate());
8025
WSLCSessionState state{};
@@ -8288,7 +8292,7 @@ class WSLCTests
8292
options.LabelsCount = 1;
8293
8294
wil::com_ptr<IWSLCContainer> container;
8291
- auto hr = m_defaultSession->CreateContainer(&options, &container);
8295
+ auto hr = m_defaultSession->CreateContainer(&options, nullptr, &container);
8296
VERIFY_ARE_EQUAL(hr, E_INVALIDARG);
8297
}
8298
@@ -8303,7 +8307,7 @@ class WSLCTests
8307
options.LabelsCount = 1;
8308
8309
wil::com_ptr<IWSLCContainer> container;
8306
- auto hr = m_defaultSession->CreateContainer(&options, &container);
8310
+ auto hr = m_defaultSession->CreateContainer(&options, nullptr, &container);
8311
VERIFY_ARE_EQUAL(hr, E_INVALIDARG);
8312
}
8313
@@ -8320,7 +8324,7 @@ class WSLCTests
8324
options.LabelsCount = static_cast<ULONG>(labels.size());
8325
8326
wil::com_ptr<IWSLCContainer> container;
8323
- auto hr = m_defaultSession->CreateContainer(&options, &container);
8327
+ auto hr = m_defaultSession->CreateContainer(&options, nullptr, &container);
8328
VERIFY_ARE_EQUAL(hr, HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS));
8329
}
8330
@@ -8425,7 +8429,7 @@ class WSLCTests
8429
options.UlimitsCount = 1;
8430
8431
wil::com_ptr<IWSLCContainer> container;
8428
- auto hr = m_defaultSession->CreateContainer(&options, &container);
8432
+ auto hr = m_defaultSession->CreateContainer(&options, nullptr, &container);
8433
VERIFY_ARE_EQUAL(hr, E_INVALIDARG);
8434
}
8435
}
@@ -8447,7 +8451,7 @@ class WSLCTests
8451
ValidateCOMErrorMessage(std::format(L"Container '{}' is not running.", id));
8452
8453
// Start the container.
8450
- VERIFY_SUCCEEDED(container->Get().Start(WSLCContainerStartFlagsAttach, nullptr));
8454
+ VERIFY_SUCCEEDED(container->Get().Start(WSLCContainerStartFlagsAttach, nullptr, nullptr));
8455
8456
// Verify that trying to attach with null handles fails.
8457
VERIFY_ARE_EQUAL(container->Get().Attach(nullptr, nullptr, nullptr, nullptr), HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER));
@@ -8736,7 +8740,7 @@ class WSLCTests
8740
expectInvalidArg(longName);
8741
8742
auto expectInvalidPull = [&](const char* name) {
8739
- VERIFY_ARE_EQUAL(m_defaultSession->PullImage(name, nullptr, nullptr), E_INVALIDARG);
8743
+ VERIFY_ARE_EQUAL(m_defaultSession->PullImage(name, nullptr, nullptr, nullptr), E_INVALIDARG);
8744
8745
auto comError = wsl::windows::common::wslutil::GetCOMErrorInfo();
8746
VERIFY_IS_TRUE(comError.has_value());
@@ -8869,7 +8873,7 @@ class WSLCTests
8873
auto container = OpenContainer(m_defaultSession.get(), "test-auto-remove");
8874
auto id = container.Id();
8875
8872
- VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr));
8876
+ VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr));
8877
VERIFY_SUCCEEDED(container.Get().Stop(WSLCSignalSIGKILL, 0));
8878
8879
// verifyContainerDeleted("test-auto-remove");
@@ -8987,11 +8991,11 @@ class WSLCTests
8991
VERIFY_ARE_EQUAL(container2.State(), WslcContainerStateCreated);
8992
8993
// Start container — should succeed.
8990
- VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr));
8994
+ VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr));
8995
VERIFY_ARE_EQUAL(container.State(), WslcContainerStateRunning);
8996
8997
// Start container 2 — should fail because the host port is already reserved by container 1.
8994
- VERIFY_ARE_EQUAL(container2.Get().Start(WSLCContainerStartFlagsNone, nullptr), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS));
8998
+ VERIFY_ARE_EQUAL(container2.Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS));
8999
VERIFY_ARE_EQUAL(container2.State(), WslcContainerStateCreated);
9000
}
9001
@@ -9022,7 +9026,7 @@ class WSLCTests
9026
VERIFY_ARE_EQUAL(getMountCount(), baselineMountCount);
9027
9028
// Start the container — volume should now be mounted.
9025
- VERIFY_SUCCEEDED(container->Get().Start(WSLCContainerStartFlagsNone, nullptr));
9029
+ VERIFY_SUCCEEDED(container->Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr));
9030
VERIFY_ARE_EQUAL(container->State(), WslcContainerStateRunning);
9031
VERIFY_ARE_EQUAL(getMountCount(), baselineMountCount + 1);
9032
@@ -9192,7 +9196,7 @@ class WSLCTests
9196
WSLCContainerLauncher launcher("debian:latest", "test-detach", {"sleep", "9999999"}, {}, {}, WSLCProcessFlagsStdin | WSLCProcessFlagsTty);
9197
9198
auto container = launcher.Create(*m_defaultSession);
9195
- VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsAttach, DetachKeys));
9199
+ VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsAttach, DetachKeys, nullptr));
9200
9201
auto initProcess = container.GetInitProcess();
9202
@@ -9243,9 +9247,9 @@ class WSLCTests
9247
WSLCContainerLauncher launcher("debian:latest", "test-detach", {"cat"}, {}, {}, WSLCProcessFlagsStdin | WSLCProcessFlagsTty);
9248
auto container = launcher.Create(*m_defaultSession);
9249
9246
- VERIFY_ARE_EQUAL(container.Get().Start(WSLCContainerStartFlagsAttach, "invalid"), E_INVALIDARG);
9250
+ VERIFY_ARE_EQUAL(container.Get().Start(WSLCContainerStartFlagsAttach, "invalid", nullptr), E_INVALIDARG);
9251
9248
- VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr));
9252
+ VERIFY_SUCCEEDED(container.Get().Start(WSLCContainerStartFlagsNone, nullptr, nullptr));
9253
9254
COMOutputHandle unusedHandle{};
9255
VERIFY_ARE_EQUAL(container.Get().Attach("invalid", &unusedHandle, &unusedHandle, &unusedHandle), E_INVALIDARG);
@@ -9678,4 +9682,153 @@ class WSLCTests
9682
9683
VERIFY_IS_FALSE(IsVmRunning(c_sessionName));
9684
}
9685
+
9686
+ // Helper: COM callback that captures all warnings received.
9687
+ class CapturingWarningCallback
9688
+ : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IWarningCallback, IFastRundown>
9689
+ {
9690
+ public:
9691
+ HRESULT OnWarning(LPCWSTR Message) override
9692
+ {
9693
+ std::lock_guard lock(m_lock);
9694
+ m_warnings.emplace_back(Message);
9695
+ return S_OK;
9696
+ }
9697
+
9698
+ std::vector<std::wstring> GetWarnings()
9699
+ {
9700
+ std::lock_guard lock(m_lock);
9701
+ return m_warnings;
9702
+ }
9703
+
9704
+ private:
9705
+ std::mutex m_lock;
9706
+ std::vector<std::wstring> m_warnings;
9707
+ };
9708
+
9709
+ WSLC_TEST_METHOD(WarningCallbackContainerRecovery)
9710
+ {
9711
+ SKIP_TEST_SERVER();
9712
+
9713
+ constexpr auto c_sessionName = L"warning-container-recovery";
9714
+ auto storagePath = (std::filesystem::current_path() / "test-warning-container-recovery").wstring();
9715
+ auto cleanupDir = wil::scope_exit([&]() {
9716
+ std::error_code ec;
9717
+ std::filesystem::remove_all(storagePath, ec);
9718
+ });
9719
+
9720
+ // Phase 1: Create a session and inject a container with a corrupt WSLC metadata label via docker CLI.
9721
+ {
9722
+ auto settings = GetDefaultSessionSettings(c_sessionName, false, WSLCNetworkingModeVirtioProxy);
9723
+ settings.StoragePath = storagePath.c_str();
9724
+ auto session = CreateSession(settings);
9725
+
9726
+ // Load a base image so docker create works.
9727
+ LoadTestImage(*session, "hello-world:latest");
9728
+
9729
+ // Create a container with an invalid WSLC metadata label.
9730
+ // RecoverExistingContainers will fail to parse this on the next session.
9731
+ auto result = ExpectCommandResult(
9732
+ session.get(),
9733
+ {"/usr/bin/docker", "create", "--label", "wslc.container.metadata=INVALID_JSON", "hello-world:latest"},
9734
+ 0);
9735
+
9736
+ // Capture the container ID from docker create output (stdout, trimmed).
9737
+ auto containerId = result.Output[1];
9738
+ containerId.erase(containerId.find_last_not_of(" \n\r") + 1);
9739
+
9740
+ VERIFY_SUCCEEDED(session->Terminate());
9741
+
9742
+ // Phase 2: Create a new session pointing to the same storage with a warning callback.
9743
+ auto warningCallback = Microsoft::WRL::Make<CapturingWarningCallback>();
9744
+
9745
+ auto settings2 = GetDefaultSessionSettings(c_sessionName, false, WSLCNetworkingModeVirtioProxy);
9746
+ settings2.StoragePath = storagePath.c_str();
9747
+
9748
+ const auto sessionManager2 = OpenSessionManager();
9749
+ wil::com_ptr<IWSLCSession> session2;
9750
+ VERIFY_SUCCEEDED(sessionManager2->CreateSession(&settings2, WSLCSessionFlagsNone, warningCallback.Get(), &session2));
9751
+ wsl::windows::common::security::ConfigureForCOMImpersonation(session2.get());
9752
+
9753
+ // Verify the warning matches the expected localized message for the corrupt container.
9754
+ auto warnings = warningCallback->GetWarnings();
9755
+ auto expectedWarning = std::format(
9756
+ L"wsl: {}\n",
9757
+ wsl::shared::Localization::MessageWslcFailedToRecoverContainer(wsl::shared::string::MultiByteToWide(containerId)));
9758
+
9759
+ VERIFY_IS_TRUE(std::ranges::any_of(warnings, [&](const auto& w) { return w == expectedWarning; }));
9760
+
9761
+ VERIFY_SUCCEEDED(session2->Terminate());
9762
+ }
9763
+ }
9764
+
9765
+ WSLC_TEST_METHOD(WarningCallbackVolumeRecovery)
9766
+ {
9767
+ SKIP_TEST_SERVER();
9768
+
9769
+ constexpr auto c_sessionName = L"warning-volume-recovery";
9770
+ auto storagePath = (std::filesystem::current_path() / "test-warning-volume-recovery").wstring();
9771
+ auto cleanupDir = wil::scope_exit([&]() {
9772
+ std::error_code ec;
9773
+ std::filesystem::remove_all(storagePath, ec);
9774
+ });
9775
+
9776
+ std::string vhdHostPath;
9777
+
9778
+ // Phase 1: Create a session with a VHD volume, then get the VHD path.
9779
+ {
9780
+ auto settings = GetDefaultSessionSettings(c_sessionName, false, WSLCNetworkingModeVirtioProxy);
9781
+ settings.StoragePath = storagePath.c_str();
9782
+ auto session = CreateSession(settings);
9783
+
9784
+ // Create a VHD volume.
9785
+ WSLCDriverOption driverOpts[] = {{"SizeBytes", "10485760"}}; // 10MB
9786
+ WSLCVolumeOptions volumeOptions{};
9787
+ volumeOptions.Name = "wslc-test-warning-recovery";
9788
+ volumeOptions.Driver = "vhd";
9789
+ volumeOptions.DriverOpts = driverOpts;
9790
+ volumeOptions.DriverOptsCount = ARRAYSIZE(driverOpts);
9791
+
9792
+ WSLCVolumeInformation volInfo{};
9793
+ VERIFY_SUCCEEDED(session->CreateVolume(&volumeOptions, &volInfo));
9794
+
9795
+ // Inspect the volume to get the host VHD path.
9796
+ wil::unique_cotaskmem_ansistring inspectOutput;
9797
+ VERIFY_SUCCEEDED(session->InspectVolume("wslc-test-warning-recovery", &inspectOutput));
9798
+ auto inspect = wsl::shared::FromJson<wsl::windows::common::wslc_schema::InspectVolume>(inspectOutput.get());
9799
+ VERIFY_IS_TRUE(inspect.Status.has_value());
9800
+ VERIFY_IS_TRUE(inspect.Status->contains("HostPath"));
9801
+ vhdHostPath = inspect.Status->at("HostPath");
9802
+ VERIFY_IS_FALSE(vhdHostPath.empty());
9803
+
9804
+ VERIFY_SUCCEEDED(session->Terminate());
9805
+ }
9806
+
9807
+ // Phase 2: Delete the VHD file, then restart with a warning callback.
9808
+ VERIFY_IS_TRUE(DeleteFileA(vhdHostPath.c_str()));
9809
+
9810
+ {
9811
+ auto warningCallback = Microsoft::WRL::Make<CapturingWarningCallback>();
9812
+
9813
+ auto settings = GetDefaultSessionSettings(c_sessionName, false, WSLCNetworkingModeVirtioProxy);
9814
+ settings.StoragePath = storagePath.c_str();
9815
+
9816
+ const auto sessionManager = OpenSessionManager();
9817
+ wil::com_ptr<IWSLCSession> session;
9818
+ VERIFY_SUCCEEDED(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, warningCallback.Get(), &session));
9819
+ wsl::windows::common::security::ConfigureForCOMImpersonation(session.get());
9820
+
9821
+ // Verify the warning matches the expected localized message for the missing volume.
9822
+ auto warnings = warningCallback->GetWarnings();
9823
+ auto expectedWarning =
9824
+ std::format(L"wsl: {}\n", wsl::shared::Localization::MessageWslcFailedToRecoverVolume(L"wslc-test-warning-recovery"));
9825
+
9826
+ VERIFY_IS_TRUE(std::ranges::any_of(warnings, [&](const auto& w) { return w == expectedWarning; }));
9827
+
9828
+ // Clean up the orphaned volume from Docker's metadata.
9829
+ LOG_IF_FAILED(session->DeleteVolume("wslc-test-warning-recovery"));
9830
+
9831
+ VERIFY_SUCCEEDED(session->Terminate());
9832
+ }
9833
+ }
9834
};
test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp
+4
-4
@@ -183,7 +183,7 @@ class WSLCE2EGlobalTests
183
settings.MaximumStorageSizeMb = 4096;
184
185
wil::com_ptr<IWSLCSession> session;
186
- HRESULT hr = sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, &session);
186
+ HRESULT hr = sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, &session);
187
VERIFY_ARE_EQUAL(hr, WSLC_E_SESSION_RESERVED);
188
}
189
@@ -202,7 +202,7 @@ class WSLCE2EGlobalTests
202
settings.MaximumStorageSizeMb = 4096;
203
204
wil::com_ptr<IWSLCSession> session;
205
- HRESULT hr = sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, &session);
205
+ HRESULT hr = sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, &session);
206
VERIFY_ARE_EQUAL(hr, WSLC_E_SESSION_RESERVED);
207
}
208
@@ -227,10 +227,10 @@ class WSLCE2EGlobalTests
227
settings.MaximumStorageSizeMb = 4096;
228
229
wil::com_ptr<IWSLCSession> session;
230
- VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, &session), WSLC_E_SESSION_RESERVED);
230
+ VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, &session), WSLC_E_SESSION_RESERVED);
231
232
settings.DisplayName = L"Wslc-Cli-Admin";
233
- VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, &session), WSLC_E_SESSION_RESERVED);
233
+ VERIFY_ARE_EQUAL(sessionManager->CreateSession(&settings, WSLCSessionFlagsNone, nullptr, &session), WSLC_E_SESSION_RESERVED);
234
}
235
}
236
test/windows/wslc/e2e/WSLCE2EHelpers.cpp
+1
-1
@@ -52,7 +52,7 @@ namespace {
52
{
53
const auto sessionManager = OpenSessionManager();
54
wil::com_ptr<IWSLCSession> session;
55
- VERIFY_SUCCEEDED(sessionManager->CreateSession(&sessionSettings, Flags, &session));
55
+ VERIFY_SUCCEEDED(sessionManager->CreateSession(&sessionSettings, Flags, nullptr, &session));
56
wsl::windows::common::security::ConfigureForCOMImpersonation(session.get());
57
58
WSLCSessionState state{};
test/windows/wslc/e2e/WSLCE2EWarningTests.cpp
new
+113
@@ -0,0 +1,113 @@
1
+/*++
2
+
3
+Copyright (c) Microsoft. All rights reserved.
4
+
5
+Module Name:
6
+
7
+ WSLCE2EWarningTests.cpp
8
+
9
+Abstract:
10
+
11
+ End-to-end tests validating that warnings emitted by the WSLC COM service are
12
+ surfaced on the wslc.exe CLI's stderr via the IWarningCallback integration.
13
+--*/
14
+
15
+#include "precomp.h"
16
+#include "windows/Common.h"
17
+#include "WSLCExecutor.h"
18
+#include "WSLCE2EHelpers.h"
19
+#include <WSLCProcessLauncher.h>
20
+
21
+namespace WSLCE2ETests {
22
+using namespace wsl::shared;
23
+
24
+class WSLCE2EWarningTests
25
+{
26
+ WSLC_TEST_CLASS(WSLCE2EWarningTests)
27
+
28
+ const TestImage AlpineImage = AlpineTestImage();
29
+ WSADATA m_wsaData{};
30
+
31
+ TEST_CLASS_SETUP(ClassSetup)
32
+ {
33
+ THROW_IF_WIN32_ERROR(WSAStartup(MAKEWORD(2, 2), &m_wsaData));
34
+ EnsureImageIsLoaded(AlpineImage);
35
+ return true;
36
+ }
37
+
38
+ TEST_CLASS_CLEANUP(ClassCleanup)
39
+ {
40
+ EnsureImageIsDeleted(AlpineImage);
41
+ WSACleanup();
42
+ return true;
43
+ }
44
+
45
+ static std::string RunDockerInSession(IWSLCSession& session, std::vector<std::string>&& args)
46
+ {
47
+ wsl::windows::common::WSLCProcessLauncher launcher("/usr/bin/docker", args);
48
+ auto result = launcher.Launch(session).WaitAndCaptureOutput();
49
+ VERIFY_ARE_EQUAL(0, result.Code);
50
+
51
+ // Trim trailing whitespace from the captured stdout (fd 1).
52
+ auto output = result.Output[1];
53
+ output.erase(output.find_last_not_of(" \n\r") + 1);
54
+ return output;
55
+ }
56
+
57
+ // Best-effort removal of a container from docker.
58
+ static void RemoveContainerNoThrow(const std::string& containerId)
59
+ try
60
+ {
61
+ wil::com_ptr<IWSLCSessionManager> sessionManager;
62
+ THROW_IF_FAILED(CoCreateInstance(__uuidof(WSLCSessionManager), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&sessionManager)));
63
+ wsl::windows::common::security::ConfigureForCOMImpersonation(sessionManager.get());
64
+
65
+ wil::com_ptr<IWSLCSession> session;
66
+ THROW_IF_FAILED(sessionManager->OpenSessionByName(nullptr, &session));
67
+ wsl::windows::common::security::ConfigureForCOMImpersonation(session.get());
68
+
69
+ wsl::windows::common::WSLCProcessLauncher launcher("/usr/bin/docker", {"/usr/bin/docker", "rm", "-f", containerId});
70
+ launcher.Launch(*session).WaitAndCaptureOutput();
71
+ }
72
+ CATCH_LOG()
73
+
74
+ // Injects a container with corrupt WSLC metadata into the default session's storage,
75
+ // then verifies that running the wslc.exe CLI surfaces the COM service's recovery
76
+ // warning on stderr.
77
+ WSLC_TEST_METHOD(WSLCE2E_Warning_ContainerRecoveryPrintedOnStderr)
78
+ {
79
+ std::string corruptContainerId;
80
+
81
+ // Inject a container whose WSLC metadata label is not valid JSON. RecoverExistingContainers
82
+ // will fail to parse it the next time the session is created.
83
+ {
84
+ auto session = OpenDefaultElevatedSession();
85
+ corruptContainerId = RunDockerInSession(
86
+ *session,
87
+ {"/usr/bin/docker",
88
+ "create",
89
+ "--label",
90
+ "wslc.container.metadata=INVALID_JSON",
91
+ string::WideToMultiByte(AlpineImage.NameAndTag())});
92
+ VERIFY_IS_FALSE(corruptContainerId.empty());
93
+ }
94
+
95
+ // cleanup: remove the corrupt container
96
+ auto cleanup = wil::scope_exit([&]() { RemoveContainerNoThrow(corruptContainerId); });
97
+
98
+ // Terminate the default session so the next wslc command recreates it and runs recovery.
99
+ EnsureSessionIsTerminated();
100
+
101
+ // Run the CLI: recovery of the corrupt container fails and the warning is printed on stderr.
102
+ auto result = RunWslc(L"container list");
103
+ VERIFY_IS_TRUE(result.ExitCode.has_value());
104
+ VERIFY_ARE_EQUAL(0u, result.ExitCode.value());
105
+ VERIFY_IS_TRUE(result.Stderr.has_value());
106
+
107
+ const auto expectedStderr = std::format(
108
+ L"wsl: {}\r\n", wsl::shared::Localization::MessageWslcFailedToRecoverContainer(string::MultiByteToWide(corruptContainerId)));
109
+ VERIFY_ARE_EQUAL(expectedStderr, result.Stderr.value());
110
+ }
111
+};
112
+
113
+} // namespace WSLCE2ETests