实现COM
接口时,我总是在成功时分配out参数,但是我也应该在出错时这样做吗?
HRESULT CDemo::Div(/*[in]*/ LONG a, /*[in]*/LONG b, /*[out,retval]*/ LONG* pRet)
{
if (pRet == NULL)
return E_POINTER;
if (b == 0)
{
*pRet = 0; // is this redundant?
return E_INVALIDARG;
}
*pRet = a/b;
return S_OK;
}
有一段时间我没有初始化一个out参数,并假设如果我初始化变量,如果我不在方法中更改它,它将保持该值。但是我从.NET
使用了这个方法,因为编组人员发现这是一个[out]
参数,它丢弃了我放在调用站点上的初始值,并在函数返回后放入垃圾(这很有趣的调试那,不是)。
即使在过度补偿失败的情况下分配给out
参数,还是我真的应该这样做?
编辑:即使正式人员不应该访问params,如果函数失败,我经常会看到(有时会编写)这样的代码(使用sharptooth's post中的示例):
ISmth *pSmth = NULL;
pObj->GetSmth(&pSmth); // HRES is ignored
if (pSmth) // Assumes that if GetSmth failed then pSmth is still NULL
{
pSmth->Foo();
pSmth->Release();
}
这在未编组的代码(同样thread apartment)中工作正常但是如果涉及编组器,它是否足够聪明,只有在函数成功时才设置返回值?
答案 0 :(得分:3)
规则是,如果呼叫失败,则不允许主叫方对out参数值执行任何操作。因此,服务器不应提供有效值,也不应将任何资源的所有权传递给out参数。
例如,如果你有
HRESULT GetSmth( [out] ISmth** );
方法然后预计服务器在返回之前调用AddRef()
变量上的ISmth**
。如果它将返回失败代码,则不能调用AddRef()
,因为不允许客户端使用返回的参数值,因此不会调用Release()
并且您将获得内存泄漏。
答案 1 :(得分:3)
虽然其他答案没有错,但他们错过了一个非常重要的观点 - 一个打算返回失败的COM服务器HRESULT 必须将所有[out]参数设置为NULL。这不仅仅是一个好的风格问题,它是COM所要求的,并且当涉及编组时不遵守它会导致随机崩溃。
那就是说,* pRet = 0;在原始代码中不是多余的,但是正确且必需。
答案 2 :(得分:1)
我不确定我是否100%同意sharptooth。我当然同意,对于失败的COM调用,您不能也不能将任何资源所有权分配给任何输出参数。这包括内存分配或AddRef'ing COM对象。
然而,我认为没有任何错误(事实上鼓励)将纯参数设置为空值,因为长期不传输任何资源所有权。例如,在您的代码设置pRet指向0时,技术上没有任何违法行为。这不会将资源所有权转移到pRet,而只是一些未正确检查呼叫成功的呼叫者的帮助者。