Skip to content

Commit 9fc6cab

Browse files
committed
AnyCpu - Add ModuleInitializer to resolve AnyCpu dlls at runtime
- Checks if the Assembly.GetEntryAssembly() is MSIL - Change CefSharp.Core.csproj to use LangVersion 9 so we can get the compiler to output the ModuleInitializer - Remove CefSharpPlatformCheck and other CefSharpAnyCpuSupport checks - CefSharp.Common.targets still has CefSharpAnyCpuSupport references for backwards compatibility with those with it set and Prefer32bit The default behaviour for this case is to simply copy the 32bit libs - Calling CefRuntime.SubscribeAnyCpuAssemblyResolver multiple times now will simply Unsubscribe the previous resolver This change should remove the need for users to perform any action when supporting AnyCpu.
1 parent 59f9608 commit 9fc6cab

File tree

5 files changed

+81
-28
lines changed

5 files changed

+81
-28
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright © 2022 The CefSharp Authors. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4+
5+
using System.Reflection;
6+
using System.Runtime.CompilerServices;
7+
8+
namespace CefSharp
9+
{
10+
/// <summary>
11+
/// When targeting NETFRAMEWORK this Module Initializer
12+
/// will call <see cref="CefRuntime.SubscribeAnyCpuAssemblyResolver(string)"/>
13+
/// when the <see cref="Assembly.GetEntryAssembly"/> has a <see cref="ProcessorArchitecture.MSIL"/>.
14+
/// </summary>
15+
public static class CefRuntimeAnyCpuInitializer
16+
{
17+
/// <summary>
18+
/// True if the AnyCpu resolver was attached via this Module Initializer
19+
/// </summary>
20+
public static bool AssemblyResolverAttached { get; private set; }
21+
22+
[ModuleInitializer]
23+
internal static void ModuleInitializer()
24+
{
25+
var executingAssembly = Assembly.GetEntryAssembly();
26+
//The GetEntryAssembly method can return null when a managed assembly has been loaded from an unmanaged application.
27+
//https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.getentryassembly
28+
if (executingAssembly != null && CefRuntime.SubscribeToAnyCpuResolverInModuleInitializer)
29+
{
30+
var name = executingAssembly.GetName();
31+
if (name.ProcessorArchitecture == ProcessorArchitecture.MSIL)
32+
{
33+
if (!CefRuntime.IsAnyCpuAssemblyResolverAttached)
34+
{
35+
AssemblyResolverAttached = true;
36+
37+
CefRuntime.SubscribeAnyCpuAssemblyResolver();
38+
}
39+
}
40+
}
41+
}
42+
}
43+
}

CefSharp.Core/CefSharp.Core.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<TargetFramework>net452</TargetFramework>
44
<OutputType>Library</OutputType>
@@ -8,6 +8,7 @@
88
<SignAssembly>true</SignAssembly>
99
<AssemblyOriginatorKeyFile>..\CefSharp.snk</AssemblyOriginatorKeyFile>
1010
<PlatformTarget>AnyCPU</PlatformTarget>
11+
<LangVersion>9</LangVersion>
1112
<!--
1213
Stop MSBuild from appending TargetFramework to output path.
1314
Remove if we use TargetFrameworks
@@ -34,7 +35,6 @@
3435
<ItemGroup>
3536
<Compile Remove="BrowserSettings.netcore.cs" />
3637
<Compile Remove="Initializer.cs" />
37-
<Compile Remove="ModuleInitializerAttribute.cs" />
3838
<Compile Remove="PostData.netcore.cs" />
3939
<Compile Remove="PostDataElement.netcore.cs" />
4040
<Compile Remove="Request.netcore.cs" />

CefSharp.Core/CefSharp.Core.netcore.csproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
<ItemGroup>
4040
<Compile Remove="BrowserSettings.cs" />
41+
<Compile Remove="CefRuntimeAnyCpuInitializer.cs" />
4142
<Compile Remove="PostData.cs" />
4243
<Compile Remove="PostDataElement.cs" />
4344
<Compile Remove="Request.cs" />
@@ -67,7 +68,7 @@
6768
</ItemGroup>
6869

6970
<ItemGroup>
70-
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
71+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
7172
</ItemGroup>
7273

7374
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />

CefSharp/CefRuntime.cs

