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? | | | | re: CDO, C# and file-locking problem.
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. | | | | re: CDO, C# and file-locking problem.
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.
[color=blue]
>-----Original Message-----
>Hi Matt,
>From your description, you try to use getmessages and[/color]
deleteAll to delete[color=blue]
>all the message in the drop directory.
>Am I right?
>
>As you guessed before, you need update after using[/color]
deleteall method.[color=blue]
>
>Here is the reason:
>Each time a message is opened and a property is viewed,[/color]
the message becomes[color=blue]
>locked and other application will recieve an error when[/color]
they attempt to[color=blue]
>open the message.
>
>I suggest you calling msg.datasource.save to update.[/color]
Please let me know if[color=blue]
>it works. Thanks!
>
>
>Rhett Gong[MS]
>Microsoft Online Partner Support
>
>This posting is provided "AS IS" with no warranties, and[/color]
confers no rights.[color=blue]
>Please reply to newsgroups only. Thanks.
>
>
>
>
>
>
>
>
>
>.
>[/color] | | | | re: CDO, C# and file-locking problem.
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" <name@micrsoft.com> or
' simply <name@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. | | | | re: CDO, C# and file-locking problem.
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
}
}
[color=blue]
>-----Original Message-----
>Hello Matt,
>
>I reviewed the complete thread and would like to jump in[/color]
to provide some[color=blue]
>information.
>
>Without your complete source code, I have not clear[/color]
understanding at this[color=blue]
>time. To keep us on the same page about the issue, I[/color]
assume the issue as[color=blue]
>follows. If I made any misunderstanding, please let me[/color]
know.[color=blue]
>
>====
>You initialize DropDirectory object to open drop[/color]
directory and call[color=blue]
>GetMessages and get reference to all messages collection[/color]
under the[color=blue]
>directory. After that, enumerate all messages under the[/color]
directory in the[color=blue]
>collection to deal with them one by one, such as rename,[/color]
move, delivery,[color=blue]
>reply or others. After that, you and then perform[/color]
deletion by call[color=blue]
>deleteall().
>
>At the time, you encournter the error: One or more[/color]
messages could not be[color=blue]
>deleted.
>===
>
>I wonder if all messages are closed before you call[/color]
deleteall()? For[color=blue]
>example, if you want to move or copy some messages into[/color]
another directory,[color=blue]
>you may open a file stream to open a message file. Did[/color]
you make sure you[color=blue]
>close it after completing operation?
>
>Here is simple VB sample to do similiar thing. I know[/color]
you are working with[color=blue]
>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[/color]
accounts[color=blue]
>
> 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"[/color]
<name@micrsoft.com> or[color=blue]
>
> ' simply <name@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[/color]
directory[color=blue]
>
> 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[/color]
complete repro sample. I[color=blue]
>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[/color]
confers no rights.[color=blue]
>
>.
>[/color] | | | | re: CDO, C# and file-locking problem.
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. |  | Similar .NET Framework bytes | | | /bytes/about
We are a network of experts and professionals in IT and software development that help one another with answers to tough questions and share insights.
Get the best answers to your questions from over 226,501 network members.
|