[python-win32] Outlook Addin Help

Bill Burns billburns at pennswoods.net
Wed May 17 05:23:12 CEST 2006

>>I think the problem is with the ActiveExplorer object(s). When I get
>>them "released" or set to None, then Outlook is happy to shutdown
>>immediately (even when *other* interfaces are still referenced). If the
>>ActiveExplorer object(s) are still referenced, then Outlook stays in
>>memory (apparently to do some clean up).
>>So, I'm looking into releasing the ActiveExplorer objects (well, all of
>>the objects, but especially the ActiveExplorer), then I think I'll have
>>the problem solved (fingers crossed).
> Hi Bill,
>   How did you end up going with this?  Do you have an updated version of the
> sample plugin that does not demonstrate the original problem?


Yes, I have fixed the original problem. I would have got back to the
list sooner, but I've been real busy lately :-).

It was definitely the ActiveExplorer object that was making Outlook hang
around in memory. I tried killing every *other* object and nothing
changed until I killed the ActiveExplorer.

My current code has a Gateway count of 1 and an Interface count of 1.
When I shutdown Outlook, it closes immediately.

The entire piece of code is below. I've used the code in the demos
directory (..\site-packages\win32com\demos\outlookAddin.py) as a basis
for my code. Keep in mind - I'm still learning, so if you see something
that smells, let me know :-)

Thank you for your help!



import sys
import os
import re

# Per comments in SpamBayes Outlook addin.py,
# I've added this here and in two other spots.
import locale
locale.setlocale(locale.LC_NUMERIC, "C")

import win32api
import win32con
from win32com import universal
from win32com.server.exception import COMException
from win32com.client import gencache, DispatchWithEvents
import winerror
import pythoncom
from win32com.client import constants

# Support for COM objects we use.
gencache.EnsureModule('{00062FFF-0000-0000-C000-000000000046}', 0, 9, 0,
bForDemand=True) # Outlook 9
gencache.EnsureModule('{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}', 0, 2, 1,
bForDemand=True) # Office 9

# The TLB defining the interfaces we implement
0, 1, 0, ["_IDTExtensibility2"])

class ButtonEvent:
     def Init(self, application):
         self.application = application
         self.activeExplorer = None

     def Close(self):
         self.application = None
         self.activeExplorer = None

     def OnClick(self, button, cancel):
         locale.setlocale(locale.LC_NUMERIC, "C")
         if self.activeExplorer is None:
             self.activeExplorer = self.application.ActiveExplorer()

         # Get the ID of the folder we're currently in.
         currentFolder = self.activeExplorer.CurrentFolder
         currentFolderID = currentFolder.Parent.StoreID,

         # Get the ID of the 'Sent Items' folder.
         sentFolder =
         sentFolderID = sentFolder.Parent.StoreID, sentFolder.EntryID

         # If we're in the 'Sent Items' folder...
         if currentFolderID[1] == sentFolderID[1]:
         else: # not in the 'Sent Items' folder.

     def getSentEmailInfo(self):
         # Grab all of the fields from the currently
         # selected sent mail item.
         sentMailItem = self.activeExplorer.Selection.Item(1)
             To = sentMailItem.To
             CC = sentMailItem.CC
             BCC = sentMailItem.BCC
             Subject = sentMailItem.Subject
             Body = sentMailItem.Body
         except: # Don't forget to change this.
             print "Hit an error in getSentEmailInfo()"
             # Better kill the ActiveExplorer or Outlook
             # will hang on shutdown.
             self.activeExplorer = None
         self.createMailMessage(To, CC, BCC, Subject, Body)

     def createMailMessage(self, To, CC, BCC, Subject, Body):
         # Create the new mail message....
         # There's usually a 'download' link in the body of the
         # 'Invitation to Bid', if we find it, we'll use it.
         site = re.findall("http://.*", Body)
         if site:
             # Our link is top-level, let's create a new link
             # that points right to the addendum directory.
             link = site[0].strip("\r")  + "/addendum"
             link = ""
         mailMessage = self.application.CreateItem(constants.olMailItem)
         mailMessage.To = To
         mailMessage.CC = CC
         mailMessage.BCC = BCC
         if "RE:" in Subject:
             mailMessage.Subject = Subject
             mailMessage.Subject = "RE: " + Subject
         mailMessage.Body = "A new addendum has been issued for this
project.\n\n%s" % (link)
         # Kill the ActiveExplorer.
         self.activeExplorer = None

     def folderError(self):
         errMsg = "You must be in the 'Sent Items' Folder to use this tool!"
                             'Folder Error',
         # Kill the ActiveExplorer.
         self.activeExplorer = None

class OutlookAddin:
     _com_interfaces_ = ['_IDTExtensibility2']
     _public_methods_ = []
     _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER
     _reg_clsid_ = "{B61B8A3D-041E-4d82-908B-1503681A55B3}"
     _reg_progid_ = "InvitationHelper.OutlookAddin"
     _reg_policy_spec_ = "win32com.server.policy.EventHandlerPolicy"

     def __init__(self):
         self.application = None

     def OnConnection(self, application, connectMode, addin, custom):
         locale.setlocale(locale.LC_NUMERIC, "C")
         self.application = application
         # ActiveExplorer may be none when started without a UI (eg,
WinCE synchronisation)
         activeExplorer = application.ActiveExplorer()
         if activeExplorer is not None:
             bars = activeExplorer.CommandBars
             toolbar = bars.Item("Standard")
             item =
toolbar.Controls.Add(Type=constants.msoControlButton, Temporary=True)
             # Hook events for the item
             item = self.toolbarButton = DispatchWithEvents(item,
             item.Caption="Invitation Helper"
             item.TooltipText = "Creates a new email from a\n" \
                                 "previously sent 'Invitation to Bid'."
             item.Enabled = True

             self.item = item

     def OnDisconnection(self, mode, custom):
         self.application = None
         print "Gateway Count: ", pythoncom._GetGatewayCount()
         print "Interface Count: ", pythoncom._GetInterfaceCount()

     def OnAddInsUpdate(self, custom):
     def OnStartupComplete(self, custom):
     def OnBeginShutdown(self, custom):

def RegisterAddin(klass):
     import _winreg
     key = _winreg.CreateKey(_winreg.HKEY_CURRENT_USER,
     subkey = _winreg.CreateKey(key, klass._reg_progid_)
     _winreg.SetValueEx(subkey, "CommandLineSafe", 0, _winreg.REG_DWORD, 0)
     _winreg.SetValueEx(subkey, "LoadBehavior", 0, _winreg.REG_DWORD, 3)
     _winreg.SetValueEx(subkey, "Description", 0, _winreg.REG_SZ,
"Invitation to Bid Tool")
     _winreg.SetValueEx(subkey, "FriendlyName", 0, _winreg.REG_SZ,
"Invitation Helper")

def UnregisterAddin(klass):
     import _winreg
"Software\\Microsoft\\Office\\Outlook\\Addins\\" + klass._reg_progid_)
     except WindowsError:

if __name__ == '__main__':
     import win32com.server.register
     if "--unregister" in sys.argv:


More information about the Python-win32 mailing list