The timeless, never-ending question

S

seacuke

Hello,

I'm writing an add-in for Outlook 2000/2002/2003. I'm using Outlook
9 DLLs as my references and everything seems fine... except the
exiting.

On OLK2000, the Outlook process just sits there in the task manager
(more on this later), and in OLK2003 I get the dreaded "5-6 second
delay" mentioned in several other threads I've read.

The Add-in is written in C#.

In the other threads I've read on this topic I've seen references to
monitoring for Explorer.Close and Inspector.Close events. Here's what
I've been able to figure out on that front:

public void OnStartupComplete(ref System.Array custom)
{
...
activeExplorer = ApplicationObject.ActiveExplorer();
if (activeExplorer != null)
{
...
activeExplorer.Deactivate+=new
Outlook.ExplorerEvents_DeactivateEventHandler(activeExplorer_Deactivate);
...
}
}

The active explorer deactivation above is pretty straight forward,
and activeExplorer_Deactivate is being called, and the activeExplorer
seems to be being released. If I start Outlook and immediately exit
(without doing anything), the Outlook process is shut down properly.
However, if I move folders or create a new email message, or anything
other than immediately exit, the Outlook process hangs out.


What I can't figure out how to capture the close events for all of the
active inspectors. I'm capturing the inspectorClass_NewInspector event
and fooling with the new mail messages when they're created. The
Outlook.InspectorsClass does not have an event for any kind of
closedown.

In the inspectorClass_NewInspector method I have a reference to the new
Outlook.Inspector being created, but how do I make sure the right
inspector is being released on shutdown?


Or maybe if I phrase it differently, how do I monitor for
Explorer.Close and Inspector.Close events for each individual Explorer
and/or Inspector? Capturing the events is easy enough, but how do I
associate a particular event with a particular explorer/inspector?

And more importantly, is this going to solve the problem?!

Thanks in advance,
brian
 
K

Ken Slovak - [MVP - Outlook]

You need to use collections to store handles to each open Explorer and
Inspector. In the VB world we create wrapper classes for Inspectors and
Explorers that have WithEvents declarations for any events we want to
handle, both at the item and Explorer/Inspector level. Then when Close fires
we check the count of Inspectors and Explorers and if the last one is
closing we release all Outlook objects so On_Disconnection can fire. It
won't otherwise, due to a bug in the Outlook COM addin implementation.

In .NET code you also should call Marshall.ReleaseCOMObject on each object
after releasing it and then call the garbage collector so things don't just
hang waiting after the objects are released by your code.

I believe a C# implementation of an Inspector wrapper is up at
www.outlookcode.com that you can study and also modify to serve as an
Explorer wrapper.
 
S

seacuke

I've looked at a lot of the outlookcode.com stuff (and bought the
book), and if I'm guessing right, I think you're talking about the
XEventHandler stuff - where active mail messages (and their inspectors)
are tracked in a hash table in the add-in's connect module.

So if that is the basis for what I need to do, I guess the thing to do
would be to write a wrapper such as the XMailItem for the inspectors
and explorers, and when/if both of these hashtables are empty (checked
each time an XEvent is fired) then I presume Outlook is shutting down
and release all my COM handles?

I'm still trying to wrap my head around this, sorry if I'm a bit slow.
I bet this will work, because as noted above, if I open Outlook and
immediately close it, the add-in is correctly unloaded and Outlook
exits. Currently I am handling the the 'activeExplorer.close' event
and deleting the associated reference (Marshal.ReleaseCOMObjecting it)
and I bet it's the only explorer open if I'm not doing anything with
Outlook, and I further bet that's why it closes correctly.

Thanks for your response, and I'll be sure and post code up here if
this works.

-brian
 
K

Ken Slovak - [MVP - Outlook]

Unless the user adds a new Explorer explicitly (or you do using code) there
will be only 1 Explorer in the Explorers collection. Although I use Explorer
wrappers in every one of my COM addins I rarely see any situations where
there is more than 1 Explorer present.

The C# wrapper I was referring to is at
http://www.outlookcode.com/codedetail.aspx?id=797. I haven't studied it so I
don't know how the Inspector references are maintained, whether in a
collection or a hash table.

For VB 6 examples I can direct you to an Explorer wrapper in the ItemsCB COM
addin sample on the Resources page at www.microeye.com and for an Inspector
wrapper in VB 6 you could look at
http://www.slovaktech.com/code_samples.htm#InspectorWrapper.

You also need to be careful on how you handle the Inspector/Explorer Close
events. Different versions of Outlook instantiate ActiveExplorer and
ActiveInspector differently and in different orders related to Close. The VB
construct I use that seems to work with various versions of Outlook is to
assign my ActiveInspector and ActiveExplorer objects in the main code in the
NewInspector and NewExplorer events. Then in the main code as well as in the
wrapper classes I have code that looks like this:

Private Sub objExpl_Close() 'in main code where objExpl is declared
WithEvents
On Error Resume Next

'Current Explorer is closing--update identity to ActiveExplorer
Set objExpl = objOutlook.ActiveExplorer

