You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

隐式DLL链接适配键盘钩子却不适配WH_SHELL钩子问题排查

WH_SHELL全局钩子调用SetWindowsHookExW失败返回NULL的问题

已通过隐式链接DLL实现WH_KEYBOARD_LL全局钩子,用于PC自动化任务,运行正常。但添加WH_SHELL全局钩子时,SetWindowsHookExW调用失败,hhShell返回NULL。

EXE项目(Visual C++ 2022)代码

// pcautomation.h
#pragma once
#define WINVER 0x0500
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

// pcautomation.cpp
#include "<path>\pcautomation.h"
#include "<path>\keyboardCallback.h"
#include "<path>\shellCallback.h"
using namespace std;

HHOOK hhKybd;
HHOOK hhShell;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int cmdShow) {
  // ... <register application window, etc> ...

  // 此部分正常工作
  hhKybd = SetWindowsHookExW(WH_KEYBOARD_LL, (HOOKPROC) KeyboardHookProc, NULL, 0);
  if (NULL == hhKybd) return 0;

  // 此部分调用失败,hhShell为NULL
  hhShell = SetWindowsHookExW(WH_SHELL, (HOOKPROC) WHShellProc, NULL, 0);
  if (NULL == hhShell) return 0;

  // ... <message loop, etc> ...
  return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  // ...
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

DLL项目代码

pcautomation-dll.h

#pragma once
#ifdef NAX_PCA_DLL_EXPORTS
#define NAX_PCA_DLL_API __declspec(dllexport)
#else
#define NAX_PCA_DLL_API __declspec(dllimport)
#endif
#define WINVER 0x0500
#include <Windows.h>

pcautomation-dll.cpp

#include "<path>\pcautomation-dll.h"
#include "<path>\keyboardCallback.h"
#include "<path>\shellCallback.h"

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved) {
  switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
      break;
  }
  return TRUE;
}

keyboardCallback.h

#pragma once
#include <Windows.h>
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam);

keyboardCallback.cpp

#include "<path>\keyboardCallback.h"
using namespace std;
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
  ...
  CallNextHookEx(NULL, nCode, wParam, lParam);
}

shellCallback.h

#pragma once
#include <Windows.h>
LRESULT CALLBACK WHShellProc(int nCode, WPARAM wParam, LPARAM lParam);

shellCallback.cpp

#include "<path>\shellCallback.h"
using namespace std;
LRESULT CALLBACK WHShellProc(int nCode, WPARAM wParam, LPARAM lParam) {
  if (nCode < 0) return(CallNextHookEx(NULL, nCode, wParam, lParam));
  if (nCode == HSHELL_WINDOWCREATED) {
    ...
  }
  return 0;
}

source.def

LIBRARY pcautomation-dll
EXPORTS
  KeyboardHookProc
  WHShellProc

配置情况

VS 2022已完成隐式DLL链接的相关配置:

  • 将source.def设置为DLL项目链接器的模块定义文件
  • 添加DLL生成的lib文件作为EXE项目的链接依赖
  • 设置解决方案编译顺序为先编译DLL,再编译EXE

疑问点

两个钩子的SetWindowsHookExW均将dwThreadId设为0以实现全局钩子。根据文档,当dwThreadId指定当前进程线程且钩子过程在当前进程代码中时,hMod参数需设为NULL,但WH_KEYBOARD_LL钩子未指定当前进程线程却传入NULL仍能正常工作。想确认WH_SHELL钩子是否存在特殊性,是否必须传入DLL的显式句柄?担心需要改为显式DLL链接,但现有DLL的使用场景较多,改动成本较高。


内容的提问来源于stack exchange,提问作者neilalex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 16:25:21