質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.58%

MailKitでメールを送信後、添付したファイルを削除しようとすると「別のプロセスで使用されているため~」のエラーが発生する。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,131

score 10

MailKitでメールを送信し、その後ローカルにある添付ファイルを削除する処理があります。

MailKitでの送信時の処理

clsSendMail.cs

public bool SendMail()
{

MimeKit.MimeMessage message = new MimeKit.MimeMessage();

//送付先の設定
message.From.Add(new MimeKit.MailboxAddress("送信者名", "送信者アドレス"));
message.To.Add(new MimeKit.MailboxAddress("宛先名", "宛先アドレス"));
message.Subject = "件名";

string strText = "本文";
var textPart = new MimeKit.TextPart(MimeKit.Text.TextFormat.Plain);
textPart.Text = strText;

//添付ファイルを取得
string FolderPath = "添付するファイルの保存ディレクトリ";
string FilePath = string.Empty;

DirectoryInfo di = new DirectoryInfo(FolderPath);
IEnumerable<DirectoryInfo> subFolders = di.EnumerateDirectories("*", SearchOption.AllDirectories);

foreach (DirectoryInfo FolderName in subFolders)
{
    if (cc.LeftByte(FolderName.Name, 3) == "宛先コード")
    {
        FilePath = FolderPath + FolderName + @"\";
        break;
    }
}

string[] FileNames = Directory.GetFiles(FilePath, "*", SearchOption.TopDirectoryOnly);
string[] Name = new string[9] { "", "", "", "", "", "", "", "", "" };
int intCnt = 0;
foreach (string FileName in FileNames)
{
    Name[intCnt] = cc.NullChecks(FileName); //宛先のフォルダ内にあるファイルを全て取得
    intCnt++; //取得したファイルの数をカウント
}

//ファイルを添付(最大9個まで)
var attachment1 = new MimeKit.MimePart();
var attachment2 = new MimeKit.MimePart();
var attachment3 = new MimeKit.MimePart();
var attachment4 = new MimeKit.MimePart();
var attachment5 = new MimeKit.MimePart();
var attachment6 = new MimeKit.MimePart();
var attachment7 = new MimeKit.MimePart();
var attachment8 = new MimeKit.MimePart();
var attachment9 = new MimeKit.MimePart();

MimeKit.MimePart[] attachments = new MimeKit.MimePart[9]
 { attachment1,attachment2,attachment3,attachment4,attachment5,
  attachment6,attachment7,attachment8,attachment9};

//ファイルの件数分、添付ファイルのpartを作成
for (int i = 0; i < Name.Length; i++)
{
    if (Name[i] != "")
    {
            attachments[i] = new MimeKit.MimePart()
            {
                Content = new MimeKit.MimeContent(File.OpenRead(Name[i])),
                ContentDisposition = new MimeKit.ContentDisposition(),
                ContentTransferEncoding = MimeKit.ContentEncoding.Base64,
                FileName = Path.GetFileName(Name[i])
            };
    }
}

var multiPart = new MimeKit.Multipart("mixed");
multiPart.Add(textPart);
for (int i = 0; i < intCnt; i++)
{
    multiPart.Add(attachments[i]);
}

//MimeMessageを完成させる
message.Body = multiPart;

 //SMTPサーバーに接続してメールを送信する
 using (var client = new MailKit.Net.Smtp.SmtpClient())
{
    try
    {
        client.Connect(SmtpServer, SmtpPort);
        Console.WriteLine("接続完了");

        client.Authenticate(MailAddress, Password);
        Console.WriteLine("認証完了");

        client.Send(message);
        Console.WriteLine("送信完了");

        client.Disconnect(true);
        Console.WriteLine("切断");                   
    }
    catch (Exception ex)
    {
        errMessage = ex.ToString();
    }
}

if (errMessage != "") { return false; }
return true;
}

その後、ファイルを含むフォルダ削除の処理

Form1.cs

    private bool DirectoryDelete(string Path)
            try
            {
                DirectoryInfo target = new DirectoryInfo(Path); 
                foreach (DirectoryInfo dir in target.GetDirectories())
                {
                    dir.Delete(true); ---☆
                }
                return true;
            }
            catch(Exception ex)
            {
                errMessage = "ローカルの送信用フォルダ削除に失敗しました。";
                return false;
            }
    }


