「C#」 カテゴリーの記事です。
nuget-icon

.NET FrameworkでDLLファイル(国際化対応リソースを含む)をexeにマージする


前のバージョンまで、「アタッシェケース」ではインストールを行うと、以下のプログラムフォルダーには、本体と並んで、DLLファイルがいくつか並んでいました。

AttacheCase.exe
AtcSetup.exe
Microsoft.WindowsAPICodePack.dll
Microsoft.WindowsAPICodePack.Shell.dll
ja-JP\AttacheCase.resources.dll

このファイル構成は、あくまで開発者側の都合であって、ユーザーにとっては良く分からないでしょう。

実行ファイルを別の場所へ移動させて使いたいとき、このDLLファイルも同時に移動させなくてはいけないのか?と迷ったり、煩わしいと思ったりするかもしれません。

実際、付属DLLファイルによっては、実行ファイルのあるフォルダーに存在しないと、正常に動かないものもあり、ユーザーにとっては、ツールのポータビリティが低く、やや使いづらいのではないかと考えました。

一つの実行ファイルに、すべての付属ファイルをマージする

というわけで、章題。これを目標にしてみました。そこで考えられる方策をいつくかご紹介したいと思います。

ILMerge(ILRepack)

もっとも手軽で定番なのは、「ILMerge」でしょうか。

Microsoft謹製のツールです。リンク先にあるツールをダウンロードしてインストールすると、使えるようになります。いろいろなオプションがあって、それに従って設定すれば、指定の実行ファイルにDLLファイルをマージできるようになります。

コマンドの例としては、以下のような感じです。

ところが、これですと、言語ファイルである、ja-JP\AttacheCase.resources.dllが残ってしまいます。どうやら、国際化対応リソースのDLLは「サテライトアセンブリ」といって、マージのときに除外されるみたいです。

ちなみに「ILMerge」は、Microsoftのプロプライエタリなツールですが、これをオープンソースにして、機能を拡張したものも存在します。「ILRepack」です。

これも、ILMergeと基本的に使い方は同じです。たとえば、以下のようにします。

ただ、これも、肝心の言語ファイルがマージされません。

NuGet

では、より簡単で、すべてのDLLファイルを実行ファイル(exe)にマージできないか調べて見ると、Visual Studio 2012以降では標準で付属しているツールの「NuGet」で、あるパッケージ群をインストールすれば可能になるようです。

まず、NuGetとは何か? マイクロソフト公式のウェブサイトにも説明はありますが、以下のウィキペディアの説明の方が簡潔ですので、そちらを参照なさってください。

NuGet
https://ja.m.wikipedia.org/wiki/NuGet

NuGetは、基本的にVS全体にインストールするというよりも、プロジェクト毎にインストールするようになっています。ですので、以下の画面では、「アタッシェケース」のソリューション(その中に各プロジェクト)が開いている状態で、NuGetパッケージ管理画面を開こうとしています。

 

Fody

次に「参照」から「fody」を検索して、Fodyを見つけたら、NuGetからパッケージを必要とするプロジェクトにチェックを入れ、そこへ「インストール」します。

Fodyとは以下のサイトにも説明がある通り、「Extensible tool for weaving .net assemblies(.NET Frameworkにアセンブリを組み込むための拡張ツール)」です。

Fody
https://github.com/Fody/Fody/

ただ、これだけではまだ不完全です。さらに、次にあるFodyのアドインをインストールする必要があります。

Fody.Costura

再びNuGetで「Fody Costura」を検索してインストールします。

Costuraとは、プロジェクトで使うリソースをアプリケーション本体に埋め込むためのツールです。

Costura is an add-in for Fody
https://github.com/Fody/Costura/

しかし、インストールしただけでは正常に動作しません。その後に、設定ファイルを所定の場所に置かなくてはなりません。ファイル名は以下の通り、決まっています。

FodyWeavers.xml

というファイルを作り、プロジェクトファイルがあるディレクトリに配置しなくてはなりません。

そのXMLファイルの中身ですが、最低限の設定であれば、以下の内容だけでO.K.です。ちなみに、公式GitHubのウェブサイトを見れば、埋め込みを除外するDLLを指定することができたり、様々なオプションが用意されているので、カスタマイズする場合はそちらを参考になさってください。なお、XMLファイルの文字エンコーディングはUTF-8なのでご注意を。

これにより、
Microsoft.WindowsAPICodePack.dll
Microsoft.WindowsAPICodePack.Shell.dll
の二つが「AttacheCase.exe」埋め込まれて、以下のようにファイルが出力されるかと思います。

 

Resource.Embedder

