从通过反射调用的COM方法中检索原始错误号

时间:2020-03-06 14:55:38  来源:igfitidea点击:

我有一个VB6 COM组件,我需要从.Net方法中调用它。我使用反射来创建COM对象的实例并按以下方式激活它:

f_oType = Type.GetTypeFromProgID(MyProgId);
f_oInstance = Activator.CreateInstance(f_oType);

我需要使用GetTypeFromProgID而不是使用tlbimp来针对COM DLL创建一个库,因为我需要实例化的类型的ProgId会有所不同。然后,我使用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应用程序中掌握该错误号。

问题似乎是由于TargetInvocationException不包含错误号(如果它是普通的COMException,这是我所期望的),因此:

如何从.Net代码中的COM对象获取错误号?

或者

当COM组件发生故障时,是否可以导致COMException(包含错误号)而不是TargetInvocationException的方式进行相同的调用?

另请注意,目标平台是.Net 2.0,我确实可以访问VB6源代码,但是将更改从VB6引发的错误消息以将错误代码作为文本的一部分包含在内可能会有些许麻烦。

解决方案

我们将处理COMException并使用该异常对象的ErrorCode属性。通常,在Visual Basic DLL中,如果引发自定义错误,则会使用以下方法引发错误:
Err.Raise vbObjectError + 88," Project1.Class1.Test3()","强制错误测试"

如果是这种情况,我们将需要从异常ErrorCode中减去vbobjecterror(-2147221504)以获取实际的错误号。如果不是,则仅使用ErrorCode值。

示例VB dll代码:(来自Project1.Class1)

公开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.

我仔细查看了一下代码,并经过反思,我们可以处理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); }
    }

只想提供@sharvell捕获代码的更新。除非我们绝对确定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
}