我会先说这是我对win32编程的新手。
我正在使用.dll创建一个全局键盘钩子,我已经安装了卸载函数来处理实际设置和删除键盘钩子。
#pragma comment(linker, "/SECTION:.SHARED,RWS")
#pragma data_seg(".SHARED")
static HHOOK hk=NULL;
//static CMyFile *pLF;
#pragma data_seg()
HINSTANCE hins = NULL;
__declspec( dllexport ) LRESULT Install(){
std::cout << "Installing" << std::endl;
hk = SetWindowsHookEx(WH_KEYBOARD_LL,EventAnalysis::KeystrokeAnalysis::KeyboardCallback,hins,0);
if(hk == NULL){
std::cout << "Hook creation failed! - " << GetLastError() << std::endl;
}
return 0;
}
__declspec(dllexport) BOOL CALLBACK UnInstall()
{
std::cout << "UnInstalling" << std::endl;
return UnhookWindowsHookEx(hk);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
hins = (HINSTANCE) hModule;
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
现在我有了.dll,我创建了一个简单的可执行文件来加载库并安装钩子:
int _tmain(int argc, _TCHAR* argv[])
{
auto funHandle = LoadLibrary(L"C:\\Users\\tprodanov\\Documents\\visual studio 2010\\Projects\\HaveFun\\Release\\HaveFun.dll");
if(funHandle == NULL){
std::cout << "Library load failed! - " << GetLastError() << std::endl;
}
auto Install = (LRESULT(*)()) GetProcAddress(funHandle, "?Install@@YAJXZ");
if(Install == NULL){
std::cout << "Procedure load failed! - " << GetLastError() << std::endl;
}
Install();
MSG Msg;
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
auto Uninstall = (BOOL(*)()) GetProcAddress(funHandle, "?UnInstall@@YGHXZ");
if(Uninstall == NULL){
std::cout << "Procedure load failed! - " << GetLastError() << std::endl;
}
Uninstall();
return 0;
}
我觉得奇怪的是我的程序的行为在其当前状态下是预期的(并且如果不是消息循环,我只是弹出一个等待用户单击OK的MessageBox()),但它如果我使用std :: cin.get()或空while循环,则不起作用。有人可以解释为什么这是行为?
另外作为后续问题 - KeyboardCallback功能只是打印到控制台“Key Pressed”。
LRESULT CALLBACK EventAnalysis::KeystrokeAnalysis::KeyboardCallback(int nCode, WPARAM wParam, LPARAM lParam){
std::cout << "Key pressed" << std::endl;
return CallNextHookEx(NULL,nCode,wParam,lParam);
}
我预计只有在我专注于控制台窗口时按键才能打印,但即使我输入记事本,“Key Pressed”消息也会出现在我的可执行文件中,该消息称为.dll的Install函数。我不明白这一点,因为据我所知,动态库是在每个进程中独立加载的,所以每个进程都有自己的KeyboardCallback函数副本,它会尝试打印到前台窗口的控制台。
答案 0 :(得分:2)
documentation清楚地说明了发生了什么:
在安装它的线程的上下文中调用此钩子。通过向安装了挂钩的线程发送消息来进行调用。因此,安装钩子的线程必须有一个消息循环。
通过对Install
的调用安装挂钩。因此,在进行该调用的同一个线程中,您需要运行一个消息循环。
至于显示消息框影响事物的原因,消息框通过运行模态消息循环来操作。该消息循环将调度挂钩消息。
对于后续问题,documentation又有了答案:
每次新的键盘输入事件即将发布到线程输入队列时,系统都会调用此函数。
当发布到线程输入队列时,它意味着任何线程输入队列。
或者在SetWindowsHookEx
文档的评论中查看各种钩子类型的列表及其范围。 WH_KEYBOARD_LL
被列为全局钩子。
另请注意,WH_KEYBOARD_LL
是一个低级别的钩子,因此不会导致DLL被注入另一个进程。实际上,您的代码过于复杂。你根本就不需要DLL。你可以从你的可执行文件调用SetWindowsHookEx
来完成所有操作,该可执行文件在同一个可执行文件中传递一个回调函数。
这是一个演示所有这些的最小示例程序:
#include <Windows.h>
#include <iostream>
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
std::cout << "Key pressed" << std::endl;
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
int main(int argc, char* argv[])
{
std::cout << "Installing" << std::endl;
HHOOK hk;
hk = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, NULL, 0);
MSG Msg;
while (GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
std::cout << "UnInstalling" << std::endl;
UnhookWindowsHookEx(hk);
return 0;
}