C#は電気羊の夢を見るか?(引っ越し中)

dtiブログが閉鎖とのことでhttp://tanocs.blog.fc2.com/へ引っ越します。

C#で、ネイティブなEXEを作成する

通常であればC#で開発したアプリケーションは.Net Frameworkが
インストールされた端末か、Monoが有る環境でしか実行できない。

しかし、Monoを使用してmkbundleというコマンドにてネイティブ化を行うことで
Monoや.Net Frameworkのインストールがされていない端末でも、
インストール作業なしで実行を行うことが可能になります。


この手法、海外のサイトを調べてみるとcygwinを使用した方法が主流ですが、
根っからの窓っ子の自分にはどうもとっつきにくい。
調べていくと、cygwinを使わずとも出来ることが解ったので、cygwinは抜きの手順です。


尚、出来上がるexeの実績については、SharpDXを使い1000本ほど配布を行いましたが、
WinXP、Vista、7にて32、64bitを問わず、実行できなかったという回答をもらっていないことから、
割と多くの端末で動作しているものと思われます。



記事を起こした時点での動作確認は以下のバージョンにて行なっています
・[Mono] 2.10.9
・[gcc for Windows] 4.7.2
・[GNU utilities for Win32] Last Update:2011-04-05


以下に長々と書いていますが、スムーズに行けば
準備3ステップ、実行3ステップの単純なものです。

◆準備
Monoをインストール
gccコンパイラをインストール
GNU utilities for Win32を配置

◆実行
mkbundleを実行
⊇侏莨紊った[temp.c]を編集
コンパイルを行う


■準備編
Monoインストーラをダウンロードする[mono-2.10.9-gtksharp-2.12.11-win32-0.exe]
http://www.mono-project.com/Main_Page

Mono for Windows, Gtk#, and XSPをダウンロードして、インストールしてください。
この時、インストールしたフォルダはコマンド等で頻繁に使用しますので、覚えておいてください。

★これ以下では、例として[C:Mono]にインストールを行ったものとして記事を書きますので、
別の場所に保存した方は適宜読み替えてください。



Win32の『32bit版』のgccコンパイラをインストールする[gcc-4.7.2-32.exe]
http://www.equation.com/servlet/equation.cmd?fa=fortran

!!64bit版のOSであっても、必ず32bit版をインストールしてください!!

☆ユーザー名が日本語の方はフォルダ名が化けているので修正してください。
 この時、日本語が含まれていないほうが望ましいと思われます。



GNU utilities for Win32を配置する[UnxUtils.zip]
http://sourceforge.net/projects/unxutils/
[UnxUtils.zip]を解凍し中に入っている[sh.exe]を[c:monobin]に配置する

☆後述しますが、このファイルは今回の目的にはかなりリッチなモノです。
また、実行時ウィンドウを小さくするバグを持っていて、最適とは言いがたいです。
詳細は[[脱線]]を御覧ください。



ず撞動して準備完了
!!環境変数を反映させるため手動での再起動が必要です!!


[[脱線開始]]
ここで必要なのは[sh.exe]という名前で[as -o temp.o temp.s]を実行してくれるアセンブリです。

.Netアセンブリのexeでも問題なく動作しますので、良く解らない物を使いたくないという人は、
適当な[HelloWorld.exe]を[sh.exe]にリネームして配置してしまうのも手です。
この場合mkbundle終了後cmdより[as -o temp.o temp.s]を手動で実行する必要があります。

ベストなのは、HelloWorldではなく[as -o temp.o temp.s]をキックするアセンブリであるのは
言うまでもありません。

さらに、mkbundleはオープンソースになっているため、
自作にて[sh.exe]を使わないようにカスタムする手もあります(後述)
[[脱線終了]]




■実行編
mkbundleを実行してgccアセンブラソースとmonoオブジェクトを取得する
【1.スタートメニューから「Mono-2.10.9 Command Prompt」を実行】


【2.ネイティブ化したい実行ファイルのあるフォルダに移動する】


【3.mkbundleを実行する】
・DLLなし
 mkbundle -c -o temp.c --deps --keeptemp hoge.exe

・DLLあり
 mkbundle -c -o temp.c --deps --keeptemp hoge.exe foo.dll bar.dll

・SharpDXを使う場合の具体例
mkbundle -c -o temp.c --deps --keeptemp Sample.exe SharpDX.Direct3D9.dll SharpDX.dll


