// The Win32 call to SetFocus does not do it for hwnd that are // not in the same process as the caller. // This is implemented here to work around this behavior. // Fake keystroke are sent to a specific hwnd to force // Windows to give the focus to that hwnd. static internal bool SetFocus(IntPtr hwnd) { // First Check for ComboLBox // Because it uses Keystrokes it dismisses the ComboLBox string className = RealGetWindowClass(hwnd); if (className == "ComboLBox") return true; // If window is currently Disabled or Invisible no need // to continue if (!SafeNativeMethods.IsWindowVisible(hwnd) || !SafeNativeMethods.IsWindowEnabled(hwnd)) { return false; } // If already focused, leave as-is. Calling SetForegroundWindow // on an already focused HWND will remove focus! if (GetFocusedWindow().Equals(hwnd)) { return true; } // Try calling SetForegroundWindow directly first; it should succeed if we // already have the focus or have UIAccess if (UnsafeNativeMethods.SetForegroundWindow(hwnd)) { return true; } // Use the hotkey technique: // Register a hotkey and send it to ourselves - this gives us the // input, and allows us to call SetForegroundWindow. short atom = GlobalAddAtom("FocusHotKey"); if (atom == 0) { return false; } short vk = 0xB9; bool gotHotkey = false; for (int tries = 0; tries < 10; tries++) { if (RegisterHotKey(IntPtr.Zero, atom, 0, vk)) { gotHotkey = true; break; } vk++; // try another key } if (gotHotkey) { // Get state of modifiers - and temporarilly release them... bool fShiftDown = (UnsafeNativeMethods.GetAsyncKeyState(UnsafeNativeMethods.VK_SHIFT) & unchecked((int)0x80000000)) != 0; bool fAltDown = (UnsafeNativeMethods.GetAsyncKeyState(UnsafeNativeMethods.VK_MENU) & unchecked((int)0x80000000)) != 0; bool fCtrlDown = (UnsafeNativeMethods.GetAsyncKeyState(UnsafeNativeMethods.VK_CONTROL) & unchecked((int)0x80000000)) != 0; if (fShiftDown) Input.SendKeyboardInputVK(UnsafeNativeMethods.VK_SHIFT, false); if (fAltDown) Input.SendKeyboardInputVK(UnsafeNativeMethods.VK_MENU, false); if (fCtrlDown) Input.SendKeyboardInputVK(UnsafeNativeMethods.VK_CONTROL, false); Input.SendKeyboardInputVK(vk, true); Input.SendKeyboardInputVK(vk, false); // Restore release modifier keys... if (fShiftDown) Input.SendKeyboardInputVK(UnsafeNativeMethods.VK_SHIFT, true); if (fAltDown) Input.SendKeyboardInputVK(UnsafeNativeMethods.VK_MENU, true); if (fCtrlDown) Input.SendKeyboardInputVK(UnsafeNativeMethods.VK_CONTROL, true); // Spin in this message loop until we get the hot key while (true) { // If the hotkey input gets lost (eg due to desktop switch), GetMessage may not return - // so use MsgWait first so we can timeout if there's no message present instead of blocking. int result = MsgWaitForMultipleObjects(null, false, 2000, NativeMethods.QS_ALLINPUT); if (result == NativeMethods.WAIT_FAILED || result == NativeMethods.WAIT_TIMEOUT) break; NativeMethods.MSG msg = new NativeMethods.MSG(); if (!GetMessage(ref msg, IntPtr.Zero, 0, 0)) break; // TranslateMessage() will not set an error to be retrieved with GetLastError, // so set the pragma to ignore the PERSHARP warning. // the PERSHARP warning. #pragma warning suppress 6031, 6523 UnsafeNativeMethods.TranslateMessage(ref msg); // From the Windows SDK documentation: // The return value specifies the value returned by the window procedure. // Although its meaning depends on the message being dispatched, the return // value generally is ignored. #pragma warning suppress 6031, 6523 UnsafeNativeMethods.DispatchMessage(ref msg); if (msg.message == NativeMethods.WM_HOTKEY && (short)msg.wParam == atom) { break; } } UnregisterHotKey(IntPtr.Zero, atom); } GlobalDeleteAtom(atom); return UnsafeNativeMethods.SetForegroundWindow(hwnd); }
// ------------------------------------------------------ // // Private Methods // // ------------------------------------------------------ #region Private Methods // Infinite loop. Wait for queue items to be processed. private void WaitForWork() { SafeWaitHandle handle = _ev.SafeWaitHandle; NativeMethods.MSG msg = new NativeMethods.MSG(); while (true) { try { // pump any messages while (UnsafeNativeMethods.PeekMessage (ref msg, IntPtr.Zero, 0, 0, NativeMethods.PM_REMOVE)) { if (msg.message == NativeMethods.WM_QUIT) { break; } Misc.DispatchMessage(ref msg); } // do any work items in the queue // It's possible items could be enqueued between when we check for the count // and dequeue but the event is set then and we'll come back into DrainQueue. // (note: don't use a for loop here because as the counter is incremented // Count is decremented and we'll only process half the queue) while (_q.Count > 0) { // pull an item off the queue, process, then clear it QueueItem item = (QueueItem) _q.Dequeue (); item.Process (); } int result = Misc.MsgWaitForMultipleObjects(handle, false, NativeMethods.INFINITE, NativeMethods.QS_ALLINPUT); if (result == NativeMethods.WAIT_FAILED || result == NativeMethods.WAIT_TIMEOUT) { Debug.Assert(false, "MsgWaitForMultipleObjects failed while WaitForWork"); break; } } catch( Exception e ) { if (Misc.IsCriticalException(e)) throw; // Might happen when if the hwnd goes away between the peek and the dispatch } // } }