将一个 dll 作为嵌入式资源嵌入到另一个 dll 中,然后从我的代码中调用它
Posted
技术标签:
【中文标题】将一个 dll 作为嵌入式资源嵌入到另一个 dll 中,然后从我的代码中调用它【英文标题】:Embedding one dll inside another as an embedded resource and then calling it from my code 【发布时间】:2010-09-10 22:29:01 【问题描述】:我有一个我正在创建的 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 数据\组装\dl3\KXPPAX6Y.ZCY\A1MZ1499.1TR\e0115d44\91bb86eb_fe18c901
),但是当我找到需要此 DLL 的代码部分时,它找不到它。
有人知道我需要做什么不同的事情吗?
【问题讨论】:
【参考方案1】:有一个名为 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
【讨论】:
【参考方案2】:您可以尝试使用 Assembly.Load(byte[] rawAssembly) 从嵌入式资源创建 rawAssembly,而不是将程序集写入磁盘。
【讨论】:
【参考方案3】:您可以使用Netz(一个 .net NET 可执行文件压缩器和打包器)轻松实现这一目标。
【讨论】:
【参考方案4】:我已经成功完成了您所描述的操作,但是因为第三方 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】:将第三方程序集作为资源嵌入后,添加代码以在应用程序启动期间订阅当前域的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 可能是另一个值得考虑的选择,尽管涉及更多。
【讨论】:
在发布 @dgvid 在响应时间上击败了我之后才意识到。 :P 我非常成功地使用了这段代码来完成我想要的。请参阅我的帖子,了解我修复的几个小的语法遗漏(没有足够的代表来编辑这个;))。 GetManifestResourceStream?该程序集将是 *.Properties.Resources 命名空间下的强类型属性。 等等,这是否仍然需要对 exe 进行“控制”,正如Richter's idea(似乎“偷了”这个答案)所暗示的那样必须发生?search for the comment starting with 28 Jul 2010 3:14 PM at that link
我在上面的答案中提示“在应用程序启动期间” - 当您仅分发 dll 并且无权访问 exe 时,这不起作用/不起作用?
之前没用过Assembly类的,需要有using System.Reflection;
声明。我花了一点时间才弄清楚缺少哪个 using 语句,所以也许这会对某人有所帮助。【参考方案6】:
最后,我几乎完全按照 raboof 建议的方式进行了操作(并且类似于 dgvid 的建议),除了一些小的改动和一些遗漏修复。我选择这种方法是因为它最接近我最初寻找的方法,并且不需要使用任何第三方可执行文件等。效果很好!
这就是我的代码最终的样子:
编辑:我决定将此函数移动到另一个程序集,以便我可以在多个文件中重用它(我只是传入 Assembly.GetExecutingAssembly())。
这是更新版本,允许您通过嵌入的 dll 传递程序集。
embeddedResourcePrefix 是嵌入资源的字符串路径,它通常是程序集的名称,后跟包含资源的任何文件夹结构(例如,如果 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();
【讨论】:
感谢您发布最终代码,我可能最终会在某个地方使用它!以上是关于将一个 dll 作为嵌入式资源嵌入到另一个 dll 中,然后从我的代码中调用它的主要内容,如果未能解决你的问题,请参考以下文章