C# 将一个 dll 作为嵌入资源嵌入到另一个 dll 中,然后从我的代码中调用它
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/96732/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Embedding one dll inside another as an embedded resource and then calling it from my code
提问by Lawrence Johnston
I've got a situation where I have a DLL I'm creating that uses another third party DLL, but I would prefer to be able to build the third party DLL into my DLL instead of having to keep them both together if possible.
我遇到了一种情况,我正在创建一个使用另一个第三方 DLL 的 DLL,但我希望能够将第三方 DLL 构建到我的 DLL 中,而不是尽可能将它们保持在一起。
This with is C# and .NET 3.5.
这是 C# 和 .NET 3.5。
The way I would like to do this is by storing the third party DLL as an embedded resource which I then place in the appropriate place during execution of the first DLL.
我想这样做的方法是将第三方 DLL 存储为嵌入式资源,然后在执行第一个 DLL 期间将其放置在适当的位置。
The way I originally planned to do this is by writing code to put the third party DLL in the location specified by System.Reflection.Assembly.GetExecutingAssembly().Location.ToString()
minus the last /nameOfMyAssembly.dll
. I can successfully save the third party .DLL
in this location (which ends up being
我最初计划这样做的方法是编写代码将第三方 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
C:\Documents and Settings\myUserName\Local Settings\Application Data\assembly\dl3\KXPPAX6Y.ZCY\A1MZ1499.1TR\e0115d44\91bb86eb_fe18c901
), but when I get to the part of my code requiring this DLL, it can't find it.
),但是当我到达需要此 DLL 的代码部分时,它找不到它。
Does anybody have any idea as to what I need to be doing differently?
有没有人知道我需要以不同的方式做些什么?
采纳答案by Atif Aziz
Once you've embedded the third-party assembly as a resource, add code to subscribe to the AppDomain.AssemblyResolve
event of the current domain during application start-up. This event fires whenever the Fusion sub-system of the CLR fails to locate an assembly according to the probing (policies) in effect. In the event handler for AppDomain.AssemblyResolve
, load the resource using Assembly.GetManifestResourceStream
and feed its content as a byte array into the corresponding Assembly.Load
overload. Below is how one such implementation could look like in C#:
将第三方程序集作为资源嵌入后,添加代码以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;
}
};
where StreamToBytes
could be defined as:
其中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();
}
}
Finally, as a few have already mentioned, ILMergemay be another option to consider, albeit somewhat more involved.
最后,正如一些人已经提到的,ILMerge可能是另一个需要考虑的选择,尽管涉及更多。
回答by Fostah
There's a tool called IlMerge that can accomplish this: http://research.microsoft.com/~mbarnett/ILMerge.aspx
有一个名为 IlMerge 的工具可以完成此操作:http://research.microsoft.com/~mbarnett/ILMerge.aspx
Then you can just make a build event similar to the following.
然后,您可以创建类似于以下内容的构建事件。
Set Path="C:\Program Files\Microsoft\ILMerge"
设置路径="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
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
回答by Hallgrim
Instead of writing the assembly to disk you can try to do Assembly.Load(byte[] rawAssembly) where you create rawAssembly from the embedded resource.
您可以尝试执行 Assembly.Load(byte[] rawAssembly),而不是将程序集写入磁盘,从嵌入的资源创建 rawAssembly。
回答by Mark Smith
回答by dgvid
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.
我已经成功地完成了您所描述的工作,但是由于第三方 DLL 也是一个 .NET 程序集,我从不将其写出到磁盘,我只是从内存中加载它。
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().
然后我用 Assembly.Load() 加载数据。
Finally, I add a handler to AppDomain.CurrentDomain.AssemblyResolve to return my loaded assembly when the type loader looks it.
最后,我向 AppDomain.CurrentDomain.AssemblyResolve 添加了一个处理程序,以便在类型加载器查找它时返回我加载的程序集。
See the .NET Fusion Workshopfor additional details.
有关其他详细信息,请参阅.NET Fusion Workshop。
回答by Lawrence Johnston
In the end I did it almost exactly the way raboof suggested (and similar to what dgvid suggested), except with some minor changes and some omissions fixed. I chose this method because it was closest to what I was looking for in the first place and didn't require using any third party executables and such. It works great!
最后,我几乎完全按照 raboof 建议的方式(与 dgvid 建议的类似)做了,除了一些小的改动和一些遗漏修复。我选择这种方法是因为它最接近我最初寻找的方法,并且不需要使用任何第三方可执行文件等。它工作得很好!
This is what my code ended up looking like:
这就是我的代码最终的样子:
EDIT: I decided to move this function to another assembly so I could reuse it in multiple files (I just pass in Assembly.GetExecutingAssembly()).
编辑:我决定将此函数移动到另一个程序集,以便我可以在多个文件中重用它(我只是传入 Assembly.GetExecutingAssembly())。
This is the updated version which allows you to pass in the assembly with the embedded dlls.
这是更新的版本,它允许您传入带有嵌入式 dll 的程序集。
embeddedResourcePrefix is the string path to the embedded resource, it will usually be the name of the assembly followed by any folder structure containing the resource (e.g. "MyComapny.MyProduct.MyAssembly.Resources" if the dll is in a folder called Resources in the project). It also assumes that the dll has a .dll.resource extension.
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();
}
}