Skip to content
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

PublishSingleFile with WindowsAppSDKSelfContained produces non runnable application #2597

Closed
Balkoth opened this issue Jun 8, 2022 · 25 comments
Assignees
Labels
area-DeveloperTools Issues related to authoring (source and IDL), debugging, HotReload, LiveVisualTree, VS integration bug Something isn't working
Milestone

Comments

@Balkoth
Copy link

Balkoth commented Jun 8, 2022

Describe the bug

If PublishSingleFile and WindowsAppSDKSelfContained are set to true while publishing, an exe is produced which crashes on startup.

Steps to reproduce the bug

  1. Add true to your project file
  2. Add true to your publish file
  3. Publish the application
  4. Try to run and see it crashing

Expected behavior

No response

Screenshots

No response

NuGet package version

No response

Packaging type

Unpackaged

Windows version

Windows 10 version 21H2 (19044, November 2021 Update)

IDE

Visual Studio 2022

Additional context

NuGet package version is 1.1.0

@btueffers btueffers added bug Something isn't working area-DeveloperTools Issues related to authoring (source and IDL), debugging, HotReload, LiveVisualTree, VS integration labels Jun 9, 2022
@DefaultRyan
Copy link
Member

Was able to get an application event trace containing:

Application: CsApp2.exe
CoreCLR Version: 6.0.522.21309
.NET Version: 6.0.5
Description: The process was terminated due to an unhandled exception.
Exception Info: System.TypeInitializationException: The type initializer for '<Module>' threw an exception.
 ---> System.DllNotFoundException: Dll was not found.
   at Microsoft.Windows.Foundation.UndockedRegFreeWinRTCS.NativeMethods.WindowsAppRuntime_EnsureIsLoaded()
   at Microsoft.Windows.Foundation.UndockedRegFreeWinRTCS.AutoInitialize.AccessWindowsAppSDK()
   at .cctor()
   --- End of inner exception stack trace ---