!!ウィンドウが小さくなった後も引き続き[as -o temp.o temp.s]が実行されるので、
ウィンドウの×で終了しないでください!!

☆GNU utilities for Win32を使用するとウィンドウが小さくなってしまい、
 mkbundleのログが見えなくなってしまいます。

☆[as -o temp.o temp.s]は一緒に入れ込むdll次第では長時間となる場合があります

☆ログメッセージの
warning: pointer targets in passing argument 2 of 'mono_register_co
nfig_for_assembly' differ in signedness [-Wpointer-sign]
については後述。


【4.[exit]コマンドで終了】
ウィンドウが小さくなっていて見づらいですが、
[as -o temp.o temp.s]の実行が終了して入力が可能になるのを待って入力してください。


【上手くいかない時は】
☆SharpDX等インターネットからダウンロードしたdllなどのアセンブリを使用する場合、ブロックの解除が必要

☆署名有りDLLはダメなことがあるので、署名なしのものを利用してください。
 ここで通ってしまうこともありますが、後々問題を引き起こすので、署名なしのものを使ってください。

☆mkbundle実行時、exeの作成時と違うバージョンのdllを同梱しようとするとFileNotFoundになるので注意
 ソリューションをバッチビルドでリビルトしてやれば直る



⊇侏莨紊った[temp.c]を編集
#ifdef _WIN32
#include
#endif

//【以下2行を追加】
#include<direct.h> //<>を半角にしてください
#undef _WIN32

/////////////////////////////////////////////////

