How to bring outlook 2021 window to front

Viewed 31

I am wanting to bring Outlook's main window to the front, from within a VSTO add-in. I tried the approaches described in the various answers to this question, and it just doesn't seem to work, at least for Outlook 2021.

I get the Outlook main window's handle (which I verified using spy++ and appears to be correct), using either

Process.GetProcessesByName("outlook").FirstOrDefault().MainWindowHandle

or

(Globals.ThisAddIn.Application.ActiveExplorer() as IOleWindow).GetWindow()

(both yield the same result).

Then I try to bring the window to the front (probably some redundant calls in there, I was just trying everything I could to get this to work):

ShowWindow(proc.MainWindowHandle, SW_SHOWNORMAL);
ShowWindow(proc.MainWindowHandle, SW_RESTORE);
SetForegroundWindow(proc.MainWindowHandle);
SwitchToThisWindow(proc.MainWindowHandle, true);

What am I doing wrong?

3 Answers

All Outlook windows implement the IOleWindow interface which provides methods that allow an application to obtain the handle to the various windows that participate in in-place activation. So, you can use the retrieved window handle for calling Windows API functions such as SetForegroundWindow method which brings the thread that created the specified window into the foreground and activates the window. Also keyboard input is directed to the window, and various visual cues are changed for the user.

Also you may consider calling the Explorer.Activate or Inspector.Activate methods that activate an explorer or inspector window by bringing it to the foreground and setting keyboard focus.

It turns out that the missing piece was simulating an ALT click (just the up part part is sufficient) before calling SetForegroundWindow(). No need to call SwitchToThisWindow().

As a bonus, setting ActiveExplorer().CurrentFolder() = ... reliably scrolls up & down to the selected folder (which it wasn't doing when outlook was not in the foreground).

Only the foreground process can set the active window using SetForegroundWindow. To trick Windows into thinking your process is in the foreground, use AttachThreadInput. Here is what I use:

        public static bool ForceForegroundWindow(IntPtr hWnd)
        {
            bool Result = false;
            uint ForegroundThreadID = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
            uint ThisThreadID = GetWindowThreadProcessId(hWnd, IntPtr.Zero);
            if (AttachThreadInput(ThisThreadID, ForegroundThreadID, true))
            { 
                BringWindowToTop(hWnd); 
                SetForegroundWindow(hWnd);
                AttachThreadInput(ThisThreadID, ForegroundThreadID, false);
                Result = (GetForegroundWindow() == hWnd);
            }
            if (!Result)
            {
                int timeout = 0;
                SystemParametersInfo(SPI.SPI_GETFOREGROUNDLOCKTIMEOUT, 0, ref timeout, 0);
                int newTimeout = 0;
                SystemParametersInfo(SPI.SPI_SETFOREGROUNDLOCKTIMEOUT, 0, ref newTimeout, SPIF.SPIF_SENDCHANGE);
                BringWindowToTop(hWnd); 
                SetForegroundWindow(hWnd);
                SystemParametersInfo(SPI.SPI_SETFOREGROUNDLOCKTIMEOUT, 0, ref timeout, SPIF.SPIF_SENDCHANGE);
                Result = (GetForegroundWindow() == hWnd);
            }
            return Result;
        }
Related