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

AnyCPU - Simplify support via ModuleInitializer #4063

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions CefSharp.Core/CefRuntimeAnyCpuInitializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright © 2022 The CefSharp Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

using System.Reflection;
using System.Runtime.CompilerServices;

namespace CefSharp
{
/// <summary>
/// When targeting NETFRAMEWORK this Module Initializer
/// will call <see cref="CefRuntime.SubscribeAnyCpuAssemblyResolver(string)"/>
/// when the <see cref="Assembly.GetEntryAssembly"/> has a <see cref="ProcessorArchitecture.MSIL"/>.
/// </summary>
public static class CefRuntimeAnyCpuInitializer
{
/// <summary>
/// True if the AnyCpu resolver was attached via this Module Initializer
/// </summary>
public static bool AssemblyResolverAttached { get; private set; }

[ModuleInitializer]
internal static void ModuleInitializer()
{
var executingAssembly = Assembly.GetEntryAssembly();
//The GetEntryAssembly method can return null when a managed assembly has been loaded from an unmanaged application.
//https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.getentryassembly
if (executingAssembly != null && CefRuntime.SubscribeToAnyCpuResolverInModuleInitializer)
{
var name = executingAssembly.GetName();
if (name.ProcessorArchitecture == ProcessorArchitecture.MSIL)
{
if (!CefRuntime.IsAnyCpuAssemblyResolverAttached)
{
AssemblyResolverAttached = true;

CefRuntime.SubscribeAnyCpuAssemblyResolver();
}
}
}
}
}
}
4 changes: 2 additions & 2 deletions CefSharp.Core/CefSharp.Core.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net452</TargetFramework>
<OutputType>Library</OutputType>
Expand All @@ -8,6 +8,7 @@
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\CefSharp.snk</AssemblyOriginatorKeyFile>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>9</LangVersion>
<!--
Stop MSBuild from appending TargetFramework to output path.
Remove if we use TargetFrameworks
Expand All @@ -34,7 +35,6 @@
<ItemGroup>
<Compile Remove="BrowserSettings.netcore.cs" />
<Compile Remove="Initializer.cs" />
<Compile Remove="ModuleInitializerAttribute.cs" />
<Compile Remove="PostData.netcore.cs" />
<Compile Remove="PostDataElement.netcore.cs" />
<Compile Remove="Request.netcore.cs" />
Expand Down
3 changes: 2 additions & 1 deletion CefSharp.Core/CefSharp.Core.netcore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

<ItemGroup>
<Compile Remove="BrowserSettings.cs" />
<Compile Remove="CefRuntimeAnyCpuInitializer.cs" />
<Compile Remove="PostData.cs" />
<Compile Remove="PostDataElement.cs" />
<Compile Remove="Request.cs" />
Expand Down Expand Up @@ -67,7 +68,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>

