Incorporando uma dll dentro de outro como um recurso incorporado e, em seguida, chamando-o do meu código

Eu tenho uma situação onde eu tenho uma DLL que estou criando que usa outra DLL de terceiros, mas eu preferiria ser capaz de construir a DLL de terceiros em minha DLL em vez de ter que manter os dois juntos, se possível.

Isso com C# e .NET 3.5.

A maneira que eu gostaria de fazer isso é armazenar a DLL de terceiros como um recurso incorporado que, em seguida, coloque no local apropriado durante a execução da primeira DLL.

A maneira que eu originalmente planejei fazer isso é escrevendo código para colocar a DLL de terceiros no local especificado por System.Reflection.Assembly.GetExecutingAssembly (). Location.ToString() menos o último /nameOfMyAssembly.dll . Eu posso salvar com sucesso o .DLL de terceiros neste local (que acaba sendo

C: \ Documents and Settings \ meuNome_Usuário \ Configurações locais \ Aplicativo   Dados \ assembly \ dl3 \ KXPPAX6Y.ZCY \ A1MZ1499.1TR \ e0115d44 \ 91bb86eb_fe18c901

), mas quando eu chego à parte do meu código que exige essa DLL, ela não consegue encontrá-lo.

Alguém tem alguma idéia do que eu preciso fazer de diferente?

0
adicionado editado
Visualizações: 1

6 Respostas

Depois de incorporar o assembly de terceiros como um recurso, adicione o código para inscrever-se no evento AppDomain.AssemblyResolve do domínio atual durante a inicialização do aplicativo. Esse evento é acionado sempre que o subsistema Fusion do CLR não consegue localizar um assembly de acordo com as probing (políticas) em vigor. No manipulador de eventos para AppDomain.AssemblyResolve , carregue o recurso usando Assembly.GetManifestResourceStream e alimenta seu conteúdo como uma matriz de bytes no correspondente Sobrecarga Assembly.Load . Abaixo está como uma tal implementação poderia parecer em 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;
    }
};

onde StreamToBytes pode ser definido como:

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();
    }
}

Finalmente, como alguns já mencionaram, ILMerge pode ser outra opção a considerar, embora um pouco mais envolvido.

0
adicionado
GetManifestResourceStream? O assembly seria uma propriedade fortemente tipada sob o namespace * .Properties.Resources.
adicionado o autor Will, fonte
Realizado depois de postar que @dgvid me bateu em tempo de resposta. : P
adicionado o autor Atif Aziz, fonte
Isso é bom, bem feito.
adicionado o autor jcollum, fonte
Eu usei esse código com muito sucesso para fazer exatamente o que eu queria. Veja o meu post para um par de pequenas omissões de sintaxe que eu consertei (não o suficiente para editar este).
adicionado o autor Lawrence Johnston, fonte
Espere, então isso ainda exigiria "controle" do exe, como A ideia de Richter (que parece ter" roubado "esta resposta) sugere que isso aconteça? pesquise o comentário começando com 28 Jul 2010 3:14 PM nesse link Estou dando uma dica em "durante o início da aplicação" na resposta acima - isso não funciona quando você só está distribuindo uma dll e não tem acesso ao exe?
adicionado o autor ruffin, fonte
Para aqueles que não usaram a classe Assembly antes, você precisa ter uma instrução usando System.Reflection; . Demorei um pouco para descobrir qual declaração estava faltando, então talvez isso ajude alguém.
adicionado o autor Keith, fonte

Em vez de gravar o assembly no disco, você pode tentar fazer o Assembly.Load (byte [] rawAssembly) onde você cria rawAssembly a partir do recurso incorporado.

0
adicionado

No final, fiz isso quase exatamente da maneira sugerida pelo raboof (e semelhante ao que o dgvid sugeriu), exceto com algumas pequenas alterações e algumas omissões corrigidas. Eu escolhi esse método porque ele estava mais próximo do que eu estava procurando e não exigia o uso de qualquer executável de terceiros e tal. Isso funciona muito bem!

Isto é o que meu código acabou parecendo:

EDIT: Eu decidi mover essa função para outro assembly para que eu pudesse reutilizá-lo em vários arquivos (eu só passo em Assembly.GetExecutingAssembly ()).

Esta é a versão atualizada que permite que você passe na montagem com as dlls incorporadas.

embeddedResourcePrefix é o caminho da string para o recurso incorporado, geralmente será o nome do assembly seguido por qualquer estrutura de pasta que contenha o recurso (por exemplo, "MyComapny.MyProduct.MyAssembly.Resources" se a dll estiver em uma pasta chamada Resources no projeto ). Ele também assume que a dll tem uma extensão .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();
        }
    }
0
adicionado
Obrigado por postar seu código final, posso acabar usando isso em algum lugar!
adicionado o autor Rob Ringham, fonte

Eu tive sucesso fazendo o que você está descrevendo, mas como a DLL de terceiros também é um assembly .NET, eu nunca a escrevo em disco, apenas carrego da memória.

Eu recebo o assembly de recurso incorporado como uma matriz de bytes da seguinte forma:

        Assembly resAssembly = Assembly.LoadFile(assemblyPathName);

        byte[] assemblyData;
        using (Stream stream = resAssembly.GetManifestResourceStream(resourceName))
        {
            assemblyData = ReadBytesFromStream(stream);
            stream.Close();
        }

Então eu carrego os dados com Assembly.Load ().

Por fim, adiciono um manipulador a AppDomain.CurrentDomain.AssemblyResolve para retornar meu assembly carregado quando o carregador de tipos o procura.

Consulte o Workshop do .NET Fusion para obter detalhes adicionais.

0
adicionado

There's a tool called IlMerge that can accomplish this: http://research.microsoft.com/~mbarnett/ILMerge.aspx

Então você pode apenas criar um evento de compilação semelhante ao seguinte.

Set Path = "C: \ Arquivos de Programas \ 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

0
adicionado

You can achieve this remarkably easily using Netz, a .net NET Executables Compressor & Packer.

0
adicionado