Inspector Wrapper Help

  • Thread starter Thread starter Kristy
  • Start date Start date
K

Kristy

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
 
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.
 
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.
 
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
 
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.
 
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
 
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.
 
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
 
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.
 
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
 
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.
 
Thank you :-)


Ken Slovak - said:
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.
 
Back
Top