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

Publish self-contained .NET 5 application #3407

Closed
peters opened this issue Feb 16, 2021 · 10 comments
Closed

Publish self-contained .NET 5 application #3407

peters opened this issue Feb 16, 2021 · 10 comments
Assignees
Milestone

Comments

@peters
Copy link
Contributor

peters commented Feb 16, 2021

First of all, thank you for the great effort of enabling .NET core support. I tried updating our kiosk software from net48 to net5.0-windows. Using the latest pre-release this was quite easy. But when I published the application using a self-contained runtime it would run but was unable to render the web browser control. After a bit of experimenting I noticed that if I copy all CefSharp assets to a separate directory and changed CefSettings.BrowserSubprocessPath property to Path.Combine(currentDirectory, "cefsharp", "CefSharp.BrowserSubprocess.exe") and not deleting the aforementioned assets from the publish directory everything works as it should.

image

  • What version of the product are you using?

    • v88.2.40-pre
  • What architecture x86 or x64?

    • x64
  • On what operating system?

    • Windows 10
  • Are you using WinForms, WPF or OffScreen?

    • WPF
  • What steps will reproduce the problem?

    • Install .NET 5.0.3 SDK
    • git clone https://github.com/cefsharp/CefSharp.MinimalExample.git
    • Change CefSharp.MinimalExample.Wpf.csproj target framework to net5.0-windows
    • Update CefSharp.Wpf.NETCore package reference to 88.2.40-pre
    • cd CefSharp.MinimalExample
    • dotnet publish .\CefSharp.MinimalExample.netcore.sln -c Release --runtime win-x64 --self-contained
    • .\CefSharp.MinimalExample.Wpf\bin.netcore\x64\Release\net5.0-windows\win-x64\publish\CefSharp.MinimalExample.Wpf.netcore.exe
  • What is the expected output? What do you see instead?

    • The application starts but is fails to load google.com website
@peters peters changed the title Publish .NET 5 self-contained application Publish self-contained .NET 5 application Feb 16, 2021
@amaitland
Copy link
Member

  • Change CefSharp.MinimalExample.Wpf.csproj target framework to net5.0-windows

Does it work if you publish leaving the TargetFramework unchanged? (Publish for netcoreapp3.1).

The BrowserSubprocess is actually a netcoreapp3.1 exe with <RollForward>Major</RollForward> specified so it should run with a newer .Net 5 runtime installed. In my limited testing this workings when using the installed runtime. I suspect (though haven't done any testing) that it's not working when self contained, possibly the framework version used is trimmed down and missing pieces required for the rollforward.

For this use case I expect you'll need to self host the BrowserSubprocess using your apps own exe. See https://github.com/cefsharp/CefSharp/blob/cefsharp/87/CefSharp.Core/BrowserSubprocess/SelfHost.cs#L26

Whilst I understand this is a little harder for WPF as there's no Main by default, it's pretty trivial to add one.

There are some linker errors trying to compile the VC++ projects targeting net5.0 so there's no way to currently build a full set of net5.0 libraries. (This will likely need to be raised with MS).

@peters
Copy link
Contributor Author

peters commented Feb 19, 2021

Does it work if you publish leaving the TargetFramework unchanged? (Publish for netcoreapp3.1).

I will test.

For this use case I expect you'll need to self host the BrowserSubprocess using your apps own exe. See https://github.com/cefsharp/CefSharp/blob/cefsharp/87/CefSharp.Core/BrowserSubprocess/SelfHost.cs#L26

Great! Then that is an acceptable solution for me.

Thank you :)

@dustinrowland12
Copy link

dustinrowland12 commented Feb 19, 2021

I also ran into an issue publishing with self-contained selected using CefSharp.Winforms.NETCore 87.1.132.

I first tried publishing with my own application with target framework of net5.0-windows. When running my published executable, the window opens, but the page does not load and my mouse pointer constantly toggles the loading icon on/off.

I then tried publishing the minimal example that targets netcoreapp3.1 with self-contained selected. The same thing happens when I run the published executable. The window opens, but the page doesn't load, and the mouse constantly toggles loading icon on/off. Screenshot attached of what loads when running the executable. When I use "framework-dependent" instead of "self-contained", the published executable works perfectly.

