Como uma dll do Windows C++ pode ser mesclada em um exe de aplicativo C#?
Pergunta
Eu tenho um programa Windows C# que usa uma dll C++ para E/S de dados.Meu objetivo é implantar o aplicativo como um único EXE.
Quais são as etapas para criar esse executável?
Solução
MOMAÇÃO ÚNICA DESPECIONAÇÃO DO Código Gerenciado e não gerenciado no domingo, 4 de fevereiro de 2007
Os desenvolvedores .NET adoram a implantação do XCOPY.E eles adoram componentes de montagem única.Pelo menos sempre me sinto meio desconfortável, se preciso usar algum componente e preciso lembrar de uma lista de arquivos para incluir também na montagem principal daquele componente.Então, quando recentemente tive que desenvolver um componente de código gerenciado e aumentá-lo com algum código não gerenciado de uma DLL C (obrigado a Marcus Heege por me ajudar com isso!), pensei em como tornar mais fácil a implantação das duas DLLs .Se fossem apenas dois assemblies, eu poderia ter usado o ILmerge para agrupá-los em apenas um arquivo.Mas isso não funciona para componentes de código misto com DLLs gerenciadas e não gerenciadas.
Então aqui está o que eu encontrei para uma solução:
Incluo todas as DLLs que desejo implantar no assembly principal do meu componente como recursos incorporados.Então configurei um construtor de classe para extrair essas DLLs como abaixo.A classe ctor é chamada apenas uma vez em cada AppDomain, então é uma sobrecarga insignificante, eu acho.
namespace MyLib
{
public class MyClass
{
static MyClass()
{
ResourceExtractor.ExtractResourceToFile("MyLib.ManagedService.dll", "managedservice.dll");
ResourceExtractor.ExtractResourceToFile("MyLib.UnmanagedService.dll", "unmanagedservice.dll");
}
...
Neste exemplo incluí duas DLLs como recursos, uma sendo uma DLL de código não gerenciado e outra sendo uma DLL de código gerenciado (apenas para fins de demonstração), para mostrar como essa técnica funciona para ambos os tipos de código.
O código para extrair as DLLs em arquivos próprios é simples:
public static class ResourceExtractor
{
public static void ExtractResourceToFile(string resourceName, string filename)
{
if (!System.IO.File.Exists(filename))
using (System.IO.Stream s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
using (System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Create))
{
byte[] b = new byte[s.Length];
s.Read(b, 0, b.Length);
fs.Write(b, 0, b.Length);
}
}
}
Trabalhar com um assembly de código gerenciado como esse é quase o mesmo de sempre.Você faz referência a ele (aqui:ManagedService.dll) no projeto principal do seu componente (aqui:MyLib), mas defina a propriedade Copy Local como false.Além disso, você vincula a montagem como um item existente e define a ação de construção como recurso incorporado.
Para o código não gerenciado (aqui:UnknownService.dll), basta vincular a DLL como um item existente e definir a ação de construção como recurso incorporado.Para acessar suas funções use o atributo DllImport normalmente, por exemplo.
[DllImport("unmanagedservice.dll")] public extern static int Add(int a, int b);
É isso!Assim que você cria a primeira instância da classe com o ctor estático, as DLLs incorporadas são extraídas em seus próprios arquivos e estão prontas para uso como se você as tivesse implantado como arquivos separados.Contanto que você tenha permissões de gravação para o diretório de execução, isso funcionará bem para você.Pelo menos para código prototípico, acho que essa forma de implantação de assembly único é bastante conveniente.
Aproveitar!
Outras dicas
Tentar aplicativo em caixa;permite carregar todas as DLLs da memória.Além disso, parece que você pode até incorporar o tempo de execução .net.É bom criar aplicativos realmente independentes ...
Usar Fody.Costura pepita
- Abra sua solução -> Projeto -> Gerenciar pacotes Nuget
- Procurar Fody.Costura
- Compilar seu projeto.
É isso !
Fonte: http://www.manuelmeyer.net/2016/01/net-power-tip-10-merging-assemblies/
Você já experimentou o ILMerge? http://research.microsoft.com/~mbarnett/ILMerge.aspx
ILMerge é um utilitário que pode ser usado para mesclar vários assemblies .NET em um único assembly.Ele está disponível gratuitamente para uso na página Ferramentas e utilitários do Microsoft .NET Framework Developer Center.
Se você estiver construindo a DLL C++ com o /clr
flag (todo ou parcialmente C++/CLI), então deve funcionar:
ilmerge /out:Composite.exe MyMainApp.exe Utility.dll
No entanto, não funcionará com uma DLL comum (nativa) do Windows.
Basta clicar com o botão direito do mouse no seu projeto no Visual Studio, escolha Propriedades do projeto -> Recursos -> Adicionar recurso -> Adicionar arquivo existente ... e inclua o código abaixo no seu app.xaml.cs ou equivalente.
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);
}
Aqui está minha postagem original no blog:http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/
Instalar é uma solução.Para um aplicativo nativo do Windows, sugiro incorporar a DLL como um objeto de recurso binário e, em seguida, extraí-la em tempo de execução antes de precisar dela.
Montagem Inteligente pode fazer isso e muito mais.Se sua dll tiver código não gerenciado, ela não permitirá mesclar as dlls em um único assembly; em vez disso, poderá incorporar as dependências necessárias como recursos em seu exe principal.O outro lado, não é de graça.
Você pode fazer isso manualmente incorporando dll aos seus recursos e depois contando com o Assembly do AppDomain ResolveHandler
.Quando se trata de dlls de modo misto, encontrei muitas das variantes e sabores de ResolveHandler
abordagem para não funcionar para mim (todos os quais leem bytes dll na memória e leem a partir dele).Todos eles funcionaram para DLLs gerenciadas.Aqui está o que funcionou para mim:
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);
};
}
A chave aqui é gravar os bytes em um arquivo e carregá-los de seu local.Para evitar o problema do ovo e da galinha, você deve garantir que declarou o manipulador antes de acessar a montagem e que não acessa os membros da montagem (ou instancia qualquer coisa que tenha que lidar com a montagem) dentro da peça de carregamento (resolução da montagem).Também tome cuidado para garantir GetMyApplicationSpecificPath()
não é nenhum diretório temporário, pois os arquivos temporários podem ser tentados para serem apagados por outros programas ou por você mesmo (não que ele seja excluído enquanto o programa estiver acessando a dll, mas pelo menos é um incômodo.AppData é uma boa localização).Observe também que você tem que escrever os bytes toda vez, você não pode carregar do local apenas porque a dll já reside lá.
Se a montagem for totalmente não gerenciada, você poderá ver isto link ou esse sobre como carregar essas DLLs.
PostBuild de Xenocódigo pode empacotar gerenciados e não gerenciados em um único exe.