# StmpClient Problem in C#



## Kreij (Mar 12, 2010)

I create a file on disk from my app, and then I want to send the file via e-mail.
This works fine by creating an SmtpClient and using the Send() method.

When the send completed I want to delete the file as there is no reason to save it any longer.


```
.... Code for creating file
SmtpClient _Client = new SmtpClient();
.... code for client configuration

try
{
    _Client.Send(myFile);
}
catch()
{
     ... handle an exception
}

System.IO.File.Delete(myFile);
```

The Send() method is supposed to block until finished, but this throws a "File in use by another process" exception on the Delete method.

So I then tried running the whole thing in a BackgroundWorker and then doing the file delete in the RunWorkerCompleted event. This too throws the same error.

It's seems that SmtpClient process still has a handle attached to the thread, but I'm not sure why it is not letting it go when it's done with the send.

Any ideas?


_Oops .. title was supposed to be SmtpClient not StmpClient._


----------



## FordGT90Concept (Mar 12, 2010)

I would make a second thread and public List<string>.  The second worker thread just loops every five seconds or so and attempts to delete the files in the List<string> collection.  If it succeeds, remove it from the list, if it fails, keep it in the list until next time.

I would only use this approach if the file is in a temp dir where it is a copy and won't be missed.


If that doesn't work either, instead of deleting using File.Delete, launch cmd.exe with the del [path] command.  That worked in situations where File.Delete wouldn't.


----------



## FordGT90Concept (Mar 12, 2010)

Oh, if Send causes the thread to pause until complete, you shouldn't need the second thread.  You might just need to have the shell delete the file instead of your app.

I know I used cmd to copy a file that another application had a write stream open on.  .NET would refuse to touch it but cmd didn't complain.  It copied the file and then I could open a read stream on the copy and delete the copy once I was done.


I guess the question is: does the SMTP Client ever release the file (at least within reason)?


----------



## regexorcist (Mar 12, 2010)

Maybe this could be of some use?

http://connect.microsoft.com/VisualStudio/feedback/details/146711/smtpclient-does-not-gracefully-close-the-underlying-tcp-ip-connection

There are interesting comments and a few work a rounds.


----------



## FordGT90Concept (Mar 12, 2010)

According to that, it won't be fixed until .NET 4.0 (Visual Studio 2010) which isn't officially out yet.


----------



## Kreij (Mar 13, 2010)

That seems to be a different issue (not gracefully releasing the connection using QUIT).
I don't know how long SmtpClient keeps a handle on the file, and the rather frustrating part is that there is no good way in .Net to check for "file in use."

It seems that many people simply try to open a stream to the file and if it throws an exception because it is in use, retry until it succeeds, and then do whatever they want to do with the file.

This is a rather "expensive" way of checking to see if a file is in use.
You can also get a list of process handles, but you have to leave the realm of managed code to do it and this too seems like overkill. It's kind of like stopping at every house in the neighborhood to see if your friend lives there when you have his address in your pocket. 

I can code around this by deleting the files at a different point in time, like looking for leftover files from a previous module run when the module starts, but that just doesn''t sit too well with my "clean up when your done" programming mentality. 

Maybe I will just launch a GC thread at application start that periodically (once every 5 minutes or so) checks for the temporary files and deletes them if it can. (shrug)

@Ford : You're getting dangerously close to 5k posts.  Do you have a custom title picked out yet?


----------



## FordGT90Concept (Mar 13, 2010)

Opening a file stream isn't expensive.  I'm pretty sure it comes straight from the NTFS table and it doesn't seek the hard drive unless you start reading or writing.

This is what I'd do (pseudocode):


```
internal volitile List<string> _DeleteFiles = new internal List<string>();
private Thread _Thread = null;
public void Start()
{
  _Thread = new Thread(Worker);
  _Thread.Start();
}
public void Stop()
{
  if (_Thread != null)
    _Thread.Abort();
}

public void Worker()
{
  while (true)
  {
    for (int i = _DeleteFiles.Count - 1; i >= 0; i--)
    {
      try
      {
        File.Delete(_DeleteFiles[i]);
        _DeleteFiles.RemoveAt(i);
      }
      catch  { }
    }
    Thread.Sleep(5000);
  }
}
```
If you got a file you want to delete, add it to the _DeleteFiles collection.


----------



## CrackerJack (Mar 13, 2010)

This might be too simple... but have you tried closing myfile?


```
.... Code for creating file
SmtpClient _Client = new SmtpClient();
.... code for client configuration

try
{
    _Client.Send(myFile);
}
catch()
{
     ... handle an exception
}
System.IO.File.Close(myfile);
{
System.IO.File.Delete(myFile);
```


----------



## FordGT90Concept (Mar 13, 2010)

myFile is of type MailMessage (so File.Delete(myFile) will always error--can't delete MailMessage).  So the files he would need to delete are probably under myFile.Attachments (an AttachmentCollection), no?

This has an example:
http://msdn.microsoft.com/en-us/library/system.net.mail.mailmessage.attachments.aspx


```
MailMessage message = new MailMessage(
               "jane@contoso.com",
               "ben@contoso.com",
               "Quarterly data report.",
               "See the attached spreadsheet.");

            // Create  the file attachment for this e-mail message.
            Attachment data = new Attachment(file, MediaTypeNames.Application.Octet);
            // Add time stamp information for the file.
            ContentDisposition disposition = data.ContentDisposition;
            disposition.CreationDate = System.IO.File.GetCreationTime(file);
            disposition.ModificationDate = System.IO.File.GetLastWriteTime(file);
            disposition.ReadDate = System.IO.File.GetLastAccessTime(file);
            // Add the file attachment to this e-mail message.
            message.Attachments.Add(data);
```

What exactly are you trying to delete?  If there isn't attachments, myFile = null would suffice.


----------



## Kreij (Mar 14, 2010)

Ford is correct. It's a PDF file that is created using the ReportDocument.ExportToDisk() method and then added as an attachment to the MailMessage.

@Cracker ... There is no System.IO.File.Close() method.


----------



## CrackerJack (Mar 14, 2010)

Kreij said:


> Ford is correct. It's a PDF file that is created using the ReportDocument.ExportToDisk() method and then added as an attachment to the MailMessage.
> 
> @Cracker ... There is no System.IO.File.Close() method.



o ok so it's physical never on the HD then? hince can't do the file.close method? never work with reportdocument or mailmessage yet.


----------



## FordGT90Concept (Mar 14, 2010)

Kreij said:


> Ford is correct. It's a PDF file that is created using the ReportDocument.ExportToDisk() method and then added as an attachment to the MailMessage.


So is ExportToDisk or the Attachment not releasing the file?  Can you try deleting the file after performing ExportToDisk to rule that out?


----------



## Kreij (Mar 15, 2010)

I haven't had time to work on this problem, but thanks for the input guys.

I am assuming (I know, that's bad) that ExportToDisk() is releasing the file otherwise SmtpClient would have problems opening it to send it as an attachment.

Of course, with some if the internals of .Net it's anyone's guess.


----------



## FordGT90Concept (Mar 15, 2010)

It depends on how Attachments work.  If it is just copying a link and not the bytes, ExportToDisk() might be the culprit.


----------

