在SetWindowsHookEx WH_CALLWNDPROC钩子中接收EM_GETTEXTEX时如何获取缓冲区转ANSI
解决方案
核心逻辑说明
WH_CALLWNDPROC钩子会在目标窗口过程接收消息前触发,EM_GETTEXTEX消息的两个参数存储在目标进程的地址空间中,不可直接在钩子进程内解引用:
wParam指向GETTEXTEX结构体,存储缓冲区长度、编码格式、标志位等配置lParam指向目标进程分配的文本缓冲区首地址
你现有代码的核心问题是直接访问了目标进程的内存地址,会触发访问越界,必须通过跨进程内存读取接口获取内容。
具体实现步骤
- 先通过窗口句柄拿到对应进程ID,判断是否为Outlook进程,避免误处理其他程序的同类型消息
- 调用
OpenProcess获取目标进程的内存读取权限 - 先调用
ReadProcessMemory将目标进程中wParam指向的GETTEXTEX结构体读取到本地变量 - 再调用
ReadProcessMemory将目标进程中lParam指向的缓冲区内容读取到本地分配的内存中,读取长度为前面拿到的GETTEXTEX.cb值 - 若读取到的内容是Unicode编码(Outlook默认使用Unicode),调用
WideCharToMultiByte指定CP_ACP转换为ANSI编码即可;若GETTEXTEX.codepage已经配置为ANSI代码页则可直接使用 - 最后过滤内容中以http/https/ftp开头的字符串即可得到目标超链接URL
修正后核心代码示例
if (nCode < 0) return CallNextHookEx(hkb, nCode, wParam, lParam); CWPSTRUCT* pwsStruct = (CWPSTRUCT*)lParam; if (pwsStruct->message == EM_GETTEXTEX) { DWORD dwPid = 0; GetWindowThreadProcessId(pwsStruct->hwnd, &dwPid); // 此处可补充进程名判断逻辑,过滤非Outlook进程 HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, dwPid); if (hProcess == NULL) return CallNextHookEx(hkb, nCode, wParam, lParam); GETTEXTEX localGetTextEx = {0}; SIZE_T dwRead = 0; // 第一步:读取目标进程中的GETTEXTEX结构体 if (ReadProcessMemory(hProcess, (LPCVOID)pwsStruct->wParam, &localGetTextEx, sizeof(GETTEXTEX), &dwRead) && dwRead == sizeof(GETTEXTEX)) { // 分配本地缓冲区 LPSTR pLocalBuf = (LPSTR)HeapAlloc(GetProcessHeap(), 0, localGetTextEx.cb); if (pLocalBuf) { // 第二步:读取目标进程中的文本内容 if (ReadProcessMemory(hProcess, (LPCVOID)pwsStruct->lParam, pLocalBuf, localGetTextEx.cb, &dwRead)) { if (localGetTextEx.codepage == CP_UNICODE) { // Unicode转ANSI int ansiLen = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pLocalBuf, localGetTextEx.cb / 2, NULL, 0, NULL, NULL); LPSTR pAnsiBuf = (LPSTR)HeapAlloc(GetProcessHeap(), 0, ansiLen + 1); if (pAnsiBuf) { WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pLocalBuf, localGetTextEx.cb / 2, pAnsiBuf, ansiLen, NULL, NULL); pAnsiBuf[ansiLen] = '\0'; // 此处pAnsiBuf即为ANSI编码的文本,可直接写入文件或过滤URL writetofile(pAnsiBuf, f1); HeapFree(GetProcessHeap(), 0, pAnsiBuf); } } else { // 已为ANSI编码可直接使用 pLocalBuf[localGetTextEx.cb - 1] = '\0'; writetofile(pLocalBuf, f1); } } HeapFree(GetProcessHeap(), 0, pLocalBuf); } } CloseHandle(hProcess); } return CallNextHookEx(hkb, nCode, wParam, lParam);
优化建议
如果只需捕获用户点击超链接的动作,可以同时HookWM_NOTIFY消息,过滤EN_LINK通知,该通知是RichEdit控件在用户点击超链接时主动触发的,比捕获EM_GETTEXTEX消息准确率更高、性能损耗更小。
内容的提问来源于stack exchange,提问作者Mohammad
相关产品推荐
相关产品推荐

