2013/05/14

__stdcall付けましたか?

この記事はInstallShieldで作るインストーラー(もアプリケーションの一種だけど)からDLLを呼び出すことを書いてるので普通にVBのアプリからDLLを呼び出すのとは少し違うかもしれない。でも多少参考になれば幸いだ。

インストールスクリプトは何で落ちる?
外部DLLを読込み、そのDLLの関数を呼び出すところで落ちてしまうといったい何が悪いのかさっぱり分からんことになりがち。
チェックポイントは

■prototype宣言の引数の種類が違う

DLLを作ってる間はCの関数宣言でコンパイル時エラーが出るが、prototype宣言は例えば引数の宣言が「STRING」、「BYREF STRING」などの宣言が間違っている場合は実行時に落ちる、という形で現れる。
改行やインデントを工夫して見た目で間違いが無さそうだと分かるようにしておくと良いだろう。

■DLL内部で落ちている

という可能性もモチロンあるのでDLL単体では正常に動作することは確かめよう。

■UseDLLが失敗している

呼出すDLLのパス指定が間違っていてDLLのロードが失敗しているとき(エラーチェックしてないと)、ロード時ではなく関数呼び出し時に落ちる。
デバッグで実行時のファイルパスにちゃんとDLLがあることを確かめ、UseDLLのリターンコードも0であることを確かめよう(DLLロードエラーなのか関数のエラーなのかを切り分ける)。
ちなみにリターンコードが失敗の場合にエラー処理に入るようにしても良いが、完成後にUseDLLが失敗することは無い、としてエラー処理は入れていない(問題が起こったことは無い…)。

■DLL関数のエントリポイント?

DLLのビルド時にモジュール定義ファイル(defファイル)を使って関数名を綺麗にエクスポートする。
かなりあいまいな理解だがdefファイルを使った関数エクスポートでないと、インストールスクリプト側でうまく読み込めない感じ。まぁ対した手間でもないので付けましょう。
判断基準は「dependency walker」でDLLを開いたときに関数名に「_」や「@」が付いてない綺麗な形になっていること。

■__stdcall付けましたか?さもなくばcdecl付けましたか?

今回嵌ったのはこれ… インストールスクリプトは
スクリプト中の最初の例外

エラー番号: 0x80040704
説明: dll関数の呼び出しは誤ったスタックを返しました。
誤ったプロトタイプが宣言されている可能性があります。: 
というエラーを示していた。DLL関数を宣言する際に、__stdcallを付け忘れていたら起きた。
解決するには「prototype宣言にcdeclを付けるか、関数宣言に__stdcallを付ける」とある。
つまり、
//C用
extern "C" {
    __declspec(dllexport) int IsContainsMbs(const char* path);
}
//Install Shield
prototype cdecl PathStringCheck.IsContainsMbs(BYREF STRING);
か、
//C用
extern "C" {
    __declspec(dllexport) int __stdcall IsContainsMbs(const char* path);
}
//Install Shield
prototype PathStringCheck.IsContainsMbs(BYREF STRING);
のどちらかにすれば良い
stdcall(スタンダードコール)とは確か引数をスタックに積む順番の指定で、、、
まぁ前に習えば良いので__stdcallを付けて解決させた。

0 件のコメント:

コメントを投稿