Side-by-Side 어셈블리를 사용하여 x64 또는 x32 버전의 DLL 로드

StackOverflow https://stackoverflow.com/questions/108971

  •  01-07-2019
  •  | 
  •  

문제

관리되는 C++ 어셈블리에는 x86용과 x64용의 두 가지 버전이 있습니다.이 어셈블리는 AnyCPU용으로 준수되는 .net 애플리케이션에 의해 호출됩니다.우리는 파일 복사 설치를 통해 코드를 배포하고 있으며 계속 그렇게 하고 싶습니다.

애플리케이션이 해당 프로세서 아키텍처를 동적으로 선택할 때 Side-by-Side 어셈블리 매니페스트를 사용하여 각각 x86 또는 x64 어셈블리를 로드할 수 있습니까?아니면 파일 복사 배포에서 이 작업을 수행하는 다른 방법이 있습니까(예:GAC를 사용하지 않음)?

도움이 되었습니까?

해결책

AnyCPU로 컴파일된 실행 파일에서 플랫폼별 어셈블리를 로드할 수 있는 간단한 솔루션을 만들었습니다.사용된 기술은 다음과 같이 요약될 수 있습니다.

  1. 기본 .NET 어셈블리 로딩 메커니즘("Fusion" 엔진)이 플랫폼별 어셈블리의 x86 또는 x64 버전을 찾을 수 없는지 확인하세요.
  2. 기본 애플리케이션이 플랫폼별 어셈블리 로드를 시도하기 전에 현재 AppDomain에 사용자 지정 어셈블리 확인자를 설치합니다.
  3. 이제 기본 애플리케이션에 플랫폼별 어셈블리가 필요할 때 Fusion 엔진은 (1단계로 인해) 포기하고 (2단계로 인해) 사용자 지정 확인자를 호출합니다.사용자 지정 확인자에서는 현재 플랫폼을 확인하고 디렉터리 기반 조회를 사용하여 적절한 DLL을 로드합니다.

이 기술을 시연하기 위해 짧은 명령줄 기반 튜토리얼을 첨부합니다.Windows XP x86과 Vista SP1 x64에서 결과 바이너리를 테스트했습니다(배포와 마찬가지로 바이너리를 복사하여).

참고 1:"csc.exe"는 C-sharp 컴파일러입니다.이 튜토리얼에서는 그것이 사용자의 경로에 있다고 가정합니다(내 테스트에서는 "C:\WINDOWS\Microsoft.NET\Framework\v3.5\csc.exe"를 사용했습니다).

노트 2:테스트용 임시 폴더를 만들고 현재 작업 디렉터리가 이 위치로 설정된 명령줄(또는 powershell)을 실행하는 것이 좋습니다.

(cmd.exe)
C:
mkdir \TEMP\CrossPlatformTest
cd \TEMP\CrossPlatformTest

1 단계:플랫폼별 어셈블리는 간단한 C# 클래스 라이브러리로 표현됩니다.

// file 'library.cs' in C:\TEMP\CrossPlatformTest
namespace Cross.Platform.Library
{
    public static class Worker
    {
        public static void Run()
        {
            System.Console.WriteLine("Worker is running");
            System.Console.WriteLine("(Enter to continue)");
            System.Console.ReadLine();
        }
    }
}

2 단계:간단한 명령줄 명령을 사용하여 플랫폼별 어셈블리를 컴파일합니다.

(cmd.exe from Note 2)
mkdir platform\x86
csc /out:platform\x86\library.dll /target:library /platform:x86 library.cs
mkdir platform\amd64
csc /out:platform\amd64\library.dll /target:library /platform:x64 library.cs

3단계:메인 프로그램은 두 부분으로 나누어져 있습니다."Bootstrapper"에는 실행 파일의 기본 진입점이 포함되어 있으며 현재 appdomain에 사용자 지정 어셈블리 확인자를 등록합니다.

// file 'bootstrapper.cs' in C:\TEMP\CrossPlatformTest
namespace Cross.Platform.Program
{
    public static class Bootstrapper
    {
        public static void Main()
        {
            System.AppDomain.CurrentDomain.AssemblyResolve += CustomResolve;
            App.Run();
        }

        private static System.Reflection.Assembly CustomResolve(
            object sender,
            System.ResolveEventArgs args)
        {
            if (args.Name.StartsWith("library"))
            {
                string fileName = System.IO.Path.GetFullPath(
                    "platform\\"
                    + System.Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")
                    + "\\library.dll");
                System.Console.WriteLine(fileName);
                if (System.IO.File.Exists(fileName))
                {
                    return System.Reflection.Assembly.LoadFile(fileName);
                }
            }
            return null;
        }
    }
}

"Program"은 응용 프로그램의 "실제" 구현입니다(App.Run은 Bootstrapper.Main 끝에서 호출되었습니다).

// file 'program.cs' in C:\TEMP\CrossPlatformTest
namespace Cross.Platform.Program
{
    public static class App
    {
        public static void Run()
        {
            Cross.Platform.Library.Worker.Run();
        }
    }
}

