GetWindowModuleFileNameW获取窗口exe路径失败报错126
问题原因
GetWindowModuleFileNameW 本身仅能在目标窗口所属进程与当前调用进程架构一致、且目标进程模块已加载到当前进程地址空间的场景下正常返回结果,从Windows Vista开始,受UAC权限隔离、Chromium系应用(含Edge、Chrome)多进程架构、UWP应用容器隔离等机制影响,跨进程调用该接口获取窗口对应exe路径时,绝大多数场景都会触发126(找不到指定模块)错误——本质是这个接口只会在当前进程的已加载模块列表里匹配对应窗口所属模块,跨进程场景下目标模块根本不在你的进程内存里,自然找不到。
正确实现方案
按照以下步骤修改回调逻辑即可稳定拿到任意前台窗口的exe完整路径,不需要管理员权限:
- 先通过窗口句柄拿到对应进程ID,不要直接使用回调参数里的
idEventThread,该值是触发事件的线程ID,和窗口所属进程不一定绑定 - 用最小权限
PROCESS_QUERY_LIMITED_INFORMATION打开进程拿到句柄,不要用PROCESS_ALL_ACCESS这类过高权限,否则普通权限下打开系统进程、高权限运行的应用会直接失败 - 调用kernel32原生提供的
QueryFullProcessImageNameW读取进程exe完整路径,这个接口是微软官方推荐的跨进程取镜像路径方案,兼容性远高于Psapi里的GetModuleFileNameEx,支持Win10全版本、Chromium系应用、UWP应用 - 操作完成后关闭打开的进程句柄,避免句柄泄漏
修正后的回调代码
#include <windows.h> #include <iostream> // MSVC编译时默认会链接kernel32.lib,无需额外配置 void HandlerFunc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD idEventThread, DWORD dwmsEventTime){ // 过滤非窗口本身的事件,避免子控件触发回调导致拿错信息 if (idObject != OBJID_WINDOW || idChild != CHILDID_SELF) { return; } wchar_t buff[256] = {0}; // 缓冲区开足长度,兼容长路径场景 wchar_t exe_buff[1024] = {0}; DWORD pid = 0; // 获取窗口标题 GetWindowTextW(hwnd, buff, _countof(buff)); // 第一步:获取窗口所属进程ID GetWindowThreadProcessId(hwnd, &pid); if (pid == 0) { wcout << L"Window title:" << buff << L"\nprocess name: get pid failed, err:" << GetLastError() << endl; return; } // 第二步:最小权限打开进程对象 HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); if (hProcess == NULL) { wcout << L"Window title:" << buff << L"\nprocess name: open process failed, err:" << GetLastError() << endl; return; } // 第三步:读取进程exe完整路径 DWORD exe_path_len = _countof(exe_buff); BOOL get_path_succ = QueryFullProcessImageNameW( hProcess, 0, // 传0返回Win32格式路径,传PROCESS_NAME_NATIVE返回NT内核格式路径 exe_buff, &exe_path_len ); // 及时关闭进程句柄,避免句柄泄漏 CloseHandle(hProcess); if (!get_path_succ) { wcout << L"Window title:" << buff << L"\nprocess name: get exe path failed, err:" << GetLastError() << endl; return; } wcout << L"Window title:" << buff << endl; wcout << L"process name:" << exe_buff << endl; }
注意事项
- 回调里必须加
idObject != OBJID_WINDOW || idChild != CHILDID_SELF的过滤逻辑,否则窗口内的子控件(比如按钮、输入框)触发前台切换事件时,拿到的hwnd是子控件句柄,会出现标题、路径匹配错误的问题 - 不要用
GetModuleFileNameEx实现该功能,该接口属于Psapi.dll,对受保护进程、UWP应用的兼容性很差,且需要额外加载Psapi库 - 不需要为了拿路径特意把程序以管理员权限运行,
PROCESS_QUERY_LIMITED_INFORMATION权限在标准用户权限下就可以访问绝大多数用户态进程的路径信息 - 原代码里为exe路径仅分配了128个宽字符的缓冲区,远小于Windows路径最大长度要求,容易出现路径截断问题,建议至少分配1024个宽字符长度的缓冲区
内容的提问来源于stack exchange,提问作者anywayName
相关产品推荐
相关产品推荐

