Hi everyone:
I've reached a complete deadend trying to install a Windows hook on a Win9x system (Windows 95/98/Me). The following code works fine on any of the WinNT systems (Windows NT/2000/XP) but it does mysteriously nothing on neither of my Windows 95 or 98. Any help will be GREATLY appreciated!
Here's what this code is doing. It resides in a DLL, fnRunHook9x(...) is exported and called from the main app with two handles: one is a window handle to the main program and second is a window handle to collect info on (very much like PasswordSpy). fnRunHook9x once being loaded into a process space of the control (whose window handle it had received) retrieves it's module and image path, start-up parameters and returns it back to the main app. This functionality will be very useful on Win9x system as there are no APIs to retrieve module and image path, also start-up parameters by window handle if that window belongs to another process.
OK, now the code (I made it here as short as possible):
typedef struct _MY_SEND_DATA{ //Data is collected in a call to CollectData(...) from a hook proc
TCHAR cmdLine[MAX_PATH];
TCHAR modPath[MAX_PATH];
TCHAR imgPath[MAX_PATH];
}MY_SEND_DATA, *LPMY_SEND_DATA;
typedef struct _MY_DATA{
BOOL bRes; //Set to TRUE if hook processed data
HOOK hHook; //Hook handle
HWND hTgdWnd; //Handle of target window
HWND hCtlWnd; //Main (control) window
UINT uPrivMessage; //Private message we trigger hook proc with
}MY_DATA, *LPMY_DATA;
//Global variables (loaded for each instance of DLL)
static MY_SEND_DATA MySendData = {0};
static HINSTANCE hDLLInst = NULL;
static HANDLE hFileMapping = NULL;
static MY_DATA* pMySharedMem = NULL; //Pointer to shared memory segment
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
//Called once when this DLL is loaded and each time from SetWindowsHookEx(...)
//in fnRunHook9x(...)
hDLLInst = (HINSTANCE)hModule;
//Open file mapping object
pMySharedMem = NULL;
hFileMapping = CreateFileMapping((HANDLE)INVALID_HANDLE_VALUE,
NULL, PAGE_READWRITE, 0, sizeof(MY_DATA), _T("Name_My_File_Mapping_123"));
if(hFileMapping)
{
//Map file into virtual memory
pMySharedMem = (MY_MESSAGE_DATA1*)
MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(MY_DATA));
}
break;
case DLL_PROCESS_DETACH:
//Uninitialization of DLL
if(pMySharedMem)
UnmapViewOfFile(pMySharedMem);
if(hFileMapping)
CloseHandle(hFileMapping);
break;
}
return TRUE;
}
WINIDHOOK_API BOOL fnRunHook9x(HWND hWndCaller, HWND hWndTarget)
{
//EXPORTED -- called from the main (control) app
//Installs and runs hook on Win9x system
//(Sends data back in WM_COPYDATA message to hWndCaller window)
//RETURN: - TRUE if success
//Check if we have shared mem and it's accessible
if(!pMySharedMem)
return FALSE;
if(IsBadReadPtr(pMySharedMem, sizeof(MY_DATA)) ||
IsBadWritePtr(pMySharedMem, sizeof(MY_DATA)))
return FALSE;
//Get private message
UINT uPrivateMsg = RegisterWindowMessage("My_Private_message_123");
//Set up shared memory data
pMySharedMem->bRes = FALSE;
pMySharedMem->hTgdWnd = hWndTarget;
pMySharedMem->hCtlWnd = hWndCaller;
pMySharedMem->uPrivMessage = uPrivateMsg;
//Set hook
//(I'm using WH_CALLWNDPROCRET because of the way WH_CALLWNDPROC is called on Win9x:
//See more details here: pMySharedMem->hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, CallMyWndProc9x, hDLLInst,
GetWindowThreadProcessId(hWndTarget, NULL));
if(!pMySharedMem->hHook)
return FALSE;
//Trigger hook with a message
DWORD nMesRes;
SendMessageTimeout(hWndTarget, uPrivateMsg, 0, 0,
SMTO_ABORTIFHUNG | SMTO_NORMAL, 100, &nMesRes);
//Check results
BOOL bResult = pMySharedMem->bRes ? TRUE : FALSE;
//Remove hook
bResult &= UnhookWindowsHookEx(pMySharedMem->hHook);
return bResult;
}
LRESULT CALLBACK CallMyWndProc9x(int nCode, WPARAM wParam, LPARAM lParam)
{
//Hook callback procedure
if(pMySharedMem)
{
//Only if we have pointer to shared memory
CWPSTRUCT* pCwp = (CWPSTRUCT*)lParam;
if(nCode == HC_ACTION)
{
if(pCwp)
if(pCwp->message == pMySharedMem->uPrivMessage)
{
//Our hook -> Run data collecting routine
CollectData(pMySharedMem);
return 0;
}
}
//Otherwise call next hook
return CallNextHookEx(pMySharedMem->hHook, nCode, wParam, lParam);
}
else
{
//Lost shared memory
ASSERT(NULL);
return 0;
}
}
void CollectData(MY_DATA* pSharedMem)
{
//Collect data and send it back to the main app
if(pSharedMem)
{
//Check if data is accessible in Shared mem
if(IsBadReadPtr(pSharedMem, sizeof(MY_DATA)) ||
IsBadWritePtr(&pSharedMem->bRes, sizeof(pSharedMem->bRes)))
return;
HWND hTrgWnd = pSharedMem->hTgdWnd;
HWND hClrWnd = pSharedMem->hCtlWnd;
//Get command line params
LPTSTR pCmdLine = GetCommandLine();
lstrcpyn(MySendData.cmdLine, pCmdLine ? pCmdLine : _T(""), MAX_PATH);
//Get module path
GetModuleFileName((HMODULE)GetClassLongPtr(hTrgWnd, GCLP_HMODULE), MySendData.modPath, MAX_PATH);
//Get image path
if(GetModuleFileName(NULL, MySendData.imgPath, MAX_PATH) == 0)
MySendData.imgPath[0] = _T('\0');
//Send data back
COPYDATASTRUCT cds;
cds.lpData = &MySendData;
cds.dwData = 0x123; //ID of the message
cds.cbData = sizeof(MySendData);
::SendMessage(hClrWnd, WM_COPYDATA, (WPARAM)hTrgWnd, (LPARAM)&cds);
//Report success
pSharedMem->bRes = TRUE;
}
}
I've reached a complete deadend trying to install a Windows hook on a Win9x system (Windows 95/98/Me). The following code works fine on any of the WinNT systems (Windows NT/2000/XP) but it does mysteriously nothing on neither of my Windows 95 or 98. Any help will be GREATLY appreciated!
Here's what this code is doing. It resides in a DLL, fnRunHook9x(...) is exported and called from the main app with two handles: one is a window handle to the main program and second is a window handle to collect info on (very much like PasswordSpy). fnRunHook9x once being loaded into a process space of the control (whose window handle it had received) retrieves it's module and image path, start-up parameters and returns it back to the main app. This functionality will be very useful on Win9x system as there are no APIs to retrieve module and image path, also start-up parameters by window handle if that window belongs to another process.
OK, now the code (I made it here as short as possible):
typedef struct _MY_SEND_DATA{ //Data is collected in a call to CollectData(...) from a hook proc
TCHAR cmdLine[MAX_PATH];
TCHAR modPath[MAX_PATH];
TCHAR imgPath[MAX_PATH];
}MY_SEND_DATA, *LPMY_SEND_DATA;
typedef struct _MY_DATA{
BOOL bRes; //Set to TRUE if hook processed data
HOOK hHook; //Hook handle
HWND hTgdWnd; //Handle of target window
HWND hCtlWnd; //Main (control) window
UINT uPrivMessage; //Private message we trigger hook proc with
}MY_DATA, *LPMY_DATA;
//Global variables (loaded for each instance of DLL)
static MY_SEND_DATA MySendData = {0};
static HINSTANCE hDLLInst = NULL;
static HANDLE hFileMapping = NULL;
static MY_DATA* pMySharedMem = NULL; //Pointer to shared memory segment
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
//Called once when this DLL is loaded and each time from SetWindowsHookEx(...)
//in fnRunHook9x(...)
hDLLInst = (HINSTANCE)hModule;
//Open file mapping object
pMySharedMem = NULL;
hFileMapping = CreateFileMapping((HANDLE)INVALID_HANDLE_VALUE,
NULL, PAGE_READWRITE, 0, sizeof(MY_DATA), _T("Name_My_File_Mapping_123"));
if(hFileMapping)
{
//Map file into virtual memory
pMySharedMem = (MY_MESSAGE_DATA1*)
MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(MY_DATA));
}
break;
case DLL_PROCESS_DETACH:
//Uninitialization of DLL
if(pMySharedMem)
UnmapViewOfFile(pMySharedMem);
if(hFileMapping)
CloseHandle(hFileMapping);
break;
}
return TRUE;
}
WINIDHOOK_API BOOL fnRunHook9x(HWND hWndCaller, HWND hWndTarget)
{
//EXPORTED -- called from the main (control) app
//Installs and runs hook on Win9x system
//(Sends data back in WM_COPYDATA message to hWndCaller window)
//RETURN: - TRUE if success
//Check if we have shared mem and it's accessible
if(!pMySharedMem)
return FALSE;
if(IsBadReadPtr(pMySharedMem, sizeof(MY_DATA)) ||
IsBadWritePtr(pMySharedMem, sizeof(MY_DATA)))
return FALSE;
//Get private message
UINT uPrivateMsg = RegisterWindowMessage("My_Private_message_123");
//Set up shared memory data
pMySharedMem->bRes = FALSE;
pMySharedMem->hTgdWnd = hWndTarget;
pMySharedMem->hCtlWnd = hWndCaller;
pMySharedMem->uPrivMessage = uPrivateMsg;
//Set hook
//(I'm using WH_CALLWNDPROCRET because of the way WH_CALLWNDPROC is called on Win9x:
//See more details here: pMySharedMem->hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, CallMyWndProc9x, hDLLInst,
GetWindowThreadProcessId(hWndTarget, NULL));
if(!pMySharedMem->hHook)
return FALSE;
//Trigger hook with a message
DWORD nMesRes;
SendMessageTimeout(hWndTarget, uPrivateMsg, 0, 0,
SMTO_ABORTIFHUNG | SMTO_NORMAL, 100, &nMesRes);
//Check results
BOOL bResult = pMySharedMem->bRes ? TRUE : FALSE;
//Remove hook
bResult &= UnhookWindowsHookEx(pMySharedMem->hHook);
return bResult;
}
LRESULT CALLBACK CallMyWndProc9x(int nCode, WPARAM wParam, LPARAM lParam)
{
//Hook callback procedure
if(pMySharedMem)
{
//Only if we have pointer to shared memory
CWPSTRUCT* pCwp = (CWPSTRUCT*)lParam;
if(nCode == HC_ACTION)
{
if(pCwp)
if(pCwp->message == pMySharedMem->uPrivMessage)
{
//Our hook -> Run data collecting routine
CollectData(pMySharedMem);
return 0;
}
}
//Otherwise call next hook
return CallNextHookEx(pMySharedMem->hHook, nCode, wParam, lParam);
}
else
{
//Lost shared memory
ASSERT(NULL);
return 0;
}
}
void CollectData(MY_DATA* pSharedMem)
{
//Collect data and send it back to the main app
if(pSharedMem)
{
//Check if data is accessible in Shared mem
if(IsBadReadPtr(pSharedMem, sizeof(MY_DATA)) ||
IsBadWritePtr(&pSharedMem->bRes, sizeof(pSharedMem->bRes)))
return;
HWND hTrgWnd = pSharedMem->hTgdWnd;
HWND hClrWnd = pSharedMem->hCtlWnd;
//Get command line params
LPTSTR pCmdLine = GetCommandLine();
lstrcpyn(MySendData.cmdLine, pCmdLine ? pCmdLine : _T(""), MAX_PATH);
//Get module path
GetModuleFileName((HMODULE)GetClassLongPtr(hTrgWnd, GCLP_HMODULE), MySendData.modPath, MAX_PATH);
//Get image path
if(GetModuleFileName(NULL, MySendData.imgPath, MAX_PATH) == 0)
MySendData.imgPath[0] = _T('\0');
//Send data back
COPYDATASTRUCT cds;
cds.lpData = &MySendData;
cds.dwData = 0x123; //ID of the message
cds.cbData = sizeof(MySendData);
::SendMessage(hClrWnd, WM_COPYDATA, (WPARAM)hTrgWnd, (LPARAM)&cds);
//Report success
pSharedMem->bRes = TRUE;
}
}