如何从 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 公开布尔值

C++基于ATL工程编写ActiveX控件步骤

VS2017 ATL创建ActiveX编程要点

ActiveX控件(ATL篇)

ATL ActiveX DLL 作为映射类型图像和数据加载两次

双击ActiveX控件时如何禁用Visual Basic对话框