MS Office Forum / Outlook / Programming Add-Ins / April 2004
Inspector Wrapper Help
|
|
Thread rating:  |
Kristy - 17 Mar 2004 23:37 GMT Hi
I am creating a Com Add-in for Outlook 2000 and XP. I have used the ItemsCB example from Microeye as the basis for my project.
I have a query regarding my Inspector wrapper that I am hoping someone will be able to help me with.
Basically I have 4 class modules ExplOutAddIn.cls, ExplWrap.cls, InspOutAddIn & InspWrap, and 2 normal modules modOutlInsp and modOutlExpl that do all the housekeeping for my project.
My issue is that when I send an email m_objInsp_close (the module responsible for destroying the Inspector object) is not called and therefore the sent item is not being removed from the Inspector collection at that time like I'd expect.
Eventually when Outlook is closed m_objExpl_close is called and from there I call gBaseClassExpl.UnInitHandlerExpl and gBaseClassInsp.UnInitHandlerInsp to ensure that everything is released from memory and Outlook closes properly but is this the correct way of doing it?
I hope that all makes sense... In summary the sent items are definitely part of the collection when created but they are not removed from the collection until you exit outlook. I would have thought that they should be removed on send in the same way that a closed item is? objMailtItem_close is called but not m_objInsp_close? Is there a really obvious mistake in my code that I'm missing?
This is basically what happens...
1. Open Outlook, explorer added to collection
Private Sub colExpl_NewExplorer(ByVal Explorer As Outlook.Explorer) On Error Resume Next gblnNewExpl = True AddExpl Explorer End Sub
2. Open new Mail Item, Inspector added to collection
Private Sub colInsp_NewInspector(ByVal Inspector As Outlook.Inspector) Dim objItem As Object Dim strID As String Set objInsp = Inspector 'declared WithEvents Set objItem = objInsp.CurrentItem Select Case objItem.Class Case olMail AddInsp Inspector Set objMailItem = objItem 'declared WithEvents Case Else End Select Set objItem = Nothing Set objInsp = Nothing End Sub
3a. Close Item without sending, in this scenario first objMailitem_close is called then m_objInsp_close is called which removes the object from the collection
Private Sub objMailItem_Close(Cancel As Boolean) On Error Resume Next If objOutlook.Explorers.Count = 0 And objOutlook.Inspectors.Count <= 1 Then UnInitHandlerInsp End If Set objMailItem = Nothing Set golApp = Nothing End Sub
Private Sub m_objInsp_Close() Dim objItem As Object On Error Resume Next modOutlInsp.KillInsp m_nID, Me 'MsgBox m_objInsp.Caption & "was just Closed" Set m_objInsp = Nothing Set objItem = Nothing End Sub
Public Sub KillInsp(anID As Integer, objInspWrap As InspWrap) Dim objInspWrap2 As InspWrap On Error Resume Next Set objInspWrap2 = gcolInspWrap.item(CStr(anID)) ' ensure removing the correct Inspector from the collection If Not objInspWrap2 Is objInspWrap Then Err.Raise 1, Description:="Unexpected Error in KillInsp" GoTo Cleanup End If gcolInspWrap.Remove CStr(anID) Cleanup: Set objInspWrap2 = Nothing End Sub
3b. Open Item is sent, under this scenario objMailItem_Send is called, then you get a objMailItem_close event and that's it - no m_objInsp_close.
Private Sub objMailItem_Send(Cancel As Boolean) 'Lots of stuff done here to sent item but it has no effect whether or not m_objInsp is called so I've left it out.
Finish: CustomField = "" Set objItem = Nothing Set MailItem = Nothing Set golApp = Nothing Set strFieldResult = Nothing End Sub
4. Close Outlook, when you eventually exit outlook m_objExpl is called and I am currently using this clean up explorer and inspector objects, it works in that it releases outlook from memory but is it sloppy?
Private Sub m_objExpl_Close()
'On Error Resume Next modOutlExpl.KillExpl m_nID, Me Set m_objExpl = Nothing If Outlook.Application.Explorers.Count <= 1 And Outlook.Application.Inspectors.Count = 0 Then gBaseClassInsp.UnInitHandlerInsp gBaseClassExpl.UnInitHandlerExpl End If End Sub
Public Sub KillExpl(anID As Integer, objExplWrap As ExplWrap) Dim objExplWrap2 As ExplWrap Set objExplWrap2 = gcolExplWrap.item(CStr(anID))
If Not objExplWrap2 Is objExplWrap Then Err.Raise 1, Description:="Unexpected Error in KillExpl" Exit Sub End If gcolExplWrap.Remove CStr(anID) End Sub
I hope that someone understands this and can advise.
Also, as you'll see from the code, I have kept all the Inspector and Explorer modules completely separate to make it easier for me to follow as I've worked through everything - should I merge them? Is this better practice for tidier projects or does it not matter?
Regards
Kristy
George Sugari - 18 Mar 2004 01:06 GMT Hi Kristy, Use MailItem_Close on the Inspector wrapper class
> My issue is that when I send an email m_objInsp_close (the module > responsible for destroying the Inspector object) is not called and > therefore the sent item is not being removed from the Inspector > collection at that time like I'd expect. and call
modOutlInsp.KillInsp m_nID, Me
to remove the Inspector from the collection. Outlook 2000 doesn't have Inspector_Close.
> 3a. Close Item without sending, in this scenario first > objMailitem_close is called then m_objInsp_close is called which [quoted text clipped - 10 lines] > Set golApp = Nothing > End Sub Ken Slovak - [MVP - Outlook] - 18 Mar 2004 14:47 GMT To add to what George said, make sure to declare a MailItem object at the module level of your class (and other item types if you want to support those): Private WithEvents m_objMail As Outlook.MailItem
Then in your init code for the class use a public property to set the mail item or in the Inspector setting property also set the mail item from Inspector.CurrentItem. That way you can handle m_objMail.Close and any other events you want to handle like Send or Forward. That let's you handle cases like you are seeing or when a user presses Next or Previous where you might not get a NewInspector event in some versions of Outlook.
 Signature Ken Slovak [MVP - Outlook] http://www.slovaktech.com Author: Absolute Beginner's Guide to Microsoft Office Outlook 2003 Reminder Manager, Extended Reminders, Attachment Options http://www.slovaktech.com/products.htm
