Catching "Move to Folder" events in Outlook

G

g62msdnatll

PLATFORM/DEVENV: VS .NET C# Com Add-In for Outlook 2003 for MAPI account.

PROBLEM: I need to detect when the user selects multiple mail items and
drags the items to another folder ("Move to Folder" via drag-and-drop UI). I
have a wrapper for the Folders and Items objects, so I already get Item_Add
and Item_Remove events. However, the problem with the UI drag-and-drop for
mail items on a MAPI server is:
1) Item_Add does not fire until the user switches to the folder
2) Item_Remove does not fire until the user hits "Purge Deleted" for the
items that were moved.

I think this is because the items are only "marked" for deletion or move
until further action. There _must_ be a way to detect this. Any ideas how?

Any help would be greatly appreciated.
Thanks!
 
P

Peter Huang [MSFT]

Hi

I can not reproduce the problem.
Here is my test steps.
1. Load Addin
2. Change to Inbox folder (Exchange MAPI box NOT the private local file)
3. drag one item from the Inbox to Drafts
4. The Item_Add and Item_Remove is fired.
You may have a try.

namespace NewInspectorAddin
{
using System;
using Microsoft.Office.Core;
using Extensibility;
using System.Runtime.InteropServices;
using Outlook = Microsoft.Office.Interop.Outlook;
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("90335B0C-26D6-452D-A657-4E19D9044F4D"),
ProgId("NewInspectorAddin.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)
{
Debug.WriteLine("OnConnection");
olApp = application as Outlook.Application;
Ips = olApp.Inspectors as Outlook.Inspectors;
Ips.NewInspector+=new
Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(I
nspectors_NewInspector);
srcItems =
olApp.GetNamespace("MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFolde
rInbox).Items as Outlook.Items;
desItems =
olApp.GetNamespace("MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFolde
rDrafts).Items as Outlook.Items;
srcItems.ItemRemove +=new
Microsoft.Office.Interop.Outlook.ItemsEvents_ItemRemoveEventHandler(srcItems
_ItemRemove);
desItems.ItemAdd +=new
Microsoft.Office.Interop.Outlook.ItemsEvents_ItemAddEventHandler(desItems_It
emAdd);
}

/// <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(srcItems !=null)
{
Marshal.ReleaseComObject(srcItems);
srcItems = null;
}
if(desItems !=null)
{
Marshal.ReleaseComObject(desItems);
desItems = null;
}
if(mi !=null)
{
Marshal.ReleaseComObject(mi);
mi = null;
}
if(Ips !=null)
{
Marshal.ReleaseComObject(Ips);
Ips = null;
}
if(olApp !=null)
{
Marshal.ReleaseComObject(olApp);
olApp = null;
}

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}

/// <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)
{
}

/// <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)
{
}
private Outlook.Application olApp=null;
private Outlook.Inspectors Ips = null;
private Outlook.MailItem mi=null;
private Outlook.Items srcItems=null;
private Outlook.Items desItems=null;
private void
Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector
Inspector)
{
mi = Inspector.CurrentItem as Outlook.MailItem;
if(mi ==null)
Debug.WriteLine("MailItem is null");
else
{
Debug.WriteLine(mi.To);
}
}

private void srcItems_ItemRemove()
{
Debug.WriteLine("srcItems_ItemRemove");
}

private void desItems_ItemAdd(object Item)
{
Debug.WriteLine("desItems_ItemAdd");
}
}
}

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.
 
G

g62msdnatll

For your repro steps, if you change step 3 to:
3. Drag one item from MAPI Inbox to another MAPI folder or subfolder

then neither Item_Add nor Item_Remove are fired for me. Can you repro that?

Also,for your repro steps with dragging item from MAPI Inbox to Drafts
folder, I do not get an Item_Remove event (the item is not really removed
until Purge Del). I am getting Item_Remove events on the MAPI Inbox Items
collection so I don't think I've implemented the wrapper incorrectly,
although I will take another look at it.

thank you for your help!

"Peter Huang" said:
Hi

I can not reproduce the problem.
Here is my test steps.
1. Load Addin
2. Change to Inbox folder (Exchange MAPI box NOT the private local file)
3. drag one item from the Inbox to Drafts
4. The Item_Add and Item_Remove is fired.
You may have a try.

