プロセス ID からそのプロセスの実行可能ファイルが置かれている場所の絶対パスが知りたい場合があります。
MSDN をみると、GetProcessImageFileName を使えばよさそうなのですが、"C:\Program Files\hoge\example.exe" なんてのを期待していると "\Device\HarddiskVolume3\Program Files\hoge\example.exe" といった謎の形式で返ってきます。"\Device\HarddiskVolume3" の部分は物理的な所在を意味しているようですが、ここでは普通にドライブ名が欲しい。
調べてみると QueryDosDevice で "C:" → "\Device\HarddiskVolume3" の変換ができるらしい。
これらを組み合わせればできるんじゃね? ということで書いてみました。
// VC2010 にてビルド、XP 以降で動作
#include <conio.h>
#include <unordered_map>
#include <string>
#include <windows.h>
#include <atlstr.h>
#include <psapi.h>
#pragma comment ( lib , "psapi.lib" )
//プロセス ID から絶対パスを取得する
bool GetFullPathFromProcessId( DWORD dwPID, CAtlString* pstrPath )
{
// プロセスハンドルを取得
HANDLE hProcess = ::OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwPID );
if ( !hProcess ) {
return false;
}
bool bOk = false;
// デバイス名('\Device\HarddiskVolume3') => ドライブ文字('C:') の変換マップを作成
std::unordered_map< std::wstring, std::wstring > mapDeviceDrive;
{
WCHAR szDrive[] = L" :";
WCHAR szDevicePath[ 4096 ] = {};
const WCHAR lpszAlpha[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for ( size_t i = 0; i < _countof( lpszAlpha ); ++i ) {
szDrive[ 0 ] = lpszAlpha[ i ];
DWORD dwCopiedLength = ::QueryDosDeviceW( szDrive, szDevicePath, _countof( szDevicePath ) );
if ( dwCopiedLength ) {
mapDeviceDrive.insert( std::make_pair( std::wstring( szDevicePath ), std::wstring( szDrive ) ) );
}
}
}
// 実行可能ファイル名を取得
WCHAR szImage[ MAX_PATH ] = {};
DWORD dwCopiedLength = ::GetProcessImageFileNameW( hProcess, szImage, _countof( szImage ) );
if ( dwCopiedLength ) {
// デバイスパスをドライブ名に置換
CAtlString strImagePath( szImage );
for each ( const auto& item in mapDeviceDrive ) {
// 先頭でのみマッチさせる
if ( strImagePath.Left( item.first.size() ) == item.first.c_str() ) {
strImagePath.Delete( 0, item.first.size() - 2 );
strImagePath.SetAt( 0, item.second[ 0 ] );
strImagePath.SetAt( 1, L':' );
break;
}
}
// ショートファイル名をロングファイル名に変換
WCHAR szLongPath[ MAX_PATH * 2 ] = {};
dwCopiedLength = ::GetLongPathNameW( strImagePath, szLongPath, _countof( szLongPath ) );
if ( dwCopiedLength ) {
*pstrPath = szLongPath;
bOk = true;
}
}
::CloseHandle( hProcess );
return bOk;
}
int main()
{
DWORD dwPID = 0;
for ( ;; ) {
wprintf( L"process-id:" );
if ( wscanf_s( L"%lu", &dwPID ) != 1 ) {
break;
}
CAtlString strPath;
if ( GetFullPathFromProcessId( dwPID, &strPath ) ) {
wprintf( L"=> %s\n", strPath.GetString() );
}
}
return 0;
}