- PR -

メール送信時のエラー処理方法

投稿者投稿内容
コウイチ
常連さん
会議室デビュー日: 2003/11/04
投稿数: 48
投稿日時: 2004-06-25 19:41
よろしくお願いします。
メール送信時のエラー処理方法で、いい方法を教えて頂けないでしょうか?

環境: WinXPPro、VS.NET2003、C#

System.Web.Mail.SmtpMail.Sendメソッドでの送信でのエラーが発生した場合に
以下のようにキャッチして処理しています。
キャッチした例外によって再送処理を行うかのフラグを変更しています。
再送処理は、1時間ごとに行うようにします。
再送するかしないかの判断は、
再送したら送信されるかもしれない場合(メールサーバ落ちていた場合)は、
再送するようにしようと思っています。
下記だと、メルアドが設定されていない場合などは何回送信処理をしても送信には失敗するので再送なしと設定しています。

コード:
catch( Exception e){
 string errMsg = e.InnerException.InnerException.Message;
 if( errMsg.Equals("少なくとも 1 人の受信者が必要ですが、1 人も見つかりませんでした。¥r¥n") || 
   errMsg.Equals("少なくとも、¥"差出人¥" または ¥"送信者¥" フィールドのどちらか一方が必要ですが、どちらも見つかりませんでした。¥r¥n") ||
   errMsg.StartsWith("サーバーによって送信者アドレスが拒否されました。") )
 {
  // 対象メールの再送信フラグを 「なし」 に設定
 }
 else if( errMsg.Equals("転送においてサーバーに接続できませんでした。¥r¥n") )
 {
  // 対象メールの再送信フラグを 「あり」 に設定
 }
 else{
  // 対象メールの再送信フラグを 「あり」 に設定
 }
}

catchした例外(Lanを抜きました)を
Console.WriteLine(e.InnerException);
で出力してみると、
System.Reflection.TargetInvocationException: 呼び出しのターゲットが例外をスローしました。 ---> System.Runtime.InteropServices.COMException (0x80040213): 転送においてサーバーに接続できませんでした。
と出力されたので、COMExceptionのMessageがエラー原因として理解できるので、
そのMessageで比較するようにしているのですが・・・。
送信するメールサーバによってメッセージが変わったらお終いなんですが、
同じエラーでもメールサーバによって、返ってくるメッセージは変わるのでしょうか?

本当は、エラー番号等を取得して、再送フラグの設定処理を分けるといったことを
したかったのですが、取得方法がわからなかったので、
上記のような処理になってしまいました><

「再送なし」となるエラーを取得する方法として、
もっといい処理方法を教えていただけないでしょうか?
catch(Exception e){
 if( 再送なしになるようなエラーの場合 ){
  再送フラグ「なし」にセット
 }
 else{
  再送フラグ「あり」にセット
 }
}
という感じにしたいと思っています。
「再送なしになるようなエラー」は、
 ・From,Toのメールアドレス未設定
 ・521 指定した<ドメイン>がメールを受け付けない(認証に失敗?)
 ・550 メールボックスが利用不可能である。例:メールボックスが存在しない
 ・551 ユーザがローカルに存在しない
 ・552 ストレージの割りあてが上回った場合
 ・553 メールボックス名は許可されない(例えばメールボックスの構文が正しくない)
だと、いくら再送してもエラーになると考えてるのですが、
他には何が考えられるでしょうか?

長くなってすみません。
よろしくお願いします。m(_ _)m
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2004-06-28 08:52
引用:

コウイチさんの書き込み (2004-06-25 19:41) より:
 ・From,Toのメールアドレス未設定


少なくともこの例は、送信する前にわかるのではないでしょうか?

COMException.ErrorCodeは、どうでした?
コウイチ
常連さん
会議室デビュー日: 2003/11/04
投稿数: 48
投稿日時: 2004-06-28 09:42
Jitta さん、返信ありがとうございます。

引用:
COMException.ErrorCodeは、どうでした?


COMExceptionのエラーコードは気になってはいたのですが、
取得方法がわかりません。

using System.Runtime.InteropServices;
catch(COMException ec)
{
 Console.WriteLine("E-Code: "+ ec.ErrorCode);
}
catch(Exception e)
{
 ・・・・
}
としたのですが、COMExceptionをキャッチしてくれません。
InnerExceptionの取得はどのようにすればいいのでしょうか?