☆の処理で
System.IO.IOException: 別のプロセスで使用されているため、プロセスはファイル '〇〇〇.pdf' にアクセスできません。
というエラーが発生します。

メール送信先を複数選択することができ、その場合は全てにメール送信をした後にローカルのファイルをまとめて削除しています。
添付ファイル、添付ファイルの保存フォルダは宛先ごとにそれぞれ別で存在しています。
フォルダ1\001_A社\A社.pdf
フォルダ1\002_B社\B社.pdf
フォルダ1\003_C社\C社.pdf
といった感じです。

1か所しか送らないと、その送信先のファイルで削除時にエラーになります。

複数に送る場合、
001_A社
002_B社
003_C社
にコード順に送った場合、
最後に送信した003_C社のファイルだけ削除時にエラーになるようです。

となると添付ファイルのpartが追加してあるmessageの解放に問題があるのかと思うのですが、
Disposeメソッドが無くどうしたら良いのかわかりません。

何かわかる方がいらっしゃればご教示をお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • kikukiku

    2020/12/11 15:58

    下記の点が怪しいと思いました。
    ・メールにファイルを添付するときに、ファイルを握ったままになっていないか?
    ・メール送信のインスタンスがきちんと終了しているか?
    ・上記で改善しない場合、数秒待ってから削除処理を開始してはどうか?

    キャンセル

  • ohno1011

    2020/12/11 16:07

    radian様、SurferOnWww様、kikukiku様
    回答ありがとうございます。
    処理を省略せず記載するよう修正しました。

    キャンセル

  • SurferOnWww

    2020/12/11 16:12

    以下は Microsoft のドキュメントの File.OpenRead メソッドのサンプルコードの一部です。このようにしてみたらどうなりますか?

    using (FileStream fs = File.OpenRead(path))
    {
    // ・・・中略・・・
    }

    キャンセル

回答 1

checkベストアンサー

+1

    Content = new MimeKit.MimeContent(File.OpenRead(Name[i])),


ファイルがロックされたままになるということは、MimeContentクラスがFile.OpenReadで取得したFileStreamを処理した後、自動でClose(またはDispose)する仕様になっていないのかもしれません。
Streamがどのタイミングで読み取られるかは不明ですが、メール送信後は既に読み取っている筈なので、FileStreamを一旦Listなりに受け取り、メール送信後に自力でまとめてDisposeしてみてはどうでしょうか。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/12/11 17:09

    解決しました。

    var multiPart = new MimeKit.Multipart("mixed");
    multiPart.Add(textPart);
    for (int i = 0; i < intCnt; i++)
    {
    multiPart.Add(attachments[i]);
    }



    List<MimeKit.MimePart> mimes = new List<MimeKit.MimePart>(); //☆ MimePartのListを作成
    var multiPart = new MimeKit.Multipart("mixed");
    multiPart.Add(textPart);
    for (int i = 0; i < intCnt; i++)
    {
    mimes.Add(attachments[i]); //☆ リストにMimePartを追加
    multiPart.Add(attachments[i]);
    }

    上記のようにListを作り

    //SMTPサーバーに接続してメールを送信する
    using (var client = new MailKit.Net.Smtp.SmtpClient())
    {
     //省略
    }
    の後に
    foreach(var m in mimes)
    {
    m.Content.Stream.Dispose();
    }

    という処理を書くことで削除のエラーが出なくなりました!

    radian様、SurferOnWww様、kikukiku様
    回答頂き本当にありがとうございました。

    キャンセル

  • 2020/12/11 17:27 編集

    MailKitでググってトップに出てきた@ITのサンプルが、添付ファイルのFileStreamのDisposeについては触れてないから、解放忘れてる人は結構居そうです。(いずれはデストラクタで解放されますが)
    https://www.atmarkit.co.jp/ait/articles/1811/21/news023.html

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.58%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • C#に関する質問
  • MailKitでメールを送信後、添付したファイルを削除しようとすると「別のプロセスで使用されているため~」のエラーが発生する。