メッセージボックスの実装 2
では、作成したプログラムを実行ファイルにしてみましょう。「build」フォルダの中に新たに「test2.txt」を作って、以下の内容を記述します。
msgbox "Hello Word!" msgbox "Hello Word!", "Title"
次にコマンドプロントを開き、カレントディレクトリをbuildフォルダに移動してコンパイルします。コンパイルのやり方については前回の記事を参考にしてください。
..\build test2.txt
なお、アセンブラはコンパイラと違い、「これをやったら危険だ」というチェック機能はありません。あるとしても実行エラーを返す程度です。そのため、アセンブルする前にソースコードを確認してチェックする習慣を身に付けておいてください。試しに次のコマンドを打ち込んでみます。
type test2.txt.asm
.586 .model flat, stdcall include iolib.inc includelib iolib.lib include kernel32.inc includelib kernel32.lib .data STR0 db "Hello Word!", 0 STR1 db "Hello Word!", 0 STR2 db "Title", 0 .code _start: invoke MessageBoxA, 0, offset STR0, offset STR0, 0 invoke MessageBoxA, 0, offset STR1, offset STR1, 0 invoke MessageBoxA, 0, offset STR2, offset STR2, 0 invoke ExitProcess, 0 end _start
「.code」の下で、3回も実行してしまっています。コンパイラのプログラムを確認すると、次の箇所が引っかかります。
違えば、もし、対象が「,」&&文字列中がいいえならば、
バッファは「{バッファ}{改行}」。
変数文字列中は、「"」が来た時点でオンとオフが反転し、その時同時にmsgbox命令の分岐も行われてしまっています。
そこで、これを回避するためアセンブリに置き換えている部分に「対象に改行が入っていた時」を加えます。
もし、コマンドライン¥1が空ならば、 「ファイル名を指定してください」と表示。 終わる。 コマンドライン¥1を開く。 それを表示。 バッファは空。 //変数「空」の中身には何も入っていません 文字列中は、いいえ。 //現在の位置が文字列の中であるか 最新命令は、空。 //さっき指定されたばかりの命令 アセンブリは空。 //出力するアセンブリ アセンブリデータは空。 //出力するアセンブリデータ 文字列数は0。 //文字列の数 コマンドライン¥1を開く。 それを文字列分解して、反復 //このようにすれば文字列に変数を埋め込めます もし、バッファが「print」ならば、 最新命令はバッファ。 バッファは空。 //まだ他の場所にprintがあっても //「print(なんとか)print」などとなってしまい //検出できなくなる 違えば、もし、バッファが「msgbox」ならば、 最新命令はバッファ。 バッファは空。 違えば、もし、対象が「,」&&文字列中がいいえならば、 バッファは「{バッファ}{改行}」。 違えば、もし、対象が「"」ならば、 //「"」は一般的な言語では文字列の最初を表す もし、文字列中がはいならば、 文字列中は、いいえ。 違えば、 文字列中は、はい。 違えば、もし、対象が改行ならば、 もし、最新命令が「print」ならば、 アセンブリデータに「STR{文字列数} db "{バッファ}", 0」を配列追加。 アセンブリに「invoke OutputString, offset STR{文字列数}」を配列追加。 文字列数は文字列数+1。 バッファは空。 違えば、もし、最新命令が「msgbox」ならば、 アセンブリデータに「STR{文字列数} db "{バッファ¥0}", 0」を
配列追加。 もし、バッファ¥1が空ならば、 アセンブリに「invoke MessageBoxA, 0, offset STR{文字列数},
offset STR{文字列数}, 0」を配列追加。 違えば、 アセンブリデータに「STR{文字列数+1} db "{バッファ¥1}", 0」
を配列追加。 アセンブリに「invoke MessageBoxA, 0, offset STR{文字列数},
offset STR{文字列数+1}, 0」を配列追加。 文字列数は文字列数+1。 //文字列を1つ多く使ったので 文字列数は文字列数+1。 バッファは空。 違えば、もし、対象が改行でなければ、 バッファは「{バッファ}{対象}」。 //上記以外の場合 //printの後の半角スペースとかは //この行から5行上で対処してしまっています 「.586 .model flat, stdcall include iolib.inc includelib iolib.lib include kernel32.inc includelib kernel32.lib .data {アセンブリデータ} .code _start: {アセンブリ} invoke ExitProcess, 0 end _start 」を「{コマンドライン¥1}.asm」に保存。
だいぶすっきりしました。printとmsgboxの命令のブロックも分かりやすくなったと思います。では、実行ファイルを作成して実行してみます。コマンドプロントのカレントディレクトリを、コンパイルするファイルのあるディレクトリ「build」に移動してください。
..\build.exe test2.txt type test2.txt.asm
.code _start: invoke MessageBoxA, 0, offset STR0, offset STR0, 0 invoke ExitProcess, 0 end _start
ところが今度は1つしかありません。しかもタイトルにも同じ文字列が表示されるところを見ると、最初の1行しか認識されていないようです。そこで、以下の行に注目します。
違えば、もし、対象が改行ならば、
この処理は、「改行のところへ来たら命令の分岐」をするわけです。ところが、プログラムの最後に改行がないため最後の行が認識されないのです。ということで、最後に改行を入れてみました。
msgbox "Hello Word!" msgbox "Hello Word!", "Title"
これをコンパイルして実行します。
.data STR0 db "Hello Word!", 0 STR1 db "Hello Word!", 0 STR2 db "Title", 0 .code _start: invoke MessageBoxA, 0, offset STR0, offset STR0, 0 invoke MessageBoxA, 0, offset STR1, offset STR2, 0 invoke ExitProcess, 0 end _start
今度は成功です。ちなみにSTR0とSTR1の内容がまったく同じ理由は、違う行で同じ文字列を指定したからです。命令は同じでも行が違うと違う処理になります。では、久しぶりにアセンブルをしてみましょう。なお「astest.exe」で指定するコマンドラインには、最後に「.asm」を入れないようにしてください。自動的に付加されます。
C:\Documents and Settings\????????\My Documents\samples\codezine\vefbl \build\test2.txt.asm(16) : error A2006: undefined symbol : MessageBoxA C:\Documents and Settings\????????\My Documents\samples\codezine\vefbl \build\test2.txt.asm(17) : error A2006: undefined symbol : MessageBoxA _ アセンブルエラーが発生しました。
アセンブルエラーが発生してしまいました。原因を調べてみましょう。C言語やWin32APIの経験のある方には、以下のソースコードがいかに不自然か一目で分かると思います。
.586 .model flat, stdcall include iolib.inc includelib iolib.lib include kernel32.inc includelib kernel32.lib .data STR0 db "Hello Word!", 0 STR1 db "Hello Word!", 0 STR2 db "Title", 0 .code _start: invoke MessageBoxA, 0, offset STR0, offset STR0, 0 invoke MessageBoxA, 0, offset STR1, offset STR2, 0 invoke ExitProcess, 0 end _start
上のエラーメッセージを翻訳してみますと「MessageBoxAが不明」という原因でエラーが起きているようです。
MessageBoxA関数はWin32APIから呼び出されています。Win32APIはDLLによって動作が定義されています。そうです、MessageBoxA関数の定義がされているところは、「user32.dll」というDLLです。ところがアセンブリの中で、「user32.dll」を呼び出しているところがありません。そのため、エラーが起きてしまったのです。
アセンブリは、テキストで表された機械語であるため、プログラムの動作全てを左右します。そのため、アセンブリを使う方も、プログラムの端から端までの動作について把握しておく必要があります。
では、呼び出し部分を改めます。
「.586
.model flat, stdcall
include iolib.inc
includelib iolib.lib
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
.data
{アセンブリデータ}
.code
_start:
{アセンブリ}
invoke ExitProcess, 0
end _start
」を「{コマンドライン¥1}.asm」に保存。
太字の行を追加したら、再度コンパイルします。今度はきちんと動くと思います。ちなみに、入力したコマンドラインの履歴は、F7を押すことで一覧を表示させることができます(Windows 9x系では別途設定が必要)。
さて、一番大事なプロセスが実行ファイルの起動です。実行ファイルの呼び出しはtest2.txt.exeと入力して行います。


見事成功しました。

配列追加。
文字列数は文字列数+1。
バッファは空。
違えば、もし、最新命令が「msgbox」ならば、
アセンブリデータに「STR{文字列数} db