よろしくお願いします。m(_ _)m
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2004-06-28 10:11
引用:

コウイチさんの書き込み (2004-06-28 09:42) より:

COMExceptionをキャッチしてくれません。
InnerExceptionの取得はどのようにすればいいのでしょうか?


 例外のスタックから、COMExceptionが発生していることがわかっているが、受けた例外は別のクラスである、ということですね?

未検証直打ち
コード:
} chatch (Exception e) {
    do {
        Console.WriteLine(e.ToString());
        e = e.InnerException;
    } while (e != null);
}


一応、こんな風に潜っていくことはできます。

拾い出しは、
コード:
if (e is typeof(COMException)) {
    COMException ce = (COMException) e;
}


こんな感じ。
コウイチ
常連さん
会議室デビュー日: 2003/11/04
投稿数: 48
投稿日時: 2004-06-28 11:55
Jitta さん、返信ありがとうございます。

do{
 e = e.InnerException;
 if (e is COMException ) {
  COMException ce = (COMException) e;
  Console.WriteLine("E-Code: " + ce.ErrorCode);
  break;
 }
}while( e!=null);

とすれば、ErrorCodeを見れました。
InnerExceptionをCOMExceptionでキャストすればよかったんですね><
気づきませんでした。

COMException.ErrorCode ですが、
SMTP通信エラー時の応答コードとは、関係ないんですね><

SMTP通信エラー時の応答コードは、(応答コードを取得したいなら)
COMExceptionのMessage内から取得ということになるのでしょうか?

よろしくお願いします。m(_ _)m
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2004-06-28 14:08
引用:

コウイチさんの書き込み (2004-06-28 11:55) より:

do{
 e = e.InnerException;
 if (e is COMException ) {
  COMException ce = (COMException) e;
  Console.WriteLine("E-Code: " + ce.ErrorCode);
  break;
 }
}while( e!=null);


 これだと一番外の例外が評価されないので、今回の例ではかまわないのですが、あまりよいコードではありません。

 COMExceptionの発生元はありませんでしたか?(InnerExceptionはNULLでしたか?)
コウイチ
常連さん
会議室デビュー日: 2003/11/04
投稿数: 48
投稿日時: 2004-06-28 15:38
Jitta さん、返信ありがとうございます。
引用:
COMExceptionの発生元はありませんでしたか?(InnerExceptionはNULLでしたか?)


キャッチしたException( e )を、
 Console.WriteLine( e.ToString() );
とすると、

System.Web.HttpException: 'CDO.Message' オブジェクトにアクセスできませんでした。
---> System.Reflection.TargetInvocationException: 呼び出しのターゲットが例外をスローしました。
---> System.Runtime.InteropServices.COMException (0x8004020E): サーバーによって送信者アドレスが拒否されました。サーバーからの応答は次のとおりです。521 mail not accepted from this domain

と表示されます。
Exception e を発生させたのが、System.Reflection.TargetInvocationException で、
System.Reflection.TargetInvocationException を発生させたのが、
System.Runtime.InteropServices.COMException で、
COMException の発生元はない?・・・と、思うのですが勘違いしてますでしょうか?

if (e is COMException ) {
 COMException ce = (COMException) e;
 Console.WriteLine("E: " + ce.InnerException);
 Console.WriteLine("E-Code: " + ce.ErrorCode);
}
とすると、
E:
E-Code: -2147220978
と表示されました。

よろしくお願いします。m(_ _)m
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2004-06-28 19:17
メールの送信はSMTPMailクラスを使っているんですよね?

 このクラスの概要にサンプルプログラムがあるのですが、そこではSystem.Web.HttpExceptionクラスで受けています。また、COMExceptionクラスの説明では、いくつかの例外種別については、クラス化された例外にマップされると書かれています。これから想像するに、エラーによって返ってくる例外が違うと思われます。したがって、すべてExceptionで受けて、COMExceptionかどうかの判定だけ行うのは、ちょっとしんどいかと。

 メールサーバを立てられるなら、適当に設定して、考えられるパターンすべてをやってみて、どのような例外が返ってくるか見て、その上で対策を立てるのがよいかと。。。

今更な内容で申し訳ないですが。。。

スキルアップ/キャリアアップ(JOB@IT)