'if this is the last Explorer, then objExpl = Nothing ->close down
If (objExpl Is Nothing) And (objOutlook.Inspectors.Count = 0) Then
UnInitHandler
End If
End Sub

Private Sub objInsp_Close()
On Error Resume Next

Set objInsp = Nothing

If objOutlook.Explorers.Count = 0 And objOutlook.Inspectors.Count <= 1
Then
UnInitHandler
End If

Err.Clear
End Sub

Then in the Explorer wrapper class's Explorer.Close event:

'if this is the last Explorer, then objExpl = Nothing ->close down
If (golApp.Explorers.Count <= 1) And (golApp.Inspectors.Count = 0) Then
gBaseClass.UnInitHandler
Set gBaseClass = Nothing
End If
 
S

seacuke

Upon researching this further, I think I've found out what is
happening, but I don't really understand why.

I put a logging method in my opening and closing methods - what appears
to happen now is that the ActiveExplorer.Deactivate method is called
when a new message is launched. Since ActiveExplorer.Deactivate is
captured by me as a closure event, I am removing the ActiveExplorer
from my hashtable. So when the inspector for the email is closed, my
inspector hash table is empty, and my explorer hash table is empty.
The COM objects are released and the add-in stops functioning. Also,
since there is still an ActiveExplorer object in memory, the Outlook
process never exits.

Here's a little piece of the log:

-----------------**************-----------------
activeExplorer registered. Counts: ActiveItems[1] activeMailItems[0]
newInspector hit. Counts: ActiveItems[1] activeMailItems[1]
outlook_itemclosed hit. Counts: ActiveItems[0] activeMailItems[1]
mailItem_closed hit. Counts: ActiveItems[0] activeMailItems[0]
closeComObjects hit. Counts: ActiveItems[0] activeMailItems[0]
-----------------**************-----------------

So the active explorer is added to my hash table.
The newInspector is added when the new message button is hit.
The outlook_itemclosed being logged next is when the
activeExplorer.deactivate is called.
mailItem_closed is when the mail message is closed.
closeComObjects is called when both lists are 0 length.

So the problem is that either a) I'm capturing the wrong event with
explorer.deactivate - more on this below, or b) I am not getting an
activate event when the activeExplorer is re-activated upon the closure
of the Inspector (mail message).


As for which events I'm capturing, the list of available events is
painfully short for the Outlook.Explorer object:

Outlook.Explorer.BeforeFolderSwitch;
Outlook.Explorer.BeforeViewSwitch;
Outlook.Explorer.Deactivate;
Outlook.Explorer.FolderSwitch;
Outlook.Explorer.SelectionChange;
Outlook.Explorer.ViewSwitch;

The Inspector only has one event I can capture:

activeInspector.Deactivate;


I've been assuming Deactivate was the event I want to capture to
maintain my collection/hashtable.


Perhaps I shouldn't have the ActiveExplorer in my hashtable of open
(and consequently identified and wrapped) explorer/inspector items.
But if I don't, how will I know when it closes?

I've got to be close, it seems like I'm teetering on the edge of
understanding this stuff, there's just one or two things that I need to
straighten out before I get it.

Thanks for all your help,
brian
 
S

seacuke

You know, in thinking about it, I am wondering how important it is that
I don't seem to have access to a close event in Outlook.Explorer or
Outlook.Inspector?
The ActiveExplorer.Deactivate event is sent any time the Outlook
application loses focus (either to something like a new email, or
clicking on another application or anything else to cause focus to be
lost on the primary Outlook window). This totally seems like the wrong
event to be monitoring for.

So now I'm guessing there's something to do with creating something to
handle the Outlook.ExplorerEvents_CloseEventHandler delegate?
 
K

Ken Slovak - [MVP - Outlook]

Deactivate is not the event you want. That can fire if any other window gets
focus, even if the Explorer/Inspector isn't closing.

I'm just hazarding a guess here since I don't use .NET code for Outlook COM
addins, but I recall something about a problem with events that overload a
method name and an event name. Close is one of those, you have both a Close
event and a Close method. Activate is the same. I believe you have to take
some special precautions in that case with the object you use to handle
events to cast it to something else that does events while the original
object handles the methods. Not very clear I know but it's not my area of
expertise.

There should be more details on this in the MSDN library and KnowledgeBase.
Randy Byrne might have written up something about this also in his .NET
Outlook addins material. You can check that at
http://www.microeye.com/resources/res_outlookvsnet.htm

I found this reference on the MicroEye page, I'm sure there are more:
http://support.microsoft.com/?kbid=315981
 
B

bluenoser

seacuke said:
You know, in thinking about it, I am wondering how important it is that
I don't seem to have access to a close event in Outlook.Explorer or
Outlook.Inspector?

For both Inspectors and Explorers, you need to cast them to InspectorClasses
and ExplorerClasses respectively in order to get at their Close events:

// inside your Connect class

((ExplorerClass)
applicationObject.ActiveExplorer()).ExplorerEvents_10_Event_Close += ...

// inside your wrapper class for the item/inspector

((InspectorClass) item.GetInspector).InspectorEvents_10_Event_Close += ...

Hope this helps (and is not too late),

-Aaron
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top