実行中のEXEファイルのフルパスが欲しい

[戻る]
シャノン 2004/07/26(月) 20:50:44
OS は WinXP Pro です。

タイトルの通りなのですが、例えばメモ帳ならば
C:\Windows\System32\Notepad.exe
という文字列が欲しいのです。

CreateToolhelp32Snapshot
Process32First / Next
を使って、プロセスの列挙はできます。

その列挙したプロセスのそれぞれに対して、
Module32First / Next
を呼んでやることで、プロセスが使用しているモジュールも列挙できます。
ここから、モジュールのフルパスを得ることは可能です。

問題は、複数あるモジュールの中から、EXE をひとつ特定することなのですが。

よく見かける実装では、Module32First で引っかかったもの、つまり
対象のプロセスがロードしている一番最初のモジュールを EXE であるとしています。
が、これは正しいのでしょうか?
Module32First の説明にも EnumProcessModules の説明にも、最初のモジュールが
EXE であるとは書かれていません。

DLL を作り、ロードアドレスを 0x00400000 よりも前にする実験を試みたことも
ありますが、EXE より前に配置されることはありませんでした。
しかし、やはり MS の文書なくしては、確固たる根拠足り得ないと思います。

列挙されたモジュールの中から EXE を特定する方法、あるいは、
列挙されたモジュールの最初の一つが EXE であるという公式文書の存在、
どちらかご存知の方がいらっしゃいましたら、どうかご教示願えませんでしょうか。

シャノン 2004/07/26(月) 20:54:11
モジュール ID で特定できないかと思い、こんな(下記参照)コードを書いてみました
が、※ の部分で一度として一致することはありませんでした。

    HANDLE hSnapshot1 = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
    if( hSnapshot1 == INVALID_HANDLE_VALUE )
    {
        return;
    }

    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof( PROCESSENTRY32 );

    BOOL bRet = Process32First( hSnapshot1, &pe32 );
    while( bRet )
    {
        HANDLE hSnapshot2 = CreateToolhelp32Snapshot( 
                TH32CS_SNAPMODULE, pe32.th32ProcessID );
        if( hSnapshot2 == INVALID_HANDLE_VALUE )
        {
            goto NEXTPROCESS;
        }

        MODULEENTRY32 me32;
        me32.dwSize = sizeof( MODULEENTRY32 );

        bRet = Module32First( hSnapshot2, &me32 );
        while( bRet )
        {
            // ※ ここが一致すればいいんだけど…しないの
            if( pe32.th32ModuleID == me32.th32ModuleID )
            {
                break;
            }

            me32.dwSize = sizeof( MODULEENTRY32 );
            bRet = Module32Next( hSnapshot2, &me32 );
        }

        CloseHandle( hSnapshot2 );

        if( ! bRet )
        {
            goto NEXTPROCESS;
        }

        // このへんで、EXE ファイルをごにょごにょする
NEXTPROCESS:

        pe32.dwSize = sizeof( PROCESSENTRY32 );
        bRet = Process32Next( hSnapshot1, &pe32 );
    }

    CloseHandle( hSnapshot1 );

ぽこ 2004/07/26(月) 21:07:52
GetModuleFileName()では駄目ですか?

シャノン 2004/07/26(月) 22:52:30
レスありがとうございます。

> GetModuleFileName()では駄目ですか?
自プロセスではなく、現在システムで実行されている全プロセスについてなんです。

で、WinXP から用意された新関数「GetProcessImageFileName」でできるかもしれませ
ん。

シャノン 2004/07/26(月) 23:03:11
一応、取得はできました。

が、

期待した結果としては C:\WINDOWS\System32\ … となってほしいところが、
\Device\HardDiskVolume1\WINDOWS\system32\ のような結果になってしまいました。

これを通常の、C:\WINDOWS\ …のようにするには、どうすればよいのでしょうか?

ぽこ 2004/07/27(火) 00:03:22
1.EnumProcesses()でプロセスIDの一覧を取得する。
2.OpenProcess()でプロセスのハンドルを取得する。
3.EnumProcessModules()でプロセス内のモジュールハンドルを取得する。
4.GetModuleFileEx()でファイル名を取得する。

VS.net2003にサンプルがありました。
丸写しではありませんが、参考までに。
#Win9x系では未サポートのAPIを使用しています。


