データ 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);
}
}
}
このようなマネージ コード アセンブリの操作は、通常とほぼ同じです。コンポーネントのメイン プロジェクト (ここでは MyLib) でそれ (ここでは ManagedService.dll) を参照しますが、Copy Local プロパティを false に設定します。さらに、アセンブリを既存のアイテムとしてリンクし、ビルド アクションを埋め込みリソースに設定します。
アンマネージド コード (ここでは UnmanagedService.dll) の場合は、DLL を既存のアイテムとしてリンクし、ビルド アクションを埋め込みリソースに設定するだけです。その関数にアクセスするには、通常どおり DllImport 属性を使用します。
[DllImport("unmanagedservice.dll")] public extern static int Add(int a, int b);
それでおしまい!静的 ctor を使用してクラスの最初のインスタンスを作成するとすぐに、埋め込まれた DLL が独自のファイルに抽出され、個別のファイルとして展開したかのように使用できるようになります。実行ディレクトリに対する書き込み権限がある限り、これは問題なく機能するはずです。少なくともプロトタイプ コードの場合、この単一アセンブリ展開の方法は非常に便利だと思います。
楽しみ!
boxedappを試してください。すべての DLL をメモリからロードできます。また、.net ランタイムを埋め込むこともできるようです。本当にスタンドアロンのアプリケーションを作成するのは良いことです...
ILMergeを試しましたか?http://research.microsoft.com/~mbarnett/ILMerge.aspx
ILMergeは、複数の.NETアセンブリを1つのアセンブリにマージするために使用できるユーティリティです。これは、Microsoft .NETFramework開発者センターの[ツールとユーティリティ]ページから無料で使用できます。
/clr
フラグ(すべてまたは部分的にC ++ / CLI)を使用してC ++ DLLを構築している場合は、次のように機能するはずです。
ilmerge /out:Composite.exe MyMainApp.exe Utility.dll
ただし、通常の(ネイティブ)WindowsDLLでは機能しません。
Visual Studio でプロジェクトを右クリックし、[プロジェクト プロパティ] -> [リソース] -> [リソースの追加] -> [既存のファイルの追加...] を選択して、以下のコードを App.xaml.cs または同等のファイルに含めます。
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-easily-in-a-net-assembly/
Smart Assemblyは、これ以上のことを行うことができます。dll にアンマネージ コードがある場合、dll を単一のアセンブリにマージすることはできません。代わりに、必要な依存関係をリソースとしてメインの exe に埋め込むことができます。その裏側、それは無料ではありません。
リソースに dll を埋め込み、AppDomain の Assembly に依存することで、これを手動で行うことができます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 をロードする方法について、このリンクまたはこれを確認できます。
Thinstallは 1 つのソリューションです。ネイティブ Windows アプリケーションの場合、DLL をバイナリ リソース オブジェクトとして埋め込み、必要になる前に実行時に抽出することをお勧めします。
Xenocode の PostBuildは、マネージドとアンマネージドの両方を 1 つの exe にパッケージ化できます。