前一階段重溫了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。
哪位高手可以來指導我一下呢?