//------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
 
        #region Private Methods

        // The loop the thread uses to wait for queue items to process
        private void WaitForWork()
        {
            SafeWaitHandle handle = _ev.SafeWaitHandle;
            UnsafeNativeMethods.MSG msg = new UnsafeNativeMethods.MSG();
            while (!_quitting)
            {
                // pump any messages
                while (UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.HWND.NULL, 0, 0, UnsafeNativeMethods.PM_REMOVE))
                {
                    if (msg.message == UnsafeNativeMethods.WM_QUIT)
                    {
                        break;
                    }
                    // 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);
                }

                // do any work items in the queue
                DrainQueue();

                int lastWin32Error = 0;
                int result = Misc.TryMsgWaitForMultipleObjects(handle, false, UnsafeNativeMethods.INFINITE, UnsafeNativeMethods.QS_ALLINPUT, ref lastWin32Error);
                if (result == UnsafeNativeMethods.WAIT_FAILED || result == UnsafeNativeMethods.WAIT_TIMEOUT)
                {
                    DrainQueue();
                    Debug.Assert(_quitting, "MsgWaitForMultipleObjects failed while WaitForWork");
                    break;
                }
            }
            DrainQueue();
        }
        // Set focus to specified HWND
        private static bool SetFocus( NativeMethods.HWND hwnd )
        {
            // If already focused, leave as-is. Calling SetForegroundWindow
            // on an already focused HWND will remove focus!
            if( GetFocusedWindow() == hwnd )
            {
                return true;
            }

            // If this is an MDI Child window, send WM_MDIACTIVATE message
            int exStyle = GetWindowExStyle(hwnd);
            if (IsBitSet(exStyle, SafeNativeMethods.WS_EX_MDICHILD))
            {
                NativeMethods.HWND parent = SafeNativeMethods.GetAncestor(hwnd, SafeNativeMethods.GA_PARENT);
                if (parent == IntPtr.Zero)
                {
                    return false;
                }
                IntPtr lresult = Misc.SendMessageTimeout(parent, UnsafeNativeMethods.WM_MDIACTIVATE, (IntPtr)hwnd, IntPtr.Zero);
                return lresult == IntPtr.Zero;
            }

            // 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 = Misc.GlobalAddAtom("FocusHotKey");
            if (atom == 0)
            {
                return false;
            }

            byte vk = 0xB9;
            bool gotHotkey = false;
            for( int tries = 0 ; tries < 10 ; tries++ )
            {
                if( Misc.RegisterHotKey( NativeMethods.HWND.NULL, 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 = Misc.MsgWaitForMultipleObjects(null, false, 2000, UnsafeNativeMethods.QS_ALLINPUT);
                    if (result == UnsafeNativeMethods.WAIT_FAILED || result == UnsafeNativeMethods.WAIT_TIMEOUT)
                        break;

                    UnsafeNativeMethods.MSG msg = new UnsafeNativeMethods.MSG();
                    if (Misc.GetMessage(ref msg, NativeMethods.HWND.NULL, 0, 0) == 0)
                        break;

                    // TranslateMessage() will not set an error to be retrieved with GetLastError,
                    // so set the pragma to ignore 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 == UnsafeNativeMethods.WM_HOTKEY
                        && msg.wParam == (IntPtr) atom)
                    {
                        break;
                    }
                }

                Misc.UnregisterHotKey(NativeMethods.HWND.NULL, atom);
            }
            Misc.GlobalDeleteAtom(atom);

            // Using this method uses the actual codepath the Alt+Tab uses
            UnsafeNativeMethods.SwitchToThisWindow(hwnd, true);
            return true;
        }