> Hi Kristy, > Use MailItem_Close on the Inspector wrapper class [quoted text clipped - 25 lines] > > Set golApp = Nothing > > End Sub Kristy - 22 Mar 2004 01:12 GMT Thanks George and Ken for replying.
Sorry but I am still having trouble understanding where to put things to fix this problem.
If you have a minute could I be so bold as to ask...
George, would you possibly be able to give me some example code on how you would set up the MailItem_Close event? I really want an easy way to check if my Item is still part of the collection and if it is to remove it like happens in m_objInsp_close. I just can't seem to get it to work (maybe it's my declarations)?
Ken, also if you have a minute could you please check my code, I thought that I had it all set up as you suggest, and am now worried that I've not got it right and I may get the next/previous issue with Outlook 2000?
Sometimes I think I am not understanding all of this Inspector Wrapper stuff as well as I thought... back to the books, the posts and the sleepless nights!
I appreciate all of your help, Thanks
Kris
> To add to what George said, make sure to declare a MailItem object at the > module level of your class (and other item types if you want to support [quoted text clipped - 37 lines] > > > Set golApp = Nothing > > > End Sub Ken Slovak - [MVP - Outlook] - 22 Mar 2004 15:29 GMT I use code something like the following. This example only shows a MailItem declared WithEvents, to handle other item types you'd add additional declarations. In the code that adds the class to the wrapper collection you would check for the type of the item in the new Inspector (Inspector.CurrentItem) and add or not add the class based on that.
Private WithEvents m_objInsp As Outlook.Inspector Private WithEvents m_objMail As Outlook.MailItem
Private m_lngID As Long
Private Sub Class_Initialize() On Error Resume Next
Set m_objInsp = Nothing Set m_objMail = Nothing End Sub
Private Sub Class_Terminate() On Error Resume Next
Set m_objInsp = Nothing Set m_objMail = Nothing End Sub
Public Property Let Inspector(objInspector As Outlook.Inspector) On Error Resume Next
Set m_objInsp = objInspector Set m_objMail = objInspector.CurrentItem End Property
Public Property Get Inspector() As Outlook.Inspector On Error Resume Next
Set Inspector = m_objInsp End Property
Public Property Let Key(lngID As Long) On Error Resume Next
m_lngID = lngID End Property
Public Property Get Key() As Long On Error Resume Next
Key = m_lngID End Property
'********************************************************************* 'Event Procedure: m_objInsp_Close() 'Destroy Inspector object in InspWrap '********************************************************************* Private Sub m_objInsp_Close() On Error Resume Next
basOutlInsp.KillInsp m_lngID, Me Set m_objInsp = Nothing
If Err <> 0 Then AddInErr Err, "clsInspWrap", "m_objInsp_Close" End If End Sub
You can now add event handlers for any event available for a MailItem, including the Close event.
 Signature Ken Slovak [MVP - Outlook] http://www.slovaktech.com Author: Absolute Beginner's Guide to Microsoft Office Outlook 2003 Reminder Manager, Extended Reminders, Attachment Options http://www.slovaktech.com/products.htm
> Thanks George and Ken for replying. > [quoted text clipped - 21 lines] > > Kris Kristy - 23 Mar 2004 01:06 GMT Thanks again Ken, I just have a few more questions if that's OK.
Firstly, instead of using...
Public Property Let Inspector(objInspl As Outlook.Inspector) On Error Resume Next Set m_objInsp = objInspl Set m_objMail = objInspl.CurrentItem End Property
as in your sample, I have been using...
Public Property Let MailItem(objMail As Outlook.MailItem) On Error Resume Next Set m_objMail = objMail End Property
Public Property Let Inspector(objInspl As Outlook.Inspector) On Error Resume Next Set m_objInsp = objInspl End Property
Where objMail is set to Inspector.CurrentItem in colInsp_NewInspector(). Property Let MailItem(objMail As Outlook.MailItem) is called from the Public Function AddInsp(Inspector as Outlook.Inspector) which checks the type of item being added using...
Case olMail objInspWrap.MailItem = objItem Case Else
To me it looks like it does the same job, but can you please confirm that this is OK and will work in the same way?
Secondly, I've updated my m_objMail code to look like yours - thanks, and added the following to capture the close event as that event seems to fire on almost everything, most importantly for my problem, it fires on send.
Private Sub m_objMail_Close(Cancel As Boolean) On Error Resume Next
modOutlInsp.KillInsp m_nID, Me Set m_objMail = Nothing If Err <> 0 Then AddInErr Err End If End Sub
This takes care of having inspectors hanging around that have infact been sent and therefore should've been removed. Now though, m_objInsp_Close() seems to be redundant as m_objMail_Close (which always happens first) has already destroyed the inspector. The below is never called...
Private Sub m_objInsp_Close() On Error Resume Next modOutlInsp.KillInsp m_nID, Me Set m_objInsp = Nothing If Err <> 0 Then AddInErr Err End If End Sub
Since you included m_objInsp_Close() in your sample code, I am assuming that you still use it, whereas I seem to have replaced it? Have I misunderstood?
Also by using m_objMail_Close() in this manner am I likely to remove some items that I shouldn't be removing since this seems to fire for everything, including Next, Previous etc. I can't seem to think up any scenarios where this might cause a problem but I'm sure there are some given what I read so far with regards Next/Previous!
Finally, I use the following code in my InspOutAddInClass to capture events, colInsp_NewInspector(ByVal Inspector As Outlook.Inspector) sets objMailItem = objItem
Private Sub objMailItem_Open(Cancel As Boolean) On Error Resume Next Call CreateInspectorButtons(objMailItem) End Sub
Private Sub objMailItem_Close(Cancel As Boolean)
If objOutlook.Explorers.Count = 0 And objOutlook.Inspectors.Count <= 1 Then UnInitHandlerInsp End If
Set objMailItem = Nothing Set golApp = Nothing End Sub
Private Sub objMailItem_Send(Cancel As Boolean) On Error Resume Next
'does stuff... End Sub
Private Sub objMailItem_Reply(ByVal Response As Object, Cancel As Boolean) On Error Resume Next 'does stuff... End Sub Private Sub objMailItem_ReplyAll(ByVal Response As Object, Cancel As Boolean) On Error Resume Next 'does stuff... End Sub Private Sub objMailItem_Forward(ByVal Response As Object, Cancel As Boolean) On Error Resume Next 'does stuff... End Sub
Should I be using m_objMail_send, m_objMail_open, m_objMail_reply etc instead of objMailItem? I'm trying to figure out if this is more advantageous and a better way of setting up my inspectors... what way do you think is the best and why?
Sorry to ask so many questions, hopefully one day I'll be able to help others too.
Kristy
> I use code something like the following. This example only shows a MailItem > declared WithEvents, to handle other item types you'd add additional [quoted text clipped - 89 lines] > > > > Kris Ken Slovak - [MVP - Outlook] - 23 Mar 2004 16:12 GMT The way you're using is just fine, setting the module level mail item object in a separate procedure than the Inspector setting procedure. I usually use your method as a matter of fact.
I leave the Inspector Close event handler in my code, it does fire under certain circumstances.
What you could also look at is changing the code in your mail item close event handler to destroy the Inspector: Set oInspector = m_objMail.GetInspector oInspector.Close As the last lines in the event handler aside from setting any objects declared in that procedure to Nothing.
If I understand your last question I'd handle all events for the mail item in the wrapper class's event handlers rather than elsewhere. In that case I'd use the module level m_objMail object.
 Signature Ken Slovak [MVP - Outlook] http://www.slovaktech.com Author: Absolute Beginner's Guide to Microsoft Office Outlook 2003 Reminder Manager, Extended Reminders, Attachment Options http://www.slovaktech.com/products.htm
> Thanks again Ken, I just have a few more questions if that's OK. > [quoted text clipped - 124 lines] > > Kristy Kristy - 24 Mar 2004 23:58 GMT Hi Ken
I feel much more confident after all you great advise, thanks :-)
The only thing that I am having problems with is the following procedure since adding the new lines to destry the Inspector.
Basically if I use Next/Previous at all, when I come out of Outlook I get a "Program Error: Outlook.exe has generated errors and will be closed by windows. You will need to restart...". Since Next/Previous does funny things with Inspectors I'd imagine it has something to do with that, but shoudn't the On Error Resume Next statement take care of that? Do I have it set up correctly?
If I remove the 2 lines that re oInspector.Close then I get no error and Outlook closes as expected, however I can definitly see the benefit of ensuring that the Inspector is also closed properly? I am compiling this on 2000.
Private Sub m_objMail_Close(Cancel As Boolean) On Error Resume Next
modOutlInsp.KillInsp m_nID, Me If Err <> 0 Then AddInErr Err End If Set oInspector = m_objMail.GetInspector oInspector.Close
Set m_objMail = Nothing Set oInspector = Nothing
End Sub
Cheers
Kristy
> The way you're using is just fine, setting the module level mail item object > in a separate procedure than the Inspector setting procedure. I usually use [quoted text clipped - 142 lines] > > > > Kristy Ken Slovak - [MVP - Outlook] - 25 Mar 2004 16:52 GMT You could add a test to see if Inspector Is Nothing or precede that with Not to negate it before you close the Inspector. You can also add an Err.Clear statement at the end of that Sub.
I've found odd circumstances where the Err object doesn't go out of local scope when a Sub is ended and the calling procedure still has Err set to something when the procedure resumes. I'm not sure of all the reasons why that might be, I just sprinkle Err.Clear statements where needed and end my UnInitHandler procedure with an Err.Clear also.
See if that helps.
 Signature Ken Slovak [MVP - Outlook] http://www.slovaktech.com Author: Absolute Beginner's Guide to Microsoft Office Outlook 2003 Reminder Manager, Extended Reminders, Attachment Options http://www.slovaktech.com/products.htm
