1つのexeからのプログラム

原則として、.NETプログラムを作成する場合、.NET BCLのクラスだけでなく、サードパーティのライブラリも使用されます。 実行時に、使用されるすべてのライブラリを見つける必要があります。 これを行うには、依存するdllをexeファイルと同じフォルダーに入れます。

ただし、サードパーティライブラリを使用するプログラムがありますが、同時に1つのファイルで構成されます。 SysInternalsのすべてのユーティリティと、私のお気に入りのLINQPadは、動作に必要なすべてを含む1つのファイルです。 そのようなユーティリティを使用することは喜びです-それらはすぐに使用する準備ができているので、転送して保存するのが便利です。

この記事では、単一のファイルからこのようなスタンドアロンプ​​ログラムを作成する方法について説明します。 AutoMapperライブラリを圧縮してプログラムに圧縮する方法と、それを後で取得して使用する方法の例が分析されました。


記事のコード


記事のソースコード- ダウンロード

プログラムコードは、サードパーティのAutoMapperライブラリを使用します。 ライブラリがリソースに縫い付けられた後にライブラリが動作することを確認するために、プログラムはサンプルからライブラリにコードを呼び出します。 この記事はAutoMapperに関するものではないため、このコードはここには示していません。 しかし、ライブラリ自体は興味深く有用です。コード内で何が行われるかを確認することをお勧めします。


アプローチ


.NETコードを機能させるには、それを使用するAppDomainで、コードで使用される型を含むアセンブリを読み込む必要があります。 型がAppDomainにまだロードされていないアセンブリにある場合、CLRは完全な名前でこのアセンブリを検索します。 検索は、正確にはいくつかの場所で行われます-AppDomainの設定に依存します。 デスクトップアプリケーションの場合、これは通常GACおよび現在のフォルダーです。

CLRがアセンブリを見つけられなかった場合、AppDomain.AssemblyResolveイベントが発生します。 このイベントにより、必要なアセンブリを手動で読み込むことができます。 したがって、1つのexeファイルで構成されるスタンドアロンプ​​ログラムを実装するには、依存するすべてのアセンブリをリソースに縫い付け、AssemblyResolveハンドラーに読み込むだけで十分です。

アセンブリロケーターの詳細については、 Essential .NET第1部:共通言語ランタイム(Don Box、Chris Sell) -第8章、AppDomainsおよびAssembly Resolverセクションを参照してください。


アーカイブアセンブリ


アセンブリリソースをアーカイブ形式にすると便利です。 アーカイブにより、最終プログラムのサイズが約2倍に削減されます。 起動速度は向上しますが、これらのほんの一瞬に気付く人はほとんどいません。 ただし、ファイルサイズを小さくすると、ネットワーク上でのダウンロードが高速になります。

.NETライブラリにはアーカイブストリーム-DeflateStreamがあります。 実際、 zipアーカイブを実行しますが、特許とは関係ありません。 依存ライブラリのアセンブリの圧縮は次のとおりです。
var fileInputName = @"OneExeProgram\Libraries\AutoMapper 1.0 RTW\AutoMapper.dll" ;
var assembly = File .ReadAllBytes( fileInputName );

var fileOutputName = @"OneExeProgram\Libraries\AutoMapper 1.0 RTW\AutoMapper.dll.deflated" ;
using ( var file = File .Open( fileOutputName, FileMode .Create ) )
using ( var stream = new DeflateStream ( file, CompressionMode .Compress ) )
using ( var writer = new BinaryWriter ( stream ) )
{
writer.Write( assembly );
}

リソースからのアセンブリの使用


そのため、サードパーティのライブラリを使用した作業プロジェクトがあります。 プロジェクトのexeファイルは自己完結型であり、そのディレクトリに依存するdllを必要としません。

プロジェクトプロパティ-リソース-ファイルを使用して、以前に取得したアーカイブアセンブリをプロジェクトリソースに追加します。 Studioは、リソースを追加するときに、Resourcesクラスを介して追加されたリソースを使用できるコードを生成します。

AssemblyResolveハンドラーを登録します(依存ライブラリクラスを使用する前に):
AppDomain .CurrentDomain.AssemblyResolve += AppDomain_AssemblyResolve;
ハンドラーコード:
private static Assembly AppDomain_AssemblyResolve( object sender, ResolveEventArgs args )
{
if ( args.Name.Contains( "AutoMapper" ) )
{
Console .WriteLine( "Resolving assembly: {0}" , args.Name );

// ,
using ( var resource = new MemoryStream ( Resources .AutoMapper_dll ) )
using ( var deflated = new DeflateStream ( resource, CompressionMode .Decompress ) )
using ( var reader = new BinaryReader ( deflated ) )
{
var one_megabyte = 1024 * 1024;
var buffer = reader.ReadBytes( one_megabyte );
return Assembly .Load( buffer );
}
}

return null ;
}
まれなビルドには1メガバイト以上かかるため、一度に読み取りを行う必要があります。 良いことには、ストリームのコンテンツのサイズと同じサイズのバッファーに読み込む必要がありますが、DeflateStreamはLengthプロパティをサポートしていません。

いくつかの依存ライブラリでは、リソースの名前と目的のアセンブリが一致する契約を導入する価値があります。 次に、リフレクションを使用して、リソース内の必要なライブラリを自動的に検索できます。

デフォルトでは、参照を介して追加された依存ライブラリは、プロジェクトの出力ディレクトリにコピーされます。 AssemblyResolveを機能させるには、出力exeファイルを別のディレクトリにコピーするか、References-AutoMapper-Properties-Copy Local = falseを使用して依存ライブラリを最終ディレクトリにコピーすることを禁止する必要があります。


おわりに


プログラム自体のリソースに依存アセンブリを含めることにより、プログラムを自律的に動作させることができます。 実行にはexeファイルのみが必要です。 これは、すぐに使用できるユーティリティユーティリティにとって重要です。

実際、このようなスタンドアロンプ​​ログラムはインストールを必要とせず、ネットワーク経由で便利に転送されるか、USBフラッシュドライブに保存されます。 アーカイブアセンブリを使用すると、プログラムのサイズを小さくし、これらのプログラムをUSBフラッシュドライブに配置したり、ネットワークからより速くダウンロードしたりできます。

Source: https://habr.com/ru/post/J85480/


All Articles