Development Tip

하나의 dll을 다른 내부에 포함 된 리소스로 포함하고 내 코드에서 호출

yourdevel 2020. 12. 30. 19:44
반응형

하나의 dll을 다른 내부에 포함 된 리소스로 포함하고 내 코드에서 호출


다른 타사 DLL을 사용하는 DLL이 있지만 가능한 경우 둘 다 함께 유지하는 대신 타사 DLL을 DLL로 빌드 할 수있는 상황이 있습니다.

이것은 C # 및 .NET 3.5입니다.

이 작업을 수행하는 방법은 타사 DLL을 포함 된 리소스로 저장 한 다음 첫 번째 DLL을 실행하는 동안 적절한 위치에 배치하는 것입니다.

처음에이 작업을 계획 한 방법 System.Reflection.Assembly.GetExecutingAssembly().Location.ToString()은 마지막 .NET Framework에서 지정한 위치에 타사 DLL을 배치하는 코드를 작성하는 /nameOfMyAssembly.dll입니다. .DLL이 위치에 제 3자를 성공적으로 저장할 수 있습니다.

C : \ Documents and Settings \ myUserName \ Local Settings \ Application Data \ assembly \ dl3 \ KXPPAX6Y.ZCY \ A1MZ1499.1TR \ e0115d44 \ 91bb86eb_fe18c901

), 그러나이 DLL이 필요한 코드 부분에 도달하면 찾을 수 없습니다.

내가 다르게해야 할 일에 대해 아는 사람이 있습니까?


타사 어셈블리를 리소스로 포함했으면 AppDomain.AssemblyResolve응용 프로그램 시작 중에 현재 도메인 이벤트 를 구독하는 코드를 추가 합니다. 이 이벤트는 CLR의 Fusion 하위 시스템이 유효한 프로빙 (정책)에 따라 어셈블리를 찾지 못할 때마다 발생합니다. 에 대한 이벤트 처리기에서 AppDomain.AssemblyResolve를 사용하여 리소스를로드하고 Assembly.GetManifestResourceStream해당 콘텐츠를 해당 Assembly.Load오버로드에 바이트 배열로 공급합니다 . 다음은 이러한 구현이 C #에서 어떻게 보일 수 있는지입니다.

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
    var resName = args.Name + ".dll";    
    var thisAssembly = Assembly.GetExecutingAssembly();    
    using (var input = thisAssembly.GetManifestResourceStream(resName))
    {
        return input != null 
             ? Assembly.Load(StreamToBytes(input))
             : null;
    }
};

곳은 StreamToBytes로 정의 할 수 있습니다 :

static byte[] StreamToBytes(Stream input) 
{
    var capacity = input.CanSeek ? (int) input.Length : 0;
    using (var output = new MemoryStream(capacity))
    {
        int readLength;
        var buffer = new byte[4096];

        do
        {
            readLength = input.Read(buffer, 0, buffer.Length);
            output.Write(buffer, 0, readLength);
        }
        while (readLength != 0);

        return output.ToArray();
    }
}

마지막으로, 이미 언급했듯이 ILMerge 는 다소 복잡하지만 고려할 수있는 또 다른 옵션이 될 수 있습니다.


결국 나는 raboof가 제안한 방식 (그리고 dgvid가 제안한 것과 유사 함)을 거의 정확하게했지만 약간의 변경과 일부 누락이 수정되었습니다. 이 방법은 처음에 내가 찾고 있던 것과 가장 가깝고 타사 실행 파일 등을 사용할 필요가 없었기 때문에 선택했습니다. 잘 작동합니다!

내 코드는 다음과 같습니다.

편집 : 여러 파일에서 재사용 할 수 있도록이 함수를 다른 어셈블리로 이동하기로 결정했습니다 (Assembly.GetExecutingAssembly ()를 전달합니다).

이것은 임베디드 dll이있는 어셈블리를 전달할 수있는 업데이트 된 버전입니다.

embedResourcePrefix는 포함 된 리소스에 대한 문자열 경로이며 일반적으로 리소스를 포함하는 폴더 구조가 뒤에 오는 어셈블리 이름입니다 (예 : dll이 프로젝트의 Resources라는 폴더에있는 경우 "MyComapny.MyProduct.MyAssembly.Resources"). ). 또한 dll에 .dll.resource 확장자가 있다고 가정합니다.

   public static void EnableDynamicLoadingForDlls(Assembly assemblyToLoadFrom, string embeddedResourcePrefix) {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { // had to add =>
            try {
                string resName = embeddedResourcePrefix + "." + args.Name.Split(',')[0] + ".dll.resource";
                using (Stream input = assemblyToLoadFrom.GetManifestResourceStream(resName)) {
                    return input != null
                         ? Assembly.Load(StreamToBytes(input))
                         : null;
                }
            } catch (Exception ex) {
                _log.Error("Error dynamically loading dll: " + args.Name, ex);
                return null;
            }
        }; // Had to add colon
    }

    private static byte[] StreamToBytes(Stream input) {
        int capacity = input.CanSeek ? (int)input.Length : 0;
        using (MemoryStream output = new MemoryStream(capacity)) {
            int readLength;
            byte[] buffer = new byte[4096];

            do {
                readLength = input.Read(buffer, 0, buffer.Length); // had to change to buffer.Length
                output.Write(buffer, 0, readLength);
            }
            while (readLength != 0);

            return output.ToArray();
        }
    }

이를 수행 할 수있는 IlMerge라는 도구가 있습니다. http://research.microsoft.com/~mbarnett/ILMerge.aspx

그런 다음 다음과 유사한 빌드 이벤트를 만들 수 있습니다.

Path = "C : \ Program Files \ Microsoft \ ILMerge"설정

ilmerge /out:$(ProjectDir)\Deploy\LevelEditor.exe $ (ProjectDir) \ bin \ Release \ release.exe $ (ProjectDir) \ bin \ Release \ InteractLib.dll $ (ProjectDir) \ bin \ Release \ SpriteLib.dll $ (ProjectDir) \ bin \ Release \ LevelLibrary.dll


I've had success doing what you are describing, but because the third-party DLL is also a .NET assembly, I never write it out to disk, I just load it from memory.

I get the embedded resource assembly as a byte array like so:

        Assembly resAssembly = Assembly.LoadFile(assemblyPathName);

        byte[] assemblyData;
        using (Stream stream = resAssembly.GetManifestResourceStream(resourceName))
        {
            assemblyData = ReadBytesFromStream(stream);
            stream.Close();
        }

Then I load the data with Assembly.Load().

Finally, I add a handler to AppDomain.CurrentDomain.AssemblyResolve to return my loaded assembly when the type loader looks it.

See the .NET Fusion Workshop for additional details.


You can achieve this remarkably easily using Netz, a .net NET Executables Compressor & Packer.


Instead of writing the assembly to disk you can try to do Assembly.Load(byte[] rawAssembly) where you create rawAssembly from the embedded resource.

ReferenceURL : https://stackoverflow.com/questions/96732/embedding-one-dll-inside-another-as-an-embedded-resource-and-then-calling-it-fro

반응형