Outlook Send Event

N

Naamat

Hi,

I have written a C# Outlook addin that intercepts the Item Send event
calculates the size of any attachments and warns the user if the total size
is above a certain number. All this works fine when sending an email within
Outlook.

The problem is that management now wants the warning to be shown to users
when sending email using the following two scenarios:

1. In windows explorer when selecting "Zip and email<DocumentName>" from the
contect menu.
2. In any other office application when the user selects "Send To --> Mail
Recipient as Attachment"

Can this be done? How? Any pointers or help are greatly appreciated.

Thanks
 
P

Peter Huang [MSFT]

Hi,

Currently I am researching the issue and we will reply here with more
information as soon as possible.
If you have any more concerns on it, please feel free to post here.

Thanks for your understanding!

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
K

Ken Slovak - [MVP - Outlook]

You would have to trap Application.ItemSend for that to work. The problem is
you get no NewInspector event for any of the MAPI SendTo calls so you have
no way of knowing that those calls were made or any way to get a handle to
an event handler for Inspector.CurrentItem.Send.
 
P

Peter Huang [MSFT]

Hi

I agree with Ken's suggestion.

Here is some simple code snippet for your reference.

using Outlook = Microsoft.Office.Interop.Outlook;
using System.Diagnostics;

[GuidAttribute("26ABBF14-153F-43FA-BCF7-D290EB5BB589"),
ProgId("OLAddinItemSend.Connect")]
public class Connect : Object, Extensibility.IDTExtensibility2
{
public void OnConnection(object application,
Extensibility.ext_ConnectMode connectMode, object addInInst, ref
System.Array custom)
{
try
{
Trace.WriteLine("OnConnection");
applicationObject = (Outlook.Application)application;
applicationObject.ItemSend+=new
Microsoft.Office.Interop.Outlook.ApplicationEvents_11_ItemSendEventHandler(a
pplicationObject_ItemSend);
Trace.WriteLine("Add ItemSend Event Handler");
addInInstance = addInInst;
}
catch(Exception ex)
{
Trace.WriteLine(ex.ToString());
}
}
private Outlook.Application applicationObject;
private object addInInstance;

private void applicationObject_ItemSend(object Item, ref bool Cancel)
{
Trace.WriteLine("Event ItemSend traped.");
}

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
N

Naamat

Ken,
Thanks for your reply. I have already implemented an event handler for the
Application.ItemSend event. This works fine if Outlook is open when a user
sends an email from windows explorer of MS Word. However, if Outlook is
closed when the user takes such actions, the emails goes through without the
user getting the warning I have implemented in the ItemSend event handler. It
seems that the event does not fire if Outlook is not open ahead of time.

Any suggestions?

Thanks for your help.
 
K

Ken Slovak - [MVP - Outlook]

Again, that's a limitation of how MAPI SendTo is implemented.

If your code is implemented as a COM addin for Outlook you could instantiate
your addin and get a handle to the Outlook.Application object that way, and
then you'd be able to handle Application.ItemSend.

Of course then you have to deal with the problem of when and how to release
your Outlook objects so Outlook can close down, but that problem exists no
matter what.

Another possibility would be if you can deal with monitoring Win32 API calls
to see when a new window is created with a name of "rctrl_renwnd32", which
will only be there if Outlook is running with either an Explorer or an
Inspector. If you see that window being created you can instantiate your
code reference to Outlook.

The flip side won't work however, since that window isn't destroyed until
after your Outlook objects have been released, so you can't monitor for the
destruction of that window because it won't be destroyed until after your
objects are released.

One other thing, make sure to explicitly release all your objects and
explicitly call the garbage collector to your objects are truly released and
Outlook can shut down.
 
N

Naamat

Ken,

I am not sure what you mean by instantiate the addin. My code is an Outlook
addin and it obtains a handle to the Outlook application and handles the
Application.ItemSend event. Still this does not work.

I must be missing something:)

Your help is greatly appreciated.

Here is my code:

