前一階段重溫了Windows中的HOOK,由此參考了《Windows via C/C++》中的示例程序——DIPS,但是我發(fā)現(xiàn)了一個有趣的問題。
默認(rèn)情況下,鏈接器并不會將支持XP或Vista的manifest鏈接到程序上,因此,生成的應(yīng)用程序運(yùn)行時的控件風(fēng)格是經(jīng)典Windows樣式,此時,DIPS小工具運(yùn)行正常。
但是,當(dāng)加上如下這段代碼(適用于x86 CPU),問題就產(chǎn)生了。
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
這意味著鏈接器將會把新的XP或Vista的manifest鏈接到程序上,使應(yīng)用程序具有XP或Vista的控件樣式。這時,問題產(chǎn)生了。
這里我貼出程序的主函數(shù)代碼:

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函數(shù)(加紅色注釋那行),該函數(shù)是在接收一個來自explorer.exe進(jìn)程的消息,這個消息是在掛鉤DLL注入之后,由掛鉤過濾函數(shù)發(fā)送的。掛鉤過濾函數(shù)代碼如下:

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));
}
明顯地,這里發(fā)送了一個WM_NULL消息給DIPS進(jìn)程,當(dāng)使用經(jīng)典樣式的控件時一切安好,經(jīng)調(diào)試得到的MSG結(jié)構(gòu)中的各個字段為正確的值。但是加上了上面那行鏈接命令后,調(diào)試得到的MSG結(jié)構(gòu)的字段壓根就不是WM_NULL、0、0,而是一個數(shù)值為49211的消息,這樣導(dǎo)致了DIPS主線程喚醒,隨后的FindWindow可能會返回一個NULL,因?yàn)樵撓⒉⒉皇菕煦^過濾函數(shù)的發(fā)送的消息。當(dāng)然,如果在這里Sleep一下,可以得到正確的窗口句柄,我在GetMessage函數(shù)上加了一個do-while循環(huán),結(jié)果也的確是這樣,幾次循環(huán)之后可以收到消息為WM_NULL的消息,且參數(shù)均為0。
但是我不明白為什么加上了一條鏈接命令會這樣?不妨大家都試試看,我用的IDE是VS2005。
哪位高手可以來指導(dǎo)我一下呢?