<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
Expand Down
40 changes: 31 additions & 9 deletions CefSharp/CefRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,27 @@ namespace CefSharp
/// </summary>
public static class CefRuntime
{
private static ResolveEventHandler currentDomainAssemblyResolveHandler;
private static ResolveEventHandler CurrentDomainAssemblyResolveHandler;

/// <summary>
/// For AnyCPU support a ModileInitializer in CefSharp.Core.dll will run when the dll is
/// loaded and call <see cref="SubscribeAnyCpuAssemblyResolver(string)"/> if tge dll is <see cref="ProcessorArchitecture.MSIL"/>
/// Set this to false before attempting to load any other CefSharp resources to avoid attaching the resolver.
/// Alternatively call <see cref="UnsubscribeAnyCpuAssemblyResolver"/> to remove the resolver.
/// </summary>
public static bool SubscribeToAnyCpuResolverInModuleInitializer { get; set; } = true;

/// <summary>
/// Check if an AnyCpu resolver has already been attached (subscribed to the
/// <see cref="AppDomain.AssemblyResolve"/> event for the <see cref="AppDomain.CurrentDomain"/>
/// </summary>
public static bool IsAnyCpuAssemblyResolverAttached
{
get
{
return CurrentDomainAssemblyResolveHandler != null;
}
}

/// <summary>
/// When using AnyCPU the architecture specific version of CefSharp.Core.Runtime.dll
Expand All @@ -24,25 +44,27 @@ public static class CefRuntime
/// based on <see cref="Environment.Is64BitProcess"/>.
/// This method MUST be called before you call Cef.Initialize, create your first ChromiumWebBrowser instance, basically
/// before anything CefSharp related happens. This method is part of CefSharp.dll which is an AnyCPU library and
/// doesn't have any references to the CefSharp.Core.Runtime.dll so it's safe to use.
/// doesn't have any references to the CefSharp.Core.Runtime.dll so it's safe to use. Multiple calls will
/// result in <see cref="UnsubscribeAnyCpuAssemblyResolver"/> being called if <see cref="IsAnyCpuAssemblyResolverAttached"/>
/// is true.
/// </summary>
/// <param name="basePath">
/// The path containing the x64/x86 folders which contain the CefSharp/CEF resources.
/// If null then AppDomain.CurrentDomain.SetupInformation.ApplicationBase will be used as the path.
/// (</param>
/// </param>
public static void SubscribeAnyCpuAssemblyResolver(string basePath = null)
{
if(currentDomainAssemblyResolveHandler != null)
if(IsAnyCpuAssemblyResolverAttached)
{
throw new Exception("UseAnyCpuAssemblyResolver has already been called, call ");
UnsubscribeAnyCpuAssemblyResolver();
}

if(basePath == null)
{
basePath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
}

currentDomainAssemblyResolveHandler = (sender, args) =>
CurrentDomainAssemblyResolveHandler = (sender, args) =>
{
if (args.Name.StartsWith("CefSharp.Core.Runtime"))
{
Expand All @@ -59,7 +81,7 @@ public static void SubscribeAnyCpuAssemblyResolver(string basePath = null)
return null;
};

AppDomain.CurrentDomain.AssemblyResolve += currentDomainAssemblyResolveHandler;
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolveHandler;
}

/// <summary>
Expand All @@ -68,9 +90,9 @@ public static void SubscribeAnyCpuAssemblyResolver(string basePath = null)
/// </summary>
public static void UnsubscribeAnyCpuAssemblyResolver()
{
AppDomain.CurrentDomain.AssemblyResolve -= currentDomainAssemblyResolveHandler;
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomainAssemblyResolveHandler;

currentDomainAssemblyResolveHandler = null;
CurrentDomainAssemblyResolveHandler = null;
}

/// <summary>
Expand Down
20 changes: 4 additions & 16 deletions NuGet/CefSharp.Common.targets
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
<Message Importance="high" Text="CefSharpTargetDir = $(CefSharpTargetDir)" />
<Message Importance="high" Text="CefSharpTargetDirAnyCpu32 = $(CefSharpTargetDirAnyCpu32)" />
<Message Importance="high" Text="CefSharpTargetDirAnyCpu64 = $(CefSharpTargetDirAnyCpu64)" />
<Message Importance="high" Text="CefSharpAnyCpuSupport = $(CefSharpAnyCpuSupport)" />
<Message Importance="high" Text="RuntimeIdentifier = $(RuntimeIdentifier)" />
<Message Importance="high" Text="EffectivePlatform = $(EffectivePlatform)" />
<Message Importance="high" Text="Platform = $(Platform)" />
Expand Down Expand Up @@ -137,16 +138,6 @@
https://github.com/dotnet/project-system/issues/4158
-->
<CefSharpPropertiesLoaded>true</CefSharpPropertiesLoaded>

<!--
For PackageReference library projects where buildTransitive is supported we'll default to setting CefSharpAnyCpuSupport to true
This should only require WinExe and Exe projects to specify CefSharpAnyCpuSupport rather than every project in a solution.
Only for PackageReference projects where NuGetToolVersion > 5.0 and OutputType is library.
Defaulting CefSharpAnyCpuSupport to true is simpler than modifying the already complex Condition for the CefSharpPlatformCheck
target below.
https://github.com/cefsharp/CefSharp/issues/3622
-->
<CefSharpAnyCpuSupport Condition="'$(CefSharpAnyCpuSupport)' != 'true' AND '$(NuGetProjectStyle)' == 'PackageReference' AND $(NuGetToolVersion) > '5.0' AND '$(OutputType)' == 'Library'">true</CefSharpAnyCpuSupport>
</PropertyGroup>

<Choose>
Expand Down Expand Up @@ -358,20 +349,17 @@
</Choose>

<!--
This Transform is no longer used by default, the project was changed to use a ModuleInitializer to resolve the references
at runtime. The transform remains here for anyone wishing to manually call
For AnyCPU we use a Transform to add entries to app.config if possible
Otherwise throw error to alert user they need to perform additional action
-->
<UsingTask TaskName="TransformXml" AssemblyFile="$(CefSharpTransformXmlDllPath)" Condition="Exists('$(CefSharpTransformXmlDllPath)') AND '$(CefSharpPlatformTarget)' == 'AnyCPU' AND '$(CefSharpAnyCpuSupport)' == '' AND '$(CefSharpBuildAction)' != 'NoAction'" />

<Target Name="CefSharpCommonAnyCPUConfigTransform" AfterTargets="_CopyAppConfigFile" Condition="'@(AppConfigWithTargetPath)' != '' AND Exists('$(CefSharpTransformXmlDllPath)') AND '$(CefSharpPlatformTarget)' == 'AnyCPU' AND '$(CefSharpAnyCpuSupport)' == '' AND '$(CefSharpBuildAction)' != 'NoAction'">
<Target Name="CefSharpCommonAnyCPUConfigTransform">
<TransformXml Source="@(AppConfigWithTargetPath->'$(OutDir)%(TargetPath)')" Transform="$(MSBuildThisFileDirectory)..\build\app.config.x86.transform" Destination="@(AppConfigWithTargetPath->'$(OutDir)%(TargetPath)')"/>
<TransformXml Source="@(AppConfigWithTargetPath->'$(OutDir)%(TargetPath)')" Transform="$(MSBuildThisFileDirectory)..\build\app.config.x64.transform" Destination="@(AppConfigWithTargetPath->'$(OutDir)%(TargetPath)')"/>
</Target>

<Target Name="CefSharpPlatformCheck" BeforeTargets="ResolveAssemblyReferences" Condition="('@(AppConfigWithTargetPath)' == '' OR !Exists('$(CefSharpTransformXmlDllPath)')) AND '$(CefSharpPlatformTarget)' == 'AnyCPU' AND '$(CefSharpAnyCpuSupport)' != 'true' AND '$(CefSharpBuildAction)' != 'NoAction'">
<Error Text="$(MSBuildThisFileName) is unable to proceeed as your current PlatformTarget is '$(CefSharpPlatformTarget)'. To target AnyCPU please read https://github.com/cefsharp/CefSharp/issues/1714. Alternatively change your PlatformTarget to x86 or x64 and the relevant files will be copied automatically." HelpKeyword="CefSharpSolutionPlatformCheck" />
</Target>

<!--
Issue https://github.com/dotnet/project-system/issues/4158
The None/Content entries aren't picked up as the .targets file doesn't exist before the Nuget restore (only when using packages.config)
Expand Down