Capture SendMail event in Outlook COM

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Stavencross
Posts: 90
Joined: 24 May 2016, 16:42

Capture SendMail event in Outlook COM

13 Sep 2018, 10:51

I'm trying to figure out how to popup a messagebox when a user clicks Send in an email that was created via outlook COM

Code: Select all


MessageSubject := "Hello World!"
CCAddress := "[email protected]"

ol := ComObjCreate("Outlook.Application")
ns := ol.getNamespace("MAPI")
ns.logon("","",true,false)
newMail := ol.CreateItem(0)
olFormatHTML := 2
newMail.BodyFormat := olFormatHTML
newMail.Subject := MessageSubject
newMail.CC := CCAddress
newMail.HTMLBody := emailTemplate
newMail.Display
;show messagebox  when user clicks send

I found the following posts on the subject, but they are difficult to follow and don't seem to get me where i want to go:
https://autohotkey.com/board/topic/1127 ... end-event/
https://autohotkey.com/board/topic/8224 ... s-failure/

EDIT:
fixed typo in title
awel20
Posts: 211
Joined: 19 Mar 2018, 14:09

Re: Capture SendMail event in Outlook COM

13 Sep 2018, 11:57

Code: Select all

#Persistent
olMailItem := 0
ol := ComObjCreate("Outlook.Application")
newMail := ol.CreateItem(olMailItem)
newMail.Subject := "Hello World!"
newMail.Display
; Connect to newMail events
ComObjConnect(newMail, new MailItemEvents)
return

class MailItemEvents
{
    ; MailItem.Send Event (Outlook)
    ; https://docs.microsoft.com/en-us/office/vba/api/Outlook.MailItem.Send(even)
    Send(Cancel, MailItem)
    {
        MsgBox % "Sending: " MailItem.Subject
        ; Disconnect from MailItem events
        ComObjConnect(MailItem)
        ExitApp
    }
}
Stavencross
Posts: 90
Joined: 24 May 2016, 16:42

Re: Capture SendMail event in Outlook COM

13 Sep 2018, 13:37

awel20 wrote:

Code: Select all

#Persistent
olMailItem := 0
ol := ComObjCreate("Outlook.Application")
newMail := ol.CreateItem(olMailItem)
newMail.Subject := "Hello World!"
newMail.Display
; Connect to newMail events
ComObjConnect(newMail, new MailItemEvents)
return

class MailItemEvents
{
    ; MailItem.Send Event (Outlook)
    ; https://docs.microsoft.com/en-us/office/vba/api/Outlook.MailItem.Send(even)
    Send(Cancel, MailItem)
    {
        MsgBox % "Sending: " MailItem.Subject
        ; Disconnect from MailItem events
        ComObjConnect(MailItem)
        ExitApp
    }
}

I see! And mailitem has all the properties of the email, like to to address and body and such? Nice!
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: Capture SendMail event in Outlook COM

13 Sep 2018, 13:46

awel20 wrote:

Code: Select all

#Persistent
olMailItem := 0
ol := ComObjCreate("Outlook.Application")
newMail := ol.CreateItem(olMailItem)
newMail.Subject := "Hello World!"
newMail.Display
; Connect to newMail events
ComObjConnect(newMail, new MailItemEvents)
return

class MailItemEvents
{
    ; MailItem.Send Event (Outlook)
    ; https://docs.microsoft.com/en-us/office/vba/api/Outlook.MailItem.Send(even)
    Send(Cancel, MailItem)
    {
        MsgBox % "Sending: " MailItem.Subject
        ; Disconnect from MailItem events
        ComObjConnect(MailItem)
        ExitApp
    }
}
The MailItem.Send Event only links to a specific MailItem which is very limiting. If you want a more global approach then you can use the application event ItemSend. This is triggered any time the application sends an email not just when a specific email is sent.

Code: Select all

#Persistent

olApp := ComObjActive("Outlook.Application")
ComObjConnect(olApp, "EventApp_")

