リフレクション経由で呼び出されたCOMメソッドから元のエラー番号を取得する
質問
.Netメソッドから呼び出す必要があるVB6 COMコンポーネントがあります。リフレクションを使用してCOMオブジェクトのインスタンスを作成し、次の方法でアクティブにします。
f_oType = Type.GetTypeFromProgID(MyProgId);
f_oInstance = Activator.CreateInstance(f_oType);
インスタンス化する必要のある型のProgIdはさまざまなので、tlbimpを使用してCOM DLLに対してライブラリを作成するのではなく、GetTypeFromProgIDを使用する必要があります。次に、Type.InvokeMemberを使用して、次のようなコードでCOMメソッドを呼び出します。
f_oType.InvokeMember("Process", BindingFlags.InvokeMethod, null, f_oInstance, new object[] { param1, param2, param3, param4 });
発生したTargetInvocationExceptionをログに記録し、TargetInvocationException.InnerExceptionフィールドから詳細なエラーの説明を取得できます。ただし、COMコンポーネントはError.Raiseを使用してエラー番号を生成し、呼び出し元の.Netアプリケーションで何らかの方法でこれを保持する必要があることを知っています。
問題は、通常のCOMExceptionである場合に予想されるように、エラー番号を含まないTargetInvocationExceptionに起因するようです。
.NetコードのCOMオブジェクトからエラー番号を取得するにはどうすればよいですか
または
COMコンポーネントが失敗したときにTargetInvocationExceptionではなくCOMException(エラー番号を含む)を引き起こす方法でこの同じ呼び出しを行うことはできますか?
ターゲットプラットフォームは.Net 2.0であり、VB6ソースコードにアクセスできることにも注意してください。ただし、VB6から発生したエラーメッセージを変更して、テキストの一部としてエラーコードを含めると、ハック。
解決
コードをもう少し詳しく見て、リフレクションを使用してTargetInvocationExceptionを処理し、COMExceptionである内部例外を処理します...以下のコード例(これも実行してテストしました):
private void button1_Click(object sender, EventArgs e)
{
try
{
var f_oType = Type.GetTypeFromProgID("Project1.Class1");
var f_oInstance = Activator.CreateInstance(f_oType);
f_oType.InvokeMember("Test3", BindingFlags.InvokeMethod, null, f_oInstance, new object[] {});
}
catch(TargetInvocationException ex)
{
//no need to subtract -2147221504 if non custom error etc
int errorNumber = ((COMException)ex.InnerException).ErrorCode - (-2147221504);
MessageBox.Show(errorNumber.ToString() + ": " + ex.InnerException.Message);
}
catch(Exception ex)
{ MessageBox.Show(ex.Message); }
}
他のヒント
COMExceptionを処理し、その例外オブジェクトのErrorCodeプロパティを使用します。通常、Visual Basic DLLでは、カスタムエラーをスローしている場合、次のエラーが発生します。 Err.Raise vbObjectError + 88、" Project1.Class1.Test3()"、"強制エラーテスト"
この場合、例外ErrorCodeからvbobjecterror(-2147221504)を減算して、実際のエラー番号を取得する必要があります。 ErrorCode値を使用しない場合。
VB dllコードの例:(Project1.Class1から)
Public Sub Test3()
MsgBox "this is a test 3"
Err.Raise vbObjectError + 88, "Project1.Class1.Test3()", "Forced error test"
サブの終了
C#消費処理コードの例:
private void button1_Click(object sender, EventArgs e)
{
try
{
var p = new Class1();
p.Test3();
}
catch (COMException ex)
{
int errorNumber = (ex.ErrorCode - (-2147221504));
MessageBox.Show(errorNumber.ToString() + ": " + ex.Message);
}
catch(Exception ex)
{ MessageBox.Show(ex.Message); }
}
完了したこのテストのErrorCodeは、予想どおり88を返します。
@sharvellのcatchコードの更新を提供したいだけです。 InnerExceptionがCOMExceptionであることが確実でない限り、最初に安全にテストすることをお勧めします。そうしないと、例外ハンドラに例外が発生します。おっと!
catch(TargetInvocationException ex)
{
if( ex.InnerException != null && ex.InnerException is COMException )
{
COMException ce = (COMException)ex.InnerException;
// do something with ce - e.g. logging the error
}
// else InnerException not set, or it's not a COMException
}