using System; using System.Collections.Generic; using System.Text; using System.Reflection; using System.Runtime.InteropServices; using System.Diagnostics; using System.Threading; namespace Foo.Platform.Interacters { /// /// BHInteracter - ButtonHook Interacter. /// Use this class abstraction to send/communicate with ButtonHook. /// public class BHInteracter { //// // Our ButtonHook.Dll Entry Points (Delegates) //// internal delegate bool MyStartButtonHook(); internal static MyStartButtonHook _MyStartButtonHook = null; internal delegate bool MyStopButtonHook(); internal static MyStopButtonHook _MyStopButtonHook = null; internal delegate bool MyIsButtonHookSet(); internal static MyIsButtonHookSet _MyIsButtonHookSet = null; internal delegate int MyGetAllHookedWindowHandles(IntPtr pBuf); internal static MyGetAllHookedWindowHandles _MyGetAllHookedWindowHandles = null; /// /// Use this to send down to Buttonhook so that it knows what to show /// public enum BUTTON_HOOK_STATE { BUTTON_NONE = 1, BUTTON_ADD = 2, BUTTON_DELETE = 3 } // Declare the Log4net Variable private static log4net.ILog Log = Logger.GetLog4NetInterface(MethodBase.GetCurrentMethod().DeclaringType); //// // Custom ButtonHook Message Handlers //// private const int WM_W32PINGISALIVE = (Platform.Win32.Win32_Constants.WM_USER + 700); private const int WM_W32SETBUTTONSTATE = (Platform.Win32.Win32_Constants.WM_USER + 701); private const int WM_W32GETBUTTONSTATE = (Platform.Win32.Win32_Constants.WM_USER + 702); private const int WM_W32SETPARENTWINDOWTRANSPERANCY = (Platform.Win32.Win32_Constants.WM_USER + 703); private const int WM_W32SETPARENTWINDOWACTIVE = (Platform.Win32.Win32_Constants.WM_USER + 704); private const int WM_W32POSTMESSAGETOPARENTWINDOW = (Platform.Win32.Win32_Constants.WM_USER + 705); //// // Custom OogyCaptionButton Message Handlers //// public const int WM_CAPTIONBUTTON_START = (Platform.Win32.Win32_Constants.WM_USER + 1200); public const int WM_CAPTIONBUTTON_STOP = (Platform.Win32.Win32_Constants.WM_USER + 1201); //// // OogyCaptionButton Spec //// private const int CAPTION_START_STOP_MAX_TRY_COUNT = 5; public const string CAPTIONBUTTON_CMDLINE = @"/App:StartThis4Real639"; private const string CAPTIONBUTTON_PROCESSNAME = "CaptionButton"; private static string CAPTIONBUTTON_EXE = CAPTIONBUTTON_PROCESSNAME + ".exe"; private static string CAPTIONBUTTON_EXE_FULLPATH = InstallationSpec.InstallPath + CAPTIONBUTTON_EXE; private static IntPtr s_CaptionButtonHandle = IntPtr.Zero; /// /// Validity internal State checking for OogyCaptionButton.exe /// private enum CAPTIONBUTTON_VALIDITY_STATE { VALIDITY_NONE = 1, VALIDITY_CREATEDSUCCESS = 2, VALIDITY_CREATEDFAILED = 3, VALIDITY_STOPPEDSUCCESS = 4, VALIDITY_STOPPEDFAILED = 5, VALIDITY_INTEGRITYCHECK_FAILED = 6 } // State Success / Error Checking private static CAPTIONBUTTON_VALIDITY_STATE s_CaptionButtonState = CAPTIONBUTTON_VALIDITY_STATE.VALIDITY_NONE; /// /// Static Constructor Loads the ButtonHook.dll dynamically from the correct path /// static BHInteracter() { try { IntPtr procaddr = IntPtr.Zero; string strButtonHookDll = InstallationSpec.InstallPath + "ButtonHook.dll"; Log.Info(string.Format("{0}() - BHInteracter() Trying to load ButtonHook.dll {1}", MethodBase.GetCurrentMethod().Name, strButtonHookDll)); IntPtr ButtonHook = Win32.Win32Functions.LoadLibrary(strButtonHookDll); if(ButtonHook != IntPtr.Zero) Log.Info(string.Format("{0}() - BHInteracter() loading ButtonHook.dll succeeded", MethodBase.GetCurrentMethod().Name)); else Log.Error(string.Format("{0}() - BHInteracter() loading ButtonHook.dll failed!", MethodBase.GetCurrentMethod().Name)); // StartButtonHook procaddr = Win32.Win32Functions.GetProcAddress(ButtonHook, "_StartButtonHook@0"); if(procaddr != null) _MyStartButtonHook = (MyStartButtonHook)Marshal.GetDelegateForFunctionPointer(procaddr, typeof(MyStartButtonHook)); else Log.Info(string.Format("{0}() - BHInteracter() GetProcAddress for StartButtonHook Failed", MethodBase.GetCurrentMethod().Name)); // StopButtonHook procaddr = Win32.Win32Functions.GetProcAddress(ButtonHook, "_StopButtonHook@0"); if (procaddr != null) _MyStopButtonHook = (MyStopButtonHook)Marshal.GetDelegateForFunctionPointer(procaddr, typeof(MyStopButtonHook)); else Log.Info(string.Format("{0}() - BHInteracter() GetProcAddress for StopButtonHook Failed", MethodBase.GetCurrentMethod().Name)); // IsButtonHookSet procaddr = Win32.Win32Functions.GetProcAddress(ButtonHook, "_IsButtonHookSet@0"); if (procaddr != null) _MyIsButtonHookSet = (MyIsButtonHookSet)Marshal.GetDelegateForFunctionPointer(procaddr, typeof(MyIsButtonHookSet)); else Log.Info(string.Format("{0}() - BHInteracter() GetProcAddress for IsButtonHookSet Failed", MethodBase.GetCurrentMethod().Name)); // GetAllHookedWindowHandles procaddr = Win32.Win32Functions.GetProcAddress(ButtonHook, "_GetAllHookedWindowHandles@4"); if (procaddr != null) _MyGetAllHookedWindowHandles = (MyGetAllHookedWindowHandles)Marshal.GetDelegateForFunctionPointer(procaddr, typeof(MyGetAllHookedWindowHandles)); else Log.Info(string.Format("{0}() - BHInteracter() GetProcAddress for GetAllHookedWindowHandles Failed", MethodBase.GetCurrentMethod().Name)); } catch (Exception e) { Log.Error(string.Format("{0}() - BHInteracter() Construction Failed", MethodBase.GetCurrentMethod().Name), e); } } /// /// Use this function wrapper to call the StartButtonHook DLLEntry Point /// public static void StartButtonHookDLLEntryW() { Log.Info(string.Format("{0}() - CaptionButton - About to call StartButtonHook()", MethodBase.GetCurrentMethod().Name)); if (_MyStartButtonHook()) Log.Info(string.Format("{0}() - StartButtonHook() Succeeded", MethodBase.GetCurrentMethod().Name)); else Log.Error(string.Format("{0}() - StartButtonHook() Failed", MethodBase.GetCurrentMethod().Name)); } /// /// Use this function wrapper to call the StopButtonHook DLLEntry Point /// public static void StopButtonHookDLLEntryW() { Log.Info(string.Format("{0}() - CaptionButton - About to call StopButtonHook()", MethodBase.GetCurrentMethod().Name)); if (_MyStopButtonHook()) Log.Info(string.Format("{0}() - StopButtonHook() Succeeded", MethodBase.GetCurrentMethod().Name)); else Log.Error(string.Format("{0}() - StopButtonHook() Failed", MethodBase.GetCurrentMethod().Name)); } /// /// Use this function wrapper to call the IsButtonHookSet DLLEntry Point /// public static bool IsButtonHookSetDLLEntryW() { return _MyIsButtonHookSet(); } /// /// Use this function wrapper to call the GetAllHookedWindowHandles DLLEntry Point /// public static void GetAllHookedWindowHandlesDLLEntryW(ref List handleList) { // Temporary ar buffer int[] ar = new int[Win32.Win32_Constants.MAX_PATH]; // allocate and reset an new unmanaged buffer IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(ar)); Marshal.Copy(ar, 0, p, ar.Length); // get the size int nsize = _MyGetAllHookedWindowHandles(p); // copy data back as needed *delete unmanaged buffer* Marshal.Copy(p,ar,0,nsize); Marshal.FreeHGlobal(p); // Make sure the passed in list is empty * and pass out the handle list* handleList.Clear(); for (int i = 0; i < nsize; ++i) handleList.Add((IntPtr)ar[i]); } /// /// CaptionButton exe is the external process that hosts ButtonHook.dll /// /// true if the CaptionButtion exe is running, false otherwise public static bool IsCaptionButtonRunning() { Process[] processes = Process.GetProcessesByName(CAPTIONBUTTON_PROCESSNAME); return (processes.Length == 1); } /// /// Start CaptionButton exe which can host the ButtonHook.dll /// public static void StartCaptionButton() { try { if (!IsCaptionButtonRunning()) { TemporaryVal.DeleteVal(); ProcessStartInfo startInfo = new ProcessStartInfo(CAPTIONBUTTON_EXE_FULLPATH, CAPTIONBUTTON_CMDLINE); startInfo.WindowStyle = ProcessWindowStyle.Hidden; startInfo.WorkingDirectory = InstallationSpec.InstallPath; Process p = Process.Start(startInfo); Log.Info(string.Format("{0}() - Created a new Oogy CaptionButton Instance EXE with PID{1}", MethodBase.GetCurrentMethod().Name, p.Id.ToString())); // Wait a little before sending any messages Thread.Sleep(3000); //// // State Checking *IMP* .exe could have not shut down correctly, and buttonhook never got stopped // so let's check to see what is going on //// bool bIsInvalidState = ((s_CaptionButtonState != CAPTIONBUTTON_VALIDITY_STATE.VALIDITY_NONE) && (s_CaptionButtonState != CAPTIONBUTTON_VALIDITY_STATE.VALIDITY_STOPPEDSUCCESS)); // Let's get the window handle from the child process s_CaptionButtonHandle = (IntPtr)TemporaryVal.GetValI(); if (s_CaptionButtonHandle == IntPtr.Zero) Log.Error(string.Format("{0}() - Child Handle is Zero! This means all functions below will return out and everything fails. Check into this.", MethodBase.GetCurrentMethod().Name)); TemporaryVal.DeleteVal(); // We somehow got into an invalid state, so let's send Stop Messages and see what happens, // if those failed there is really not much more we can do except fail here if (bIsInvalidState && !Helper_CaptionButtonMessageSender(s_CaptionButtonHandle, WM_CAPTIONBUTTON_STOP)) Log.Info(string.Format("{0}() - Trying to StopButtonHook gracefully failed - ignoring and trying to StartButtonHook Anyways", MethodBase.GetCurrentMethod().Name, p.Id.ToString())); // Regular starting... if (Helper_CaptionButtonMessageSender(s_CaptionButtonHandle, WM_CAPTIONBUTTON_START)) { s_CaptionButtonState = CAPTIONBUTTON_VALIDITY_STATE.VALIDITY_CREATEDSUCCESS; Log.Info(string.Format("{0}() - StartCaptionButton Succeeded - Started the ButtonHook Worked Successfully", MethodBase.GetCurrentMethod().Name, p.Id.ToString())); } else { s_CaptionButtonState = CAPTIONBUTTON_VALIDITY_STATE.VALIDITY_CREATEDFAILED; Log.Error(string.Format("{0}() - StartCaptionButton Failed - Started the ButtonHook Did not work - VALIDITY_CREATEDFAILED - forcefully Killing the Process", MethodBase.GetCurrentMethod().Name, p.Id.ToString())); p.Kill(); } } else { // Process is already running for some reason - this should never happen, but if it does, // we'll handle it by doing an integrity check Log.Info(string.Format("{0}() - StartCaptionButton - CaptionButton.exe already exists - performing Complete Integrity Check...", MethodBase.GetCurrentMethod().Name)); bool bIntegrityIsGood = CompleteButtonHookIntegrityCheck(); if (bIntegrityIsGood) s_CaptionButtonState = CAPTIONBUTTON_VALIDITY_STATE.VALIDITY_CREATEDSUCCESS; else s_CaptionButtonState = CAPTIONBUTTON_VALIDITY_STATE.VALIDITY_INTEGRITYCHECK_FAILED; } } catch (Exception e) { Log.Error(string.Format("{0}() - StartCaptionButton - Error occured", MethodBase.GetCurrentMethod().Name), e); } } /// /// Stop CaptionButton exe which can host the ButtonHook.dll /// public static void StopCaptionButton() { Log.Info(string.Format("{0}() - StopCaptionButton Called", MethodBase.GetCurrentMethod().Name)); if (IsCaptionButtonRunning()) { Process[] processes = Process.GetProcessesByName(CAPTIONBUTTON_PROCESSNAME); foreach (Process p in processes) { Log.Info(string.Format("{0}() - Found OogyCaptionButton Pid {1}", MethodBase.GetCurrentMethod().Name, p.Id)); if (Helper_CaptionButtonMessageSender(s_CaptionButtonHandle, WM_CAPTIONBUTTON_STOP)) { s_CaptionButtonState = CAPTIONBUTTON_VALIDITY_STATE.VALIDITY_STOPPEDSUCCESS; Log.Info(string.Format("{0}() - StopCaptionButton Succeeded - Closing the ButtonHook Worked Successfully", MethodBase.GetCurrentMethod().Name, p.Id.ToString())); } else { s_CaptionButtonState = CAPTIONBUTTON_VALIDITY_STATE.VALIDITY_STOPPEDFAILED; Log.Error(string.Format("{0}() - StopCaptionButton Failed - Closing the ButtonHook Did not work - forcefully Killing the Process", MethodBase.GetCurrentMethod().Name, p.Id.ToString())); } Log.Info(string.Format("{0}() - Closing a Oogy CaptionButton Instance EXE of PID{1}", MethodBase.GetCurrentMethod().Name, p.Id.ToString())); p.Kill(); } } } /// /// Use this to send the Start/Stop Messages to Oogy CaptionButton.exe. Small Helper /// function allows us to do cleaner error handling for starting and stopping (above) /// /// WM_CAPTIONBUTTON_START or WM_CAPTIONBUTTON_STOP /// returns true if successful, false otherwise private static bool Helper_CaptionButtonMessageSender(IntPtr hWnd, int Message) { if((hWnd != IntPtr.Zero) && (Message == WM_CAPTIONBUTTON_START || Message == WM_CAPTIONBUTTON_STOP)) { for (int i = 0; i < CAPTION_START_STOP_MAX_TRY_COUNT; ++i) { // Send the Stop ButonHook Message to the Hidden window of the Process Win32.Win32Functions.SendMessage(hWnd, Message, IntPtr.Zero, IntPtr.Zero); // Wait a little second before querying for the state Thread.Sleep(500); // If this is a start message we can stop once the button hook is started if (Message == WM_CAPTIONBUTTON_START && IsButtonHookSetDLLEntryW()) return true; // If this is a stop message we can stop once the button hook no longer set if (Message == WM_CAPTIONBUTTON_STOP && !IsButtonHookSetDLLEntryW()) return true; } } return false; } /// /// Check to see if buttonhook is alive for the passed in window /// /// handle to a buttonHooked Parent Window public static bool IsButtonHookAliveOnWindow(IntPtr hWnd) { if (hWnd != IntPtr.Zero) { int retVal = Win32.Win32Functions.SendMessage((IntPtr)hWnd, WM_W32PINGISALIVE, IntPtr.Zero, IntPtr.Zero); if (retVal == 0) { Log.Error(string.Format("{0}() - IsButtonHookAlive Failed for Window {1}", MethodBase.GetCurrentMethod().Name, hWnd.ToString())); return false; } else { return true; } } return false; } /// /// Call this function to run a complete ButtonHook Integrity Check /// /// true if buttonhook passed all checks, false otherwise public static bool CompleteButtonHookIntegrityCheck() { bool bSuccess = true; Log.Info(string.Format("{0}() - CompleteButtonHookIntegrityCheck got called", MethodBase.GetCurrentMethod().Name)); // Make first sure that the Hook Is Set if (!IsButtonHookSetDLLEntryW()) bSuccess = false; // Now iterate through each hooked window pinging it // for feedback List handleList = new List(); foreach (IntPtr hWnd in handleList) { if (!IsButtonHookAliveOnWindow(hWnd) && Win32.Win32Functions.IsWindow(hWnd)) { bSuccess = false; // buttonhook not responsive on an existing window break; } } if(bSuccess) Log.Info(string.Format("{0}() - CompleteButtonHookIntegrityCheck passed successfully", MethodBase.GetCurrentMethod().Name)); else Log.Error(string.Format("{0}() - CompleteButtonHookIntegrityCheck failed", MethodBase.GetCurrentMethod().Name)); return bSuccess; } /// /// Use this to set the Transparency on a Window that is running the ButtonHook. ~Will Only work /// for those windows. /// /// handle to a buttonHooked Parent Window /// 0-255 byte value indicating the Transparency public static void SetWindowTransparency(IntPtr hWnd, byte alpha) { if (hWnd != IntPtr.Zero) { int retVal = Win32.Win32Functions.SendMessage((IntPtr)hWnd, WM_W32SETPARENTWINDOWTRANSPERANCY, IntPtr.Zero, (IntPtr)alpha); if (retVal == 0) Log.Error(string.Format("{0}() - SetWindowTransparency Failed for Window {1} with alpha {2}", MethodBase.GetCurrentMethod().Name, hWnd.ToString(), alpha.ToString())); } } /// /// Use this to set the ActiveWindow on a Window that is running the ButtonHook. ~Will Only work /// for those windows. /// /// handle to a buttonHooked Parent Window public static void SetAsActiveWindow(IntPtr hWnd) { if (hWnd != IntPtr.Zero) { int retVal = Win32.Win32Functions.SendMessage((IntPtr)hWnd, WM_W32SETPARENTWINDOWACTIVE, IntPtr.Zero, IntPtr.Zero); if (retVal == 0) Log.Error(string.Format("{0}() - SetAsActiveWindow Failed for Window {1}", MethodBase.GetCurrentMethod().Name, hWnd.ToString())); } } /// /// Use this to send a message to a window to send a message to itself /// /// public static void PostMessageAsIfFromWindow(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam ) { if (hWnd != IntPtr.Zero) { StringBuilder strAtom = new StringBuilder(hWnd.ToString() + ";" + Msg.ToString() + ";" + wParam.ToString() + ";" + lParam.ToString()); int nAtom = Win32.Win32Functions.GlobalAddAtom(strAtom); if (nAtom == 0) Log.Error(string.Format("{0}() - SendMessageAsIfFromWindow GlobalAddAtom Failed for Window {1}", MethodBase.GetCurrentMethod().Name, hWnd.ToString())); int retVal = Win32.Win32Functions.SendMessage((IntPtr)hWnd, WM_W32POSTMESSAGETOPARENTWINDOW, IntPtr.Zero, (IntPtr)nAtom); if (retVal == 0) Log.Error(string.Format("{0}() - SendMessageAsIfFromWindow ButtonHook Failed to process the String for Window {1}", MethodBase.GetCurrentMethod().Name, hWnd.ToString())); Win32.Win32Functions.GlobalDeleteAtom(nAtom); } } /// /// Use this to set a new ButtonState to a buttonhooked window /// /// handle to a buttonHooked Parent Window /// a new button state public static BUTTON_HOOK_STATE SetW32ButtonToNewState(IntPtr hWnd, BUTTON_HOOK_STATE buttonState) { if (hWnd != IntPtr.Zero) { int retVal = Platform.Win32.Win32Functions.SendMessage((IntPtr)hWnd, WM_W32SETBUTTONSTATE, IntPtr.Zero, (IntPtr)buttonState); if (retVal == 0) Log.Error(string.Format("{0}() - SetW32ButtonToNewState Failed for Window {1} ", MethodBase.GetCurrentMethod().Name, hWnd.ToString())); } return buttonState; } /// /// Use this to get a ButtonState from a buttonhooked window /// /// handle to a buttonHooked Parent Window /// a new button state public static BUTTON_HOOK_STATE GetW32ButtonState(IntPtr hWnd) { BUTTON_HOOK_STATE buttonState = BUTTON_HOOK_STATE.BUTTON_NONE; if (hWnd != IntPtr.Zero) { int retVal = Platform.Win32.Win32Functions.SendMessage((IntPtr)hWnd, WM_W32GETBUTTONSTATE, IntPtr.Zero, IntPtr.Zero); if (retVal == 0) Log.Error(string.Format("{0}() - SetW32ButtonToNewState Failed for Window {1} ", MethodBase.GetCurrentMethod().Name, hWnd.ToString())); try { buttonState = (BUTTON_HOOK_STATE)retVal; } catch (Exception e) { Log.Error(string.Format("{0}() - Error Thrown ", MethodBase.GetCurrentMethod().Name), e); } } return buttonState; } } }