web-dev-qa-db-ja.com

1つのdllを別のdllに埋め込みリソースとして埋め込み、それをコードから呼び出す

DLL別のサードパーティDLLを使用して作成していますが、サードパーティのDLL into my 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を必要とするコードの一部に到達したとき、それを見つけることができません。

誰かが私が違うやり方でやるべきことについて何か考えを持っていますか?

56

サードパーティのアセンブリをリソースとして組み込んだら、アプリケーションの起動時に現在のドメインの 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 は、もう少し複雑ではありますが、考慮すべきもう1つのオプションです。

42
Atif Aziz

最後に、いくつかの小さな変更といくつかの省略が修正されたことを除いて、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();
        }
    }
19

これを実現できるIlMergeというツールがあります。 http://research.Microsoft.com/~mbarnett/ILMerge.aspx

次に、次のようなビルドイベントを作成します。

Path = "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

13
Fostah

あなたが説明していることは成功しましたが、サードパーティDLLも.NETアセンブリであるため、ディスクに書き出すことはなく、メモリからロードするだけです。

埋め込みリソースAssemblyを次のようなバイト配列として取得します。

        Assembly resAssembly = Assembly.LoadFile(assemblyPathName);

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

次に、Assembly.Load()でデータをロードします。

最後に、AppLoader.CurrentDomain.AssemblyResolveにハンドラーを追加して、タイプローダーが検索したときにロードされたアセンブリを返します。

詳細については、 。NET Fusion Workshop を参照してください。

9
dgvid

.NET NET Executables Compressor&Packerである Netz を使用すると、これを非常に簡単に実現できます。

8
Mark Smith

アセンブリをディスクに書き込む代わりに、組み込みリソースからrawAssemblyを作成するAssembly.Load(byte [] rawAssembly)を試みることができます。

2
Hallgrim