Files
Oogynize/Hooks/ButtonHook/WPFCaller.cpp

1417 lines
51 KiB
C++

#include "StdAfx.h"
#include "WPFCaller.h"
#include "CWHPaccess.h"
#include "shellapi.h"
#include "dde.h"
#include "ddeml.h"
using namespace Ooganizer;
// Specifies a nonzero timer identifier
#define NID_RESTORE_EVENT_TIMER (WM_USER + 1)
#define NID_CLOSE_BUT_NOT_DESTROY_EVENT_TIMER (WM_USER + 2)
// * Messages send from WPFCaller class to WPFProc Thread
#define WM_KILLTHREAD (WM_USER + 501)
// * Messages Send from W32Button to WPFProc Thread
#define WM_GETBUTTONTHEMESETTING (WM_USER + 601)
#define WM_W32BUTTONCLICKED (WM_USER + 602)
// * External Messages * Send to us by outside
// Programs (most likely WPFForm or Platform)
#define WM_W32PINGISALIVE (WM_USER + 700)
#define WM_W32SETBUTTONSTATE (WM_USER + 701)
#define WM_W32GETBUTTONSTATE (WM_USER + 702)
#define WM_W32SETPARENTWINDOWTRANSPERANCY (WM_USER + 703)
#define WM_W32SETPARENTWINDOWACTIVE (WM_USER + 704)
#define WM_W32POSTMESSAGETOPARENTWINDOW (WM_USER + 705)
// * Messages Only handled by WPFThreadProc * Events Send to
// C# Client Event Dispatcher for handling
#define WM_CLIENT_EVENT_WINDOWACTIVATE (WM_USER + 800)
#define WM_CLIENT_EVENT_DRAGNDROPOCCURED (WM_USER + 801)
#define WM_CLIENT_EVENT_MDICHILDWINDOWSWITCHOCCURED (WM_USER + 802)
#define WM_CLIENT_EVENT_PARENTGETSETTEXTOCCURED (WM_USER + 803)
#define WM_CLIENT_EVENT_PARENTNOTIFYLBUTTONCLICKOCCURED (WM_USER + 804)
#define WM_CLIENT_EVENT_PARENTNOTIFYWMCREATEOCCURED (WM_USER + 805)
#define WM_CLIENT_EVENT_PARENTCLOSEWITHOUTDESTROYOCCURED (WM_USER + 806)
#define WM_CLIENT_EVENT_PARENTSETFOCUSOCCURED (WM_USER + 807)
#define WM_CLIENT_EVENT_OPENFILEORSAVEASDIALOGOCCURED (WM_USER + 808)
#ifdef _DEBUG
#import "..\\..\\Target\\Debug\\Settings.tlb" raw_interfaces_only
#else
#import "..\\..\\Target\\Release\\Settings.tlb" raw_interfaces_only
#endif
using namespace Settings;
// access the SharedDS *initialized by WPFCaller() constructor*
CWHPaccess* s_WPFCallerAccess = NULL;
//////////////////////////////////////////////////////////////////////
// Func: ReadButtonThemeSetting
// Desc: static function to quickly read in Button Theme Settings.
// ~Uses COM, must be called ONLY from within a COM initialized Thread!
//////////////////////////////////////////////////////////////////////
bool ReadButtonThemeSetting(WPFCaller* pCaller)
{
HRESULT hr = S_FALSE;
_ButtonThemeNIconSettingsCCWPtr pButtonThemeNIconSetting;
hr = pButtonThemeNIconSetting.CreateInstance(__uuidof(ButtonThemeNIconSettingsCCW));
if(SUCCEEDED(hr))
{
log(LOGGING_HIGH, L"Succeed creating ButtonThemeNIconSettings Instance");
}
else
{
log(LOGGING_LOW, L"FAILED to create the ButtonThemeNIconSettings COM Instance");
return false;
}
////
// Get the Button Theme N Icon Settings
////
IButtonThemeSettingPtr pThemeSetting;
IButtonIconLocationPtr pIconLocation;
pButtonThemeNIconSetting->GetIButtonThemeInterface(&pThemeSetting);
pButtonThemeNIconSetting->GetIButtonIconLocation(&pIconLocation);
if(pThemeSetting == NULL)
log(LOGGING_LOW, L"pThemeSetting == NULL - Error");
if(pIconLocation == NULL)
log(LOGGING_LOW, L"pIconLocation == NULL - Error");
if((pThemeSetting == NULL) || (pIconLocation == NULL))
return false;
////
// Get Button Dimension/Location
////
if(SUCCEEDED(hr))
hr = pThemeSetting->Top(&pCaller->m_ButtonThemeSetting.Top);
if(SUCCEEDED(hr))
hr = pThemeSetting->Right(&pCaller->m_ButtonThemeSetting.Right);
if(SUCCEEDED(hr))
hr = pThemeSetting->Height(&pCaller->m_ButtonThemeSetting.Height);
if(SUCCEEDED(hr))
hr = pThemeSetting->Width(&pCaller->m_ButtonThemeSetting.Width);
////
// Get Button Icon Location
////
if(SUCCEEDED(hr))
pIconLocation->GetBUTTON_UP(pCaller->m_ButtonThemeSetting.bstrBUTTON_UP.GetAddress());
if(SUCCEEDED(hr))
pIconLocation->GetBUTTON_DOWN(pCaller->m_ButtonThemeSetting.bstrBUTTON_DOWN.GetAddress());
if(SUCCEEDED(hr))
pIconLocation->GetTOGGLE_UP(pCaller->m_ButtonThemeSetting.bstrTOGGLE_UP.GetAddress());
if(SUCCEEDED(hr))
{
log(LOGGING_HIGH, L"ReadButtonTheme Settings Successfully - Height=%i, Width=%i, Top=%i, Right=%i",
pCaller->m_ButtonThemeSetting.Height,pCaller->m_ButtonThemeSetting.Width,pCaller->m_ButtonThemeSetting.Top, pCaller->m_ButtonThemeSetting.Right);
if(pCaller->m_ButtonThemeSetting.bIsInitialized)
pCaller->m_ButtonThemeSetting.bIsChanged = true;
if(!pCaller->m_ButtonThemeSetting.bIsInitialized)
pCaller->m_ButtonThemeSetting.bIsInitialized = true;
}
else
{
pCaller->m_ButtonThemeSetting.bIsChanged = false;
pCaller->m_ButtonThemeSetting.bIsInitialized = false;
log(LOGGING_LOW, L"ReadButtonTheme Settings FAILED!");
}
if(SUCCEEDED(hr))
return true;
else
return false;
}
////
// When we want to communicate back to the ButtonHook from externaly windows,
// we use these custom messages in order to do so. We keep track of them seperatly
// because we allow Custom Messages to return a Value to the caller.
////
UINT s_nMessagesCustomMessages = 6;
UINT s_nMessagesCustomMessagesWeHave[] =
{
WM_W32PINGISALIVE,
WM_W32SETBUTTONSTATE,
WM_W32GETBUTTONSTATE,
WM_W32SETPARENTWINDOWTRANSPERANCY,
WM_W32SETPARENTWINDOWACTIVE,
WM_W32POSTMESSAGETOPARENTWINDOW
};
bool g_IsMessageACustomMessage(UINT msgID)
{
for(UINT i = 0; i < s_nMessagesCustomMessages; ++i)
{
if(s_nMessagesCustomMessagesWeHave[i] == msgID)
return true;
}
return false;
}
////
// For wndproc to know what we care about and pass it down to to W32WndProc
// ~these messages will be tagged with WM_USER for us to handle in our W32WndProc
////
UINT s_nMessagesWPFormCaresAbout = 2;
UINT s_MessagesWPFormCaresAbout[] =
{
WM_ACTIVATE,
WM_SIZE,
};
bool g_IsMessageWPFormCaresAbout(UINT msgID)
{
for(UINT i = 0; i < s_nMessagesWPFormCaresAbout; ++i)
{
if(s_MessagesWPFormCaresAbout[i] == msgID)
return true;
}
return false;
}
////
// For wndproc to know what we care about and pass it down to to MainWndProc
// ~these messages will be send straight down for us to handle in our MainWndProc
////
UINT s_nMessagesW32WindowCaresAbout = 8;
UINT s_MessagesW32WindowCaresAbout[] =
{
WM_PAINT,
WM_DISPLAYCHANGE,
WM_THEMECHANGED,
WM_SIZE,
WM_MOVE,
WM_LBUTTONDOWN,
WM_MOUSEMOVE,
WM_SHOWWINDOW
};
bool g_IsMessageW32WindowCaresAbout(UINT msgID)
{
for(UINT i = 0; i < s_nMessagesW32WindowCaresAbout; ++i)
{
if(s_MessagesW32WindowCaresAbout[i] == msgID)
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////
// Desc: We need to make sure that we have the init button settings,
// before we can continue in many of our Messaging functions
//////////////////////////////////////////////////////////////////////
void MakeSureButtonThemeSettingWereInitialized(WPFCaller* pCaller)
{
// We must make sure the Button has all the settings before
// we can draw,move it, etc so sleep the thread until we know we have them
while(!pCaller->m_ButtonThemeSetting.bIsInitialized)
Sleep(20);
}
//////////////////////////////////////////////////////////////////////
// Desc: For our WPFThread callback into WPForm we must make sure
// that we have the handle for our W32Button so to pass it down to them
//////////////////////////////////////////////////////////////////////
void MakeSureW32ButtonWindowGotCreated(WPFCaller* pCaller)
{
// We must make sure the Button window got created so we can pass the
// handle down
while(pCaller->m_hwndW32Window == NULL)
Sleep(20);
}
//////////////////////////////////////////////////////////////////////
// Desc: For our Win32 Thread we must make sure we have a valid pointer to
// a WPFCaller Object.
//////////////////////////////////////////////////////////////////////
WPFCaller* GetWPFCallerObject(HWND ParenthWnd)
{
if(s_WPFCallerAccess)
{
BLANK_HookWndItem(Item);
Item = s_WPFCallerAccess->GetHookedWndItem(ParenthWnd);
WPFCaller* pCaller = Item.wpfcaller;
return pCaller;
}
return NULL;
}
//////////////////////////////////////////////////////////////////////
// Func: DrawIconButton (Owner Drawing out Buttons)
// Desc: Responsible for drawing the owner buttons onto the caption bar
//////////////////////////////////////////////////////////////////////
bool DrawIconButton(HINSTANCE hInstance,HDC hdc, WPFCaller* pCaller)
{
// static Icon handles:
static HANDLE hCurrIcon = NULL;
static HANDLE hButtonUp = NULL;
static HANDLE hButtonDown = NULL;
static HANDLE hToggleUp = NULL;
// LoadIcon only loads once, but LoadImage does not,
// so in case you call the latter, use this flag
static bool bIconsLoaded = false;
// Make sure that we don't continue unless we have all the settings
MakeSureButtonThemeSettingWereInitialized(pCaller);
if (!bIconsLoaded || pCaller->m_ButtonThemeSetting.bIsChanged)
{
////
// ButtonUp
////
if(!hButtonUp)
{
HANDLE hTemp = hButtonUp;
hButtonUp = NULL; //*avoid poss. threading issue
DestroyIcon((HICON)hTemp);
}
hButtonUp = LoadImage(hInstance, (WCHAR*)pCaller->m_ButtonThemeSetting.bstrBUTTON_UP,IMAGE_ICON,pCaller->m_ButtonThemeSetting.Width,pCaller->m_ButtonThemeSetting.Height,LR_LOADFROMFILE);
if(!hButtonUp)
log(LOGGING_LOW, L"hButtonUp LoadImage Failed! with error %i - path %s",GetLastError(),(wchar_t*)pCaller->m_ButtonThemeSetting.bstrBUTTON_UP);
////
// ButtonDown
////
if(!hButtonDown)
{
HANDLE hTemp = hButtonDown;
hButtonDown = NULL; //*avoid poss. threading issue
DestroyIcon((HICON)hTemp);
}
hButtonDown = LoadImage(hInstance, (WCHAR*)pCaller->m_ButtonThemeSetting.bstrBUTTON_DOWN,IMAGE_ICON,pCaller->m_ButtonThemeSetting.Width,pCaller->m_ButtonThemeSetting.Height,LR_LOADFROMFILE);
if(!hButtonDown)
log(LOGGING_LOW, L"hButtonDown LoadImage Failed! with error %i - path %s",GetLastError(),(wchar_t*)pCaller->m_ButtonThemeSetting.bstrBUTTON_DOWN);
////
// ToogleUp
////
if(!hToggleUp)
{
HANDLE hTemp = hToggleUp;
hToggleUp = NULL; //*avoid poss. threading issue
DestroyIcon((HICON)hTemp);
}
hToggleUp = LoadImage(hInstance, (WCHAR*)pCaller->m_ButtonThemeSetting.bstrTOGGLE_UP,IMAGE_ICON,pCaller->m_ButtonThemeSetting.Width,pCaller->m_ButtonThemeSetting.Height,LR_LOADFROMFILE);
if(!hToggleUp)
log(LOGGING_LOW, L"hToggleUp LoadImage Failed! with error %i - path %s",GetLastError(),(wchar_t*)pCaller->m_ButtonThemeSetting.bstrTOGGLE_UP);
// Everything went well.
bIconsLoaded = true;
pCaller->m_ButtonThemeSetting.bIsChanged = false;
}
////
// Now Draw the proper Icon State
////
bool bUseToogleState = (pCaller->m_ButtonToggledState != BUTTON_NONE);
if(!bUseToogleState && pCaller->m_ButtonState == BUTTON_DOWN)
hCurrIcon = hButtonDown;
else if(pCaller->m_ButtonState == BUTTON_UP)
hCurrIcon = hButtonUp;
else if(bUseToogleState || pCaller->m_ButtonState == BUTTON_TOGGLED)
hCurrIcon = hToggleUp;
if(hCurrIcon != NULL)
{
if (!DrawIconEx(hdc,0,0,(HICON) hCurrIcon,
pCaller->m_ButtonThemeSetting.Width,
pCaller->m_ButtonThemeSetting.Height,
0, NULL, DI_NORMAL))
{
log(LOGGING_LOW, L"DrawIconEx Failed!");
return false;
}
}
return true;
}
//////////////////////////////////////////////////////////////////////
// Desc: Set static HeightNWidth (keeping track of the Window size)
//////////////////////////////////////////////////////////////////////
void Helper_GetRectAndSetHeightNWidth(HWND hWnd, WPFCaller* pCaller)
{
RECT rect;
GetWindowRect(hWnd,&rect);
pCaller->m_wndHeight = (rect.bottom - rect.top);
pCaller->m_wndWidth = (rect.right - rect.left);
pCaller->m_wndRECT = rect;
}
//////////////////////////////////////////////////////////////////////
// Desc: Set static HeightNWidth (keeping track of the Window size)
//////////////////////////////////////////////////////////////////////
void Helper_GetRectAndSetHeightNWidthUsingPlacement(HWND hWnd, WPFCaller* pCaller)
{
WINDOWPLACEMENT place;
place.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(hWnd,&place);
pCaller->m_wndHeight = (place.rcNormalPosition.bottom - place.rcNormalPosition.top);
pCaller->m_wndWidth = (place.rcNormalPosition.right - place.rcNormalPosition.left);
pCaller->m_wndRECT = place.rcNormalPosition;
}
//////////////////////////////////////////////////////////////////////
// Func: W32WndProc (W32Window)
// Desc: Responsible for handling the Wnd32Window's incoming messages
//////////////////////////////////////////////////////////////////////
LRESULT CALLBACK W32WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
PAINTSTRUCT ps;
HDC hdc;
int nTop = 0;
int nRight = 0;
bool bIsMaximized = false;
HINSTANCE hInst = NULL;
// Use the ParentHWND to get access to the correct WPFCaller instance,
// we need to do this in case multiple windows exist in the same process
WPFCaller* pCaller = GetWPFCallerObject(::GetParent(hWnd));
// Nothing to do here, WPFCaller is not ready (just handle the message)
if(pCaller == NULL)
goto HANDLE_MESSAGE;
////
// Messages should be handled here (if we are interested)
////
try
{
// For some calls we need th HINSTANCE
hInst = pCaller->m_hInst;
switch(Msg)
{
////
// Win32Button Messages we handle
////
case WM_SIZE:
if(wParam == SIZE_MINIMIZED)
{
pCaller->m_bButtonIsMinimized = true;
}
else if((wParam == SIZE_RESTORED && pCaller->m_bButtonIsMinimized) ||
(wParam == SIZE_MAXIMIZED && pCaller->m_bButtonIsMinimized))
{
// the Button receives this message way before the parent does.
pCaller->m_bButtonIsBeingRestored = true;
}
// Intentional Fallthru
case WM_DISPLAYCHANGE:
case WM_MOVE:
{
// ~Calculate Offset for Maximized Windows - IMP
// in vista, we need 7 height offset
// we need 5 width offset
// ~i tried using the best values that make sense
const int YHEIGHT_OFFSET = GetSystemMetrics(SM_CYFRAME) - 1; // subtract 1 to better position the button (and not cut it off as much)
const int XWIDTH_OFFSET = (2 * GetSystemMetrics(SM_CXEDGE)) + GetSystemMetrics(SM_CXBORDER);
bool bIsMaximized = (IsZoomed(pCaller->m_hWnd) == TRUE);
int YOFFSET = 0;
int XOFFSET = 0;
if(bIsMaximized)
{
YOFFSET = YHEIGHT_OFFSET;
XOFFSET = XWIDTH_OFFSET;
}
// Set the Window Position (Use Offset if need be)
MakeSureButtonThemeSettingWereInitialized(pCaller);
Helper_GetRectAndSetHeightNWidth(pCaller->m_hWnd, pCaller);
nTop = pCaller->m_wndRECT.top + pCaller->m_ButtonThemeSetting.Top;
nRight = pCaller->m_wndRECT.left + pCaller->m_wndWidth - pCaller->m_ButtonThemeSetting.Right;
SetWindowPos(hWnd, HWND_TOP, nRight - XOFFSET, nTop + YOFFSET, pCaller->m_ButtonThemeSetting.Width, pCaller->m_ButtonThemeSetting.Height, NULL);
bool bIsMinimizeMoveMessage = false;
if(Msg == WM_MOVE)
{
int x = LOWORD(lParam);
int y = HIWORD(lParam);
bIsMinimizeMoveMessage = (x > 33500) && (y > 33500);
}
// If the window is just too damn small to hold all the buttons just hide it!
// ~better user experience
const int MIN_WITDH_TO_DISPLAY_BUTTON_IN = 300;
if((pCaller->m_wndWidth > 25) && /* make sure width is bigger >0 may as well put in some magic number buffer */
!bIsMinimizeMoveMessage &&
(pCaller->m_wndWidth < MIN_WITDH_TO_DISPLAY_BUTTON_IN) && !pCaller->m_bButtonIsArtificiallyHidden_MOVE && !pCaller->m_bButtonIsMinimized)
{
log(LOGGING_DEBUG, L"WM_MOVE Setting ButtonWindow Artificially Hidden to true");
pCaller->m_bButtonIsArtificiallyHidden_MOVE = true;
RedrawWindow(pCaller->m_hwndW32Window,NULL,NULL,(RDW_INVALIDATE | RDW_UPDATENOW));
}
else if((pCaller->m_wndWidth >= MIN_WITDH_TO_DISPLAY_BUTTON_IN) && pCaller->m_bButtonIsArtificiallyHidden_MOVE && !pCaller->m_bButtonIsMinimized)
{
log(LOGGING_DEBUG, L"WM_MOVE Setting ButtonWindow Artificially Hidden to false");
pCaller->m_bButtonIsArtificiallyHidden_MOVE = false;
pCaller->StartRestoreEventFadeTimer();
}
return 0;
}
break;
case WM_SHOWWINDOW:
if((BOOL) wParam == FALSE) // window is being hidden
{
pCaller->SetAlpha(0);
}
else if((BOOL) wParam == TRUE) // window is being shown
{
pCaller->StartRestoreEventFadeTimer();
}
return 0;
case WM_MOUSEMOVE:
TRACKMOUSEEVENT mouseEvent;
mouseEvent.cbSize = sizeof(TRACKMOUSEEVENT);
mouseEvent.dwFlags = TME_HOVER | TME_LEAVE;
mouseEvent.hwndTrack = hWnd;
mouseEvent.dwHoverTime = 25;
TrackMouseEvent(&mouseEvent);
break;
case WM_LBUTTONDOWN:
log(LOGGING_HIGH, L"ButtonWindow received ButtonClick Sending Click Event to WPForm");
PostThreadMessage(pCaller->m_threadID,WM_W32BUTTONCLICKED,NULL,NULL);
return 0;
case WM_MOUSEHOVER:
pCaller->m_ButtonState = BUTTON_UP;
RedrawWindow(pCaller->m_hwndW32Window,NULL,NULL,(RDW_INVALIDATE | RDW_UPDATENOW));
return 0;
case WM_MOUSELEAVE:
pCaller->m_ButtonState = BUTTON_DOWN;
RedrawWindow(pCaller->m_hwndW32Window,NULL,NULL,(RDW_INVALIDATE | RDW_UPDATENOW));
return 0;
case WM_TIMER:
// FadeInTimer (Restore from Minimize)
if(wParam == NID_RESTORE_EVENT_TIMER)
{
// Increment Opaqueness
const int SLEEP_INCREMENT = 1; // 1 == lowest
const int LOWER_INCREMENT = 7;
const int MIDDLE_INCREMENT = 15;
const int UPPER_INCREMENT = 25;
// Increment Points
const int SLEEP_DONE_POINT = 7; // (7 + 1) * 20 = 160ms of sleep
const int LOWER_POINT = 60;
const int MIDDLE_POINT = 160;
const int UPPER_POINT = 230;
BYTE alpha = pCaller->GetAlpha();
if(alpha >= UPPER_POINT)
alpha = 255; // just show the thing already
else if(alpha <= SLEEP_DONE_POINT) // sleep a little in the beginning...
alpha += SLEEP_INCREMENT;
else if(alpha <= LOWER_POINT)
alpha += LOWER_INCREMENT;
else if(alpha <= MIDDLE_POINT)
alpha += MIDDLE_INCREMENT;
else
alpha += UPPER_INCREMENT;
// Set the calculated Alpha Opaqueness
pCaller->SetAlpha(alpha);
// Close Restore Timer if we are done
if(pCaller->GetAlpha() == 255)
{
// Kill the Restore Timer
KillTimer(hWnd,NID_RESTORE_EVENT_TIMER);
log(LOGGING_DEBUG, L"Stopping Restore Event Timer");
}
}
else if(wParam == NID_CLOSE_BUT_NOT_DESTROY_EVENT_TIMER) // Closing Timer for MDI Apps (notify WPFCaller)
{
// We'll run this timer 21x, that should give us ~5 seconds (more than enough, i hope in 99% of cases)
if(pCaller->m_StartCloseButNotDestroyTimerCount == 20)
{
log(LOGGING_DEBUG, L"Stopping Close But not Destroy Timer");
pCaller->m_StartCloseButNotDestroyTimerCount = 0;
KillTimer(hWnd,NID_CLOSE_BUT_NOT_DESTROY_EVENT_TIMER);
}
else
{
log(LOGGING_DEBUG, L"EventDispatcherHandler - WM_CLOSE but no WM_DESTROY Occured letting WPFCaller know");
pCaller->m_StartCloseButNotDestroyTimerCount = pCaller->m_StartCloseButNotDestroyTimerCount + 1;
PostThreadMessage(pCaller->m_threadID, WM_CLIENT_EVENT_PARENTCLOSEWITHOUTDESTROYOCCURED,0,0);
}
}
break;
case WM_PAINT:
// If we are minimized and we are getting messages from our parent
// ~now we can launch the restore Timer
if(pCaller->m_bButtonIsBeingRestored &&
(wParam == (WPARAM) pCaller->m_hWnd) && (lParam == (LPARAM) pCaller->m_hWnd))
{
// restore timer will from now on handle the restore
pCaller->m_bButtonIsBeingRestored = false;
pCaller->m_bButtonIsMinimized = false;
// Hides the Button Completly
log(LOGGING_DEBUG, L"WM_PAINT is starting StartRestoreEventFadeTimer()");
pCaller->StartRestoreEventFadeTimer();
}
// Only Draw the button when we want to
if(!pCaller->m_bButtonIsMinimized &&
!pCaller->m_bButtonIsArtificiallyHidden_MOVE)
{
hdc = BeginPaint(hWnd, &ps);
DrawIconButton(hInst,ps.hdc,pCaller);
EndPaint(hWnd, &ps);
}
return 0;
case WM_THEMECHANGED:
log(LOGGING_MEDIUM, L"Received WM_THEMECHANGED. W32Button is trying to retrieve new Theme Settings via WPFProc");
PostThreadMessage(pCaller->m_threadID,WM_GETBUTTONTHEMESETTING,NULL,NULL);
break;
case WM_W32PINGISALIVE:
return 1;
case WM_W32SETBUTTONSTATE:
log(LOGGING_DEBUG, L"Received WM_W32SetButtonState Message. Setting Button to %d", (int)lParam);
pCaller->m_ButtonState = (BUTTON_STATE) lParam;
// Button will be toggled / untoggled when the caller set it
if(pCaller->m_ButtonState == BUTTON_TOGGLED)
pCaller->m_ButtonToggledState = BUTTON_TOGGLED;
else
pCaller->m_ButtonToggledState = BUTTON_NONE;
RedrawWindow(pCaller->m_hwndW32Window,NULL,NULL,(RDW_INVALIDATE | RDW_UPDATENOW));
return 1;
case WM_W32GETBUTTONSTATE:
log(LOGGING_DEBUG, L"Received WM_W32GetButtonState Message. Sending Button State %d", (int)pCaller->m_ButtonState);
return (LRESULT) pCaller->m_ButtonState;
case WM_W32SETPARENTWINDOWTRANSPERANCY:
{
if((int)lParam == 0)
log(LOGGING_DEBUG, L"Received WM_W32SETPARENTWINDOWTRANSPERANCY Message with Alpha Setting of %u - Invisible", (int)lParam);
else if((int)lParam == 255)
log(LOGGING_DEBUG, L"Received WM_W32SETPARENTWINDOWTRANSPERANCY Message with Alpha Setting of %u - Visible", (int)lParam);
long dwStyle = (GetWindowLong(pCaller->m_hWnd,GWL_EXSTYLE) | WS_EX_LAYERED);
long retVal = SetWindowLong(pCaller->m_hWnd,GWL_EXSTYLE,dwStyle);
if(retVal == 0)
{
log(LOGGING_LOW, L"WM_W32SETPARENTWINDOWTRANSPERANCY SetWindowLong Failed! It returned 0");
return 0;
}
// Set the Alpha Channel on the Parent Window
if(!SetLayeredWindowAttributes(pCaller->m_hWnd,RGB(0,0,0),(BYTE)lParam,LWA_ALPHA))
{
log(LOGGING_LOW, L"WM_W32SETPARENTWINDOWTRANSPERANCY SetLayeredWindowAttributes Failed! It returned false");
return 0;
}
// Also Set it on the Button (let it become transparent/untransparent with the hooked window)
pCaller->SetAlpha((BYTE)lParam);
}
return 1;
case WM_W32SETPARENTWINDOWACTIVE:
{
HWND hWnd = ::SetActiveWindow(pCaller->m_hWnd);
if(hWnd == NULL)
return 0;
else
return 1;
}
return 0;
case WM_W32POSTMESSAGETOPARENTWINDOW:
{
if((int)lParam != 0)
{
wchar_t buf[MAX_PATH] = {0};
if(GlobalGetAtomName((ATOM) lParam, buf,MAX_PATH) == 0)
{
log(LOGGING_DEBUG, L"*Error* - WM_W32SENDMESSAGETOPARENTWINDOW received no String from Atom");
}
else
{
HWND hWndM = NULL;
UINT MsgM = 0;
WPARAM wParamM = 0;
LPARAM lParamM = 0;
int IntsM[4] = {0};
bool bParseFailed = false;
log(LOGGING_DEBUG, L"WM_W32SENDMESSAGETOPARENTWINDOW received String from Atom %s", buf);
wchar_t* pToken = wcstok(buf, L";");
for (int i = 0; (pToken != NULL) && (i < 4); ++i)
{
log(LOGGING_DEBUG, L"WM_W32SENDMESSAGETOPARENTWINDOW Token %d - %s", (i + 1), pToken);
IntsM[i] = _wtoi(pToken);
if(((i == 0) || (i == 1))&& (IntsM[i] == 0))
{
log(LOGGING_DEBUG, L"*Error* - WM_W32SENDMESSAGETOPARENTWINDOW token %d is invalid", (i + 1));
bParseFailed = true;
}
pToken = wcstok(NULL, L";");
}
if(!bParseFailed)
{
hWndM = (HWND) IntsM[0];
MsgM = (UINT) IntsM[1];
wParamM = (WPARAM) IntsM[2];
lParamM = (LPARAM) IntsM[3];
if(IsWindow(hWndM))
{
log(LOGGING_DEBUG, L"WM_W32SENDMESSAGETOPARENTWINDOW Posting Message to hWnd %d, Msg %d, wParam %d, lParam %i", hWndM, MsgM, wParamM, lParamM);
PostMessage(hWndM,MsgM,wParamM,lParamM);
return 1;
}
else
log(LOGGING_DEBUG, L"*Error* - WM_W32SENDMESSAGETOPARENTWINDOW window handle %d is invalid", hWndM);
}
return 0;
}
}
else
{
log(LOGGING_DEBUG, L"*Error* - WM_W32SENDMESSAGETOPARENTWINDOW received no Atom");
}
}
return 0;
}
}
catch(...)
{
log(LOGGING_LOW, L"*Error* - A fatal error occured in W32WndProc for W32Button");
}
HANDLE_MESSAGE:
LRESULT lr = DefWindowProc(hWnd,Msg,wParam,lParam);
return (lr);
}
//////////////////////////////////////////////////////////////////////
// Func: WPFThreadProc (WPFButtonForm)
// Desc: Responsible for handling messages that are supposed to go to the WPFButtonForm
//////////////////////////////////////////////////////////////////////
DWORD WINAPI WPFThreadProc(__in LPVOID lpParameter)
{
CoInitialize(NULL);
MSG msg;
// Force the system to create a message queue for the thread
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
IButtonFormPtr wpfForm;
IClientEventsPtr EventDispatcherForwarder;
WPFCaller* pCaller = (WPFCaller*) lpParameter;
// handle to parent window (Hooked Window)
HWND hWnd = pCaller->m_hWnd;
// First thing we want to do is read the icon setting for the win32 button
// *we are doing it here because we are reading the settings using COM,
// and this thread is COM enabled
if(!ReadButtonThemeSetting(pCaller))
return -1;
// We need to sit here idly and wait to make sure that we got the Button Window Handle
MakeSureW32ButtonWindowGotCreated(pCaller);
HWND hWndButton = pCaller->m_hwndW32Window; // handle to the Button Window
log(LOGGING_DEBUG, L"WPFThreadProc: Wait over. ButtonW32 Window got created (%u).", hWndButton);
// Try to create a WPF Form Objects
HRESULT hr = wpfForm.CreateInstance(__uuidof(ButtonWPFormCCW));
if(SUCCEEDED(hr))
{
try
{
// Let's get the IClientEvents Interface to forward Window Client Events
log(LOGGING_HIGH, L"Trying to query for IClientEvents Interface");
HRESULT hr = wpfForm.QueryInterface(__uuidof(IClientEvents), &EventDispatcherForwarder);
if(SUCCEEDED(hr))
log(LOGGING_HIGH, L"Querying for IClientEvents succeeded");
else
log(LOGGING_LOW, L"Querying for IClientEvents failed");
// Let the WPF form know that we activated (and pass out the window handle
// that activated it) ~this is importanted for housekeeping. also pass out the
// Button Handle so that it can set it's state. ~Imp
log(LOGGING_DEBUG, L"Calling WPFThreadProc_ButtonWPForm_Activated Method with Parent (%u) and Button (%u)", hWnd, hWndButton);
wpfForm->WPFThreadProc_ButtonWPForm_Activated((long)hWnd, (long) hWndButton);
log(LOGGING_HIGH, L"Calling WPFThreadProc_ButtonWPForm_Activated Method Returned - Everything is O.K.");
// Also let the Client Event know the same thing
log(LOGGING_DEBUG, L"WM_CLIENT_EVENT hWndInitialActivate() forwarding it to WPFButton for PID %u and hWnd %u with CommandLine %s", ::GetCurrentProcessId(), hWnd, ::GetCommandLine());
_bstr_t CommandLinePrms(GetCommandLine());
EventDispatcherForwarder->hWndInitialActivate(GetCurrentProcessId(), (long)hWnd,CommandLinePrms);
log(LOGGING_HIGH, L"WM_CLIENT_EVENT hWndInitialActivate() returned - Everything is O.K.");
// Let OnDefault know that this Thread is ready to receive messages
pCaller->m_bThreadIsReady = true;
BOOL bRet = FALSE;
while( (bRet = GetMessage( &msg, (HWND)-1, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
log(LOGGING_LOW, L"A Fatal Error Occured in ThreadProc. Exiting!", hr);
goto STOPPING_THREAD;
break;
}
else
{
////
// MAIN MESSAGE LOOP
////
switch(msg.message)
{
case WM_KILLTHREAD:
log(LOGGING_MEDIUM, L"WM_KILLTHREAD was Posted to Thread. Exiting Window(%u) in pid(%u).", pCaller->m_hWnd, GetCurrentProcessId());
pCaller->m_hwndWPForm = NULL;
goto STOPPING_THREAD;
break;
case WM_GETBUTTONTHEMESETTING:
if(ReadButtonThemeSetting(pCaller))
log(LOGGING_HIGH, L"Received GetButtonThemeSetting Message - Read ButtonThemeSettings Succeeded");
else
log(LOGGING_LOW, L"Received GetButtonThemeSetting Message - Read ButtonThemeSettings Failed!");
break;
case WM_W32BUTTONCLICKED:
log(LOGGING_DEBUG, L"WM_W32BUTTONCLICKED passing down to WPFForm->W32BUTTONCLICKED()");
pCaller->m_hwndWPForm = (HWND) wpfForm->W32BUTTONCLICKED();
break;
case (WM_USER + WM_ACTIVATE):
if((LOWORD(msg.wParam) == WA_ACTIVE) ||
(LOWORD(msg.wParam) == WA_CLICKACTIVE))
{
if(HIWORD(msg.wParam) == 0)
wpfForm->WindowEvent_WindowActivated();
}
else if(LOWORD(msg.wParam) == WA_INACTIVE)
{
if(HIWORD(msg.wParam) == 0)
wpfForm->WindowEvent_WindowDeactivated();
}
break;
case (WM_USER + WM_SIZE):
if(msg.wParam == SIZE_MAXIMIZED)
wpfForm->WindowEvent_MaximizeOccured((long)HIWORD(msg.lParam),(long)LOWORD(msg.lParam));
else if(msg.wParam == SIZE_MINIMIZED)
wpfForm->WindowEvent_MinimizeOccured();
break;
////
// Client Window Events * WPFThreadProc sends them to a Event Dispatcher
// via COM to let C# know about these *
////
case (WM_CLIENT_EVENT_MDICHILDWINDOWSWITCHOCCURED):
case (WM_CLIENT_EVENT_PARENTGETSETTEXTOCCURED):
case (WM_CLIENT_EVENT_PARENTSETFOCUSOCCURED):
{
log(LOGGING_DEBUG, L"WM_CLIENT_EVENT ReResolve() forwarding it to WPFButton");
EventDispatcherForwarder->ReResolve(GetCurrentProcessId(),(long)hWnd); // Ideally only switches of active document occur this way
}
break;
case (WM_CLIENT_EVENT_PARENTNOTIFYLBUTTONCLICKOCCURED):
case (WM_CLIENT_EVENT_PARENTNOTIFYWMCREATEOCCURED):
case (WM_CLIENT_EVENT_PARENTCLOSEWITHOUTDESTROYOCCURED):
{
log(LOGGING_DEBUG, L"WM_CLIENT_EVENT PossArtifactAddedDeleted() forwarding it to WPFButton");
//EventDispatcherForwarder->ReResolve(GetCurrentProcessId(),(long)hWnd);
}
break;
case (WM_CLIENT_EVENT_DRAGNDROPOCCURED):
{
log(LOGGING_DEBUG, L"WM_CLIENT_EVENT DragNDropOccured() forwarding it to WPFButton");
EventDispatcherForwarder->DragNDropOccured(GetCurrentProcessId(),(long)hWnd,pCaller->m_nDragDroppedFilesCount,pCaller->m_bstrDragDroppedFilesSemiColonSep);
}
break;
case (WM_CLIENT_EVENT_OPENFILEORSAVEASDIALOGOCCURED):
{
log(LOGGING_DEBUG, L"WM_CLIENT_EVENT OpenOrSaveFileDialogOccured() forwarding it to WPFButton");
_bstr_t possLocAndFileName(pCaller->m_strFileNameLocationBuf);
_bstr_t FileTypes(pCaller->m_strFileTypesBuf);
EventDispatcherForwarder->OpenOrSaveFileDialogOccured(GetCurrentProcessId(), (long)hWnd, possLocAndFileName, FileTypes);
}
break;
}
}
}
// Let the WPF Form know that this thread is exiting to allow it to do
// any cleanup it may want to do
STOPPING_THREAD:
log(LOGGING_LOW, L"EventDispatcherHandler - hWindow %u Deactivation Occured for PID %u", hWnd,::GetCurrentProcessId());
wpfForm->WPFThreadProc_ButtonWPForm_Deactivated();
// Also let the Client Event know the same thing
log(LOGGING_DEBUG, L"WM_CLIENT_EVENT hWndLastDeactivate() forwarding it to WPFButton for PID %u and hWnd %u", ::GetCurrentProcessId(), hWnd);
EventDispatcherForwarder->hWndLastDeactivate(GetCurrentProcessId(), (long)hWnd);
}
catch(...)
{
log(LOGGING_LOW, L"*Error* - A fatal Error Occured in WPFThreadProc WpfButton. Exiting...");
}
}
else
{
log(LOGGING_LOW, L"Error occured creating WPForm Instance COM %i. Exiting...",hr);
}
pCaller->m_bThreadIsReady = false;
CoUninitialize();
return 0;
}
//********************************************************************
// Class WPFCaller
//
// Programmer Daniel Romischer
// Ooganizer Development Team
//
// Description ~This class holds the default wndproc to process all the
// messages and send them to the WPF COM Object via the COM Thread.
//
// Note ~ All the host's Windows Messages enter in on OnDefault()
//
// Created 05-26-2008
//********************************************************************
//////////////////////////////////////////////////////////////////////
//********************************************************************
// Construction / Destruction
//********************************************************************
//////////////////////////////////////////////////////////////////////
WPFCaller::WPFCaller(HookWndItem Item, HINSTANCE hInstance):
m_hWnd(Item.hWnd),
m_hwndWPForm(NULL),
m_hwndW32Window(NULL),
m_DefWndProc(Item.DefWndProc),
m_HookWndProc(Item.HookWndProc),
m_thread(NULL),
m_threadID(0),
m_bThreadIsReady(false),
m_hInst(hInstance),
m_wndHeight(0),
m_wndWidth(0),
m_ButtonState(BUTTON_NONE),
m_ButtonToggledState(BUTTON_NONE),
m_ButtonStateLastSaved(BUTTON_NONE),
m_ButtonToggledStateLastSaved(BUTTON_NONE),
m_bButtonIsArtificiallyHidden_MOVE(false),
m_bButtonIsMinimized(false),
m_bButtonIsBeingRestored(false),
m_StartCloseButNotDestroyTimerCount(0),
m_nDragDroppedFilesCount(0)
{
// Allow us access to the static DS *only load once*
if(s_WPFCallerAccess == NULL)
s_WPFCallerAccess = new CWHPaccess();
// just to make sure (to avoid any thread issues) assign out the this instance
// BEFORE creating the threads *below*.
Item.wpfcaller = this;
if(s_WPFCallerAccess->SetHookedWndItem(Item.hWnd, Item) == FALSE)
log(LOGGING_LOW, L"WPFCaller SetHookedWndItem FAILED!! (THIS IS NOT GOOD!)");
// Create the COM WPF Object for WPF Power and Flexibility
m_thread = CreateThread(NULL,0,&WPFThreadProc,(LPVOID)this,0,&m_threadID);
if(m_threadID > 0)
log(LOGGING_HIGH, L"WPFCaller Spawned WPFThreadID %i",m_threadID);
else
log(LOGGING_LOW, L"Error - WPFCaller Failed to Spawn WPFThread",m_threadID);
// Clear FileName/Types Buffer
memset(m_strFileNameLocationBuf,0,(MAX_PATH + 1));
memset(m_strFileTypesBuf,0,(MAX_PATH + 1));
// We are creating a new Thread to deal with 2 windows (WPForm Window,
// Start Window32 Button (for speed)
Start();
}
WPFCaller::~WPFCaller()
{
log(LOGGING_DEBUG, L"~WPFCaller() got called");
// Destroy the Button Window
if(m_hwndW32Window && ::IsWindow(m_hwndW32Window))
::DestroyWindow(m_hwndW32Window);
// Destroy the COM Thread
if(m_bThreadIsReady)
{
// First let's make sure the thread is even still running and valid,
// an error could have closed down the thread. Let's check if the handle
// gives us a valid id.
DWORD dwThreadId = GetThreadId(m_thread);
bool bThreadIsValid = ((dwThreadId != 0) && (dwThreadId == m_threadID));
// if this thread is not valid no need to continue below
if(!bThreadIsValid)
log(LOGGING_LOW, L"WPFThread ID %i was invalid when compared to Id %i, ~thread must already have been closed. An error must have occured", m_threadID, dwThreadId);
if(bThreadIsValid)
{
// Pass QUIT Message to the COM Thread
log(LOGGING_DEBUG, L"Posting WM_KILLTHREAD Message to COM Thread %i", m_threadID);
PostThreadMessage(m_threadID, WM_KILLTHREAD, NULL, NULL);
// We need to wait to make sure COM deallocated before exiting,
// otherwise the WPForm remains active, because deallocation never occurs
while(m_bThreadIsReady)
Sleep(20);
log(LOGGING_DEBUG, L"Closing Thread Handle to Thread %i", m_threadID);
CloseHandle(m_thread);
// Also Unregister all Window Classes here
//UnregisterClass(L"W32ButtonHookWndClass", m_hInst);
}
else
{
log(LOGGING_LOW, L"Did NOT close WPFThread m_bThreadIsReady was not true - this should mean WPFThread closed due to an error with COM");
}
}
else
{
log(LOGGING_LOW, L"Did NOT close WPFThread m_bThreadIsReady was not true - this should mean WPFThread closed due to an error with COM");
}
}
/////////////////////////////////////////////////////////////////////
// Desc: Get the Alpha Channel / Opaqueness of the Button
/////////////////////////////////////////////////////////////////////
BYTE WPFCaller::GetAlpha()
{
COLORREF pcrKey;
BYTE pbAlpha;
DWORD dwFlags;
GetLayeredWindowAttributes(m_hwndW32Window, &pcrKey, &pbAlpha, &dwFlags);
return pbAlpha;
}
/////////////////////////////////////////////////////////////////////
// Desc: Set the Alpha Channel / Opaqueness for the Button
/////////////////////////////////////////////////////////////////////
void WPFCaller::SetAlpha(BYTE bAlpha)
{
SetLayeredWindowAttributes(m_hwndW32Window, RGB(255,255,255),bAlpha, LWA_COLORKEY|LWA_ALPHA);
}
/////////////////////////////////////////////////////////////////////
// Desc: Use this to start the Fade event on the Button
/////////////////////////////////////////////////////////////////////
void WPFCaller::StartRestoreEventFadeTimer()
{
log(LOGGING_DEBUG, L"Launching Restore Event Timer...");
SetAlpha(0); // Hide Button Completly
SetTimer(m_hwndW32Window, NID_RESTORE_EVENT_TIMER,20,NULL);
}
/////////////////////////////////////////////////////////////////////
// Desc: Use this when a WM_CLOSE message is received. Some Apps won't
// close when this event occurs (but rather close an artifact),
// ~this allows us to detect this occurance
/////////////////////////////////////////////////////////////////////
void WPFCaller::StartCloseButNotDestroyTimer()
{
// quarter of a second is more than enough time to know whether an app closed or not
// ~but just in case we'll run the timer multiple times over and over
m_StartCloseButNotDestroyTimerCount = 0;
log(LOGGING_DEBUG, L"Launching CloseButNotDestroyTimer...");
SetTimer(m_hwndW32Window, NID_CLOSE_BUT_NOT_DESTROY_EVENT_TIMER,250,NULL);
}
/////////////////////////////////////////////////////////////////////
// Func: EventDispatcherHandler()
// Desc: Responsible for sending certain client events to the WPFThreadProc
// This allows us to be more interactive.
//
// Note: Not all Client events are handled here *only the easiest ones*
//////////////////////////////////////////////////////////////////////
void WPFCaller::EventDispatcherHandler(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
{
switch(Msg)
{
// DragNDropOccured
case WM_DROPFILES:
{
HDROP hdrop = (HDROP) wParam;
wchar_t buf[MAX_PATH + 1] = {0};
m_bstrDragDroppedFilesSemiColonSep = L"";
m_nDragDroppedFilesCount = DragQueryFile(hdrop,0xFFFFFFFF,NULL,0);
//log(LOGGING_DEBUG, L"EventDispatcherHandler - hWindow %u received %d Dropped Files", hWnd, m_nDragDroppedFilesCount);
for(int i = 0; i < m_nDragDroppedFilesCount; ++i)
{
if(i > 0)
m_bstrDragDroppedFilesSemiColonSep += L";";
DragQueryFile(hdrop,i,buf,MAX_PATH);
//log(LOGGING_DEBUG, L"EventDispatcherHandler - File %u - received dropped file %s", i, (char*) buf);
lstrcat((wchar_t *)m_bstrDragDroppedFilesSemiColonSep, buf);
}
// Let WPFCaller know
PostThreadMessage(m_threadID, WM_CLIENT_EVENT_DRAGNDROPOCCURED ,0,0);
}
break;
// MDIChildWindowSwitchOccured
case WM_KEYDOWN:
{
// Did Ctrl + F6 or Ctrl + Tab get pushed, if so a possible MDI
// switch occured so relay that information
if((wParam == VK_TAB || wParam == VK_F6) &&
(GetKeyState(VK_CONTROL) < 0))
{
// Let WPFCaller know
//log(LOGGING_DEBUG, L"EventDispatcherHandler - Ctrl + F6 or Ctrl + Tab got pushed - poss. MDIChildWindowSwitch occured");
PostThreadMessage(m_threadID, WM_CLIENT_EVENT_MDICHILDWINDOWSWITCHOCCURED ,0,0);
}
}
break;
// ParentGetSetTextOccured
case WM_GETTEXT:
case WM_SETTEXT:
{
// Let WPFCaller know
//log(LOGGING_DEBUG, L"EventDispatcherHandler - ParentGetSetText Occured - poss. MDIChildWindowSwitch occured");
PostThreadMessage(m_threadID, WM_CLIENT_EVENT_PARENTGETSETTEXTOCCURED,0,0);
}
break;
// ParentNotifyLButtonClick Occured
// ParentNotifyWMCreate Occured
case WM_PARENTNOTIFY:
{
WORD wMsg = LOWORD(wParam);
switch(wMsg)
{
case WM_CREATE:
{
// Let WPFCaller know
//log(LOGGING_DEBUG, L"EventDispatcherHandler - ParentNotify WM_CREATE Occured - poss. MDIChildWindow Creation occured");
PostThreadMessage(m_threadID, WM_CLIENT_EVENT_PARENTNOTIFYWMCREATEOCCURED,0,0);
}
break;
case WM_LBUTTONDOWN:
{
// Let WPFCaller know
//log(LOGGING_DEBUG, L"EventDispatcherHandler - ParentNotify LBUTTONDOWN Occured - poss. MDIChildWindowSwitch occured");
PostThreadMessage(m_threadID, WM_CLIENT_EVENT_PARENTNOTIFYLBUTTONCLICKOCCURED,0,0);
}
break;
}
}
break;
// Parent received a WM_CLOSE but didn't destroy itself
case WM_CLOSE:
{
//log(LOGGING_DEBUG, L"EventDispatcherHandler - Received WM_CLOSE running StartCloseButNotDestroyTimer()");
StartCloseButNotDestroyTimer(); // Win32Button will let the WPFCaller know
}
break;
// Parent received focus message
case WM_SETFOCUS:
{
// Let WPFCaller know
//log(LOGGING_DEBUG, L"EventDispatcherHandler - Parent SetFocus Occured - poss. MDIChildWindowSwitch occured");
PostThreadMessage(m_threadID, WM_CLIENT_EVENT_PARENTSETFOCUSOCCURED,0,0);
}
break;
}
}
/////////////////////////////////////////////////////////////////////
// Func: HandleDDEMessages()
// Desc: Because DDE is a message-based protocol, it employs no functions or libraries.
// All DDE transactions are conducted by passing certain defined DDE messages between the
// client and server windows.
// ~We may need to use this for certain applications to listen in
//
// Note: We call this OnDefault(), because DDE message can get passed into us before WPFThread is up and running
//////////////////////////////////////////////////////////////////////
void WPFCaller::HandleDDEMessages(HWND hWnd, UINT Msg,WPARAM wParam,LPARAM lParam)
{
switch(Msg)
{
case WM_DDE_ACK:
//log(LOGGING_DEBUG, L"Received DDE Message WM_DDE_ACK");
break;
case WM_DDE_ADVISE:
//log(LOGGING_DEBUG, L"Received DDE Message WM_DDE_ADVISE");
break;
case WM_DDE_DATA:
//log(LOGGING_DEBUG, L"Received DDE Message WM_DDE_DATA");
break;
case WM_DDE_EXECUTE:
//log(LOGGING_DEBUG, L"Received DDE Message WM_DDE_EXECUTE");
break;
case WM_DDE_INITIATE:
//log(LOGGING_DEBUG, L"Received DDE Message WM_DDE_INITIATE");
{
wchar_t strBuf[MAX_PATH] = {0};
ATOM lowword = LOWORD(lParam);
ATOM highword = HIWORD(lParam);
if(lowword != NULL)
{
int nSize = ::GlobalGetAtomName((ATOM)lowword,strBuf,MAX_PATH);
strBuf[nSize] = 0;
//log(LOGGING_DEBUG, L"Found lowword Atom, %d, with lowword String, %s", lowword, strBuf);
}
if(highword != NULL)
{
int nSize = ::GlobalGetAtomName((ATOM)highword,strBuf,MAX_PATH);
strBuf[nSize] = 0;
//log(LOGGING_DEBUG, L"Found highword Atom, %d, with highword String, %s", highword, strBuf);
}
}
break;
case WM_DDE_POKE:
//log(LOGGING_DEBUG, L"Received DDE Message WM_DDE_POKE");
break;
case WM_DDE_REQUEST:
//log(LOGGING_DEBUG, L"Received DDE Message WM_DDE_REQUEST");
break;
case WM_DDE_TERMINATE:
//log(LOGGING_DEBUG, L"Received DDE Message WM_DDE_TERMINATE");
break;
case WM_DDE_UNADVISE:
//log(LOGGING_DEBUG, L"Received DDE Message WM_DDE_UNADVISE");
break;
}
}
/////////////////////////////////////////////////////////////////////
// Func: OnDefault()
// Desc: Default handler for any WM_message received (ALL except WM_NCDESTROY
// and m_msgWMUnHook) the Destroy Messages delete this class and are handled
// therefore outside of it
//////////////////////////////////////////////////////////////////////
LRESULT WPFCaller::OnDefault(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
{
LRESULT result = 0;
try
{
// Handle DDE Messages
HandleDDEMessages(hWnd,Msg,wParam,lParam);
// If this is a custom message forward it our Button,
// it will be the main handler for our custom messages because it can communicate
// to the COMThread as well as manipulate the Button (we can skip and return the value)
if((m_hwndW32Window != NULL) && (g_IsMessageACustomMessage(Msg)))
{
return SendMessage(m_hwndW32Window,Msg,wParam,lParam);
}
// regular messages that our W32Button Cares about it, send it (no WM_USER offset needed here)
if((m_hwndW32Window != NULL) && (g_IsMessageW32WindowCaresAbout(Msg)))
{
// work-around for Minimize/Restore. Let the parent communicate
// to the child that it is being redrawn
if(Msg == WM_PAINT && m_bButtonIsMinimized)
{
wParam = (WPARAM) m_hWnd;
lParam = (LPARAM) m_hWnd;
PostMessage(m_hwndW32Window, Msg, wParam, lParam);
}
else
{
SendMessage(m_hwndW32Window, Msg, wParam, lParam);
}
}
// We pass the Messages we care about forward to our COM/W32/WPF Thread
if(m_bThreadIsReady && (m_threadID != 0))
{
// IF our COM object cares about this, send it
if(g_IsMessageWPFormCaresAbout(Msg))
{
// We error if we use PostThreadMessage with message < WM_USER so we offset it
PostThreadMessage(m_threadID,(WM_USER + Msg),wParam,lParam);
}
// our Event Dispatcher maybe interested in this
// message let it know about it
EventDispatcherHandler(hWnd,Msg,wParam,lParam);
}
// Call the Default Window Proc - IMPORTANT - Always do this
result = CallWindowProc(m_DefWndProc, hWnd, Msg, wParam, lParam);
}
catch(...)
{
log(LOGGING_LOW, L"*Error* - A Fatal Error occured in WpfCaller's OnDefault() - (This can't be good)");
}
return result;
}
//////////////////////////////////////////////////////////////////////
// Func: threadproc
// Desc: Thread Entry Point responsible for Dispatching the W32Window Messages
//
// Note: We are on purpose Ignoring Error Msg 1410. If the window class is already registered it
// will throw an error, we can safely continuen and instantiate the window class
//////////////////////////////////////////////////////////////////////
int WINAPI WPFCaller::threadproc(HINSTANCE hinstance, HINSTANCE hPrevInstance, void* vArg, int nCmdShow)
{
if (!InitApplication(hinstance))
{
// "Class already exists" this happens if the
// same window get's called again to be hooked into.
// this error can safely be ignored.
if(!(GetLastError() == 1410))
{
log(LOGGING_LOW, L"initApplication failed error %i",GetLastError());
return FALSE;
}
}
if (!InitInstance(hinstance, nCmdShow))
{
log(LOGGING_LOW, L"initInstance failed error %i",GetLastError());
return FALSE;
}
MSG msg;
BOOL bGotMessage;
m_bThreadIsReady = true; // Thread is up N' Ready and Receiving Messages
while ((bGotMessage = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0 )
{
if(bGotMessage == -1)
{
// handle the error and possible exit
log(LOGGING_LOW, L"An Error Occured in ThreadProc Messaging. Exiting!");
ExitThread(0);
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
//////////////////////////////////////////////////////////////////////
// Func: InitApplication()
// Desc: Responsible for setting up the WNDCLASSEX struct and register
// the transparent window with Windows via RegisterClassEx()
//
// Retr: TRUE, if successful, FALSE otherwise
//////////////////////////////////////////////////////////////////////
BOOL WPFCaller::InitApplication(HINSTANCE hInstance)
{
// Fill in the window class structure with parameters
// that describe the main window.
WNDCLASSEX wcx;
wcx.cbSize = sizeof(wcx); // size of structure
wcx.style = CS_HREDRAW | CS_VREDRAW; // redraw if size changes
wcx.lpfnWndProc = (WNDPROC) W32WndProc; // points to W32 window procedure
wcx.cbClsExtra = 0; // no extra class memory
wcx.cbWndExtra = 0; // no extra window memory
wcx.hInstance = hInstance; // handle to instance
wcx.hIcon = NULL;
wcx.hCursor = LoadCursor(NULL, IDC_ARROW); // predefined arrow
wcx.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); // white background brush
wcx.lpszMenuName = L"MainMenu"; // name of menu resource
wcx.lpszClassName = L"W32ButtonHookWndClass"; // name of window class
wcx.hIconSm = NULL;
// Register the window class. *may throw error 1410*
return RegisterClassEx(&wcx);
}
//////////////////////////////////////////////////////////////////////
// Func: InitInstance()
// Desc: Responsible for Creating and Showing a new Window Class Instance
//
// Retr: TRUE, if successful, FALSE otherwise
//////////////////////////////////////////////////////////////////////
BOOL WPFCaller::InitInstance(HINSTANCE hInstance, int nCmdShow)
{
// Get the Dimensions of the hooked in Window
RECT rect;
GetWindowRect(m_hWnd,&rect);
// set owner/parent window
HWND hParent = m_hWnd;
DWORD dwStyle;
//dwStyle = ( WS_POPUP | WS_BORDER ); // For Debugging
dwStyle = ( WS_POPUP );
// If parent Window get's hidden for some reason, should we hide this window?
// by default owned windows don't hide, when their parent is hidden,
// they do hide when parent is minimized.
log(LOGGING_DEBUG, L"Calling CreateWindowEx with Parent %i", hParent);
//( WS_POPUP | WS_BORDER ), // top-level window
m_hwndW32Window = CreateWindowEx(
( WS_EX_LAYERED ), // A Layered window is by default transparent
L"W32ButtonHookWndClass", // name of window class
L"W32ButtonHookWndTitle",// title-bar string
dwStyle, // top-level window
rect.left, // default horizontal position
rect.top, // default vertical position
(rect.right - rect.left), // default width
(rect.bottom - rect.top), // default height
(HWND) hParent, // set owner window
// ~An owned window is always above its owner in the Z order and
// is hidden when its owner is minimized
(HMENU) NULL, // use class menu
hInstance, // handle to application instance
(LPVOID) NULL); // no window-creation data
log(LOGGING_DEBUG, L"CreateWindowEx returns (m_hwndW32Window is set to %i) ", m_hwndW32Window);
// we can use this to fade the window if we want too... (something to think about...)
//if(SetLayeredWindowAttributes(m_hwndW32Window, RGB(255,255,255),255, LWA_COLORKEY|LWA_ALPHA)){}
SetLayeredWindowAttributes(m_hwndW32Window, RGB(255,255,255),255, LWA_COLORKEY|LWA_ALPHA);
// Show the window and send a WM_PAINT message to the window procedure.
ShowWindow(m_hwndW32Window, nCmdShow);
UpdateWindow(m_hwndW32Window);
return (m_hwndW32Window != NULL);
}