現在実行中のプログラムがどこのモジュールに所属するか調べる
で、なぜ、そんなことをやっているかというと、スタティックリンクライブラリのある関数が、リンクされた結果できたモジュールを実行時に検出するという問題を解決するためだ。
何を書いているのか分かり難いな。
まぁ、会社の仕事なんだけけど。
うちはSDKを売っている会社で、さらにそのSDKがスタティックリンクライブラリで、ユーザーの会社が、リンクしてDLLやら、EXEにする。そのときに、その実行形式に対してライセンスチェックをしたいという要求。
あんまり考えずに実装すると、
WCHAR modulePath[MAX_PATH];
GetModuleFileName(NULL, modulePath, MAX_PATH);
なんだろうけど、これだとEXEのパスは取れても、実際にSDKをつかって作成したモジュールの名前は取れないかもしれない。たとえば、Photoshopのプラグインなどを作成した場合に、このコードだと、Photoshopの実行ファイルのパスを返してしまう。
プロセスにロードされたモジュール一覧は、CreateToolhelp32Snapshot, Module32First, Module32Nextで取得できる。Module32First/Module32Nextは、MODULEENTRY32という構造体に書くモジュールの情報を返してくれるが、この構造体の、modBaseAddr, modBaseSizeが、モジュールのロードされたアドレス、そしてサイズを示している。従って、
CString GetModuleNameFromAddress(void* address) { HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, GetCurrentProcessId()); MODULEENTRY32W me; ZeroMemory(&me, sizeof(me)); me.dwSize = sizeof(me); if(Module32FirstW(hSnapshot, &me)) { do { if(me.modBaseAddr <= address && address < me.modBaseAddr + me.modBaseSize) { CloseHandle(hSnapshot); return me.szExePath; } } while(Module32NextW(hSnapshot, &me)); } CloseHandle(hSnapshot); return L""; }
といった関数を書けば、任意のアドレスに対するモジュールを知ることができる。CreateToolhelp32Snapshotが返すモジュール一覧が十中八九、ソートされているという事実を加味すれば、二分探索もできるけど、別にそこまでしなくて良いだろう。
CString moduleName = GetModuleNameFromAddress(CloseHandle);
とかすると、C:\Windows\system32\kernel32.dllと返ってくる。しかしながら、
CString moduleName = GetModuleNameFromAddress(CreateToolhelp32Snapshot);
とすると、kernel32.dllとは返ってこない。すべてlib内で実装されているのか、それとも、実はAPIではないのか。このあたりは非常に悩ましい。Rtl系のAPIではないので、一応、kernel32.libからロードされていると思うのだが・・・。と思って、
HMODULE hKernel32 = LoadLibrary("kernel32.dll"); CString moduleName = GetModuleNameFromAddress(GetProcAddress(hKernel32, "CreateToolhelp32Snapshot")); CloseHandle(hKernel32);
としてみると、ちゃんとkernel32.dllと返ってくる。本筋とは違うので、リロケーションのためのスタブでも入っているんだろう。と思うことにした。
ということで本来の作業に戻ると、
CString moduleName = GetModuleNameFromAddress(_ReturnAddress());
で現在の位置がどのモジュールに属しているかわかるはず。
一応、目的は達成できた・・・と思いきや、
1>c:\program files\microsoft visual studio 8\vc\include\intrin.h(912) : error C2733: second C linkage of overloaded function '_interlockedbittestandset' not allowed 1> c:\program files\microsoft visual studio 8\vc\include\intrin.h(912) : see declaration of '_interlockedbittestandset' 1> c:\program files\microsoft visual studio 8\vc\include\intrin.h(913) : error C2733: second C linkage of overloaded function '_interlockedbittestandreset' not allowed 1> c:\program files\microsoft visual studio 8\vc\include\intrin.h(913) : see declaration of '_interlockedbittestandreset'
という謎のエラー群が。どうやら、_interlockedbittestandsetが何度も定義されているのが問題らしい。うーん。ググったけど、同じ症状の人が一人いることがわかったぐらい。VS2005 + Windows SDK RTMなんて、普通の環境だと思うのだけれどもなぁ。
組み込み関数なのでちょっと不安だなぁと思いながら思い切って、
#include <intrin.h>
を
extern "C" void * _ReturnAddress(void);
と置き換えてみる。ビルドが通った。少なくともリンクが正常にできたわけで、ちゃんと認識されているんだろう。実際、ちゃんと動作する。