+++++++++++++++++++++++++++++++++++++++++
namespace Outlook_Events
{
using System;
//using System.Configuration;
using System.IO;
using Microsoft.Office.Core;
using Extensibility;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Outlook;
using System.Windows.Forms;
using System.Reflection;
using System.Diagnostics;




#region Read me for Add-in installation and setup information.
// When run, the Add-in wizard prepared the registry for the Add-in.
// At a later time, if the Add-in becomes unavailable for reasons such as:
// 1) You moved this project to a computer other than which is was
originally created on.
// 2) You chose 'Yes' when presented with a message asking if you wish to
remove the Add-in.
// 3) Registry corruption.
// you will need to re-register the Add-in by building the MyAddin21Setup
project
// by right clicking the project in the Solution Explorer, then choosing
install.
#endregion

/// <summary>
/// The object for implementing an Add-in.
/// </summary>
/// <seealso class='IDTExtensibility2' />
[GuidAttribute("1B6A385B-DF44-4DB7-9A36-1C30FA7FC512"),
ProgId("Outlook_Events.Connect")]
public class Connect : Object, Extensibility.IDTExtensibility2
{
/// <summary>
/// Implements the constructor for the Add-in object.
/// Place your initialization code within this method.
/// </summary>
public Connect()
{


}

/// <summary>
/// Implements the OnConnection method of the IDTExtensibility2
interface.
/// Receives notification that the Add-in is being loaded.
/// </summary>
/// <param term='application'>
/// Root object of the host application.
/// </param>
/// <param term='connectMode'>
/// Describes how the Add-in is being loaded.
/// </param>
/// <param term='addInInst'>
/// Object representing this Add-in.
/// </param>
/// <seealso class='IDTExtensibility2' />
public void OnConnection(object application, Extensibility.ext_ConnectMode
connectMode, object addInInst, ref System.Array custom)
{
try
{
applicationObject =
(Microsoft.Office.Interop.Outlook.Application)application;

//++++++++++++++++++++++++++++++++++++++++++++++++++
applicationObject.ItemSend +=new
ApplicationEvents_11_ItemSendEventHandler(applicationObject_ItemSend);
//++++++++++++++++++++++++++++++++++++++++++++++++++
addInInstance = addInInst;
if(connectMode != Extensibility.ext_ConnectMode.ext_cm_Startup)
{
OnStartupComplete(ref custom);
}

}
catch{}

}

/// <summary>
/// Implements the OnDisconnection method of the IDTExtensibility2
interface.
/// Receives notification that the Add-in is being unloaded.
/// </summary>
/// <param term='disconnectMode'>
/// Describes how the Add-in is being unloaded.
/// </param>
/// <param term='custom'>
/// Array of parameters that are host application specific.
/// </param>
/// <seealso class='IDTExtensibility2' />
public void OnDisconnection(Extensibility.ext_DisconnectMode
disconnectMode, ref System.Array custom)
{

if(disconnectMode != Extensibility.ext_DisconnectMode.ext_dm_HostShutdown)
{
OnBeginShutdown(ref custom);
}
applicationObject = null;
}

/// <summary>
/// Implements the OnAddInsUpdate method of the IDTExtensibility2
interface.
/// Receives notification that the collection of Add-ins has changed.
/// </summary>
/// <param term='custom'>
/// Array of parameters that are host application specific.
/// </param>
/// <seealso class='IDTExtensibility2' />
public void OnAddInsUpdate(ref System.Array custom)
{
}

/// <summary>
/// Implements the OnStartupComplete method of the IDTExtensibility2
interface.
/// Receives notification that the host application has completed
loading.
/// </summary>
/// <param term='custom'>
/// Array of parameters that are host application specific.
/// </param>
/// <seealso class='IDTExtensibility2' />
public void OnStartupComplete(ref System.Array custom)
{
//MessageBox.Show ("OnStartupComplete");
CommandBars oCommandBars;
CommandBar oStandardBar;

try
{
oCommandBars
= (CommandBars)applicationObject.GetType().InvokeMember("CommandBars",BindingFlags.GetProperty , null, applicationObject ,null);
}
catch(System.Exception)
{
object oActiveExplorer;
oActiveExplorer=
applicationObject.GetType().InvokeMember("ActiveExplorer",BindingFlags.GetProperty,null,applicationObject,null);
oCommandBars=
(CommandBars)oActiveExplorer.GetType().InvokeMember("CommandBars",BindingFlags.GetProperty,null,oActiveExplorer,null);
}
try
{
oStandardBar = oCommandBars["Standard"];
}
catch(System.Exception)
{
oStandardBar = oCommandBars["Database"];
}
try
{
MyButton=(CommandBarButton)oStandardBar.Controls["Reply to A&ll"];
}
catch(System.Exception)
{
}

try
{
MyButton.Click += new
Microsoft.Office.Core._CommandBarButtonEvents_ClickEventHandler(this.MyButton_Click);
oStandardBar = null;
oCommandBars = null;

colInspectors = (InspectorsClass) applicationObject.Inspectors;
colExplorers = (ExplorersClass) applicationObject.Explorers;
oExplorer = (ExplorerClass) applicationObject.ActiveExplorer();

colInspectors.NewInspector += new
InspectorsEvents_NewInspectorEventHandler(OnColInspectors_NewInspector);
oExplorer.SelectionChange += new
ExplorerEvents_10_SelectionChangeEventHandler(OnoExplorer_SelectionChange);

}
catch{}
}

private void MyButton_Click(CommandBarButton cmdBarbutton,ref bool cancel)
{

System.Windows.Forms.DialogResult retVal;
retVal = System.Windows.Forms.MessageBox.Show("Replying to all can cause
unnecessary email traffic – are you sure you want to reply to
everyone?","Warning!",System.Windows.Forms.MessageBoxButtons.YesNo,System.Windows.Forms.MessageBoxIcon.Warning);
if(retVal==System.Windows.Forms.DialogResult.Yes)
{
cancel=false;
}
else
{
cancel=true;
}

bReplyAllFlag = true;

}


/// <summary>
/// Implements the OnBeginShutdown method of the IDTExtensibility2
interface.
/// Receives notification that the host application is being unloaded.
/// </summary>
/// <param term='custom'>
/// Array of parameters that are host application specific.
/// </param>
/// <seealso class='IDTExtensibility2' />
public void OnBeginShutdown(ref System.Array custom)
{


try
{

Process[] oProcs = Process.GetProcessesByName("showform.ixos");

System.Collections.IEnumerator myEnumerator = oProcs.GetEnumerator();

while ( myEnumerator.MoveNext() )
{
((Process)myEnumerator.Current).Kill();
}

}
catch{}
}

public void
OnColInspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector
mInspector )

{
//MessageBox.Show("OnColInspectors_NewInspector");
try
{
if (mInspector.CurrentItem.GetType().Name=="MailItemClass")
{
oMailItem = (MailItemClass)mInspector.CurrentItem;
oMailItem.ItemEvents_Event_ReplyAll += new
ItemEvents_ReplyAllEventHandler(onMailItemReplyAll);
//oMailItem.ItemEvents_Event_Send += new
Microsoft.Office.Interop.Outlook.ItemEvents_SendEventHandler(onMailItemSend);
}
}
catch(System.Exception e)
{
//MessageBox.Show("OnColInspectors_NewInspector: " + e.Message);
}

}


public void onMailItemReplyAll(Object mResonse, ref bool Cancel)
{
try
{
if (!bReplyAllFlag)
{
System.Windows.Forms.DialogResult retVal;
retVal = System.Windows.Forms.MessageBox.Show("Replying to all can
cause unnecessary email traffic – are you sure you want to reply to
everyone?","Warning!",System.Windows.Forms.MessageBoxButtons.YesNo,System.Windows.Forms.MessageBoxIcon.Warning);
if(retVal==System.Windows.Forms.DialogResult.Yes)
{
Cancel=false;
}
else
{
Cancel=true;
}
}
}
catch{}
}


/*public void onMailItemSend( ref bool Cancel)
{


}*/

private void applicationObject_ItemSend(object Item, ref bool Cancel)
{
//MessageBox.Show( "applicationObject_ApplicationEvents_Event_ItemSend");
string ItemType;
System.Collections.IEnumerator myEnumerator=null;
try
{
ItemType = Item.GetType().Name;
switch (ItemType)
{
case "AppointmentItemClass":

myEnumerator =
((AppointmentItemClass)Item).Attachments.GetEnumerator();
break;

case "MeetingItemClass":

myEnumerator = ((MeetingItemClass)Item).Attachments.GetEnumerator();
break;
case "MailItemClass":


myEnumerator = ((MailItemClass)Item).Attachments.GetEnumerator();
break;

case "__ComObject":

oMailItem= (MailItemClass)oExplorer.Selection[1];
myEnumerator = oMailItem.Attachments.GetEnumerator();
break;
case "TaskItemClass":

myEnumerator = ((TaskItemClass)Item).Attachments.GetEnumerator();
break;

}


//AppointmentItem, MeetingItem, MailItem, or TaskItem
//System.Collections.IEnumerator myEnumerator =
oMailItem.Attachments.GetEnumerator();
/*(if (Item.GetType().Name!="MailItemClass")
{
return;
}*/



//System.Collections.IEnumerator myEnumerator =
((MailItemClass)Item).Attachments.GetEnumerator();
Attachment oAttachment;
string tmpPath= Environment.GetEnvironmentVariable("TEMP");
//MessageBox.Show ("tmpPath: " + tmpPath);
long totalSize = 0;
while ( myEnumerator.MoveNext() )
{

try
{
oAttachment= (Attachment)myEnumerator.Current;

oAttachment.SaveAsFile (tmpPath + @"\tmpAttachment.tmp");

FileInfo oFileInfo;
oFileInfo = new FileInfo(tmpPath + @"\tmpAttachment.tmp");

totalSize += oFileInfo.Length ;
oFileInfo.Delete();
}
catch(System.Exception e)
{
//MessageBox.Show(e.Message);
}
}

long maxAttSize;

#region Commented
//maxAttSize=
Convert.ToInt64(System.Configuration.ConfigurationSettings.AppSettings["maxAttachmentsSize"].ToString());
//(Inspector)applicationObject.ActiveWindow
//MessageBox.Show (applicationObject.ActiveInspector().Caption);
#endregion Commented

maxAttSize =512000;
if (totalSize > maxAttSize)
{
System.Windows.Forms.DialogResult retVal;


retVal = System.Windows.Forms.MessageBox.Show(new
WindowWrapper(GetForegroundWindow()) , "This message is over 500kb – if it’s
going to recipients who use dial up, it could inconvenience them. Are you
sure you want to send this
message?","Warning!",System.Windows.Forms.MessageBoxButtons.YesNo,System.Windows.Forms.MessageBoxIcon.Warning);

if(retVal==System.Windows.Forms.DialogResult.Yes)
{
Cancel=false;
}
else
{
Cancel=true;
}

}


}
catch (System.Exception e)
{
//MessageBox.Show("onMailItemSend: " + e.Message);
}


}

public void OnoExplorer_SelectionChange()
{
//MessageBox.Show( "OnoExplorer_SelectionChange");

#region Commented
/*try
{
if (!bReplyAllFlag)
{

oMailItem =
(MailItemClass)(applicationObject.ActiveExplorer().Selection[1]);
oMailItem.ItemEvents_Event_ReplyAll += new
ItemEvents_ReplyAllEventHandler(onMailItemReplyAll);
//oMailItem.ItemEvents_Event_Send += new
Microsoft.Office.Interop.Outlook.ItemEvents_SendEventHandler(onMailItemSend);
}
}
catch(System.Exception e)
{
//MessageBox.Show(e.Message);
}
*/
#endregion

bReplyAllFlag = false;
}

//private object applicationObject;
private Microsoft.Office.Interop.Outlook.Application applicationObject;
//private Microsoft.Office.Interop.Outlook.ApplicationClass oApp;
private object addInInstance;


//

private Microsoft.Office.Interop.Outlook.InspectorsClass colInspectors;
private Microsoft.Office.Interop.Outlook.ExplorersClass colExplorers;
//private Microsoft.Office.Interop.Outlook.InspectorClass oInspector;
private Microsoft.Office.Interop.Outlook.ExplorerClass oExplorer;
private Microsoft.Office.Interop.Outlook.MailItemClass oMailItem;
private CommandBarButton MyButton;
private bool bReplyAllFlag =false;

[ DllImport("user32.dll") ]
static extern IntPtr GetForegroundWindow();


}



public class WindowWrapper : System.Windows.Forms.IWin32Window
{
public WindowWrapper(IntPtr handle)
{
_hwnd = handle;
}

public IntPtr Handle
{
get { return _hwnd; }
}

private IntPtr _hwnd;
}


}



++++++++++++++++++++++++++++++++++++++++
 
P

Peter Huang [MSFT]

Hi,

Have you tried my code?
It works on my side even when the outlook is not started before.
Here is my reproduce steps.
1. make sure the outlook.exe is not in the taskmanager
2. open excel
3. File/SendTo/MailRecipient(As attachment)
4. Input recipient email address
5. click the send button

I find the ItemSend event handle will be traped.
Have you tried my code that will trace the addin load process?
You can use the tool below to trap the debug output.
DebugView
http://www.sysinternals.com/ntw2k/freeware/debugview.shtml

Here is sample debug output based on my test steps above.

//Excel process tried to find invoke the outlook
00000000 14:09:47 [5032] In DLLMain, DLL_PROCESS_ATTACH
00000001 14:09:47 [5032] In DllGetClassObject
00000002 14:09:47 [5032] CShellExtClassFactory::CShellExtClassFactory()
00000003 14:09:47 [5032] CShellExtClassFactory::QueryInterface()
00000004 14:09:47 [5032] CShellExtClassFactory::CreateInstance()
00000005 14:09:47 [5032] CShellExt::CShellExt()
00000006 14:09:47 [5032]
CShellExt::QueryInterface()==>IShellIconOverlayIdentifier
00000007 14:09:47 [5032] CShellExt::AddRef()
00000008 14:09:47 [5032] CShellExt::AddRef()
00000009 14:09:47 [5032] CShellExt::Release()
00000010 14:09:47 [5032]
CShellExt::QueryInterface()==>IShellIconOverlayIdentifier
00000011 14:09:47 [5032] CShellExt::AddRef()
00000012 14:09:47 [5032] CShellExt::Release()
00000013 14:09:47 [5032] CShellExt::GetOverlayInfo()
00000014 14:09:47 [5032] CShellExt::GetPriority()

//Outlook started and trying to load the addin
00000015 14:09:48 [2928] OnConnection
00000016 14:09:48 [2928] Add ItemSend Event Handler
00000017 14:09:48 [2928] OnStartupComplete
00000018 14:10:00 [2928] In DLLMain, DLL_PROCESS_ATTACH
00000019 14:10:00 [2928] In DllGetClassObject
00000020 14:10:00 [2928] CShellExtClassFactory::CShellExtClassFactory()
00000021 14:10:00 [2928] CShellExtClassFactory::QueryInterface()
00000022 14:10:00 [2928] CShellExtClassFactory::CreateInstance()
00000023 14:10:00 [2928] CShellExt::CShellExt()
00000024 14:10:00 [2928]
CShellExt::QueryInterface()==>IShellIconOverlayIdentifier
00000025 14:10:00 [2928] CShellExt::AddRef()
00000026 14:10:00 [2928] CShellExt::AddRef()
00000027 14:10:00 [2928] CShellExt::Release()
00000028 14:10:00 [2928]
CShellExt::QueryInterface()==>IShellIconOverlayIdentifier
00000029 14:10:00 [2928] CShellExt::AddRef()
00000030 14:10:00 [2928] CShellExt::Release()
00000031 14:10:00 [2928] CShellExt::GetOverlayInfo()
00000032 14:10:00 [2928] CShellExt::GetPriority()
00000033 14:10:10 [2928] Event ItemSend traped. //Note the ItemSend is
traped.
00000034 14:10:11 [2928] OnDisconnection
00000035 14:10:13 [5032] In DLLCanUnloadNow
00000036 14:10:13 [5032] In DLLMain, DLL_PROCESS_DETACH


Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
N

Naamat

This is a great tool!

You are right; I have traced my code and the event does get called. I now
know where the problem is. It is in the "__ComObject" case of the switch
statement.

when outlook is open ahead of time, there is an explorer so it works, but
when outlook is not opened ahead of time there is no explorer. Originalyy I
was not doing things this way(oMailItem=
(MailItemClass)oExplorer.Selection[1];
),instead was trying to cast the Object received in the event as a
MailItemClass but this was failing(oMailItem= (MailItemClass)Item;
)

<b>Any idea on how to get a MailItemClass object from the received object?</b>

Thanks a lot for your help

++++++++++++++++++++++++++++++
ItemType = Item.GetType().Name;
Trace.WriteLine("ItemType:" + ItemType);
switch (ItemType)
{
case "AppointmentItemClass":

myEnumerator =
((AppointmentItemClass)Item).Attachments.GetEnumerator();
break;

case "MeetingItemClass":

myEnumerator = ((MeetingItemClass)Item).Attachments.GetEnumerator();
break;
case "MailItemClass":


myEnumerator = ((MailItemClass)Item).Attachments.GetEnumerator();
break;

case "__ComObject":

//oMailItem= (MailItemClass)oExplorer.Selection[1];
oMailItem= (MailItemClass)Item;
myEnumerator = oMailItem.Attachments.GetEnumerator();
break;
case "TaskItemClass":

myEnumerator = ((TaskItemClass)Item).Attachments.GetEnumerator();
break;

}

++++++++++++++++++++++++++++


"Peter Huang" said:
Hi,

Have you tried my code?
It works on my side even when the outlook is not started before.
Here is my reproduce steps.
1. make sure the outlook.exe is not in the taskmanager
2. open excel
3. File/SendTo/MailRecipient(As attachment)
4. Input recipient email address
5. click the send button

I find the ItemSend event handle will be traped.
Have you tried my code that will trace the addin load process?
You can use the tool below to trap the debug output.
DebugView
http://www.sysinternals.com/ntw2k/freeware/debugview.shtml

Here is sample debug output based on my test steps above.

//Excel process tried to find invoke the outlook
00000000 14:09:47 [5032] In DLLMain, DLL_PROCESS_ATTACH
00000001 14:09:47 [5032] In DllGetClassObject
00000002 14:09:47 [5032] CShellExtClassFactory::CShellExtClassFactory()
00000003 14:09:47 [5032] CShellExtClassFactory::QueryInterface()
00000004 14:09:47 [5032] CShellExtClassFactory::CreateInstance()
00000005 14:09:47 [5032] CShellExt::CShellExt()
00000006 14:09:47 [5032]
CShellExt::QueryInterface()==>IShellIconOverlayIdentifier
00000007 14:09:47 [5032] CShellExt::AddRef()
00000008 14:09:47 [5032] CShellExt::AddRef()
00000009 14:09:47 [5032] CShellExt::Release()
00000010 14:09:47 [5032]
CShellExt::QueryInterface()==>IShellIconOverlayIdentifier
00000011 14:09:47 [5032] CShellExt::AddRef()
00000012 14:09:47 [5032] CShellExt::Release()
00000013 14:09:47 [5032] CShellExt::GetOverlayInfo()
00000014 14:09:47 [5032] CShellExt::GetPriority()

//Outlook started and trying to load the addin
00000015 14:09:48 [2928] OnConnection
00000016 14:09:48 [2928] Add ItemSend Event Handler
00000017 14:09:48 [2928] OnStartupComplete
00000018 14:10:00 [2928] In DLLMain, DLL_PROCESS_ATTACH
00000019 14:10:00 [2928] In DllGetClassObject
00000020 14:10:00 [2928] CShellExtClassFactory::CShellExtClassFactory()
00000021 14:10:00 [2928] CShellExtClassFactory::QueryInterface()
00000022 14:10:00 [2928] CShellExtClassFactory::CreateInstance()
00000023 14:10:00 [2928] CShellExt::CShellExt()
00000024 14:10:00 [2928]
CShellExt::QueryInterface()==>IShellIconOverlayIdentifier
00000025 14:10:00 [2928] CShellExt::AddRef()
00000026 14:10:00 [2928] CShellExt::AddRef()
00000027 14:10:00 [2928] CShellExt::Release()
00000028 14:10:00 [2928]
CShellExt::QueryInterface()==>IShellIconOverlayIdentifier
00000029 14:10:00 [2928] CShellExt::AddRef()
00000030 14:10:00 [2928] CShellExt::Release()
00000031 14:10:00 [2928] CShellExt::GetOverlayInfo()
00000032 14:10:00 [2928] CShellExt::GetPriority()
00000033 14:10:10 [2928] Event ItemSend traped. //Note the ItemSend is
traped.
00000034 14:10:11 [2928] OnDisconnection
00000035 14:10:13 [5032] In DLLCanUnloadNow
00000036 14:10:13 [5032] In DLLMain, DLL_PROCESS_DETACH


Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
R

Roberto Huezo

Hello Naamat,

Hi my name is Roberto and this sounds like the problem that I was having
with late binding of events for Office Add-Ins.
In the OnConnection method the line:
applicationObject =
(Microsoft.Office.Interop.Outlook.Application)application;

won't work because 'application' is a COM object and this casting won't do.
I needed to catch the SheetSelectionChange event from withing my Add-In so
in
'OnStartupComplete' I call my method 'ConnectEvents' which looks like this:


private void ConnectEvents()
{
try
{
if(isExcel)
{
if(m_Cookie!= 0)
return;
m_Excel = (Excel.Application)
System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");

UCOMIConnectionPointContainer oConnPointContainer =
(UCOMIConnectionPointContainer) m_Excel;

Guid m_Guid = typeof(Excel.AppEvents).GUID;
if(oConnPointContainer!=null)
{
oConnPointContainer.FindConnectionPoint(ref m_Guid, out
m_oConnectionPoint);
if(m_oConnectionPoint!=null)
m_oConnectionPoint.Advise(this, out m_Cookie);
else
m_oConnectionPoint = null;
}
}
}
catch(COMException ex)
{
Tracing.TraceException(ex);
}
catch(Exception ex)
{
Tracing.TraceException(ex);
}
}

Where m_Excel is my global Excel.Application variable, m_Cookie an int,
m_oConnectionPoint is UCOMIConnectionPoint. For your Outlook project you
have to get the Guid of Outlook.ApplicationEvents and not my
Excel.AppEvents! Once you call teh m_oConnectionPoint.Advise method, ALL of
the public events will fire when called by Outlook and you have to CATCH
them like this:

[DispId(xxxx)]

public void ItemSend(object oItem, bool Cancel)

{

string name = oItem.GetType().InvokeMember("Name",
BindingFlags.GetProperty, null, oItem, null);

}

User the Framework program ILDASM to find out which events are public and
which parameters you must include to be triggered correctly. The one thing
you have to find out is which DispId hast the ItemSend event ( xxxx) above.
Here is a PowerPoint example from MSDN:
http://support.microsoft.com/kb/308825/EN-US/

Good luck,



Roberto
 
K

Ken Slovak - [MVP - Outlook]

Sorry, I can't help with specifics for .NET code for Outlook, I don't code
in .NET for Outlook.




Naamat said:
Ken,

I am not sure what you mean by instantiate the addin. My code is an
Outlook
addin and it obtains a handle to the Outlook application and handles the
Application.ItemSend event. Still this does not work.

I must be missing something:)

Your help is greatly appreciated.

Here is my code:
<snip>
 
N

Naamat

I have resolved the issue by adding the following code to the ItemSend event:

Type itemType = Item.GetType();
itemTypeName = itemType.Name;

Attachments attachments;
attachments =
(Attachments)itemType.InvokeMember("Attachments",BindingFlags.GetProperty ,
null, Item ,null);

The only remaining issue is that when Outlook is not open when user attempts
to send email from Windows explorer, then the Outlook process does not close.

I have tried setting all Outlook objects to null including the application
object in the Addin's OnDisconnection handler, and I added also added call
to GC.Collect().

Still this did not work! Any Ideas?

Thanks for your great help
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Naamat said:
This is a great tool!

You are right; I have traced my code and the event does get called. I now
know where the problem is. It is in the "__ComObject" case of the switch
statement.

when outlook is open ahead of time, there is an explorer so it works, but
when outlook is not opened ahead of time there is no explorer. Originalyy I
was not doing things this way(oMailItem=
(MailItemClass)oExplorer.Selection[1];
),instead was trying to cast the Object received in the event as a
MailItemClass but this was failing(oMailItem= (MailItemClass)Item;
)

<b>Any idea on how to get a MailItemClass object from the received object?</b>

Thanks a lot for your help

++++++++++++++++++++++++++++++
ItemType = Item.GetType().Name;
Trace.WriteLine("ItemType:" + ItemType);
switch (ItemType)
{
case "AppointmentItemClass":

myEnumerator =
((AppointmentItemClass)Item).Attachments.GetEnumerator();
break;

case "MeetingItemClass":

myEnumerator = ((MeetingItemClass)Item).Attachments.GetEnumerator();
break;
case "MailItemClass":


myEnumerator = ((MailItemClass)Item).Attachments.GetEnumerator();
break;

case "__ComObject":

//oMailItem= (MailItemClass)oExplorer.Selection[1];
oMailItem= (MailItemClass)Item;
myEnumerator = oMailItem.Attachments.GetEnumerator();
break;
case "TaskItemClass":

myEnumerator = ((TaskItemClass)Item).Attachments.GetEnumerator();
break;

}

++++++++++++++++++++++++++++


"Peter Huang" said:
Hi,

Have you tried my code?
It works on my side even when the outlook is not started before.
Here is my reproduce steps.
1. make sure the outlook.exe is not in the taskmanager
2. open excel
3. File/SendTo/MailRecipient(As attachment)
4. Input recipient email address
5. click the send button

I find the ItemSend event handle will be traped.
Have you tried my code that will trace the addin load process?
You can use the tool below to trap the debug output.
DebugView
http://www.sysinternals.com/ntw2k/freeware/debugview.shtml

Here is sample debug output based on my test steps above.

//Excel process tried to find invoke the outlook
00000000 14:09:47 [5032] In DLLMain, DLL_PROCESS_ATTACH
00000001 14:09:47 [5032] In DllGetClassObject
00000002 14:09:47 [5032] CShellExtClassFactory::CShellExtClassFactory()
00000003 14:09:47 [5032] CShellExtClassFactory::QueryInterface()
00000004 14:09:47 [5032] CShellExtClassFactory::CreateInstance()
00000005 14:09:47 [5032] CShellExt::CShellExt()
00000006 14:09:47 [5032]
CShellExt::QueryInterface()==>IShellIconOverlayIdentifier
00000007 14:09:47 [5032] CShellExt::AddRef()
00000008 14:09:47 [5032] CShellExt::AddRef()
00000009 14:09:47 [5032] CShellExt::Release()
00000010 14:09:47 [5032]
CShellExt::QueryInterface()==>IShellIconOverlayIdentifier
00000011 14:09:47 [5032] CShellExt::AddRef()
00000012 14:09:47 [5032] CShellExt::Release()
00000013 14:09:47 [5032] CShellExt::GetOverlayInfo()
00000014 14:09:47 [5032] CShellExt::GetPriority()

//Outlook started and trying to load the addin
00000015 14:09:48 [2928] OnConnection
00000016 14:09:48 [2928] Add ItemSend Event Handler
00000017 14:09:48 [2928] OnStartupComplete
00000018 14:10:00 [2928] In DLLMain, DLL_PROCESS_ATTACH
00000019 14:10:00 [2928] In DllGetClassObject
00000020 14:10:00 [2928] CShellExtClassFactory::CShellExtClassFactory()
00000021 14:10:00 [2928] CShellExtClassFactory::QueryInterface()
00000022 14:10:00 [2928] CShellExtClassFactory::CreateInstance()
00000023 14:10:00 [2928] CShellExt::CShellExt()
00000024 14:10:00 [2928]
CShellExt::QueryInterface()==>IShellIconOverlayIdentifier
00000025 14:10:00 [2928] CShellExt::AddRef()
00000026 14:10:00 [2928] CShellExt::AddRef()
00000027 14:10:00 [2928] CShellExt::Release()
00000028 14:10:00 [2928]
CShellExt::QueryInterface()==>IShellIconOverlayIdentifier
00000029 14:10:00 [2928] CShellExt::AddRef()
00000030 14:10:00 [2928] CShellExt::Release()
00000031 14:10:00 [2928] CShellExt::GetOverlayInfo()
00000032 14:10:00 [2928] CShellExt::GetPriority()
00000033 14:10:10 [2928] Event ItemSend traped. //Note the ItemSend is
traped.
00000034 14:10:11 [2928] OnDisconnection
00000035 14:10:13 [5032] In DLLCanUnloadNow
00000036 14:10:13 [5032] In DLLMain, DLL_PROCESS_DETACH


Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
P

Peter Huang [MSFT]

Hi

I think you may try to release all the COM reference. in the
OnDisconnection event handler.
NOTE: Since I just add a applicationObject var to reference to the
Outlook.Application, so I just release the one.
If you have other reference, you need to make sure all the reference(e.g.
MailItem...) is released, so that the COM Object(Outlook.exe) will exit.
I think you may add the code complex one by one if you have trouble about
the outlook process exit.

public void OnDisconnection(Extensibility.ext_DisconnectMode
disconnectMode, ref System.Array custom)
{
Trace.WriteLine("OnDisconnection");
System.Runtime.InteropServices.Marshal.ReleaseComObject(applicationObject);
applicationObject =null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
K

Ken Slovak - [MVP - Outlook]

Note that if the disconnection mode is ext_dm_HostShutdown (user closes
Outlook) that On_Disconnection will not ever fire if any Outlook objects are
still instantiated. On_Disconnection will only fire in that case if the
disconnection mode is ext_dm_UserClosed (from COM AddIns dialog).

Typically if you have any Explorers you monitor for Explorer.Close:
Set objExplorer = objOutlook.ActiveExplorer
If (objExplorer Is Nothing) AND objOutlook.Inspectors.Count = 0 Then
'call release handler

However that doesn't work if only an Inspector is present from a SendTo call
and in that case you typically would not get an Inspector.Close event. A
real Catch-22 situation.

That's why most COM addins don't work with SendTo, we can't figure out when
to release our objects.

One suggestion, and I haven't tried this. If an Inspector is present with no
Explorers you might release your objects on the completion of ItemSend. It
might work...
 

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