|
みなさん回答ありがとうございます。
#お盆休みで遅くなり申し訳ございません。
再現(質問)用のミニマムコードでしたので、情報不足で申し訳ございません。
まず回答を行います。
・そもそも何がしたいのか分からない
→COM(Microsoft.Office.Interop.Excel or Word)の操作のため
→xls/docが過去資産として残っている為、xlsx/docxと決め打ちできない=OpenXML系ライブラリを使用できない
→さらに補足すると、バッチ処理の一部です。これが落ちると、他のバッチ処理まで止まるためです。
・catch句で再スローする場合に、引数省略しないと、破棄した意味になっちゃうってやつ?
→その通りです。ExceptionDispatchInfoは知りませんでした。調べてみます。
・例外の推奨事項
→「イベントが頻繁に発生しない場合、つまり、イベントが本当に例外的であり、予期しないファイルの終わりなどのエラーを示す場合は、例外処理を使用します。 例外処理を使用すると、通常の状況では、実行されるコードが少なくなります。」
に該当するかと。通常は問題ないのですが、処理中にApplicationが落ちて、
「System.Runtime.InteropServices.COMException (0x800706BA): RPC サーバーを利用できません。 (HRESULT からの例外:0x800706BA)」が稀に発生するためです
・なぜTry〜Catch〜Finallyなのか?
→Finally句でRange等の一時オブジェクトの一括開放、BookやapplicationのCloseとログ出力処理(担当者へのメール通知)を行うため
→Dispose自体はusingで行う
→Finally句で例外が発生する原因は、Comラッパークラスを利用してusingで開放するのですが、
アプリケーション側が何らかの原因で落ちると、Close/Exit等で例外が前述の例外(RPCエラー)が発生するのをどうにかしたいと悩んでいます。
・データベースに 〜 アプリケーションを停止させるというものです。
→このサンプルに近いです。
→COMException と、 ErrorCode でキャッチする様に工夫してみます。
-----現在のミニマムコード
namespace hoge
{
internal class Program
{
static void Main(string[] args)
{
try
{
proc(); // 18行目
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
//if (ex.InnerException != null)
// Console.WriteLine(ex.InnerException.ToString());
//else
// Console.WriteLine($"InnerException=null");
//if (ex.GetBaseException() != null)
//{
// Exception bex = ex.GetBaseException();
// Console.WriteLine(bex.ToString());
//}
//else
// Console.WriteLine($".GetBaseException()=null");
}
Console.WriteLine($"any key.");
Console.Read();
}
static void proc()
{
Exception exKeep = null;
try
{
throw new NullReferenceException($"proc:Test!!"); // 45行目
}
catch (Exception ex) { exKeep = ex; throw; } // 47行目
finally
{
// InnerException に NullReferenceExceptionを入れたい
try
{
// 実際はCOMException (0x800706BA)
unchecked
{
throw new COMException("Proc:finally", (int)0x800706BA); // 56行目
}
}
catch (COMException e)
{
unchecked
{
if (e.ErrorCode == (int)0x800706BA)
throw new ApplicationException($"Proc:finally", exKeep); // 64行目
else
throw;
}
}
}
}
}
}
-----現在のミニマムコードの実行結果
System.ApplicationException: Proc:finally ---> System.NullReferenceException: proc:Test!!
場所 hoge.Program.proc() 場所 C:\Users\71420505\source\repos\hoge\hoge\Program.cs:行 47
場所 hoge.Program.Main(String[] args) 場所 C:\Users\71420505\source\repos\hoge\hoge\Program.cs:行 18
--- 内部例外スタック トレースの終わり ---
場所 hoge.Program.proc() 場所 C:\Users\71420505\source\repos\hoge\hoge\Program.cs:行 64
場所 hoge.Program.Main(String[] args) 場所 C:\Users\71420505\source\repos\hoge\hoge\Program.cs:行 18
any key.
----期待値
・47行目→45行目のスタックトレースが知りたい
・64行目→56行目のスタックトレースが知りたい
※ExceptionDispatchInfoは現時点で未勉強
-----Comラッパークラス ※どっかのblog?から引用
/// <summary>COMのオブジェクトにIDisposableを実装させる</summary>
/// <typeparam name="T">型指定</typeparam>
public class ComWrapper<T> : IDisposable
{
public T ComObject { get; }
public ComWrapper(T comObject)
{
this.ComObject = comObject;
}
public ComWrapper()
{
}
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
//nop
}
try
{
Marshal.ReleaseComObject(ComObject);
}
catch (Exception)
{
throw;
}
disposedValue = true;
}
}
~ComWrapper()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
}
}
/// <summary>List()にリソース開放処理</summary>
/// <typeparam name="T">型指定</typeparam>
public class DispoableList<T> : List<T>, IDisposable
{
/// <summary>開放処理</summary>
public void Dispose()
{
// List()内要素にてリソース解放が必要なら実施
ForEach(value =>
{
if (value is IDisposable disposable)
{
disposable.Dispose();
}
});
// List()から全ての要素を削除
Clear();
}
}
----- 皆さんの回答を
internal class Program
{
static void Main(string[] args)
{
try
{
proc();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
if (ex.InnerException != null)
Console.WriteLine(ex.InnerException.ToString());
else
Console.WriteLine($"InnerException=null");
if (ex.GetBaseException() != null)
{
Exception bex = ex.GetBaseException();
Console.WriteLine(bex.ToString());
}
else
Console.WriteLine($".GetBaseException()=null");
}
Console.WriteLine($"any key.");
Console.Read();
}
static void proc()
{
Exception exKeep = null;
try
{
throw new NullReferenceException($"proc:Test!!");
}
catch (Exception ex) { exKeep = ex; throw; }
finally
{
// InnerException に NullReferenceExceptionを入れたい
if( e != null ) throw new ApplicationException($"Proc:finally", exKeep);
else throw new ApplicationException($"Proc:finally");
}
}
}
|