> Hi Ken > [quoted text clipped - 35 lines] > > Kristy Kristy - 30 Mar 2004 02:37 GMT Hi Ken
Thanks for providing even more useful info, the err.clear in the uninithandler is a great idea.
I changed my code to the below, but still have the same problem (ie, Program Error: Outlook.exe has generated errors...) if I used next/previous? If I leave the code out completley and the inspector isn't closed properly (under some circumstances I guess this could happen if m_objInsp isn't called) will this casue Outlook to stay in memory?
Private Sub m_objMail_Close(Cancel As Boolean) On Error Resume Next
modOutlInsp.KillInsp m_nID, Me If Err <> 0 Then AddInErr Err End If
Set oInspector = m_objMail.GetInspector If Not oInspector Is Nothing Then oInspector.Close End If
Set m_objMail = Nothing Set oInspector = Nothing
Err.Clear End Sub
Cheers
Kristy
> You could add a test to see if Inspector Is Nothing or precede that with Not > to negate it before you close the Inspector. You can also add an Err.Clear [quoted text clipped - 47 lines] > > > > Kristy Ken Slovak - [MVP - Outlook] - 30 Mar 2004 17:11 GMT With Outlook coding both belts and suspenders are recommended <g>
In the UnInitHandler code I always have a section that unwraps and nulls any of my wrapper collections. I always have a global Outlook.Application object in my code and I check that to make sure that if UnInitHandler is called more than once that errors aren't caused.
So, something like this in UnInitHandler:
If g_objOL Is Nothing Then Exit Sub End If
j = g_colInspectorWrap.Count For i = j To 1 Step -1 g_colInspectorWrap.Remove i Next Set g_colInspectorWrap= Nothing
And so on for every wrapper collection I used. Then I end the UnInitHandler code with Err.Clear.
 Signature Ken Slovak [MVP - Outlook] http://www.slovaktech.com Author: Absolute Beginner's Guide to Microsoft Office Outlook 2003 Reminder Manager, Extended Reminders, Attachment Options http://www.slovaktech.com/products.htm
> Hi Ken > [quoted text clipped - 32 lines] > > Kristy Kristy - 02 Apr 2004 00:39 GMT Thank you :-)
> With Outlook coding both belts and suspenders are recommended <g> > [quoted text clipped - 54 lines] > > > > Kristy
|
|
|