int main (int argc, char* argv[])
{

//【以下5行を追加】
char Path[4];
if(0 != GetModuleFileName( NULL, Path, 4)) {
Path[3] = '¥0'; //¥を半角にしてください
chdir(Path);
}


"#undef _WIN32"を書かないとコンパイル時にエラーが出て先に進めない
他は日本語を含むパスから実行するときにmono-2.0.dllが見つかるようになる魔法の呪文



コンパイルを行う
gcc -g -o AssemblyName -Wall temp.c -mms-bitfields -mwindows -IC:/Mono/include/mono-2.0 -mms-bitfields -mwindows -LC:/Mono/lib -lmono-2.0 -lws2_32 -lpsapi -lole32 -lwinmm -loleaut32 -ladvapi32 -lversion temp.o

!!以下の引数についてMONOのインストールディレクトリに適宜を記述を変更すること!!
-IC:/Mono/include/mono-2.0
-LC:/Mono/lib


☆「アクセスが拒否されました」とかメッセージが出る場合
cygwinがすでにインストールとかされてて、インストールしたgccより優先されてしまっているので、
環境変数の順番変えてインストールしたwin32のgccが実行されるようにしてくりゃれ。


☆cannot find -lmono-2.0とかってメッセージが出てる場合
このメッセージが出力されるのは64bit版gccをインストールしているから
Mono-2.0は32bitアセンブリのため、読み込むことが出来ずエラーメッセージが出力される。

解決するにはインストール時に登録されたgccの環境変数を手動で消して、
インストールしたフォルダを消して32bit版を再インストールしてください。

[蛇足]64bit版で実行しても速度に大きな改善はないので、面倒の少ない32bit版で行うのが良いと思う


☆最適化オプションについて
ある程度、最適化オプションを自由に設定できるが、思ったほど効果が上がらなかった。
なにか良いオプションを知っている方がいたら、ご一報ください。



exeが出力されて完成
出来上がったexeと[Mono-2.0.dll]を同じフォルダにいれれば、.Netのない環境でも実行できる。

配布時、exeのみとする方法もありますが
『mkbundle実行時に--staticフラグ付けて静的リンクしたら、お前のプログラムもLGPLだかんね』
http://www.mono-project.com/Mono:Runtime

とのことなので、おとなしく[Mono-2.0.dll]を外に出しておくのが無難だと思います。
「LGPL?上等!逆アセンブルなんて怖くねーぜッ!」って方はご自由に。



★★★★★実行がうまくできない時は★★★★★
=SharpDXでエラーが出る時は=
・最新版のDX入れてね☆(ゝω・)
http://www.microsoft.com/ja-jp/download/details.aspx?id=35


=実行しても、うんともすんとも言わないとき=
・同梱したdllのバージョンが、アセンブリ生成時と違ってたりする事があるのでリビルトを試す

・署名がついてるdllが混ざっていないか確認する

経験則で理解できてはいないけどmkbundle実行時、アセンブリの記述順で良くなったりダメになったりする事がある。
コンパイルは通ってexeは出来るけど、実行時うんともすんとも言わないパターンなので容赦ならない。


=署名関係について=
署名付きのdllしか提供されていない場合Mono付属の[sn.exe]で署名の上書きが出来るらしいが、権利的に怪しい。
出来る事ならソースコードをダウンロードして、自分で署名なしのバイナリを作るのが望ましい。

オープンソースじゃないのなら、作者に嘆願するか諦めるか個人の範囲で楽しみましょう。


=mkbundle実行時のメッセージについて=
temp.c: In function 'install_dll_config_files':
temp.c:81:2: warning: pointer targets in passing argument 2 of 'mono_register_co
nfig_for_assembly' differ in signedness [-Wpointer-sign]
In file included from temp.c:3:0:
C:/Mono/include/mono-2.0/mono/metadata/assembly.h:101:15: note: expected 'const
char *' but argument is of type 'const unsigned char *'

mkbundle実行時このメッセージが出力されていても、
致命的では無いようで大丈夫な時は大丈夫。
出ない時もある、よく解からん(´・ω・`)

内容は署名が違うとか言ってるっぽい。書き換えてやれば黙るのかは未確認。



◆mkbundleを自作する
・mkbundleのソースコード
https://github.com/mono/mono/tree/master/mcs/tools/mkbundle

自作したmkbundleを通して出来上がったexeを実行して
this application has requested the runtime to terminate it in an usual way
Please contact the application's support team for more information.
とかってエラーが出たら
MkBundleが実行時、Monoの環境ではなくMS.Net環境で実行されてる。


このエラーはMS謹製のmscorlib.dllを参照したためmono-2.0.dllの実行時に落ちてると思われる。
mkbundle時のログを見れば、MS製のmscorlib.dllを参照しているはず。

Mono用のコマンドプロンプトから"Mono"キーワードをアセンブリ名の前につけて
"Mono Mkbundle.exe[引数略]"とかってして実行してくりゃれ。


未確認な手法ですが、mscorlib.dll等の関連するファイルの署名をsn.exeで上書きすると
mscorlib.dllがMS版のままmkbundleが出来るかもしれない。

しかし、これは許諾以外の使用方法で、権利的に非常に怪しいと思うので、
個人の趣味の範囲でどうぞ。


=出来なかったこと=
mkbundleを単体で動作できるようにしたかったが、どうにも上手くいかなかった。
何か上手い事GUI化出来た方がいたらご一報ください。



◆おまけ
この手法でネイティブ化出来るのはMonoで実装されている機能だけなので、
全ての.Netアセンブリがネイティブ化出来るわけではない。
それを調べるためにMomaというフリーウェアがあるが、コレも割とめんどくさい。

そこで、下記の手法をつかってVS上からあぶり出すことが可能。
バージョンが違うので、dllは自力で入れ替える必要があるが、割と便利だったのでご紹介。
http://jpobst.blogspot.jp/2009/06/mono-in-visual-studio-2010.html


この方法で恐らくIronPythonから出力した.Netアセンブリもネイティブに動かせるんじゃないかと思う、という蛇足。蛇だけに。



◆最後に
この手法は、足がかりに過ぎず、ただ出来ているという状態を超えていません。
よりよい手法がありましたら、一つコメントをいただけると助かります。


この手法が、どなたかの助けとなれば幸いです。
このエントリーをはてなブックマークに追加
LINEで送る

コメントの編集

秘密にする

トラックバック

http://t01a.dtiblog.com/tb.php/159-4dc8ccc9

-

管理人の承認後に表示されます

« FXAAをXNA4.0で使う  | HOME |  ComboBoxへDictionaryを関連付ける »

PAGE TOP ▲

Appendix

■春条

■春条

生息地は愛知
車と甘い物が好きな31歳
特技は無限昼寝

MONOからSharpDXを使う
変態的な手法で、
.NET Frameworkを使わずに
ゲーム作りやってます。

Search

Calender

« | 2017-12 | »
S M T W T F S
- - - - - 1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31 - - - - - -

Twitter

Recent Entries

DTIブログポータルへ
このブログを通報
Report Abuse

利用規約