-
Notifications
You must be signed in to change notification settings - Fork 433
[Draft] Modify Bootstrap to support Hybrid #6400
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,52 @@ | |
|
|
||
| #include <filesystem> | ||
|
|
||
| static bool IsHybridDeploy() noexcept | ||
| { | ||
| wchar_t value[2]{}; | ||
| return GetEnvironmentVariableW(L"MICROSOFT_WINDOWSAPPRUNTIME_HYBRID_DEPLOY", value, ARRAYSIZE(value)) > 0; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SET MICROSOFT_WINDOWSAPPRUNTIME_HYBRID_DEPLOY=1 How is this not a massive DOS attack waiting to happen? |
||
| } | ||
|
|
||
| static void SetFrameworkPathEnvironmentVariable(PCWSTR frameworkPath) | ||
| { | ||
| SetEnvironmentVariableW(L"MICROSOFT_WINDOWSAPPRUNTIME_FRAMEWORK_PATH", frameworkPath); | ||
| } | ||
|
|
||
| // For C++ apps (no C# auto-initializer), set BASE_DIRECTORY to exe dir if not already set | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this needed?
|
||
| static void SetBaseDirectoryEnvironmentVariableIfNotSet() | ||
| { | ||
| wchar_t existing[2]{}; | ||
| if (GetEnvironmentVariableW(L"MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", existing, ARRAYSIZE(existing)) > 0) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is very loose in behavior But hard to assess how problematic as I can't find docs or spec about P.S. For an example how to deal with this sort of hackery in a more rigorous fashion see |
||
| { | ||
| return; // Already set by C# auto-initializer | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the C# auto-initializer handles this for C# |
||
| } | ||
| wchar_t exePath[MAX_PATH]{}; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not LongPath friendly |
||
| if (GetModuleFileNameW(nullptr, exePath, ARRAYSIZE(exePath)) > 0) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
You can find uses in winappsdk e.g. |
||
| { | ||
| // Extract directory from exe path and ensure trailing backslash | ||
| auto exeDir{ std::filesystem::path(exePath).parent_path().wstring() }; | ||
| if (!exeDir.empty() && exeDir.back() != L'\\') | ||
| { | ||
| exeDir += L'\\'; | ||
| } | ||
| SetEnvironmentVariableW(L"MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY", exeDir.c_str()); | ||
| } | ||
| } | ||
|
|
||
| // Resolve framework package install path from package full name | ||
| static std::wstring GetFrameworkPackagePath(PCWSTR packageFullName) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better to just use That's only 2 steps away from checkin
|
||
| { | ||
| UINT32 pathLength{ 0 }; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT: |
||
| const auto rc{ GetPackagePathByFullName(packageFullName, &pathLength, nullptr) }; | ||
| if (rc != ERROR_INSUFFICIENT_BUFFER) | ||
| { | ||
| THROW_WIN32(rc); | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT: |
||
| auto path{ std::make_unique<WCHAR[]>(pathLength) }; | ||
| THROW_IF_WIN32_ERROR(GetPackagePathByFullName(packageFullName, &pathLength, path.get())); | ||
| return std::wstring(path.get()); | ||
| } | ||
|
|
||
| HRESULT _MddBootstrapInitialize( | ||
| UINT32 majorMinorVersion, | ||
| PCWSTR versionTag, | ||
|
|
@@ -417,43 +463,75 @@ void FirstTimeInitialization( | |
| const UINT32 minVersionToUseWin11Support{ 0x00010007 }; | ||
| if ((majorMinorVersion >= minVersionToUseWin11Support) && MddCore::Win11::IsSupported()) | ||
| { | ||
| // Add the framework package to the package graph | ||
| const std::wstring frameworkPackageFamilyName{ GetFrameworkPackageFamilyName(majorMinorVersion, packageVersionTag.c_str()) }; | ||
| const MddPackageDependencyProcessorArchitectures architectureFilter{}; | ||
| const auto lifetimeKind{ MddPackageDependencyLifetimeKind::Process }; | ||
| const MddCreatePackageDependencyOptions createOptions{}; | ||
| wil::unique_process_heap_string packageDependencyId; | ||
| THROW_IF_FAILED(MddCore::Win11::TryCreatePackageDependency(nullptr, frameworkPackageFamilyName.c_str(), minVersion, architectureFilter, lifetimeKind, nullptr, createOptions, &packageDependencyId)); | ||
| // | ||
| const MddAddPackageDependencyOptions addOptions{}; | ||
| MDD_PACKAGEDEPENDENCY_CONTEXT packageDependencyContext{}; | ||
| wil::unique_process_heap_string packageFullName; | ||
| THROW_IF_FAILED(MddCore::Win11::AddPackageDependency(packageDependencyId.get(), MDD_PACKAGE_DEPENDENCY_RANK_DEFAULT, addOptions, &packageDependencyContext, &packageFullName)); | ||
|
|
||
| // Update the activity context | ||
| auto& activityContext{ WindowsAppRuntime::MddBootstrap::Activity::Context::Get() }; | ||
| activityContext.SetInitializationPackageFullName(packageFullName.get()); | ||
|
|
||
| // Pass along test information (if necessary) | ||
| if (!g_test_frameworkPackageNamePrefix.empty()) | ||
| const bool hybridDeploy{ IsHybridDeploy() }; | ||
| if (hybridDeploy) | ||
| { | ||
| FAIL_FAST_HR_IF(E_UNEXPECTED, g_test_ddlmPackageNamePrefix.empty()); | ||
| FAIL_FAST_HR_IF(E_UNEXPECTED, g_test_ddlmPackagePublisherId.empty()); | ||
| FAIL_FAST_HR_IF(E_UNEXPECTED, g_test_mainPackageNamePrefix.empty()); | ||
|
|
||
| ::WindowsAppRuntime::VersionInfo::TestInitialize(frameworkPackageFamilyName.c_str()); | ||
| // HybridDeploy: temporarily add to package graph to resolve the framework path, | ||
| // then immediately remove so that the package graph stays empty. | ||
| // An empty package graph lets the SxS manifest loadFrom control DLL loading. | ||
| const MddAddPackageDependencyOptions addOptions{}; | ||
| MDD_PACKAGEDEPENDENCY_CONTEXT tempContext{}; | ||
| wil::unique_process_heap_string packageFullName; | ||
| THROW_IF_FAILED(MddCore::Win11::AddPackageDependency(packageDependencyId.get(), MDD_PACKAGE_DEPENDENCY_RANK_DEFAULT, addOptions, &tempContext, &packageFullName)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MddCore::Win11::* are calls to the OS DynDep APIs I don't understand the intent. It looks like it's playing hide'n'seek with OS DynDep and winappsdk OS-alternative polyfill hacks (SetFrameworkPathEnvironmentVariable, AddDllDirectory, etc). This is all terribly confusing and disturbing, unless I'm missing something. |
||
|
|
||
| const auto frameworkPath{ GetFrameworkPackagePath(packageFullName.get()) }; | ||
|
|
||
| // Remove from package graph immediately — keep it empty for manifest loadFrom | ||
| MddCore::Win11::RemovePackageDependency(tempContext); | ||
|
|
||
| SetFrameworkPathEnvironmentVariable(frameworkPath.c_str()); | ||
| AddDllDirectory(frameworkPath.c_str()); | ||
|
|
||
| // Update the activity context | ||
| auto& activityContext{ WindowsAppRuntime::MddBootstrap::Activity::Context::Get() }; | ||
| activityContext.SetInitializationPackageFullName(packageFullName.get()); | ||
|
|
||
| // Track our initialized state (keep packageDependencyId alive for lifetime protection) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Say whaaat? Track it...but we just removed it (line 487) |
||
| g_usingWin11Support = UsingWin11Support::Yes; | ||
| g_packageDependencyId = std::move(packageDependencyId); | ||
| g_initializationMajorMinorVersion = majorMinorVersion; | ||
| g_initializationVersionTag = std::move(packageVersionTag); | ||
| const auto frameworkPackageIdentity{ ::AppModel::Identity::PackageIdentity::FromPackageFullName(packageFullName.get()) }; | ||
| g_initializationFrameworkPackageVersion.Version = frameworkPackageIdentity.Version().Version; | ||
| } | ||
| else | ||
| { | ||
| // Normal path: add the framework package to the package graph | ||
| const MddAddPackageDependencyOptions addOptions{}; | ||
| MDD_PACKAGEDEPENDENCY_CONTEXT packageDependencyContext{}; | ||
| wil::unique_process_heap_string packageFullName; | ||
| THROW_IF_FAILED(MddCore::Win11::AddPackageDependency(packageDependencyId.get(), MDD_PACKAGE_DEPENDENCY_RANK_DEFAULT, addOptions, &packageDependencyContext, &packageFullName)); | ||
|
|
||
| // Update the activity context | ||
| auto& activityContext{ WindowsAppRuntime::MddBootstrap::Activity::Context::Get() }; | ||
| activityContext.SetInitializationPackageFullName(packageFullName.get()); | ||
|
|
||
| // Pass along test information (if necessary) | ||
| if (!g_test_frameworkPackageNamePrefix.empty()) | ||
| { | ||
| FAIL_FAST_HR_IF(E_UNEXPECTED, g_test_ddlmPackageNamePrefix.empty()); | ||
| FAIL_FAST_HR_IF(E_UNEXPECTED, g_test_ddlmPackagePublisherId.empty()); | ||
| FAIL_FAST_HR_IF(E_UNEXPECTED, g_test_mainPackageNamePrefix.empty()); | ||
|
|
||
| // Track our initialized state | ||
| g_usingWin11Support = UsingWin11Support::Yes; | ||
| // | ||
| g_packageDependencyId = std::move(packageDependencyId); | ||
| g_packageDependencyContext = packageDependencyContext; | ||
| // | ||
| g_initializationMajorMinorVersion = majorMinorVersion; | ||
| g_initializationVersionTag = std::move(packageVersionTag); | ||
| const auto frameworkPackageIdentity{ ::AppModel::Identity::PackageIdentity::FromPackageFullName(packageFullName.get()) }; | ||
| g_initializationFrameworkPackageVersion.Version = frameworkPackageIdentity.Version().Version; | ||
| ::WindowsAppRuntime::VersionInfo::TestInitialize(frameworkPackageFamilyName.c_str()); | ||
| } | ||
|
|
||
| // Track our initialized state | ||
| g_usingWin11Support = UsingWin11Support::Yes; | ||
| g_packageDependencyId = std::move(packageDependencyId); | ||
| g_packageDependencyContext = packageDependencyContext; | ||
| g_initializationMajorMinorVersion = majorMinorVersion; | ||
| g_initializationVersionTag = std::move(packageVersionTag); | ||
| const auto frameworkPackageIdentity{ ::AppModel::Identity::PackageIdentity::FromPackageFullName(packageFullName.get()) }; | ||
| g_initializationFrameworkPackageVersion.Version = frameworkPackageIdentity.Version().Version; | ||
| } | ||
| } | ||
| else | ||
| { | ||
|
|
@@ -469,6 +547,15 @@ 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) }; | ||
|
|
||
| const bool hybridDeploy{ IsHybridDeploy() }; | ||
| if (hybridDeploy) | ||
| { | ||
| // HybridDeploy: set env vars BEFORE loading the DLL so that | ||
| // catalog.cpp can expand %FRAMEWORK_PATH% and %BASE_DIRECTORY% in loadFrom | ||
| SetFrameworkPathEnvironmentVariable(frameworkPackageInfo->path); | ||
| SetBaseDirectoryEnvironmentVariableIfNotSet(); | ||
| } | ||
|
|
||
| auto windowsAppRuntimeDllFilename{ std::wstring(frameworkPackageInfo->path) + L"\\Microsoft.WindowsAppRuntime.dll" }; | ||
| wil::unique_hmodule windowsAppRuntimeDll(LoadLibraryEx(windowsAppRuntimeDllFilename.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH)); | ||
| if (!windowsAppRuntimeDll) | ||
|
|
@@ -477,20 +564,25 @@ void FirstTimeInitialization( | |
| THROW_WIN32_MSG(lastError, "Error in LoadLibrary: %d (0x%X) loading %ls", lastError, lastError, windowsAppRuntimeDllFilename.c_str()); | ||
| } | ||
|
|
||
| // Add the framework package to the package graph | ||
| const MddPackageDependencyProcessorArchitectures architectureFilter{}; | ||
| const auto lifetimeKind{ MddPackageDependencyLifetimeKind::Process }; | ||
| const MddCreatePackageDependencyOptions createOptions{}; | ||
| wil::unique_process_heap_string packageDependencyId; | ||
| THROW_IF_FAILED(MddTryCreatePackageDependency(nullptr, frameworkPackageInfo->packageFamilyName, minVersion, architectureFilter, lifetimeKind, nullptr, createOptions, &packageDependencyId)); | ||
| // | ||
| const MddAddPackageDependencyOptions addOptions{}; | ||
| MDD_PACKAGEDEPENDENCY_CONTEXT packageDependencyContext{}; | ||
| THROW_IF_FAILED(MddAddPackageDependency(packageDependencyId.get(), MDD_PACKAGE_DEPENDENCY_RANK_DEFAULT, addOptions, &packageDependencyContext, nullptr)); | ||
|
|
||
| // Remove our temporary path addition | ||
| RemoveFrameworkFromPath(frameworkPackageInfo->path); | ||
| dllDirectoryCookie.reset(); | ||
| if (!hybridDeploy) | ||
| { | ||
| // Normal path: add the framework package to the package graph | ||
| const MddPackageDependencyProcessorArchitectures architectureFilter{}; | ||
| const auto lifetimeKind{ MddPackageDependencyLifetimeKind::Process }; | ||
| const MddCreatePackageDependencyOptions createOptions{}; | ||
| THROW_IF_FAILED(MddTryCreatePackageDependency(nullptr, frameworkPackageInfo->packageFamilyName, minVersion, architectureFilter, lifetimeKind, nullptr, createOptions, &packageDependencyId)); | ||
| // | ||
| const MddAddPackageDependencyOptions addOptions{}; | ||
| THROW_IF_FAILED(MddAddPackageDependency(packageDependencyId.get(), MDD_PACKAGE_DEPENDENCY_RANK_DEFAULT, addOptions, &packageDependencyContext, nullptr)); | ||
|
|
||
| // Remove our temporary path addition (package graph handles DLL search now) | ||
| RemoveFrameworkFromPath(frameworkPackageInfo->path); | ||
| dllDirectoryCookie.reset(); | ||
| } | ||
| // else: HybridDeploy keeps the framework path in DLL search order | ||
| // since there's no package graph to handle it | ||
|
|
||
| // Pass along test information (if necessary) | ||
| if (!g_test_ddlmPackageNamePrefix.empty()) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
env vars should be avoided if at all possible. MICROSOFT_WINDOWSAPPRUNTIME_FRAMEWORK_PATH and MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY are required for loadFrom. But the boolean MICROSOFT_WINDOWSAPPRUNTIME_HYBRID_DEPLOY could instead be an API that's explicitly set by the autoinitialization source. Or better yet, can we just infer it from the existing and differing values of MICROSOFT_WINDOWSAPPRUNTIME_FRAMEWORK_PATH and MICROSOFT_WINDOWSAPPRUNTIME_BASE_DIRECTORY?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1000
We should be seriously examining
dev\UndockedRegFreeWinRT\UndockedRegFreeWinRT-AutoInitializer.csto see how we can removenot lean in even more heavily on it.
Q: If we need altered Assembly.Load*() behavior why aren't we adding a Custom Load Context to .NET callers? We're already adding
*autoiniitalizer*.csto .NET projects through build magic. What's a little more code?