Outlook Fast Shutdown: under the yellow hood
The Fast Shutdown feature found in Outlook 2010 represents a new direction for Microsoft Outlook towards Outlook stability and Outlook integrity. From Microsoft Outlook ’97 till Microsoft Outlook 2007, system administrators around the world had to deal with Outlook hanging, Outlook Slow closing, corrupted PST (or OST) files and various Outlook Add-ins that hang for no reason. While Microsoft has, over the years, introduced Mail (PST file) integrity checker (2002), Outlook Backup feature (2003), Outlook Security (2005), “DEP” Data Execution Prevention (Windows XP-SP2 in 2006), new Outlook 2007 file format (2007) to prevent data corruption and enforce PST file integrity, there is action towards one certain culprit that cannot be easily corrected.
To understand Outlook Fast Shutdown feature, we must understand the COM API interface that replaced the Interrupts[i], WordBasic[ii], Access Basic, Word WLL[iii] interface and DDE found in older versions of Office. When Microsoft Outlook starts, it starts the Visual Basic Automation, loads the Outlook TLB library, starts calling the Outlook Add-ins in sequential fashion and loads them into memory via COM interface. The opposite happens when Outlook is closed. When all Add-ins are unloaded from memory, the internal Outlook MAPI closes. So far, so good… except the closing action is sequential and any add-ins that hang-up Outlook causes Outlook to hang.
Outlook startup
Legend
- Outlook starts VBA Automation, load Outlook COM TLB library, start the Addins interface.
- Read the list of Add-ins in HKCU\Software\Office\(version)\Outlook\ and find filename.
- Connect to the DLL and call OnConnection/OnInitialize, OnStartupComplete COM routine
- On successful OnConnection/OnInitialize, it returns back an OLE Pointer to the Add-in for Outlook to store and to subsequently call.
- and 6. Outlook repeats the same process with each Add-in obtained from Registry list.
Outlook 2000-2007 Finalization (closing and memory-clean-up)
Legend
- On close, Outlook sends a COM dispatch to the OnBeginShutdown. During this time, the Add-in should release memory, close all toolbars, free all interfaces, etc.
- Outlook sends another COM dispatch to disconnect. This should be where last settings are being done.
- This process is repeated for each Add-in.
- (Not in diagram) For some reason, the COM dispatch thread does not return back to Outlook. This causes Outlook to wait endlessly. The user then has to start Task Manger and close Outlook.
Outlook 2010 Fast Finalization (closing and memory-clean-up)
Legend
- On close, Outlook sends a COM dispatch to the OnBeginShutdown event.
- Since the COM dispatch is multi-threaded, the COM dispatch makes a copy of the current thread and sends it along to your add-in. If it returns back to Outlook, it will start the Shutdown event.
- The new IMAPIProviderShutdown interface to signals shut-down as well.
- If the thread hangs, Outlook does not hang and then closes down. Hung threads are closed by TerminateThread.
Help Desk, we have another Outlook issue…
Since many Corporate-customers may have several productivity or Outlook packages installed, inclusive of your package, it becomes trickier to identify who caused Outlook to hang.
From the Help Desk:
- Most Microsoft Outlook errors are so vague and strange HelpDesk cannot identify them except take a screenshot and sent it to vendor. Screenshots with “Access Violation Error 0512312 in Module “YourAddin.dll” are almost impossible to debug. With ALR (Address Location Randomization) most errors become meaningless.
- It becomes a case of Who Did it? Was it the Add-in from company A, or from company B or maybe company Z.
- Internal development issue. Most developers are also Helpdesk support. They do not have in-depth expertise to debug and correct Outlook errors in in-house made Add-ins.
- NET developers have problems pop-up dialog upon close. This is because the thread does not allow MessageBox to pop-up during Outlook close.
To elevate problem of hanging, Microsoft implemented the IMAPIClientShutdown interface.
Tips to Shut-down correctly
Warning: Reading this could be hazardous to your development health. People reading this have taken less coffee, spend less time at the water cooler and more time enjoying writing Add-ins for Outlook.
Being a seasoned Outlook developer, here are tips to help your Add-in shut-down correctly:
- Take effort to look through your codes to see any final sections where COM interfaces are not freed. Free COM or NULL (or NIL) COM interfaces when processing Outlook COM interfaces.
procedure TAddInModule.adxCOMAddInModuleCreate(Sender: TObject); var explorer: _Explorer; begin explorer := Self.OutlookApp.ActiveExplorer; // // ... // explorer := nil; end;
- If your Add-in causes an access violation, try to trap the exception and not pass it back to Outlook. (Outlook will prompt an access violation) Put your codes inside a TRY, EXCEPT block and log down exception errors to disk. On exception, remember to NULL (or NIL) COM interface.
procedure TAddInModule.adxCOMAddInModuleCreate(Sender: TObject); var explorer: _Explorer; begin explorer := Self.OutlookApp.ActiveExplorer; // try except // in case exception occurs due there is no valid explorer window explorer := nil; // put your code here to log down exception to disk end // end;
- If you use sinning skinning managers, make sure your skinning manager is Data Execution Prevent (DEP) compliant. Sometimes DEP errors are caused by certain skinning managers. This error is really hard to track-down and unless the vendor has made their skinning manager/ library DEP compliant, it will take lot of effort to remove that skinning manager/library.
- Be careful when allocating heap memory. If your add-in does not track memory and has memory leaks, this causes Outlook to hang.
procedure TAddInModule.adxCOMAddInModuleAddInInitialize(Sender: TObject); begin appList := TList.Create; // Global TList variable end; procedure TAddInModule.adxCOMAddInModuleAddInFinalize(Sender: TObject); var iNo, iCount: integer; Mydata: MyDataObject; begin iCount := appList.Count; if iCount >= 1 then begin for iNo := 0 to iCount -1 do begin MyData := appList.Item[iNo]; // remember to free custom objects MyData.Free; end; end; FreeAndNil(appList); // Free memory List end;
- When using CDO and MAPI interfaces, remember to Free COM or NULL (or NIL) COM interfaces.
procedure SampleMapi; var CdoInfoStore: InfoStore; begin CdoInfoStore := CoInfoStore.Create; // // Processing // CdoInfoStore := nil; end;
- Do not put a Modal Dialog box on shutdown (e.g., Do you want to save your custom works?). This may not pop-up at all and may hang Outlook.
- If your DLL depends on other DLLs and they need to be unloaded from memory too.
- If you initialized several SQL connections and forgot to close “Sorry. This product has expired and will now close” – and cause Microsoft Outlook to hang. Your add-in should disable its own menu-options instead.
- If you created toolbars in-memory, you should remember to hold them in-memory and release them OnBeginShutDown time instead of OnDisconnect time.
If you still need more help:
C++ tips:
- When using C++, remember to clear BSTR pointers.
- There’s an exception logger called “Luke Stack Walker” and Ultimate Toolkit's Assert Logger. Using the Stack Walker to get last call stack and line number of error before the C++ Add-in hangs and Ultimate Toolbox’s Assert email functionality to send back the exception log to you.
- You can use DbgHelp.dll instead to make C++ stack dump. From the stack dump file, you can reproduce call-stack information required to find out which line number error occurs at.
- Use MadExcept or Eureka Log. If your exception logger does not work, you have an “initialization error” or “Canvas drawing error issue”. The former happens when Outlook starts and hangs immediately. This can be remedy by putting break-points and single-stepping all the Initialization parts of all the libraries you have and remove the offending code. The latter, “Canvas drawing error issue” happens when you do not free your form after Form1.Show().
- When using ActiveX libraries, if Outlook hangs on shut-down, they are prime suspects. Request vendor for full native NET solution instead.
- In most instances, if you encounter errors in your code, make log file of events
- Use memory profiler such as MemProfiler to check for not-freed NET objects in memory.
Delphi tips:
- Use MadExcept or Eureka Log. If your exception logger does not work, you have an “initialization error” or “Canvas drawing error issue”. The former happens when Outlook starts and hangs immediately. This can be remedy by putting break-points and single-stepping all the Initialization parts of all the libraries you have and remove the offending code. The latter, “Canvas drawing error issue” happens when you do not free your form after Form1.Show().
.NET tips:
- When using ActiveX libraries, if Outlook hangs on shut-down, they are prime suspects. Request vendor for full native NET solution instead.
- In most instances, if you encounter errors in your code, make log file of events
- Use memory profiler such as MemProfiler to check for not-freed NET objects in memory.
Beyond Fast Shutdown
I would suggest an about-face. This would be:
- Request for Microsoft to provide better debugging tools for COM. Tools to debug COM errors such as COM memory-leak, COM invalid calls are getting more expensive to license every year.
- Update Visual Basic 6, FoxPro and COM. “Visual Fred” needs a second home after getting dumped by the Visual Basic Development Team. Some developers still use Visual Basic 6.0 to maintain Office Add-ins written 8 or maybe 10 years ago.
[i] Ralf Brown’s Interrupt List. Microsoft LAN Manager API, https://www.ctyme.com/rbrown.htm
[ii] Word Basic from WinWord 2.0 and made compatible in VB6
[iii] “C API” or CAPI Interface, https://support.microsoft.com/kb/190057
You may also be interested in:
Novel way to handle Outlook 2010 Fast Shutdown
Outlook 2010 Fast Shutdown feature