Unpackaged framework-dependent app crashes if fusion manifest not present/embedded
riverar opened this issue · 6 comments
Describe the bug
In scenarios where the fusion manifest is not embedded into the output executable, an app bootstrapping with Windows App SDK will fail to start. This appears to have regressed since 1.0.0.
Repro project: repro.zip
Steps to reproduce the bug
- Open repro project provided above
- Compile/execute output
- Observe app does not start correctly
- Edit the .vcxproj and replace all instances of
<EmbedManifest>false</EmbedManifest>
with<EmbedManifest>true</EmbedManifest>
. - Compile/execute output
- Observe app starts correctly
Expected behavior
App to start correctly regardless of fusion manifest presence.
NuGet package version
1.1.1 ❌ Broken
1.1.0 ❌ Broken
1.0.4 ❌ Broken
1.0.3 ❌ Broken
1.0.2 ❌ Broken
1.0.1 ❌ Broken
1.0.0 ✔️ Works
Packaging type
Unpackaged
Windows version
Insider Build (xxxxx)
IDE
Visual Studio 2022
Additional context
Impacted Rust bindings, which does not use fusion manifests.
It appears the bootstrapping component is attempting to locate reg-free WinRT information. In non-framework-dependent apps, that is expected. In framework-dependent apps it is not. The fusion manifest in these scenarios will have very little aside from optional common controls/DPI flags.
Output window
'repro.exe' (Win32): Loaded 'C:\Sources\was-repros\redacted\x64\Debug\repro\repro.exe'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\ntdll.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\kernel32.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\KernelBase.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\combase.dll'. Symbol loading disabled by Include/Exclude setting.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbase.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140d.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\rpcrt4.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\msvcp140d.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\oleaut32.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Sources\was-repros\redacted\x64\Debug\repro\Microsoft.WindowsAppRuntime.Bootstrap.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140_1d.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\msvcp_win.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbased.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\ucrtbased.dll'. Symbols loaded.
'repro.exe' (Win32): Unloaded 'C:\Windows\System32\ucrtbased.dll'
'repro.exe' (Win32): Loaded 'C:\Windows\System32\ole32.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\gdi32.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\win32u.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\gdi32full.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\user32.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\advapi32.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\msvcrt.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\sechost.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\shell32.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\imm32.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\bcryptprimitives.dll'. Symbols loaded.
D:\a\_work\1\s\BuildOutput\Release\x64\WindowsAppRuntime_DLL\WindowsAppRuntimeInsights.h(53)\Microsoft.WindowsAppRuntime.Bootstrap.dll!00007FFC598324DB: (caller: 00007FFC5982E1C6) LogHr(1) tid(b6f8) 8007007E The specified module could not be found.
Msg:[Unable to load resource dll. Microsoft.WindowsAppRuntime.Insights.Resource.dll]
'repro.exe' (Win32): Loaded 'C:\Windows\System32\kernel.appcore.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\clbcatq.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\AppXDeploymentClient.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\Windows.ApplicationModel.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\WinTypes.dll'. Symbols loaded.
D:\a\_work\1\s\dev\WindowsAppRuntime_BootstrapDLL\MddBootstrap.cpp(776)\Microsoft.WindowsAppRuntime.Bootstrap.dll!00007FFC5982B0D9: (caller: 00007FFC59829C2F) LogHr(2) tid(b6f8) 80040010 Object is not in any of the inplace active states
Msg:[Bootstrap.Intitialize: Scanning packages for Major.Minor=1.1, Tag=, MinVersion=1001.524.1918.0] CallContext:[\Initialize]
'repro.exe' (Win32): Loaded 'C:\Windows\System32\Windows.StateRepositoryCore.dll'. Symbols loaded.
D:\a\_work\1\s\dev\WindowsAppRuntime_BootstrapDLL\MddBootstrap.cpp(865)\Microsoft.WindowsAppRuntime.Bootstrap.dll!00007FFC5982B85C: (caller: 00007FFC59829C2F) LogHr(3) tid(b6f8) 80040011 Not able to convert object
Msg:[Bootstrap.Intitialize: Microsoft.WinAppRuntime.DDLM.1000.516.2156.0-x6_1000.516.2156.0_x64__8wekyb3d8bbwe not applicable. Version doesn't match MinVersion criteria (Major.Minor=1.1, Tag=, MinVersion=1001.524.1918.0)] CallContext:[\Initialize]
D:\a\_work\1\s\dev\WindowsAppRuntime_BootstrapDLL\MddBootstrap.cpp(865)\Microsoft.WindowsAppRuntime.Bootstrap.dll!00007FFC5982B85C: (caller: 00007FFC59829C2F) LogHr(4) tid(b6f8) 80040011 Not able to convert object
Msg:[Bootstrap.Intitialize: Microsoft.WinAppRuntime.DDLM.1000.516.2156.0-x8_1000.516.2156.0_x86__8wekyb3d8bbwe not applicable. Version doesn't match MinVersion criteria (Major.Minor=1.1, Tag=, MinVersion=1001.524.1918.0)] CallContext:[\Initialize]
D:\a\_work\1\s\dev\WindowsAppRuntime_BootstrapDLL\MddBootstrap.cpp(885)\Microsoft.WindowsAppRuntime.Bootstrap.dll!00007FFC5982BB5E: (caller: 00007FFC59829C2F) LogHr(5) tid(b6f8) 80040012 Not able to perform the operation because object is not given storage yet
Msg:[Bootstrap.Intitialize: Microsoft.WinAppRuntime.DDLM.1001.524.1918.0-x6_1001.524.1918.0_x64__8wekyb3d8bbwe is applicable (Major.Minor=1.1, Tag=, MinVersion=1001.524.1918.0)] CallContext:[\Initialize]
D:\a\_work\1\s\dev\WindowsAppRuntime_BootstrapDLL\MddBootstrap.cpp(908)\Microsoft.WindowsAppRuntime.Bootstrap.dll!00007FFC5982C486: (caller: 00007FFC59829C2F) LogHr(6) tid(b6f8) 80040013 Msg:[Bootstrap.Intitialize: Microsoft.WinAppRuntime.DDLM.1001.524.1918.0-x6_1001.524.1918.0_x64__8wekyb3d8bbwe best matches the criteria (Major.Minor=1.1, Tag=, MinVersion=1001.524.1918.0) of 172 packages scanned] CallContext:[\Initialize]
'repro.exe' (Win32): Loaded 'C:\Windows\System32\twinui.appcore.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\windows.storage.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\OneCoreUAPCommonProxyStub.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\SHCore.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\daxexec.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\container.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\userenv.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\usermgrcli.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\Windows.StateRepositoryClient.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\Windows.StateRepositoryPS.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\LicenseManagerApi.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\capauthz.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\ntmarta.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\shlwapi.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\uxtheme.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\propsys.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\profapi.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\cfgmgr32.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\apphelp.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\networkexplorer.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\Windows.FileExplorer.Common.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\edputil.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\urlmon.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\iertutil.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\srvcli.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\netutils.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\cldapi.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\sspicli.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\AppResolver.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\BCP47Langs.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\mpr.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\OneCoreCommonProxyStub.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\execmodelproxy.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Program Files\WindowsApps\Microsoft.WindowsAppRuntime.1.1_1001.524.1918.0_x64__8wekyb3d8bbwe\Microsoft.WindowsAppRuntime.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\powrprof.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\rometadata.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\powrprof.dll'. Symbols loaded.
'repro.exe' (Win32): Loaded 'C:\Windows\System32\xmllite.dll'. Symbols loaded.
'repro.exe' (Win32): Unloaded 'C:\Windows\System32\powrprof.dll'
'repro.exe' (Win32): Loaded 'C:\Windows\System32\umpdc.dll'. Symbols loaded.
D:\a\_work\1\s\dev\UndockedRegFreeWinRT\urfw.cpp(416)\Microsoft.WindowsAppRuntime.dll!00007FFC5901C617: (caller: 00007FFC5902FA12) ReturnHr(1) tid(b6f8) 8007000E Not enough memory resources are available to complete this operation.
D:\a\_work\1\s\dev\WindowsAppRuntime_DLL\dllmain.cpp(30)\Microsoft.WindowsAppRuntime.dll!00007FFC5902FBF3: (caller: 00007FFC5903B7EB) FailFast(1) tid(b6f8) 8007000E Not enough memory resources are available to complete this operation.
Unhandled exception at 0x00007FFC5902FBF3 (Microsoft.WindowsAppRuntime.dll) in repro.exe: Fatal program exit requested.
That's surprising - I thought I'd built apps using WinAppSDK/fwk-dependent and no Fusion manifest. Will investigate.
It appears the bootstrapping component is attempting to locate reg-free WinRT information...In framework-dependent apps [that is not expected]
Checking for Fusion manifest is expected. Failing when not found or embedded (even if it has no RegFreeWinRT info) is not expected.
The WinRT activation+metadata lookup hunt sequence1,2,3 with WinAppSDK is...
- Package Graph
- Undocked RegFree WinRT (URFW)
- OS
When the Bootstrapper fires in an app using WinAppSDK framework-dependent it should find WinAppSDK types via 1 and OS types via 3 (e.g. PackageManager). The latter case will cause lookups to walk through the Fusion manifest (if any) but it should be fine if the executable has none (or an embedded one w/o any RegFree info).
The fusion manifest in these scenarios will have very little aside from optional common controls/DPI flags.
In the simple case. It could have more e.g. if a LolzCatzVidz project uses WinAppSDK framework-dependent and a AwesomeSauceVideoPlaybackWidget WinRT object from another library in a self-contained way.
1 WinRT types in the Windows.*
namespace must be defined by Windows. The PackageGraph and RegFreeWinRT are ignored for Windows.*
types.
2 Windows (without WinAppSDK) has a slightly different ordering: 1=PackageGraph, 2=RegFreeWinRT (19H1+).
3 Langauge projections can supplment the hunt sequence e.g. C++/WinRT and C#/WinRT add a step 4. Crawl file system
to look for a DLL containing the type if not found by one of the other mechanisms.
Given that the package graph satisfied the hunt, I don't think WAS should have ever attempted to look for URFW / OS metadata right? Or are you searching all sources for the best candidate? (If so, would be great to see that debug spew added in a future release I think.)
I don't think WAS should have ever attempted to look for URFW / OS metadata right
For what type? And used how?
if (RoActivateInstance(acid) == REGDB_E_CLASSNOTFOUND)
isn't an unusual "use X if available" recipe, just like
if (CoCreateInstance(clsid) == REGDB_E_CLASSNOTFOUND)
It depends on what ACID is being looked for when this went sideways.
Or are you searching all sources for the best candidate?
Not COM/WinRT. That's purely "Find X, and 1st match wins" model (same as it's been for decades)
Something seems fishy but I can't put my finger on it. Yet. I need to do some poking and prodding...
TL;DR UndockedRegFreeWinRT has a few discrepancies from Windows' RegFreeWinRT behavior. Putting together some tests to repro and then verify changes.
UndockedRegFreeWinRT (URFW) will load an Fusion manifest embedded for an exe or dll or a file if not exe|dll
HRESULT LoadManifestFromPath(std::wstring path)
{
if (path.size() < 4)
{
return COR_E_ARGUMENT;
}
std::wstring ext(path.substr(path.size() - 4, path.size()));
if ((CompareStringOrdinal(ext.c_str(), -1, L".exe", -1, TRUE) == CSTR_EQUAL) ||
(CompareStringOrdinal(ext.c_str(), -1, L".dll", -1, TRUE) == CSTR_EQUAL))
{
return LoadFromEmbeddedManifest(path.c_str());
}
else
{
return LoadFromSxSManifest(path.c_str());
}
}
That's not how Windows does it. Windows will load an embedded manifest if exists, and then from a file (as an override). Supposed to give devs the easy 'embed when built' and admins the ability to 'provide via file and override the embedded (if any)'.
And that's only called once via ExtRoLoadCatalog()
-- only called in DllMain() when Microsoft.WindowsAppRuntime.dll.
So LoadFromEmbeddedManifest()
is called for exe+dll (and LoadFromSxSManifest()
never is. Problem #1.
And even if we have an embedded manifest we're not looking for a file manifest (as an admin override). Problem #2.
LoadFromEmbeddedManifest()
looks for the resource via LoadLibrary, FindResource(), LoadResource(), LockResource() and any failure will return ERROR_FILE_NOT_FOUND
but these functions all have error info via GetLastError()
. Problem #3.
Lookup of the embedded manifest resource is IF (FindResource(1)!=OK) THEN FindResource(2)
. That's based on winuser.h line 314+
#define CREATEPROCESS_MANIFEST_RESOURCE_ID MAKEINTRESOURCE( 1)
#define ISOLATIONAWARE_MANIFEST_RESOURCE_ID MAKEINTRESOURCE(2)
#define ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID MAKEINTRESOURCE(3)
1=CREATEPROCESS_MANIFEST_RESOURCE_ID == exe
2=ISOLATIONAWARE_MANIFEST_RESOURCE_ID == dll
3=ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID Remarks mention "This is never used by the loader." and it's not otherwise interesting to URFW so we can ignore it.
We're always checking id 1+2. We should only check 1 for exe and 2 for dl. Problem #4.
Whipping up a test suite for the various permutations and then fixes.
Oops! Sorry I missed this question earlier
Given that the package graph satisfied the hunt
UndockedRegFreeWinRT resolves Fusion + MSIX manifests at different times - DLL load vs 1st-use.
Fusion manifest resolution occurs in Microsoft.WindowsAppRuntime.dll's DllMain()
when the DLL's loaded. Whatever answer's found is used until the DLL unloads. An embedded file can't change for the life of the DLL in the process, and the file (if any) is expected to match (or at least any changes after load wouldn't affect).
This differs from URFW's package graph support. That can change dynamically over the life of the process so that's resolved on 1st use as needed (i.e. just-in-time). As that's based on appxmanifest.xml and packages should be immutable once installed the manifest itself wouldn't change, we simply don't know which packages may be needed until later when we use WinRT activation (or metadata) APIs. We walk the package graph looking for answers and if a package's manifest hasn't been loaded yet it's loaded and cached for all successive uses of that package's manifest.
I don't think WAS should have ever attempted to look for
URFWFusion / OS metadata
Small correction. Technically we use URFW to resolve Fusion+MSIX manifested WinRT metadata. URFW is the code altering WinRT's activation+metadata lookup to also use our Fusion+MSIX data.
Very happy to see you tracked it down and we're going to see URFW support loose fusion manifests again. That will make things a lot easier for Rust where there's no easy access to MT.