Seasons.NET

ちょっとした技術ブログです

wxEntryをDll側に持たせてコールする場合の注意点

まずやりたいこと。

DLL側にwxWidgetsをリンクさせて、UIをDLL側に持たせておく。
んでこのDLLをリンクさせるDLLがあり、exe側からは、DLLでexportした
UI表示関数をコールして、UIを表示する。

とまぁ、DLLとexeを作って、明示的リンクor暗黙的リンク
すればいいと思いきやこれが嵌る。


まず、DLL側のマニフェスト埋め込みでYesにしてしまうと、
SideBySideで、6.0.0.0のcomctl32.dllのみロードするようになってしまう。
まぁこれはこれで正常なんですけどね、wxWidgets側のGUIで不都合が出てしまう。
なぜかというと、wxWidgets側では、TrackMouseEvent等をcomctl32.dllをloadlibraryして
ゲット!!する仕様になっているんですねぇ〜。これが・・・
window.cpp見ればそうなってます。

まずDLLの仕様のおさらいとして、

LoadLibraryは、参照カウント方式をとっていて、LoadLibraryをした時に、参照カウントが1以上なら、
カウントが増えるだけで、FreeLibraryは、それを減らすだけなのです。
んで、参照カウントが1の時にプロセスから始めて解放を行います。

とまぁこんな仕様になっとりまして、
SideBySideの恩恵を受けると、6.00しかロードされません。
いやらしいのが、LoadLibraryをDll側から行うと、5.82の方(dllの検索順序に従ってロードされる)がロードされてしまい、
これは、プログラム起動時にはロードされてないんですよね・・・
よって、wxWidgets側で、Load〜Freeする行為は、毎回プロセスに確保、解放していて、
その後のコードでcomctl32.dllの空間を使うようなコードは、アクセス違反になります。

これの解決方法としては、

マニフェスト埋め込みをNoにして、しまうこと。
こうすると、5.82と6.00の両方ロードされます。
この5.82がロードされるというのが【重要】で、これを一度ロードしておけば、
wxWidget側でLoad〜Freeしても問題ないわけです。ちゃんとプロセス空間に残っていると。

もしくは、exportする関数を以下のようにしておくか。
wxEntryコールする前にcomctrl32.dllをロードしておく。なんかキモイコードだな・・・

UI_API int ShowUI( void )
{
    HMODULE hModule = ::LoadLibrary( "comctl32.dll" );
    wxEntry( GetModuleHandle( NULL ) , NULL , ::GetCommandLine(), SW_SHOWNORMAL );
    ::FreeLibrary( hModule );
    return 0;
}

マニフェスト埋め込みについてはここを参考にさせてもらいました。
自作のプログラムを Windows XP のビジュアルスタイルに対応させる