【问题标题】:E_FAIL or S_FALSE, which is more appropriate to represent no such attribute?E_FAIL 或 S_FALSE,哪个更适合表示没有这个属性?
【发布时间】:2016-07-11 13:53:14
【问题描述】:

我有一个图像检测模块,它被封装为一个 COM 模块。我导出了一个Key/Value Getter API,例如:GetImageAttr(UINT key, void* pValue);。我们的产品可能会或可能不会在图像上附加特殊结构,因此我的客户可以通过此 API 查询特定结构。

可能的用法是这样的:

ImageSpecialAttribute attr = {};
HRESULT hr = pImageDetector->GetImageAttr(IMAGE_SPECIAL_ATTRIBUTE, (void*)&attr);

如果图像确实具有这样的附加结构,则返回S_OK 是微不足道的。但如果没有,我应该返回 E_FAIL 还是 S_FALSE?

  1. S_FALSE:一切都很好,只是图像没有这个可选属性。

    • 强制用户检查 hr == S_OK
    • 查询没有此类可选属性的图像不会出错。
  2. E_FAIL:不!出了点问题。你不应该查询这个键。

    • 客户可以通过 FAILED(hr) 轻松检查
    • 使用此键查询此不存在的值是错误的。

已更新,(感谢 Remy Lebeau)

  1. HRESULT_FROM_WIN32(ERROR_NOT_FOUND):不!不存在这样的元素/属性。

    • 客户可以通过 FAILED(hr) 轻松检查
    • 虽然代表错误,但用户通过查看hr仍然可以知道是什么意思。

【问题讨论】:

  • IMO,任何返回 HRESULT 的东西都应该能够使用 FAILED() 进行测试,所以我对选项 2 投了赞成票。
  • 某些语言(例如 Visual Basic)会将E_ 代码转换为运行时异常。所以,我的建议是使用类似的规则来决定你的 C++ 函数是抛出失败还是返回状态码。通常,函数应该只在特殊情况下抛出,并且如果它应该中止或中断代码的执行流程。
  • 无论您决定采用哪种方式,如果该属性不存在,请确保使用中性值填写attr
  • 避免使用结构,它们很难正确编组(google IRecordInfo)。请改用接口。该接口上的 IsValid 属性肯定是明智的。避免使用 S_FALSE,没有多少语言运行时允许客户端代码看到该值。并没有多少客户端程序员认为他们应该捕获异常或 E_FAIL 意味着什么。
  • @HansPassant:OLE 到处使用S_FALSE,没有理由避免它。它不能是该属性不存在的唯一指示,但除了另一种方法之外使用它是完全合理的。

标签: c++ api winapi interface


【解决方案1】:

我建议关注the pattern of COM enumerators' Next method 其中检索到的对象数在输出参数中返回。 (S_FALSE 也会在对象计数为零但调用有效时返回)

因为是输出参数,客户端不能忽略,VB等层对用户隐藏HRESULT不会导致信息丢失。

【讨论】:

  • 其实我对使用S_FALSE的想法大部分来自于这个COM的枚举器next方法。这让我认为如果不存在此类或更多元素,则返回 S_FALSE 可能是合理的。但是,我有时会误用它,并且看到许多其他人也误用它,因为 FAILED(hr) 对其进行了测试。这就是为什么我发布这个问题是为了想听听更多关于这个的意见。
  • S_FALSE 非常适合用作“未找到”返回值。这意味着方法成功但没有采取任何行动。因此,它是由SUCCEEDED() 处理的成功值,而不是由FAILED() 处理的错误值。对于 COM 枚举器,Next() 在返回的对象数小于请求的对象数时返回S_FALSE。这包括数字为0的情况,但并不特指0。例如,如果您请求 5 个对象,而 Next() 返回 2 个对象,您将得到 S_FALSE
【解决方案2】:

S_FALSE 是成功值,而不是错误值。当方法本身成功但请求的数据不可用或未执行请求的操作时,Microsoft 自己的许多 COM API 都会返回S_FALSE。这在 Microsoft 的文档中有所提及:

Error Handling in COM

所有带有前缀“E_”的常量都是错误代码。常量 S_OK 和 S_FALSE 都是成功代码。大概 99% 的 COM 方法在成功时返回 S_OK;但不要让这个事实误导你。方法可能会返回其他成功代码,因此请始终使用 SUCCEEDEDFAILED 宏来测试错误。
...
成功代码 S_FALSE 值得一提。一些方法使用 S_FALSE 粗略地表示不是失败的否定条件。它也可以表示“无操作”——方法成功,但没有效果。例如,如果您从同一线程再次调用CoInitializeEx 函数,它会返回 S_FALSE。如果您需要在代码中区分 S_OK 和 S_FALSE,您应该直接测试该值,但仍然使用FAILEDSUCCEEDED 来处理其余情况...

我建议你遵循相同的约定,例如:

HRESULT hr = pImageDetector->GetImageAttr(IMAGE_SPECIAL_ATTRIBUTE, (void*)&attr);
if (SUCCEEDED(hr))
{
    if (hr != S_FALSE)
    {
        // use attribute as needed...
    }
    else
    {
        // attribute not found...
    }
}
else
{
    // error...
}

如果您确实想为不存在的属性返回错误代码,我建议您为该特定条件定义一个自定义HRESULT,例如:

#define E_ATTR_NOT_FOUND MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 1)

或者:

#define E_ATTR_NOT_FOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND)

然后您可以将该错误返回给调用者,例如:

HRESULT hr = pImageDetector->GetImageAttr(IMAGE_SPECIAL_ATTRIBUTE, (void*)&attr);
if (SUCCEEDED(hr))
{
    // use attribute as needed...
}
else if (hr == E_ATTR_NOT_FOUND)
{
    // attribute not found...
}
else
{
    // error...
}

COM 没有为“未找到”条件定义标准化错误 HRESULT 代码(HRESULT_FROM_WIN32(ERROR_NOT_FOUND) 将是最接近的标准等效项)。

【讨论】:

  • HRESULT_FROM_WIN32(ERROR_NOT_FOUND) 看起来像是一个非常有意义的返回码。谢谢。
猜你喜欢
  • 2014-08-19
  • 1970-01-01
  • 2011-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-17
  • 2010-11-09
相关资源
最近更新 更多