[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?
>
Mark,
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!
Bill
<code>
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
universal.RegisterInterfaces('{AC0714F2-3D04-11D1-AE7D-00A0C90F26F4}',
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
self.close()
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,
currentFolder.EntryID
# Get the ID of the 'Sent Items' folder.
sentFolder =
self.application.Session.GetDefaultFolder(constants.olFolderSentMail)
sentFolderID = sentFolder.Parent.StoreID, sentFolder.EntryID
# If we're in the 'Sent Items' folder...
if currentFolderID[1] == sentFolderID[1]:
self.getSentEmailInfo()
else: # not in the 'Sent Items' folder.
self.folderError()
def getSentEmailInfo(self):
# Grab all of the fields from the currently
# selected sent mail item.
sentMailItem = self.activeExplorer.Selection.Item(1)
try:
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"
else:
link = ""
mailMessage = self.application.CreateItem(constants.olMailItem)
mailMessage.To = To
mailMessage.CC = CC
mailMessage.BCC = BCC
if "RE:" in Subject:
mailMessage.Subject = Subject
else:
mailMessage.Subject = "RE: " + Subject
mailMessage.Body = "A new addendum has been issued for this
project.\n\n%s" % (link)
mailMessage.Display()
# Kill the ActiveExplorer.
self.activeExplorer = None
def folderError(self):
errMsg = "You must be in the 'Sent Items' Folder to use this tool!"
win32api.MessageBox(0,
errMsg,
'Folder Error',
win32con.MB_ICONEXCLAMATION)
# 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,
ButtonEvent)
item.Caption="Invitation Helper"
item.TooltipText = "Creates a new email from a\n" \
"previously sent 'Invitation to Bid'."
item.Enabled = True
item.Init(self.application)
self.item = item
def OnDisconnection(self, mode, custom):
self.item.Close()
self.application = None
print "Gateway Count: ", pythoncom._GetGatewayCount()
print "Interface Count: ", pythoncom._GetInterfaceCount()
def OnAddInsUpdate(self, custom):
pass
def OnStartupComplete(self, custom):
pass
def OnBeginShutdown(self, custom):
pass
def RegisterAddin(klass):
import _winreg
key = _winreg.CreateKey(_winreg.HKEY_CURRENT_USER,
"Software\\Microsoft\\Office\\Outlook\\Addins")
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
try:
_winreg.DeleteKey(_winreg.HKEY_CURRENT_USER,
"Software\\Microsoft\\Office\\Outlook\\Addins\\" + klass._reg_progid_)
except WindowsError:
pass
if __name__ == '__main__':
import win32com.server.register
win32com.server.register.UseCommandLine(OutlookAddin)
if "--unregister" in sys.argv:
UnregisterAddin(OutlookAddin)
else:
RegisterAddin(OutlookAddin)
</code>
More information about the Python-win32
mailing list