namespace NewInspectorAddin
{
using System;
using Microsoft.Office.Core;
using Extensibility;
using System.Runtime.InteropServices;
using Outlook = Microsoft.Office.Interop.Outlook;
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("90335B0C-26D6-452D-A657-4E19D9044F4D"),
ProgId("NewInspectorAddin.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)
{
Debug.WriteLine("OnConnection");
olApp = application as Outlook.Application;
Ips = olApp.Inspectors as Outlook.Inspectors;
Ips.NewInspector+=new
Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(I
nspectors_NewInspector);
srcItems =
olApp.GetNamespace("MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFolde
rInbox).Items as Outlook.Items;
desItems =
olApp.GetNamespace("MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFolde
rDrafts).Items as Outlook.Items;
srcItems.ItemRemove +=new
Microsoft.Office.Interop.Outlook.ItemsEvents_ItemRemoveEventHandler(srcItems
_ItemRemove);
desItems.ItemAdd +=new
Microsoft.Office.Interop.Outlook.ItemsEvents_ItemAddEventHandler(desItems_It
emAdd);
}

/// <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(srcItems !=null)
{
Marshal.ReleaseComObject(srcItems);
srcItems = null;
}
if(desItems !=null)
{
Marshal.ReleaseComObject(desItems);
desItems = null;
}
if(mi !=null)
{
Marshal.ReleaseComObject(mi);
mi = null;
}
if(Ips !=null)
{
Marshal.ReleaseComObject(Ips);
Ips = null;
}
if(olApp !=null)
{
Marshal.ReleaseComObject(olApp);
olApp = null;
}

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}

/// <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)
{
}

/// <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)
{
}
private Outlook.Application olApp=null;
private Outlook.Inspectors Ips = null;
private Outlook.MailItem mi=null;
private Outlook.Items srcItems=null;
private Outlook.Items desItems=null;
private void
Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector
Inspector)
{
mi = Inspector.CurrentItem as Outlook.MailItem;
if(mi ==null)
Debug.WriteLine("MailItem is null");
else
{
Debug.WriteLine(mi.To);
}
}

private void srcItems_ItemRemove()
{
Debug.WriteLine("srcItems_ItemRemove");
}

private void desItems_ItemAdd(object Item)
{
Debug.WriteLine("desItems_ItemAdd");
}
}
}

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

It is strange that I still can not reproduce the problem.
1. Move from Inbox to JunkMail box
public void OnConnection(object application,
Extensibility.ext_ConnectMode connectMode, object addInInst, ref
System.Array custom)
{
Debug.WriteLine("OnConnection");
olApp = application as Outlook.Application;
Ips = olApp.Inspectors as Outlook.Inspectors;
Ips.NewInspector+=new
Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(I
nspectors_NewInspector);
srcItems =
olApp.GetNamespace("MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFolde
rInbox).Items as Outlook.Items;
desItems =
olApp.GetNamespace("MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFolde
rJunk).Items as Outlook.Items;
srcItems.ItemRemove +=new
Microsoft.Office.Interop.Outlook.ItemsEvents_ItemRemoveEventHandler(srcItems
_ItemRemove);
desItems.ItemAdd +=new
Microsoft.Office.Interop.Outlook.ItemsEvents_ItemAddEventHandler(desItems_It
emAdd);
}
2. Move from Inbox to My Created TestFolder.
public void OnConnection(object application,
Extensibility.ext_ConnectMode connectMode, object addInInst, ref
System.Array custom)
{
Debug.WriteLine("OnConnection");
olApp = application as Outlook.Application;
Ips = olApp.Inspectors as Outlook.Inspectors;
Ips.NewInspector+=new
Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(I
nspectors_NewInspector);
srcItems =
olApp.GetNamespace("MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFolde
rInbox).Items as Outlook.Items;
foreach(Outlook.MAPIFolder mf in
(olApp.GetNamespace("MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFold
erInbox).Parent as Outlook.MAPIFolder).Folders)
{
Debug.WriteLine(mf.Name);
if (mf.Name == "TestFolder")
{
desItems = mf.Items as Outlook.Items;
}
}
srcItems.ItemRemove +=new
Microsoft.Office.Interop.Outlook.ItemsEvents_ItemRemoveEventHandler(srcItems
_ItemRemove);
desItems.ItemAdd +=new
Microsoft.Office.Interop.Outlook.ItemsEvents_ItemAddEventHandler(desItems_It
emAdd);
}

