469,920 Members | 2,190 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,920 developers. It's quick & easy.

CDO, C# and file-locking problem.

I have created a windows service that reads emails from a
drop directory and moves them to the appropriate mail
folder every 15 seconds. I can move, rename and delete
the files as needed, up until the
CDO.DropDirectory.GetMessages() method is called. At
this point, the files are locked until I shut down the
service. After processing and delivery, I need to be
able to delete all the files in the drop directory.

I can delete them via Windows Explorer, but when using my
C# service and the DropDirectory.GetMessages().DeleteAll
() method I receive the error "One or more messages could
not be deleted" and none of the messages are deleted.
Attempts to use other methods to delete the files, such
as FileInfo objects, also result in no file deletion and
errors being thrown.

However, when I shut down the service, it does finally
clear out the folder. It seems like the CDO object only
marks the files for deletion and locks them until it can,
which seems to be only when the CDO object is permanently
destroyed. Is there any way to force the CDO object to
immediately DeleteAll() messages?
Nov 22 '05 #1
5 5209
Hi Matt,
From your description, you try to use getmessages and deleteAll to delete
all the message in the drop directory.
Am I right?

As you guessed before, you need update after using deleteall method.

Here is the reason:
Each time a message is opened and a property is viewed, the message becomes
locked and other application will recieve an error when they attempt to
open the message.

I suggest you calling msg.datasource.save to update. Please let me know if
it works. Thanks!
Rhett Gong[MS]
Microsoft Online Partner Support

This posting is provided "AS IS" with no warranties, and confers no rights.
Please reply to newsgroups only. Thanks.



Nov 22 '05 #2
That didn't work. When I tried to loop through all
messages after reading the properties (to deliver them) I
got an Access Denied message.

However, I finally did get it working by calling
GC.Collect() before I attempted to DeleteAll().

There seems to be no other way to explicitly release the
messages.

-----Original Message-----
Hi Matt,
From your description, you try to use getmessages and deleteAll to deleteall the message in the drop directory.
Am I right?

As you guessed before, you need update after using deleteall method.
Here is the reason:
Each time a message is opened and a property is viewed, the message becomeslocked and other application will recieve an error when they attempt toopen the message.

I suggest you calling msg.datasource.save to update. Please let me know ifit works. Thanks!
Rhett Gong[MS]
Microsoft Online Partner Support

This posting is provided "AS IS" with no warranties, and confers no rights.Please reply to newsgroups only. Thanks.



.

Nov 22 '05 #3
Hello Matt,

I reviewed the complete thread and would like to jump in to provide some
information.

Without your complete source code, I have not clear understanding at this
time. To keep us on the same page about the issue, I assume the issue as
follows. If I made any misunderstanding, please let me know.

====
You initialize DropDirectory object to open drop directory and call
GetMessages and get reference to all messages collection under the
directory. After that, enumerate all messages under the directory in the
collection to deal with them one by one, such as rename, move, delivery,
reply or others. After that, you and then perform deletion by call
deleteall().

At the time, you encournter the error: One or more messages could not be
deleted.
===

I wonder if all messages are closed before you call deleteall()? For
example, if you want to move or copy some messages into another directory,
you may open a file stream to open a message file. Did you make sure you
close it after completing operation?

Here is simple VB sample to do similiar thing. I know you are working with
C# and I just provided it for your reference.

Dim iDropDir as New CDO.DropDirectory

Dim iMsgs as CDO.IMessages

Dim iMsg as CDO.Message

Dim iStream as ADODB.Stream

Dim strTo as String

Dim ToRecipients as Variant

Dim strEmailName as String

Dim strAccountName as String

Dim strFileName as String

Dim strMailboxDir as String

Set iMsgs = iDropDir.GetMessages

For Each iMsg in iMsgs

strFileName = iMsgs.FileName(iMsg)

' trim to get the short file name

' from the full path

