关于c#:将一个dll嵌入另一个中作为嵌入式资源,然后从我的代码中调用它

关于c#:将一个dll嵌入另一个中作为嵌入式资源,然后从我的代码中调用它

Embedding one dll inside another as an embedded resource and then calling it from my code

我遇到的情况是,我正在创建一个使用另一个第三方DLL的DLL,但我希望能够将第三方DLL构建到我的DLL中,而不是尽可能将它们保持在一起。

这是C#和.NET 3.5。

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

我最初计划执行此操作的方法是编写代码以将第三方DLL放置在System.Reflection.Assembly.GetExecutingAssembly().Location.ToString()指定的位置
减去最后一个/nameOfMyAssembly.dll。 我可以在此位置成功保存第三方.DLL(最终被保存为

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#中的样子:

1
2
3
4
5
6
7
8
9
10
11
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可以定义为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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的程序集。

EmbeddedResourcePrefix是嵌入式资源的字符串路径,通常是程序集的名称,后跟包含该资源的任何文件夹结构(例如,如果dll位于项目中名为Resources的文件夹中,则为" MyComapny.MyProduct.MyAssembly.Resources"。 )。它还假定该dll具有.dll.resource扩展名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
   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

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

设置路径=" 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


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

我将嵌入式资源汇编作为字节数组获得,如下所示:

1
2
3
4
5
6
7
8
        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。


您可以使用.net NET可执行文件压缩器和打包器Netz轻松实现这一目标。


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


推荐阅读

    linux调用函数的命令?

    linux调用函数的命令?,系统,代码,策略,上调,时间,设计,通信,网络,设备,项目,

    linux外部命令调用?

    linux外部命令调用?,系统,软件,标准,命令,盘中,管理,外部,进程,程序,内存,lin

    linux怎么调用命令行?

    linux怎么调用命令行?,系统,地址,工具,工作,首页,终端,命令,密码,信息,情况,l

    嵌入式linux网络命令?

    嵌入式linux网络命令?,系统,基础,网络,设备,平台,培训,环境,服务,项目,嵌入

    浏览器调用linux命令?

    浏览器调用linux命令?,系统,信息,人工智能,软件,数据,首次,地址,代码,咨询,

    py调用linux的命令?

    py调用linux的命令?,系统,代码,状态,环境,标准,工具,命令,文件,脚本,终端,lin

    浏览器调用linux命令?

    浏览器调用linux命令?,系统,信息,人工智能,软件,数据,首次,地址,代码,咨询,

    py调用linux的命令?

    py调用linux的命令?,系统,代码,状态,环境,标准,工具,命令,文件,脚本,终端,lin

    linux系统命令调用?

    linux系统命令调用?,系统,单位,工具,工作,管理,地址,权威,密码,电脑,信息,怎

    linux调用上一条命令?

    linux调用上一条命令?,系统,命令,一致,数字,名称,网上,电脑,目录,空格,终端,l

    linux命令行调用程序?

    linux命令行调用程序?,工具,环境,代码,初级,工程,系统,网上,服务,管理,发行,l

    脚本调用linux命令?

    脚本调用linux命令?,代码,系统,工作,底部,脚本,位置,环境,行用,官网,标准,typ

    调用函数命令linux?

    调用函数命令linux?,系统,管理,网络,通用,统一,观察,地址,代码,设备,地方,怎

    linux嵌入式截图命令?

    linux嵌入式截图命令?,系统,环境,软件,网络,支柱,代码,工具,标准,发展,工作,l

    linux内核总调用命令?

    linux内核总调用命令?,工作,地址,系统,信息,管理,策略,命令,目录,时间,基础,

    linux编程调用命令?

    linux编程调用命令?,系统,标准,管理,工作,基础知识,情况,环境,设备,基础,首

    嵌入式的linux命令?

    嵌入式的linux命令?,系统,工作,地址,软件,信息,基础,命令,嵌入式,电脑,环境,l

    linux命令窗口调用?

    linux命令窗口调用?,系统,工具,首页,终端,命令,数据,盘中,代码,密码,快捷键,

    linux服务器调用命令?

    linux服务器调用命令?,系统,地址,设备,标准,工作,密码,中心,盘中,网络,软件,

    linuxc调用命令框?

    linuxc调用命令框?,系统,工作,标准,情况,设备,环境,命令,函数,语言,程序,如何