【VC++6.0】ReleaseモジュールだとLoadLibrary()が0x00を返す

[戻る]
nakajima 2009/11/09(月) 10:40:43
WinXP(SP3) VC++6.0

Win32DLLを、テスト用MFCプログラムから呼び出すと下記のような現象になります。
・DebugモードでビルドしたDLLは、正常に動作する
・ReleaseモードでビルドしたDLLに置き換えて呼び出すとLoadLibrary()が0x00を返す

dumpbin /EXPORTS (DLL名)にて確認してみましたが、Debug/Release両モジュール共に
意図した関数の名前が表示されているような状況です。

呼び出し側に問題があるのか、DLL側の問題なのかすら分からず困っています。
何が原因で、どうすれば解決(Releaseモジュールの正常呼び出し)できるか
お心当たりのある方いらっしゃいましたら、ヒントだけでも宜しくお願いします。

仲澤@失業者 2009/11/09(月) 12:01:28
GetLastError()又は、ウオッチでの@err,hrはなんと言ってますか(質問)。

nakajima 2009/11/09(月) 14:04:16
>>仲澤@失業者さん

LoadLibrary()を実行する行にブレークポイントを挿入し、ステップ実行(F10)した直後の
@errは。。。
---
@err→1114
@err,hr→0x0000045aダイナミック リンク ライブラリ(DLL)初期化ルーチンの実行に失
敗しました。
---
でした。

ITO 2009/11/09(月) 14:26:55
DLLがDEBUGモードでビルドされているとかないですか?

nakajima 2009/11/09(月) 14:34:06
>>ITOさん

「DLLがDEBUGモードでビルドされている」と、上手く動作するのですが
ReleaseモードでビルドしたDLLに置き換えると動作しないのです。><;

ちなみに、DLLがDebugモードでビルドされたモジュールであれば、
呼び出し側のテスト用MFCプログラムはDebug/Release両方で動作します。

ITO 2009/11/09(月) 15:08:23
なるほど、
となると、
  ・new-delete等の処理がうまくいってますか?
  ・GlobalAlloc、HeapAlloc等を使った後、
   ----Free()を呼び出していますか?
ぐらいが考えられますね。
あと、配列変数の要素数が足りないとか?
調べてみるといいかなとおもいます。

一回、暗黙的リンクで動作するか確かめてみるのもいいと思います。

nakajima 2009/11/09(月) 15:44:17
>>ITOさん
ありがとうございます。
しかしながら、DLLの呼び出し部分は下記のようなコーディングとなっており
GlobalAlloc、HeapAlloc等は使用していない状況です。
暗黙的リンクも、DebugビルドのDLLで正常に動作しているので何とも。。。

---
【DLLのヘッダファイル(dll.h)】
    typedef void (*LPFUNC1)(構造体A *, 構造体B *);

【呼び出し側のテスト用MFCプログラムのDLL呼び出し部分(抜粋)】
    HMODULE         mHandle;
    LPFUNC1         pFunc;
    static  構造体A paramIn;
    static  構造体B paramOut;

    [paramInの設定、paramOutの初期化など]

★  mHandle = ::LoadLibrary( "DLL名" );
    pFunc = (LPFUNC1)GetProcAddress( mHandle, "関数名" );
    pFunc( &paramIn, &paramOut );
    FreeLibrary( mHandle );
---

DebugモードでビルドしたDLLなら、想定通りにparamInの内容が
DLLの"関数名"で指定した処理に引き渡され、paramOutにDLL内で
設定した戻り値などが格納されて返ってくるのですが
ReleaseでビルドしたDLLに置き換えると★の行でmHandleにNULLが
返されてしまいます。。。

ITO 2009/11/09(月) 16:00:51
>GlobalAlloc、HeapAlloc等は使用していない状況です。
 DLL内の処理で使っていないですか。
 説明不十分でした。 DLL内の処理です。
 GlobalAlloc、HeapAlloc等だけでなく、「CloseHandle」等も
 呼び出していますか。
 つまり、後処理を行なっていないところはないか調べておいたほうがいいと
 思います。