しかし、これでも、まだ国際化対応リソース(言語ファイル)DLL、
ja-JP\AttacheCase.resources.dll が統合されておりません。

そこで、もう一つNuGetパッケージをインストールします。「Resource.Embedder」です。

Resource.Embedder
https://github.com/MarcStan/Resource.Embedder

インストールは、以下の通りです。

これにより、ビルドを行うと、すべてのDLLリソースが「AttacheCase.exe」に埋め込まれて、以下のように単一で出力されます。

 

ただし、.NET Framework 4.0 だと古いバージョンを使う

Fody パッケージ自体は、.NET Frameworkのバージョン依存関係はありませんが、Fodyのプラグインである Costura の最新版の方には、

.NET Framework 4.6
Fody (>= 3.2.6)

といった依存関係があります。

ですので、アタッシェケースは、一応WindowsXP上でも動作するように、.NET Frameworkは、“4.0” でビルドするという縛りを設けているため

Fody ver.2.5.0
Costura.Fody ver.1.6.2

と、あえて古いバージョンのパッケージをそれぞれ使っています。

以上です。

「アタッシェケース#3」を正式版としました。


β版リリース時にもブログ記事を書きましたが、バグ報告もなくなり、自身で使っていても、目立った不具合がなくなってきたため、正式版としました。それでも細かいバグはまだまだありそうですので、もし何かあれば報告をいただけるとうれしいです。

アタッシェケース#3アイコンhttps://hibara.org/software/attachecase/

前述の記事でも書きましたが、Ver.2からの変更点のおさらい。

  • ファイルフォーマットの変更(Ver.3独自)
  • パスワードの扱いについての改良(RFC2898によるキー派生)
  • 暗号化、復号の処理速度の向上
  • Windows 10(タッチ操作など)に対応
  • パスワード付きZIPファイルの作成機能(おまけ)

ver.2は、2004年の開発開始からほとんど修正されることのなかったファイルフォーマットに手を入れました。冗長な部分を削除し、やや弱かったパスワード部分の扱いを改良、メモリで扱う部分を大きくし、また高速化(並列処理)に適したフォーマットにしました。

ですので、Ver.3で暗号化されたファイルは、Ver.2では復号できませんので、あらかじめご注意ください。ただし、Ver.2ファイルはVer.3では開けます。つまり上位互換です。

パスワード付きZIPファイルへの対応はおまけです(笑)。知人からの要望を受けて、入れてみました。邪魔で不評なら将来的に削除、好評なら復号処理も入れようかと思います。

技術的な変更点は、

  • .NET Frameworkでの開発
  • コードサイニング証明書の付加

今までC++Builderで開発を行ってきましたが、毎年のバージョンアップ費用がもはや個人ユースとして耐えられなくなってきたのと、無料で使える、Microsoftの「Microsoft Visual Studio Express 2015 for Windows Desktop」にした方が、より多くの人にとって、オープンソースからのプルリクエストや、フォークがしやすいのではないかと思い、乗り換えてみました。

また、暗号化ツールという性質上、セキュリティ面での使用を躊躇してしまうのを少しでも軽減しようと、コードサイニング証明書を付加してみました。法人ではなく僕個人のもので、けっこうなお値段でしたが、少しでも安心して使っていただけるようにと自腹で負担しました(泣)。

より多くの人に使っていただけるのが、開発者としては望外の喜びです。

電卓の16進から10進へ

C#で文字列をバイナリサーチする



前提として、僕のケースでは、『アタッシェケース#3』にて、ファイル先頭から固定値である「_AttacheCaseData」(16バイト)を検索していきます。それにより、自己実行形式ファイルのデータ境界が分かるようになります。

ウェブを検索してみたら、以下のサイトが近い感じがするのですが、

バイナリデータを検索する方法(vb.net)
http://www.my-hobby.jp/index.php/2012/01/vb-net2/

File.ReadAllBytes()で、一気にファイルをバイト単位での読み込みを行っています。僕のアタッシェケース#3では、出力されるファイルが、2GBを余裕で超えてくるファイルも扱う可能性もあるので、それは使えません。

そこで、File.ReadByte()を使います。知ってましたか、ReadByte();

ReadByte()は、ストリームから1バイトずつ読み込んで行きます。ただし、返値がバイトではなく、Int32 にキャストされた符号なしバイト(int)で返ってくるのに要注意。

あらかじめ分かっている定数ならば、僕のようにint配列にしますが、場合によっては、byte値をその度にintにキャストして比較しても良いでしょう。

電卓の16進から10進へ

ちなみにbyte値をintにするには、Windowsの電卓を「プログラマ」にして「16進」→数値入力→「10進」にして、値を出しました。16進を10進に脳内変換で出来ちゃうプログラマーさんはすごいと思う(常識デスカ?)。

