cefsharp / CefSharp

.NET (WPF and Windows Forms) bindings for the Chromium Embedded Framework
http://cefsharp.github.io/
Other
9.87k stars 2.92k forks source link

Feature Request - Add .Net Core 3.1 Support #2796

Closed amaitland closed 3 years ago

amaitland commented 5 years ago

With .Net Core 3.0 adding support for WPF and WinForms I'm seeing more and more queries about running CefSharp using .Net Core 3.0. This issue will track the official progress, there is no timeframe yet, please don't ask when this will be implemented, .Net Core isn't even officially out yet. As per https://github.com/dotnet/core/blob/master/roadmap.md#upcoming-ship-dates the release is still some months away.

Microsoft has started implementing C++/CLI as part of .Net Core you can track the issue https://github.com/dotnet/coreclr/issues/18013 They are only adding support for Windows initially. See also https://github.com/dotnet/coreclr/issues/659

.Net Core will support mixed mode assemblies on Windows Only.

https://devblogs.microsoft.com/dotnet/net-core-3-and-support-for-windows-desktop-applications/

The sync JavaScript Binding implementation will not work as Microsoft is not supporting WCF in .Net Core 3.0.

amaitland commented 5 years ago

With https://github.com/cefsharp/CefSharp/pull/2767 it may be possible to load the next CefSharp release (75 at time of writing) with .Net Core 3.0 referencing the current .Net 4.5.2 framework assemblies. I have no personally tested this personally yet. You will have to avoid using the sync JavaScript Binding as it relies on WCF.

michalczerwinski commented 5 years ago

Any news on this one?

kpreisser commented 5 years ago

Hi, first, many thanks for this great project!

I was able to successfully use CefSharp.WinForms 75.0.110-CI3174 on a .NET Core 3.0 (Preview 5) WinForms application (form title in the screenshot contains process filename and RuntimeInformation.FrameworkDescription when running with dotnet.exe): cefsharp-windows

I also tested that the IDownloadHandler was working successfully.

However, directly adding the NuGet package to the .NET Core project did not work as it didn't resolve the dependencies (probably because they are only declared for .NET Framework 4.5.2).

Instead, I first added it to a .NET Framework project and built it (so all the necessary files would be copied into the bin folder), and then in the .NET Core 3.0 project I manually added references to the CefSharp.dll, CefSharp.Core.dll and CefSharp.WinForms.dll assemblies. Additionally, I copied all other files from the bin folder of the .NET Framework app to the bin folder of the .NET Core app.

Another thing to consider is the browser subprocess (CefSharp.BrowserSubprocess.exe) which is built for .NET Framework 4.5.2, meaning that you still would need to have .NET Framework 4.5.2 or higher installed to run the .NET Core 3.0 app.

