GetProcessImageFileName()で取得できるパス名について

[戻る]
MistyGreen 2010/10/19(火) 16:01:34
開発環境: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プロセスの情報も取得できました。

aetos 2010/10/20(水) 02:50:53
対象の 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 とかでしょうか?

MistyGreen 2010/10/20(水) 08:16:09
アドバイスありがとうございました。

「QueryDosDevice」というAPIはさっそく試してみたいと思います。

「QueryDosDevice」のヘルプのサンプルの欄に「Obtaining a File Name From a File 
Handle」
というのがありますが、これが流用できそうな感じですね。

MistyGreen 2010/10/20(水) 08:19:24
すみません。ご質問の回答を忘れていました。

> 対象が 64bit で、作っているアプリは 32bit とかでしょうか?

はい。今回の機能はDLLの中に追加する予定で、そのDLLはグローバルなキーボードフッ
クなどを
既に実装している「32bitの常駐DLL」です。

aetos 2010/10/20(水) 10:30:15
> 今回の機能はDLLの中に追加する予定で、そのDLLは
> グローバルなキーボードフックなどを既に実装している「32bitの常駐DLL」です。

キーボードフックって SetWindowsHookEx で?
64bit プロセスは 32bit DLL を読み込めませんが大丈夫でしょうか?
WH_KEYBOARD だと、フック DLL を対象プロセスにロードさせるはずなので。
WH_KEYBOARD_LL ならいけるのかな。

aetos 2010/10/20(水) 12:14:39
有用な情報かどうかわかりませんが… RtlNtPathNameToDosPathName という関数があり
ます。
が、Undocumented なもので、MSDN には載っていません。使い方もわかりません。

一応、あまり役に立たなかった参考 URL を挙げておきます。
http://forum.sysinternals.com/topic7126.html

aetos 2010/10/20(水) 13:09:25
いろいろやってみました。

・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 を使うというのも選択肢としてアリでしょう。

aetos 2010/10/20(水) 13:51:05
> ローカルであっても、ひとつのボリュームにつき複数のドライブレターを
> 振ることはできるわけで、

できないですね。嘘言ってごめんなさい。

が、本質的には同じことです。

MistyGreen 2010/10/20(水) 16:12:50
[[解決]]
> 64bit プロセスは 32bit DLL を読み込めませんが大丈夫でしょうか?

はい。読み込めないはずですが、64bitアプリでキー操作をすると、
フックプロシージャは問題なくコールされます。
どのプロセスにロードされたDLLなのかは分かりません・・・

「RtlNtPathNameToDosPathName」は気になるところですが、
確かにドキュメントが見つかりませんね。

ネットワーク上のアプリは取得できませんが、とりあえず
「QueryDosDevice」を使用した方法でいってみようと思います。

いろいろとアドバイスを頂きましてありがとうございました。

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