我的目标在概念上很简单:我想设置一个利用共享文件句柄的GetMessage全局钩子函数。问题出现是因为我的理解是每个进程多次加载包含钩子函数的DLL,每个进程都有自己的“地址空间”。出于这个原因,我开始相信我不能简单地处理DllMain的DLL_PROCESS_ATTACH来创建所需的文件,因为将使用不同的句柄创建多个文件。
引起我注意的解决方案是命名管道。基本上,应用程序将充当服务器端;它会创建文件一次然后提供DLL客户端的文件句柄,因此每个全局钩子将使用相同的文件。
我似乎无法从我收集的代码中获得它。在应用程序中,我创建文件,设置全局钩子函数,然后让它通过这个循环:
while(1)
{
HANDLE hPipe = CreateNamedPipe("\\\\.\\pipe\\pipename", PIPE_ACCESS_OUTBOUND,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 32, 32, 5000, NULL);
if(hPipe == INVALID_HANDLE_VALUE)
return 42;
if(!ConnectNamedPipe(hPipe, NULL))
return 43;
DWORD dwWritten;
WriteFile(hPipe, logFile, sizeof(logFile), &dwWritten, NULL);
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
}
然后我处理DllMain的DLL_PROCESS_ATTACH:
case DLL_PROCESS_ATTACH:
{
HANDLE hPipe;
while(1)
{
hPipe = CreateFile("\\\\.\\pipe\\pipename", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if(hPipe != INVALID_HANDLE_VALUE)
break;
WaitNamedPipe("\\\\.\\pipe\\pipename", NMPWAIT_USE_DEFAULT_WAIT);
}
DWORD dwRead;
ReadFile(hPipe, logFile, sizeof(logFile), &dwRead, NULL);
CloseHandle(hPipe);
break;
}
简单地说,它不起作用,我似乎无法弄清楚原因。在我的代码中是否有我遗漏或做错的事情?
我无法弄清楚的另一个问题是应用程序陷入了无限循环的无限循环中。我想设置一个DLL将在某种情况下设置的事件并导致主应用程序取消挂钩全局挂钩,关闭文件并退出,但ConnectNamedPipe是一个阻塞函数。有什么方法可以确定何时服务所有客户端,以便服务循环可以中断?
感谢您的帮助。
答案 0 :(得分:1)
在DLL_PROCESS_ATTACH
或DLL_THREAD_ATTACH
期间,您可以调用哪些系统API存在很大的限制。来自MSDN documentation.
入口点功能应该 只执行简单的初始化或 终止任务。一定不要打电话 LoadLibrary或LoadLibraryEx 函数(或调用的函数) 这些功能),因为这可能 在DLL中创建依赖循环 加载订单。这可能会导致DLL 在系统使用之前使用 执行其初始化代码。 同样,入口点功能 不得调用FreeLibrary函数 (或调用FreeLibrary的函数) 在流程终止期间,因为 这可能导致使用DLL 系统执行完之后 终止码。
因为Kernel32.dll可以保证 被加载到进程地址空间 当入口点功能是 调用,调用函数 Kernel32.dll不会导致 在它之前使用的DLL 初始化代码已被执行。 因此,入口点功能 可以在Kernel32.dll中调用函数 不加载其他DLL。对于 例如,DllMain可以创建 同步对象如 关键部分和互斥体,以及使用 TLS。不幸的是,没有 全面的安全功能列表 在Kernel32.dll中。
Windows 2000:不要创建命名 DllMain中的同步对象 因为系统会加载一个 额外的DLL。
调用那些函数 需要除Kernel32.dll以外的DLL 可能会导致问题 难以诊断。例如, 调用User,Shell和COM函数 可能导致访问冲突错误, 因为有些功能会加载其他 系统组件。相反,打电话 这些功能在这些期间 终止可能导致访问冲突 因为相应的错误 组件可能已经存在 卸载或未初始化。
对于实验级工作,请考虑使用线程附加事件来查看“会发生什么”。对于生产工作,您需要一个完全修改过的方法,在DllMain中不需要繁重的工作。您可以在上面看到,未来的历史记录将在此OS工具中包含更多错误。
答案 1 :(得分:1)
在我看来,您的主要问题可能是CreateNamedPipe
函数的最后一个参数(SECURITY_ATTRIBUTES)或其他安全问题(见下文)。
我真的不明白你计划在logFile
中使用哪种信息,这些信息不能超过32个字节(16个WCHAR)。在sizeof()
中使用CreateNamedPipe
也会更好一些(也可以考虑64位操作系统)。是否要将在一个进程中打开的日志文件的句柄发送到其他进程?如果您这样做,则应使用DuplicateHandle
之类的功能(请参阅http://msdn.microsoft.com/en-us/library/ms724251.aspx)。一般来说,关于您发布的命名管道的通信的代码示例我发现不太好。我建议你首先调试挂钩DLL的命名管道通信(至少有两个独立的客户端进程,最好在不同的用户凭据和创建管道的服务器进程下运行)。
在DLL_THREAD_ATTACH中使用Windows API有限制,但在你的情况下使用Kernel32.dll似乎是安全的。
我不知道你想要实现什么样的通信,但一般来说,使用unblocked模式就像使用完成例程或其他异步操作(参见http://msdn.microsoft.com/en-us/library/aa365788.aspxhttp://msdn.microsoft.com/en-us/library/aa365788.aspx和http://msdn.microsoft.com/en-us/library/aa365601.aspx) DllMain
可能会更好。
还有一个小建议:您应该在DisableThreadLibraryCalls()
的情况下使用DLL_PROCESS_ATTACH
并尝试选择DLL的合理基址(链接器开关),这将减少DLL在重载期间的重定位流程。这些会加快你的编程并节省内存。