ComObjConnect() help

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

ComObjConnect() help

16 Mar 2017, 14:35

Hey all,

This is my first dive into classes with AHK. I'm trying to do something fairly simple. I have a tray menu item that launches a function called changeControl(). This creates a new instance of my class call changeControl.

I have it correctly creating the email via the createCCEmail method but I am having trouble connecting onto the events of the email. I tried creating a nested class for the email's events and another for the events of an appointment item (we can worry about that later). I tried changing my menu item to launch an actual label instead of a function because i thought the problem was that my class instance was disappearing after the function returned but that still didn't seem to work. Below is a snippet my code. The menu item's function is at the bottom. This gets called correctly and the instance of changeControl is created and the email displays as desired. I just can't get the event sink to work.

Code: Select all

class changeControl {
	Static olFolderCalendar := 9
	Static olAppointmentItem := 1
	Static olFree := 0
	Static olTentative := 1
	Static olBusy := 2
	Static olOutOfOffice := 3
	
	__New(itemName) {
		if(!this.ol := this.getOutlookReference())
			return
			
		this.labelName := itemName
		return this
	}
	
	; Attempts to get the active Outlook application
	; If it fails, it attempts to create one and display the main inbox
	; Returns the reference if it is found, otherwise returns false
	getOutlookReference() {
		try
			ol := ComObjActive("Outlook.Application")
		catch e1 {
			try {
				ol := ComObjCreate("Outlook.Application")
				ol.Session.GetDefaultFolder(6).Display
			}
			catch e2 {
				MsgBox, % "Did not find an instance of Outlook and was unable to create one!`r`nErrors were:`r`n`t" e1.message "`r`n`t" e2.message
				return False
			}
		}
		Return ol
	}
	
	createCCEmail() {
		olNS := ol.session
		ccBody := "
			(
			<body style='font-family:Arial;'>
			<div>
			<p><b>Purpose of the change:</b>
				<o:p><br /></o:p>
			</p>
			<p><b>Risk Level:</b>
				<o:p><br /></o:p>
			</p>
			<p><b>Date of the change:</b>
				<o:p></o:p>
			</p>
			<p>
				<o:p>" A_MM "/" A_DD "/" A_YYYY "</o:p>
			</p>
			<p><b>Implementation Plan (Technical Version):</b>
				<o:p><br /></o:p>
			</p>
			<p><b>Downtime expected/potential:</b>
				<o:p><br /></o:p>
			</p>
			<p><b>Rollback Plan:</b>
				<o:p><br /></o:p>
			</p>
			<p><b>Peer Review:</b>
				<o:p><br /></o:p>
			</p>
			<p><b>Management/Team Lead Approval:</b> 
				<o:p><br /></o:p>
			</p>
			<p><b>User awareness communication plan:</b>
				<o:p><br /></o:p>
			</p>
			</div>
			<body>
			)"
		
		olEmail := this.ol.CreateItem(0)
		olEmail.To := "<email>"
		olEmail.Subject := "CC# - "
		olEmail.Display
		if (this.labelName = "Change Control")
			olEmail.HTMLBody := ccBody . olEmail.HTMLBody
		ComObjConnect(olEmail, new this.email_events(this))
	}
	
	createCCAppt(subject, body) {
		olOwner := this.CreateRecipient("IS-EventsCalendar")
		olOwner.Resolve
		If olOwner.Resolved
			olCalendar := this.ol.session.GetSharedDefaultFolder(olOwner, olFolderCalendar).Items
		olNewAppt := olCalendar.Add(olAppointmentItem)
		olNewAppt.BusyStatus := olFree
		olNewAppt.AllDayEvent := True
		olNewAppt.ReminderSet := False
		olNewAppt.Subject := (subject ? subject : "CC# - ")
		olNewAppt.Body := body
		olNewAppt.Display
		ComObjConnect(olNewAppt, new this.appt_events(this))
	}
	
	class email_events {
		__New(parent) {
			this.parent := parent
		}
		
		Send(args*) {
			this.parent.createCCAppt(args[1].Subject, args[1].RTFBody)
		}
		
		Close(args*) {
			msgbox, test
			for _,arg in args
				msgbox, % arg
		}
	}
	
	class appt_events {
		__New(parent) {
			this.parent := parent
		}
		
		BeforeAutoSave(args*) {
			return true
		}
		
		Send(args*) {
			this.parent.ol := ""
		}
	}
}

changeControl(itemName, itemPos, menuName) {
	ccEmail := new changeControl(itemName).createCCEmail()
}
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: ComObjConnect() help

16 Mar 2017, 15:16

My Changes are marked with ; <--. See my comments for more explanation.

Code: Select all

#Persistent
#SingleInstance, Force
itemName := "Change Control"  ; <--
ccEmail := new changeControl(itemName)  ; <--
ccEmail.createCCEmail()  ; <--
return

Esc::ExitApp  ; <--

class changeControl {
    Static olFolderCalendar := 9
    Static olAppointmentItem := 1
    Static olFree := 0
    Static olTentative := 1
    Static olBusy := 2
    Static olOutOfOffice := 3
    
    __New(itemName) {
        if(!this.ol := this.getOutlookReference())
            return
            
        this.labelName := itemName
        ;~ return this  ; <--
    }
    
    ; Attempts to get the active Outlook application
    ; If it fails, it attempts to create one and display the main inbox
    ; Returns the reference if it is found, otherwise returns false
    getOutlookReference() {
        try
            ol := ComObjActive("Outlook.Application")
        catch e1 {
            try {
                ol := ComObjCreate("Outlook.Application")
                ol.Session.GetDefaultFolder(6).Display
            }
            catch e2 {
                MsgBox, % "Did not find an instance of Outlook and was unable to create one!`r`nErrors were:`r`n`t" e1.message "`r`n`t" e2.message
                return False
            }
        }
        Return ol
    }
    
