C++ Windows DLL を C# アプリケーション exe にマージするにはどうすればよいですか?
質問
データ I/O に C++ DLL を使用する Windows C# プログラムがあります。私の目標は、アプリケーションを単一の EXE としてデプロイすることです。
このような実行可能ファイルを作成するにはどのような手順を踏めばよいでしょうか?
解決
2007年2月4日日曜日、マネージドコードおよび管理されていないコードの単一アセンブリ展開
.NET 開発者は XCOPY の展開を好みます。そして、彼らは単一のアセンブリコンポーネントを好みます。少なくとも、あるコンポーネントを使用する必要があり、そのコンポーネントのメイン アセンブリに含めるファイルのリストを覚えておく必要がある場合、私は常に少し不安を感じます。そこで、最近マネージド コード コンポーネントを開発し、それを C DLL のアンマネージド コードで拡張する必要があったとき (これについては Marcus Heege さん、助けていただきました!)、2 つの DLL の展開を簡単にする方法を考えました。 。これが 2 つのアセンブリだけであれば、ILmerge を使用してそれらを 1 つのファイルにまとめることもできます。ただし、これは、マネージ DLL とアンマネージ DLL を含む混合コード コンポーネントでは機能しません。
そこで私が解決策として思いついたのは次のとおりです。
デプロイしたい DLL はすべて、埋め込みリソースとしてコンポーネントのメイン アセンブリに含めます。次に、以下のように DLL を抽出するクラス コンストラクターを設定します。クラス ctor は各 AppDomain 内で 1 回だけ呼び出されるため、オーバーヘッドは無視できると思います。
namespace MyLib
{
public class MyClass
{
static MyClass()
{
ResourceExtractor.ExtractResourceToFile("MyLib.ManagedService.dll", "managedservice.dll");
ResourceExtractor.ExtractResourceToFile("MyLib.UnmanagedService.dll", "unmanagedservice.dll");
}
...
この例では、この手法が両方の種類のコードでどのように機能するかを示すために、2 つの DLL をリソースとして含めました。1 つはアンマネージ コード DLL、もう 1 つはマネージ コード DLL (デモ目的のみ) です。
DLL を独自のファイルに抽出するコードは簡単です。
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);
}
}
}
このようなマネージド コード アセンブリの操作は、通常とほとんど同じです。あなたはそれを参照します(ここで:ManagedService.dll) をコンポーネントのメイン プロジェクトに追加します (ここでは:MyLib) を使用しますが、Copy Local プロパティを false に設定します。さらに、アセンブリ内で既存アイテムとしてリンクし、ビルド アクションを埋め込みリソースに設定します。
アンマネージ コードの場合 (ここでは:UnmanagedService.dll) を既存の項目として DLL にリンクし、ビルド アクションを埋め込みリソースに設定するだけです。その関数にアクセスするには、通常どおり DllImport 属性を使用します。
[DllImport("unmanagedservice.dll")] public extern static int Add(int a, int b);
それでおしまい!静的 ctor を使用してクラスの最初のインスタンスを作成するとすぐに、埋め込み DLL が独自のファイルに抽出され、別のファイルとしてデプロイしたかのように使用できるようになります。実行ディレクトリへの書き込み権限がある限り、これは問題なく機能するはずです。少なくともプロトタイプのコードでは、この単一アセンブリのデプロイメント方法は非常に便利だと思います。
楽しむ!
他のヒント
試す ボックスアプリ;すべての DLL をメモリからロードできるようになります。また、.netランタイムを埋め込むこともできるようです。本当にスタンドアロンのアプリケーションを作成するのは良いことです...
使用 フォディ・コスチュラ ナゲット
- ソリューションを開く -> プロジェクト -> Nuget パッケージの管理
- 検索する フォディ・コスチュラ
- コンパイル あなたの プロジェクト.
それでおしまい !
ソース: http://www.manuelmeyer.net/2016/01/net-power-tip-10-merging-assemblies/
ILMergeを試してみましたか? http://research.microsoft.com/~mbarnett/ILMerge.aspx
ILMerge は、複数の .NET アセンブリを 1 つのアセンブリにマージするために使用できるユーティリティです。これは、Microsoft .NET Framework Developer Center の [ツールとユーティリティ] ページから無料で使用できます。
C++ DLL をビルドしている場合は、 /clr
フラグ (すべてまたは部分的に C++/CLI) を指定すると、機能するはずです。
ilmerge /out:Composite.exe MyMainApp.exe Utility.dll
ただし、通常の (ネイティブ) Windows DLL では動作しません。
Visual Studioでプロジェクトを右クリックするだけで、プロジェクトのプロパティ - >リソース - > [リソースの追加]を選択します。既存のファイルを追加してください。
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);
}
私の元のブログ投稿は次のとおりです。http://codeblog.larsholm.net/2011/06/embed-dlls-easy-in-a-net-assembly/
インストールする 一つの解決策です。ネイティブ Windows アプリケーションの場合は、DLL をバイナリ リソース オブジェクトとして埋め込み、実行時に必要になる前に抽出することをお勧めします。
スマートアセンブリ これ以上のことができます。DLL にアンマネージ コードが含まれている場合、DLL を単一のアセンブリにマージすることはできませんが、代わりに必要な依存関係をリソースとしてメインの EXE に埋め込むことができます。裏を返せば、無料ではありません。
これを手動で行うには、DLL をリソースに埋め込み、AppDomain のアセンブリに依存します。 ResolveHandler
. 。混合モード DLL に関しては、次の亜種やフレーバーが多数見つかりました。 ResolveHandler
私にとっては機能しないアプローチ(DLLバイトをメモリに読み取ってそこから読み取るすべて)。これらはすべてマネージド DLL で動作しました。私にとってうまくいったのは次のとおりです。
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);
};
}
ここで重要なのは、バイトをファイルに書き込み、その場所からロードすることです。鶏が先か卵が先かの問題を回避するには、アセンブリにアクセスする前にハンドラーを宣言し、読み込み (アセンブリ解決) 部分内でアセンブリ メンバーにアクセス (またはアセンブリを処理する必要があるものをインスタンス化) しないようにする必要があります。確保にも気をつけてください GetMyApplicationSpecificPath()
一時ファイルは他のプログラムや自分自身によって消去される可能性があるため、一時ディレクトリではありません (プログラムが DLL にアクセスしている間に削除されるわけではありませんが、少なくとも迷惑です)。AppData は良い場所です)。また、毎回バイトを書き込む必要があることにも注意してください。DLL が既にそこに存在するため、その場所からロードすることはできません。
アセンブリが完全に管理されていない場合は、次のように表示されます。 リンク または これ そのような DLL をロードする方法については。
ポストビルド元 ゼノコード 管理対象と非管理対象の両方を 1 つの exe にパッケージ化できます。