Here is the debug output log.(DBGVIEW tool at sysinternals)

00000000 11:35:33 [4284] OnConnection
00000001 11:35:33 [4284] Deleted Items
00000002 11:35:33 [4284] Inbox
00000003 11:35:33 [4284] Outbox
00000004 11:35:33 [4284] Sent Items
00000005 11:35:33 [4284] Calendar
00000006 11:35:33 [4284] Contacts
00000007 11:35:33 [4284] Drafts
00000008 11:35:33 [4284] Journal
00000009 11:35:33 [4284] Junk E-mail
00000010 11:35:33 [4284] Notes
00000011 11:35:33 [4284] Sync Issues
00000012 11:35:33 [4284] Tasks
00000013 11:35:33 [4284] TestFolder //This is my new created test folder.
When I drag three mailitems from inbox to here one by one, the event fired.
00000014 11:35:42 [4284] desItems_ItemAdd
00000015 11:35:42 [4284] srcItems_ItemRemove
00000016 11:35:51 [4284] desItems_ItemAdd
00000017 11:35:51 [4284] srcItems_ItemRemove
00000018 11:35:58 [4284] desItems_ItemAdd
00000019 11:35:58 [4284] srcItems_ItemRemove


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.
 
G

g62msdnatll

Hi Peter,

I believe the main difference between our scenarios is in the following line:
foreach(Outlook.MAPIFolder mf in
(olApp.GetNamespace("MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFold
erInbox).Parent as Outlook.MAPIFolder).Folders)

When I run this code and print out the folders, I get the set of "Personal
Folders", the following:

Log,Comment,Inbox
Log,Comment,Outbox
Log,Comment,Sent Items
Log,Comment,Calendar
Log,Comment,Contacts
Log,Comment,Journal
Log,Comment,Notes
Log,Comment,Tasks
Log,Comment,Drafts
Log,Comment,Junk E-mail
Log,Comment,Deleted Items

The problem is, the folder I am trying to monitor is a mail folder on a
server. Not any of these _default folders_. I agree with you that I can
repro the correct behavior of item_add and item_remove firing for the default
folders. What I cannot repro is the same behavior on my mail server's
folders.

Another way to explain it is that my olApp.Session.Folders returns "Personal
Folders" and "llpop", where llpop is my server folder. The folders inside
"Personal Folders" are the default folders, and the folders inside llpop are
my "Inbox" (not the same Inbox as the one in default folders), and my
subfolders.

Does that help to clarify the issue?

Thanks for your continued help through this problem.
 
P

Peter Huang [MSFT]

Hi

I am sorry if I did not make clear.
Based on my knowledge, the GetDefaultFolder returned the folders on my
Exchange Server of my account.
As you said, olApp.Session.Folders will return the root folder of our
outlook.
The root folder maybe the Exchange Server folder or the Private Folder
which is a file on our machine.
The two folders has the same structure.

Since I create the Folder "TestFolder" on my Exchange Server but not the
Private Folder which is a file.
The code below will print out the TestFolder refer to my last post. That
should mean the TestFolder is of the same level of the
"Outlook.OlDefaultFolders.olFolderInbox".
Since the TestFolder I created is on the Server side, so I think the
Outlook.OlDefaultFolders.olFolderInbox is the inbox on our exchange server.
foreach(Outlook.MAPIFolder mf in
(olApp.GetNamespace("MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFold
erInbox).Parent as Outlook.MAPIFolder).Folders)

You may have a try to see if I have any misunderstanding.

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.
 
G

g62msdnatll

Hi Peter,
The thing is, I am not working the an Exchange server. Our mail servers are
SMTP. I'm sorry for not being clear, because I don't understand the
infrastructure and differences between the two very much.
I have been told that there are some differences and problems if we are not
working with Exchange.
-Lisa
 
P

Peter Huang [MSFT]

Hi

Thanks for your clarification.
Based on my knowledge, usually outlook worked with two type of server.
Exchange server via MAPI interface and the internet SMTP server via
POP3(receive mail,port 110), SMTP(send mail, port 25)

While if we receive the mail via POP3, the mail will be downloaded into
local pst file(just as the Personal Folders which is a pst file on our
machine).
So what is your mail server, is the llpop a pst file or something else?
If your mail server implement its own protocol, the behavior may be not
expected by outlook.

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.
 

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