    createCCEmail() {
        olNS := ol.session
        ccBody :=
            (Ltrim Join`r`n
           "<body style='font-family:Arial;'>
            <div>
            <p><b>Purpose of the change:</b>
            </p>
            </div>
            <body>"
            )
        
        olEmail := this.ol.CreateItem(0)
        olEmail.To := "<email>"
        olEmail.Subject := "CC# - "
        olEmail.Display
        if (this.labelName = "Change Control")
            olEmail.HTMLBody := ccBody . olEmail.HTMLBody
        ComObjConnect(olEmail, new this.email_events(this))
        this.Email := olEmail  ; <-- Save a reference to the mailitem object
    }
    
    ;~ createCCAppt(subject, body) {
        ;~ olOwner := this.CreateRecipient("IS-EventsCalendar")
        ;~ olOwner.Resolve
        ;~ If olOwner.Resolved
            ;~ olCalendar := this.ol.session.GetSharedDefaultFolder(olOwner, olFolderCalendar).Items
        ;~ olNewAppt := olCalendar.Add(olAppointmentItem)
        ;~ olNewAppt.BusyStatus := olFree
        ;~ olNewAppt.AllDayEvent := True
        ;~ olNewAppt.ReminderSet := False
        ;~ olNewAppt.Subject := (subject ? subject : "CC# - ")
        ;~ olNewAppt.Body := body
        ;~ olNewAppt.Display
        ;~ ComObjConnect(olNewAppt, new this.appt_events(this))
    ;~ }
    
    class email_events {
        __New(parent) {
            this.parent := parent
        }
        
        Send(args*) {
            MsgBox, % args[1].Subject "`n"  ; <-- First arg is "Cancel"
                    . args[1].RTFBody "`n"
                    . args[2].Subject "`n"
                    . args[2].RTFBody "`n"
            ;~ this.parent.createCCAppt(args[1].Subject, args[1].RTFBody)
        }
        
        Close(args*) {
            msgbox, test
            ; <-- The Msgbox in the for-loop will not display any text. First arg will be "cancel" param, second arg will be the mailitem object.
            for _, arg in args
            {
                msgbox, % arg
                if (_ = 2)  ; <--
                    MsgBox, % arg.class  ; olMail = 43
            }
        }
    }
    
    ;~ class appt_events {
        ;~ __New(parent) {
            ;~ this.parent := parent
        ;~ }
        
        ;~ BeforeAutoSave(args*) {
            ;~ return true
        ;~ }
        
        ;~ Send(args*) {
            ;~ this.parent.ol := ""
        ;~ }
    ;~ }
}

;~ changeControl(itemName, itemPos, menuName) {
    ;~ ccEmail := new changeControl(itemName).createCCEmail()  ; <-- This stores the return value of 'createCCEmail', not the new object.
;~ }
[Edited shortly after posting]
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: ComObjConnect() help

16 Mar 2017, 15:50

Fantastic! So it seems my biggest mishaps were not saving a reference to the mailItem and misunderstanding the order of the arguments passed to the event handlers. I have the full thing working now. Thanks kindly!
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: ComObjConnect() help

16 Mar 2017, 17:04

kczx3 wrote:So it seems my biggest mishaps were not saving a reference to the mailItem and misunderstanding the order of the arguments passed to the event handlers.
Yes. Also there is this one additional comment:
kon wrote:

Code: Select all

;~ changeControl(itemName, itemPos, menuName) {
    ;~ ccEmail := new changeControl(itemName).createCCEmail()  ; <-- This stores the return value of 'createCCEmail', not the new object.
;~ }
You may not have looked at this because I commented-out this portion. But, my comment is incorrect. It does not "store the return value of createCCEmail", but your version still needs to be changed too. (Split it into two lines as I have done in the auto-execute section of the code posted above. or maybe (ccEmail := new changeControl(itemName)).createCCEmail() - untested)
Ex:

Code: Select all

MsgBox, % ">" x := new test.Mthd()  ; Blank, (not "abc" as I previously said). Regardless, it should be 'x := new test', not 'x := new test.Mthd()'.
MsgBox, % ">" IsObject(x) "`n"      ; 1
        . ">" x.Mthd() "`n"         ; Blank
        . ">" x.Now                 ; Blank
        
MsgBox, % ">" x := new test         ; Blank because it's an object
MsgBox, % ">" IsObject(x) "`n"      ; 1
        . ">" x.Mthd() "`n"         ; "abc"
        . ">" x.Now                 ; OK
return

class test
{
    __New()
    {
        this.Now := A_Now
    }
    Mthd()
    {
        return "abc"
    }
}
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: ComObjConnect() help

16 Mar 2017, 19:12

Yeah I did see that and some on the IRC said not to chain methods. I work with JavaScript a lot and really like being able to chain class methods so it's something I was comfortable with and thought to copy. I know that typically requires returning 'this' from every method though.
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: ComObjConnect() help

17 Mar 2017, 06:38

Does it appear that I am properly cleaning things up? I am thinking that I need to create an __Delete() meta function that goes through and garbage collects any of my COM object references.
kon
Posts: 1756
Joined: 29 Sep 2013, 17:11

Re: ComObjConnect() help

20 Mar 2017, 18:30

Simply freeing the references to the COM objects is enough. And that should happen when you delete the object that contains the references.
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: ComObjConnect() help

20 Mar 2017, 18:34

Ok. Wasn't sure because I'd assume that my object is getting delete quickly as I instantiate the class inside a function and nothing in the class is halting the script as far as I can tell

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Joey5 and 226 guests