image

I checked out the potential fix you mentioned for self hosting the BrowserSubprocess. I added the following code to my Main method and publishing with self-contained on .NET 5 for WinForms seems to work:

Cef.EnableHighDPISupport();
// added this section below
var exitCode = CefSharp.BrowserSubprocess.SelfHost.Main(args);
if (exitCode >= 0)
{
    return exitCode;`
}
var settings = new CefSettings();`
settings.BrowserSubprocessPath = System.IO.Path.GetFullPath("{InsertMyExecutableFile.exe}");

@amaitland
Copy link
Member

amaitland commented Feb 21, 2021

The BrowserSubprocess is actually a netcoreapp3.1 exe with <RollForward>Major</RollForward> specified so it should run with a newer .Net 5 runtime installed. In my limited testing this workings when using the installed runtime. I suspect (though haven't done any testing) that it's not working when self contained, possibly the framework version used is trimmed down and missing pieces required for the rollforward.

This may actually have nothing to do with it at all. The CefSharp.BrowserSubprocess.exe distributed with the Nuget packages doesn't want to use the self contained .Net Core/Net 5 distribution. As the generated exe is a native executable it's not really going to be easy to debug the difference.

For publishing self contained and single file applications self hosting the BrowserSubprocess is required.

For WinForms your Program.cs would look something like:

[STAThread]
public static int Main(string[] args)
{
	//To support High DPI this must be before CefSharp.BrowserSubprocess.SelfHost.Main so the BrowserSubprocess is DPI Aware
	Cef.EnableHighDPISupport();

	var exitCode = CefSharp.BrowserSubprocess.SelfHost.Main(args);

	if (exitCode >= 0)
	{
		return exitCode;
	}

	var settings = new CefSettings()
	{
		//By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data
		CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache"),
		BrowserSubprocessPath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName
	};

	//Perform dependency check to make sure all relevant resources are in our output directory.
	Cef.Initialize(settings, performDependencyCheck: false, browserProcessHandler: null);

	var browser = new BrowserForm();
	Application.Run(browser);

	return 0;
}

See https://github.com/cefsharp/CefSharp/blob/cefsharp/87/CefSharp.Core/BrowserSubprocess/SelfHost.cs#L26 for additional details.

Target machines still require VC++ 2019 Runtime. It's likely you can package this as the Universal CRT is already included (so is a custom vcruntime140_cor3.dll for .Net Core 3.1, not clear which version this is though, or if it's usable.)


For future reference
dotnet/sdk#14488 (comment)
dotnet/runtime#46990
https://github.com/dotnet/cli/blob/master/Documentation/specs/runtime-configuration-file.md

amaitland added a commit that referenced this issue Feb 25, 2021
…bprocess.runtimeconfig.json

For SelfContained and PublishSingleFile we remove the CefSharp.BrowserSubprocess.runtimeconfig.json file so
the BrowserSubprocess runs using the packages .net runtime

Issue #3407
amaitland added a commit that referenced this issue Feb 25, 2021
…bprocess.runtimeconfig.json

For SelfContained and PublishSingleFile we remove the CefSharp.BrowserSubprocess.runtimeconfig.json file so
the BrowserSubprocess runs using the packages .net runtime

Issue #3407
@amaitland
Copy link
Member

amaitland commented Feb 26, 2021

For SelfContained builds deleting the CefSharp.BrowserSubprocess.runtimeconfig.json appears to resolve the issue with the BrowserSubprocess not starting.

MyApp.runtimeconfig.json - An optional configuration file containing runtime configuration settings. This file is required for framework-dependent applications, but not for self-contained apps.

As per https://github.com/dotnet/cli/blob/master/Documentation/specs/runtime-configuration-file.md#what-produces-the-files-and-where-are-they

As CefSharp.BrowserSubprocess.runtimeconfig.json is optional when included in self-contained removing it when SelfContained = true would appear to be a reasonable option. Commit a3e8e91 removes the file from ReferenceCopyLocalPaths. The CefSharp.BrowserSubprocess.exe would appear to default to the included runtime. When running as Framework Dependant this shouldn't be a problem as the exe should run on .Net Core 3.1 and .Net 5.0 runtimes.


.Net Core 3.1

  • Testing with and both self contained and single file apps appear to be working as expected.

.Net 5.0

  • Normal (non publish) net5.0-windows build with SelfContained = true works as expected
  • When publishing as SelfContained the CefSharp.BrowserSubprocess.runtimeconfig.json file still included, removing it manually and updating the apps .deps.json works as expected, so it's just a matter of excluding the file from the publish target.
  • Publishing a SingleFile app as both SelfContained = true and SelfContained = false would appear to work as expected if the .Net Core 3.1 runtime is installed. Tested with Windows Sandbox (remember to install VC++ 2019 runtime for the arch you are targeting x86 or x64)

Removing the file from the Publish target should hopefully resolve this.

Important
.Net 5.0 behaves differently to .Net Core 3.1 in terms of Single File apps see

  1. Native files aren't included by default for Single File, need to set IncludeNativeLibrariesForSelfExtract=true
  2. To use the included BrowsersubProcess the exe needs to extract the program to disk which is no longer the default. Need to set IncludeAllContentForSelfExtract=true I've not tested a self hosted BrowserSubprocess with the default in memory behaviour.

Using 88.2.90-RCI3922 build with https://github.com/cefsharp/CefSharp.MinimalExample/tree/cefsharp/88 changing the TargetFramework to net5.0-windows

dotnet publish CefSharp.MinimalExample.netcore.sln -f net5.0-windows -r win-x86 -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:IncludeAllContentForSelfExtract=true --self-contained true --verbosity n

Disclaimer
There are potential issues with runtime dependency mismatch as detailed in dotnet/sdk#14488 (comment)

  • When self hosting the BrowserSubprocess (using your applications exe) this shouldn't be a problem as they're all the same runtime.
  • Using the provided CefSharp.BrowserSubprocess.exe may be problematic with SelfContained builds (.Net runtime included with the application).

@amaitland amaitland added this to the 88.2.x milestone Feb 27, 2021
amaitland added a commit that referenced this issue Feb 27, 2021
@amaitland
Copy link
Member

  • Publishing a SingleFile app as both SelfContained = true and SelfContained = false would appear to work as expected if the .Net Core 3.1 runtime is installed. Tested with Windows Sandbox (remember to install VC++ 2019 runtime for the arch you are targeting x86 or x64)

This should now work with the SelfContained = true and SingleFile = false with .Net 5.0

SelfContained = true and SingleFile = true under .Net 5.0 the apphost has hostfxr.dll and hostpolicy.dll statically linked into the runtime exe. As the included runtime doesn't include the required dlls to run the CefSharp.BrowserSubprocess the files will be removed from the generated executable for this use case. SelfHosting the BrowserSubprocess will be required.

Generating a net5.0-windows self hosting the BrowserSubprocess appears to work as expected under the Windows Sandbox (remembering to install VC++ 2019)

dotnet publish CefSharp.MinimalExample.WinForms.netcore.csproj -f net5.0-windows -r win-x86 -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:IncludeAllContentForSelfExtract=true --self-contained true --verbosity n

See https://github.com/dotnet/designs/blob/main/accepted/2020/single-file/design.md#host-builds
hostfxr.dll and hostpolicy.dll are statically linked, so they aren't usable for our purposes, in theory you could bundle them yourself yourself.

Add the following to your project file (or set via command line when using dotnet publish) to see details of the files that are packed into the single file exe.

<TraceSingleFileBundler>true</TraceSingleFileBundler>

The .Net 5.0 SingleFile SelfContained exe doesn't appear to contain the Universal CRT, I'm assuming it's statically linked into the resulting exe, I've not seen seen any documentation confirming this though, just an assumption, https://devblogs.microsoft.com/cppblog/introducing-the-universal-crt/ suggests it's possible.

amaitland added a commit that referenced this issue Mar 1, 2021
amaitland added a commit that referenced this issue Mar 1, 2021
amaitland added a commit that referenced this issue Mar 1, 2021
amaitland added a commit to cefsharp/CefSharp.MinimalExample that referenced this issue Mar 2, 2021
- Use the main application exe as the BrowserSubprocess when self publishing a .Net 5.0 exe
- Only the WPF and WinForms examples have been updated

cefsharp/CefSharp#3407
@amaitland
Copy link
Member

The following should copy the VC++ runtime into the bin/publish folders when run under Visual Studio (won't work from the command line). This should work for SelfContained and Framework Dependant builds. I've not tested though I don't expect this to work for PublishSingleFile.

  <Import Project="$(DevEnvDir)..\..\VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.props"/>
  
  <Target Name="IncludeVisualCppInOutFolder" AfterTargets="ResolveReferences">
    <PropertyGroup>
      <_VCRedistLocation>$(DevEnvDir)..\..\VC\Redist\MSVC\$(VCToolsRedistVersion)\$(PlatformTarget)\Microsoft.VC142.CRT</_VCRedistLocation>
    </PropertyGroup>
    <Message Importance="high" Text="_VCRedistLocation = $(_VCRedistLocation)" />
    <ItemGroup>
      <ReferenceCopyLocalPaths  Include="$(_VCRedistLocation)\**\*" />
    </ItemGroup>
  </Target>

The latest Redist version included with Visual Studio should be used.

amaitland added a commit to cefsharp/CefSharp.MinimalExample that referenced this issue Mar 5, 2021
* Upgrade to v88.2.40-pre

WPF - Add https scheme to default URL to workaround upstream issue
https://bitbucket.org/chromiumembedded/cef/issues/3079/cant-load-urls-without-scheme

* Net Core - Add AnyCPU Platform to projects/solution

* Net Core - Add RuntimeIdentifier based on PlatformTarget

- Set RuntimeIdentifier based on PlatformTarget (PlatformTarget isn't set of AnyCPU)
- Set SelfContained to false so as not to provide a Framework Dependant build (don't include the whole .net framework)

* WPF/WinForms - Change CefSharpBuildAction to Content

For testing of ClickOnce publish

* Net 5.0 - Update to include PublishSingleFile settings

- .Net 5.0 Publish Settings for PublishSingleFile
  https://docs.microsoft.com/en-us/dotnet/core/deploying/single-file
  Defaults differ compared to .Net Core 3.1
- Set RollForward to Major so runs on newer runtime version
- Add net5.0-windows TargetFramework

* .Net 5.0 - Publish Single Exe Example

- Use the main application exe as the BrowserSubprocess when self publishing a .Net 5.0 exe
- Only the WPF and WinForms examples have been updated

cefsharp/CefSharp#3407

* Upgrade to 88.2.90
@amaitland amaitland self-assigned this Mar 5, 2021
@amaitland
Copy link
Member

The CefSharp.MinimalExample has been updated to include an example of self hosting the BrowserSubprocess when publishing PublishSingleFile = true.

  • For demo purposes new application entry points were added to the WPF/WinForms examples, technically this would only have been required for the WPF Example. The new entry point is only used when PublishSingleFile = true
  • Rather than have a lot of #if statements a self contained ProgramPublishSingleFile.Main was added to both WPF/WinForms projects. This should provide an example you can copy/paste with minimal modification.
  • Version 88.2.90 is required for everything to work properly (now available on Nuget.org see Release Notification - 88.2.x #3389 (comment) for details)

Changes were added in commit cefsharp/CefSharp.MinimalExample@9da6406 which should provide a fairly detailed example for those of you who require one.


Researching/Debugging/Documenting takes a significant amount of my time. If you are using CefSharp in commercial software please considering supporting me via GitHub Sponsors. I need your support to continue developing/maintaining the project.

@peters
Copy link
Contributor Author

peters commented Mar 5, 2021

@amaitland Thank you for solving this! 😃I have enabled github sponsorship from our company.

@amaitland
Copy link
Member

I have enabled github sponsorship from our company.

@peters Thank you so much!! Please let me know if you have any problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants