Closing open handles when using CDO.dll Unmanaged code

I accidently posted this in the Visual Basic General forum the first time... Opps...

I'm trying to write a simple .Net service that uses the CDO.dll to make a mapi connection to multiple Exchange 2k/2k3 mailboxes and count the number of items in the inbox and then write an event to the Application Event Log if the number of items is above a certain threshold defined in an ini file.

The service does what it is suppose to do however I have noticed that over time the size of the application seemed to grow and the number of open handles sky rocketed (4000+ open handles for this small app!!!). Using the Sys Internals Process Explorer I found that my service was keeping a bunch of open handles on objects that looked like this:

\BaseNamedObjects\MAPI-HP!851AA374141C128E

and some security tokens. So having said all that I believe that my application is not releasing the unmanaged resources from CDO.dll.

I've pasted my code for my service below. Please have a look at it and make any suggestions which you feel can help me out. I'm at my witts end and I've already lost enough hair over this so I thought I would post my problem on a forum.

Finally, if you have another way in which I can accomplish this task without using CDO.dll or unmanaged code I'm all ears.

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.IO;
using System.Reflection;
using MAPI;

namespace KVSMonitor
{
publicclass KVSService : System.ServiceProcess.ServiceBase
{
private System.Diagnostics.EventLog appEventLog;
private System.Timers.Timer timer1;
privatestring [] serverName;
privatestring [] profileName;
privateint [] threshold;
privateint counter;

private System.ComponentModel.Container components =null;

public
KVSService()
{
InitializeComponent();
}

staticvoid Main()
{
System.ServiceProcess.ServiceBase[] ServicesToRun;
ServicesToRun =
new System.ServiceProcess.ServiceBase[] {new KVSService() };
System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}

privatevoid InitializeComponent()
{
this.appEventLog =new System.Diagnostics.EventLog();
this.timer1 =new System.Timers.Timer();
((System.ComponentModel.ISupportInitialize)(
this.appEventLog)).BeginInit();
((System.ComponentModel.ISupportInitialize)(
this.timer1)).BeginInit();

this.appEventLog.Log = "Application";
this.appEventLog.Source = "KVS Monitor";
this.timer1.Enabled =false;
this.timer1.Interval = 5000;//600000;
this.timer1.Elapsed +=new System.Timers.ElapsedEventHandler(this.timer1_Elapsed);

this.ServiceName = "KVS Monitor";
((System.ComponentModel.ISupportInitialize)(
this.appEventLog)).EndInit();
((System.ComponentModel.ISupportInitialize)(
this.timer1)).EndInit();
}

protectedoverridevoid Dispose(bool disposing )
{
if( disposing )
{
if (components !=null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

protectedoverridevoid OnStart(string[] args)
{
string line =null;
string [] tmpString =new String[3];
int tmpInt = 0;
this.counter = 0;
FileStream fs =
new FileStream(string.Format("{0}\\KVS.ini", System.Windows.Forms.Application.StartupPath), FileMode.Open, FileAccess.Read);

StreamReader r =new StreamReader(fs);

while((line = r.ReadLine()) !=null)
{
this.counter++;
}

this.serverName =new String[this.counter];
this.profileName =new String[this.counter];
this.threshold =newint[this.counter];

fs.Seek(0, System.IO.SeekOrigin.Begin);

while((line = r.ReadLine()) !=null)
{
tmpString = line.Split('|');
this.serverName[tmpInt] = tmpString[0];
this.profileName[tmpInt] = tmpString[1];
this.threshold[tmpInt] = Convert.ToInt32(tmpString[2]);
tmpInt++;
}

fs.Flush();
fs.Close();

this.timer1.Enabled =true;
}

protectedoverridevoid OnStop()
{
this.timer1.Enabled =false;
this.serverName =null;
this.profileName =null;
this.threshold =null;
this.counter = 0;
this.Dispose(true);
}

privatevoid timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
MAPI._Session session =
new MAPI.SessionClass();
Object vEmpty = Missing.Value;

string appLogEntry = "The following mailboxes have exceeded the allowable number of items in the inbox:\n\n";

for(int i = 0; i < 1; i++)
{
session.Logon(vEmpty, vEmpty,
false,false, vEmpty, vEmpty,this.serverNameIdea + "\n" +this.profileNameIdea);
MAPI.Folder inboxFolder = (MAPI.Folder)session.Inbox;
MAPI.Messages messages = (MAPI.Messages)inboxFolder.Messages;

if ((int)messages.Count >this.thresholdIdea)
{
appLogEntry +=
string.Format("{0}, on {1} contains {2} items.\n",this.profileNameIdea ,this.serverNameIdea, (int)messages.Count);
}

messages =null;
inboxFolder =
null;

session.Logoff();
}

this
.appEventLog.WriteEntry(appLogEntry, EventLogEntryType.Error, 5);

session =null;

//this.Dispose(false);
}
}
}

[13537 byte] By [AReSGodOfWar] at [2007-12-16]
# 1
Hi,

First of all, check if this KB Article applies to you:
There is known issue of memory leak:
http://support.microsoft.com/default.aspx?scid=kb;en-us;326389

Setting the object to null wont help here. You need to release the unmanaged COM resource using a Marshal.ReleaseCOMObject()
Regards,
Vikram

Vikram at 2007-9-9 > top of Msdn Tech,Visual C#,Visual C# General...
# 2

Vikram,

Thanks a lot for your reply. I took a look at the KB Article however it does not apply to me. I've also tried using Marshal.ReleaseCOMObject() to release the unmanaged COM resource however I received the following exception, System.Runtime.InteropServices.InvalidComObjectException. Any ideas? When I added a reference to CDO.DLL into my project a new DLL was created called interop.mapi.dll, is this new DLL a wrapper created automatically by .Net? And would this affect the calls I've been making to the Marshal class? IE it sees the session variable as a managed object rather then an unmanaged object.

Finally does anyone have any good examples which uses a DLL in a similar fashion and then releases resources using Marshal?

Thanks again

AReSGodOfWar at 2007-9-9 > top of Msdn Tech,Visual C#,Visual C# General...