示例#1
0
        // 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
                }
                // 
            }
        }