ウィンドウハンドル(hWnd)しかない場合、GetModuleFileName()を実行するにはどうすればよいですか?
質問
C#2.0アプリケーションの外部にあるウィンドウの実行可能ファイルの名前を取得しようとしています。私のアプリは現在、" user32.dll"からのGetForegroundWindow()呼び出しを使用してウィンドウハンドル(hWnd)を取得しています。
私ができた掘り出しから、(PSAPIから)GetModuleFileNameEx()関数を使用して名前を取得したいと思いますが、GetModuleFileNameEx()はウィンドウではなくプロセスへのハンドルを必要とします。
ウィンドウハンドルからプロセスハンドルを取得することは可能ですか? (最初にウィンドウのスレッドハンドルを取得する必要がありますか?)
最初の文を編集して、私がやろうとしていることを明確にしました。
UPDATE!これは、私のために働いたことがわかったC#コードです。唯一の注意点はときどきで、ドライブ文字が「?」であるファイル/パスを返します。実際のドライブ文字の代わりに(" C"など)。 -まだ理由がわかりません。
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("kernel32.dll")]
static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);
[DllImport("psapi.dll")]
static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
private string GetWindowModuleFileName(IntPtr hWnd)
{
uint processId = 0;
const int nChars = 1024;
StringBuilder filename = new StringBuilder(nChars);
GetWindowThreadProcessId(hWnd, out processId);
IntPtr hProcess = OpenProcess(1040, 0, processId);
GetModuleFileNameEx(hProcess,IntPtr.Zero,filename,nChars);
CloseHandle(hProcess);
return (filename.ToString());
}
解決
GetWindowThreadProcessId を呼び出すことができます。ウィンドウに関連付けられたプロセスが返されます。
そこから、 OpenProcess を呼び出してプロセスを開くことができます。プロセスのハンドルを取得します。
他のヒント
1時間同じ問題に苦しんでいる今、GetModuleFileNameExを使用して、最初の文字を?に置き換えました。 Finalyは、 System.Diagnostics.Process クラスを使用してこのソリューションを考案しました。
[DllImport("user32.dll")]
public static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
void GetProcessPathFromWindowHandle(IntPtr hwnd)
{
uint pid = 0;
Win32.GetWindowThreadProcessId(hwnd, out pid);
Process p = Process.GetProcessById((int)pid);
return p.MainModule.FileName;
}
Windows 64ビットプラットフォームで実行している場合は、代わりにQueryFullProcessImageNameを使用する必要があります。これは、NtQuerySymbolicLinkObjectまたはZwQuerySymbolicLinkObjectを使用して変換する必要があるシステムスタイルパスを返すGetProcessImageFileNameと比較して、ユーザースタイルパスを返します。
1つの巨大なサンプル関数-再利用可能なビットに分割することをお勧めします。
typedef DWORD (__stdcall *PfnQueryFullProcessImageName)(HANDLE hProcess, DWORD dwFlags, LPTSTR lpImageFileName, PDWORD nSize);
typedef DWORD (__stdcall *PfnGetModuleFileNameEx)(HANDLE hProcess, HMODULE hModule, LPTSTR lpImageFileName, DWORD nSize);
std::wstring GetExeName( HWND hWnd ){
// Convert from Window to Process ID
DWORD dwProcessID = 0;
::GetWindowThreadProcessId(hWnd, &dwProcessID);
// Get a handle to the process from the Process ID
HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessID);
// Get the process name
if (NULL != hProcess) {
TCHAR szEXEName[MAX_PATH*2] = {L'\0'};
DWORD nExeName = sizeof(szEXEName)/sizeof(TCHAR);
// the QueryFullProcessImageNameW does not exist on W2K
HINSTANCE hKernal32dll = LoadLibrary(L"kernel32.dll");
PfnQueryFullProcessImageName pfnQueryFullProcessImageName = NULL;
if(hKernal32dll != NULL) {
pfnQueryFullProcessImageName = (PfnQueryFullProcessImageName)GetProcAddress(hKernal32dll, "QueryFullProcessImageNameW");
if (pfnQueryFullProcessImageName != NULL)
pfnQueryFullProcessImageName(hProcess, 0, szEXEName, &nExeName);
::FreeLibrary(hKernal32dll);
}
// The following was not working from 32 querying of 64 bit processes
// Use as backup for when function above is not available
if( pfnQueryFullProcessImageName == NULL ){
HINSTANCE hPsapidll = LoadLibrary(L"Psapi.dll");
PfnGetModuleFileNameEx pfnGetModuleFileNameEx = (PfnGetModuleFileNameEx)GetProcAddress(hPsapidll, "GetModuleFileNameExW");
if( pfnGetModuleFileNameEx != NULL )
pfnGetModuleFileNameEx(hProcess, NULL, szEXEName, sizeof(szEXEName)/sizeof(TCHAR));
::FreeLibrary(hPsapidll);
}
::CloseHandle(hProcess);
return( szEXEName );
}
return std::wstring();
}
あなたがやろうとしていることは正確に何ですか? GetWindowThreadProcessId()、続いて OpenProcess ()プロセスハンドルを取得します。しかし、これは非常に気味が悪いようであり、あなたがやりたいことを行うためのよりエレガントな方法があると感じています。
これを試して、実行可能ファイルのファイル名を取得します:
C#:
string file = System.Windows.Forms.Application.ExecutablePath;
mfg