From c3be5325e1f95683d241240e9c26456b13a99c0e Mon Sep 17 00:00:00 2001 From: "Haonan Tang (from Dev Box)" Date: Fri, 24 Apr 2026 16:25:36 +0800 Subject: [PATCH 1/6] HybridDeploy: switch unpackaged path to always-on URFW detour Co-Authored-By: Claude Opus 4.7 (1M context) --- .../WindowsAppRuntimeAutoInitializer.cpp | 20 ++++++++++++ .../WindowsAppRuntimeAutoInitializer.cs | 8 +++++ dev/UndockedRegFreeWinRT/urfw.cpp | 22 +++---------- .../MddBootstrap.cpp | 32 +++++++++++++++++++ 4 files changed, 65 insertions(+), 17 deletions(-) diff --git a/dev/Common/WindowsAppRuntimeAutoInitializer.cpp b/dev/Common/WindowsAppRuntimeAutoInitializer.cpp index 543e7348e4..710407f1b6 100644 --- a/dev/Common/WindowsAppRuntimeAutoInitializer.cpp +++ b/dev/Common/WindowsAppRuntimeAutoInitializer.cpp @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation and Contributors. // Licensed under the MIT License. See LICENSE in the project root for license information. +#include + // Forward-declare the various AutoInitialize functions namespace Microsoft::Windows::ApplicationModel::DynamicDependency::Bootstrap { @@ -38,6 +40,24 @@ namespace Microsoft::Windows::ApplicationModel::WindowsAppRuntime::Common static void Initialize() { + // HybridDeploy: URFW reads the app's SxS manifest and expands + // %MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY% inside . + // Set it before Bootstrap runs so the framework DLL's DllMain can load + // the catalog with correct pinned-DLL paths. +#if MICROSOFT_WINDOWSAPPSDK_AUTOINITIALIZE_HYBRIDDEPLOYSETUP + WCHAR exePath[MAX_PATH]{}; + if (::GetModuleFileNameW(nullptr, exePath, ARRAYSIZE(exePath)) > 0) + { + // Strip filename, keep trailing backslash for concatenation. + WCHAR* lastBackslash{ wcsrchr(exePath, L'\\') }; + if (lastBackslash != nullptr) + { + *(lastBackslash + 1) = L'\0'; + ::SetEnvironmentVariableW(L"MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", exePath); + } + } +#endif + // Call the AutoInitialize functions, as needed, starting with those initializing the WindowsAppRuntime #if MICROSOFT_WINDOWSAPPSDK_AUTOINITIALIZE_BOOTSTRAP Microsoft::Windows::ApplicationModel::DynamicDependency::Bootstrap::AutoInitialize::Initialize(); diff --git a/dev/Common/WindowsAppRuntimeAutoInitializer.cs b/dev/Common/WindowsAppRuntimeAutoInitializer.cs index 3505b78cf2..62f547fd78 100644 --- a/dev/Common/WindowsAppRuntimeAutoInitializer.cs +++ b/dev/Common/WindowsAppRuntimeAutoInitializer.cs @@ -14,6 +14,14 @@ class AutoInitialize [global::System.Runtime.CompilerServices.ModuleInitializer] internal static void InitializeWindowsAppSDK() { + // HybridDeploy: URFW reads the app's SxS manifest and expands + // %MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY% inside . + // Set it before Bootstrap runs so the framework DLL's DllMain can load + // the catalog with correct pinned-DLL paths. +#if MICROSOFT_WINDOWSAPPSDK_AUTOINITIALIZE_HYBRIDDEPLOYSETUP + global::System.Environment.SetEnvironmentVariable("MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", global::System.AppContext.BaseDirectory); +#endif + // Call the AutoInitialize functions, as needed, starting with those initializing the WindowsAppRuntime #if MICROSOFT_WINDOWSAPPSDK_AUTOINITIALIZE_BOOTSTRAP Microsoft.Windows.ApplicationModel.DynamicDependency.BootstrapCS.AutoInitialize.AccessWindowsAppSDK(); diff --git a/dev/UndockedRegFreeWinRT/urfw.cpp b/dev/UndockedRegFreeWinRT/urfw.cpp index e44847f464..bb6a12db18 100644 --- a/dev/UndockedRegFreeWinRT/urfw.cpp +++ b/dev/UndockedRegFreeWinRT/urfw.cpp @@ -9,8 +9,6 @@ #include #include -#include - #include "urfw.h" #include "catalog.h" @@ -405,27 +403,17 @@ HRESULT ExtRoLoadCatalog() return S_OK; } -static bool UrfwUseOSImplementation() noexcept -{ - // Delegate to the OS' implementation on >= Windows 11 24H1 - return WindowsVersion::IsWindows11_24H1OrGreater(); -} - bool UrfwNeedsDetours() noexcept { - // Detour to our own implementation if Windows doesn't provide a sufficient implementation - return !UrfwUseOSImplementation(); + // pureUrfw: always detour. Hybrid deploy requires URFW to intercept + // RoGetActivationFactory before combase so that pinned-component classes + // resolve via the SxS manifest's loadFrom (pointing to the app's bin dir) + // rather than via the package graph (which points to the framework package). + return true; } HRESULT UrfwInitialize() noexcept { - // Delegate to the OS' implementation if we can - if (UrfwUseOSImplementation()) - { - return S_OK; - } - - // OS Reg-Free WinRT isn't available so let's do it ourselves... DetourAttach(&(PVOID&)TrueRoActivateInstance, RoActivateInstanceDetour); DetourAttach(&(PVOID&)TrueRoGetActivationFactory, RoGetActivationFactoryDetour); DetourAttach(&(PVOID&)TrueRoGetMetaDataFile, RoGetMetaDataFileDetour); diff --git a/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp b/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp index 1bc1a48409..eeca79f102 100644 --- a/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp +++ b/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp @@ -14,6 +14,32 @@ #include +// Publish the framework package's install directory via env var. +// WinUI's LoadLibraryAbs consults this when a sibling DLL is not next to +// Microsoft.UI.Xaml.dll (the hybrid case, where Microsoft.UI.Xaml.dll is +// pinned in the app's bin dir while siblings remain in the framework). +// In non-hybrid apps the env var is still set but WinUI's primary path +// (muxPath) succeeds first and this fallback never fires. +static void SetFrameworkPathEnvironmentVariable(PCWSTR frameworkPath) +{ + SetEnvironmentVariableW(L"MICROSOFT_WINDOWSAPPRUNTIME_FRAMEWORK_PATH", frameworkPath); +} + +// Resolve a framework package's install path from its full name (Win11 path +// receives only a full name from MddCore::Win11::AddPackageDependency). +static std::wstring GetFrameworkPackagePath(PCWSTR packageFullName) +{ + UINT32 pathLength{ 0 }; + const auto rc{ GetPackagePathByFullName(packageFullName, &pathLength, nullptr) }; + if (rc != ERROR_INSUFFICIENT_BUFFER) + { + THROW_WIN32(rc); + } + auto path{ std::make_unique(pathLength) }; + THROW_IF_WIN32_ERROR(GetPackagePathByFullName(packageFullName, &pathLength, path.get())); + return std::wstring(path.get()); +} + HRESULT _MddBootstrapInitialize( UINT32 majorMinorVersion, PCWSTR versionTag, @@ -430,6 +456,9 @@ void FirstTimeInitialization( wil::unique_process_heap_string packageFullName; THROW_IF_FAILED(MddCore::Win11::AddPackageDependency(packageDependencyId.get(), MDD_PACKAGE_DEPENDENCY_RANK_DEFAULT, addOptions, &packageDependencyContext, &packageFullName)); + // Publish framework path for WinUI's sibling-DLL fallback (see helper comment). + SetFrameworkPathEnvironmentVariable(GetFrameworkPackagePath(packageFullName.get()).c_str()); + // Update the activity context auto& activityContext{ WindowsAppRuntime::MddBootstrap::Activity::Context::Get() }; activityContext.SetInitializationPackageFullName(packageFullName.get()); @@ -488,6 +517,9 @@ void FirstTimeInitialization( MDD_PACKAGEDEPENDENCY_CONTEXT packageDependencyContext{}; THROW_IF_FAILED(MddAddPackageDependency(packageDependencyId.get(), MDD_PACKAGE_DEPENDENCY_RANK_DEFAULT, addOptions, &packageDependencyContext, nullptr)); + // Publish framework path for WinUI's sibling-DLL fallback (see helper comment). + SetFrameworkPathEnvironmentVariable(frameworkPackageInfo->path); + // Remove our temporary path addition RemoveFrameworkFromPath(frameworkPackageInfo->path); dllDirectoryCookie.reset(); From f652e98f421c261dbe55cfd380ac55593ceb3626 Mon Sep 17 00:00:00 2001 From: "Haonan Tang (from Dev Box)" Date: Mon, 27 Apr 2026 14:39:46 +0800 Subject: [PATCH 2/6] HybridDeploy: pre-load pinned Microsoft.WindowsAppRuntime.dll from app dir Co-Authored-By: Claude Opus 4.7 (1M context) --- .../MddBootstrap.cpp | 68 ++++++++++++++++++- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp b/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp index eeca79f102..f92a21bae1 100644 --- a/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp +++ b/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp @@ -25,6 +25,59 @@ static void SetFrameworkPathEnvironmentVariable(PCWSTR frameworkPath) SetEnvironmentVariableW(L"MICROSOFT_WINDOWSAPPRUNTIME_FRAMEWORK_PATH", frameworkPath); } +// Ensure Microsoft.WindowsAppRuntime.dll is loaded into the process, preferring +// the pinned (NEW) copy in BASE_DIRECTORY (app bin) over the framework copy. +// This is the URFW-detour bootstrapping point: the detour is installed by this +// DLL's DllMain, so whichever physical file gets loaded first determines which +// version of UrfwInitialize runs. If the framework copy were loaded first +// (e.g. because the OS package graph raises framework-dir search priority +// above app-dir), an older UrfwInitialize might early-return on Win11 24H2+ +// and skip detour installation, causing pinned class activations to fall +// through to combase Step 4 and use the framework version instead. +// +// Loading by absolute path with LOAD_WITH_ALTERED_SEARCH_PATH bypasses the +// OS DLL search order entirely, making this race-free regardless of how the +// package graph is later configured. +// +// Falls back to the framework copy when no pinned copy exists in app dir +// (non-hybrid builds, or hybrid scenarios where Foundation is not the pinned +// component, e.g. hybrid-WinUI). The framework fallback is required for the +// Win10 path which must have this DLL loaded before calling MddTryCreate / +// MddAddPackageDependency. On Win11 the framework load happens later via +// the package graph, so callers may pass nullptr to skip the fallback. +static wil::unique_hmodule EnsureFoundationDllLoaded(PCWSTR frameworkPath) noexcept +{ + // 1. Prefer the pinned copy in app dir. + WCHAR baseDir[MAX_PATH]{}; + const DWORD chars{ ::GetEnvironmentVariableW( + L"MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", baseDir, ARRAYSIZE(baseDir)) }; + if (chars > 0 && chars < ARRAYSIZE(baseDir)) + { + std::wstring pinned{ baseDir }; + pinned += L"Microsoft.WindowsAppRuntime.dll"; + if (::GetFileAttributesW(pinned.c_str()) != INVALID_FILE_ATTRIBUTES) + { + wil::unique_hmodule hmod{ ::LoadLibraryExW(pinned.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH) }; + if (hmod) + { + return hmod; + } + } + } + + // 2. Fall back to the framework copy. + if (frameworkPath != nullptr) + { + std::wstring framework{ frameworkPath }; + framework += L"\\Microsoft.WindowsAppRuntime.dll"; + return wil::unique_hmodule{ ::LoadLibraryExW(framework.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH) }; + } + + // 3. No fallback available; caller is responsible for ensuring the DLL + // gets loaded later (e.g. Win11 path relies on package graph + later PInvoke). + return wil::unique_hmodule{}; +} + // Resolve a framework package's install path from its full name (Win11 path // receives only a full name from MddCore::Win11::AddPackageDependency). static std::wstring GetFrameworkPackagePath(PCWSTR packageFullName) @@ -443,6 +496,13 @@ void FirstTimeInitialization( const UINT32 minVersionToUseWin11Support{ 0x00010007 }; if ((majorMinorVersion >= minVersionToUseWin11Support) && MddCore::Win11::IsSupported()) { + // HybridDeploy: pre-load the pinned Microsoft.WindowsAppRuntime.dll from + // app dir if present, BEFORE Win11::AddPackageDependency raises framework + // search priority. See helper comment for rationale. No framework + // fallback here — package graph + later PInvoke will load framework if + // app dir has no pinned copy. + EnsureFoundationDllLoaded(nullptr); + // Add the framework package to the package graph const std::wstring frameworkPackageFamilyName{ GetFrameworkPackageFamilyName(majorMinorVersion, packageVersionTag.c_str()) }; const MddPackageDependencyProcessorArchitectures architectureFilter{}; @@ -498,12 +558,14 @@ void FirstTimeInitialization( // Temporarily add the framework's package directory to PATH so LoadLibrary can find it and any colocated imports wil::unique_dll_directory_cookie dllDirectoryCookie{ AddFrameworkToPath(frameworkPackageInfo->path) }; - auto windowsAppRuntimeDllFilename{ std::wstring(frameworkPackageInfo->path) + L"\\Microsoft.WindowsAppRuntime.dll" }; - wil::unique_hmodule windowsAppRuntimeDll(LoadLibraryEx(windowsAppRuntimeDllFilename.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH)); + // HybridDeploy: prefer the pinned (NEW) Microsoft.WindowsAppRuntime.dll + // from app dir if present; otherwise fall back to the framework copy. + // See helper comment for rationale. + wil::unique_hmodule windowsAppRuntimeDll{ EnsureFoundationDllLoaded(frameworkPackageInfo->path) }; if (!windowsAppRuntimeDll) { const auto lastError{ GetLastError() }; - THROW_WIN32_MSG(lastError, "Error in LoadLibrary: %d (0x%X) loading %ls", lastError, lastError, windowsAppRuntimeDllFilename.c_str()); + THROW_WIN32_MSG(lastError, "Error in LoadLibrary: %d (0x%X) loading Microsoft.WindowsAppRuntime.dll (framework=%ls)", lastError, lastError, frameworkPackageInfo->path); } // Add the framework package to the package graph From b88ab9d41f096be5cfbfbc7995e20900fe407dc0 Mon Sep 17 00:00:00 2001 From: "Haonan Tang (from Dev Box)" Date: Tue, 28 Apr 2026 10:17:24 +0800 Subject: [PATCH 3/6] HybridDeploy: add temporary diagnostic logging for pre-load investigation Co-Authored-By: Claude Opus 4.7 (1M context) --- .../MddBootstrap.cpp | 108 +++++++++++++++++- .../dllmain.cpp | 35 ++++++ dev/WindowsAppRuntime_DLL/dllmain.cpp | 34 ++++++ 3 files changed, 175 insertions(+), 2 deletions(-) diff --git a/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp b/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp index f92a21bae1..f2cc0db1d2 100644 --- a/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp +++ b/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp @@ -14,6 +14,57 @@ #include +// ====== TEMPORARY DIAGNOSTIC LOGGING (remove before final merge) ====== +// Writes to %TEMP%\preload-debug.log to diagnose pinned-DLL pre-load behavior. +// All entries are append-only and tagged with a phase label. +static void DiagWrite(PCWSTR phase, PCWSTR detail) noexcept +{ + WCHAR temp[MAX_PATH]{}; + const DWORD chars{ ::GetEnvironmentVariableW(L"TEMP", temp, ARRAYSIZE(temp)) }; + if (chars == 0 || chars >= ARRAYSIZE(temp)) return; + std::wstring path{ temp }; + path += L"\\preload-debug.log"; + HANDLE h{ ::CreateFileW(path.c_str(), FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr) }; + if (h == INVALID_HANDLE_VALUE) return; + SYSTEMTIME st{}; + ::GetSystemTime(&st); + WCHAR line[2048]{}; + swprintf_s(line, L"[%02u:%02u:%02u.%03u tid=%lu] %ls: %ls\r\n", + st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, + ::GetCurrentThreadId(), phase, detail ? detail : L""); + char narrow[4096]{}; + int n = ::WideCharToMultiByte(CP_UTF8, 0, line, -1, narrow, sizeof(narrow), nullptr, nullptr); + if (n > 0) + { + ::SetFilePointer(h, 0, nullptr, FILE_END); + DWORD written{}; + ::WriteFile(h, narrow, static_cast(n - 1), &written, nullptr); + } + ::CloseHandle(h); +} + +static void DiagSnapshotMicrosoftWindowsAppRuntimeDll(PCWSTR phase) noexcept +{ + // Enumerate all currently-loaded modules whose name matches Microsoft.WindowsAppRuntime.dll. + HMODULE byName{ ::GetModuleHandleW(L"Microsoft.WindowsAppRuntime.dll") }; + if (!byName) + { + DiagWrite(phase, L"GetModuleHandle('Microsoft.WindowsAppRuntime.dll') = NULL (not loaded)"); + return; + } + WCHAR p[MAX_PATH * 2]{}; + ::GetModuleFileNameW(byName, p, ARRAYSIZE(p)); + std::wstring m{ L"GetModuleHandle by name returned hmod=" }; + WCHAR buf[64]{}; + swprintf_s(buf, L"0x%p, file=", byName); + m += buf; + m += p; + DiagWrite(phase, m.c_str()); +} +// ====== END DIAGNOSTIC ====== + // Publish the framework package's install directory via env var. // WinUI's LoadLibraryAbs consults this when a sibling DLL is not next to // Microsoft.UI.Xaml.dll (the hybrid case, where Microsoft.UI.Xaml.dll is @@ -47,34 +98,81 @@ static void SetFrameworkPathEnvironmentVariable(PCWSTR frameworkPath) // the package graph, so callers may pass nullptr to skip the fallback. static wil::unique_hmodule EnsureFoundationDllLoaded(PCWSTR frameworkPath) noexcept { + DiagWrite(L"EnsureFoundationDllLoaded.entry", + frameworkPath ? frameworkPath : L"(null framework path = Win11 mode)"); + DiagSnapshotMicrosoftWindowsAppRuntimeDll(L"EnsureFoundationDllLoaded.entry.snapshot"); + // 1. Prefer the pinned copy in app dir. WCHAR baseDir[MAX_PATH]{}; const DWORD chars{ ::GetEnvironmentVariableW( L"MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", baseDir, ARRAYSIZE(baseDir)) }; + { + WCHAR m[2048]{}; + swprintf_s(m, L"BASE_DIRECTORY chars=%lu value='%ls'", chars, chars > 0 ? baseDir : L""); + DiagWrite(L"EnsureFoundationDllLoaded", m); + } if (chars > 0 && chars < ARRAYSIZE(baseDir)) { std::wstring pinned{ baseDir }; pinned += L"Microsoft.WindowsAppRuntime.dll"; - if (::GetFileAttributesW(pinned.c_str()) != INVALID_FILE_ATTRIBUTES) + DWORD attrs{ ::GetFileAttributesW(pinned.c_str()) }; + { + WCHAR m[2048]{}; + swprintf_s(m, L"GetFileAttributes('%ls') = 0x%08lX", pinned.c_str(), attrs); + DiagWrite(L"EnsureFoundationDllLoaded", m); + } + if (attrs != INVALID_FILE_ATTRIBUTES) { wil::unique_hmodule hmod{ ::LoadLibraryExW(pinned.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH) }; + const DWORD lastError{ ::GetLastError() }; + { + WCHAR m[2048]{}; + swprintf_s(m, L"LoadLibraryExW(pinned='%ls') hmod=0x%p lastError=%lu", + pinned.c_str(), hmod.get(), lastError); + DiagWrite(L"EnsureFoundationDllLoaded", m); + } if (hmod) { + WCHAR resolved[MAX_PATH * 2]{}; + ::GetModuleFileNameW(hmod.get(), resolved, ARRAYSIZE(resolved)); + WCHAR m[2048]{}; + swprintf_s(m, L"LoadLibraryExW SUCCESS, resolved actual path = '%ls'", resolved); + DiagWrite(L"EnsureFoundationDllLoaded.success", m); return hmod; } + DiagWrite(L"EnsureFoundationDllLoaded.warn", L"LoadLibraryExW(pinned) returned NULL"); + } + else + { + DiagWrite(L"EnsureFoundationDllLoaded", L"pinned file does not exist, skipping"); } } + else + { + DiagWrite(L"EnsureFoundationDllLoaded", L"BASE_DIRECTORY env var empty/missing, skipping pinned attempt"); + } // 2. Fall back to the framework copy. if (frameworkPath != nullptr) { std::wstring framework{ frameworkPath }; framework += L"\\Microsoft.WindowsAppRuntime.dll"; - return wil::unique_hmodule{ ::LoadLibraryExW(framework.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH) }; + DiagWrite(L"EnsureFoundationDllLoaded.fallback", framework.c_str()); + wil::unique_hmodule hmod{ ::LoadLibraryExW(framework.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH) }; + if (hmod) + { + WCHAR resolved[MAX_PATH * 2]{}; + ::GetModuleFileNameW(hmod.get(), resolved, ARRAYSIZE(resolved)); + WCHAR m[2048]{}; + swprintf_s(m, L"framework fallback SUCCESS, resolved = '%ls'", resolved); + DiagWrite(L"EnsureFoundationDllLoaded.fallback.success", m); + } + return hmod; } // 3. No fallback available; caller is responsible for ensuring the DLL // gets loaded later (e.g. Win11 path relies on package graph + later PInvoke). + DiagWrite(L"EnsureFoundationDllLoaded.exit", L"no fallback, returning empty"); return wil::unique_hmodule{}; } @@ -482,6 +580,9 @@ void FirstTimeInitialization( PCWSTR versionTag, PACKAGE_VERSION minVersion) { + DiagWrite(L"FirstTimeInitialization.entry", L"---- begin ----"); + DiagSnapshotMicrosoftWindowsAppRuntimeDll(L"FirstTimeInitialization.entry.snapshot"); + // Sanity check we're not already initialized // g_lifetimeManager is optional. Don't check it // g_endTheLifetimeManagerEvent is optional. Don't check it @@ -496,12 +597,15 @@ void FirstTimeInitialization( const UINT32 minVersionToUseWin11Support{ 0x00010007 }; if ((majorMinorVersion >= minVersionToUseWin11Support) && MddCore::Win11::IsSupported()) { + DiagWrite(L"FirstTimeInitialization", L"taking Win11 path"); + // HybridDeploy: pre-load the pinned Microsoft.WindowsAppRuntime.dll from // app dir if present, BEFORE Win11::AddPackageDependency raises framework // search priority. See helper comment for rationale. No framework // fallback here — package graph + later PInvoke will load framework if // app dir has no pinned copy. EnsureFoundationDllLoaded(nullptr); + DiagSnapshotMicrosoftWindowsAppRuntimeDll(L"FirstTimeInitialization.afterPreload.snapshot"); // Add the framework package to the package graph const std::wstring frameworkPackageFamilyName{ GetFrameworkPackageFamilyName(majorMinorVersion, packageVersionTag.c_str()) }; diff --git a/dev/WindowsAppRuntime_BootstrapDLL/dllmain.cpp b/dev/WindowsAppRuntime_BootstrapDLL/dllmain.cpp index fea531256b..ade999c6be 100644 --- a/dev/WindowsAppRuntime_BootstrapDLL/dllmain.cpp +++ b/dev/WindowsAppRuntime_BootstrapDLL/dllmain.cpp @@ -9,14 +9,49 @@ // begin logging a new error. This greatly improves the debuggability of errors that propagate before a failfast. #include +// ====== TEMPORARY DIAGNOSTIC LOGGING (remove before final merge) ====== +static void DiagWriteFromDllMain(PCWSTR phase, PCWSTR detail) noexcept +{ + WCHAR temp[MAX_PATH]{}; + const DWORD chars{ ::GetEnvironmentVariableW(L"TEMP", temp, ARRAYSIZE(temp)) }; + if (chars == 0 || chars >= ARRAYSIZE(temp)) return; + std::wstring path{ temp }; + path += L"\\preload-debug.log"; + HANDLE h{ ::CreateFileW(path.c_str(), FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr) }; + if (h == INVALID_HANDLE_VALUE) return; + SYSTEMTIME st{}; + ::GetSystemTime(&st); + HMODULE wprt{ ::GetModuleHandleW(L"Microsoft.WindowsAppRuntime.dll") }; + WCHAR wprtPath[MAX_PATH * 2]{}; + if (wprt) { ::GetModuleFileNameW(wprt, wprtPath, ARRAYSIZE(wprtPath)); } + WCHAR line[2048]{}; + swprintf_s(line, L"[%02u:%02u:%02u.%03u tid=%lu] %ls: %ls (Microsoft.WindowsAppRuntime.dll handle=0x%p path='%ls')\r\n", + st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, + ::GetCurrentThreadId(), phase, detail ? detail : L"", wprt, wprt ? wprtPath : L""); + char narrow[4096]{}; + int n = ::WideCharToMultiByte(CP_UTF8, 0, line, -1, narrow, sizeof(narrow), nullptr, nullptr); + if (n > 0) + { + ::SetFilePointer(h, 0, nullptr, FILE_END); + DWORD written{}; + ::WriteFile(h, narrow, static_cast(n - 1), &written, nullptr); + } + ::CloseHandle(h); +} +// ====== END DIAGNOSTIC ====== + BOOL APIENTRY DllMain(HMODULE hmodule, DWORD reason, LPVOID reserved) { switch (reason) { case DLL_PROCESS_ATTACH: { + DiagWriteFromDllMain(L"Bootstrap.dll.DllMain", L"DLL_PROCESS_ATTACH begin"); DisableThreadLibraryCalls(hmodule); FAIL_FAST_IF_FAILED(MddWin11Initialize()); + DiagWriteFromDllMain(L"Bootstrap.dll.DllMain", L"DLL_PROCESS_ATTACH end (after MddWin11Initialize)"); break; } case DLL_PROCESS_DETACH: diff --git a/dev/WindowsAppRuntime_DLL/dllmain.cpp b/dev/WindowsAppRuntime_DLL/dllmain.cpp index 19587d0085..a57262a931 100644 --- a/dev/WindowsAppRuntime_DLL/dllmain.cpp +++ b/dev/WindowsAppRuntime_DLL/dllmain.cpp @@ -65,15 +65,49 @@ static HRESULT DetoursShutdown() return S_OK; } +// ====== TEMPORARY DIAGNOSTIC LOGGING (remove before final merge) ====== +static void DiagLogFrameworkDllMain(PCWSTR phase, HMODULE selfHmodule) noexcept +{ + WCHAR temp[MAX_PATH]{}; + const DWORD chars{ ::GetEnvironmentVariableW(L"TEMP", temp, ARRAYSIZE(temp)) }; + if (chars == 0 || chars >= ARRAYSIZE(temp)) return; + std::wstring path{ temp }; + path += L"\\preload-debug.log"; + HANDLE h{ ::CreateFileW(path.c_str(), FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr) }; + if (h == INVALID_HANDLE_VALUE) return; + SYSTEMTIME st{}; + ::GetSystemTime(&st); + WCHAR selfPath[MAX_PATH * 2]{}; + ::GetModuleFileNameW(selfHmodule, selfPath, ARRAYSIZE(selfPath)); + WCHAR line[2048]{}; + swprintf_s(line, L"[%02u:%02u:%02u.%03u tid=%lu] FrameworkDLL.DllMain %ls: hmod=0x%p path='%ls'\r\n", + st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, + ::GetCurrentThreadId(), phase, selfHmodule, selfPath); + char narrow[4096]{}; + int n = ::WideCharToMultiByte(CP_UTF8, 0, line, -1, narrow, sizeof(narrow), nullptr, nullptr); + if (n > 0) + { + ::SetFilePointer(h, 0, nullptr, FILE_END); + DWORD written{}; + ::WriteFile(h, narrow, static_cast(n - 1), &written, nullptr); + } + ::CloseHandle(h); +} +// ====== END DIAGNOSTIC ====== + BOOL APIENTRY DllMain(HMODULE hmodule, DWORD reason, LPVOID reserved) { switch (reason) { case DLL_PROCESS_ATTACH: { + DiagLogFrameworkDllMain(L"DLL_PROCESS_ATTACH begin", hmodule); DisableThreadLibraryCalls(hmodule); FAIL_FAST_IF_FAILED(MddWin11Initialize()); FAIL_FAST_IF_FAILED(DetoursInitialize()); + DiagLogFrameworkDllMain(L"DLL_PROCESS_ATTACH end (after DetoursInitialize)", hmodule); break; } case DLL_PROCESS_DETACH: From e202a3cc838cc1097065ded7c643c58349bb184e Mon Sep 17 00:00:00 2001 From: "Haonan Tang (from Dev Box)" Date: Wed, 29 Apr 2026 10:01:52 +0800 Subject: [PATCH 4/6] HybridDeploy: keep pre-loaded Microsoft.WindowsAppRuntime.dll alive on Win11 path Co-Authored-By: Claude Opus 4.7 (1M context) --- dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp b/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp index f2cc0db1d2..27bf185fd2 100644 --- a/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp +++ b/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp @@ -604,7 +604,12 @@ void FirstTimeInitialization( // search priority. See helper comment for rationale. No framework // fallback here — package graph + later PInvoke will load framework if // app dir has no pinned copy. - EnsureFoundationDllLoaded(nullptr); + // + // CRITICAL: store the returned hmod into g_windowsAppRuntimeDll. Without + // this, the wil::unique_hmodule destructor would free the DLL immediately + // after the call, undoing the pre-load. (Win10 path does the same store + // a few lines lower, just for the framework load.) + g_windowsAppRuntimeDll = EnsureFoundationDllLoaded(nullptr); DiagSnapshotMicrosoftWindowsAppRuntimeDll(L"FirstTimeInitialization.afterPreload.snapshot"); // Add the framework package to the package graph From cb887b2278bd281c5b7012f6b7cf917a76735876 Mon Sep 17 00:00:00 2001 From: "Haonan Tang (from Dev Box)" Date: Wed, 29 Apr 2026 13:09:36 +0800 Subject: [PATCH 5/6] HybridDeploy: remove temporary diagnostic logging Co-Authored-By: Claude Opus 4.7 (1M context) --- .../MddBootstrap.cpp | 108 +----------------- .../dllmain.cpp | 37 +----- dev/WindowsAppRuntime_DLL/dllmain.cpp | 34 ------ 3 files changed, 3 insertions(+), 176 deletions(-) diff --git a/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp b/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp index 27bf185fd2..e26484cfc5 100644 --- a/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp +++ b/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp @@ -14,57 +14,6 @@ #include -// ====== TEMPORARY DIAGNOSTIC LOGGING (remove before final merge) ====== -// Writes to %TEMP%\preload-debug.log to diagnose pinned-DLL pre-load behavior. -// All entries are append-only and tagged with a phase label. -static void DiagWrite(PCWSTR phase, PCWSTR detail) noexcept -{ - WCHAR temp[MAX_PATH]{}; - const DWORD chars{ ::GetEnvironmentVariableW(L"TEMP", temp, ARRAYSIZE(temp)) }; - if (chars == 0 || chars >= ARRAYSIZE(temp)) return; - std::wstring path{ temp }; - path += L"\\preload-debug.log"; - HANDLE h{ ::CreateFileW(path.c_str(), FILE_APPEND_DATA, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr) }; - if (h == INVALID_HANDLE_VALUE) return; - SYSTEMTIME st{}; - ::GetSystemTime(&st); - WCHAR line[2048]{}; - swprintf_s(line, L"[%02u:%02u:%02u.%03u tid=%lu] %ls: %ls\r\n", - st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, - ::GetCurrentThreadId(), phase, detail ? detail : L""); - char narrow[4096]{}; - int n = ::WideCharToMultiByte(CP_UTF8, 0, line, -1, narrow, sizeof(narrow), nullptr, nullptr); - if (n > 0) - { - ::SetFilePointer(h, 0, nullptr, FILE_END); - DWORD written{}; - ::WriteFile(h, narrow, static_cast(n - 1), &written, nullptr); - } - ::CloseHandle(h); -} - -static void DiagSnapshotMicrosoftWindowsAppRuntimeDll(PCWSTR phase) noexcept -{ - // Enumerate all currently-loaded modules whose name matches Microsoft.WindowsAppRuntime.dll. - HMODULE byName{ ::GetModuleHandleW(L"Microsoft.WindowsAppRuntime.dll") }; - if (!byName) - { - DiagWrite(phase, L"GetModuleHandle('Microsoft.WindowsAppRuntime.dll') = NULL (not loaded)"); - return; - } - WCHAR p[MAX_PATH * 2]{}; - ::GetModuleFileNameW(byName, p, ARRAYSIZE(p)); - std::wstring m{ L"GetModuleHandle by name returned hmod=" }; - WCHAR buf[64]{}; - swprintf_s(buf, L"0x%p, file=", byName); - m += buf; - m += p; - DiagWrite(phase, m.c_str()); -} -// ====== END DIAGNOSTIC ====== - // Publish the framework package's install directory via env var. // WinUI's LoadLibraryAbs consults this when a sibling DLL is not next to // Microsoft.UI.Xaml.dll (the hybrid case, where Microsoft.UI.Xaml.dll is @@ -98,81 +47,34 @@ static void SetFrameworkPathEnvironmentVariable(PCWSTR frameworkPath) // the package graph, so callers may pass nullptr to skip the fallback. static wil::unique_hmodule EnsureFoundationDllLoaded(PCWSTR frameworkPath) noexcept { - DiagWrite(L"EnsureFoundationDllLoaded.entry", - frameworkPath ? frameworkPath : L"(null framework path = Win11 mode)"); - DiagSnapshotMicrosoftWindowsAppRuntimeDll(L"EnsureFoundationDllLoaded.entry.snapshot"); - // 1. Prefer the pinned copy in app dir. WCHAR baseDir[MAX_PATH]{}; const DWORD chars{ ::GetEnvironmentVariableW( L"MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", baseDir, ARRAYSIZE(baseDir)) }; - { - WCHAR m[2048]{}; - swprintf_s(m, L"BASE_DIRECTORY chars=%lu value='%ls'", chars, chars > 0 ? baseDir : L""); - DiagWrite(L"EnsureFoundationDllLoaded", m); - } if (chars > 0 && chars < ARRAYSIZE(baseDir)) { std::wstring pinned{ baseDir }; pinned += L"Microsoft.WindowsAppRuntime.dll"; - DWORD attrs{ ::GetFileAttributesW(pinned.c_str()) }; - { - WCHAR m[2048]{}; - swprintf_s(m, L"GetFileAttributes('%ls') = 0x%08lX", pinned.c_str(), attrs); - DiagWrite(L"EnsureFoundationDllLoaded", m); - } - if (attrs != INVALID_FILE_ATTRIBUTES) + if (::GetFileAttributesW(pinned.c_str()) != INVALID_FILE_ATTRIBUTES) { wil::unique_hmodule hmod{ ::LoadLibraryExW(pinned.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH) }; - const DWORD lastError{ ::GetLastError() }; - { - WCHAR m[2048]{}; - swprintf_s(m, L"LoadLibraryExW(pinned='%ls') hmod=0x%p lastError=%lu", - pinned.c_str(), hmod.get(), lastError); - DiagWrite(L"EnsureFoundationDllLoaded", m); - } if (hmod) { - WCHAR resolved[MAX_PATH * 2]{}; - ::GetModuleFileNameW(hmod.get(), resolved, ARRAYSIZE(resolved)); - WCHAR m[2048]{}; - swprintf_s(m, L"LoadLibraryExW SUCCESS, resolved actual path = '%ls'", resolved); - DiagWrite(L"EnsureFoundationDllLoaded.success", m); return hmod; } - DiagWrite(L"EnsureFoundationDllLoaded.warn", L"LoadLibraryExW(pinned) returned NULL"); - } - else - { - DiagWrite(L"EnsureFoundationDllLoaded", L"pinned file does not exist, skipping"); } } - else - { - DiagWrite(L"EnsureFoundationDllLoaded", L"BASE_DIRECTORY env var empty/missing, skipping pinned attempt"); - } // 2. Fall back to the framework copy. if (frameworkPath != nullptr) { std::wstring framework{ frameworkPath }; framework += L"\\Microsoft.WindowsAppRuntime.dll"; - DiagWrite(L"EnsureFoundationDllLoaded.fallback", framework.c_str()); - wil::unique_hmodule hmod{ ::LoadLibraryExW(framework.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH) }; - if (hmod) - { - WCHAR resolved[MAX_PATH * 2]{}; - ::GetModuleFileNameW(hmod.get(), resolved, ARRAYSIZE(resolved)); - WCHAR m[2048]{}; - swprintf_s(m, L"framework fallback SUCCESS, resolved = '%ls'", resolved); - DiagWrite(L"EnsureFoundationDllLoaded.fallback.success", m); - } - return hmod; + return wil::unique_hmodule{ ::LoadLibraryExW(framework.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH) }; } // 3. No fallback available; caller is responsible for ensuring the DLL // gets loaded later (e.g. Win11 path relies on package graph + later PInvoke). - DiagWrite(L"EnsureFoundationDllLoaded.exit", L"no fallback, returning empty"); return wil::unique_hmodule{}; } @@ -580,9 +482,6 @@ void FirstTimeInitialization( PCWSTR versionTag, PACKAGE_VERSION minVersion) { - DiagWrite(L"FirstTimeInitialization.entry", L"---- begin ----"); - DiagSnapshotMicrosoftWindowsAppRuntimeDll(L"FirstTimeInitialization.entry.snapshot"); - // Sanity check we're not already initialized // g_lifetimeManager is optional. Don't check it // g_endTheLifetimeManagerEvent is optional. Don't check it @@ -597,8 +496,6 @@ void FirstTimeInitialization( const UINT32 minVersionToUseWin11Support{ 0x00010007 }; if ((majorMinorVersion >= minVersionToUseWin11Support) && MddCore::Win11::IsSupported()) { - DiagWrite(L"FirstTimeInitialization", L"taking Win11 path"); - // HybridDeploy: pre-load the pinned Microsoft.WindowsAppRuntime.dll from // app dir if present, BEFORE Win11::AddPackageDependency raises framework // search priority. See helper comment for rationale. No framework @@ -610,7 +507,6 @@ void FirstTimeInitialization( // after the call, undoing the pre-load. (Win10 path does the same store // a few lines lower, just for the framework load.) g_windowsAppRuntimeDll = EnsureFoundationDllLoaded(nullptr); - DiagSnapshotMicrosoftWindowsAppRuntimeDll(L"FirstTimeInitialization.afterPreload.snapshot"); // Add the framework package to the package graph const std::wstring frameworkPackageFamilyName{ GetFrameworkPackageFamilyName(majorMinorVersion, packageVersionTag.c_str()) }; diff --git a/dev/WindowsAppRuntime_BootstrapDLL/dllmain.cpp b/dev/WindowsAppRuntime_BootstrapDLL/dllmain.cpp index ade999c6be..df8c78feb4 100644 --- a/dev/WindowsAppRuntime_BootstrapDLL/dllmain.cpp +++ b/dev/WindowsAppRuntime_BootstrapDLL/dllmain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation and Contributors. +// Copyright (c) Microsoft Corporation and Contributors. // Licensed under the MIT License. #include "pch.h" @@ -9,49 +9,14 @@ // begin logging a new error. This greatly improves the debuggability of errors that propagate before a failfast. #include -// ====== TEMPORARY DIAGNOSTIC LOGGING (remove before final merge) ====== -static void DiagWriteFromDllMain(PCWSTR phase, PCWSTR detail) noexcept -{ - WCHAR temp[MAX_PATH]{}; - const DWORD chars{ ::GetEnvironmentVariableW(L"TEMP", temp, ARRAYSIZE(temp)) }; - if (chars == 0 || chars >= ARRAYSIZE(temp)) return; - std::wstring path{ temp }; - path += L"\\preload-debug.log"; - HANDLE h{ ::CreateFileW(path.c_str(), FILE_APPEND_DATA, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr) }; - if (h == INVALID_HANDLE_VALUE) return; - SYSTEMTIME st{}; - ::GetSystemTime(&st); - HMODULE wprt{ ::GetModuleHandleW(L"Microsoft.WindowsAppRuntime.dll") }; - WCHAR wprtPath[MAX_PATH * 2]{}; - if (wprt) { ::GetModuleFileNameW(wprt, wprtPath, ARRAYSIZE(wprtPath)); } - WCHAR line[2048]{}; - swprintf_s(line, L"[%02u:%02u:%02u.%03u tid=%lu] %ls: %ls (Microsoft.WindowsAppRuntime.dll handle=0x%p path='%ls')\r\n", - st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, - ::GetCurrentThreadId(), phase, detail ? detail : L"", wprt, wprt ? wprtPath : L""); - char narrow[4096]{}; - int n = ::WideCharToMultiByte(CP_UTF8, 0, line, -1, narrow, sizeof(narrow), nullptr, nullptr); - if (n > 0) - { - ::SetFilePointer(h, 0, nullptr, FILE_END); - DWORD written{}; - ::WriteFile(h, narrow, static_cast(n - 1), &written, nullptr); - } - ::CloseHandle(h); -} -// ====== END DIAGNOSTIC ====== - BOOL APIENTRY DllMain(HMODULE hmodule, DWORD reason, LPVOID reserved) { switch (reason) { case DLL_PROCESS_ATTACH: { - DiagWriteFromDllMain(L"Bootstrap.dll.DllMain", L"DLL_PROCESS_ATTACH begin"); DisableThreadLibraryCalls(hmodule); FAIL_FAST_IF_FAILED(MddWin11Initialize()); - DiagWriteFromDllMain(L"Bootstrap.dll.DllMain", L"DLL_PROCESS_ATTACH end (after MddWin11Initialize)"); break; } case DLL_PROCESS_DETACH: diff --git a/dev/WindowsAppRuntime_DLL/dllmain.cpp b/dev/WindowsAppRuntime_DLL/dllmain.cpp index a57262a931..19587d0085 100644 --- a/dev/WindowsAppRuntime_DLL/dllmain.cpp +++ b/dev/WindowsAppRuntime_DLL/dllmain.cpp @@ -65,49 +65,15 @@ static HRESULT DetoursShutdown() return S_OK; } -// ====== TEMPORARY DIAGNOSTIC LOGGING (remove before final merge) ====== -static void DiagLogFrameworkDllMain(PCWSTR phase, HMODULE selfHmodule) noexcept -{ - WCHAR temp[MAX_PATH]{}; - const DWORD chars{ ::GetEnvironmentVariableW(L"TEMP", temp, ARRAYSIZE(temp)) }; - if (chars == 0 || chars >= ARRAYSIZE(temp)) return; - std::wstring path{ temp }; - path += L"\\preload-debug.log"; - HANDLE h{ ::CreateFileW(path.c_str(), FILE_APPEND_DATA, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr) }; - if (h == INVALID_HANDLE_VALUE) return; - SYSTEMTIME st{}; - ::GetSystemTime(&st); - WCHAR selfPath[MAX_PATH * 2]{}; - ::GetModuleFileNameW(selfHmodule, selfPath, ARRAYSIZE(selfPath)); - WCHAR line[2048]{}; - swprintf_s(line, L"[%02u:%02u:%02u.%03u tid=%lu] FrameworkDLL.DllMain %ls: hmod=0x%p path='%ls'\r\n", - st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, - ::GetCurrentThreadId(), phase, selfHmodule, selfPath); - char narrow[4096]{}; - int n = ::WideCharToMultiByte(CP_UTF8, 0, line, -1, narrow, sizeof(narrow), nullptr, nullptr); - if (n > 0) - { - ::SetFilePointer(h, 0, nullptr, FILE_END); - DWORD written{}; - ::WriteFile(h, narrow, static_cast(n - 1), &written, nullptr); - } - ::CloseHandle(h); -} -// ====== END DIAGNOSTIC ====== - BOOL APIENTRY DllMain(HMODULE hmodule, DWORD reason, LPVOID reserved) { switch (reason) { case DLL_PROCESS_ATTACH: { - DiagLogFrameworkDllMain(L"DLL_PROCESS_ATTACH begin", hmodule); DisableThreadLibraryCalls(hmodule); FAIL_FAST_IF_FAILED(MddWin11Initialize()); FAIL_FAST_IF_FAILED(DetoursInitialize()); - DiagLogFrameworkDllMain(L"DLL_PROCESS_ATTACH end (after DetoursInitialize)", hmodule); break; } case DLL_PROCESS_DETACH: From d0cc5d96efb6083f19a8b1421690e150f77f2f6c Mon Sep 17 00:00:00 2001 From: "Haonan Tang (from Dev Box)" Date: Wed, 6 May 2026 10:53:46 +0800 Subject: [PATCH 6/6] URFW detour fallback --- .../MddBootstrap.cpp | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp b/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp index e26484cfc5..e2b8e8f6be 100644 --- a/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp +++ b/dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp @@ -39,12 +39,10 @@ static void SetFrameworkPathEnvironmentVariable(PCWSTR frameworkPath) // OS DLL search order entirely, making this race-free regardless of how the // package graph is later configured. // -// Falls back to the framework copy when no pinned copy exists in app dir -// (non-hybrid builds, or hybrid scenarios where Foundation is not the pinned -// component, e.g. hybrid-WinUI). The framework fallback is required for the -// Win10 path which must have this DLL loaded before calling MddTryCreate / -// MddAddPackageDependency. On Win11 the framework load happens later via -// the package graph, so callers may pass nullptr to skip the fallback. +// Loads APP_DIR pin if present; otherwise falls back to framework path. +// Framework fallback is what runs the DLL's DllMain → URFW always-on detour +// installs. Pass nullptr only if you'll re-invoke with framework path later +// (Win11 path does this around AddPackageDependency). static wil::unique_hmodule EnsureFoundationDllLoaded(PCWSTR frameworkPath) noexcept { // 1. Prefer the pinned copy in app dir. @@ -496,16 +494,9 @@ void FirstTimeInitialization( const UINT32 minVersionToUseWin11Support{ 0x00010007 }; if ((majorMinorVersion >= minVersionToUseWin11Support) && MddCore::Win11::IsSupported()) { - // HybridDeploy: pre-load the pinned Microsoft.WindowsAppRuntime.dll from - // app dir if present, BEFORE Win11::AddPackageDependency raises framework - // search priority. See helper comment for rationale. No framework - // fallback here — package graph + later PInvoke will load framework if - // app dir has no pinned copy. - // - // CRITICAL: store the returned hmod into g_windowsAppRuntimeDll. Without - // this, the wil::unique_hmodule destructor would free the DLL immediately - // after the call, undoing the pre-load. (Win10 path does the same store - // a few lines lower, just for the framework load.) + // Try APP_DIR pin first (before AddPackageDependency raises framework + // search priority). Framework fallback below covers the null case. + // Capture into g_windowsAppRuntimeDll or unique_hmodule frees it. g_windowsAppRuntimeDll = EnsureFoundationDllLoaded(nullptr); // Add the framework package to the package graph @@ -524,6 +515,13 @@ void FirstTimeInitialization( // Publish framework path for WinUI's sibling-DLL fallback (see helper comment). SetFrameworkPathEnvironmentVariable(GetFrameworkPackagePath(packageFullName.get()).c_str()); + // Framework fallback when APP_DIR had no pin — runs DllMain → URFW + // detour. Without this, non-Foundation hybrid apps lose routing. + if (!g_windowsAppRuntimeDll) + { + g_windowsAppRuntimeDll = EnsureFoundationDllLoaded(GetFrameworkPackagePath(packageFullName.get()).c_str()); + } + // Update the activity context auto& activityContext{ WindowsAppRuntime::MddBootstrap::Activity::Context::Get() }; activityContext.SetInitializationPackageFullName(packageFullName.get());