如何从 ATL activex 控件将错误字符串和错误代码返回到 VB6?
Posted
技术标签:
【中文标题】如何从 ATL activex 控件将错误字符串和错误代码返回到 VB6?【英文标题】:How can I return both an error string and error code to VB6 from an ATL activex control? 【发布时间】:2010-11-07 08:41:31 【问题描述】:我正在尝试使用 CComCoClass::Error 向 VB6 返回一个详细的错误,但似乎我只能返回一个错误代码/或/一条消息 - 但不能同时返回两者。
return Error(_T("Not connected"), __uuidof(IMyInterface), HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID));
导致在 VB6 端的 Err.Description 中出现通用的“方法 'Request' of object 'IMyInterface' failed”错误消息(但 Err.Number 中的 ERROR_CONNECTION_INVALID),而
return Error(_T("Not connected"));
导致相应的错误消息,但 Err.Number 中的通用错误代码。我怎样才能两全其美?
【问题讨论】:
愚蠢的问题:你在实现 ISupportErrorInfo 吗? 是的 - support_error_info(IMyInterface) 在我的实现类之前的属性块中,我尝试过使用和不使用 ISupportErrorInfoImpl 我还应该注意,我对 COM 和 ATL 还很陌生,所以我可能在某处遗漏了一些明显的东西 :) 【参考方案1】:你不能,这似乎是设计使然。详细信息如下,但简而言之,您有三个选择:
根据KB article,不返回任何消息和 VB 友好的 COM 错误,即 VB 运行时众所周知; VB 运行时会将此“COM 错误”转换为 VB 错误消息。 返回错误信息和 DISP_E_EXCEPTION; VB 运行时将通过此“服务器错误”和您的自定义错误消息。这是您的第二个示例中隐含发生的情况,请参阅下文了解详细信息。 不返回任何消息和任何其他 COM 错误,即 VB 运行时未知; VB 运行时将求助于原始 HRESULT 加上通用消息“Method '~' of object '~' failed
”。
请注意,此运行时行为也适用,如果您在此处提供错误消息,即您的消息将被忽略!这是您的第一个示例中发生的情况,请参阅下文了解详细信息。
对于手头的任务,它归结为两个选择:
如果您想为 VB 等自动化客户端提供上下文正确的“COM 错误”(您可能应该这样做),您必须省略自定义错误消息。 如果您想为“服务器错误”提供自定义错误消息(即关于自动化服务器内功能的自定义错误条件),您唯一的选择是 DISP_E_EXCEPTION。详情
VB 运行时似乎只对 COM 错误提供非常有限的处理。这可能是出于特定于 VB 实现方式的历史和/或技术原因,在这里并不特别感兴趣(关键字将是 IDispatch only vs dual interface 和 ActiveX 作为 COM 的“子集”)。
虽然我无法为上述行为提供明确的规范,但可以通过挖掘其他来源得出结论:
来自KB articlejustadreamer pointed out already:
[...] 向 GetErrorInfo 方法来检索 可用的错误信息。这 然后运行时确定是否 bstrDescription 的值不是 空值。如果运行时找到一个值 除了 NULL,[...],原始 HRESULT 在这种情况下使用值。如果 运行时找到一个 NULL 值,[...] Visual Basic 然后使用 HRESULT 查找对应的 Visual 基本错误。
这解释了关于您的第一个示例的行为:您确实提供了一条错误消息,因此运行时只是诉诸其通用消息“Method '~' of object '~' failed
”加上您的HRESULT
。
一旦您查看CComCoClass::Error
的(第一个列出的)构造函数的定义,第二个示例的行为也是一致的:它具有未指定参数的默认值,尤其是“hRes = 0”。 “备注”部分进一步指出“如果 hRes 为零,则错误的前四个版本返回 DISP_E_EXCEPTION。”。因此,这会隐式触发“服务器错误”传递行为。
最后,对于类似自动化客户端行为的 VB 的具体 C++ 实现示例,请参见 Automating Microsoft Office 97 and Microsoft Office 2000 中的“错误处理”和以下“练习 5”。
【讨论】:
【参考方案2】:从 ISupportErrorInfoImpl 派生实现 COM 公开接口的类,调用 SetErrorInfo 设置错误的详细说明。不要忘记将 ISupportErrorInfo 包含到您班级的 COM_MAP 中。
【讨论】:
我已经这样做了——尽管我的代码是属性化的,所以 support_error_info 声明位于类声明上方的属性块中。正如我上面指出的,如果我不传递特定的错误代码,我的错误字符串会被传回,所以我认为这不是问题...... 如果您将它作为输出参数传递 - 只有当您不返回错误代码时它才会被传递。那是一个约定。您需要调用 SetErrorInfo 来提供错误文本。 我曾尝试使用 SetErrorInfo,但如果我返回 DISP_E_EXCEPTION 以外的 HRESULT,VB6 将忽略该描述... 这很奇怪。我们使用早期绑定从 VB6 调用 COM 接口方法,并且在错误描述方面没有问题。出错时,我们手动调用 CreateErrorInfo()、ICreateErrorInfo::SetDescription(),然后是 ICreateErrorInfo::QueryInterface(),然后是 SetErrorInfo()——这比 AtlSetErrorInfo 少一点——VB6 方面很高兴。你在VB6端使用早期绑定还是后期绑定? 我将它用作表单上的控件,而不是将其声明为变量。【参考方案3】:我现在也在为此苦苦挣扎。到目前为止,我的挖掘表明错误代码实际上是 HRESULT 值。 VB6 试图变得聪明并解释 HRESULT,但它所理解的 HRESULT 列表似乎相当有限。对于 VB6 不熟悉的 HRESULT,它只是将 HRESULT 放入 Err.Number 属性中,希望开发者足够聪明,能够弄清楚如何处理它。
我最接近返回错误号的方法是使用 MAKE_SCODE 生成 HRESULT,并将 HRESULT 的代码字段设置为我想要的、设置严重性标志以及我希望是正确的工具。
结合 CreateErrorInfo 和 SetErrorInfo 可以得到一个错误代码和 VB6 中的错误描述。这让我们回到了 VB6,试图在有限的错误列表中变得聪明。
【讨论】:
您使用的是什么设备? FACILITY_ITF 没有为我显示正确的错误描述 对于我们想要发回的特定错误,我正在使用 FACILITY_STORAGE。但我们也尝试了 FACILITY_WIN32 和 FACILITY_ITF,错误代码和消息仍然通过。【参考方案4】:查看这篇文章http://support.microsoft.com/kb/827994。因此,您的对象必须实现返回 S_OK 的方法 ISupportsErrorInfo::InterfaceSupportsErrorInfo()。然后在返回之前,您必须调用 SetErrorInfo,并使用指向实现 IErrorInfo::GetDescription() 的 COM 对象的指针。 这里有一个例子: http://msdn.microsoft.com/en-us/library/ms221409.aspx.
如果你在返回之前SetErrorInfo,VB会查询你传递给SetErrorInfo的对象指针的GetDescription方法。
我对您正在使用的属性代码并不太了解 - 我更愿意使用更原始的 COM 来测试它,这肯定总是有很多样板代码 - 但至少它可以工作,然后你可以使用复杂的包装器而不是它。
【讨论】:
以上是关于如何从 ATL activex 控件将错误字符串和错误代码返回到 VB6?的主要内容,如果未能解决你的问题,请参考以下文章
ATL ActiveX 控件如何向 javascript 公开布尔值