#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);
}