>暗黙的リンクも、DebugビルドのDLLで正常に動作しているので何とも。。。
ReleaseでビルドしたDLLでも動作しますか?

FlatWest 2009/11/09(月) 16:02:10
エラー値が ERROR_DLL_INIT_FAILED なのだから DllMain()のほうも検証してみては?

仲澤@失業者 2009/11/09(月) 16:43:18
FlatWestさんに一票

1.DllMain( HINSTANCE i, DWORD r, LPVOID v)の「r」に依存した
  処理を全てコメントアウトしてください。なければOK。
2.エクスポートされる関数を1つだけにして、他を全て
  コメントアウトしてください。
3.当然*.defファイルの定義も上と矛盾しないようにします。
4.EXE側でのGetProcAddress()は残した関数だけにします。
5.mHandle = ::LoadLibrary( "DLL名" ); のDLL名の部分から
  パスの指定を削除します。つまり当該DLLがMyDllなら
  ::LoadLibrary( "MyDll.dll" ); とします。
6.プロセスが残らないように、PCをリスタートします。
7.EXEの実行ファイルのあるフォルダに当該のDLL(デバッグ/リリース)が
  それぞれコピーされていることを確認してデバッグ実行します。

と、やってみてはどうでしょう。

仲澤@失業者 2009/11/09(月) 16:51:20
すみません。

1.DllMain( HINSTANCE i, DWORD r, LPVOID v)の「r」に依存した
  処理を全てコメントアウトしてください。なければOK。
  戻り値は常にTRUEにします。

に修正

bun 2009/11/09(月) 17:10:42
直接の回答ではないですが、
私が以前にはまったのは、エクスポートされた関数の
関数実体の引数とtypedefで指定した引数が違っていたときです。
やはり、デバッグ版は正常動作し、リリース版は異常終了しました。

そのときは、
確か関数実体側でint型の省略可能な引数(= 0とか指定するやつね)を
付け足して、省略可能な引数なんだから、試しにtypedef側は指定な
しでやってみてデバッグ版が動作したから、大丈夫だ(^-^)といい気
になってて、ある日リリ−ス版が落ちたときには、そんなこと忘れて
て、大パニックになりました(笑)。

nakajima 2009/11/09(月) 17:12:44
>>ITOさん
いえいえ、こちらこそ申し訳ございません。
DLL内部では、動的なメモリ確保/解放をcalloc()とfree()にて行っており
CloseHandle()は、どこにも記載されていない状態でした。
また、ReleaseでビルドしたDLLを暗黙的リンクで呼び出すと、下記のエラーとなりまし
た。。。
「アプリケーションを正しく初期化できませんでした(0xc0000142)。[OK]をクリックして
アプリケーションを終了してください。」
「ハンドルされていない例外 は テスト用MFCプログラム名.exe (NTDLL.DLL)にありま
す:0xC0000142: DLL Initialization Failed。」

>>FlatWestさん
DLL側のコーディングは下記のような状態で。。。
---
void 関数名( 構造体A *paramIn, 構造体B *paramOut )
{
    (略)
}

extern "C"
void APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID 
lpReserved )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
            (static変数のインクリメント処理)
            break;
        case DLL_PROCESS_DETACH:
            (static変数のデクリメント処理)
            break;
    }
    return;
}
---

.DEFファイルにて"関数名"を外部から使用できる設定にしています。
---
LIBRARY     "関数名"
DESCRIPTION '関数名 Win32 Dynamic Link Library'

SECTIONS
    .shared1  READ WRITE SHARED

EXPORTS
    ; 明示的なエクスポートはここへ記述できます
    関数名  @1
---

ひょっとして何か根本的に間違っていたりしますでしょうか。。。

nakajima 2009/11/09(月) 17:13:09
[[解決]]
>>仲澤@失業者さん
ありがとうございます!
ReleaseでビルドしたDLLが上手く動作しました!!!

