我正在尝试创建一个简单的应用程序来记录和回放一系列键盘和鼠标命令(宏)。阅读文档并得出结论,最合适的实现(如果不是唯一的实现)将是设置Windows日记记录挂钩(WH_JOURNALRECORD
)并以日记回放一个(WH_JOURNAL_PLAYBACK
)进行回放。
根据文档,这些挂钩不需要驻留在DLL中,而是可以位于可执行文件(应用程序)中。因此,我让Visual Studio为我创建了一个简单的Win32应用程序。这是一个非常经典的应用程序,它注册一个窗口类,创建窗口并运行消息循环。该文档还提到WH_JOURNALRECORD
/ WH_JOURNAL_PLAYBACK
挂钩的挂钩过程在设置它们的线程上下文中运行。但是,它没有具体提到此线程应该做什么,例如运行消息循环,以可警报状态休眠或做什么。因此,我只是设置了钩子并运行了消息循环-它是应用程序的主线程,也是唯一的线程。这是我发现的一些代码示例,尽管它们似乎已经很旧了,尽管它们似乎现在还无法运行,并且一些Windows安全更新使事情变得更加困难。
我相信我已经采取了一些示例和帖子中发现的所有必要步骤:
我设法成功(?)安装了WH_JOURNALRECORD
钩子(SetWindowsHookEx()
返回了非零句柄),但是未调用该钩子过程。
下面是我的代码(我省略了窗口类注册,窗口创建,窗口过程和“关于”对话框的内容,因为其中没有什么有趣或特别的地方,它们只是准系统):
// Not sure if these are needed, found it in some code samples
#pragma comment(linker, "/SECTION:.SHARED,RWS")
#pragma data_seg(".SHARED")
HHOOK hhJournal = NULL;
#pragma data_seg()
// Not sure if the Journal proc needs to be exported
__declspec(dllexport) LRESULT CALLBACK _JournalRProc(_In_ int code, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
Beep(1000, 30); // Clumsy way to trace the JournalRProc calls
return CallNextHookEx(NULL, code, wParam, lParam);
}
void AddKMHooks(HMODULE _hMod)
{
if (hhJournal) return;
MessageBox(NULL, "Adding Hooks", szTitle, MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
hhJournal = SetWindowsHookEx(WH_JOURNALRECORD, _JournalRProc, _hMod, 0);
if (!hhJournal)
{
CHAR s[100];
wsprintf(s, "Record Journal Hook Failed!\nThe Error-Code was %d", GetLastError());
MessageBox(NULL, s, szTitle, MB_OK | MB_ICONSTOP | MB_TASKMODAL);
}
}
void RemoveKMHooks()
{
if (!hhJournal) return;
MessageBox(NULL, "Removing Hooks", szTitle, MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
UnhookWindowsHookEx(hhJournal);
hhJournal = NULL;
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_KMRECORD, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow)) return FALSE;
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_KMRECORD));
AddKMHooks(hInstance);
// Calling AddKMHooks(GetModuleHandle(NULL)) instead, delivers the same results
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Once the hook has is set the GetMessage() call above
// always returns a WM_TIMER message with a timer ID of 1,
// posted to the queue with the PostMessage() function,
// as the Spy++ tool reports
RemoveKMHooks();
return (int) msg.wParam;
}
我使用Spy ++工具监视了该应用程序,发现设置了挂钩之后,应用程序会接收到一系列连续的WM_TIMER
计时器消息,其计时器ID为1,并与{ {1}}函数(在Spy ++中为P)。据报告,这些消息的窗口句柄与主窗口属于同一应用程序和线程,并且其类名称为“ UserAdapterWindowClass”。我的代码既不会创建计时器,也不会显式创建此类的任何窗口,因此显然它们是由系统创建的。此外,还有另一条ID为0x0060(未知!)的消息,在第一个PostMessage()
或第二个WM_TIMER
之后,仅一次发布到同一窗口。
应用程序似乎以某种方式“锁定”了系统(记录,等待资源或什么?),直到我按Alt + Ctrl + Del,该文档说它停止记录(我尚未实现某种机制来随意停止录制/卸载该挂钩,所以这是我到目前为止使用的内容)。挂钩过程似乎从未被调用过,这是我目前面临的问题。我已经考虑过在钩子过程中检查code
参数并采取相应的措施,但是我什至还没有这样做,因为从未调用过该过程-因此它没有任何作用-所以我只调用{{ 1}}在其中(并假设它会很好地工作)。
注释:
CallNextHookEx()
参数为0(code
)。尽管对其进行了测试,并且返回零或非零(无论是否调用HC_ACTION
)都没有任何区别。有人可以在这里解释什么地方出问题吗?还检查了其他一些帖子,尤其是这些帖子:SetWindowsHookEx for WH_JOURNALRECORD fails under Vista/Windows 7和WH_JOURNALRECORD hook in Windows (C++) - Callback never called. ,但是找不到解决方案,我必须注意使用条件也有所不同。我遇到的情况与此SetWindowsHookEx(WH_JOURNALRECORD, ..) sometimes hang the system最接近,尽管就我而言,这总是发生,而不仅仅是“有时”。
非常感谢您的帮助。
我已上载了解决方案(源+ VS文件,但未上载.exe,.obj。,。pch,.pdb等文件,因此需要重新构建)here(如果有人想使用)一看。
提前谢谢
编辑:
在不同配置下测试了应用程序。
因此,这可能是Windows模块或对我不满足的证书的某些要求导致的。 Seacrhed在Internet上使用了很多,但是找不到证书必须符合的规格。例如,对格式,私钥或用途有什么要求(现在我已经检查了所有用途,尽管我还尝试仅检查代码签名,但没有区别)。现在已不建议使用makecert实用程序(该文档建议使用PowerShell代替创建证书),但我不知道这是否与我的问题有关。
我只有两个结论: -证书必须安装在“ PrivateCertStore”下,以便可以构建应用程序(否则签名失败)。可能是因为我在脚本中设置了/ s PrivateCertStore选项。但, -必须将证书安装在“受信任的根证书颁发机构”下,才能运行该应用程序(否则执行失败,并出现访问被拒绝的错误)。