Novel way to handle Outlook 2010 Fast Shutdown
In order to understand how Outlook’s Fast Shutdown mechanism affects your Outlook Add-ins, I have made a utility to test and demonstrate Outlook 2010 fast shutdown mechanism and an example Outlook Add-in.
Fast Shutdown Utility
The Fast Shutdown Exercise Machine Utility “FSU” is a tray-area application written in Delphi 2009 (Unicode) with best practices. It loads and saves into \Application Data\ or \AppData\ location in your user account using XML instead of Registry or INI files.
Tab 1 Global: This controls global startup settings. FSU detects both 32-bits and 64-bits Outlook. It reads and writes both 32-bit and 64-bit registry locations.
You can select from four different shutdown options: Enabled, Opt-in, Disabled and Default. This corresponds to [Software\Microsoft\Office\14.0\Outlook\Options\Shutdown\FastShutdownBehavior:DWORD] entry in the Windows Registry.
Tab 2 Individual Add-ins: Inclusive of global settings, you can select Enable Shutdown Notification for all Add-ins or default settings, along with individual settings for each Add-in.
Tab 3 Utility: In order to simulate more annoying Outlook and Word situations, there is ability to kill Outlook processes in memory, make Outlook hidden, make Word sessions hidden and kill Word processes in memory.
Tab 4: About is the About page. This tab contains the copyright notice and link to this article.
Outlook Add-in example
I've developed an example Outlook Add-in to get ready for Outlook 2010 and discovered a novel way to handle Outlook Fast Shutdown 2010.
The software used to develop this Add-in and additional components are:
- Delphi 2009/2010
- Add-in Express for Office and Delphi
64-bit, 32-bit registry on Windows 7, Windows 2008 Server
Using Delphi 2009 / Delphi 2010 we use the function OutlookVersion64 to detect if 64-bit Outlook is installed or else use OutlookVersion to find the Outlook version
function OutlookVersion64: string; var sVer, sTmp: string; const sOutlookDefault = 'Not Installed'; sOutlook14 = 'Outlook.Application.14'; // first 64-bit version begin sVer := sOutlookDefault; with TRegistry.Create() do try // 64-bit Outlook Access := KEY_WOW64_64KEY or KEY_READ; RootKey := HKEY_CLASSES_ROOT; if OpenKey('Outlook.Application.14', false) then begin sVer := sOutlook14; end; finally free end; Result := sVer; end;
Note the usage of KEY_READ to allow read-only access to HKEY_CLASSES_ROOT (required when the user is not an Administrator) and KEY_WOW64_64KEY to access 64-bit portions of Windows Registry.
//Detects Outlook version for 32-bit Machines function OutlookVersion: string; var sVer, sTmp: string; reg: TRegistry; const sOutlookDefault = 'Not Installed'; sOutlook14 = 'Outlook.Application.14'; sOutlook12 = 'Outlook.Application.12'; sOutlook11 = 'Outlook.Application.11'; sOutlook10 = 'Outlook.Application.10'; sOutlook9 = 'Outlook.Application.9'; sOutlook8 = 'Outlook.Application.8'; begin sVer := sOutlookDefault; reg := TRegistry.Create(KEY_READ); reg.RootKey := HKEY_CLASSES_ROOT; if reg.OpenKey('Outlook.Application\CurVer\', false) then begin sTmp := reg.ReadString(''); if sTmp = sOutlook14 then sVer := sOutlook14 else if sTmp = sOutlook12 then sVer := sOutlook12 else if sTmp = sOutlook11 then sVer := sOutlook11 else if sTmp = sOutlook10 then sVer := sOutlook10 else if sTmp = sOutlook9 then sVer := sOutlook9 else if sTmp = sOutlook8 then sVer := sOutlook8; end; reg.free; Result := sVer; end;
Novel method to alleviate Fast Startup issue
|Issue||Before Fast Startup||After Fast Startup|
|Saving of toolbars||Code written in OnShutDown||Redirect to semaphore and specific routine (see below)|
|Database||Code written in OnDisconnect|
|Registry or INI settings save||Code written in OnShutDown|
|Additional clean-ups from your program||Code written in OnDisconnect|
When should I use OnShutDown and OnDisconnect?
OnShutDown, the toolbars, certain registry values are present. Thus, if you wrote your own toolbar customizations and need to save those values to registry, you should use OnShutDown. When you close your own final settings (e.g., the database connection, freeing custom objects in your own list) you should use OnDisconnect.
|Outlook Pointers||Present (e.g., Toolbars, etc.). Opportunity to save GUI changes.||May not be present. They are 0x000000 (null) or values may have changed.
(Accessing any NULL variables may cause 0x8000ffff catastrophic failure error message or various COM error messages)
|Your Database Connection, Registry key saving, DLL unloading||Database connection may have unsaved data.
DLLs may be locked.Questions:
Try to unload any DLLs when not in use during processing finish (after loading DLL)Is a persistent database connection truly necessary?
|Where you unload your DLLs, close database connection.
(Suggestion: Move codes to OnShutDown instead)
The flowchart for pre-Outlook 2010 is:
You write your code in OnShutDown/OnBeginShutDown (1) and OnDisconnect/OnAddinFinalize (3).
With Outlook 2010 it is:
Refactoring steps are:
- You would place an event sink to capture the OnQuit event from Outlook and start Shutdown processing.
There is a semaphore to guard against Shutdown twice.
- Since OnShutDown is called, if the semaphore is not set, it will call the Shutdown code.
This code change handles every situation:
|Testing from Fast Shutdown Utility||Logic|
|Outlook 2010 with Fast Shutdown or Opt-in||
|Outlook 2010 with Fast Shutdown disabled||
|Outlook 2007, Outlook 2003||
The interface code is:
TAddInModule = class(TadxCOMAddInModule) dataconn: TADOConnection; adxOutlookAppEvents1: TadxOutlookAppEvents; procedure adxCOMAddInModuleCreate(Sender: TObject); procedure adxOutlookAppEvents1Quit(Sender: TObject); procedure adxCOMAddInModuleAddInBeginShutdown(Sender: TObject); procedure adxCOMAddInModuleAddInFinalize(Sender: TObject); private protected public bFastShutdown: Boolean; // Semaphore procedure MergedClose; end;
The implementation code is:
procedure TAddInModule.adxCOMAddInModuleCreate(Sender: TObject); begin bFastShutdown := false; // Initialize Fast Shutdown to false end; procedure TAddInModule.adxCOMAddInModuleAddInBeginShutdown(Sender: TObject); begin MergedClose; // Call new procedure instead end; procedure TAddInModule.adxCOMAddInModuleAddInFinalize(Sender: TObject); begin //Nothing to do end; procedure TAddInModule.adxOutlookAppEvents1Quit(Sender: TObject); begin MergedClose; // Call new procedure instead end; procedure TAddInModule.MergedClose; begin if bFastShutdown then // Semaphore to prevent Shutdown called twice. Exit; bFastShutDown := true; // Save toolbars // Free any memory // Close any pending database connection (if file-based) end;
1Icons were used from https://www.wpclipart.com/ (public domain license)