4단계:명령줄에서 기본 애플리케이션을 컴파일합니다.

(cmd.exe from Note 2)
csc /reference:platform\x86\library.dll /out:program.exe program.cs bootstrapper.cs

5단계:이제 끝났습니다.우리가 만든 디렉터리의 구조는 다음과 같아야 합니다.

(C:\TEMP\CrossPlatformTest, root dir)
    platform (dir)
        amd64 (dir)
            library.dll
        x86 (dir)
            library.dll
    program.exe
    *.cs (source files)

이제 32비트 플랫폼에서 program.exe를 실행하면 platform\x86\library.dll이 로드됩니다.64비트 플랫폼에서 program.exe를 실행하면 platform\amd64\library.dll이 로드됩니다.작업 관리자/프로세스 탐색기를 사용하여 로드된 DLL을 조사하거나 Visual Studio/Windows 디버거를 사용하여 프로세스에 연결하여 확인할 수 있도록 Worker.Run 메서드 끝에 Console.ReadLine()을 추가했습니다. 콜스택 등

program.exe가 실행되면 사용자 지정 어셈블리 확인자가 현재 appdomain에 연결됩니다..NET은 Program 클래스 로드를 시작하자마자 '라이브러리' 어셈블리에 대한 종속성을 확인하여 로드를 시도합니다.그러나 그러한 어셈블리는 발견되지 않습니다(platform/* 하위 디렉터리에 숨겨졌기 때문입니다).운 좋게도 사용자 지정 확인자는 우리의 속임수를 알고 있으며 현재 플랫폼을 기반으로 적절한 platform/* 하위 디렉터리에서 어셈블리를 로드하려고 시도합니다.

다른 팁

내 버전은 @Milan과 유사하지만 몇 가지 중요한 변경 사항이 있습니다.

  • 발견되지 않은 모든 DLL에 대해 작동합니다.
  • 켜고 끌 수 있습니다
  • AppDomain.CurrentDomain.SetupInformation.ApplicationBase 대신에 사용됩니다 Path.GetFullPath() 현재 디렉토리가 다를 수 있기 때문입니다.호스팅 시나리오에서 Excel은 플러그인을 로드할 수 있지만 현재 디렉터리는 DLL로 설정되지 않습니다.

  • Environment.Is64BitProcess 대신에 사용됩니다 PROCESSOR_ARCHITECTURE, OS가 무엇인지보다는 이 프로세스가 어떻게 시작되었는지에 의존해서는 안 되므로 x64 OS의 x86 프로세스일 수도 있습니다..NET 4 이전에는 다음을 사용하십시오. IntPtr.Size == 8 대신에.

다른 모든 것보다 먼저 로드되는 일부 메인 클래스의 정적 생성자에서 이 코드를 호출하세요.

public static class MultiplatformDllLoader
{
    private static bool _isEnabled;

    public static bool Enable
    {
        get { return _isEnabled; }
        set
        {
            lock (typeof (MultiplatformDllLoader))
            {
                if (_isEnabled != value)
                {
                    if (value)
                        AppDomain.CurrentDomain.AssemblyResolve += Resolver;
                    else
                        AppDomain.CurrentDomain.AssemblyResolve -= Resolver;
                    _isEnabled = value;
                }
            }
        }
    }

    /// Will attempt to load missing assembly from either x86 or x64 subdir
    private static Assembly Resolver(object sender, ResolveEventArgs args)
    {
        string assemblyName = args.Name.Split(new[] {','}, 2)[0] + ".dll";
        string archSpecificPath = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
                                               Environment.Is64BitProcess ? "x64" : "x86",
                                               assemblyName);

        return File.Exists(archSpecificPath)
                   ? Assembly.LoadFile(archSpecificPath)
                   : null;
    }
}

SetDllDirectory를 살펴보십시오.저는 x64와 x86 모두에 대해 IBM spss 어셈블리를 동적으로 로드하는 데 이를 사용했습니다.또한 제 경우에는 spss dll의 경우 어셈블리에 의해 로드된 비 어셈블리 지원 dll에 대한 경로를 해결했습니다.

http://msdn.microsoft.com/en-us/library/ms686203%28VS.85%29.aspx

당신은 사용할 수 있습니다 코르플래그 AnyCPU exe를 x86 또는 x64 실행 파일로 강제로 로드하는 유틸리티이지만 대상에 따라 복사할 exe를 선택하지 않으면 파일 복사 배포 요구 사항을 완전히 충족하지 못합니다.

이 솔루션은 관리되지 않는 어셈블리에도 작동할 수 있습니다.나는 Milan Gardian의 훌륭한 예와 유사한 간단한 예를 만들었습니다.제가 만든 예제에서는 Managed C++ dll을 Any CPU 플랫폼용으로 컴파일된 C# dll에 동적으로 로드합니다.솔루션은 어셈블리의 종속성이 로드되기 전에 InjectModuleInitializer nuget 패키지를 사용하여 AssemblyResolve 이벤트를 구독합니다.

https://github.com/kevin-marshall/Managed.AnyCPU.git

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top