我有一个情况,我有一个我正在创建使用另一个第三方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#中的样子:
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传递程序集.
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(); } }
有一个名为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 \发布\ LevelLibrary.dll
我已经成功地完成了你所描述的内容,但由于第三方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.
使用Netz,一个.net NET可执行文件Compressor和Packer,您可以非常轻松地实现这一目标.