1153 lines
40 KiB
C++
1153 lines
40 KiB
C++
// This is the main DLL file.
|
|
|
|
#include "stdafx.h"
|
|
#define DLLAPI_BUTTON_HOOK
|
|
#include "ButtonHook.h"
|
|
#include <psapi.h>
|
|
|
|
using namespace Ooganizer;
|
|
|
|
#ifdef _DEBUG
|
|
#import "..\\..\\Target\\Debug\\Settings.tlb" raw_interfaces_only
|
|
#else
|
|
#import "..\\..\\Target\\Release\\Settings.tlb" raw_interfaces_only
|
|
#endif
|
|
using namespace Settings;
|
|
|
|
#ifdef _DEBUG
|
|
#import "..\\..\\Target\\Debug\\Platform.tlb" raw_interfaces_only
|
|
#else
|
|
#import "..\\..\\Target\\Release\\Platform.tlb" raw_interfaces_only
|
|
#endif
|
|
using namespace Platform;
|
|
|
|
#ifdef _DEBUG
|
|
#import "..\\..\\Target\\Debug\\ButtonWPForm.tlb"
|
|
#else
|
|
#import "..\\..\\Target\\Release\\ButtonWPForm.tlb"
|
|
#endif
|
|
using namespace ButtonWPForm;
|
|
|
|
////
|
|
// ***** What type of Windows should have the Button *****
|
|
////
|
|
const DWORD WS_STYLES_TO_TEST_FOR = WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU;
|
|
|
|
////
|
|
// ***** Top-Level Dialogs *****
|
|
////
|
|
const DWORD WS_STYLES_TO_TEST_FOR_DIALOGS = WS_SYSMENU | WS_POPUP;
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//********************************************************************
|
|
// Macros and typedefs
|
|
//********************************************************************
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#define MSG_HOOK ( L"MSG_HOOK" )
|
|
#define MSG_UNHOOK ( L"MSG_UNHOOK" )
|
|
|
|
////
|
|
// Each DLL instance (All Instances) need to keeps track of the following
|
|
// Data members (these end up being the same values so no need to put them into the DS)
|
|
////
|
|
HINSTANCE s_hModule = NULL;
|
|
UINT m_WMHook = NULL;
|
|
UINT m_WMUnHook = NULL;
|
|
|
|
// enum to send MSG_HOOK and MSG_UNHOOK
|
|
typedef enum _MSG{_MSG_HOOK,_MSG_UNHOOK} MSG_;
|
|
|
|
////
|
|
// Each DLL instance (All instances not just the once whose window,
|
|
// we'll hook into) will hold a pointer to it's own
|
|
// CWHPaccess Object. (allowing it access to the Shared Data Segment)
|
|
////
|
|
CWHPaccess* s_ButtonHookAccess = NULL;
|
|
|
|
////
|
|
// Each DLL instanc needs to keep track of it's recently closed windows
|
|
////
|
|
#define NCLOSES_TO_TRACK 25
|
|
// timeout values, we need to ignore activate messages for nTime when
|
|
// we unhook to avoid rehooking again in processes that have multiple windows
|
|
const int IGNORE_ACTIVATE_MESSAGE_TIMEOUT_MS = 150;
|
|
struct RecentWindowsWTimeouts
|
|
{
|
|
HWND hWnd;
|
|
DWORD dwTimeInserted;
|
|
};
|
|
RecentWindowsWTimeouts s_hRecentClosedWindows[NCLOSES_TO_TRACK] = {NULL,NULL};
|
|
UINT s_HRecentListI = 0;
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: InsertRecentClosedWindow()
|
|
// Desc: Insert a recently closed window
|
|
//////////////////////////////////////////////////////////////////////
|
|
void InsertRecentClosedWindow(HWND hWnd)
|
|
{
|
|
RecentWindowsWTimeouts recent;
|
|
recent.hWnd = hWnd;
|
|
recent.dwTimeInserted = GetTickCount();
|
|
|
|
s_hRecentClosedWindows[s_HRecentListI] = recent;
|
|
s_HRecentListI = s_HRecentListI + 1;
|
|
|
|
if(s_HRecentListI == NCLOSES_TO_TRACK)
|
|
s_HRecentListI = 0;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: FindRecentClosedWindow()
|
|
// Desc: Find a recently closed window
|
|
//////////////////////////////////////////////////////////////////////
|
|
BOOL FindRecentClosedWindow(HWND hWnd)
|
|
{
|
|
DWORD dwTickCount = GetTickCount();
|
|
for (UINT i = 0; i < s_HRecentListI; ++i)
|
|
{
|
|
if(s_hRecentClosedWindows[i].hWnd == hWnd)
|
|
{
|
|
DWORD diffTime = dwTickCount - s_hRecentClosedWindows[i].dwTimeInserted;
|
|
if(diffTime < IGNORE_ACTIVATE_MESSAGE_TIMEOUT_MS)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
bool IsAWindowToHookInto(HWND hwnd);
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: EnumProc()
|
|
// Desc: Hook/Unhook all top level windows. Called for every top-level
|
|
// Window on the screen.
|
|
//
|
|
// Prms: lParam, if true send Hook Message, false send UnHook Msg
|
|
//
|
|
// Retr: TRUE, always
|
|
//////////////////////////////////////////////////////////////////////
|
|
BOOL CALLBACK EnumProc( HWND hWnd, LPARAM lParam )
|
|
{
|
|
switch (lParam)
|
|
{
|
|
case _MSG_HOOK:
|
|
if((m_WMHook >= 0xC000) && (m_WMHook <= 0xFFFF) &&
|
|
(IsAWindowToHookInto(hWnd)))
|
|
{
|
|
log(LOGGING_DEBUG, L"Sending m_WMHook to hWnd %u Msg %u", hWnd, m_WMHook);
|
|
SendMessage( hWnd, m_WMHook, 0, 0 );
|
|
}
|
|
break;
|
|
|
|
case _MSG_UNHOOK:
|
|
if((m_WMUnHook >= 0xC000) && (m_WMUnHook <= 0xFFFF) &&
|
|
/* Check if Window is hooked into */
|
|
( s_ButtonHookAccess->FindHookedWnd(hWnd) == TRUE ))
|
|
{
|
|
log(LOGGING_DEBUG, L"Sending m_WMUnHook to hWnd %u Msg %u", hWnd, m_WMHook);
|
|
SendMessage( hWnd, m_WMUnHook, 0, 0 );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: GetProcessNameFromPID()
|
|
// Retr: Returns the process name in the buffer and true if everything went well,
|
|
// false otherwise.
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool GetProcessNameFromHWND(HWND hwnd, wchar_t strProcessNameBuf[MAX_PATH + 1])
|
|
{
|
|
DWORD pid = NULL;
|
|
GetWindowThreadProcessId(hwnd,&pid);
|
|
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid );
|
|
|
|
// Get the process name
|
|
if (NULL != hProcess )
|
|
GetModuleBaseName( hProcess, NULL, strProcessNameBuf, (MAX_PATH + 1));
|
|
|
|
CloseHandle(hProcess);
|
|
return true;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: IsWhiteListed()
|
|
// Desc: Function allows us to Blacklist certain windows to not be
|
|
// included when hooking the WinProcButton Hook.
|
|
// a Window can be whitelisted either on Title or Type, passed in
|
|
// thru the configuration
|
|
//
|
|
// Prms: hwnd, handle to a window we want to check
|
|
//
|
|
// Retr: true, if window is WhiteListed, false otherwise
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool IsWhiteListed(HWND hwnd)
|
|
{
|
|
wchar_t strBuf[MAX_PATH + 1] = {0};
|
|
|
|
if(GetProcessNameFromHWND(hwnd,strBuf))
|
|
{
|
|
_wcsupr_s(strBuf,MAX_PATH);
|
|
if(s_ButtonHookAccess->IsAllowedProcessName(strBuf))
|
|
return true;
|
|
}
|
|
|
|
if (GetWindowText(hwnd,strBuf,MAX_PATH))
|
|
{
|
|
_wcsupr_s(strBuf,MAX_PATH);
|
|
if(s_ButtonHookAccess->IsAllowedWindowsTitle(strBuf))
|
|
return true;
|
|
}
|
|
|
|
if(GetClassName(hwnd,strBuf,MAX_PATH))
|
|
{
|
|
_wcsupr_s(strBuf,MAX_PATH);
|
|
if(s_ButtonHookAccess->IsAllowedWindowsClass(strBuf))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: IsFileOpenOrFileSaveAsDialog()
|
|
// Desc: We also hook into FileOpen and FileSaveAs Dialogs(), in order
|
|
// to do so we compare the window class
|
|
//
|
|
// Retr: true, if dialog is of a fileopen/filesaveas class, false otherwise
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool IsFileOpenOrFileSaveAsDialog(HWND hwnd)
|
|
{
|
|
wchar_t strBuf[MAX_PATH + 1] = {0};
|
|
if(GetClassName(hwnd,strBuf,MAX_PATH))
|
|
{
|
|
_wcsupr_s(strBuf);
|
|
if(lstrcmp(strBuf, L"#32770") == 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Strc: FileNameAndLocationStruct()
|
|
// Desc: We use this to pass out location and file name by EnumFileNameAndLocationProc
|
|
//////////////////////////////////////////////////////////////////////
|
|
struct FileNameAndLocationStruct
|
|
{
|
|
wchar_t location[MAX_PATH + 1];
|
|
wchar_t filename[MAX_PATH + 1];
|
|
wchar_t filetypes[MAX_PATH + 1];
|
|
};
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: EnumFileNameAndLocationProc()
|
|
// Desc: Iterate all child windows and extract FileName and FileLocation
|
|
//
|
|
// Prms: lParam, a FileNameAndLocationStruct*
|
|
//
|
|
// Retr: TRUE, always
|
|
//////////////////////////////////////////////////////////////////////
|
|
BOOL CALLBACK EnumFileNameAndLocationProc( HWND hWnd, LPARAM lParam )
|
|
{
|
|
wchar_t strWindowText[MAX_PATH + 1] = {0};
|
|
wchar_t strClassName[MAX_PATH + 1] = {0};
|
|
GetWindowText(hWnd, strWindowText, MAX_PATH);
|
|
GetClassName(hWnd, strClassName, MAX_PATH);
|
|
|
|
bool bContinue = false;
|
|
// We only care about the following 3 window classes
|
|
// all others continue iteration
|
|
if((lstrcmp(L"Edit", strClassName) == 0) ||
|
|
(lstrcmp(L"ComboBox", strClassName) == 0) ||
|
|
(lstrcmp(L"ToolbarWindow32", strClassName) == 0))
|
|
{
|
|
bContinue = true;
|
|
}
|
|
|
|
// skip this window
|
|
if(!bContinue)
|
|
return TRUE;
|
|
|
|
// passed in file struct pointer
|
|
FileNameAndLocationStruct* pstruct = (FileNameAndLocationStruct*) lParam;
|
|
|
|
// Stop if there is no window text
|
|
if(lstrlen(strWindowText) <= 0)
|
|
return TRUE;
|
|
|
|
// ignore any window texts that have an &
|
|
if(wcsstr(strWindowText, L"&"))
|
|
return TRUE;
|
|
|
|
// * Good for Debugging *
|
|
//log(LOGGING_DEBUG, L"Found Window Text - %s ", strWindowText);
|
|
|
|
// File Types Window Text (pass out in case it's needed later)
|
|
if(wcsstr(strWindowText, L"*."))
|
|
{
|
|
log(LOGGING_DEBUG, L"Found FileType Text %s", strWindowText);
|
|
lstrcpy(pstruct->filetypes,strWindowText);
|
|
return TRUE;
|
|
}
|
|
|
|
// Is this a location text
|
|
if(wcsstr(strWindowText, L": ")) // This could be very much English Windows Specific (or not?, should check)
|
|
{
|
|
// get the location of ": "
|
|
int i = 0;
|
|
wchar_t* pBegin = strWindowText;
|
|
for (; i < MAX_PATH; ++i)
|
|
{
|
|
if((*pBegin == L':') && (*(pBegin + 1) == L' '))
|
|
{
|
|
pBegin = pBegin + 2; // start after the ": "
|
|
break;
|
|
}
|
|
++pBegin;
|
|
}
|
|
|
|
if(pBegin != NULL)
|
|
{
|
|
log(LOGGING_DEBUG, L"Found Location Text %s", pBegin);
|
|
lstrcpy(pstruct->location,pBegin);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// illegal file name characters
|
|
wchar_t* illegalfilechars = L"\\/:*?\"<>|";
|
|
int illegalsize = lstrlen(illegalfilechars);
|
|
|
|
if(wcsstr(strWindowText,L"."))
|
|
{
|
|
// check to see if string contains any illegal
|
|
// file name characters
|
|
for(int i = 0; i < illegalsize; ++i)
|
|
{
|
|
wchar_t c[2] = {0};
|
|
c[0] = illegalfilechars[i];
|
|
|
|
// found illegal character, ignore string as file name
|
|
if(wcsstr(strWindowText,c))
|
|
return TRUE;
|
|
}
|
|
|
|
// Found a 100% file name string (it contains a '.')
|
|
log(LOGGING_DEBUG, L"Found FileName Text %s", strWindowText);
|
|
lstrcpy(pstruct->filename,strWindowText);
|
|
return TRUE;
|
|
}
|
|
else if(lstrlen(strWindowText) >= 1)
|
|
{
|
|
// check to see if string contains any illegal
|
|
// file name characters
|
|
for(int i = 0; i < illegalsize; ++i)
|
|
{
|
|
wchar_t c[2] = {0};
|
|
c[0] = illegalfilechars[i];
|
|
|
|
// found illegal character, ignore string as file name
|
|
if(wcsstr(strWindowText,c))
|
|
return TRUE;
|
|
}
|
|
|
|
// Found a possible Most likely file name string (must not contain a '.')
|
|
log(LOGGING_DEBUG, L"Found FileName Text %s", strWindowText);
|
|
lstrcpy(pstruct->filename,strWindowText);
|
|
return TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: GetFileNameAndLocationStringValuesFromDialogWindow()
|
|
// Desc: Iterates through all the child windows and get the values we need
|
|
// and pass them back out
|
|
//
|
|
// Prms: hwnd, handle the fileOpen dialog
|
|
// pCallerObj, WPFCaller object where we will write the retrieved information to
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool GetFileNameAndLocationStringValuesFromDialogWindow(HWND hwnd, WPFCaller* pCallerObj)
|
|
{
|
|
// Iterate all child Windows and parse all the texts, the enum function
|
|
// will do all the work
|
|
if(hwnd && ::IsWindow(hwnd))
|
|
{
|
|
// Clear any old values in the caller object
|
|
memset(pCallerObj->m_strFileNameLocationBuf, 0, sizeof(pCallerObj->m_strFileNameLocationBuf));
|
|
memset(pCallerObj->m_strFileTypesBuf, 0, sizeof(pCallerObj->m_strFileTypesBuf));
|
|
|
|
// Create temporary object to hold values
|
|
FileNameAndLocationStruct filenameAndLocation;
|
|
memset(filenameAndLocation.filename, 0, sizeof(filenameAndLocation.filename));
|
|
memset(filenameAndLocation.location, 0, sizeof(filenameAndLocation.location));
|
|
memset(filenameAndLocation.filetypes, 0, sizeof(filenameAndLocation.filetypes));
|
|
|
|
// Try to Resolve
|
|
log(LOGGING_DEBUG, L"About to call EnumFileNameAndLocationProc (EnumChildWindows)");
|
|
EnumChildWindows(hwnd, EnumFileNameAndLocationProc, (LPARAM) &filenameAndLocation );
|
|
|
|
// get resolving lengths
|
|
int nLen1 = lstrlen(filenameAndLocation.filename);
|
|
int nLen2 = lstrlen(filenameAndLocation.location);
|
|
int nLen3 = lstrlen(filenameAndLocation.filetypes);
|
|
|
|
// Handle errors * Good for Debugging *
|
|
/*
|
|
if(nLen1 == 0)
|
|
log(LOGGING_DEBUG, L"Error resolving - FileName - FileOpen or FileSaveAs Dialog");
|
|
if(nLen2 == 0)
|
|
log(LOGGING_DEBUG, L"Error resolving - FileLocation - FileOpen or FileSaveAs Dialog");
|
|
if(nLen3 == 0)
|
|
log(LOGGING_DEBUG, L"Error resolving - FileTypes - FileOpen or FileSaveAs Dialog");
|
|
*/
|
|
|
|
// Something went wrong! no point moving on
|
|
if(nLen1 == 0 || nLen2 == 0 || nLen3 == 0)
|
|
return false;
|
|
|
|
////
|
|
// Now Copy out the values to the buffers of the caller object that was passed in
|
|
////
|
|
lstrcpyn(pCallerObj->m_strFileNameLocationBuf, filenameAndLocation.location, MAX_PATH);
|
|
lstrcat(pCallerObj->m_strFileNameLocationBuf, L"\\");
|
|
lstrcat(pCallerObj->m_strFileNameLocationBuf, filenameAndLocation.filename);
|
|
lstrcpyn(pCallerObj->m_strFileTypesBuf, filenameAndLocation.filetypes, MAX_PATH);
|
|
|
|
// Log info to see what's going on
|
|
log(LOGGING_DEBUG, L"GetFileNameAndLocation setting m_strFileNameLocattionBuf to %s", pCallerObj->m_strFileNameLocationBuf);
|
|
log(LOGGING_DEBUG, L"GetFileNameAndLocation setting m_strFileTypesBuf to %s", pCallerObj->m_strFileTypesBuf);
|
|
return true;
|
|
|
|
}
|
|
return false;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: IsAWindowToHookInto()
|
|
// Desc: Broad check to see if we want to hook into a window.
|
|
// Called by CallWndHookProc.
|
|
// Window on the screen.
|
|
//
|
|
// Prms: hwnd, handle to a window we want to check//
|
|
//
|
|
// Note: This function calls IsBlackListed() for more window exclusions.
|
|
//
|
|
// Retr: true, if window is should be hooked into, false otherwise
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool IsAWindowToHookInto(HWND hwnd)
|
|
{
|
|
DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
|
|
DWORD dwExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
|
|
|
if ( /* top-raised windows */
|
|
( ( dwExStyle & WS_EX_WINDOWEDGE ) == WS_EX_WINDOWEDGE )
|
|
|
|
/* Styles to test for (named above) */
|
|
&& ( ( dwStyle & WS_STYLES_TO_TEST_FOR ) == WS_STYLES_TO_TEST_FOR )
|
|
|
|
/* Has no parent = top-level window */
|
|
&& ( GetParent(hwnd) == NULL )
|
|
|
|
/* Check if Window is not already hooked into*/
|
|
&& ( s_ButtonHookAccess->FindHookedWnd(hwnd) == FALSE )
|
|
|
|
/* Check if Window has not been recently closed in this process*/
|
|
&& ( FindRecentClosedWindow(hwnd) == FALSE )
|
|
|
|
// **** CUSTOM INCLUSION LIST ****
|
|
&& ( IsWhiteListed(hwnd) )
|
|
|
|
// Make sure the window is visible to begin with * bug fix for ms word*
|
|
&& ( ::IsWindowVisible(hwnd))
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
else if ( // We also want to hook into the FileOpen & FileSaveAs Dialog
|
|
|
|
/* top-raised windows */
|
|
( ( dwExStyle & WS_EX_WINDOWEDGE ) == WS_EX_WINDOWEDGE )
|
|
|
|
/* Styles to test for (named above) */
|
|
&& ( ( dwStyle & WS_STYLES_TO_TEST_FOR_DIALOGS ) == WS_STYLES_TO_TEST_FOR_DIALOGS )
|
|
|
|
/* Has a parent = it is a top-level window with parent */
|
|
&& ( GetParent(hwnd) != NULL )
|
|
|
|
/* Check if Window's parent is hooked into */
|
|
&& ( s_ButtonHookAccess->FindHookedWnd(GetParent(hwnd)) == TRUE )
|
|
|
|
/* Check if Window is not already hooked into*/
|
|
&& ( s_ButtonHookAccess->FindHookedWnd(hwnd) == FALSE )
|
|
|
|
/* Check if Window has not been recently closed in this process*/
|
|
&& ( FindRecentClosedWindow(hwnd) == FALSE )
|
|
|
|
// **** CUSTOM INCLUSION LIST ****
|
|
&& ( IsWhiteListed(GetParent(hwnd)) )
|
|
|
|
// Make sure the window's parent is visible to begin with * bug fix for ms word *
|
|
&& ( ::IsWindowVisible(GetParent(hwnd)))
|
|
|
|
// * We currently hook only into Top-Level FileOpen/SaveAs Dialogs
|
|
&& ( IsFileOpenOrFileSaveAsDialog(hwnd) )
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: HookWndProc()
|
|
// Desc: Function that implements the WndProc for every process that
|
|
// has the window we are looking for. This is code is shared within
|
|
// the process on every window in the system.
|
|
//
|
|
// Note: This function is just the WndProc() Implementation
|
|
// All we have to do is process it if we care for it or pass it down
|
|
// to the next hook (that may be in the system).
|
|
//
|
|
// Prms: hWnd, handle to window
|
|
// Msg, passed in message
|
|
// wParam, specifies additional message information
|
|
// lParam, specifies additional message information
|
|
//
|
|
// Retr: LRESULT,
|
|
//////////////////////////////////////////////////////////////////////
|
|
LRESULT CALLBACK MyWndProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
// default return
|
|
LRESULT result = 0;
|
|
|
|
// Get the HookWndItem Information
|
|
BLANK_HookWndItem(item);
|
|
item = s_ButtonHookAccess->GetHookedWndItem(hWnd);
|
|
|
|
bool bCloseMsg = ((Msg == WM_NCDESTROY) || (Msg == WM_DESTROY) || (Msg == WM_CLOSE));
|
|
if(bCloseMsg)
|
|
log(LOGGING_DEBUG, L"Received WM_NCDESTROY or DESTROY MESSAGE or WM_CLOSE for window %u", item.hWnd);
|
|
|
|
// Is this is a Window that is hooked? --- Ignore FileOpen / SaveDialog (Deal with them below)
|
|
if((item.hWnd != NULL) && (hWnd == item.hWnd) && !IsFileOpenOrFileSaveAsDialog(item.hWnd))
|
|
{
|
|
////
|
|
// Create wpfcaller object for this Window, if it doesn't exist
|
|
// ~Every wpfcaller is mapped to a single HookWndItem
|
|
////
|
|
if(item.wpfcaller == NULL)
|
|
{
|
|
log(LOGGING_DEBUG, L"Creating a wpfCaller Instance for window %u", item.hWnd);
|
|
item.wpfcaller = new WPFCaller(item, s_hModule);
|
|
}
|
|
|
|
////
|
|
// Case StopHook wants us to Unhook the WmProc Hook or
|
|
// the Window gets Closed
|
|
////
|
|
if ( (Msg == m_WMUnHook) || bCloseMsg )
|
|
{
|
|
log(LOGGING_MEDIUM, L"Received WMUnook or bCloseMsg message for window %u", item.hWnd);
|
|
|
|
// No Longer need to monitor this Window
|
|
s_ButtonHookAccess->DeleteHookedWnd(item.hWnd);
|
|
InsertRecentClosedWindow(item.hWnd);
|
|
|
|
// Unhook Our WndProc Procedure (put the old one back)
|
|
log(LOGGING_MEDIUM, L"Unhooking a Window %u from the HookWndProc", item.hWnd);
|
|
SetWindowLong(hWnd,GWL_WNDPROC,(LONG) item.DefWndProc);
|
|
|
|
// Delete this Window's CaptionButton Object
|
|
if(item.wpfcaller != NULL)
|
|
{
|
|
log(LOGGING_DEBUG, L"Deleting a wpfCaller Instance for Window %u", item.hWnd);
|
|
delete item.wpfcaller;
|
|
}
|
|
|
|
// Call the old WindProc to handle Close message
|
|
if(bCloseMsg)
|
|
result = CallWindowProc( item.DefWndProc, hWnd, Msg, wParam, lParam );
|
|
}
|
|
// All other Messages
|
|
else
|
|
{
|
|
// FOR TESTING - Skip Handling of Messages (Below)
|
|
// result = CallWindowProc( item->DefWndProc, hWnd, Msg, wParam, lParam );
|
|
try
|
|
{
|
|
// the wpfCaller Handles all other Messages
|
|
if(item.wpfcaller != NULL)
|
|
result = item.wpfcaller->OnDefault( hWnd, Msg, wParam, lParam);
|
|
else
|
|
result = CallWindowProc( item.DefWndProc, hWnd, Msg, wParam, lParam);
|
|
}
|
|
catch(...)
|
|
{
|
|
log(LOGGING_LOW, L"*Error* Occured calling WpfCaller->OnDefault()");
|
|
}
|
|
}
|
|
}
|
|
else if((item.hWnd != NULL) && (hWnd == item.hWnd) && IsFileOpenOrFileSaveAsDialog(item.hWnd)) // Deal with FileOpen / FileSave Dialog Here
|
|
{
|
|
////
|
|
// Case StopHook wants us to Unhook the WmProc Hook or
|
|
// the Window gets Closed
|
|
////
|
|
if ( (Msg == m_WMUnHook) || bCloseMsg )
|
|
{
|
|
log(LOGGING_DEBUG, L"FileOpenOrFileSaveDialog() Received WMUnook or bCloseMsg message for Window %u", item.hWnd);
|
|
|
|
// No Longer need to monitor this Window
|
|
s_ButtonHookAccess->DeleteHookedWnd(item.hWnd);
|
|
InsertRecentClosedWindow(item.hWnd);
|
|
|
|
// Unhook Our WndProc Procedure (put the old one back)
|
|
log(LOGGING_DEBUG, L"FileOpenOrFileSaveDialog() Unhooking from the HookWndProc for Window %u", item.hWnd);
|
|
SetWindowLong(hWnd,GWL_WNDPROC,(LONG) item.DefWndProc);
|
|
|
|
// Call the old WindProc to handle Close message
|
|
if(bCloseMsg)
|
|
{
|
|
// Handle CLose Message
|
|
result = CallWindowProc( item.DefWndProc, hWnd, Msg, wParam, lParam );
|
|
}
|
|
}
|
|
else if((Msg == WM_COMMAND) && (LOWORD(wParam) == 1)) // Received Ok or Open Button
|
|
{
|
|
log(LOGGING_DEBUG, L"Reveived Command Message from Dialog Button %u - Ok or Open Button",LOWORD(wParam));
|
|
// First Let's get the WPFCaller Instance of the main window and let it know that this occured
|
|
BLANK_HookWndItem(itemTmp);
|
|
itemTmp = s_ButtonHookAccess->GetHookedWndItem(GetParent(hWnd));
|
|
|
|
if(itemTmp.hWnd != NULL && itemTmp.wpfcaller != NULL)
|
|
{
|
|
// we want to obtain the File Location & File Name & File Types from the Dialogs
|
|
// ~this is the whole point why we hooked into it
|
|
log(LOGGING_DEBUG, L"Calling GetFileNameAndLocationStringValuesFromDialogWindow()");
|
|
if((GetFileNameAndLocationStringValuesFromDialogWindow(hWnd, itemTmp.wpfcaller)) &&
|
|
(lstrlen(itemTmp.wpfcaller->m_strFileNameLocationBuf) > 0))
|
|
{
|
|
log(LOGGING_DEBUG, L"About to post WM_CLIENT_EVENT_OPENFILEORSAVEASDIALOGOCCURED to WPFCallerThread");
|
|
log(LOGGING_DEBUG, L"Found FileNameLocation - %s", itemTmp.wpfcaller->m_strFileNameLocationBuf);
|
|
log(LOGGING_DEBUG, L"Found FileTypes - %s", itemTmp.wpfcaller->m_strFileTypesBuf);
|
|
|
|
// Post to the WPFCallerThread that this Event Occured
|
|
// WM_CLIENT_EVENT_OPENFILEORSAVEASDIALOGOCCURED (WM_USER + 808)
|
|
PostThreadMessage(itemTmp.wpfcaller->m_threadID, (WM_USER + 808),0,0);
|
|
}
|
|
else
|
|
{
|
|
// This should resolve now since we are looking only for command id 1
|
|
log(LOGGING_LOW, L"FileOpenOrFileSaveDialog() Error - GetFileNameAndLocationStringValuesFromDialogWindow Failed!");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log(LOGGING_LOW, L"FileOpenOrFileSaveDialog() Error - Couldn't fetch Parents WPFCaller object - skipping file resolution");
|
|
}
|
|
}
|
|
|
|
// Forward all Messages
|
|
result = CallWindowProc(item.DefWndProc, hWnd, Msg, wParam, lParam);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: CallWndHookProc()
|
|
// Desc: Function that will replace the normal WndProc with the HookWndProc
|
|
// procedure. It will do that for every process on the system that
|
|
// has a window. It will assign the function HookWndProc()
|
|
//
|
|
// Prms: nCode [in] = if == HC_ACTION CallWndHookProc must process the message
|
|
// wParam [in] = specifies if message is send by current process
|
|
// non-zero if it is current process, null otherwise
|
|
// LPARAM [in] = pointer to a CWPRETSTRUCT that contains details about the message
|
|
//
|
|
// Note: see MSDN for CallWndRetProc Function passed in with SetWindowHookEx
|
|
// ~We don't check wParam (pass directly thru) because we want to process all messages
|
|
// for all processes and don't care where it originates
|
|
//
|
|
// IMP!: In this function we check the Window and decide whether to display the button or not
|
|
// ~!IMP) WH_CALLWNDPROC hook is called in the context of the thread that calls SendMessage
|
|
//
|
|
// Retr: LRESULT,
|
|
//////////////////////////////////////////////////////////////////////
|
|
LRESULT WINAPI CallWndHookProc(int nCode, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
|
|
if (nCode == HC_ACTION)
|
|
{
|
|
LPCWPSTRUCT pCwp = (LPCWPSTRUCT) lParam;
|
|
|
|
// Do we care about this Window?, We don't want to hook into all Windows
|
|
if(IsAWindowToHookInto(pCwp->hwnd))
|
|
{
|
|
// Do we care about this Message?
|
|
if((pCwp->message == m_WMHook) /* We'll receive the Hook Message StartButtonHook is called */
|
|
|| (pCwp->message == WM_NCACTIVATE) /* Activates & deactivates a window, caption is redrawn. */
|
|
|| (pCwp->message == WM_CREATE) /* A new window is being created */
|
|
|| (pCwp->message == WM_ACTIVATE) /* a window is being activated */
|
|
|| (pCwp->message == WM_PAINT) /* I added this after figguring out that drag n' droped files don't trigger above messages */
|
|
|| (pCwp->message == WM_NCPAINT) /* Same as for WM_PAINT */
|
|
)
|
|
{
|
|
|
|
// Log that we are hooking into a FileOpen Or FileSaveAsDialog
|
|
if(IsFileOpenOrFileSaveAsDialog(pCwp->hwnd))
|
|
log(LOGGING_DEBUG, L"FileOpenOrFileSaveDialog() We are hooking into a FileOpen or FileSaveDialog");
|
|
|
|
// Create a hook Item, We are ready to hook into this Window
|
|
HookWndItem item;
|
|
|
|
// hWnd = the handle to the current window
|
|
// DefWndProc = handle of the this window procedure (the address that is replaced by our Hook Function)
|
|
// HookWndProc = the address of this DLL instance's HookWndProc Function
|
|
item.hWnd = pCwp->hwnd;
|
|
item.DefWndProc = reinterpret_cast<WNDPROC>(GetWindowLong(pCwp->hwnd,GWL_WNDPROC));
|
|
item.HookWndProc = MyWndProc;
|
|
item.wpfcaller = NULL; // The Caption Button will be initialized by MyHookWnd
|
|
|
|
// Add the hook Item to the Global List
|
|
s_ButtonHookAccess->InsertHookedWndItem(item);
|
|
|
|
////
|
|
// ********************** INSTALL THE NEW WNDHOOK_PROCEDURE **********************
|
|
// *******************************************************************************
|
|
// GWL_WNDPROC - Sets a new address for the window procedure (WinProc())
|
|
// Windows NT/2000/XP: You cannot change this attribute if the window does not
|
|
// belong to the same process as the calling thread. */The hook is responsible for
|
|
// putting the .dll into the memoryspace of every process. This Dll iterates thru
|
|
// all the Windows and right here hooks into the WndProc
|
|
////
|
|
log(LOGGING_LOW, L"Installing HookWndProc for Window %u", pCwp->hwnd);
|
|
|
|
// We have to call SetWindowLongPtr to install the WNDPROC. Not doins so causes
|
|
// our UnHook message to never be received by the Hooked Wnd
|
|
int result = SetWindowLongPtr( pCwp->hwnd, GWL_WNDPROC, (LONG) MyWndProc );
|
|
}
|
|
}
|
|
}
|
|
|
|
// We now pass in our Hook Handle down the current hook chain (in case other hooks are listening)
|
|
return (CallNextHookEx( s_ButtonHookAccess->get_hHook(), nCode, wParam, lParam ));
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: Helper_ReadSafeArrayIntoWCharArr()
|
|
// Desc: Small Helper function to quickly read a SafeArray Into a WChar Array
|
|
//
|
|
// Prms: +pDes, char destination buffer to copy into
|
|
// -size, passes out the size of the safearray
|
|
// +pSrc, the Safearray source to copy from
|
|
//
|
|
// Note: Not much safety checking going on. Make sure the buffer has enough space!
|
|
// ~also only 1 dimensional arrays/safearrays work
|
|
//
|
|
// IMP!: MAX_PATH is the ultimate Max of size and elements of the pDest Buffer
|
|
//////////////////////////////////////////////////////////////////////
|
|
void Helper_ReadSafeArrayIntoWCharArr(wchar_t pDes[MAX_PATH][MAX_PATH],UINT& size,SAFEARRAY* pSrc)
|
|
{
|
|
try
|
|
{
|
|
// this func only handles 1-dim arrays/safearrays
|
|
const int ONE_DIMENSIONAL = 1;
|
|
|
|
long lUBound = 0;
|
|
long lLBound = 0;
|
|
SafeArrayGetLBound(pSrc, ONE_DIMENSIONAL, &lLBound);
|
|
SafeArrayGetUBound(pSrc, ONE_DIMENSIONAL, &lUBound);
|
|
|
|
BSTR* pBSTR;
|
|
SafeArrayAccessData(pSrc, (void**) &pBSTR);
|
|
for (long n = lLBound; ((n <= lUBound) && (n < MAX_PATH)); ++n)
|
|
{
|
|
_bstr_t bstr(pBSTR[n], true);
|
|
lstrcpyn(&pDes[n][0],(wchar_t*) bstr , MAX_PATH);
|
|
}
|
|
SafeArrayUnaccessData(pSrc);
|
|
|
|
// pass out the size
|
|
size = lUBound - lLBound + 1;
|
|
|
|
// pSrc was empty so set size to 0
|
|
if((size == 1) && (pDes[0][0] == 0))
|
|
size = 0;
|
|
}
|
|
catch(...)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: ReadOoganizerSettings()
|
|
// Desc: Calls OoganizerSettings COM Object to read in all the settings
|
|
// into our SharedDS.
|
|
//
|
|
// These are the important settings we need like Logging details,
|
|
// What windows to hook into, what window classes, and even
|
|
// window layout as well as debugging options
|
|
//
|
|
// Retr: true, if settings were read, false otherwise
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool ReadOoganizerSettings()
|
|
{
|
|
try
|
|
{
|
|
IOoganizerSettingsPtr pSettings(__uuidof(OoganizerSettingsAcc));
|
|
|
|
// Read Configuration File
|
|
_configurationPtr config = NULL;
|
|
if(FAILED(pSettings->ReadConfigXMLFile(&config)))
|
|
return false;
|
|
|
|
// Get ButtonHook Settings
|
|
_ButtonHookSettingsPtr buttonhookSettings = NULL;
|
|
if(FAILED(config->get_ButtonHook(&buttonhookSettings)))
|
|
return false;
|
|
|
|
// Let's do that First! ~Imp! Get/Set LogPath & Debug Settings
|
|
_bstr_t LogPath;
|
|
_LoggingSettingsPtr logSettings;
|
|
buttonhookSettings->get_LogSettings(&logSettings);
|
|
logSettings->get_LogPath(LogPath.GetAddress());
|
|
s_ButtonHookAccess->SetLogPath((wchar_t*) LogPath);
|
|
log(LOGGING_DEBUG, L"Setting Log Path: %s", (wchar_t*) LogPath);
|
|
|
|
// Logging Detail
|
|
unsigned long nLoggingDetail = 0;
|
|
logSettings->get_LoggingDetail(&nLoggingDetail);
|
|
s_ButtonHookAccess->SetLoggingDetail(static_cast<LoggingDetail>(nLoggingDetail));
|
|
#ifdef _DEBUG
|
|
log(LOGGING_DEBUG, L"Setting Log Detail: 4 (DEBUG)", nLoggingDetail);
|
|
#else
|
|
log(LOGGING_DEBUG, L"Setting Log Detail: %u", nLoggingDetail);
|
|
#endif
|
|
|
|
////
|
|
// Now set this instance's Logging option
|
|
////
|
|
SetLoggingDetail(s_ButtonHookAccess->GetLoggingDetail(),s_ButtonHookAccess->GetLogPath());
|
|
|
|
SAFEARRAY* safeArray;
|
|
|
|
// Get AllowedProcessNames
|
|
_AllowedProcessNamesWPtr processNames;
|
|
buttonhookSettings->get_AllowedProcessNames(&processNames);
|
|
processNames->get_ProcessName(&safeArray);
|
|
Helper_ReadSafeArrayIntoWCharArr(s_ButtonHookAccess->ALLOWED_PROCESS_NAMES,s_ButtonHookAccess->NUMBER_OF_ALLOWED_PROCESS_NAMES,safeArray);
|
|
|
|
// log values
|
|
if(s_ButtonHookAccess->NUMBER_OF_ALLOWED_PROCESS_NAMES)
|
|
{
|
|
for (UINT i = 0; i < s_ButtonHookAccess->NUMBER_OF_ALLOWED_PROCESS_NAMES; ++i)
|
|
log(LOGGING_DEBUG, L"AllowedProcessName: %s", s_ButtonHookAccess->ALLOWED_PROCESS_NAMES[i]);
|
|
}
|
|
|
|
// Get AllowedWindowTitles
|
|
_AllowedWindowTitlesWPtr windowTitles;
|
|
buttonhookSettings->get_AllowedWindowTitles(&windowTitles);
|
|
windowTitles->get_WindowTitle(&safeArray);
|
|
Helper_ReadSafeArrayIntoWCharArr(s_ButtonHookAccess->ALLOWED_WINDOW_TITLES,s_ButtonHookAccess->NUMBER_OF_ALLOWED_WINDOW_TITLES,safeArray);
|
|
|
|
// log values
|
|
if(s_ButtonHookAccess->NUMBER_OF_ALLOWED_WINDOW_TITLES)
|
|
{
|
|
for (UINT i = 0; i < s_ButtonHookAccess->NUMBER_OF_ALLOWED_WINDOW_TITLES; ++i)
|
|
log(LOGGING_DEBUG, L"AllowedWindowTitle: %s", s_ButtonHookAccess->ALLOWED_WINDOW_TITLES[i]);
|
|
}
|
|
|
|
// Get AllowedWindowClasses
|
|
_AllowedWindowClassesWPtr windowClasses;
|
|
buttonhookSettings->get_AllowedWindowClasses(&windowClasses);
|
|
windowClasses->get_WindowClass(&safeArray);
|
|
Helper_ReadSafeArrayIntoWCharArr(s_ButtonHookAccess->ALLOWED_WINDOW_CLASSES,s_ButtonHookAccess->NUMBER_OF_ALLOWED_WINDOW_CLASSES,safeArray);
|
|
|
|
// log values
|
|
if(s_ButtonHookAccess->NUMBER_OF_ALLOWED_WINDOW_CLASSES)
|
|
{
|
|
for (UINT i = 0; i < s_ButtonHookAccess->NUMBER_OF_ALLOWED_WINDOW_CLASSES; ++i)
|
|
log(LOGGING_DEBUG, L"AllowedWindowClass: %s", s_ButtonHookAccess->ALLOWED_WINDOW_CLASSES[i]);
|
|
}
|
|
return true;
|
|
}
|
|
catch(...)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: SystemMetricsTesterHELPER()
|
|
// Desc: Use this for Debugging only when you need to know certain system metrics values
|
|
//////////////////////////////////////////////////////////////////////
|
|
void SystemMetricsTesterHELPER()
|
|
{
|
|
int k = 0;
|
|
k = GetSystemMetrics(SM_CXEDGE); //2 on vista
|
|
k = GetSystemMetrics(SM_CYEDGE); //2 on vista
|
|
|
|
k = GetSystemMetrics(SM_CXFRAME); //8 on vista
|
|
k = GetSystemMetrics(SM_CYFRAME); //8 on vista
|
|
|
|
k = GetSystemMetrics(SM_CXFIXEDFRAME); //3 on vista
|
|
k = GetSystemMetrics(SM_CYFIXEDFRAME); //3 on vista
|
|
|
|
k = GetSystemMetrics(SM_CXMIN); //124 on vista
|
|
k = GetSystemMetrics(SM_CYMIN); //36 on vista
|
|
|
|
k = GetSystemMetrics(SM_CXBORDER); // 1 on vista
|
|
k = GetSystemMetrics(SM_CYBORDER); // 1 on vista
|
|
|
|
k = GetSystemMetrics(SM_CXSIZE); //32 on vista
|
|
k = GetSystemMetrics(SM_CYSIZE); //19 on vista
|
|
|
|
k = GetSystemMetrics(SM_CYCAPTION); //20 on vista
|
|
//k = GetSystemMetrics(SM_CXCAPTION);
|
|
|
|
k = GetSystemMetrics(SM_CYSMCAPTION); //18 on vista
|
|
//k = GetSystemMetrics(SM_CXSMCAPTION);
|
|
|
|
k = GetSystemMetrics(SM_CYSMSIZE); //17 on vista
|
|
k = GetSystemMetrics(SM_CXSMSIZE); //17 on vista
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: StartWinProcButtonHook()
|
|
// Desc: Exported DLL function to allow a process to start/attach
|
|
// the system WinProcButtonhook. This function will attach the WinProc function
|
|
// to the process's window via the CallWndHookProc() function
|
|
//
|
|
// Note: We'll check if the system is supported here. We'll only allow
|
|
// to start the hook, if that is the case
|
|
//
|
|
// Retr: TRUE, if succesfully attached, FALSE otherwise (i.e. OS NOT SUPPORTED)
|
|
//////////////////////////////////////////////////////////////////////
|
|
BOOL WINAPI StartButtonHook()
|
|
{
|
|
// Debugging ONLY when we need certain values (comment/uncomment)
|
|
//SystemMetricsTesterHELPER();
|
|
|
|
// Let's try to see if we can force a rehook
|
|
bool bThereWasAnExistingHook = false;
|
|
if(s_ButtonHookAccess->get_hHook() != NULL)
|
|
{
|
|
s_ButtonHookAccess->set_hHook(NULL);
|
|
bThereWasAnExistingHook = true;
|
|
}
|
|
|
|
// Global doesn't exist, we can start
|
|
if(s_ButtonHookAccess->get_hHook() == NULL)
|
|
{
|
|
// Delete All Shared Data Hook Data - Just in case previous DLL Instance corrupted HookData
|
|
s_ButtonHookAccess->ClearHookSData();
|
|
|
|
// Don't start ButtonHook unless the OS supports it
|
|
VARIANT_BOOL bIsSupported;
|
|
_EnvironmentCCWPtr env(__uuidof(EnvironmentCCW));
|
|
env->IsWindowsOSSupported(&bIsSupported);
|
|
|
|
if(bIsSupported == -1) // TRUE
|
|
{
|
|
bool bSettings = ReadOoganizerSettings();
|
|
clearLog(); // Start Fresh (with a new Log)
|
|
|
|
// We won't be able to log until we have read in the settings
|
|
if(bThereWasAnExistingHook)
|
|
log(LOGGING_LOW, L"A previous ButtonHook Existed - overwriting it");
|
|
|
|
if(!bSettings)
|
|
log(LOGGING_LOW, L"ReadOoganizerSettings Failed!");
|
|
else
|
|
log(LOGGING_MEDIUM, L"ReadOoganizerSettings Succeeded");
|
|
|
|
// There really is no sense to start ButtonHook with no Settings Info
|
|
if(bSettings)
|
|
{
|
|
log(LOGGING_LOW, L"StartButtonHook Called()");
|
|
|
|
// Create/Store Global Hook (every instance needs access to this HOOK)
|
|
// ~WH_CALLWNDPROC hook is called in the context of the thread that calls SendMessage
|
|
log(LOGGING_DEBUG, L"Setting the Windows Hook");
|
|
HHOOK hook = SetWindowsHookEx( WH_CALLWNDPROC, CallWndHookProc, s_hModule, 0 );
|
|
|
|
if(hook == NULL)
|
|
{
|
|
log(LOGGING_LOW, L"Windows Hook Failed error %i", GetLastError());
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Assign the hook value to the Global DS
|
|
log(LOGGING_DEBUG, L"Windows Hook Succeeded Hook Set %i", hook);
|
|
s_ButtonHookAccess->set_hHook(hook);
|
|
}
|
|
|
|
// Send Hook Msg - This forces all windows already open in the system and that
|
|
// we want to hook into to load the captionbutton
|
|
|
|
if(m_WMHook >= 0xC000 && m_WMHook <= 0xFFFF)
|
|
{
|
|
log(LOGGING_DEBUG, L"StartButtonHook - About to call EnumWindows");
|
|
EnumWindows( EnumProc, _MSG_HOOK );
|
|
log(LOGGING_LOW, L"StartButtonHook EnumWindows Returns - Everything is O.K.");
|
|
}
|
|
else
|
|
{
|
|
log(LOGGING_LOW, L"Registered Message m_WMHook is invalid - something is terribly wrong");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: StopWinProcButtonHook()
|
|
// Desc: Exported DLL function to allow a process to stop/deattach
|
|
// the WinProcButton system hook
|
|
//
|
|
// Retr: TRUE, if succesfully deattached, FALSE otherwise
|
|
//////////////////////////////////////////////////////////////////////
|
|
BOOL WINAPI StopButtonHook()
|
|
{
|
|
// Global does exist, we can exit
|
|
if(s_ButtonHookAccess->get_hHook() != NULL)
|
|
{
|
|
// Send UnHook Msg
|
|
if(m_WMUnHook >= 0xC000 && m_WMUnHook <= 0xFFFF)
|
|
{
|
|
log(LOGGING_LOW, L"StopButtonHook Called()");
|
|
EnumWindows( EnumProc, _MSG_UNHOOK );
|
|
log(LOGGING_LOW, L"StopButtonHook() EnumWindows Returns - Everything is O.K.");
|
|
}
|
|
else
|
|
{
|
|
log(LOGGING_LOW, L"Registered Message m_WMUnHook is invalid - something is terribly wrong");
|
|
return FALSE;
|
|
}
|
|
|
|
try
|
|
{
|
|
// now we can exit
|
|
if ( UnhookWindowsHookEx( s_ButtonHookAccess->get_hHook() ) != 0 )
|
|
{
|
|
log(LOGGING_MEDIUM, L"Successfully Unhooked Hook %i", s_ButtonHookAccess->get_hHook());
|
|
}
|
|
else
|
|
{
|
|
log(LOGGING_LOW, L"Error Occrured in StopButtonHook %i", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
// Reset Global Hook - Delete All Shared Data
|
|
log(LOGGING_HIGH, L"Reset Global Hook & Clearing ALL Hook Related Data");
|
|
s_ButtonHookAccess->set_hHook(NULL);
|
|
s_ButtonHookAccess->ClearHookSData();
|
|
|
|
// New Code: ~errors out~ Unregister all Window Classes here (used to be done in ~WPFCaller())
|
|
/*if(!UnregisterClass(L"W32ButtonHookWndClass", s_hModule))
|
|
log(LOGGING_DEBUG, L"Error occured for UnregisterClass W32ButtonHookWndClass");*/
|
|
}
|
|
catch(...)
|
|
{
|
|
log(LOGGING_LOW, L"Error occured in StopButtonHook");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: IsButtonHookSet()
|
|
// Desc: Exported DLL function to allow a caller to know if the buttonhook
|
|
// is set or not
|
|
//
|
|
// Retr: TRUE, if Hook is attached, FALSE otherwise
|
|
//////////////////////////////////////////////////////////////////////
|
|
BOOL WINAPI IsButtonHookSet()
|
|
{
|
|
return (s_ButtonHookAccess->get_hHook() != NULL);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: GetAllHookedWindowHandles()
|
|
// Desc: Exported DLL function to allow a caller to retrieve all Hooked
|
|
// Window Handles.
|
|
// ~passed in pBuf must be at least of size MAX_PATH
|
|
//
|
|
// Retr: int, returns the size of the passed out pBuf list
|
|
//////////////////////////////////////////////////////////////////////
|
|
int WINAPI GetAllHookedWindowHandles(int* pBuf)
|
|
{
|
|
if(s_ButtonHookAccess->get_hHook() != NULL)
|
|
{
|
|
int buf[MAX_PATH] = {0};
|
|
int nsize = 0;
|
|
s_ButtonHookAccess->GetHookedWindowListNSize(buf,&nsize);
|
|
|
|
////
|
|
// there could be nulls in the buf, so filter them out,
|
|
// to return a complete list the caller. Copy only Non-Null
|
|
// values to the output buffer * and ONLY EXISTING WINDOWS *
|
|
////
|
|
int nNullsOrInvalid = 0;
|
|
for(int i = 0; i < nsize; ++i)
|
|
{
|
|
if(buf[i] != NULL && ::IsWindow((HWND) buf[i]))
|
|
*(pBuf + i) = buf[i];
|
|
else
|
|
++nNullsOrInvalid;
|
|
}
|
|
|
|
// return the adjusted size, if needed
|
|
log(LOGGING_DEBUG, L"GetAllHookedWindowHandles() got called returning WindowHandles Buf size of %u", (nsize - nNullsOrInvalid));
|
|
return (nsize - nNullsOrInvalid);
|
|
}
|
|
return 0;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Func: DllMain()
|
|
// Desc: Dll Main Entry Point - The DLL is being attached to every
|
|
// process in the system! ~therefore it must be fast. Also
|
|
// we can't start the main register hook function from here
|
|
// system call from here. (because this dll get loaded many times,
|
|
// we only however want the system hook code to be called only once.
|
|
//
|
|
// Note: Attaching the dll will cause MSG_HOOK to be registred
|
|
//
|
|
// Retr: TRUE, always
|
|
//////////////////////////////////////////////////////////////////////
|
|
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
|
|
{
|
|
|
|
switch ( ul_reason_for_call )
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
// This is to access DS segment's Hook Information,
|
|
// Across all instances of this DLL.
|
|
s_ButtonHookAccess = new CWHPaccess();
|
|
|
|
// Store local Module Handle
|
|
s_hModule = (HINSTANCE) hModule;
|
|
|
|
// message is used to send to all windows when the HOOK attaches
|
|
m_WMHook = RegisterWindowMessage( MSG_HOOK );
|
|
|
|
// message is used to send to all windows when the HOOK unattaches
|
|
m_WMUnHook = RegisterWindowMessage( MSG_UNHOOK );
|
|
|
|
// get the shared setting logging information and store it into this instance
|
|
SetLoggingDetail(s_ButtonHookAccess->GetLoggingDetail(), s_ButtonHookAccess->GetLogPath());
|
|
|
|
break;
|
|
case DLL_PROCESS_DETACH:
|
|
case DLL_THREAD_DETACH:
|
|
case DLL_THREAD_ATTACH:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
} |