前一阶段重温了Windows中的HOOK,由此参考了《Windows via C/C++》中的示例程序——DIPS,但是我发现了一个有趣的问题。
默认情况下,链接器并不会将支持XP或Vista的manifest链接到程序上,因此,生成的应用程序运行时的控件风格是经典Windows样式,此时,DIPS小工具运行正常。
但是,当加上如下这段代码(适用于x86 CPU),问题就产生了。
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
这意味着链接器将会把新的XP或Vista的manifest链接到程序上,使应用程序具有XP或Vista的控件样式。这时,问题产生了。
这里我贴出程序的主函数代码:
int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine, int) {
// Convert command-line character to uppercase.
CharUpperBuff(pszCmdLine, 1);
TCHAR cWhatToDo = pszCmdLine[0];
if ((cWhatToDo != TEXT('S')) && (cWhatToDo != TEXT('R'))) {
// An invalid command-line argument; prompt the user.
cWhatToDo = 0;
}
if (cWhatToDo == 0) {
// No command-line argument was used to tell us what to
// do; show usage dialog box and prompt the user.
switch (DialogBox(hInstExe, MAKEINTRESOURCE(IDD_DIPS), NULL, Dlg_Proc)) {
case IDC_SAVE:
cWhatToDo = TEXT('S');
break;
case IDC_RESTORE:
cWhatToDo = TEXT('R');
break;
}
}
if (cWhatToDo == 0) {
// The user doesn't want to do anything.
return(0);
}
// The Desktop ListView window is the grandchild of the ProgMan window.
HWND hWndLV = GetFirstChild(GetFirstChild(
FindWindow(TEXT("ProgMan"), NULL)));
chASSERT(IsWindow(hWndLV));
// Set hook that injects our DLL into the Explorer's address space. After
// setting the hook, the DIPS hidden modeless dialog box is created. We
// send messages to this window to tell it what we want it to do.
chVERIFY(SetDIPSHook(GetWindowThreadProcessId(hWndLV, NULL)));
// Wait for the DIPS server window to be created.
MSG msg;
GetMessage(&msg, NULL, 0, 0); // 请注意这里
// Find the handle of the hidden dialog box window.
HWND hWndDIPS = FindWindow(NULL, TEXT("Wintellect DIPS"));
// Make sure that the window was created.
chASSERT(IsWindow(hWndDIPS));
// Tell the DIPS window which ListView window to manipulate
// and whether the items should be saved or restored.
BOOL bSave = (cWhatToDo == TEXT('S'));
SendMessage(hWndDIPS, WM_APP, (WPARAM) hWndLV, bSave);
// Tell the DIPS window to destroy itself. Use SendMessage
// instead of PostMessage so that we know the window is
// destroyed before the hook is removed.
SendMessage(hWndDIPS, WM_CLOSE, 0, 0);
// Make sure that the window was destroyed.
chASSERT(!IsWindow(hWndDIPS));
// Unhook the DLL, removing the DIPS dialog box procedure
// from the Explorer's address space.
SetDIPSHook(0);
return(0);
}
看到上面代码中的GetMessage函数(加红色注释那行),该函数是在接收一个来自explorer.exe进程的消息,这个消息是在挂钩DLL注入之后,由挂钩过滤函数发送的。挂钩过滤函数代码如下:
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) {
static BOOL bFirstTime = TRUE;
if (bFirstTime) {
// The DLL just got injected.
bFirstTime = FALSE;
// Uncomment the line below to invoke the debugger
// on the process that just got the injected DLL.
// ForceDebugBreak();
// Create the DIPS Server window to handle the client request.
CreateDialog(g_hInstDll, MAKEINTRESOURCE(IDD_DIPS), NULL, Dlg_Proc);
// Tell the DIPS application that the server is up
// and ready to handle requests.
PostThreadMessage(g_dwThreadIdDIPS, WM_NULL, 0, 0);
}
return(CallNextHookEx(g_hHook, nCode, wParam, lParam));
}
明显地,这里发送了一个WM_NULL消息给DIPS进程,当使用经典样式的控件时一切安好,经调试得到的MSG结构中的各个字段为正确的值。但是加上了上面那行链接命令后,调试得到的MSG结构的字段压根就不是WM_NULL、0、0,而是一个数值为49211的消息,这样导致了DIPS主线程唤醒,随后的FindWindow可能会返回一个NULL,因为该消息并不是挂钩过滤函数的发送的消息。当然,如果在这里Sleep一下,可以得到正确的窗口句柄,我在GetMessage函数上加了一个do-while循环,结果也的确是这样,几次循环之后可以收到消息为WM_NULL的消息,且参数均为0。
但是我不明白为什么加上了一条链接命令会这样?不妨大家都试试看,我用的IDE是VS2005。
哪位高手可以来指导我一下呢?