E_FAIL或S_FALSE,哪个更适合表示没有这样的属性?

时间:2016-03-24 02:37:06

标签: c++ api winapi interface

我有一个图像检测模块,它封装为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(小时)轻松检查
    • 使用此密钥查询此不存在的值是错误。
  3. 更新,(感谢Remy Lebeau)

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

      • 客户可以通过FAILED(小时)轻松检查
      • 虽然它代表了错误,但用户仍然可以通过查看hr来了解其含义。

2 个答案:

答案 0 :(得分:4)

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)将是最接近的标准等值)。

答案 1 :(得分:1)

我建议关注the pattern of COM enumerators' Next method 其中检索的对象数在输出参数中返回。 (如果对象计数为零但调用无效,则还会返回S_FALSE

因为它是一个输出参数,所以客户端不能忽略它,而VB等隐藏用户HRESULT的图层不会导致丢失信息。