The code for that method is here: WindowsAppSDK/UndockedRegFreeWinRT-AutoInitializer.cs at main · microsoft/WindowsAppSDK (github.com)

    [DllImport("Microsoft.WindowsAppRuntime.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
    internal static extern int WindowsAppRuntime_EnsureIsLoaded();

My relatively uninformed hunch is that the DllImport is not playing nicely with the single file publishing.

@DefaultRyan
Copy link
Member

I've created an internal task to track this: https://task.ms/39946393 and assigning to @DrusTheAxe

@DrusTheAxe
Copy link
Member

Do you have Microsoft.WindowsAppRuntime.dll as a file along with your "single file"?

Does your project define <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>?

Do you set the property IncludeNativeLibrariesForSelfExtract?


PublishSingleFile has variation of behavior. Depending how you use it you may or may not run into problems

https://docs.microsoft.com/en-us/dotnet/core/deploying/single-file/overview#output-differences-from-net-3x

Bundling all application-dependent files into a single binary provides an application developer with the attractive option to deploy and distribute the application as a single file. This deployment model has been available since .NET Core 3.0 and has been enhanced in .NET 5. Previously in .NET Core 3.0, when a user runs your single-file app, .NET Core host first extracts all files to a directory before running the application. .NET 5 improves this experience by directly running the code without the need to extract the files from the app.

Which would be problematic as [DllImport(Microsoft.WindowsAppRuntime.dll,...)] == LoadLibrary(Microsoft.WindowsAppRuntime.dll) but .NET 5's not quite that simple as noted further down

Output differences from .NET 3.x
In .NET Core 3.x, publishing as a single file produced exactly one file, consisting of the app itself, dependencies, and any other files in the folder during publish. When the app starts, the single file app was extracted to a folder and run from there. Starting with .NET 5, only managed DLLs are bundled with the app into a single executable. When the app starts, the managed DLLs are extracted and loaded in memory, avoiding the extraction to a folder. On Windows, this means that the managed binaries are embedded in the single-file bundle, but the native binaries of the core runtime itself are separate files. To embed those files for extraction and get exactly one output file, like in .NET Core 3.x, set the property IncludeNativeLibrariesForSelfExtract to true. For more information about extraction, see Including native libraries.

More info at that link at the end

https://docs.microsoft.com/en-us/dotnet/core/deploying/single-file/overview#including-native-libraries

Single-file doesn't bundle native libraries by default. On Linux, we prelink the runtime into the bundle and only application native libraries are deployed to the same directory as the single-file app. On Windows, we prelink only the hosting code and both the runtime and application native libraries are deployed to the same directory as the single-file app. This is to ensure a good debugging experience, which requires native files to be excluded from the single file.

Starting with .NET 6 the runtime is prelinked into the bundle on all platforms.

There is an option to set a flag, IncludeNativeLibrariesForSelfExtract, to include native libraries in the single file bundle, but these files will be extracted to a directory in the client machine when the single file application is run.

Specifying IncludeAllContentForSelfExtract will extract all files (even the managed assemblies) before running the executable. This preserves the original .NET Core single-file deployment behavior.

@Balkoth
Copy link
Author

Balkoth commented Jun 10, 2022

I set IncludeNativeLibrariesForSelfExtract in my project yes.

@Balkoth
Copy link
Author

Balkoth commented Jul 8, 2022

As of 1.1.2 an application published like this still crashes on startup:

<Project>
  <PropertyGroup>
    <PublishProtocol>FileSystem</PublishProtocol>
    <Platform>x64</Platform>
    <RuntimeIdentifier>win10-x64</RuntimeIdentifier>
    <PublishDir>bin\win10-x64\publish\win10-x64\</PublishDir>
    <SelfContained>true</SelfContained>
    <PublishReadyToRun>false</PublishReadyToRun>
    <PublishSingleFile>true</PublishSingleFile>
    <Configuration>Release</Configuration>
    <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
    <PublishTrimmed>false</PublishTrimmed>
    <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
    <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
  </PropertyGroup>
</Project>
Application: TestWindowsAppSDKSelfContained.exe
CoreCLR Version: 6.0.622.26707
.NET Version: 6.0.6
Description: The process was terminated due to an unhandled exception.
Exception Info: System.TypeInitializationException: The type initializer for 'WinRT.ActivationFactory`1' threw an exception.
 ---> System.ArgumentNullException: Value cannot be null. (Parameter 'path1')
   at System.IO.Path.Combine(String path1, String path2)
   at WinRT.DllModule.TryCreate(String fileName, DllModule& module)
   at WinRT.DllModule.TryLoad(String fileName, DllModule& module)
   at WinRT.BaseActivationFactory..ctor(String typeNamespace, String typeFullName)
   at WinRT.ActivationFactory`1..ctor()
   at WinRT.ActivationFactory`1..cctor()
   --- End of inner exception stack trace ---
   at WinRT.ActivationFactory`1.As(Guid iid)
   at Microsoft.UI.Xaml.Application.Make___objRef_global__Microsoft_UI_Xaml_IApplicationStatics()
   at Microsoft.UI.Xaml.Application.get__objRef_global__Microsoft_UI_Xaml_IApplicationStatics()
   at Microsoft.UI.Xaml.Application.Start(ApplicationInitializationCallback callback)
   at TestWindowsAppSDKSelfContained.Program.Main(String[] args) in C:\Work\VisualStudioProjects\TestWindowsAppSDKSelfContained\obj\x64\Release\net6.0-windows10.0.19041.0\win10-x64\App.g.i.cs:line 31

@aepot
Copy link

aepot commented Nov 1, 2022

Same issue for 1.1.5

<PropertyGroup>
  <OutputType>WinExe</OutputType>
  <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
  <TargetPlatformMinVersion>10.0.18362.0</TargetPlatformMinVersion>
  <RootNamespace>TestApp</RootNamespace>
  <ApplicationManifest>app.manifest</ApplicationManifest>
  <Platforms>x64</Platforms>
  <RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
  <PublishProfile>win10-$(Platform).pubxml</PublishProfile>
  <UseWinUI>true</UseWinUI>
  <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
  <WindowsPackageType>None</WindowsPackageType>
  <SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
  <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
  <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
</PropertyGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.1.5" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />

Exception in Windows Event Viewer from .NET Runtime

Application: TestApp.exe
CoreCLR Version: 6.0.1022.47605
.NET Version: 6.0.10
Description: The process was terminated due to an unhandled exception.
Exception Info: System.TypeInitializationException: The type initializer for 'WinRT.ActivationFactory`1' threw an exception.
 ---> System.ArgumentNullException: Value cannot be null. (Parameter 'path1')
   at System.IO.Path.Combine(String path1, String path2)
   at WinRT.DllModule.TryCreate(String fileName, DllModule& module)
   at WinRT.DllModule.TryLoad(String fileName, DllModule& module)
   at WinRT.BaseActivationFactory..ctor(String typeNamespace, String typeFullName)
   at WinRT.ActivationFactory`1..ctor()
   at WinRT.ActivationFactory`1..cctor()
   --- End of inner exception stack trace ---
   at WinRT.ActivationFactory`1.As(Guid iid)
   at Microsoft.UI.Xaml.Application.Make___objRef_global__Microsoft_UI_Xaml_IApplicationStatics()
   at Microsoft.UI.Xaml.Application.get__objRef_global__Microsoft_UI_Xaml_IApplicationStatics()
   at Microsoft.UI.Xaml.Application.Start(ApplicationInitializationCallback callback)
   at TestApp.Program.Main(String[] args) in C:\Source\TestApp\TestApp\obj\x64\Release\net6.0-windows10.0.19041.0\win10-x64\App.g.i.cs:line 31

If I remove <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained> then everything works fine.

Windows 11 (OS Build 22000.1098)

@DooblyNoobly
Copy link

Same problem here, same configs as above

@Balkoth
Copy link
Author

Balkoth commented Nov 24, 2022

Not being supported is one thing, but then it should error out when trying to compile the app, listing the incompatible options. Producing a non runnable application is the worst that can happen.

@DooblyNoobly
Copy link

DooblyNoobly commented Nov 25, 2022

I've just removed Self Contained Windows App SDK and now I package the WindowsAppRuntimeInstall.exe separately with my inno installer that I send to the clients. I run the WindowsAppRuntimeInstall.exe on the install setup using the --quiet and --force parameters. Works well for me. Now I don't have to worry about trying to add it to single file on publish.

Reference: https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/deploy-unpackaged-apps#deploy-windows-app-sdk-runtime

@Balkoth
Copy link
Author

Balkoth commented Nov 25, 2022

Be carefull if your app has to be run under another user, because this works with WindowsAppSDKSelfContained, but breaks when using the installed runtime.

@farag2
Copy link

farag2 commented Jan 11, 2023

I have the same issue. 🐈‍⬛

@kmanev073
Copy link

Same issue :(

@nickrandolph
Copy link

Is this supposed to work with the latest version of the SDK - I just tried with a blank project and it's the same as the issue reported

@kmanev073
Copy link

Any updates on the topic?

@riverar
Copy link
Contributor

riverar commented Aug 30, 2023

@DrusTheAxe This appears to be a URFW issue, am hitting the same problem here.

Particularly, it doesn't appear URFW understands that in single-file scenarios, the single file host (my.exe) is in one directory and everything else is extracted into another directory. (.NET only appears to wire up enough hooks for DllImport scenarios to work and leaves the rest to the developer.)

This particular URFW line has me curious:
https://github.com/microsoft/WindowsAppSDK/blob/main/dev/UndockedRegFreeWinRT/catalog.cpp#L48-L50

I was going to fix up the search directories myself but it's not clear how this is supposed to this piece of code is meant to behave; I'm seeing URFW pass a lpFileName of Microsoft.UI.Xaml.dll whilst the LoadLibraryEx documentation states:

If this value is used and lpFileName specifies a relative path, the behavior is undefined.

@robertev-alchemy
Copy link

Same issue :(

@DrusTheAxe - can you please recommend any temporary work-around to unblock this scenario?

Perhaps a custom build target to copy dependencies or something?

Thanks!

@DrusTheAxe
Copy link
Member

DrusTheAxe commented Sep 4, 2023

doesn't appear URFW understands that in single-file scenarios, the single file host (my.exe) is in one directory and everything else is extracted into another directory

URFW expects DLLs+EXE are colocated. It's not specialized for DotNet (or other runtimes).

I can think of several options but none particularly good. Need to investigate a little further and discuss with some folks. I suspect we can do something.

@Scottj1s @bpulliam let's talk offline.

This particular URFW line has me curious:...

Why? Is it the LOAD_WITH_ALTERED_SEARCH_PATH?

That's standard fare how COM loads DLLs (even since Windows NT 4.0).

I'm seeing URFW pass a lpFileName of Microsoft.UI.Xaml.dll whilst the LoadLibraryEx documentation states:

Hmmm. That should be an absolute filename (to avoid ambiguities). Thanks for pointing this out!

But even if that's changed to an absolute filename that won't fix your single-file .NET scenario, right?

@riverar
Copy link
Contributor

riverar commented Sep 4, 2023

But even if that's changed to an absolute filename that won't fix your single-file .NET scenario, right?

Agreed.

I can think of several options but none particularly good. Need to investigate a little further and discuss with some folks. I suspect we can do something.

I think ensuring URFW attempts to find its libraries via search path traversal would allow .NET developers to add the extracted directory to the search path. (Perhaps a stretch goal could be to do that for developers via the .NET bootstrap.)

We should also consider adding a note somewhere that dissuades folks from trying single-file publishing as it's known to be completely unsupported/incompatible. Would save some folks a few hours/days of digging!

@DrusTheAxe
Copy link
Member

I think ensuring URFW attempts to find its libraries via search path traversal

What search path?

URFW is native code. One question is what .NET needs, then if/how to teach URFW where to look in ways that also work for .NET (w/o negatively impacting others, and w/o 1off solutions for every language and framework).

And of course there's the key question: how's this work for OS reg-free WinRT? Is that any different?

Of course if the OS RegFree WinRT is no help we have the merit of consistency, but also potential opportunity to do something beyond the OS.

Following up but this will likely involve key experts across a few teams and some vacations and other scheduling fine print so please be patient on this topic.

Perhaps a stretch goal could be to do that for developers via the .NET bootstrap

Perhaps. I need to talk to some folks more deeply immersed in that.

We should also consider adding a note somewhere that dissuades folks from trying single-file publishing as it's known to be completely unsupported/incompatible

Is it? I've heard it mentioned but I haven't kept track of its current status. Something else to check with folks.

@riverar
Copy link
Contributor

riverar commented Sep 5, 2023

Some additional dev notes/thoughts as I poke around more:

It looks like the issue is ultimately a result of OS SxS not performing a traditional LoadLibrary-like DLL search for the components in the app's fusion manifest. That is, SxS sees <asmv3:file name="CoreMessagingXP.dll">...</asmv3:fle> in the manifest, attempts to load the file adjacent to the executing image, fails, and gives up.

We wouldn't normally need the manifest but Windows App SDK does due to some component naming decisions made in the past. (Perhaps activation factory stubs following the traditional pattern could help here?)

Enterprising developers wanting to bounce through a loader, to externalize the fusion manifest at runtime, may discover URFW doesn't support loose manifests. But perhaps putting a new activation context on the stack with an auto-generated manifest could work here? (Might be tricky with threading and WASDK/WinUI may have some assumptions built around the active context...)

Anyway, thanks @DrusTheAxe for looking into this. We will all wait (impatiently) for you to return with more news!

@tpoint75
Copy link

tpoint75 commented Nov 2, 2023

I have the problem that a build exe works fine, but I can't run it from visual studio until my custom dll is in system32! Next to the exe is not enough..

@akhanalcs
Copy link

It's been 6 months since the issue was opened. Any updates on it?

@Scottj1s
Copy link
Member

Scottj1s commented Jan 6, 2024

Thanks for your patience folks. Support is in the works!

@Scottj1s
Copy link
Member

PublishSingleFile will be supported in Windows App SDK 1.5

@Scottj1s Scottj1s added this to the 1.5 milestone Feb 6, 2024
@Scottj1s
Copy link
Member

Scottj1s commented Feb 6, 2024

Closing as completed with 1.5.240205001-preview1. If we've missed any edge cases, please enter new issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-DeveloperTools Issues related to authoring (source and IDL), debugging, HotReload, LiveVisualTree, VS integration bug Something isn't working
Projects
None yet
Development

No branches or pull requests