開発環境:Windows 7, VS2010 Pro, MFC
GetProcessImageFileName()にプロセスのハンドルを渡すと、
実行ファイルのフルパス名が取得できますが、
このフルパス名は「device form」という書式になっています。
\Device\Harddisk0\Partition1\WINNT\System32\Notepad.exe
これを下記のような通常のフルパス名(win32 path format)に変換したいのですが、
何か良い方法はありませんか?
C:\Winnt\System32\Notepad.exe
ヘルプによると「QueryFullProcessImageName」というAPIを使えば
できそうなのですが、このAPIはWinVista以降でしか使えないようです。
私のアプリはWinXP以上を動作対象としています。
あるいは、「win32 path format」を「device form」に変換する方法でも
構いません。
よろしくお願い致します。
なお、
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200407/04070064.txt
上記の過去スレに似たような質問があるのですが、私の場合には、
すべてのプロセスを列挙するのではなく、フォアグラウンドアプリの
プロセスの情報(実行ファイル名)のみが必要です。
また、「EnumProcessModules, GetModuleFileNameEx」を使用したサンプルが
ありますが、これですと64bitプロセスの情報が取得できませんでした。
GetProcessImageFileNameを使うと64bitプロセスの情報も取得できました。
対象の exe ファイルがローカルドライブ上にあれば、QueryDosDevice で何とかなりま
す。
GetLogicalDrives なり GetLogicalDriveStrings なりでドライブレターを列挙し、それ
を QueryDosDevice に食わせると、例えば「C:」という文字列から
「\Device\Harddisk0」が得られるので、GetProcessImageFileName の結果のうち、
「\Device\Harddisk0」の部分を「C:」に置換してやれば OK です。
# GetLogicalDriveStrings は「C:\」のような結果を返しますので、末尾の \ は
# 消してやらないと QueryDosDevice が失敗します。
ただし、うまくいかないと言うか、なんか気に入らないケースはあります。
例えば subst で仮想ドライブを作っている場合、その仮想ドライブの実パスが取れてし
まいます(QueryFullProcessImageName でも同様)。
他に、ドライブをディレクトリにマップしている場合やリパースポイントで上手く行く
かなど、時間が取れたら検証してみます。
で、今のところ行き詰っているのが、exe がネットワーク上にある場合です。
QueryFullProcessImageName ではちゃんと \\server\share\path と UNC を取ってきて
くれますが、GetProcessImageFileName でどうにかする方法はわかりません。
> これですと64bitプロセスの情報が取得できませんでした。
というのは?
対象が 64bit で、作っているアプリは 32bit とかでしょうか?
アドバイスありがとうございました。
「QueryDosDevice」というAPIはさっそく試してみたいと思います。
「QueryDosDevice」のヘルプのサンプルの欄に「Obtaining a File Name From a File
Handle」
というのがありますが、これが流用できそうな感じですね。
すみません。ご質問の回答を忘れていました。
> 対象が 64bit で、作っているアプリは 32bit とかでしょうか?
はい。今回の機能はDLLの中に追加する予定で、そのDLLはグローバルなキーボードフッ
クなどを
既に実装している「32bitの常駐DLL」です。
> 今回の機能はDLLの中に追加する予定で、そのDLLは
> グローバルなキーボードフックなどを既に実装している「32bitの常駐DLL」です。
キーボードフックって SetWindowsHookEx で?
64bit プロセスは 32bit DLL を読み込めませんが大丈夫でしょうか?
WH_KEYBOARD だと、フック DLL を対象プロセスにロードさせるはずなので。
WH_KEYBOARD_LL ならいけるのかな。
有用な情報かどうかわかりませんが… RtlNtPathNameToDosPathName という関数があり
ます。
が、Undocumented なもので、MSDN には載っていません。使い方もわかりません。
一応、あまり役に立たなかった参考 URL を挙げておきます。
http://forum.sysinternals.com/topic7126.html
いろいろやってみました。
・exe がローカルディスク上にある場合
GetProcessImageFileName では \Device\Harddisk1\Hoge.exe のようなパスが得られ
る。
\Device\Harddisk1 を QueryDosDevice で C: に読み替えることができる。
・subst で仮想ドライブを割り当てた場合
・ディレクトリのシンボリックリンクでやった場合
・ディレクトリのジャンクションでやった場合
・マウントポイントの場合
いずれもリンク先の実体のパスが取得できる。
・ネットワーク共有上にある場合
・ネットワークドライブ上にある場合
GetProcessImageFileName では \Device\Mup\Server\Share\Hoge.exe のようなパスが得
られる。
QueryFullProcessImageName だと \\Server\Share\Hoge.exe が得られる。
QueryDosDevice でネットワークドライブを問い合わせても \Device\Mup は返ってこな
い(\Device\Mup は UNC にマップされている)。
考えてみますと、subst やネットワークの場合は言わずもがな、ローカルであっても、
ひとつのボリュームにつき複数のドライブレターを振ることはできるわけで、
QueryDosDevice や QueryFullProcessImageName を使っても、意図した結果を得られな
い可能性があるわけです。
この場合でも、EnumProcessModules であれば正しい(どこから起動したかという)パス
が取得できることは確認しました。
結果と労力の落としどころをどこに置くかの問題ではありますが、32bit 版と 64bit 版
の DLL を作って EnumProcessModules を使うというのも選択肢としてアリでしょう。
> ローカルであっても、ひとつのボリュームにつき複数のドライブレターを
> 振ることはできるわけで、
できないですね。嘘言ってごめんなさい。
が、本質的には同じことです。
> 64bit プロセスは 32bit DLL を読み込めませんが大丈夫でしょうか?
はい。読み込めないはずですが、64bitアプリでキー操作をすると、
フックプロシージャは問題なくコールされます。
どのプロセスにロードされたDLLなのかは分かりません・・・
「RtlNtPathNameToDosPathName」は気になるところですが、
確かにドキュメントが見つかりませんね。
ネットワーク上のアプリは取得できませんが、とりあえず
「QueryDosDevice」を使用した方法でいってみようと思います。
いろいろとアドバイスを頂きましてありがとうございました。