//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Initializes a new instance of the Oceanside.Win32.Native.WindowHookManager.Message class. /// </summary> /// /// <param name="hWnd"> The h window. </param> /// <param name="msg"> The message. </param> /// <param name="wparam"> The w parameter. </param> /// <param name="lparam"> The l parameter. </param> //////////////////////////////////////////////////////////////////////////////////////////////////// public HookMessage(IntPtr hWnd, HookMsgType msg, IntPtr wparam, IntPtr lparam) { HWnd = hWnd; HookMsgType = msg; WParam = wparam; LParam = lparam; Result = IntPtr.Zero; }
public Win32HookMsgEventArgs(IntPtr hIEWnd, HookMsgType msgType) { this.hWnd = hIEWnd; this.type = msgType; }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Returns the fully qualified type name of this instance. </summary> /// /// <returns> The fully qualified type name. </returns> /// /// <seealso cref="System.ValueType.ToString()"/> //////////////////////////////////////////////////////////////////////////////////////////////////// public override string ToString() { return(HookMsgType.ToString()); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Custom window procedure. </summary> /// /// <param name="hWnd"> The window. </param> /// <param name="msg"> The message. </param> /// <param name="wParam"> The parameter. </param> /// <param name="lParam"> The parameter. </param> /// /// <returns> An IntPtr. </returns> //////////////////////////////////////////////////////////////////////////////////////////////////// private IntPtr CustomWndProc(IntPtr hWnd, HookMsgType msg, IntPtr wParam, IntPtr lParam) { var m = new HookMessage(hWnd, msg, wParam, lParam); //Deactivated means there are zero callbacks and destroyed means that the WM_DESTROY message // has already been processed and there is no need to call our custom logic. I think there // are a few messages that can still come in after WM_DESTROY, such as WM_NCDESTROY that is // sent after all child Windows have been destroyed. Note that we try to unhook our custom // logic upon a WM_DESTROY by replacing it with the original WndProc but race conditions // could affect that timing. if (_isDeActivated || _isDestroyed) { return(CallWindowProc(_originalWndProc, m.HWnd, (uint)m.HookMsgType, m.WParam, m.LParam)); } //Grab a copy of the callbacks so we do not remain inside the lock while we are looping through // and calling back into unknown code that could (but hopefully not) be lengthy. In rare race // conditions this could cause a slight delay in the amount of time it takes to "UnRegister" // a callback if code desired to do so. // // TODO Window Procedure Performance Evaluation // Note that the performance of this design should be considered carefully. Although it makes // it flexible to be able to add callbacks to a Window throughout the application, the fact // that I am locking inside of a hot function is cause for careful thought. Atop of locking, // I am copying the current callbacks into another local variable. Since Window procedures // can cause recursion by design, that could lead to a stack overflow if the Size of // the RegisteredWndProcCallbacks grew. If an application only intended to attach one // callback at any given time, this could be simplified greatly. WndProcCallback[]? callbacksCopy = null; lock (_callbacksLocker) { callbacksCopy = RegisteredWndProcCallbacks.ToArray(); } if (callbacksCopy != null) { //Loop through all callbacks until one callback handles the message or we exhaust all // items in the loop. If a callback handles the message we can return the result so // long as it is not a DESTROY message, then we need to unhook. foreach (var callback in callbacksCopy) { if (!callback(ref m)) { continue; } if (msg != HookMsgType.DESTROY) { return(m.Result); } break; } } //This one makes me nervous, keep an eye on it. Of the many examples I happened upon, they // were re-hooking the WndProc with the original loop and for that reason I will do the // same. If you are seeing exceptions, this could be a race condition of some sort. if (msg == HookMsgType.DESTROY) { Destroy(); } //Note that this is still called after we call Destroy to unhook this routine because // it needs to receive the DESTROY message or else the window will not close. Also // notice that we are sending the values inside of the local HookMessage rather than // the ones that came into this CustomWndProc. This lets other callbacks change the // parameter values if needed. return(CallWindowProc(_originalWndProc, m.HWnd, (uint)m.HookMsgType, m.WParam, m.LParam)); }