web-dev-qa-db-ja.com

Visual C#2010の.exeへのDLLの埋め込み

私はiTextSharp.dllとWebCam_Capture.dllを使用するC#プログラムに取り組んでいます。プログラムをビルドすると、デバッグフォルダーに実行可能ファイルが作成され、期待どおりにこれら2つのdllがデバッグフォルダーにコピーされます。それらを単一の実行可能ファイルにマージしたいのですが、失敗しました。これらの2つのライブラリは、通常、ソリューションエクスプローラーの参照に表示されます。また、リソースとして追加します。実行可能ファイルのサイズが大きくなり、3つのファイルの合計になりますが、それでも実行可能ファイルのディレクトリにはこれらのライブラリが必要です...リソースファイルの「ビルドアクション」プロパティで遊んでみましたが、変更はありませんでした。 ILmergeも試しましたが、エラーが発生しました。だから私は何をすべきですか?

更新:これは私がILmergeから得たものです:

An exception occurred during merging:
Unresolved Assembly reference not allowed: System.Core.
at System.Compiler.Ir2md.GetAssemblyRefIndex(AssemblyNode Assembly)
   at System.Compiler.Ir2md.GetTypeRefIndex(TypeNode type)

ちなみに、これは単なるWindowsアプリケーションであり、利用可能な場合はWebカメラで撮影した写真をPDFとして入力して印刷するフォームです。皆さんありがとう!

14
Mehmed

ILMerge を使用して、複数のアセンブリをマージできます。あなたはすでにこれをしたと言っていて、エラーを受け取りました。理由はわかりませんが、別の方法を使用できます。ライブラリがオープンソースである(そしてそれらのライセンスがあなたのライセンスと互換性がある)場合は、ソースコードをダウンロードし、プロジェクトに追加してコンパイルできます。これにより、単一のアセンブリになります。

ILMergeページには、問題を解決するためのさらに別の代替手段として Jeffrey Richterのブログ もリストされています。

多くのアプリケーションは、多くのDLLファイルに依存するEXEファイルで構成されています。このアプリケーションを展開するときは、すべてのファイルを展開する必要があります。ただし、1つだけを展開するために使用できる手法があります。 EXEファイル。まず、EXEファイルが依存するすべてのDLLファイルを特定し、Microsoft .NET Framework自体の一部として出荷されないようにします。次に、これらのDLLをVisualStudioプロジェクトに追加します。追加した各DLLファイル、そのプロパティを表示し、その「ビルドアクション」を「埋め込みリソース」に変更します。これにより、C#コンパイラはDLLファイルを埋め込みます(s)をEXEファイルに追加すると、この1つのEXEファイルを展開できます。

実行時に、CLRは依存するDLLアセンブリを見つけることができません。これは問題です。これを修正するには、アプリケーションの初期化時に、コールバックメソッドをAppDomainのResolveAssemblyイベントに登録します。コードは次のようになります。

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { 
   String resourceName = "AssemblyLoadingAndReflection." + 
       new AssemblyName(args.Name).Name + ".dll"; 
   using (var stream = Assembly.GetExecutingAssembly()
                               .GetManifestResourceStream(resourceName)) { 
      Byte[] assemblyData = new Byte[stream.Length]; 
      stream.Read(assemblyData, 0, assemblyData.Length); 
      return Assembly.Load(assemblyData); 
   }   
}; 

これで、スレッドが依存型DLLファイル内の型を参照するメソッドを初めて呼び出すと、AssemblyResolveイベントが発生し、上記のコールバックコードで埋め込みDLL必要なリソースを取得し、Byte []を引数として取るAssemblyのLoadメソッドのオーバーロードを呼び出してロードします。

9
foxy
  1. DLLファイルをVisualStudioプロジェクトに追加します。
  2. ファイルごとに[プロパティ]に移動し、ビルドアクションを[埋め込みリソース]に設定します
  3. コードでGetManifestResourceStream( "DLL_Name_Here")を使用してリソースを取得すると、ロード可能なストリームが返されます。
  4. 「AssemblyResolve」イベントハンドラーを記述してロードします。

コードは次のとおりです。

using System;
using System.Collections.Generic;
using System.Reflection;
using System.IO;

namespace WindowsForm
{
    public partial class Form1 : Form
    {
        Dictionary<string, Assembly> _libs = new Dictionary<string, Assembly>();            

        public Form1()
        {
            InitializeComponent();
            AppDomain.CurrentDomain.AssemblyResolve += FindDLL;
        }

