How to give Outlook the focus from Add-In code?

  • Thread starter Thread starter Jim
  • Start date Start date
J

Jim

Hello,

I have written an Outlook Add-In using VB6. It works. Now I want to
add new code so that when the Add-In receives a particular UDP packet
as an IPC message from a separate application, Outlook (only if
already running) becomes the foreground application, with Windows
focus, and switches CurrentFolder to the Inbox.

If Outlook is running but currently minimized, the following code in
the Add-In does the trick:

objOutlook.ActiveExplorer.Activate
Set objOutlook.ActiveExplorer.CurrentFolder = _
objNamespaceMAPI.GetDefaultFolder(olFolderInbox)

But -- if Outlook is not minimized, the code above switches the folder
but does *not* result in Outlook receiving Windows focus. So Outlook
has switched folders but may remain buried behind other applications
on the desktop.

How can I programmatically (from within my Add-In) give Outlook the
focus, so it is certain to appear on top of any other applications on
the desktop?

Thanks.

Jim
 
This is a Windows feature as of Windows 2000 - background applications
cannot activate their windows by calling SetForegroundWindow(). Your code
needs to attach itself to the currently active window (even if it is in
another process), call SetForegroundWindow (you will now be allowed to set
the foreground window), then detach itself. To get the window handle of an
Outlook explorer, you need to use IOleWindow interface, then call
IOleWindow::GetWindow().
In Delphi:

Explorer.Display;
if S_OK = IUnknown(Explorer).QueryInterface(IOLEWindow, OW) then begin
if S_OK = OW.GetWindow(wnd) then begin
ForceForegroundWindow(wnd);
end;
OW := nil;
end;

function ForceForegroundWindow(hWnd: THandle): BOOL;
var hCurWnd: THandle;
begin
hCurWnd := GetForegroundWindow;
AttachThreadInput(
GetWindowThreadProcessId(hCurWnd, nil),
GetCurrentThreadId, True);
Result := SetForegroundWindow(hWnd);
AttachThreadInput(
GetWindowThreadProcessId(hCurWnd, nil),
GetCurrentThreadId, False);
end;

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool
 
Dmitry Streblechenko \(MVP\) said:
This is a Windows feature as of Windows 2000 - background applications
cannot activate their windows by calling SetForegroundWindow(). Your code
needs to attach itself to the currently active window (even if it is in
another process), call SetForegroundWindow (you will now be allowed to set
the foreground window), then detach itself. To get the window handle of an
Outlook explorer, you need to use IOleWindow interface, then call
IOleWindow::GetWindow().
In Delphi:

Explorer.Display;
if S_OK = IUnknown(Explorer).QueryInterface(IOLEWindow, OW) then begin
if S_OK = OW.GetWindow(wnd) then begin
ForceForegroundWindow(wnd);
end;
OW := nil;
end;

function ForceForegroundWindow(hWnd: THandle): BOOL;
var hCurWnd: THandle;
begin
hCurWnd := GetForegroundWindow;
AttachThreadInput(
GetWindowThreadProcessId(hCurWnd, nil),
GetCurrentThreadId, True);
Result := SetForegroundWindow(hWnd);
AttachThreadInput(
GetWindowThreadProcessId(hCurWnd, nil),
GetCurrentThreadId, False);
end;

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool

Dmitry,

Thank you very much for your code example. It was just what I needed
to get started. I don't speak Delphi, but equipped with your
explanation and sample code I was able to search for similar VB source
and I have my code working now.

I was unsure of what to do with IOLEWindow/etc., but based on another
post of yours elsewhere I seized on using the variable Outlook caption
to help FindWindow locate the Outlook window, so I don't need a VB
equivalent of the Delphi IOLEWindow code.

Your code led me to the ForceFore VB source at:

http://www.mvps.org/vb/index2.html?samples.htm

That code was very helpful, and clearly mapped onto your Delphi code.

I did run into a problem where Outlook would still not necessarily be
the top window when my new code ran, depending on the order in which
other applications were launched. But I added a call to Win32
function BringWindowToTop() and that was the last piece needed to make
it all work.

I have included my code below for future readers. It works on my
bench with Outlook 2000 running under Windows 98. I still need to
test it on other Windows versions running other Outlook versions.

Thanks.

Jim

Option Explicit

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function GetWindowThreadProcessId Lib "user32" _
(ByVal hWnd As Long, lpdwProcessId As Long) As Long

Private Declare Function AttachThreadInput Lib "user32" _
(ByVal idAttach As Long, ByVal idAttachTo As Long, ByVal fAttach As
Long) _
As Long

Private Declare Function GetForegroundWindow Lib "user32" _
() As Long

Private Declare Function SetForegroundWindow Lib "user32" _
(ByVal hWnd As Long) As Long

Private Declare Function BringWindowToTop Lib "user32" _
(ByVal hWnd As Long) As Long

Private Declare Function IsIconic Lib "user32" _
(ByVal hWnd As Long) As Long

Private Declare Function ShowWindow Lib "user32" _
(ByVal hWnd As Long, ByVal nCmdShow As Long) As Long

Private Const SW_SHOW = 5
Private Const SW_RESTORE = 9

Public Sub MakeOutlookForeground(objOutlook As Outlook.Application)

On Error Resume Next

'MS Office Class Names for Outlook
'---------------------------------
'Outlook 97 rctrl_renwnd32
'Outlook 98 rctrl_renwnd32
'Outlook 2000 rctrl_renwnd32
'Outlook XP rctrl_renwnd32

Dim lngHwnd As Long

'Find window by caption since Outlook title changes with current
folder.
lngHwnd = FindWindow("rctrl_renwnd32",
objOutlook.ActiveExplorer.Caption)

If lngHwnd = 0 Or lngHwnd = GetForegroundWindow() Then
'Could not find Outlook window, or Outlook is already foreground.
Exit Sub
End If

Dim lngThreadForegnd As Long
Dim lngThreadOutlook As Long

'Get thread pid for current foreground window.
lngThreadForegnd = GetWindowThreadProcessId(GetForegroundWindow,
ByVal 0&)
'Get thread pid for Outlook.
lngThreadOutlook = GetWindowThreadProcessId(lngHwnd, ByVal 0&)

If lngThreadForegnd <> lngThreadOutlook Then
'Attach threads to share input states so can control focus.
AttachThreadInput lngThreadForegnd, lngThreadOutlook, True
'Put Outlook in foreground and activate window.
SetForegroundWindow lngHwnd
'Uncover Outlook window by bringing it to top of Z Order.
BringWindowToTop lngHwnd
'Detach threads.
AttachThreadInput lngThreadForegnd, lngThreadOutlook, False
Else
SetForegroundWindow lngHwnd
BringWindowToTop lngHwnd
End If

If IsIconic(lngHwnd) Then
'Outlook is minimized -- restore, activate, and display window.
ShowWindow lngHwnd, SW_RESTORE
Else
'Outlook is not minimized -- activate and display window.
ShowWindow lngHwnd, SW_SHOW
End If

End Sub
 
Back
Top