【问题标题】:How do I GetModuleFileName() if I only have a window handle (hWnd)?如果我只有一个窗口句柄 (hWnd),我如何 GetModuleFileName()?
【发布时间】:2010-09-21 13:59:01
【问题描述】:

我正在尝试获取位于我的 C# 2.0 应用程序之外的窗口的可执行文件的名称。我的应用当前使用来自“user32.dll”的 GetForegroundWindow() 调用获取窗口句柄 (hWnd)。

根据我已经能够做的挖掘,我想我想使用 GetModuleFileNameEx() 函数(来自 PSAPI)来获取名称,但 GetModuleFileNameEx() 需要一个进程的句柄,而不是一个窗口。

是否可以从窗口句柄中获取进程句柄? (需要先获取窗口的线程句柄吗?)

编辑了第一句话,让我更清楚我想要做什么。

更新!这是我发现对我有用的 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());
}

【问题讨论】:

  • 对我来说没问题。在 OpenProcess() 之后需要 if ((int)hProcess != 0)

标签: c# winapi hwnd getmodulefilename


【解决方案1】:

现在已经为同一个问题苦苦挣扎了一个小时,还通过使用 GetModuleFileNameEx 将第一个字母替换为 ?。 最后使用 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;
}

【讨论】:

  • 我认为这是迄今为止提供的最佳解决方案。没有奇怪的字符,最重要的是,不依赖不一致的 GetModuleFileNameEx 函数,该函数在 Windows 7 上位于另一个 DLL(不是 psapi 而是 kernel32)中。在这种情况下,绝对首选使用 .Net 类。完美运行。
  • 我意识到这个线程已经过时了,但是对这个建议的解决方案有一点评论。如果您尝试从 32 位进程获取 64 位进程的文件名(反之亦然),这将不起作用。
【解决方案2】:

您可以调用GetWindowThreadProcessId,这将返回与窗口关联的进程。

由此,您可以调用OpenProcess 来打开进程并获取进程的句柄。

【讨论】:

  • 嘿,欢迎来到堆栈溢出 - 我一直是您的长期读者
【解决方案3】:

如果您在 Windows 64 位平台上运行,您可能需要改用 QueryFullProcessImageName。这将返回一个用户样式路径,而 GetProcessImageFileName 返回需要使用 NtQuerySymbolicLinkObject 或 ZwQuerySymbolicLinkObject 转换的系统样式路径。

一个庞大的示例函数 - 建议分解成可重复使用的位。

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();
}

【讨论】:

    【解决方案4】:

    您到底想做什么?您可以使用GetWindowThreadProcessId() 获取创建窗口的进程的进程ID,然后使用OpenProcess() 获取进程句柄。但这似乎很笨拙,我觉得有一种更优雅的方式来做你想做的事。

    【讨论】:

    • 也许这对您来说似乎很笨拙,但是声誉为 1 的人在您前 1 分钟给出了完全相同的答案,而且我认为他的声誉不会长期保持在 1。
    • 是的,我也觉得它很笨拙。我正在尝试跟踪哪个窗口具有焦点。当然,窗口句柄和标题栏名称会发生​​变化,但应用程序 exe 名称不会。
    • 好的,但无论如何拉里奥斯特曼给出了相同的答案,所以它可能不如所有替代方案那么笨拙。而且他的名声不再是 1。
    • Larry Osterman 在 Windows 编程领域的终身声誉与 Raymond Chen 不相上下
    • Larry Osterman 的回答刚刚被接受,所以现在他的声誉是 46。他在 SO 之前的一生不算在内。
    【解决方案5】:

    试试这个来获取可执行文件的文件名:

    C#:

    string file = System.Windows.Forms.Application.ExecutablePath;
    

    制造

    【讨论】:

    • 现在这个问题有点老了,但当时我正试图根据进程 ID 获取 另一个 进程的 EXE 名称。 (不是正在运行的那个。)所以在这种情况下,这不是有用的答案。
    猜你喜欢
    • 2020-10-22
    • 1970-01-01
    • 1970-01-01
    • 2013-02-08
    • 2011-06-16
    • 2011-08-12
    • 1970-01-01
    • 2013-02-10
    • 1970-01-01
    相关资源
    最近更新 更多