In order to avoid a dependency on .NET Framework and only use .NET Core, I think you will probably have to provide your own browser subprocess, implemented as a .NET Core app (using the same .NET Core version as your main app so that it can share runtime files when publishing a self-contained app). (I haven't tested this yet.)

Thank you!

campersau commented 5 years ago

You can try setting <PackageTargetFallback>net452</PackageTargetFallback> in your project file which might restore the nuget package correctly. https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets#packagetargetfallback

kpreisser commented 5 years ago

Hi @campersau,

You can try setting <PackageTargetFallback>net452</PackageTargetFallback> in your project file which might restore the nuget package correctly.

Thanks for your suggestion! Unfortunately, it did not seem to have an effect when I added that setting to the project file - it still only added CefSharp.WinForms.dll but none of the dependencies. I guess the NuGet packages would need to have an additional .NET Core 3.0 target for this to work.


I also had a look at the browser subprocess, and I was also able to successfully provide the subprocess as .NET Core 3.0 app, to avoid a dependency on the .NET Framework 4.5.2. However, actually using a different project for the subprocess might be a bit cumbersome with .NET Core because the build results (binaries) might different for a normal (framework-dependent) app (e.g. when debugging in Visual Studio) compared to a published (self-contained) app etc., and you would need to copy the correct subprocess binaries near your app binaries each time.

I think the easiest way to use CEFSharp including the browser subprocess in .NET Core 3.0 might be to use the same executable for both your regular WinForms app and the browser subprocess.

To test this, I added a reference to CefSharp.BrowserSubprocess.Core.dll to my .NET Core 3.0 WinForms project, set CefSettings.BrowserSubprocessPath = Process.GetCurrentProcess().MainModule.FileName, and then used a Main() method like this (as I noticed the subprocess will be called with a --type=.... argument):

    [STAThread]
    static int Main(string[] args)
    {
        if (args.Length > 0 && args[0].StartsWith(
            CefSharpArguments.SubProcessTypeArgument + "=", StringComparison.Ordinal))
        {
            // Run the CEFSharp browser subprocess.
            return RunBrowserSubprocess(args);
        }
        else
        {
            // Run the regular application.
            RunGui();
            return 0;
        }
    }

Here, RunBrowserSubprocess() would contain the code from the Main() method of CefSharp.BrowserSubprocess, and RunGui() would do the regular Application.Run(new Form1()). (The code of RunBrowserSubprocess() could also be implemented as utiltiy method in the .NET Core 3.0 package of CefSharp.)

This means you won't have to bother with building and copying the correct subprocess executable before running your main application. (A minor disadvantage of this is that you cannot implement the command line argument --type= for your own use.)

What do you think? Thanks!

amaitland commented 5 years ago

Future reference for myself https://github.com/dotnet/samples/tree/master/core/hosting/HostWithHostFxr

kpreisser commented 5 years ago

Regarding the subprocess handling for .NET Core, my idea was the following:

That way, a user could modify the Main method in a WinForms project like this when adding CefSharp:

         [STAThread]
         static void Main()
         {
             Application.SetHighDpiMode(HighDpiMode.SystemAware);
+            
+            // Handle the CefSharp browser subprocess logic on start-up.
+            CefSharpBrowserSubprocess.HandleSubprocess();
+            
             Application.EnableVisualStyles();
             Application.SetCompatibleTextRenderingDefault(false);
             Application.Run(new Form1());
         }

In this case, the application executable would also run the browser subprocess logic, and a separate EXE is no longer needed.

With that change, I think the only other thing needed is to adjust the NuGet spec files to declare dependencies to .NETCoreApp3.0 so the dependencies will resolve when compiling for .NET Core. (Note: The logic that currently allows to compile .NET Framework projects for AnyCPU might no longer work on .NET Core, but that shouldn't be a big issue because you will probably publish a self-contained application which is platform-specific.)


On a related note, I think using the same executable for the app and the browser subprocess will also solve a rendering issue that we experienced with a WPF app using CefSharp.WinForms: When moving the window between monitors with different DPI settings, the browser control starts to display some contents in an incorrect size when moving the mouse, like this: grafik

This seems to occur because the subprocess executable (CefSharp.BrowserSubprocess.exe) declares <dpiAware>true/PM</dpiAware> (per-monitor DPI aware V1), whereas in our case the application only supports <dpiAware>true</dpiAware> (System DPI aware).

Whereas when using the same executable, both processes will use the same DPI settings, so this issue will no longer occur there.

What do you think?

Thanks!

amaitland commented 5 years ago

@kpreisser Thanks for the very detailed summaries, some very useful insights πŸ‘

  • Switch project CefSharp.BrowserSubprocess to the .NET Core SDK and multi-target for net452 and netcoreapp3.0, so that for net452 the result is a EXE (like today) and for netcoreapp3.0 the result is a DLL, e.g.:

For simplicity we'll just rewrite the Main method in VC++ and move the code into CefSharp.BrowserSubprocess.Core

  • Make the Program.Main method internal and add a public method like the following that is only compiled for .NET Core:

Will keep the changes to CefSharp.BrowserSubprocess at a minimum, replace the Main body with a simple call to a method in CefSharp.BrowserSubprocess.Core that codes the actual work, this can be reused. Will need some fairly descriptive comments.

  • Adjust the NuGet files so that for projects targeting .NET Core, a reference to CefSharp.BrowserSubprocess.dll is added.

My initial plan is to create a new CefSharp.DotNetCore Nuget package, it'll depend on CefSharp.Common and add the required include for CefSharp.BrowserSubprocess.Core.dll, only minor changes will need to be made to the Nuget packages to support this.

  • In CefSharp.Core, adjust CefSettings.BrowserSubprocessPath to specify "CefSharp.BrowserSubprocess.exe" when running on .NET Framework, and specify the current executable path when running on .NET Core (this can be done with a runtime check, e.g. using RuntimeInformation.FrameworkDescription; but that would require compiling for .NET Framework 4.7.1 or higher).

Some sort of runtime check would be idea, will have to investigate alternatives as .Net 4.5.2 is our current target (I still get people asking to target .Net 4.0). Alternatively we'll provide some sort of helper class in the new DotNetCore specific Nuget package.

With that change, I think the only other thing needed is to adjust the NuGet spec files to declare dependencies to .NETCoreApp3.0 so the dependencies will resolve when compiling for .NET Core.

What does declare dependencies to .NETCoreApp3.0 actually get us? As it stands I don't see any great benefit in compiling an actual .Net Core specific set of dlls as the current ones appear to load correctly. If we can work out a runtime check then we'll add that to any calls that would require WCF so that users get an exception.

This seems to occur because the subprocess executable (CefSharp.BrowserSubprocess.exe) declares <dpiAware>true/PM</dpiAware> (per-monitor DPI aware V1), whereas in our case the application only supports <dpiAware>true</dpiAware> (System DPI aware).

It's hard to please everyone, the general idea is to provide a sensible set of defaults and let you customise as required. I rewrote the entire CefSharp.BrowserSubprocess.exe quite some time ago so it's just a single file, makes it easy to implement your own when the need arises.

kpreisser commented 5 years ago

Hi @amaitland,

thanks for your reply!

For simplicity we'll just rewrite the Main method in VC++ and move the code into CefSharp.BrowserSubprocess.Core

Will keep the changes to CefSharp.BrowserSubprocess at a minimum, replace the Main body with a simple call to a method in CefSharp.BrowserSubprocess.Core that codes the actual work, this can be reused. Will need some fairly descriptive comments.

Agreed, that way no change to the project file for CefSharp.BrowserSubprocess is needed - the .NET Core project can then reference CefSharp.BrowserSubprocess.Core.dll to call that method.

What does declare dependencies to .NETCoreApp3.0 actually get us? As it stands I don't see any great benefit in compiling an actual .Net Core specific set of dlls as the current ones appear to load correctly. If we can work out a runtime check then we'll add that to any calls that would require WCF so that users get an exception.

Sorry, actually here I only meant that the .nuspec file probably needs a <group targetFramework=".NETCoreApp3.0"> element in addition to <group targetFramework=".NETFramework4.5.2">, so that the transitive dependencies to the other NuGet packages will be resolved correctly for .NET Core projects. The projects itself (CefSharp.WinForms etc.) don't need to be changed, because like you said the binaries for .NET Framework 4.5.2 already work on .NET Core 3.0.

For example, right now if I create a new .NET Core 3.0 project and add <PackageReference Include="CefSharp.WinForms" Version="75.1.141" />, then the project will have a reference to CefSharp.WinForms, but is missing references to CefSharp.Common, CefSharp etc. and the redist files. Instead I currently have to manually add packages for all references:

  <PackageReference Include="CefSharp.WinForms" Version="75.1.141" />
  <PackageReference Include="CefSharp.Common" Version="75.1.141" />
  <PackageReference Include="cef.redist.x64" Version="75.1.14" />
  <PackageReference Include="cef.redist.x86" Version="75.1.14" />

However, this also does not yet work in the .NET Core 3.0 project because at runtime it will not find CefSharp.WinForms.dll even though the file exists. I think this is because the reference is declared private in CefSharp.WinForms.props:

      <ItemGroup>
        <Reference Include="CefSharp.WinForms">
          <HintPath>$(MSBuildThisFileDirectory)..\CefSharp\x64\CefSharp.WinForms.dll</HintPath>
          <Private>False</Private>
        </Reference>
      </ItemGroup>

This seems to have the effect that the dependency is not specified in the .deps.json file when building the project, so that the .NET Core CLR doesn't load the assembly. It should work when using <Private>True</Private> (but I guess this change would mean that the support for AnyCPU would no longer work; but as said I think we can ignore that for .NET Core projects).


To summarize, CefSharp.WinForms already works today on a .NET Core 3.0 WinForms project (created with dotnet new winforms) when using a project file like the following, and setting the <Platform> explicitely to x64 or x86 depending on the used .NET Core runtime – provided that .NET Framework 4.5.2 or higher is installed on the machine as that is currently used by CefSharp.BrowserSubprocess.exe:

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <RootNamespace>MyNetCoreApp</RootNamespace>
    <UseWindowsForms>true</UseWindowsForms>
    <Platforms>x86;x64</Platforms>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="CefSharp.WinForms" Version="75.1.141" />
    <PackageReference Include="CefSharp.Common" Version="75.1.141" />
    <PackageReference Include="cef.redist.x64" Version="75.1.14" />
    <PackageReference Include="cef.redist.x86" Version="75.1.14" />
  </ItemGroup>

  <ItemGroup>
    <Reference Update="CefSharp">
      <Private>true</Private>
    </Reference>
    <Reference Update="CefSharp.Core">
      <Private>true</Private>
    </Reference>
    <Reference Update="CefSharp.WinForms">
      <Private>true</Private>
    </Reference>
  </ItemGroup>
</Project>

Thanks!

kpreisser commented 5 years ago

Additionally, I found that the condition in CefSharp.WinForms.targets currently checks for the Platform Variable like this:

<When Condition="'$(Platform)' == 'x64'">

Maybe this needs to be changed to use PlatformTarget instead of Platform, because e.g. when you publish a self-contained .NET Core app with dotnet publish -f netcoreapp3.0 -r win-x64, the PlatformTarget will be x64, but Platform will still be AnyCPU; thus the check will currently fail.

Thanks!

amaitland commented 5 years ago

For example, right now if I create a new .NET Core 3.0 project and add <PackageReference Include="CefSharp.WinForms" Version="75.1.141" />, then the project will have a reference to CefSharp.WinForms, but is missing references to CefSharp.Common, CefSharp etc. and the redist files. Instead I currently have to manually add packages for all references:

It was my understanding (and that's a limited understanding at this point) that transitive dependencies meant that only top level references were included?

This seems to have the effect that the dependency is not specified in the .deps.json file when building the project, so that the .NET Core CLR doesn't load the assembly. It should work when using <Private>True</Private> (but I guess this change would mean that the support for AnyCPU would no longer work; but as said I think we can ignore that for .NET Core projects).

The files should be copied via the .targets file. What you are seeing may actually be the same as #2642

Additionally, I found that the condition in CefSharp.WinForms.targets currently checks for the Platform Variable like this:

The current packages require that you set the solution target, this is a current limitation.

Maybe this needs to be changed to use PlatformTarget instead of Platform, because e.g. when you publish a self-contained .NET Core app with dotnet publish -f netcoreapp3.0 -r win-x64, the PlatformTarget will be x64, but Platform will still be AnyCPU; thus the check will currently fail.

Last I checked this isn't possible, PlatformTarget is defined after the .props files are included, so it's not actually set early enough to be useful.

If you have the time to contribute a .Net Core example to https://github.com/cefsharp/CefSharp.MinimalExample that would make debugging/testing much easier.

kpreisser commented 5 years ago

Hi @amaitland,

For example, right now if I create a new .NET Core 3.0 project and add <PackageReference Include="CefSharp.WinForms" Version="75.1.141" />, then the project will have a reference to CefSharp.WinForms, but is missing references to CefSharp.Common, CefSharp etc. and the redist files. Instead I currently have to manually add packages for all references:

It was my understanding (and that's a limited understanding at this point) that transitive dependencies meant that only top level references were included?

Sorry, I'm not sure if I fully understood what you say here. With 'transitive reference', I meant that if project MyWinFormsApp references CefSharp.WinForms, and CefSharp.WinForms references CefSharp.Common, then MyWinFormsApp will also get a (transitive) reference to CefSharp.Common.

I don't have detailed knowledge about how NuGet packages work, but I think that when the .nuspec file of CefSharp.WinForms would be changed to contain the following:

    <dependencies>
      <group targetFramework=".NETFramework4.5.2">
        <dependency id="CefSharp.Common" version="[75.1.141]" />
      </group>
      <group targetFramework=".NETCoreApp3.0">
        <dependency id="CefSharp.Common" version="[75.1.141]" />
      </group>
    </dependencies>

then CefSharp.Common should be correctly included when the project only contains <PackageReference Include="CefSharp.WinForms" Version="75.1.141" />. VS will then display the package references as follows: grafik

Currently, when using a .NET Core 3.0 project I also have to add <PackageReference Include="CefSharp.Common" Version="75.1.141" />, and the VS will display the packages like this: grafik

The files should be copied via the .targets file. What you are seeing may actually be the same as #2642

Actually, the files are copied correctly into the output directory, but because the <Reference> element contains <Private>false</Private>, the references to these DLLs will not be specified in the <exe>.deps.json file when compiling the project, which means the CoreCLR will not load them (as AFAIK it will only load libraries that are specified in that JSON file).

Last I checked this isn't possible, PlatformTarget is defined after the .props files are included, so it's not actually set early enough to be useful.

OK, thanks. It might be worth to check if this is also the case with SDK-style (.NET Core) projects.

If you have the time to contribute a .Net Core example to https://github.com/cefsharp/CefSharp.MinimalExample that would make debugging/testing much easier.

OK, I will submit a PR with a minimal .NET Core example.

Thank you!

537mfb commented 5 years ago

Was testing @kpreisser's method of adding all packages individually and adding the private true in a WPF project and got the following error:

Unable to find an entry point named 'CopyMemory' in DLL 'kernel32.dll'.'

Looking around i found [https://github.com/dotnet/coreclr/issues/24008]https://github.com/dotnet/coreclr/issues/24008 where @vatsan-madhavan points out that:

In .NET Framework there was a special case for a few function names and CopyMemory happened to be one of them. The special case was removed in .NET Core. In order to address this in your code, please update the attribute to include the following: [DllImport EntryPoint="RtlMoveMemory"]. If the EntryPoint property is set as above, the P/Invoke will work in both .NET Framework and .NET Core

Its not just CopyMemory, as you can see in [https://github.com/AArnott/pinvoke/issues/431]https://github.com/AArnott/pinvoke/issues/431, other memory related functions are also affected - MoveMemory, CopyMemory, FillMemory and ZeroMemory at least

kpreisser commented 5 years ago

@537mfb Thanks! I created #2885 to fix the CopyMemory declaration.

I also updated https://github.com/cefsharp/CefSharp.MinimalExample/pull/57 to include a minimal WPF example using .NET Core 3.0 which should work once the CopyMemory declaration is fixed.

amaitland commented 5 years ago

@537mfb Thanks for the links πŸ‘ @kpreisser Thanks for the PR and updated WPF example πŸ‘

I am planning on releasing 75.1.142 in the next couple of days, I've committed https://github.com/cefsharp/CefSharp/commit/c11157fa8e77991baab83f417dfb78ba22f1e34b into the cefsharp/75 branch (copy and paste fail I've linked the incorrect issue, oh well) which contains the bare minimum required to get WPF working.

537mfb commented 5 years ago

@kpreisser Thanks for the quick PR @amaitland Thanks for the update

amaitland commented 5 years ago

Started the refactor of the BrowserProcess see #2891 there is a little bit to go before it's done.

Sorry, actually here I only meant that the .nuspec file probably needs a <group targetFramework=".NETCoreApp3.0"> element in addition to <group targetFramework=".NETFramework4.5.2">, so that the transitive dependencies to the other NuGet packages will be resolved correctly for .NET Core projects.

@kpreisser Have you tested that adding .NETCoreApp3.0 to the group resolves the dependency issue? If so are you able to create a PR?

kpreisser commented 5 years ago

@kpreisser Have you tested that adding .NETCoreApp3.0 to the group resolves the dependency issue? If so are you able to create a PR?

Yes, I tested it with the minimal .NET Core examples and the transitive references were successfully resolved. I created PR #2894 to add these. Thanks!

amaitland commented 5 years ago

I've committed c11157f into the cefsharp/75 branch (copy and paste fail I've linked the incorrect issue, oh well) which contains the bare minimum required to get WPF working.

Appears RtlCopyMemory only works in x64, reverted to using RtlMoveMemory in https://github.com/cefsharp/CefSharp/commit/050d354b82e054be122f1c2ca70bc64c8ec4900f

Suggestion that EntryPoint = "CopyMemory" actually mapped to RtlMoveMemory anyway. Will look at updating to RtlCopyMemory in the master branch.

amaitland commented 5 years ago

My initial plan is to create a new CefSharp.DotNetCore Nuget package, it'll depend on CefSharp.Common and add the required include for CefSharp.BrowserSubprocess.Core.dll, only minor changes will need to be made to the Nuget packages to support this.

Thanks to @kpreisser #2895 eliminates the requirements for a separate package, we'll go with this option instead.

inf9144 commented 5 years ago

Do you already know when you will release the .NET Core Support?

kpreisser commented 5 years ago

Do you already know when you will release the .NET Core Support?

Note that it's already possible use the current NuGet packages (75.1.142) within .NET Core 3.0 projects (with some limitations).

The CefSharp.MinimalExample repo contains example .NET Core 3.0 projects for WinForms, WPF and OffScreen (in solution CefSharp.MinimalExample.netcore.sln). See also ".NET Core support" in README.md.

If you want to completely avoid a dependency on .NET Framework 4.5.2 (which is currently still required by the CefSharp.BrowserSubprocess.exe), you can implement the code of the CefSharp.BrowserSubprocess.Program.Main() method in your own Main() method, so that it is run if the first argument starts with --type=, and otherwise the regular GUI application is run.

(Edit: But for that you would need to manually add a reference to CefSharp.BrowserSubprocess.Core.dll until #2895 is merged.)

In that case you would also need to set CefSettings.BrowserSubprocessPath = Process.GetCurrentProcess().MainModule.FileName (but that currently doesn't seem to work when you want to publish the application as single EXE with -p:PublishSingleFile=true).

There is a PR #2891 to refactor the subprocess handling for better .NET Core support.

TonyValenti commented 5 years ago

Really looking forward to this!

dmitrya1979 commented 5 years ago

Will it be possible to create cross-platform application with .net core, so it can work on Windows and MacOS? Minimal example is referencing "Microsoft.WindowsDesktop.App.WindowsForms" wich will not work on MacOS. Are you considering Cefsharp with Avalonia, so it will be cross-platform?

I mean something like this https://github.com/VitalElement/CefGlue.Core where .net core introduces Windows, MacOs and Linux

amaitland commented 5 years ago

Will it be possible to create cross-platform application with .net core, so it can work on Windows and MacOS?

@dmitrya1979 Unfortunately no. Microsoft have stated they have no current plans to port C++/CLI to MacOS/Linux, see https://devblogs.microsoft.com/cppblog/the-future-of-cpp-cli-and-dotnet-core-3/ for a very recent confirmation of this.

Microsoft has started implementing C++/CLI as part of .Net Core you can track the issue dotnet/coreclr#18013 They are only adding support for Windows initially. See also dotnet/coreclr#659

See also my comments in https://github.com/cefsharp/CefSharp/issues/2796#issue-450564341

You can subscribe to dotnet/coreclr#659 if you are interested in the subject.

hez2010 commented 5 years ago

In .NET Core 3.1 preview 2, C++/CLI support has been added. I think it's time to port CefSharp to .NET Core.

See https://devblogs.microsoft.com/cppblog/an-update-on-cpp-cli-and-dotnet-core/ and https://devblogs.microsoft.com/dotnet/announcing-net-core-3-1-preview-2/

amaitland commented 5 years ago

I think it's time to port CefSharp to .NET Core.

@hez2010 For what reason exactly? What problems are you seeing loading the existing dlls?

hez2010 commented 5 years ago

@amaitland Sorry, what I actually mean is to add .NET Core 3.0 nuget package support :)

However .NET Core provides higher performance and richer BCLs, I think it valuable to add a .NET Core version of CefSharp.

jahmai-ca commented 5 years ago

I was actually thinking a version of CefSharp that runs on Mac via NetCore3. Should that be possible with this announcement?

hez2010 commented 5 years ago

Should that be possible with this announcement?

@jahmai Nope. Currently there's no cross-platform C++/CLI and Microsoft has no plan for it yet. It also needs some works from gcc and clang to support compiling to MSIL.

Telavian commented 5 years ago

I thought .Net core 3 was already supported. I am actually using it currently in a .net core 3 project.

jahmai-ca commented 5 years ago

@jahmai Nope. Currently there's no cross-platform C++/CLI and Microsoft has no plan for it yet. It also needs some works from gcc and clang to support compiling to MSIL.

Excitement rescinded.

amaitland commented 5 years ago

Sorry, what I actually mean is to add .NET Core 3.0 nuget package support :)

@hez2010 It's already possible to use the current packages with .Net Core 3.0. All the details are listed above. Credit goes to @kpreisser

This issue only remains open as there are still some improvements to make integration easier.

I thought .Net core 3 was already supported

@Telavian Correct, it is.


A more modern set of Nuget packages will be created to better support PackageReference style projects, these will be the preferred packages for .Net Core 3.x. See #2795 (When this issue is resolved then this one will likely be closed).

The next major release of CefSharp will include #2891 which will make it easier to use your own exe for the Browser SubProcess.

TonyValenti commented 4 years ago

Hi All, Any ideas when the PackageReference packages will be available? I'm really looking forward to testing this out.

Also - RE .NET Core - Does the Sync and Async Javascript library use WCF or just the sync?

amaitland commented 4 years ago

You can use the current packages as is with a few limitations, see https://github.com/cefsharp/CefSharp.MinimalExample#net-core-support for an example.

Only the sync JavaScript Binding requires WCF, the Async version uses the IPC provided by CEF.

amaitland commented 4 years ago

Under .Net Core when VC++ is not installed the error message is different, the FAQ should be updated. The error message is misleading, unfortunately we have no control over that.

https://github.com/cefsharp/CefSharp/issues/3024 has stack trace

amaitland commented 4 years ago

The MinimalExample has been updated to demo self hosting the BrowserSubProcess. The changes were made in commit https://github.com/cefsharp/CefSharp.MinimalExample/commit/898eb755c6bb7f504f9b5bdc889ff9142e105848 The OffScreen example is crashing on exit so it has been updated, the line that specifies the BrowserSubProcessPath has been commented out until this has been fixed.

This eliminates the requirement to have .Net 4.5.2 or greater installed.

IMPORTANT:

You need to manually include CefSharp.BrowserSubprocess.Core in your proj file. The following example will work for x86/x64/Win32. Once #2795 is complete this step won't be required.

<!-- Include CefSharp.BrowserSubprocess.Core so we can selfhost the BrowserSubProcess using our exe -->
<Choose>
<When Condition="'$(PlatformTarget)' == 'x64'">
  <ItemGroup>
    <Reference Include="CefSharp.BrowserSubprocess.Core">
      <HintPath>$(CefSharpBrowserProcessCore64)</HintPath>
      <Private>true</Private>
    </Reference>
  </ItemGroup>
</When>
<!-- x86, Win32 and AnyCPU -->
<Otherwise>
  <ItemGroup>
    <Reference Include="CefSharp.BrowserSubprocess.Core">
      <HintPath>$(CefSharpBrowserProcessCore32)</HintPath>
      <Private>true</Private>
    </Reference>
  </ItemGroup>
</Otherwise>
</Choose>

Example application entry point for WPF/WinForms. CEF/Chromium uses the --type command line arg for launching the subproceses, you should see --type=gpu-process, --type=renderer, --type=utility, --type=crashpad-handler, etc.

public static class Program
{
    /// <summary>
    /// Application Entry Point.
    /// </summary>
    [STAThread]
    public static int Main(string[] args)
    {
        //For Windows 7 and above, app.manifest entries will take precedences of this call
        Cef.EnableHighDPISupport();

        //We are using our current exe as the BrowserSubProcess
        //Multiple instances will be spawned to handle all the 
        //Chromium proceses, render, gpu, network, plugin, etc.
        var subProcessExe = new CefSharp.BrowserSubprocess.BrowserSubprocessExecutable();
        var result = subProcessExe.Main(args);
        if (result > 0)
        {
            return result;
        }

        //We use our current exe as the BrowserSubProcess
        var exePath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;

        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 = exePath
        };

        //Example of setting a command line argument
        //Enables WebRTC
        settings.CefCommandLineArgs.Add("enable-media-stream");

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

        //Example for WPF
        var app = new App();
        app.InitializeComponent();
        return app.Run();

        //Example for WinForms
        var browser = new BrowserForm();
                Application.Run(browser);

        return 0;
    }
}
dora912 commented 4 years ago

Run the WPF example in Winows7 sp1 machine, it is also crashed on exit. Could you please tell me how to resolve this?

amaitland commented 4 years ago

Run the WPF example in Winows7 sp1 machine, it is also crashed on exit

@dora912 Just saying it doesn't work on Win7 using WPF Example is too vague.

Please provide a detailed report including:

vatsan-madhavan commented 4 years ago

Please make sure you are’t missing UCRT redistributables and KB2533623 on Win7.

See https://docs.microsoft.com/en-us/dotnet/core/install/dependencies

Also https://github.com/vatsanm/checknetcoreprereqs FYI.

amaitland commented 4 years ago

Anyone who is up for testing the more modern set of Nuget packages see https://github.com/cefsharp/CefSharp/issues/2795#issuecomment-587991081 for details. Looking for a few people to provide feedback before they'll be released for general consumption.

John0King commented 4 years ago

Is there a official sample of the .net core wpf ? I can't find any of the BrowserSubprocessExecutable class in current nuget package

John0King commented 4 years ago

I'm a bit confusing about why CefSharp.BrowerSubProcess.Core is not a .exe file on windows, base on my test, if a winexe project PackageReferece a exe project for .net core 3.1 is also copy the .exe file (the loader) to the buildouput too , if we need to just run the exe , why not just include all the asset of PublishTrimmed + selft-contained to the nuget? (include the loader and the app dll )

amaitland commented 4 years ago

Is there a official sample of the .net core wpf

Commit https://github.com/cefsharp/CefSharp.MinimalExample/commit/898eb755c6bb7f504f9b5bdc889ff9142e105848 adds support for self hosting the BrowserSubProcess as detailed in https://github.com/cefsharp/CefSharp/issues/2796#issuecomment-583932675 (which also includes this link).

I'm a bit confusing about why CefSharp.BrowerSubProcess.Core is not a .exe file on windows

CefSharp.BrowerSubProcess.Core.dll exists as a library for the purpose of self hosting the BrowserSubProcess, this is desirable in many cases. The Core in the dll name is in no way a reference to .Net Core, it's a CLI/C++ library that targets .Net 4.5.2 currently.

if we need to just run the exe , why not just include all the asset of PublishTrimmed + selft-contained to the nuget? (include the loader and the app dll )

You are welcome to submit a PR with some proposed changes, you can discuss them there. Including a .exe as a library reference is not standard practice and is something we're unlikely to go with. For self contained self hosting the BrowserSubProcess is the current recommended option.

It's unlikely we'll be able to support all of the uses cases out of the box (unless we can convince Microsoft to make some improvements).

Sybren- commented 4 years ago

I'm trying use ChromiumWebBrowser in an NET Core 3.1 Class Library. I have modified the .csproj according to the netcore .csproj here: https://github.com/cefsharp/CefSharp.MinimalExample/tree/master/CefSharp.MinimalExample.Wpf

But the BrowserSubProcess and other initialization has to be done from a Main method (Program.netcore.cs in the example)

A a Class Library has no Program.cs, how to start the BrowserSubProcess and do the initialization? Also, I want to target AnyCPU but therefore is also an App or a Program class required which I don't have in a Class Library. AnyCPU examples: #1714

Or do I have to do the initialization in my WinExe project and can the Class Library use the BrowserSubProcess from the WinExe project?

An example of how the two .csrpoj files need to look + initiliazation code would be helpful.

amaitland commented 4 years ago

A a Class Library has no Program.cs, how to start the BrowserSubProcess and do the initialization?

To self host the BrowserSubProcess then the code must be in the applications main entry point. The application will be spawned multiple times to support the Chromium Multi-process Architecture. Your class library can easily expose a helper method to wrap the required code.

Overall there are plenty of options available for providing a BrowserSubProcess

I want to target AnyCPU

There are no .Net Core AnyCPU examples as it's not clear that it's possible to support. There are many limitations currently with .Net Core

For your scenario of having an AnyCPU Class Library you'll have to do your own research to see what is possible. Personally I'd suggest keeping things simple by having an x86 and x64 target.

@Sybren- I'd also suggest you all the comments in this issue as there is a lot of relevant background information here already.

vatsan-madhavan commented 4 years ago
  • .Net Core will only load files that are included in .deps.json (as described in #2796 (comment)). This makes dynamically loading files are runtime near impossible.

@kpreisser I think I'm following what is being described in the comment, but I'm not 100% sure. if you can create a simple barebones project that illustrates the problem you are encountering, I can try to troubleshoot and see if we can come up with a solutions (or craft a viable workaround).

For your scenario of having an AnyCPU Class Library you'll have to do your own research to see what is possible. Personally I'd suggest keeping things simple by having an x86 and x64 target.

Have you had a chance to look at the cross platform targeting documentation yet?

If you don't have architecture specific code, then why not just build a netstandard2.1 targeted library?

If you truly have architecture specific code, then you'll probably need a code, build that generates reference-assemblies, RID-specific runtime assemblies and separately and also have a packaging architecture that uses the bait & switch technique_ (which is not very well documented). I wouldn't recommend going down this path if you can help it. Adding some complexity to make your library conform to nestandard2.1 would be worth it ultimately. But if you must, even this can be done with a little effort. Microsoft.NetCore.App is a good example of a package that illustrates how this technique is used. You might find it in your NuGet cache already, or else you can download it from NuGet.org and peek into it using NuGetPackageExplorer. If you look into runtime.json in the package, you'll see how the base package 'links' to RID-specific packages. A project only needs to PackageReference a package such as this - the RID-specific packages would be automatically selected and restored by NuGet intelligently on a need basis. (Microsoft.NetCore.App itself is not intended to be used in PackageReference scenarios - I'm just calling attention to it to illustrate the bait & switch technique).

There is no probing privatePath option in .Net Core, support for loading from a sub directory is very limited currently see dotnet/sdk#10366

This can be worked around in an inline MSBuild task. Take a look at _AssemblyResolverForSystemReflectionMetadataLoadContext task here. There are several things that can trip you, but it can be done reliably if you are in reasonable control of some of the parameters involved.

amaitland commented 4 years ago

@vatsan-madhavan Thanks for taking the time to comment πŸ‘ Any insights you could give would be greatly appreciated. Even just a pointer to the appropriate place to ask some additional questions would be helpful.

I think I'm following what is being described in the comment, but I'm not 100% sure. if you can create a simple barebones project that illustrates the problem you are encountering, I can try to troubleshoot and see if we can come up with a solutions (or craft a viable workaround).

For the current set of Nuget packages (CefSharp.WinForms, CefSharp.Wpf, CefSharp.OffScreen) the project will compile, the dlls are included in the bin directory, they are however not included in .deps.json, which I believe is described in https://github.com/dotnet/sdk/issues/2162#issuecomment-382852510

The current Nuget packages allow for an old school bait and switch for AnyCPU support and an optional $(CefSharpTargetDir) property in MSBuild used to customise the file copy location. It's fairly common complain that CefSharp pollutes the bin folder with too many dlls.

I was hoping to create a new set of Nuget packages that utilise runtimes/{rid}/native for the unmanaged dlls and resources.

The folder structure used by the Chromium Embedded Framework(CEF) looks like the following screenshot

folderstructure

If you have any suggestions or potential workarounds for this I'd be interested in testing them out, I can provide an example without too much effort.

Ignore the missing files for now (copying them manually to be precise) the Mixed Mode CLI/C++ assembly (CefSharp.Core.dll) is unable to automatically load libcef.dll when it's located in the runtimes/{rid}/native folder. If I manually call NativeLibrary to load the assembly then I can successfully load CefSharp.Core.dll. I know VC++ is fairly new to .Net Core so I'm wondering if the scenario is supported yet? I can provide an example, if required.

There are some other issues that I've run into along the way:

Have you had a chance to look at the cross platform targeting documentation yet?

I haven't yet no, I'll check it out thanks.

If you don't have architecture specific code, then why not just build a netstandard2.1 targeted library?

The situation as it stands

Adding some complexity to make your library conform to nestandard2.1 would be worth it ultimately

There are quite a few other build environment complexities that I won't get into now.

Microsoft.NetCore.App is a good example of a package that illustrates how this technique is used. You might find it in your NuGet cache already, or else you can download it from NuGet.org and peek into it using NuGetPackageExplorer. If you look into runtime.json in the package, you'll see how the base package 'links' to RID-specific packages. A project only needs to PackageReference a package such as this - the RID-specific packages would be automatically selected and restored by NuGet intelligently on a need basis. (Microsoft.NetCore.App itself is not intended to be used in PackageReference scenarios - I'm just calling attention to it to illustrate the bait & switch technique).

I downloaded microsoft.netcore.app.2.2.8.nupkg a few months ago as I started doing research into using a runtime.json file as I'd seen references suggesting it might be the solution, unfortunately I couldn't find any official documentation. Any chance there is some documentation somewhere that I missed?

I got stuck on the earlier issues and reverted to a more traditional .targets approach in the short term.

This can be worked around in an inline MSBuild task. Take a look at _AssemblyResolverForSystemReflectionMetadataLoadContext task here. There are several things that can trip you, but it can be done reliably if you are in reasonable control of some of the parameters involved.

Thanks for the reference, hopefully others will find this helpful πŸ‘

vatsan-madhavan commented 4 years ago

I was hoping to create a new set of Nuget packages that utilise runtimes/{rid}/native for the unmanaged dlls and resources.

I took a look at CefSharp.Winforms, and I think you have already tried the one thing I would have suggested - which is to attempt <Reference Include="..." /> directly.

Attempting to build a normalized structure within NuGet package based on folder-conventions would be the next thing to try, which you've already figured out I think.

re: the swiftshader problem...

unfortunately it only works when the package is directly referenced, when it's included as a transitive reference then they folders aren't copied to the native folder.

Have you tried changing PrivateAssets (in the PackageReference?) to analyzers;build? The default normally includes contentfiles.

re: c++/cli: you're absolutely right, it can only build netcoreap* or netfx targets. I hadn't realized that you were dealing with c++/cli when I made that comment.

unfortunately I couldn't find any official documentation. Any chance there is some documentation somewhere that I missed?

You didn't miss any - there just isn't any out there. Folks including me who have implemented this have done so through trial and error.

The general idea goes like this:

A concrete example may look like this:

  "runtimes": {
    "win-x64": {
      "$(NormalizedPackageName)": {
        "runtime.win-x64.$(PackageName)": "$(PackageVersion)"
      }
    },
    "win-x86": {
      "$(NormalizedPackageName)": {
        "runtime.win-x86.$(PackageName)": "$(PackageVersion)"
      }
    }
  }
}
amaitland commented 4 years ago

@vatsan-madhavan Thanks for the very detailed reply, greatly appreciated πŸ‘

I took a look at CefSharp.Winforms,

I'll rework the demo to illustrate the problems I'm having using runtimes/{rid}/native with the Mixed Mode CLI/C++ dlls loading the unmanaged dlls, perhaps there is something trivial I'm missing or perhaps switching to the runtime.json will resolve the issues.

Hopefully have a demo of the problem shortly.

Have you tried changing PrivateAssets (in the PackageReference?) to analyzers;build? The default normally includes contentfiles.

I haven't yet no, I'll give that a try.

I did see the Nuget 5+ supports buildTransitive which should allow me to use a .targets file to copy the swiftshader and locales folders to the runtimes/{rid}/native folder, they're only required at runtime so this sounds workable to me.

Folks including me who have implemented this have done so through trial and error.

Thanks again for taking the time to write such a detailed post, I'm pretty sure I understand the basic concept πŸ˜„

Looking at the microsoft.netcore.app.2.2.8 package in a little more detail to understand how the bait and switch works for the managed dlls. From what I can tell it provides a set of compiler reference assemblies that likely target AnyCPU in the ref directory. Are you aware of any packages that use VC++ dlls? The From a convention-based working directory section for ref say the dlls should target AnyCPU. For CefSharp.Core.dll which is a Mixed Mode CLI/C++ dll I cannot compile as AnyCPU, it's required for both compile time and runtime.