「アタッシェケース#3」をリリースしました



「アタッシェケース#3」のβテスト版をリリースしました。ようするにアタッシェケースの「Version.3」へのメジャーバージョンアップです。

アタッシェケース#3アイコンhttps://hibara.org/software/attachecase/   

僕が好んで使っていた統合開発環境「C++Builder」の価格高騰に伴いバージョンアップを諦め、無料で使える、Microsoftの「Microsoft Visual Studio Express 2015 for Windows Desktop」に乗り換えて、開発しました。言語は、C#ですので、ほぼフルスクラッチでの開発でした。

2004/07/25 に『ver.2』がリリースされてから、ここまで少しずつ改良を重ねてきましたが、 それはほとんどが本体側だけで、そこから生成される暗号化データの形式は、互換性を保つため、 ほぼ当時のままの設計で来ていました。

あれから年月が経つにつれて、当時の僕の拙いプログラミングから、パスワードの扱い方にやや弱い部分があることや、 暗号化するバッファの一部がとても小さく、暗号化・復号処理に時間がかかっていたことなどが分かって来ました。

また、データに格納するファイル情報が冗長になり、不要なもの、次第に使われなくなったものが多くなってきました。 そこで今回のメジャーバージョンをキッカケにして、データの仕様も全面的に見直し、再設計を行いました。 その結果、Ver.3 は、Ver.2よりも暗号強度の高いファイルを生成します。

そのため、暗号化ファイルは上位互換です。Ver.3で暗号化したファイルは、ver.2では開くことはできません。ただし、Ver.3では、Ver.2のファイルは開くことができます。

ソースコードは、GPLv3ライセンスとして、GitHubにもアップロードされています。ご興味のある方はぜひご覧いただき、フィードバックや改良案などいただけると嬉しいです(ただし、簡単な質問はググってね♥)。

また今回から、Windows 10にも正式対応しました。一応、タッチパネルを意識した作りをしたつもりですが、細かいところではどうでしょうか。タッチパネルを頻繁に使われる方で、この辺りの挙動でご不便なところがあれば、ご意見いただきたいです。

DotNetZipでEncryptionAlgorithm.WinZipAes256が選択できない



Visual Studio C#で『アタッシェケース』の新版を開発中です。その中で、パスワードZIPファイルを作成するために、DotNetZipというライブラリを使っています。

DotNetZip-Logo

ところが、以下のコードのように設定しても、コンパイルエラーとなります。

Visual Studioのインテリセンスを表示させてみても、
None
PkzipWeak
Unsupported
と、肝心の「AES」が出てきません。おかしいなあ、とDotNetZipのソースを見てみたら、defineで切られているではありませんか。

AESはdefineで切られている

おそらくパスワード付きZIPのAESは、アーカイバによって解凍できなかったりするので、こういう扱いなのでしょうか。実際、DotNetZipヘルプの「EncryptionAlgorithm 列挙体」には、こう書かれていました。

Values of WinZipAes128 and WinZipAes256 are not part of the Zip specification, but rather imply the use of a vendor-specific extension from WinZip. If you want to produce interoperable Zip archives, do not use these values. For example, if you produce a zip archive using WinZipAes256, you will be able to open it in Windows Explorer on Windows XP and Vista, but you will not be able to extract entries; trying this will lead to an “unspecified error”. For this reason, some people have said that a zip archive that uses WinZip’s AES encryption is not actually a zip archive at all. A zip archive produced this way will be readable with the WinZip tool (Version 11 and beyond).

WinZipAes128 値と WinZipAes256 値は zip 仕様に含まれていませんが、WinZip のベンダー固有の拡張を使用することを意味しています。相互運用可能な Zip アーカイブを生成するには、これらの値を使用しないでください。たとえば、WinZipAes256 を使用して zip アーカイブを生成する場合、Windows XP および Vista で Windows Explorer で開くことができますが、エントリーを解凍することはできません。解凍しようとすると、「未定義のエラー」となります。このため、WinZip の AES 暗号化を使用した zip アーカイブは、実際にはまったく zip アーカイブでないと言う人もいます。この方法で生成された zip アーカイブは、WinZip ツール (バージョン 11 以降) で読み込むことができます。

ですので、AESをどうしても使いたい場合は、Visual Studioの「条件付きコンパイルシンボル」に「AESCRYPTO」を追加します。

プロジェクトのプロパティを開く

プロジェクトのプロパティを開いて、

条件付きコンパイルシンボル

追加すると、コンパイルも通り、インテリセンスにも「WinZipAes128」「WinZipAes256」の値が表示されるようになります。

s