Toxter

Hello everyone,

While developing Outlook addin I and got one problem.

I'm intercepting Inspector.Close() event successfuly.

However, I'm unable to determine a pointer to Inspector window which is closing.

Here is what I tried:

void __stdcall Caddin:: OnClickCloseWindow()

{

CComQIPtr<_Inspector> spOneInspector;

applicationPtr->ActiveInspector(&spOneInspector);

}

This works fine when there is only one open window (e.g. new mail window).

But, for instance, if there are two windows, than ActiveInspector() method

gives me a pointer not to one which is closing, but to second one (which now becomes

active).

I've tried to cicle through Inspectors collection and somehow determine

which Inspector is closed, but unsuccessfuly.

So my question woud be, how to determine if Inspector window (from Inspector collection

is closed Which property will indicate it (I tried with get_Width(), expecting zero

because window is closed and has no width, but I got window width which was not zero)

Thanks.



Re: Visual Studio Tools for Office How to get closing Inspector pointer?

X4U

Hello toxter,

you have to write a small wrapper for the Inspector.

Everytime when a new Inspector was opened,

wrap it up and register for the inspector close event.

Intercept the Application.Inspectors.NewInspector event.

Only C# - hope this helps,

greets, Helmut

Code Snippet

Hashtable _WrappedInspectors;

Outlook.Inspectors _Inspectors;

/// <summary>

/// The new Inspector event occures whenever an outlook inspector was opened.

/// </summary>

/// <param name="Inspector">The outlook inspector object.</param>

void _Inspectors_NewInspector(Outlook.Inspector Inspector)

{

try

{

Outlook.Inspector inspector = Inspector;

WrapInspector(inspector);

}

catch (System.Exception ex)

{

// log the error always

Trace.TraceError("{0}: [class]:{1} [method]:{2}\n[message]:{3}\n[Stack]:\n{4}",

DateTime.Now, // when was the error happened

MethodInfo.GetCurrentMethod().DeclaringType.Name, // the class name

MethodInfo.GetCurrentMethod().Name, // the method name

ex.Message, // the error message

ex.StackTrace // the stack trace information

);

}

}

/// <summary>

/// This method wraps an Outlook Inspector object and registers for the InspectorClosed event.

/// </summary>

/// <param name="Inspector">The Outlook Inspector Object.</param>

void WrapInspector(Outlook.Inspector Inspector)

{

// Wrap the Explorer and do some usefull with it

InspectorWrapper wrappedInspector = new InspectorWrapper(Inspector);

// register for the closed event, so we can releas it from memory

wrappedInspector.InspectorClosed += new InspectorClosedDelegate(UnwrapInspector);

_WrappedInspectors.Add(wrappedInspector.ID, wrappedInspector);

}

/// <summary>

/// This method should be called when a wrapped Outlook Inspector has been closed.

/// </summary>

/// <param name="explorerId">The unique ID of the closed explorer.</param>

void UnwrapInspector(Guid explorerId)

{

// if we have a reference to this explorer we can release it now

if (_WrappedInspectors.ContainsKey(explorerId))

{

_WrappedInspectors.Remove(explorerId);

GC.Collect();

GC.WaitForPendingFinalizers();

}

}

#region delegates used handling explorers

/// <summary>

/// A delegate used to inform the application that an inspector has been closed.

/// </summary>

/// <param name="inspectorId">The unique ID of the Explorer that has been closed.</param>

internal delegate void InspectorClosedDelegate(Guid inspectorId);

#endregion

/// <summary>

/// The <b>InspectorWrapper</b> class is used to handle multiple open document windows within outlook.

/// </summary>

internal class InspectorWrapper

{

/// <summary>

/// This event informs our application that an "wrapped" inspector has been closed and should be released from memory.

/// </summary>

public event InspectorClosedDelegate InspectorClosed;

#region explorer wrapper variables & properties

/// <summary>

/// An unique ID that can used to identify this explorer (commandbars, buttons, etc...)

/// </summary>

Guid _Id;

/// <summary>

/// An unique ID that can used to identify this explorer (commandbars, buttons, etc...)

/// </summary>

public Guid ID

{

get { return _Id; }

}

/// <summary>

/// This variable holds a reference to the outlook iitem behind the inspector.

/// Used to handle to item.open event.

/// Only when the item has been displayed, it's save to manipulate the inspector - or close the inspector.

/// </summary>

object _Item;

/// <summary>

/// The outlook inspector object that is wrapped by this class.

/// </summary>

Outlook.InspectorClass _Inspector;

#endregion

/// <summary>

/// Initialisation code.

/// Remembers the inspector in memory and registers for the close event and the item.open event

/// </summary>

/// <param name="inspector">The outlook inspector object that should be handled.</param>

public InspectorWrapper(Outlook.Inspector inspector)

{

try

{

// each inspector gets a unique id.

_Id = Guid.NewGuid();

// remember the inspector object in memory

_Inspector = inspector as Outlook.InspectorClass;

// register for the close event - used to release ourself from memory

_Inspector.InspectorEvents_Event_Close += new Outlook.InspectorEvents_CloseEventHandler(_Inspector_InspectorEvents_Event_Close);

// remember the item behind the inspector in memory

_Item = _Inspector.CurrentItem;

// register for the item_open event

if (_Item is Outlook.ContactItem)

{

// we have no latebinding - so we have to cast explicitly to get events

Outlook.ContactItem item = _Item as Outlook.ContactItem;

item.Open += new Outlook.ItemEvents_10_OpenEventHandler(item_Open);

// remember the object in memory

_Item = item;

}

else if (_Item is Outlook.TaskItem)

{

Outlook.TaskItem item = _Item as Outlook.TaskItem;

item.Open += new Outlook.ItemEvents_10_OpenEventHandler(item_Open);

_Item = item;

}

else if (_Item is Outlook.JournalItem)

{

Outlook.JournalItem item = _Item as Outlook.JournalItem;

item.Open += new Outlook.ItemEvents_10_OpenEventHandler(item_Open);

_Item = item;

}

else if (_Item is Outlook.MailItem)

{

Outlook.MailItem item = _Item as Outlook.MailItem;

item.Open += new Outlook.ItemEvents_10_OpenEventHandler(item_Open);

_Item = item;

}

else

{

// we don't handle this inspector and release it from memory

_Item = null;

}

}

catch (System.Exception ex)

{

// log the error always

Trace.TraceError("{0}: [class]:{1} [method]:{2}\n[message]:{3}\n[Stack]:\n{4}",

DateTime.Now, // when was the error happened

MethodInfo.GetCurrentMethod().DeclaringType.Name, // the class name

MethodInfo.GetCurrentMethod().Name, // the method name

ex.Message, // the error message

ex.StackTrace // the stack trace information

);

// the wrapper is called only from application - throw the message to it.

throw new Exception("There was an application error, the new Window could not be handled.\nyou should save your work and restart Outlook.",ex);

}

}

/// <summary>

/// Handles the item open event and checks if this item a OutlooAdminTools item.

/// When we have a new item - and we are in an OAT folder - we close this item and create a new OAT item.

/// </summary>

/// <param name="Cancel"></param>

void item_Open(ref bool Cancel)

{

try

{

Type itemType = _Item.GetType();

// check the messageclass of the new opened inspector.

string messageclass = (string)itemType.InvokeMember("MessageClass", BindingFlags.GetProperty, null, _Item, null);

// access the entryId - so we can get the parent folder object

string entryId = (string)itemType.InvokeMember("EntryID", BindingFlags.GetProperty, null, _Item, null);

// after we have accessed the entryID - we can access the parentfolder property

Outlook.MAPIFolder parent = itemType.InvokeMember("Parent", BindingFlags.GetProperty, null, _Item, null) as Outlook.MAPIFolder;

string folderName = parent.Description;

int itemSize = (int)itemType.InvokeMember("Size", BindingFlags.GetProperty, null, _Item, null);

}

catch (System.Exception ex)

{

// log the error always

Trace.TraceError("{0}: [class]:{1} [method]:{2}\n[message]:{3}\n[Stack]:\n{4}",

DateTime.Now, // when was the error happened

MethodInfo.GetCurrentMethod().DeclaringType.Name, // the class name

MethodInfo.GetCurrentMethod().Name, // the method name

ex.Message, // the error message

ex.StackTrace // the stack trace information

);

// the wrapper is called anytime from application.

// display a messagebox directly to the user

MessageBox.Show("There was an application error, a new Window could not be handled,\nyou should save your work and restart Outlook.",

"OutlookAdminTools",

MessageBoxButtons.OK,

MessageBoxIcon.Error);

}

}

/// <summary>

/// This event occures when the user has closed the open inspector.

/// Used to release ourself from memory and avoid memory leaks.

/// </summary>

void _Inspector_InspectorEvents_Event_Close()

{

try

{

_Item = null;

_Inspector.InspectorEvents_Event_Close -= new Outlook.InspectorEvents_CloseEventHandler(_Inspector_InspectorEvents_Event_Close);

_Inspector = null;

if (InspectorClosed != null)

{

InspectorClosed(_Id);

}

}

catch (System.Exception ex)

{

// log the error always

Trace.TraceError("{0}: [class]:{1} [method]:{2}\n[message]:{3}\n[Stack]:\n{4}",

DateTime.Now, // when was the error happened

MethodInfo.GetCurrentMethod().DeclaringType.Name, // the class name

MethodInfo.GetCurrentMethod().Name, // the method name

ex.Message, // the error message

ex.StackTrace // the stack trace information

);

// the wrapper is called anytime from application.

// display a messagebox directly to the user

MessageBox.Show("There was an application error, a Window close could not be handled,\nyou should save your work and restart Outlook.",

"OutlookAdminTools",

MessageBoxButtons.OK,

MessageBoxIcon.Error);

}

}

}






Re: Visual Studio Tools for Office How to get closing Inspector pointer?

Toxter

Hello Helmut,

You have been very helpful to me these days, and I'm very grateful.

I'm not very experienced with C#, so if it is not to much of a trouble,

could you point in this last code, where are you getting Inspector reference

which is currently closing (I'm a bit lost in delegates Smile )

Thank you for your time.





Re: Visual Studio Tools for Office How to get closing Inspector pointer?

X4U

Hello Toxter,

They Key thing is:

Have a Collection in your Connect class wich holds a reference to the active inspectors.

Hashtable _WrappedInspectors;

When you get a NewInspector event, wrap that Inspector in a class, that internally holds a reference to the inspector and intercepts the Inspector close event.

WrapInspector(inspector);

// Wrap the Inspector and do some usefull with it

InspectorWrapper wrappedInspector = new InspectorWrapper(Inspector);

// register for the closed event, so we can release it from memory

wrappedInspector.InspectorClosed += new InspectorClosedDelegate(UnwrapInspector);

_WrappedInspectors.Add(wrappedInspector.ID, wrappedInspector);

For every Inspector a unique ID is created to identify the inspector.

This class also exposes a close event (thats what the delegates are used for - only a method signature for the close event.

Remember this wrapped object in your Collection.

public InspectorWrapper(Outlook.Inspector inspector)

{

// each inspector gets a unique id.

_Id = Guid.NewGuid();

// remember the inspector object in memory

_Inspector = inspector as Outlook.InspectorClass;

// register for the close event - used to release ourself from memory - intercept the close event

_Inspector.InspectorEvents_Event_Close += new Outlook.InspectorEvents_CloseEventHandler(_Inspector_InspectorEvents_Event_Close);

when you get the close event, internally release all references to the Inspector object and raise our own close event.

void _Inspector_InspectorEvents_Event_Close()

{

_Item = null;

_Inspector.InspectorEvents_Event_Close -= new Outlook.InspectorEvents_CloseEventHandler(_Inspector_InspectorEvents_Event_Close);

_Inspector = null;

// Raise our own close event here

In the Connect Class register for the close event - and remove the wrapper object from the Collection.

// if we have a reference to this inspector we can release it now

if (_WrappedInspectors.ContainsKey(inspectorId))

{

_WrappedInspectors.Remove(inspectorId);

That's it.

Hope that helps,

greets, Helmut






Re: Visual Studio Tools for Office How to get closing Inspector pointer?

Toxter

Hello Helmut,

Again, your post was very precise and clear. I really appreciate it.

This was very helpful to me.

What I can say now is only that I hope that I could return a favor to you some time

in the future.

P.S. I see that you do addin development in C#. So IĄŻm curious:

Is development much easier using that programming language (guess client

has to have .NET framework installed on his computer before installing addin), or maybe you are

experienced in C# so you decided to do development of addinĄŻs in C# too

(IĄŻve been developing some addins for Office apps in C++ and it can be a bit pain sometimes)

Thanks again!




Re: Visual Studio Tools for Office How to get closing Inspector pointer?

Joel Stevick

Hello,

Would it be safe to simply access the active inspector through the add-in using the following

Outlook.Inspector inspector = Globals.MyAddin.Application.ActiveInspector

Joel




Re: Visual Studio Tools for Office How to get closing Inspector pointer?

X4U

Hello Joel,

yes, if you just want to get some Information of the current active Inspector,

but no - if you want to monitor events or the state of an Inspector and therefore not for monitoring an Inspector Close event.

As soon as you click on another Inspector window - this propery changes and so you loose all references to the previous.

So the answer is yes and no - it depends on your requirements.

Greets, Helmut