0

I found the code bellow here on stackoverflow with following link: Python Outlook win32 event trigger when email is opened

I'm trying use this code and add the "def OnForward(self, disp, item):" to add a signature on the e-mail that i forward. But the forward does not work well. I will try explain, every time i click on foward it popsup the e-mail on new window, but appear on main outlook window too, and then the "def OnRead(self):" stop working too! i dont know if express myself correctly, but any help will be appreciated

outlook main Window

import win32com.client
import pythoncom

#Handler for Application Object
class Application_Handler(object):
    def OnItemLoad(self, item):
        print('Application::OnItemLoad')

        #Only want to work with MailItems 
        if( item.Class == win32com.client.constants.olMail ): 
            #Get a Dispatch interface to the item
            cli = win32com.client.Dispatch(item)
            #Set up a handler
            handler = win32com.client.WithEvents(cli,MailItem_Handler)
            #Store the MailItem's Dispatch interface for use later
            handler.setDisp(cli)
 
#Handler for MailItem object
class MailItem_Handler(object):
    def setDisp(self,disp):
        self._disp = disp

    def OnOpen(self,item):
        print('MailItem::OnOpen')
    
    def OnRead(self):
        print('MailItem::OnRead')
        subj = self._disp.Subject
        print('Subject:',subj)
        body = self._disp.Body
        print('Body:',body)
    def OnClose(self, item):
        return
        print('---------------MailItem::OnClose-------------------')
        print('-----------------------------------------------------')

    def OnForward(self, disp, item):
        print('---------------MailItem::OnForward-------------------')

        newMail = self._disp.Forward()
        newMail.HTMLBody = self._disp.HTMLBody + 'teste'
        newMail.Display()

        print('-----------------------------------------------------')

outlook = win32com.client.DispatchWithEvents("Outlook.Application", Application_Handler)
#Message loop
pythoncom.PumpMessages()
Eugene Astafiev
  • 47,483
  • 3
  • 24
  • 45
  • Do you have the right signature for OnForward? This is the reference: https://learn.microsoft.com/en-us/office/vba/api/outlook.mailitem.forward(even) The parameters are OnForward(self,item,bCancel). – DS_London Dec 16 '21 at 21:44
  • I corrected the quoted link above as I made an error on the OnOpen signature. – DS_London Dec 16 '21 at 22:02

3 Answers3

1

every time i click on foward it popsup the e-mail on new window, but appear on main outlook window too

In the Forward event handler you can find the following code:

newMail = self._disp.Forward()
newMail.HTMLBody = self._disp.HTMLBody + 'teste'
newMail.Display()

Which calls the Forward again and again when the corresponding event is fired. So, the Forward method causes the Forward event fired.

The Forward event is fired when the user selects the Forward action for an item, or when the Forward method is called for the item. So, there is no need to call it anew in the event handler. This is a reaction to the Forward method. The new item being forwarded is passed as a parameter to the event handler.

def OnForward(self, disp, item):
        print('---------------MailItem::OnForward-------------------')

        newMail = item
        newMail.HTMLBody = self._disp.HTMLBody + 'teste'
        
        newMail.Display()

        print('-----------------------------------------------------')
Eugene Astafiev
  • 47,483
  • 3
  • 24
  • 45
  • I think the signature is wrong for OnForward. See comment above. The 2nd parameter is the new item, the 3rd is a Boolean allowing the operation to be cancelled. – DS_London Dec 16 '21 at 21:56
  • The signature doesn't play any key role there. The event may not be fired if the signature is not correct. Or the code looses ability to cancel the default action. If the event procedure sets the `cancel` argument to `true`, the forward operation is not completed and the new item is not displayed. So, this is not the case. – Eugene Astafiev Dec 17 '21 at 08:59
  • The code on the answer sets `newMail = item`. If the event if passing (IDispatch*, bool) as the parameters, this is setting a MailItem reference to bool. I haven't tried it, but that looks likely to cause an issue? In win32com, all that is checked is the number of parameters, not their type (this is Python after all), so the likely the handler will get called (as the number of parameters is correct). If I wrong this code: `Message='bool' object has no attribute 'HTMLBody'`, which is what I would expect. – DS_London Dec 17 '21 at 10:31
  • The `Forward` event handler takes an item being forwarded and a boolean value. So, there is no need to call `Forward` in the event handler. – Eugene Astafiev Dec 17 '21 at 12:59
  • I suggest you try the amended code as written and see what happens for yourself. – DS_London Dec 17 '21 at 16:19
0

EDIT: As @EugeneAstafiev quite correctly points out, the call to Forward() is unnecessary and will duplicate the action that Outlook is taking. There is also no need to call Display().

However, while the signature for the event is correct in that it takes two parameters, it is wrong about the implied types. The 1st argument (after self) is a Dispatch pointer to the new item, the 2nd is a flag which allows the handler to cancel the event.

Should be:

def OnForward(self,newItem,bCancel):
    newItem.HTMLBody = self._disp.HTMLBody + 'teste'

The newItem will be displayed as long as bCancel is not set to True by the event handler. The newItem does not actually get sent until the user clicks on the Send button, or the Send() method is called.

The reference for MailItem and its Events is here.

As an aside, on my tests setting bCancel=True has no effect, and the item is always displayed. My guess is that win32com is not handling the [in,out] reference parameter correctly.

DS_London
  • 3,644
  • 1
  • 7
  • 24
  • The signature doesn't play any key role there. The event may not be fired if the signature is not correct. Or the code looses ability to cancel the default action. If the event procedure sets the cancel argument to true, the forward operation is not completed and the new item is not displayed. So, this is not the case. – Eugene Astafiev Dec 17 '21 at 08:59
  • @EugeneAstafiev The code in my answer runs (in my tests at least), and adds 'teste' to the mail body before displaying. However, the bool parameter does seems to be ignored: even if I set bCancel=True, the message is still displayed: maybe the syntax for an [in,out] parameter is incorrect. bCancel is certainly arriving in the function as a boolean, set to False. – DS_London Dec 17 '21 at 10:45
  • The problem is not related to the declaration. – Eugene Astafiev Dec 17 '21 at 12:57
  • Hi @DS_London ,I tryed use your code. The part that i copyed works perfectly, but in the forward event i'm getting some problems. What i'm looking for, is something that when i click on foward, or reply or reply all, open a new window with the signature on HTML mail body before sending (i want to see the signature before send the e-mail). I already have a kind of script like this written on VBA and it works well, but i think python is more robust. And after see your code i was trying to contact you, but i did not find anyway to contact you :) – Mário Jorge Rocha Dec 17 '21 at 13:31
  • @MárioJorgeRocha Perhaps add what you have done that works in VBA. Usually that code can be directly ported to win32com and Python, as the Outlook object model is the same. – DS_London Dec 17 '21 at 16:32
  • @DS_London i already tryed, and I was uncucessfull, so i dicide write a new draft in python starting with your code. But i just stopped in this part. Do you know where to find any documentation, with some python exemples? Any help would be appreciated :) – Mário Jorge Rocha Dec 17 '21 at 17:43
0

I had a similar problem and found a solution. Indeed win32com is handling this perfectly well. The client side implementation of the event handler function simply has to have the (new) value of the by-ref parameter as an (additional) return value. In my case, the COM server signature required an HRESULT return. By adding a second return value, I was able to manipulate the by-ref parameter as whished.

Max
  • 109
  • 2
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 28 '23 at 08:40