57

別のサード パーティの 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 を必要とするコードの部分に到達すると、それが見つかりません。

私が別のやり方で何をする必要があるかについて誰かが何か考えを持っていますか?

4

6 に答える 6

44

サードパーティ アセンブリをリソースとして埋め込んだら、AppDomain.AssemblyResolveアプリケーションの起動時に現在のドメインのイベントをサブスクライブするコードを追加します。このイベントは、CLR の Fusion サブシステムが有効なプローブ (ポリシー) に従ってアセンブリを見つけられない場合に発生します。のイベント ハンドラでAppDomain.AssemblyResolve、 を使用してリソースをロードし、Assembly.GetManifestResourceStreamそのコンテンツをバイト配列として対応するAssembly.Loadオーバーロードにフィードします。以下は、そのような実装の 1 つが 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は、もう少し複雑ではありますが、考慮すべき別のオプションになる可能性があります。

于 2008-09-18T21:40:49.083 に答える
21

最後に、いくつかの小さな変更といくつかの省略を修正したことを除いて、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();
        }
    }
于 2008-09-19T17:11:37.823 に答える
13

これを実現できる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

于 2008-09-18T20:48:43.637 に答える
9

私はあなたが説明していることを成功させましたが、サードパーティの 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を参照してください。

于 2008-09-18T21:17:50.620 に答える
8

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

于 2008-09-18T21:02:40.907 に答える
2

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

于 2008-09-18T20:56:58.173 に答える