>>1.DllMain( HINSTANCE i, DWORD r, LPVOID v)の「r」に依存した
>>  処理を全てコメントアウトしてください。なければOK。
>>  戻り値は常にTRUEにします。
>>2.エクスポートされる関数を1つだけにして、他を全て
>>  コメントアウトしてください。
>>3.当然*.defファイルの定義も上と矛盾しないようにします。
>>4.EXE側でのGetProcAddress()は残した関数だけにします。
>>5.mHandle = ::LoadLibrary( "DLL名" ); のDLL名の部分から
>>  パスの指定を削除します。つまり当該DLLがMyDllなら
>>  ::LoadLibrary( "MyDll.dll" ); とします。
>>6.プロセスが残らないように、PCをリスタートします。
>>7.EXEの実行ファイルのあるフォルダに当該のDLL(デバッグ/リリース)が
>>  それぞれコピーされていることを確認してデバッグ実行します。

1.return;のみ残してすべてコメントアウトしてみました。
2.元の状態がDLL内に関数が1つだけだったので変更しませんでした。
3.*.defファイルも関数1つのみだったので変更しませんでした。
4.呼び出しexe側でのGetProcAddress()も関数1つのみでしたので、そのままです。
5.::LoadLibrary( "DLL名" );の「"DLL名"」部分はパスを含んでいない記述
  でしたので、これもそのままにしました。
6.PCを再起動する前に試行してみました。
7.ReleaseでビルドしたDLLが上手く動作しました!!!

さらに検証してみた所、DllMain()内の
・(static変数のインクリメント処理)
・(static変数のデクリメント処理)
の部分のみコメントアウトする事でも、ReleaseでビルドしたDLLが正常に
動作しました。

まだ根本的な理由と仕組みが理解できていない状態ではありますが
本件を「解決」にさせていただきたいと思います。

>>ご意見をくださったみなさま
ほんとうにありがとうございました。

nakajima 2009/11/09(月) 17:14:40
>>bunさん

まさに
>>デバッグ版が動作したから、大丈夫だ(^-^)
の状況でした。(大汗
ありがとうございました。

nakajima 2009/11/09(月) 17:42:23
ちなみに、DLL内にて下記のようなコーディングになっておりまして
★の行のみコメントアウトする事でReleaseでビルドしたDLLが
呼び出せるようになりました。

---
#pragma data_seg(".shared1")
static UINT カウンタ = 0;
#pragma data_seg()
 ・
 ・
 ・
extern "C"
void APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID 
lpReserved )
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
★          ++カウンタ;
            break;
        case DLL_PROCESS_DETACH:
★          --カウンタ;
            break;
    }
    return;
}
---

仲澤@失業者 2009/11/09(月) 19:41:07
一種の参照カウンタを共有領域におきたいという気持ちのコードですね。

やったことありませんが。名前つきデータセクションを使用する場合は
*.defファイルで指定(SECTIONS)するか、
リンカーに教える(/SECTION)必要があるそうです。
その部分が欠落しているのではないでしょうか。

そうするとリリースだけで動かなくなるんですね。「10へぇ〜」

仲澤@失業者 2009/11/09(月) 19:46:53
あれ? 良く見ると*.defに指定がありますねぇ。不思議だ。

ITO 2009/11/09(月) 20:48:54
http://msdn.microsoft.com/ja-jp/library/sf9b18xk(VS.100).aspx
defファイル以外に、リンクの設定も必要みたいですね。

subaru 2009/11/10(火) 11:27:34
DllMain の戻り値は BOOL型で
DLL_PROCESS_ATTACH のときは TRUE を返さなければいけないのでは?
このコードで動く方が不思議な気がしますが。。。

nakajima 2009/11/10(火) 16:48:09
>>仲澤@失業者さん
>>ITOさん
>>subaruさん
ありがとうございます!!!

subaruさんのおっしゃる通り、DllMain()をvoidからBOOLに変更し
TRUEを返すように変更する事で、上記★のカウンタ操作をコメント
アウトする事なく、ReleaseビルドしたDLLが正常に動作しました。


重ね重ね、本当にありがとうございました。

古いスレッドには追加発言できません。