VS2008、Win7で開発をしております。
現在、とあるDLLを作成しております。
・当DLLがロードされると、いくつかのAPIを自前の関数に置き換える
・GDI系のAPI(CreateBitmap等)が呼ばれた際には、生成されたハンドルを記憶。
・GDI開放系のAPI(DeleteObject等)が呼ばれた際には、記憶したハンドルを忘れる。
・当DLLアンロード時には、記憶したハンドルをログファイルに出力。
と言ったような、GDIリソースのリークを調べる様なDLLです。
基本部分は完成したのです。しかし、以下の様な問題が発生します。
具体的には、MFCのCRectTrackerを使用しているEXEに当DLLをロードさせます。
すると、CRectTrackerはコンストラクタでglobalなHBITMAPとHPENを作成します。
作成後、atexitに「globalなBITMAPとPENを開放する関数」AfxTrackerTermを登録しま
す。
EXE終了時、当DLLがアンロードされた時にGDIリーク検知機構が動くのですが
先ほどのCRectTrackerのAfxTrackerTermが実行されていないため、リークした、と誤検
知してしまいます。
この現象の原因は、MFCxx.dllより先に、当DLLがアンロードされるから、と判断しまし
た。
DLLのアンロード順を指定する方法はありませんでしょうか?
尚、文中の当DLLがMFCxx.dllを使用しない事は確認しています。
また、ここでのロード/アンロードはDLL_PROCESS_ATTACH/DETACHを意味しています。
主題は「DLLのアンロード順を指定する方法はありませんでしょうか?」ですが
「EXEの実行から、DLLのロード/アンロード」が解る本や資料
でも勿論問題ないです。
DllMain ではダメかいな?
>tetrapod さん
返信ありがとうございます。
DllMainで処理はしています。が件の現象となっています。
それはまさにMFCxx.dllがまだアンロードされていないから、だと判断しています。
そして、自己解決しそうですが、作成中のDLL(仮にGdiLeak.dllとします)を
動的リンクから、静的リンクに換えた所、MFCxx.dllより先にロードされ
MFCxx.dllより後にアンロードされた様です。
(納得といえば納得の挙動ですが...)
出来れば、動的リンクしたDLLをMFCxx.dllより後にアンロードしたいのですが
何か良い案があれば、ヒント頂けませんか?
今はGdiLeak.dllのロード時に、さらに自分をLoadLibraryして参照カウントを無理やり
一個増やすという技が使えないかな、と思っています。
急ぐ案件でも無いのでじっくり考えようと思います。
ありがとうございました。
スタティック変数の初期化の順序の話のような気がします。
class GdiLeakModule; // ctorでLoadLibrary,dtorでFreeLibrary
をつくり、静的なインスタンスを1つ作れば、何とかなりそうな。。
-------
私が思いつく、目一杯の手は独自のエントリーポイントを作ることです。
やったことありませんが、こんな感じと思います。
int MyEntryPoint()
{
LoadLibrary(GdiLeak.dll);
int result = WinMainCRTStratup();
if(Leak()) // GdiLeak.dllの関数
MessageBox();
FreeLibrary(hModule);
return result;
}
# リンカーオプションでエントリーポイントをMyEntryPointに設定
>ロマ さん
返信ありがとうございます。
エントリーポイントを作ること、考えてみたのですが
書いて頂いた参考コードのWinMainCRTStartupを実行しようとすると
(UnicodeのWindowsアプリなので、こちらではwWinMainCRTStartupもしくは
_tmainCRTStartupになっています)
宣言が無いためビルドエラーになってしまいます。
勿論、TCHAR.hをIncludeし、自分で_tWinMainをコールする事は可能ですが
それではソースコードのコピーが発生し、無駄になるかなと思い
_tmainCRTStartupを実行する(コンパイルが通る)方法を探しています。
何かIncludeするべきファイルがあるのでしょうか?
宜しければご教授ください。
お願いします。
> 宣言が無いためビルドエラーになってしまいます。
マップファイルを見ると、wWinmainCRTStartupは外部シンボルなので
extern C wWinMainCRTStartup();
MyEntryPointも名前の変形を避けるなら同様に。
マップファイルを見ると、
wWinMainCRTStartupは外部シンボルなので
extern C wWinMainCRTStartup();
MyEntryPointも名前の変形を避けたければ同様に。
ごめん、ダブった
連投すまんです
エントリーポイントを使うのはあまりお勧めできません。
理由は、C/C++の枠を越えているので、例外や後片付けを
自分でやる必要があるからです。
楽しみで色々試すのはよい事ですが、止むに止まれぬ場合のみ実施すべきと思います。
それよりも、
MyApp.cppで
CMyApp theApp;
// 私の1/20投稿のクラスのインスタンスをファイルスコープで追加
GdiLeakModule LeakModule;
のようにしておけば、
WinMain開始後に生成されたstaticオブジェクトの寿命より
LeakModuleの寿命のほうが長いので、キャッチできると思うのですが。
まぁ、ファイルスコープのCPenがあるかもしれないので、
それも捕らえたいということなのでしょうか。
>ロマ さん
何度も有難うございます。
ビルドエラーの件
>extern C wWinMainCRTStartup();
で解決出来ました。
まだexternや呼び出し規約を理解出来てませんでした。
もっと勉強しなければいけないですね。
有難うございます
エントリーポイントを作るか否かについてですが両方試してみようと思います。
勉強がてらに色々と実験してみたいのです。
ただ恐らく
>まぁ、ファイルスコープのCPenがあるかもしれないので、
>それも捕らえたいということなのでしょうか。
という具合に、出来る事なら「全て」を捕まえたいので
エントリーポイントを用意する事になりそうです。
(自分のコードが存在しない箇所を捕まえて意味があるのかは疑問ですが。)
しばらく実験コードを書くことに入るので、一先ず解決とさせていただきたいです。
tetrapodさん、ロマさん、ありがとうございました。