代码之家  ›  专栏  ›  技术社区  ›  Redwood

将一个dll作为嵌入资源嵌入到另一个dll中,然后从我的代码调用它

  •  54
  • Redwood  · 技术社区  · 16 年前

    我有一种情况,我正在创建一个使用另一个第三方dll的dll,但我希望能够将第三方dll构建到我的dll中,而不必在可能的情况下将它们都保留在一起。

    这是C和.NET 3.5。

    我想这样做的方法是将第三方DLL存储为嵌入式资源,然后在执行第一个DLL时将其放置在适当的位置。

    我最初计划这样做的方法是编写代码,将第三方dll放在 System.Reflection.Assembly.GetExecutingAssembly().Location.ToString() 减去最后 /nameOfMyAssembly.dll . 我能成功拯救第三方 .DLL 在这个地方

    C:\文档和设置\我的用户名\本地设置\应用程序 数据\程序集\dl3\kxppax6y.zcy\a1mz1499.1tr\e0115d44\91bb86eb_fe18c901

    ,但是当我找到需要这个dll的代码部分时,它找不到它。

    有人知道我需要做什么不同吗?

    6 回复  |  直到 16 年前
        1
  •  42
  •   Atif Aziz    7 年前

    将第三方程序集嵌入为资源后,添加代码以订阅 AppDomain.AssemblyResolve 应用程序启动期间当前域的事件。每当clr的Fusion子系统未能根据有效的探测(策略)定位程序集时,就会触发此事件。在的事件处理程序中 应用程序域.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 可能是另一个需要考虑的选择,尽管有点复杂。

        2
  •  19
  •   Redwood    16 年前

    最后,我几乎完全按照拉博夫的建议做了这件事(和DGVID的建议类似),除了一些细微的变化和一些遗漏得到了修正。我之所以选择这个方法,是因为它最接近我最初所寻找的,并且不需要使用任何第三方可执行文件等。很好用!

    这就是我的代码最终的样子:

    编辑:我决定将此函数移动到另一个程序集,以便在多个文件中重用它(我只是传入assembly.getExecutingAssembly())。

    这是一个更新版本,允许您使用嵌入的DLL传入程序集。

    EmbeddedResourcePrefix是嵌入资源的字符串路径,它通常是程序集的名称,后跟包含该资源的任何文件夹结构(例如,“MyMapny.MyProduct.MyAssembly.Resources”,如果该dll位于项目中名为“资源”的文件夹中)。它还假定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();
            }
        }
    
        3
  •  13
  •   Fostah    16 年前

    有一个叫做ilmerge的工具可以做到这一点: http://research.microsoft.com/~mbarnett/ILMerge.aspx

    然后您可以创建类似于以下内容的构建事件。

    set path=“c:\program files\microsoft\ilmerge”

    ilmerge/out:$(projectdir)\deploy\levelditor.exe$(projectdir)\bin\release\release.exe$(projectdir)\bin\release\interactlib.dll$(projectdir)\bin\release\spritelib.dll$(projectdir)\bin\release\levellibrary.dll

        4
  •  9
  •   dgvid    16 年前

    我已经成功地完成了您所描述的工作,但是由于第三方DLL也是.NET程序集,所以我从未将其写入磁盘,而是从内存中加载它。

    我将嵌入式资源程序集作为字节数组,如下所示:

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

    然后我用assembly.load()加载数据。

    最后,我向appdomain.currentdomain.assemblyresolve添加一个处理程序,以便在类型加载器查找时返回已加载的程序集。

    .NET Fusion Workshop 更多细节。

        5
  •  8
  •   Mark Smith    16 年前

    您可以非常容易地使用 Netz ,一个.NET可执行文件压缩程序和打包程序。

        6
  •  2
  •   Hallgrim    16 年前

    您可以尝试执行assembly.load(byte[]rawasassembly),从嵌入资源创建rawasassembly,而不是将程序集写入磁盘。