#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include "psapi.h"

int _tmain(int argc, _TCHAR* argv[])
{
    DWORD ProcessArray[1024];
    DWORD Needed;
    DWORD ProcessNum;
    char szProcessName[MAX_PATH] = "unknown";

    //1.
    if(! ::EnumProcesses(ProcessArray, sizeof(ProcessArray), &Needed) ) {
        return 0;
    }

    ProcessNum = Needed / sizeof(DWORD);

    for(int i = 0; i < ProcessNum; ++i) {
        //2.
        HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION 
                                       | PROCESS_VM_READ,
                                       FALSE, 
                                       ProcessArray[i]);
       if (NULL != hProcess ) {
            HMODULE hMod;
            DWORD cbNeeded;

            //3.
            if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), 
                 &cbNeeded) )
            {
                //4.
                GetModuleFileNameEx( hProcess, hMod, szProcessName, 
                                   sizeof(szProcessName) );
            }
            else continue;
        }
        else continue;

    printf( "%s (Process ID: %u)\n", szProcessName, ProcessArray[i] );

    CloseHandle( hProcess );

    }

    getchar();
    return 0;
}

あいる 2004/07/27(火) 09:40:28
たしか、CodeProjectでそんなの見かけた記憶があったので、
ちょっと検索してみましたぁ〜

http://www.codeproject.com/threads/processapi.asp

中身までは詳しく見てないですが、サンプルProc1ではexe名を拾ってくれるようです。

シャノン 2004/07/27(火) 09:53:52
ぽこさん>
申し訳ありませんが、最初の書き込みをよくお読みいただきたく存じます。
モジュールを全て列挙するのはできておりますが、それは EXE だけでなく、
多数の DLL も一緒に拾ってきてしまいます。
その中から、プロセスのメインモジュールである EXE を特定する方法を
知りたいのです。

あいるさん>
ありがとうございます。
サンプルを拝見しましたが、希望に沿うものではないようです。
EXE ファイル名は取ってきてくれますが、フルパス名が欲しいのです。

あいる 2004/07/27(火) 10:10:03
> サンプルを拝見しましたが、希望に沿うものではないようです。
> EXE ファイル名は取ってきてくれますが、フルパス名が欲しいのです。

サンプルProc1しか私も見てなかったので、それしか書かなかったのですが、
サンプルProc2を見てみたら、フルパスで取ってるようですよ〜

# 提示する前にちゃんとみておけばよかった(涙

ぽこ 2004/07/27(火) 10:18:52
EmunProcessModules()について既に調査済みだったんですね。
大変失礼しました。

euro2004 2004/07/27(火) 10:19:16
マイクロソフトのサポート技術情報に下に挙げる一文が記載されてます。
( http://support.microsoft.com/default.aspx?scid=kb;EN-US;175030 )

Remember that a process does not really have a name, but that the first module 
in the process is going to be the executable of the process.

これをexiteの翻訳サイトで和訳したら、
「プロセスが実際に名前を持っていないが、プロセスでの第1のモジュールが
実行可能だろう(プロセスの)ことを覚えておいてください。」
となりました。

参考になりますか?

シャノン 2004/07/27(火) 10:39:50
[[解決]]
あいるさん>
Proc2 は、やはりロードされているモジュールの最初の一つが EXE であると仮定してい
ます。
求めているのは、その仮定が正しいという根拠なのです。が…

euro2004さん>
日本語版の資料でもその一文を確認しました。見落としていたとは誠に恥ずかしい限り
です。

(再び)あいるさん>
というわけで、正しかったようです。

ぽこさん、あいるさん、euro2004 さん
レスありがとうございました。


なお、PROCESSENTRY32 の szExeFile メンバと MODULEENTRY32 の szExePath メンバの
比較では望む結果を得られなかったことを報告しておきます。
Win9x 系では PROCESSENTRY32.szExeFile に EXE のフルパスが入りますが、NT 系では
ファイル名しか入りません。
(こんなことがあまりあるとは思えませんが)プロセスが、別のパスにある自分と同じ
ファイル名のファイルをロードしていると、PROCESSENTRY32.szExeFile と同じファイル
名を持つモジュールが複数ロードされている可能性があるためです。

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