+31-9
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,27 @@ namespace CefSharp
1414
/// </summary>
1515
public static class CefRuntime
1616
{
17-
private static ResolveEventHandler currentDomainAssemblyResolveHandler;
17+
private static ResolveEventHandler CurrentDomainAssemblyResolveHandler;
18+
19+
/// <summary>
20+
/// For AnyCPU support a ModileInitializer in CefSharp.Core.dll will run when the dll is
21+
/// loaded and call <see cref="SubscribeAnyCpuAssemblyResolver(string)"/> if tge dll is <see cref="ProcessorArchitecture.MSIL"/>
22+
/// Set this to false before attempting to load any other CefSharp resources to avoid attaching the resolver.
23+
/// Alternatively call <see cref="UnsubscribeAnyCpuAssemblyResolver"/> to remove the resolver.
24+
/// </summary>
25+
public static bool SubscribeToAnyCpuResolverInModuleInitializer { get; set; } = true;
26+
27+
/// <summary>
28+
/// Check if an AnyCpu resolver has already been attached (subscribed to the
29+
/// <see cref="AppDomain.AssemblyResolve"/> event for the <see cref="AppDomain.CurrentDomain"/>
30+
/// </summary>
31+
public static bool IsAnyCpuAssemblyResolverAttached
32+
{
33+
get
34+
{
35+
return CurrentDomainAssemblyResolveHandler != null;
36+
}
37+
}
1838

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

4062
if(basePath == null)
4163
{
4264
basePath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
4365
}
4466

45-
currentDomainAssemblyResolveHandler = (sender, args) =>
67+
CurrentDomainAssemblyResolveHandler = (sender, args) =>
4668
{
4769
if (args.Name.StartsWith("CefSharp.Core.Runtime"))
4870
{
@@ -59,7 +81,7 @@ public static void SubscribeAnyCpuAssemblyResolver(string basePath = null)
5981
return null;
6082
};
6183

62-
AppDomain.CurrentDomain.AssemblyResolve += currentDomainAssemblyResolveHandler;
84+
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolveHandler;
6385
}
6486

6587
/// <summary>
@@ -68,9 +90,9 @@ public static void SubscribeAnyCpuAssemblyResolver(string basePath = null)
6890
/// </summary>
6991
public static void UnsubscribeAnyCpuAssemblyResolver()
7092
{
71-
AppDomain.CurrentDomain.AssemblyResolve -= currentDomainAssemblyResolveHandler;
93+
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomainAssemblyResolveHandler;
7294

73-
currentDomainAssemblyResolveHandler = null;
95+
CurrentDomainAssemblyResolveHandler = null;
7496
}
7597

7698
/// <summary>

NuGet/CefSharp.Common.targets

+3-16
Original file line numberDiff line numberDiff line change
@@ -138,16 +138,6 @@
138138
https://github.com/dotnet/project-system/issues/4158
139139
-->
140140
<CefSharpPropertiesLoaded>true</CefSharpPropertiesLoaded>
141-
142-
<!--
143-
For PackageReference library projects where buildTransitive is supported we'll default to setting CefSharpAnyCpuSupport to true
144-
This should only require WinExe and Exe projects to specify CefSharpAnyCpuSupport rather than every project in a solution.
145-
Only for PackageReference projects where NuGetToolVersion > 5.0 and OutputType is library.
146-
Defaulting CefSharpAnyCpuSupport to true is simpler than modifying the already complex Condition for the CefSharpPlatformCheck
147-
target below.
148-
https://github.com/cefsharp/CefSharp/issues/3622
149-
-->
150-
<CefSharpAnyCpuSupport Condition="'$(CefSharpAnyCpuSupport)' != 'true' AND '$(NuGetProjectStyle)' == 'PackageReference' AND $(NuGetToolVersion) > '5.0' AND '$(OutputType)' == 'Library'">true</CefSharpAnyCpuSupport>
151141
</PropertyGroup>
152142

153143
<Choose>
@@ -359,20 +349,17 @@
359349
</Choose>
360350

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

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

372-
<Target Name="CefSharpPlatformCheck" BeforeTargets="ResolveAssemblyReferences" Condition="('@(AppConfigWithTargetPath)' == '' OR !Exists('$(CefSharpTransformXmlDllPath)')) AND '$(CefSharpPlatformTarget)' == 'AnyCPU' AND '$(CefSharpAnyCpuSupport)' != 'true' AND '$(CefSharpBuildAction)' != 'NoAction'">
373-
<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" />
374-
</Target>
375-
376363
<!--
377364
Issue https://github.com/dotnet/project-system/issues/4158
378365
The None/Content entries aren't picked up as the .targets file doesn't exist before the Nuget restore (only when using packages.config)

0 commit comments

Comments
 (0)