From 85d294638aa394aadcc452a8d5c0ff5fc4dbc2ee Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Wed, 1 Apr 2026 16:23:57 -0700 Subject: [PATCH 1/4] devicehost: stop re-signing and fix MSI installer failing to replace wsldevicehost.dll --- .pipelines/build-stage.yml | 2 +- CMakeLists.txt | 1 - msipackage/package.wix.in | 15 +++++++++++- packages.config | 2 +- src/windows/wslinstall/CMakeLists.txt | 3 ++- src/windows/wslinstall/DllMain.cpp | 34 +++++++++++++++++++++++++++ src/windows/wslinstall/wslinstall.def | 1 + 7 files changed, 53 insertions(+), 5 deletions(-) diff --git a/.pipelines/build-stage.yml b/.pipelines/build-stage.yml index 8aa4e92ab..c9b96063e 100644 --- a/.pipelines/build-stage.yml +++ b/.pipelines/build-stage.yml @@ -24,7 +24,7 @@ parameters: type: object default: - target: "wsl;libwsl;wslg;wslservice;wslhost;wslrelay;wslinstaller;wslinstall;initramfs;wslserviceproxystub;wslsettings;wslinstallerproxystub;testplugin" - pattern: "wsl.exe,libwsl.dll,wslg.exe,wslservice.exe,wslhost.exe,wslrelay.exe,wslinstaller.exe,wslinstall.dll,wslserviceproxystub.dll,wslsettings/wslsettings.dll,wslsettings/wslsettings.exe,wslinstallerproxystub.dll,wsldevicehost.dll,WSLDVCPlugin.dll,testplugin.dll,wsldeps.dll" + pattern: "wsl.exe,libwsl.dll,wslg.exe,wslservice.exe,wslhost.exe,wslrelay.exe,wslinstaller.exe,wslinstall.dll,wslserviceproxystub.dll,wslsettings/wslsettings.dll,wslsettings/wslsettings.exe,wslinstallerproxystub.dll,WSLDVCPlugin.dll,testplugin.dll,wsldeps.dll" - target: "msixgluepackage" pattern: "gluepackage.msix" - target: "msipackage" diff --git a/CMakeLists.txt b/CMakeLists.txt index d55d3a9ee..de4dce8c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,7 +157,6 @@ set (GENERATED_DIR ${CMAKE_BINARY_DIR}/generated) file(MAKE_DIRECTORY ${GENERATED_DIR}) set(PACKAGE_CERTIFICATE ${GENERATED_DIR}/dev-cert.pfx) -file(CREATE_LINK ${WSL_DEVICE_HOST_SOURCE_DIR}/bin/${TARGET_PLATFORM}/wsldevicehost.dll ${BIN}/wsldevicehost.dll) file(CREATE_LINK ${WSLG_SOURCE_DIR}/${TARGET_PLATFORM}/${WSLG_TS_PLUGIN_DLL} ${BIN}/${WSLG_TS_PLUGIN_DLL}) file(CREATE_LINK ${WSLDEPS_SOURCE_DIR}/bin/wsldeps.dll ${BIN}/wsldeps.dll) diff --git a/msipackage/package.wix.in b/msipackage/package.wix.in index 6ebc9f3b2..96218c49a 100644 --- a/msipackage/package.wix.in +++ b/msipackage/package.wix.in @@ -221,6 +221,8 @@ N.B. It shouldn't be started on install because it will fail to start if the OC is missing, which would fail the install. --> + + @@ -232,7 +234,6 @@ - @@ -398,6 +399,14 @@ Execute="deferred" /> + + + + + + - - - + diff --git a/src/windows/wslinstall/CMakeLists.txt b/src/windows/wslinstall/CMakeLists.txt index a5b84bd76..4f9fcf439 100644 --- a/src/windows/wslinstall/CMakeLists.txt +++ b/src/windows/wslinstall/CMakeLists.txt @@ -14,5 +14,4 @@ target_link_libraries(wslinstall common legacy_stdio_definitions Crypt32.lib - sfc.lib - rstrtmgr.lib) \ No newline at end of file + sfc.lib) \ No newline at end of file diff --git a/src/windows/wslinstall/DllMain.cpp b/src/windows/wslinstall/DllMain.cpp index 8ccfc5df2..c9ab7d800 100644 --- a/src/windows/wslinstall/DllMain.cpp +++ b/src/windows/wslinstall/DllMain.cpp @@ -19,7 +19,6 @@ Module Name: #include #include #include -#include #include "defs.h" using unique_msi_handle = wil::unique_any; @@ -801,46 +800,6 @@ extern "C" UINT __stdcall WslFinalizeInstallation(MSIHANDLE install) return NOERROR; } -extern "C" UINT __stdcall StopDeviceHostSurrogate(MSIHANDLE install) -{ - // Use the Restart Manager to find and shut down any process that has wsldevicehost.dll locked. - // This must run before InstallFiles to ensure the DLL can be overwritten when installing files. - - try - { - WSL_INSTALL_LOG("StopDeviceHostSurrogate"); - - const auto installRoot = wsl::windows::common::wslutil::GetMsiPackagePath(); - if (!installRoot.has_value()) - { - return NOERROR; - } - - const auto dllPath = installRoot.value() + L"wsldevicehost.dll"; - - DWORD session{}; - WCHAR sessionKey[CCH_RM_SESSION_KEY + 1]{}; - THROW_IF_WIN32_ERROR(RmStartSession(&session, 0, sessionKey)); - auto endSession = wil::scope_exit([&] { RmEndSession(session); }); - - LPCWSTR file = dllPath.c_str(); - THROW_IF_WIN32_ERROR(RmRegisterResources(session, 1, &file, 0, nullptr, 0, nullptr)); - - // First attempt a graceful shutdown of processes using wsldevicehost.dll. - DWORD rmError = RmShutdown(session, 0, nullptr); - if (rmError != ERROR_SUCCESS) - { - // If graceful shutdown failed, do a forced shutdown. - WSL_INSTALL_LOG("StopDeviceHostSurrogate: graceful shutdown failed, attempting forced shutdown."); - THROW_IF_WIN32_ERROR(RmShutdown(session, RmForceShutdown, nullptr)); - } - } - CATCH_LOG(); - - // Always succeed — failure to stop the surrogate is not fatal. - return NOERROR; -} - extern "C" UINT __stdcall WslValidateInstallation(MSIHANDLE install) try { diff --git a/src/windows/wslinstall/wslinstall.def b/src/windows/wslinstall/wslinstall.def index 9badfd990..ec3577ff4 100644 --- a/src/windows/wslinstall/wslinstall.def +++ b/src/windows/wslinstall/wslinstall.def @@ -4,7 +4,6 @@ EXPORTS CleanExplorerState CleanMsixState DeprovisionMsix - StopDeviceHostSurrogate WslValidateInstallation WslFinalizeInstallation InstallMsix From 7156342a906871cf3b0274121dc1334e86770c85 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Fri, 3 Apr 2026 11:15:49 -0700 Subject: [PATCH 4/4] Release COM DLLs before installer test MSI operations (#40093) * Release COM DLLs before installer test MSI operations Add PrepareForMsiOperation() that calls CoFreeUnusedLibrariesEx(0) before each msiexec invocation. This releases in-process COM DLLs like wslserviceproxystub.dll loaded by prior test classes, preventing the Restart Manager from detecting the test process as holding file locks and failing the install on older Server SKUs like ni_release. * fix CoFreeUnusedLibrariesEx --------- Co-authored-by: Ben Hillis --- test/windows/InstallerTests.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/windows/InstallerTests.cpp b/test/windows/InstallerTests.cpp index c578ba91a..423b05815 100644 --- a/test/windows/InstallerTests.cpp +++ b/test/windows/InstallerTests.cpp @@ -175,8 +175,17 @@ class InstallerTests return wsl::windows::common::registry::ReadString(m_lxssKey.get(), L"MSI", L"ProductCode", L""); } + // Release any in-process COM DLLs (e.g. wslserviceproxystub.dll loaded by prior tests) + // so the Restart Manager doesn't detect the test process as holding files. + // This avoids install failures on older Server SKUs where the RM has stricter silent-mode behavior. + static void PrepareForMsiOperation() + { + CoFreeUnusedLibrariesEx(0, 0); + } + void UninstallMsi() { + PrepareForMsiOperation(); auto productCode = GetMsiProductCode(); VERIFY_IS_FALSE(productCode.empty()); @@ -185,6 +194,7 @@ class InstallerTests void InstallMsi() { + PrepareForMsiOperation(); CallMsiExec(std::format(L"/qn /norestart /i {} /L*V {}", m_msiPath, GenerateMsiLogPath())); } @@ -367,6 +377,7 @@ class InstallerTests LogInfo("Installing: %ls", installerFile.c_str()); if (wsl::shared::string::EndsWith(installerFile, L".msi")) { + PrepareForMsiOperation(); CallMsiExec(std::format(L"/qn /norestart /i {} /L*V {}", installerFile, GenerateMsiLogPath())); } else