EventApp_ItemSend(olItem, Cancel)
{
	if (olItem.SendUsingAccount.DisplayName = "[email protected]")
	{
		MsgBox % olItem.Subject "`nYou are not authorized to send emails from: [email protected]"
		return ComObject(0xB,0) ; VT_Bool False = Cancels Send with Email staying open
	}
		
	if InStr(olItem.Body, "stupid")
	{
		MsgBox The new company policy is to NOT call our customers "stupid".`nPlease rephrase your email.
		return ComObject(0xB,0) ; VT_Bool False = Cancels Send with Email staying open
	}
}
Just an aside, I us a script like this on multiple computers within an office to check for stuff before an email is sent but I found it better to use an VBA script within Outlook so that the script is always running when Outlook is running. It does require some know how to get the VBA script to run automatically when Outlook starts without any security warnings. It has been very clean and reliable, and pretty dummy-proof once setup in Outlook.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
Stavencross
Posts: 90
Joined: 24 May 2016, 16:42

Re: Capture SendMail event in Outlook COM

13 Sep 2018, 14:31

FanaticGuru wrote:
awel20 wrote:

Code: Select all

#Persistent
olMailItem := 0
ol := ComObjCreate("Outlook.Application")
newMail := ol.CreateItem(olMailItem)
newMail.Subject := "Hello World!"
newMail.Display
; Connect to newMail events
ComObjConnect(newMail, new MailItemEvents)
return

class MailItemEvents
{
    ; MailItem.Send Event (Outlook)
    ; https://docs.microsoft.com/en-us/office/vba/api/Outlook.MailItem.Send(even)
    Send(Cancel, MailItem)
    {
        MsgBox % "Sending: " MailItem.Subject
        ; Disconnect from MailItem events
        ComObjConnect(MailItem)
        ExitApp
    }
}
The MailItem.Send Event only links to a specific MailItem which is very limiting. If you want a more global approach then you can use the application event ItemSend. This is triggered any time the application sends an email not just when a specific email is sent.

Code: Select all

#Persistent

olApp := ComObjActive("Outlook.Application")
ComObjConnect(olApp, "EventApp_")

EventApp_ItemSend(olItem, Cancel)
{
	if (olItem.SendUsingAccount.DisplayName = "[email protected]")
	{
		MsgBox % olItem.Subject "`nYou are not authorized to send emails from: [email protected]"
		return ComObject(0xB,0) ; VT_Bool False = Cancels Send with Email staying open
	}
		
	if InStr(olItem.Body, "stupid")
	{
		MsgBox The new company policy is to NOT call our customers "stupid".`nPlease rephrase your email.
		return ComObject(0xB,0) ; VT_Bool False = Cancels Send with Email staying open
	}
}
Just an aside, I us a script like this on multiple computers within an office to check for stuff before an email is sent but I found it better to use an VBA script within Outlook so that the script is always running when Outlook is running. It does require some know how to get the VBA script to run automatically when Outlook starts without any security warnings. It has been very clean and reliable, and pretty dummy-proof once setup in Outlook.

FG

Both of these solutions are amazing and solved two very different problems!

Is there anyway to pass an additional Param to the class? For instance, I was to pass a string with a value of 'samesfordfd' so that when the class is called, it can write to a file that the email has been sent to account named 'samesfordfd'?
awel20
Posts: 211
Joined: 19 Mar 2018, 14:09

Re: Capture SendMail event in Outlook COM

13 Sep 2018, 14:42

Code: Select all

#Persistent
olMailItem := 0
ol := ComObjCreate("Outlook.Application")
newMail := ol.CreateItem(olMailItem)
newMail.Subject := "Hello World!"
newMail.Display
; you can pass params when creating the object
MailEventsObj := new MailItemEvents("samesfordfd")
; you can also add to the object after it is created
MailEventsObj.myOtherString := "abc"
; Connect to newMail events
ComObjConnect(newMail, MailEventsObj)
return

class MailItemEvents
{
    ; __New is run when the object is created
    __New(myString)
    {
        ; store 'myString' in this object
        this.myString := myString
    }
    
    ; MailItem.Send Event (Outlook)
    ; https://docs.microsoft.com/en-us/office/vba/api/Outlook.MailItem.Send(even)
    Send(Cancel, MailItem)
    {
        MsgBox % "Sending: " MailItem.Subject "`n" this.myString "`n" this.myOtherString
        ; Disconnect from MailItem events
        ComObjConnect(MailItem)
        ExitApp
    }
}
Stavencross
Posts: 90
Joined: 24 May 2016, 16:42

Re: Capture SendMail event in Outlook COM

13 Sep 2018, 15:04

awel20 wrote:

Code: Select all

#Persistent
olMailItem := 0
ol := ComObjCreate("Outlook.Application")
newMail := ol.CreateItem(olMailItem)
newMail.Subject := "Hello World!"
newMail.Display
; you can pass params when creating the object
MailEventsObj := new MailItemEvents("samesfordfd")
; you can also add to the object after it is created
MailEventsObj.myOtherString := "abc"
; Connect to newMail events
ComObjConnect(newMail, MailEventsObj)
return

class MailItemEvents
{
    ; __New is run when the object is created
    __New(myString)
    {
        ; store 'myString' in this object
        this.myString := myString
    }
    
    ; MailItem.Send Event (Outlook)
    ; https://docs.microsoft.com/en-us/office/vba/api/Outlook.MailItem.Send(even)
    Send(Cancel, MailItem)
    {
        MsgBox % "Sending: " MailItem.Subject "`n" this.myString "`n" this.myOtherString
        ; Disconnect from MailItem events
        ComObjConnect(MailItem)
        ExitApp
    }
}


Wow this is really amazing! Thank you so much!
Stavencross
Posts: 90
Joined: 24 May 2016, 16:42

Re: Capture SendMail event in Outlook COM

13 Sep 2018, 15:48

awel20 wrote:

Code: Select all

#Persistent
olMailItem := 0
ol := ComObjCreate("Outlook.Application")
newMail := ol.CreateItem(olMailItem)
newMail.Subject := "Hello World!"
newMail.Display
; you can pass params when creating the object
MailEventsObj := new MailItemEvents("samesfordfd")
; you can also add to the object after it is created
MailEventsObj.myOtherString := "abc"
; Connect to newMail events
ComObjConnect(newMail, MailEventsObj)
return

class MailItemEvents
{
    ; __New is run when the object is created
    __New(myString)
    {
        ; store 'myString' in this object
        this.myString := myString
    }
    
    ; MailItem.Send Event (Outlook)
    ; https://docs.microsoft.com/en-us/office/vba/api/Outlook.MailItem.Send(even)
    Send(Cancel, MailItem)
    {
        MsgBox % "Sending: " MailItem.Subject "`n" this.myString "`n" this.myOtherString
        ; Disconnect from MailItem events
        ComObjConnect(MailItem)
        ExitApp
    }
}

Code: Select all


makeanemail()

makeanemail() {
ol := ComObjCreate("Outlook.Application")
ns := ol.getNamespace("MAPI")
ns.logon("","",true,false)		
newMail := ol.CreateItem(0)
newMail.Display	
ComObjConnect(newMail, new MailItemEvents)
return
}

class MailItemEvents
{
    ; MailItem.Send Event (Outlook)
    ; https://docs.microsoft.com/en-us/office/vba/api/Outlook.MailItem.Send(even)
	Send(Cancel, MailItem)
	{
		MsgBox % "Sending: " MailItem.Subject
        ; Disconnect from MailItem events
		ComObjConnect(MailItem)
		ExitApp
	}
}
Your example works perfectly. However, when I drop the mail creation into a function, the class is no longer called?
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: Capture SendMail event in Outlook COM

13 Sep 2018, 16:05

awel20 wrote:

Code: Select all

#Persistent
olMailItem := 0
ol := ComObjCreate("Outlook.Application")
newMail := ol.CreateItem(olMailItem)
newMail.Subject := "Hello World!"
newMail.Display
; you can pass params when creating the object
MailEventsObj := new MailItemEvents("samesfordfd")
; you can also add to the object after it is created
MailEventsObj.myOtherString := "abc"
; Connect to newMail events
ComObjConnect(newMail, MailEventsObj)
return

class MailItemEvents
{
    ; __New is run when the object is created
    __New(myString)
    {
        ; store 'myString' in this object
        this.myString := myString
    }
    
    ; MailItem.Send Event (Outlook)
    ; https://docs.microsoft.com/en-us/office/vba/api/Outlook.MailItem.Send(even)
    Send(Cancel, MailItem)
    {
        MsgBox % "Sending: " MailItem.Subject "`n" this.myString "`n" this.myOtherString
        ; Disconnect from MailItem events
        ComObjConnect(MailItem)
        ExitApp
    }
}
Before I did not see much point in wrapping it in a class but this is pretty slick for limiting the scope of variables to the method/function.

In the past, I have taken the lazy approach and just used a global variable to get info into the event function but this is cooler. I will remember this for future use.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
Stavencross
Posts: 90
Joined: 24 May 2016, 16:42

Re: Capture SendMail event in Outlook COM

13 Sep 2018, 16:10

Before I did not see much point in wrapping it in a class but this is pretty slick for limiting the scope of variables to the method/function.

In the past, I have taken the lazy approach and just used a global variable to get info into the event function but this is cooler. I will remember this for future use.

FG
Why would you want to limit the scope of variables to the method/funtion? I'm assuming this has to do with the whole 'private/public' function modifiers, which I really don't understand haha
awel20
Posts: 211
Joined: 19 Mar 2018, 14:09

Re: Capture SendMail event in Outlook COM

13 Sep 2018, 16:12

Stavencross wrote:Your example works perfectly. However, when I drop the mail creation into a function, the class is no longer called?

Code: Select all

#Persistent
makeanemail()

makeanemail() {
    static olMailItem := 0
    ol := ComObjCreate("Outlook.Application")
    newMail := ol.CreateItem(olMailItem)
    newMail.Subject := "Hello World!"
    newMail.Display
    ; When the function returns it clears the local variables. We need to save at least one reference
    ; to 'newMail' somewhere, so here we pass it as a param and save it in the new MailItemEvents object.
    ComObjConnect(newMail, new MailItemEvents(newMail))
    return
}

class MailItemEvents
{
    __New(MailItem)
    {
        this.savedMailItem := MailItem
    }
    
    Send(Cancel, MailItem)
    {
        MsgBox % "Sending: " MailItem.Subject
        ; Disconnect from MailItem events
        ComObjConnect(MailItem)
        this := ""
        ExitApp
    }
}
Stavencross
Posts: 90
Joined: 24 May 2016, 16:42

Re: Capture SendMail event in Outlook COM

13 Sep 2018, 16:15

awel20 wrote:
Stavencross wrote:Your example works perfectly. However, when I drop the mail creation into a function, the class is no longer called?

Code: Select all

#Persistent
makeanemail()

makeanemail() {
    static olMailItem := 0
    ol := ComObjCreate("Outlook.Application")
    newMail := ol.CreateItem(olMailItem)
    newMail.Subject := "Hello World!"
    newMail.Display
    ; When the function returns it clears the local variables. We need to save at least one reference
    ; to 'newMail' somewhere, so here we pass it as a param and save it in the new MailItemEvents object.
    ComObjConnect(newMail, new MailItemEvents(newMail))
    return
}

class MailItemEvents
{
    __New(MailItem)
    {
        this.savedMailItem := MailItem
    }
    
    Send(Cancel, MailItem)
    {
        MsgBox % "Sending: " MailItem.Subject
        ; Disconnect from MailItem events
        ComObjConnect(MailItem)
        this := ""
        ExitApp
    }
}
Damn you're good. I'll see if I can stitch it all together! Thanks so much for your help!

Code: Select all


createEmail("legacy")

createEmail(newAccountName) {
	;Create the email
	olMailItem := 0
	ol := ComObjCreate("Outlook.Application")
	newMail := ol.CreateItem(olMailItem)
	newMail.Subject := "Hello World!"
	;show the email
	newMail.Display
	;Now, we create our obj that we'll pass to our listening class. Pass in the newMail reference.
	MailEventsObj := new MailItemEvents(newMail)
	;Now, we'll add in the name of this account.
	MailEventsObj.accountName := "" . newAccountName . "" ;double quotes to escape it, pass the func param in 
	ComObjConnect(newMail,MailEventsObj) ;Lets start listening to events that happen with 'newMail' reference
	
}

;Create the listener class
class MailItemEvents
{
	;this section fires once the object (newMail) is created. 
	__New(MailItem)
	{
		;pull in the newMail reference
		this.savedMailItem := MailItem
		;pull in the accountName from the MailEventsObj object
		this.account := accountName		
	}
	Send(Cancel, MailItem)
	{
		;CALL IT OUT LIKE A BOSS! this will pop a messagebox when you click send on a newMail email!		
		MsgBox % "Sending: " MailItem.Subject "`n" this.accountName
        ; Disconnect from MailItem events
		ComObjConnect(MailItem)
		ExitApp
	}
}

Woo hoo! it works perfectly!

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot], sbrady19 and 136 guests