Mat*_*ias 726
我强烈建议使用Costura.Fody - 迄今为止在装配中嵌入资源的最佳和最简单的方法.它可以作为NuGet包使用.
Install-Package Costura.Fody
Run Code Online (Sandbox Code Playgroud)
将其添加到项目后,它会自动将复制到输出目录的所有引用嵌入到主程序集中.您可能希望通过向项目添加目标来清理嵌入的文件:
Install-CleanReferencesTarget
Run Code Online (Sandbox Code Playgroud)
您还可以指定是否包含pdb,排除某些程序集或动态提取程序集.据我所知,还支持非托管程序集.
更新
目前,有些人正在尝试添加对DNX的支持.
Sho*_*og9 85
如果它们实际上是托管程序集,则可以使用ILMerge.对于本机DLL,您还需要做更多工作.
另请参阅: 如何将C++窗口DLL合并到C#应用程序exe中?
Lar*_*sen 80
只需在Visual Studio中右键单击您的项目,选择Project Properties - > Resources - > Add Resource - > Add Existing File ...并将以下代码包含在App.xaml.cs或同等代码中.
public App()
{
AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
return System.Reflection.Assembly.Load(bytes);
}
Run Code Online (Sandbox Code Playgroud)
这是我原来的博文:http: //codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/
Bob*_*bby 26
是的,可以将.NET可执行文件与库合并.有多种工具可以完成工作:
此外,它可以与Mono Linker结合使用,它可以删除未使用的代码,从而使得到的组件更小.
另一种可能性是使用.NETZ,它不仅可以压缩程序集,还可以将dll直接打包到exe中.与上述解决方案的不同之处在于.NETZ不会合并它们,它们保持独立的程序集,但被打包到一个程序包中.
.NETZ是一个开源工具,可压缩和打包Microsoft .NET Framework可执行文件(EXE,DLL)文件,以使其更小.
naw*_*fal 20
如果程序集只有托管代码,ILMerge可以将程序集组合到一个程序集中.您可以使用命令行应用程序,或添加对exe的引用并以编程方式合并.对于GUI版本,有Eazfuscator,以及.Netz这两个都是免费的.付费应用包括BoxedApp和SmartAssembly.
如果必须使用非托管代码合并程序集,我建议使用SmartAssembly.我从来没有与SmartAssembly打嗝,但与其他所有人打嗝.在这里,它可以将所需的依赖项作为资源嵌入到主exe中.
您可以手动完成所有这些操作,无需担心如果管理程序集或处于混合模式,方法是将dll嵌入到资源中,然后依赖AppDomain的程序集ResolveHandler
.这是采用最坏情况的一站式解决方案,即具有非托管代码的程序集.
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string assemblyName = new AssemblyName(args.Name).Name;
if (assemblyName.EndsWith(".resources"))
return null;
string dllName = assemblyName + ".dll";
string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
//or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);
File.WriteAllBytes(dllFullPath, data);
}
return Assembly.LoadFrom(dllFullPath);
};
}
Run Code Online (Sandbox Code Playgroud)
这里的关键是将字节写入文件并从其位置加载.为了避免鸡和蛋问题,您必须确保在访问程序集之前声明处理程序,并且不要在加载(程序集解析)部分内访问程序集成员(或实例化必须处理程序集的任何内容).另外要注意确保GetMyApplicationSpecificPath()
不是任何临时目录,因为临时文件可能会被其他程序或自己删除(不是在程序访问dll时它会被删除,但至少它是一个麻烦.AppData是好的地点).另请注意,每次都必须写入字节,不能从位置加载'因为dll已经驻留在那里.
对于托管dll,您不需要写入字节,而是直接从dll的位置加载,或者只读取字节并从内存加载程序集.像这样左右:
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
return Assembly.Load(data);
}
//or just
return Assembly.LoadFrom(dllFullPath); //if location is known.
Run Code Online (Sandbox Code Playgroud)
如果组件是完全不受管理的,你可以看到这个链接或此为如何加载的DLL等.
Ste*_*eve 14
在通过杰弗里里希特摘录非常好.简而言之,将库添加为嵌入式资源,并在其他任何内容之前添加回调.这是我在控制台应用程序的Main方法的开头放置的代码版本(在他的页面的注释中找到)(只是确保使用库的任何调用与Main的方法不同).
AppDomain.CurrentDomain.AssemblyResolve += (sender, bargs) =>
{
String dllName = new AssemblyName(bargs.Name).Name + ".dll";
var assem = Assembly.GetExecutingAssembly();
String resourceName = assem.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
if (resourceName == null) return null; // Not found, maybe another handler will find it
using (var stream = assem.GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
Run Code Online (Sandbox Code Playgroud)
Jos*_*osh 14
要扩展@Bobby的asnwer以上.您可以编辑.csproj以使用IL-Repack在构建时自动将所有文件打包到单个程序集中.
Install-Package ILRepack.MSBuild.Task
这是一个将ExampleAssemblyToMerge.dll合并到项目输出中的简单示例.
<!-- ILRepack -->
<Target Name="AfterBuild" Condition="'$(Configuration)' == 'Release'">
<ItemGroup>
<InputAssemblies Include="$(OutputPath)\$(AssemblyName).exe" />
<InputAssemblies Include="$(OutputPath)\ExampleAssemblyToMerge.dll" />
</ItemGroup>
<ILRepack
Parallel="true"
Internalize="true"
InputAssemblies="@(InputAssemblies)"
TargetKind="Exe"
OutputFile="$(OutputPath)\$(AssemblyName).exe"
/>
</Target>
Run Code Online (Sandbox Code Playgroud)
该功能是通过在项目文件 (.csproj) 中使用以下属性启用的:
<PropertyGroup>
<PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>
Run Code Online (Sandbox Code Playgroud)
这是在没有任何外部工具的情况下完成的。
有关更多详细信息,请参阅我对此问题的回答。
您可以将DLL添加为嵌入式资源,然后让程序在启动时将它们解压缩到应用程序目录中(在检查它们是否已存在之后).
但是,安装文件很容易制作,我认为这不值得.
编辑:使用.NET程序集这种技术很容易.使用非.NET DLL会有更多的工作(你必须找出解压缩文件的位置并注册它们等等).
另一款可以优雅处理这种产品的产品是SmartAssembly,SmartAssembly.com.除了将所有依赖项合并到单个DLL中之外,该产品还可以(可选地)对代码进行模糊处理,删除额外的元数据以减少生成的文件大小,还可以实际优化IL以提高运行时性能.它还为您的软件添加了某种全局异常处理/报告功能(如果需要),我没有花时间去理解,但可能很有用.我相信它还有一个命令行API,因此您可以将其作为构建过程的一部分.
ILMerge方法和Lars Holm Jensen处理AssemblyResolve事件都不适用于插件主机.假设可执行文件H动态加载程序集P并通过单独程序集中定义的接口IP访问它.要将IP嵌入H,需要对Lars的代码进行一些修改:
Dictionary<string, Assembly> loaded = new Dictionary<string,Assembly>();
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{ Assembly resAssembly;
string dllName = args.Name.Contains(",") ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if ( !loaded.ContainsKey( dllName ) )
{ if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
resAssembly = System.Reflection.Assembly.Load(bytes);
loaded.Add(dllName, resAssembly);
}
else
{ resAssembly = loaded[dllName]; }
return resAssembly;
};
Run Code Online (Sandbox Code Playgroud)
处理重复尝试解决同一个程序集并返回现有程序集而不是创建新实例的技巧.
编辑: 为了避免破坏.NET的序列化,请确保为未嵌入您的所有程序集返回null,从而默认为标准行为.您可以通过以下方式获取这些库的列表:
static HashSet<string> IncludedAssemblies = new HashSet<string>();
string[] resources = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
for(int i = 0; i < resources.Length; i++)
{ IncludedAssemblies.Add(resources[i]); }
Run Code Online (Sandbox Code Playgroud)
如果传递的程序集不属于,则返回null IncludedAssemblies
.
以下方法请勿使用外部工具并自动包含所有需要的 DLL(无需手动操作,一切都在编译时完成)
我在这里阅读了很多答案,说要使用ILMerge、ILRepack或Jeffrey Ritcher方法,但这些方法都不适用于WPF 应用程序,也不易于使用。
当您有很多 DLL 时,可能很难手动将您需要的 DLL 包含在您的 exe 中。我发现的最好方法是由 Wegged 解释的在 StackOverflow 上
为清楚起见,将他的答案复制粘贴到此处(所有功劳均归功于Wegged)
.csproj
文件中:<Target Name="AfterResolveReferences">
<ItemGroup>
<EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
<LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Target>
Run Code Online (Sandbox Code Playgroud)
Program.cs
看起来像这样:[STAThreadAttribute]
public static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
App.Main();
}
Run Code Online (Sandbox Code Playgroud)
OnResolveAssembly
方法:private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
AssemblyName assemblyName = new AssemblyName(args.Name);
var path = assemblyName.Name + ".dll";
if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false) path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
using (Stream stream = executingAssembly.GetManifestResourceStream(path))
{
if (stream == null) return null;
var assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
353475 次 |
最近记录: |