Outlook incorrectly displaying Exchange public folder content

  • Thread starter Thread starter Godandag
  • Start date Start date
G

Godandag

Hi.

I am writing a C# (VSTO) Outlook 2003 addin, that stores some
references on
Microsoft.Office.Interop.Outlook.Items objects during all Outlook
session.
It is necessary to catch Items events (ItemChange, for example).

This work fine for pst, but not for Exchange public folders.

When I store 2 or more Items references (for different forlders),
Outlook becomes
incorrectly display all that folders content - when I remove Item from
folder, the
item is actually removed, but Outlook dislays it with empty properties
(blank
line in the grid). Items count in status bar is also wrong.
After Outlook restart all became correct.

The same problems is when I add new item.

So, how can I correctly catch Items evets for public folders?
 
I've never had to change my event handler code specifically for public
folders Items collections.

Show some of the code you're using.
 
Hello, Ken!

Thanks for responding.

Here is that code (some code is skipped for reduce size):


using System;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using Outlook = Microsoft.Office.Interop.Outlook;

namespace Test
{
public partial class ThisApplication
{
public static ThisApplication OlApp;

private void ThisApplication_Startup(object sender,
System.EventArgs e)
{
_folder1 = GetFolder(@"\\Public Folders\All Public
Folders\Folder1");
_folder2 = GetFolder(@"\\Public Folders\All Public
Folders\Folder2");

_fev1 = new FolderEventHandler(_folder1);
_fev2 = new FolderEventHandler(_folder2);
}

private void ThisApplication_Shutdown(object sender,
System.EventArgs e)
{
}

private Outlook.MAPIFolder _folder1, _folder2;
private FolderEventHandler _fev1, fev2;

public static Outlook.MAPIFolder GetFolder(string folderName)
{
Outlook.MAPIFolder result = null;
// result = ... Skipped
return result;
}


#region VSTO generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new
System.EventHandler(ThisApplication_Startup);
this.Shutdown += new
System.EventHandler(ThisApplication_Shutdown);
}

#endregion
}

class FolderEventHandler
{
public FolderEventHandler(Outlook.MAPIFolder folder)
{
_folderItems = folder.Items;
_folderItems.ItemChange += new
Outlook.ItemsEvents_ItemChangeEventHandler(_folderItems_ItemChange);
}

void _folderItems_ItemChange(object Item)
{
// ...
}

private Outlook.Items _folderItems;
}
}

"""Ken Slovak - [MVP - Outlook] ÐÉÓÁÌ(Á):
"""
 
I can't reproduce any problems like you described originally.

I took the code you supplied and put it in a VSTO 2005 Outlook 2003 project
and added my own code to get 2 public folders as MAPIFolder objects. I
marked items as read and unread in the public folders, deleted items from
there and added a couple of items. In all cases the ItemChange events fired,
as did ItemAdd and ItemRemove events that I added handlers for. The folder
display was OK, no blank grid rows or any other weird things.

I can't tell if the other code you have that wasn't shown does something or
what, but based on the code you supplied I had no problems.

Are you sure you're getting valid MAPIFolder references and Items
collections?
 
Sorry, Ken. I did not check that code carefully - it is works ÏË.
The problem depends on the order, in witch I call GetFolder and
folder.Items for different folders.

This is the new code (nothing is skipped). I had check it on 3
different comps.
It is to public folders (i had test on task folders) required:
\\Public Folders\All Public Folders\Test1\Folder1
\\Public Folders\All Public Folders\Test1\Folder2
To see the problem:
- create some items in both folders
- close Outlook, start Outlook (addin must be loaded)
- go to folder1 and switch to view without groupping and filtering!!!!!
- remove one item in folder1 - items count in status bar not changed!!!
- switch to folder2 and back to folder1 (for refresh folder1 content) -
blank line instead of deleted item!!!

The creating of items in folder1 is also wrong (they not displayed
until Outlooks restart).
All above only for views without groupping and filtering!!!!!

Client - Outlook 2003 SP2, VSTO 2005.
Server - Exchage 2003 on Win2003 SP1

This is the code:

using System;
using System.Windows.Forms;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace OutlookAddin3
{
public partial class ThisApplication
{
public static ThisApplication OlApp;

private void ThisApplication_Startup(object sender,
System.EventArgs e)
{
try
{
OlApp = (ThisApplication)sender;

string s = @"\\Public Folders\All Public
Folders\Test1";

Outlook.MAPIFolder folder1 = GetFolder(s +
@"\Folder1");
_fev1 = new FolderEventHandler(folder1);
Marshal.ReleaseComObject(folder1);

Outlook.MAPIFolder folder2 = GetFolder(s +
@"\Folder2");
_fev2 = new FolderEventHandler(folder2);
Marshal.ReleaseComObject(folder2);

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
catch (Exception ex)
{
MessageBox.Show(" OutlookAddin3:
ThisApplication_Startup error.\n" + ex.Message);
}
}

private void ThisApplication_Shutdown(object sender,
System.EventArgs e)
{
}

private Outlook.MAPIFolder _folder1, _folder2;
private FolderEventHandler _fev1, _fev2;

public static Outlook.MAPIFolder GetFolder(string folderName)
{
Outlook.Folders folders = null;
Outlook.MAPIFolder folder = null, subFolder = null;

Outlook.NameSpace ns = OlApp.GetNamespace("MAPI");

try
{
folders = ns.Folders;

string[] fld = folderName.Split(new char[] { '\\', '/'
}, StringSplitOptions.RemoveEmptyEntries);

for (int i = 0; i < fld.Length; i++)
{
NAR(folder);
folder = null;
folder = folders[fld];
NAR(folders);
folders = null;
folders = folder.Folders;
}

subFolder = folder;
folder = null;
}
finally
{
NAR(folder);
NAR(folders);
NAR(ns);
}

return subFolder;
}

public static void NAR(object o)
{
try
{
if (o != null)
Marshal.ReleaseComObject(o);
}
catch { }
}

#region VSTO generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new
System.EventHandler(ThisApplication_Startup);
this.Shutdown += new
System.EventHandler(ThisApplication_Shutdown);
}

#endregion
}

class FolderEventHandler
{
public FolderEventHandler(Outlook.MAPIFolder folder)
{
_folderItems = folder.Items;
_folderItems.ItemChange += new
Outlook.ItemsEvents_ItemChangeEventHandler(_folderItems_ItemChange);
}

void _folderItems_ItemChange(object Item)
{
Debug.Print("_folderItems_ItemChange");
}

private Outlook.Items _folderItems;
}
}




"""Ken Slovak - [MVP - Outlook] ÐÉÓÁÌ(Á):
"""
 
I'll see if I can get a chance to try out the code this afternoon.

Is there a specific reason why the code is getting the folders and setting
up event handlers for the folders using local variables and then releasing
them without setting them to null in the startup code?
 
I tested this on an Outlook 2003 SP2 system running a mailbox against
Exchange 2003. I used VSTO 2005.

Using your code I could replicate the problems you reported. I did change
the folder paths to use Tasks1 and Tasks2 as subfolders directly under All
Public Folders, just to simplify things on my public folders.

I then rewrote your code to not release the objects in the startup code,
moved some release code to shutdown and made a few other changes according
to what I felt were best practices. After the changes the problems went
away.

Change the folder paths back to yours and try out my code and see if that
solves your problems. Let us know.

using System;
using System.Windows.Forms;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace Test
{
public partial class ThisApplication
{
public static ThisApplication OlApp;

private void ThisApplication_Startup(object sender, System.EventArgs
e)
{

try
{
OlApp = (ThisApplication)sender;

string s = @"\\Public Folders\All Public Folders";

Outlook.MAPIFolder _folder1 = GetFolder(s + @"\Tasks1");
_fev1 = new FolderEventHandler(_folder1);
//Marshal.ReleaseComObject(folder1);

Outlook.MAPIFolder _folder2 = GetFolder(s + @"\Tasks2");
_fev2 = new FolderEventHandler(_folder2);
//Marshal.ReleaseComObject(folder2);

}
catch (Exception ex)
{
MessageBox.Show(" OutlookAddin3:ThisApplication_Startup
error.\n" + ex.Message);
}

}

private void ThisApplication_Shutdown(object sender,
System.EventArgs e)
{
_fev1.ReleaseHandler();
_fev1 = null;

_fev2.ReleaseHandler();
_fev2 = null;

_folder1 = null;
_folder2 = null;

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

private Outlook.MAPIFolder _folder1, _folder2;
private FolderEventHandler _fev1, _fev2;

public static Outlook.MAPIFolder GetFolder(string folderName)
{
Outlook.Folders folders = null;
Outlook.MAPIFolder folder = null, subFolder = null;

Outlook.NameSpace ns = OlApp.GetNamespace("MAPI");

try
{
folders = ns.Folders;

string[] fld = folderName.Split(new char[] { '\\', '/' },
StringSplitOptions.RemoveEmptyEntries);

for (int i = 0; i < fld.Length; i++)
{
NAR(folder);
folder = null;
folder = folders[fld];
NAR(folders);
folders = null;
folders = folder.Folders;
}

subFolder = folder;
folder = null;
}
finally
{
NAR(folder);
NAR(folders);
NAR(ns);
}

return subFolder;
}

public static void NAR(object o)
{
try
{
if (o != null)
Marshal.ReleaseComObject(o);
}
catch { }
}

#region VSTO generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new
System.EventHandler(ThisApplication_Startup);
this.Shutdown += new
System.EventHandler(ThisApplication_Shutdown);
}

#endregion
}

class FolderEventHandler
{
public FolderEventHandler(Outlook.MAPIFolder folder)
{
_folderItems = folder.Items;
_folderItems.ItemChange += new
Outlook.ItemsEvents_ItemChangeEventHandler(_folderItems_ItemChange);
}

void _folderItems_ItemChange(object Item)
{
Debug.Print("_folderItems_ItemChange");
}

private Outlook.Items _folderItems;

public void ReleaseHandler()
{
_folderItems.ItemChange -= new
Outlook.ItemsEvents_ItemChangeEventHandler(_folderItems_ItemChange);
_folderItems = null;
}
}

}





Godandag said:
Sorry, Ken. I did not check that code carefully - it is works ÏË.
The problem depends on the order, in witch I call GetFolder and
folder.Items for different folders.

This is the new code (nothing is skipped). I had check it on 3
different comps.
It is to public folders (i had test on task folders) required:
\\Public Folders\All Public Folders\Test1\Folder1
\\Public Folders\All Public Folders\Test1\Folder2
To see the problem:
- create some items in both folders
- close Outlook, start Outlook (addin must be loaded)
- go to folder1 and switch to view without groupping and filtering!!!!!
- remove one item in folder1 - items count in status bar not changed!!!
- switch to folder2 and back to folder1 (for refresh folder1 content) -
blank line instead of deleted item!!!

The creating of items in folder1 is also wrong (they not displayed
until Outlooks restart).
All above only for views without groupping and filtering!!!!!

Client - Outlook 2003 SP2, VSTO 2005.
Server - Exchage 2003 on Win2003 SP1

This is the code:
<snip>
 
No, Ken. The problem is still remain.
In accordance with my observation the minimal code to get error is:

Outlook.Items _items1 = null;
Outlook.Items _items2 = null;

private void ThisApplication_Startup(object sender, System.EventArgs e)
{
Outlook.MAPIFolder _folder1 = GetFolder(@"\\Public Folders\All
Public Folders\Task1");
_items1 = _folder1.Items;
Outlook.MAPIFolder _folder2 = GetFolder(@"\\Public Folders\All
Public Folders\Task2");
_items2 = _folder2.Items;
}

It is not necessarily to use exactly Items property, at the least
CurrentView
property result in a same situation.

BUT!!! If I rewrite code like this:

private void ThisApplication_Startup(object sender, System.EventArgs e)
{
Outlook.MAPIFolder _folder1 = GetFolder(@"\\Public Folders\All
Public Folders\Task1");
Outlook.MAPIFolder _folder2 = GetFolder(@"\\Public Folders\All
Public Folders\Task2");

_items1 = _folder1.Items;
_items2 = _folder2.Items;
}

All works OK!!!!
I improved my main application. Firstly I get the references to all
folders I use (it is not much,
thanks God and customer) and only after that I can safely use objects
(MAPIFolder) properties.

PS.
Sorry for my English:)


"""Ken Slovak - [MVP - Outlook] ÐÉÓÁÌ(Á):
"""
I tested this on an Outlook 2003 SP2 system running a mailbox against
Exchange 2003. I used VSTO 2005.

Using your code I could replicate the problems you reported. I did change
the folder paths to use Tasks1 and Tasks2 as subfolders directly under All
Public Folders, just to simplify things on my public folders.

I then rewrote your code to not release the objects in the startup code,
moved some release code to shutdown and made a few other changes according
to what I felt were best practices. After the changes the problems went
away.

Change the folder paths back to yours and try out my code and see if that
solves your problems. Let us know.

using System;
using System.Windows.Forms;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace Test
{
public partial class ThisApplication
{
public static ThisApplication OlApp;

private void ThisApplication_Startup(object sender, System.EventArgs
e)
{

try
{
OlApp = (ThisApplication)sender;

string s = @"\\Public Folders\All Public Folders";

Outlook.MAPIFolder _folder1 = GetFolder(s + @"\Tasks1");
_fev1 = new FolderEventHandler(_folder1);
//Marshal.ReleaseComObject(folder1);

Outlook.MAPIFolder _folder2 = GetFolder(s + @"\Tasks2");
_fev2 = new FolderEventHandler(_folder2);
//Marshal.ReleaseComObject(folder2);

}
catch (Exception ex)
{
MessageBox.Show(" OutlookAddin3:ThisApplication_Startup
error.\n" + ex.Message);
}

}

private void ThisApplication_Shutdown(object sender,
System.EventArgs e)
{
_fev1.ReleaseHandler();
_fev1 = null;

_fev2.ReleaseHandler();
_fev2 = null;

_folder1 = null;
_folder2 = null;

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

private Outlook.MAPIFolder _folder1, _folder2;
private FolderEventHandler _fev1, _fev2;

public static Outlook.MAPIFolder GetFolder(string folderName)
{
Outlook.Folders folders = null;
Outlook.MAPIFolder folder = null, subFolder = null;

Outlook.NameSpace ns = OlApp.GetNamespace("MAPI");

try
{
folders = ns.Folders;

string[] fld = folderName.Split(new char[] { '\\', '/' },
StringSplitOptions.RemoveEmptyEntries);

for (int i = 0; i < fld.Length; i++)
{
NAR(folder);
folder = null;
folder = folders[fld];
NAR(folders);
folders = null;
folders = folder.Folders;
}

subFolder = folder;
folder = null;
}
finally
{
NAR(folder);
NAR(folders);
NAR(ns);
}

return subFolder;
}

public static void NAR(object o)
{
try
{
if (o != null)
Marshal.ReleaseComObject(o);
}
catch { }
}

#region VSTO generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new
System.EventHandler(ThisApplication_Startup);
this.Shutdown += new
System.EventHandler(ThisApplication_Shutdown);
}

#endregion
}

class FolderEventHandler
{
public FolderEventHandler(Outlook.MAPIFolder folder)
{
_folderItems = folder.Items;
_folderItems.ItemChange += new
Outlook.ItemsEvents_ItemChangeEventHandler(_folderItems_ItemChange);
}

void _folderItems_ItemChange(object Item)
{
Debug.Print("_folderItems_ItemChange");
}

private Outlook.Items _folderItems;

public void ReleaseHandler()
{
_folderItems.ItemChange -= new
Outlook.ItemsEvents_ItemChangeEventHandler(_folderItems_ItemChange);
_folderItems = null;
}
}

}





Godandag said:
Sorry, Ken. I did not check that code carefully - it is works IE.
The problem depends on the order, in witch I call GetFolder and
folder.Items for different folders.

This is the new code (nothing is skipped). I had check it on 3
different comps.
It is to public folders (i had test on task folders) required:
\\Public Folders\All Public Folders\Test1\Folder1
\\Public Folders\All Public Folders\Test1\Folder2
To see the problem:
- create some items in both folders
- close Outlook, start Outlook (addin must be loaded)
- go to folder1 and switch to view without groupping and filtering!!!!!
- remove one item in folder1 - items count in status bar not changed!!!
- switch to folder2 and back to folder1 (for refresh folder1 content) -
blank line instead of deleted item!!!

The creating of items in folder1 is also wrong (they not displayed
until Outlooks restart).
All above only for views without groupping and filtering!!!!!

Client - Outlook 2003 SP2, VSTO 2005.
Server - Exchage 2003 on Win2003 SP1

This is the code:
<snip>
 
I'm glad you solved it.

In the code I showed I didn't set either object to null until shutdown. And
I used the class level objects rather than local objects so the references
wouldn't go out of scope. My tests indicated that the original objects you
were using were being garbage collected and therefore were out of scope and
not valid.
 
Back
Top