        private Assembly FindDLL(object sender, ResolveEventArgs args)
        {
            string keyName = new AssemblyName(args.Name).Name;

            // If DLL is loaded then don't load it again just return
            if (_libs.ContainsKey(keyName)) return _libs[keyName];


            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("YourNamespaceGoesHere." + keyName + ".dll"))  // <-- To find out the Namespace name go to Your Project >> Properties >> Application >> Default namespace
            {
                byte[] buffer = new BinaryReader(stream).ReadBytes((int)stream.Length);
                Assembly assembly = Assembly.Load(buffer);
                _libs[keyName] = Assembly;
                return Assembly;
            }
        }

        //
        // Your Methods here
        //

    }
}

お役に立てば幸いです、パブロ

7
Pabinator

Pabloのコードを少し変更したところ、うまくいきました。
DLLのリソース名を正しく取得していませんでした。

IDictionary<string, Assembly> _libs = new Dictionary<string, Assembly>();

public Form1()
{
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
    InitializeComponent();
}

// dll handler
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    string keyName = new AssemblyName(args.Name).Name;

    // If DLL is loaded then don't load it again just return
    if (_libs.ContainsKey(keyName)) return _libs[keyName];

    using (Stream stream = Assembly.GetExecutingAssembly()
           .GetManifestResourceStream(GetDllResourceName("itextsharp.dll")))  // <-- To find out the Namespace name go to Your Project >> Properties >> Application >> Default namespace
    {
        byte[] buffer = new BinaryReader(stream).ReadBytes((int)stream.Length);
        Assembly assembly = Assembly.Load(buffer);
        _libs[keyName] = Assembly;
        return Assembly;
    }
}

private string GetDllResourceName(string dllName)
{
    string resourceName = string.Empty;
    foreach (string name in Assembly.GetExecutingAssembly().GetManifestResourceNames())
    {
        if (name.EndsWith(dllName))
        {
            resourceName = name;
            break;
        }
    }

    return resourceName;
}
3
Amit Bhagat

あなたが探している答え:

// To embed a dll in a compiled exe:
// 1 - Change the properties of the dll in References so that Copy Local=false
// 2 - Add the dll file to the project as an additional file not just a reference
// 3 - Change the properties of the file so that Build Action=Embedded Resource
// 4 - Paste this code before Application.Run in the main exe
AppDomain.CurrentDomain.AssemblyResolve += (Object sender, ResolveEventArgs args) =>
    {
        String thisExe = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
        System.Reflection.AssemblyName embeddedAssembly = new System.Reflection.AssemblyName(args.Name);
        String resourceName = thisExe + "." + embeddedAssembly.Name + ".dll";

        using (var stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
        {
            Byte[] assemblyData = new Byte[stream.Length];
            stream.Read(assemblyData, 0, assemblyData.Length);
            return System.Reflection.Assembly.Load(assemblyData);
        }
    };
3
userSteve

この無名関数コードをアプリケーションコンストラクターの上部に追加します。これにより、同じプロジェクトの埋め込みリソースからdllが追加されます。

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
    string resourceName = new AssemblyName(args.Name).Name + ".dll";
    string resource = Array.Find(this.GetType().Assembly.GetManifestResourceNames(), element => element.EndsWith(resourceName));

    using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource))
    {
        Byte[] assemblyData = new Byte[stream.Length];
        stream.Read(assemblyData, 0, assemblyData.Length);
        return Assembly.Load(assemblyData);
    }
};
1
Mahsh Nikam

トピックが古いことは知っていますが、将来使いたい人のために書きます。

私はserSteveによるコードに基づいています。

これを変更することをお勧めします。

String thisExe = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;

これに

String thisExe = System.Reflection.Assembly.GetExecutingAssembly().EntryPoint.DeclaringType.Namespace;

そうすれば、名前空間がアセンブリ名と異なっていても機能します

また、ディレクトリからDLLを使用する場合は、そのように使用できます(例としてディレクトリリソース)

String resourceName = thisExe + ".Resources." + embeddedAssembly.Name + ".dll";

それでもこのコードをC#フォームアプリケーションのどこに配置するかが見つからない場合は、次の行の上のファイル「Program.cs」内に貼り付けてください。

Application.Run(new Form_1());

以下の行:

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
0
Asmendorius

アプリドメインの AssemblyResolve イベントを確認してください。

サンプルはありませんが、基本的に要求内容を確認し、リソースDLLをストリーミングバックします。 LinqPAD これはうまくいくと思います-逆コンパイラなどを使用したJosephAlbahariの実装を見ることができます。

0
Paul Kohler