从通过反射调用的 COM 方法中检索原始错误号
Posted
技术标签:
【中文标题】从通过反射调用的 COM 方法中检索原始错误号【英文标题】:Retrieving the original error number from a COM method called via reflection 【发布时间】:2008-09-30 15:00:01 【问题描述】:我有一个需要从我的 .Net 方法调用的 VB6 COM 组件。我使用反射来创建 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 引发的错误消息更改为包含错误代码作为文本的一部分有点破解。
【问题讨论】:
【参考方案1】:我仔细查看了您的代码,并通过反射处理 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);
【讨论】:
这明白了。最初被抛出,因为 InnerException 没有作为 COMException 返回,但调查显示 COM 组件错误地引发了错误。修复了这个问题,这是一种享受【参考方案2】:您将处理 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。
【讨论】:
如原始问题所述,代码不会抛出 COMException,它会抛出没有错误号的 TargetInvocationException【参考方案3】:只想对@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
【讨论】:
以上是关于从通过反射调用的 COM 方法中检索原始错误号的主要内容,如果未能解决你的问题,请参考以下文章
vb.net制作的Excel文件 未捕获通过反射调用的方法而引发的异常