strFileName = Right(strFileName, Len(strFileName) -
InStrRev(strFileName,"\")
)

' Get the To recipients...and assume they are all local accounts

strTo = iMsg.To

ToRecipients = Split(strTo,",")

Dim j, lpos, rpos, posdiff

' loop through recipients and get account names for each

' Each address will be in either the "Name" <na**@micrsoft.com> or

' simply <na**@microsoft.com>

' We get the account name by getting the string

' between "<" and "@"

For j = LBound(ToRecipients) to UBound(ToRecipients)

strEmailName = ToRecipients(j)

lpos = InStr(strEmailName,"<")

rpos = InStr(strEmailName,"@")

posdiff = rpos - lpos - 1

strAccountName = Mid(strEmailName,lpos + 1, posdiff)

' For the purposes of this example,

' each account's mailbox directory resides in the

' directory c:\mailboxes. For user Joe, their account

' directory would be c:\mailboxes\joe

strMailboxDir = "c:\mailboxes\" & strAccountName

' Get the message stream

Set iStream = iMsg.GetStream

' write the stream to the user's mailbox directory

' the file name is the same as the one in the drop directory

iStream.SaveToFile strMailboxDir & "\" & strFileName

iStream.Close

Set iStream = Nothing

Next j

Next iMsg

' once we're done, delete the picked up messages

' this deletes the files from the file system as well.

iMsgs.DeleteAll

Set iMsgs = Nothing

Set iDropDir = Nothing
If my information doesn't help, please provide a complete repro sample. I
may investigate it by checking its souce code. Thank you!

Regards,
Justin Wan
Microsoft Partner Online Support

This posting is provided "AS IS" with no warranties, and confers no rights.

Nov 22 '05 #4
Everything is being closed. Also, I have tried
processing the mails, then not performing a DeleteAll()
until before the next mail processing 15 seconds later
and it still hangs up. It works as it is as long as I
include the GC.Collect() call.

Source code:
using System;
using System.Data;
using System.IO;
using System.Text;
using CDO;
using System.Configuration;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace MailService
{
/// <summary>
/// Contains methods used for copying e-mail
messages from a drop folder to
/// the appropriate recipients' inboxes.
/// </summary>
public class SmtpReceive:IDisposable
{
private String _dropDirectory =
@"C:\Inetpub\mailroot\Drop";
private String _tempDirectory =
@"C:\Inetpub\mailroot\Temp";
private String _badmailDirectory =
@"C:\Inetpub\mailroot\Badmail";
private String _mailboxDirectoryPrefix =
@"C:\Inetpub\mailroot\Mailbox\";
private String _mailboxDirectorySuffix =
@"\inbox";
private String _emailSuffix
= "@myDomain.net";
private EventLog log;
private String _machineName = "MYMACHINE";

// Pointer to an external unmanaged
resource.
private IntPtr handle;
// Track whether Dispose has been called.
private bool disposed = false;
/// <summary>
/// Default constructor.
/// </summary>
public SmtpReceive()
{
this.handle = handle;
log = new EventLog("Application",
_machineName, "MailService.exe:SMTPReceive");
}
/// <summary>
/// Checks to see if the specified path
exists; if not, create it.
/// </summary>
/// <param name="directoryPath">Path to
be validated/created.</param>
private void ValidateDirectory(String
directoryPath)
{
try
{
DirectoryInfo di = new
DirectoryInfo(directoryPath);
if(!di.Exists)

Directory.CreateDirectory(directoryPath);
}
catch(Exception ex)
{
log.WriteEntry("Error in
ValidateDirectory: " + ex.Message);
}
}
/// <summary>
/// Gets all the email addresses in the
drop directory and delivers them
/// to the recipients.
/// </summary>
public void Receive()
{
ValidateDirectory(_tempDirectory);
ValidateDirectory
(_badmailDirectory);
//move emails to a temp directory
so we know that no more will be added
//during processing:
FileMove(_dropDirectory,
_tempDirectory);
ProcessMails();
DeleteTempFiles();
}
private void ProcessMails()
{
CDO.DropDirectory drop = new
CDO.DropDirectory();
CDO.IMessages imsg;
imsg = drop.GetMessages
(_tempDirectory);
try
{
for(int i=1; i <=
imsg.Count; i++)
{
string fileName =
imsg.get_FileName(i);
string[]
toAddresses = null;
string[]
ccAddresses = null;
string[]
bccAddresses = null;
try
{
//get
email addresses to send to

toAddresses = imsg[i].To.Split(Convert.ToChar
(","));

ccAddresses = imsg[i].CC.Split(Convert.ToChar
(","));

bccAddresses = imsg[i].BCC.Split(Convert.ToChar
(","));
//get rid
of extra whitespace and other unneeded characters

FixArrayStrings(ref toAddresses);

FixArrayStrings(ref ccAddresses);

FixArrayStrings(ref bccAddresses);
}
catch
{
//bad
mail, drop in BadMail directory
string
badMailPath = _badmailDirectory + @"\" + Path.GetFileName
(fileName);
FileInfo
fi1 = new FileInfo(fileName);
FileInfo
fi2 = new FileInfo(badMailPath);
// Create
the file and clean up handles.
using
(FileStream fs = fi1.Create()) {}
//Ensure
that the target does not exist.
fi2.Delete
();
//Copy
the file.
fi1.CopyTo
(badMailPath,true);
}

//copy email file
to imail folders
//Send "To":
if(toAddresses !=
null)
{
foreach
(string s in toAddresses)
{

CopyMail(fileName, s);
}
}
if(ccAddresses !=
null)
{

//Send "CC":
foreach
(string s in ccAddresses)
{

CopyMail(fileName, s);
}
}
if(bccAddresses !
= null)
{

//Send "BCC":
foreach
(string s in bccAddresses)
{

CopyMail(fileName, s);
}
}
}
}
catch(Exception ex)
{
log.WriteEntry("Error in
ProcessMails: " + ex.Message);
}
finally
{
int iRefCount =
Marshal.ReleaseComObject( drop );
Marshal.ReleaseComObject
(imsg);
/*
* THIS IS THE CALL
TO GC.Collect() THAT SOLVES THE PROBLEM:
*/
GC.Collect();
}
}
/// <summary>
/// Delete temporary files after
processing
/// </summary>
private void DeleteTempFiles()
{
CDO.DropDirectory drop = new
CDO.DropDirectory();
try
{
drop.GetMessages
(_tempDirectory).DeleteAll();
}
catch(Exception ex)
{
log.WriteEntry("Error in
DeleteTempFiles: " + ex.Message);
}
finally
{
int iRefCount =
Marshal.ReleaseComObject( drop );
}
}
/// <summary>
/// Copy file from drop directory to
recipient's inbox
/// </summary>
/// <param name="fileName">Name of the
file to copy</param>
/// <param name="recipient">Email address
of the recipient</param>
private void CopyMail(string fileName,
string recipient)
{
try
{
string userID =
recipient.Substring(0, (recipient.Length -
_emailSuffix.Length));
string suffix =
recipient.Substring(recipient.Length -
_emailSuffix.Length);
if(suffix != _emailSuffix)
return;

string mailbox =
_mailboxDirectoryPrefix + userID +
_mailboxDirectorySuffix;
string inboxPath =
Path.Combine(mailbox, Path.GetFileName(fileName));
FileInfo fi1 = new
FileInfo(fileName);

//check to see if mailbox
exists, create if not
ValidateDirectory
(mailbox);
fi1.CopyTo(inboxPath,
true);
fi1 = null;
}
catch(Exception ex)
{
log.WriteEntry("Error in
CopyMail: " + ex.Message);
}
}
/// <summary>
/// Trims all whitespace from each string
in the array, converts all characters
/// to lowercase and extracts the e-mail
address from the field.
/// </summary>
/// <param name="arr">The array to be
cleansed.</param>
private void FixArrayStrings(ref string[]
arr)
{
try
{
if((arr.Length == 1) &&
(arr[0].Length == 0))
{
arr = null;
return;
}
for(int i = 0; i <
arr.Length; i++)
{
arr[i] = arr
[i].Trim();
arr[i] = arr
[i].ToLower();
arr[i] = arr
[i].Remove(0, arr[i].IndexOf("<") + 1);
arr[i] = arr
[i].Remove(arr[i].IndexOf(">"), arr[i].Length - arr
[i].IndexOf(">"));
}
}
catch(Exception ex)
{
log.WriteEntry("Error in
FixArrayStrings: " + ex.Message);
}
}
/// <summary>
/// Moves files from one directory to
another
/// </summary>
/// <param name="srcdir">The source
directory</param>
/// <param name="destdir">The destination
directory</param>
private void FileMove(string srcdir,
string destdir)
{
try
{
DirectoryInfo dir;
FileInfo[] files;
string tmppath;

//determine if the
destination directory exists, if not create it
ValidateDirectory
(destdir);

dir = new DirectoryInfo
(srcdir);

//if the source dir
doesn't exist, throw
if (! dir.Exists)
{
throw new
ArgumentException("source dir doesn't exist -> " +
srcdir);
}

//get all files in the
current dir
files = dir.GetFiles();

//loop through each file
foreach(FileInfo file in
files)
{
//create the path
to where this file should be in destdir

tmppath=Path.Combine(destdir,
file.Name);

//copy file to
dest dir
file.MoveTo
(tmppath);
}
//cleanup
files = null;
dir = null;
}
catch(Exception ex)
{
log.WriteEntry("Error in
FileMove: " + ex.Message);
}
}
#region IDisposable Members

/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);

}
private void Dispose(bool disposing)
{
// Check to see if Dispose has
already been called.
if(!this.disposed)
{
CloseHandle(handle);
handle =
IntPtr.Zero;
}
disposed = true;
}

[System.Runtime.InteropServices.DllImport
("Kernel32")]
private extern static Boolean CloseHandle
(IntPtr handle);

/// <summary>
/// Destructor
/// </summary>
~SmtpReceive()
{
Dispose(false);
}
#endregion
}
}
-----Original Message-----
Hello Matt,

I reviewed the complete thread and would like to jump in to provide someinformation.

Without your complete source code, I have not clear understanding at thistime. To keep us on the same page about the issue, I assume the issue asfollows. If I made any misunderstanding, please let me know.
====
You initialize DropDirectory object to open drop directory and callGetMessages and get reference to all messages collection under thedirectory. After that, enumerate all messages under the directory in thecollection to deal with them one by one, such as rename, move, delivery,reply or others. After that, you and then perform deletion by calldeleteall().

At the time, you encournter the error: One or more messages could not bedeleted.
===

I wonder if all messages are closed before you call deleteall()? Forexample, if you want to move or copy some messages into another directory,you may open a file stream to open a message file. Did you make sure youclose it after completing operation?

Here is simple VB sample to do similiar thing. I know you are working withC# and I just provided it for your reference.

Dim iDropDir as New CDO.DropDirectory

Dim iMsgs as CDO.IMessages

Dim iMsg as CDO.Message

Dim iStream as ADODB.Stream

Dim strTo as String

Dim ToRecipients as Variant

Dim strEmailName as String

Dim strAccountName as String

Dim strFileName as String

Dim strMailboxDir as String

Set iMsgs = iDropDir.GetMessages

For Each iMsg in iMsgs

strFileName = iMsgs.FileName(iMsg)

' trim to get the short file name

' from the full path

strFileName = Right(strFileName, Len(strFileName) -
InStrRev(strFileName,"\")
)

' Get the To recipients...and assume they are all local accounts
strTo = iMsg.To

ToRecipients = Split(strTo,",")

Dim j, lpos, rpos, posdiff

' loop through recipients and get account names for each

' Each address will be in either the "Name" <na**@micrsoft.com> or
' simply <na**@microsoft.com>

' We get the account name by getting the string

' between "<" and "@"

For j = LBound(ToRecipients) to UBound(ToRecipients)

strEmailName = ToRecipients(j)

lpos = InStr(strEmailName,"<")

rpos = InStr(strEmailName,"@")

posdiff = rpos - lpos - 1

strAccountName = Mid(strEmailName,lpos + 1, posdiff)

' For the purposes of this example,

' each account's mailbox directory resides in the

' directory c:\mailboxes. For user Joe, their account

' directory would be c:\mailboxes\joe

strMailboxDir = "c:\mailboxes\" & strAccountName

' Get the message stream

Set iStream = iMsg.GetStream

' write the stream to the user's mailbox directory

' the file name is the same as the one in the drop directory
iStream.SaveToFile strMailboxDir & "\" & strFileName

iStream.Close

Set iStream = Nothing

Next j

Next iMsg

' once we're done, delete the picked up messages

' this deletes the files from the file system as well.

iMsgs.DeleteAll

Set iMsgs = Nothing

Set iDropDir = Nothing
If my information doesn't help, please provide a complete repro sample. Imay investigate it by checking its souce code. Thank you!

Regards,
Justin Wan
Microsoft Partner Online Support

This posting is provided "AS IS" with no warranties, and confers no rights.
.

Nov 22 '05 #5
Hi Matt,

To make sure all files/messages are closed, please install process explorer
from http://www.sysinternals.com/ntw2k/fr.../procexp.shtml to check it.

Regards,
Justin Wan
Microsoft Partner Online Support

This posting is provided "AS IS" with no warranties, and confers no rights.

Nov 22 '05 #6

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

5 posts views Thread by simon place | last post: by
3 posts views Thread by Pernell Williams | last post: by
7 posts views Thread by Joseph | last post: by
3 posts views Thread by StGo | last post: by
3 posts views Thread by Shapper | last post: by
1 post views Thread by Mika M | last post: by
1 post views Thread by Waqarahmed | last post: by
reply views Thread by Salome Sato | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.