3

在实现COM接口时,我总是在成功时分配输出参数,但我也应该在错误时这样做吗?

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参数还是我真的应该这样做?


编辑:即使在函数失败的情况下正式不应访问参数我经常看到(有时编写)这样的代码(使用Sharptooth的帖子中的示例):

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();
}  

这在未编组的代码(相同的线程单元)中可以正常工作,但是如果涉及编组器,它是否足够聪明,仅在函数成功时才设置返回值?

4

3 回答 3

3

规则是,如果呼叫失败,则不允许主叫方对 out 参数值进行任何操作。因此,服务器不应提供有效值,也不应将任何资源的所有权传递给 out 参数。

例如,如果您有

HRESULT GetSmth( [out] ISmth** );

方法,那么预计服务器AddRef()ISmth**返回之前调用变量。如果它要返回失败代码,则不能调用AddRef(),因为不允许客户端使用返回的参数值,因此不会调用Release(),你会得到内存泄漏。

于 2009-04-01T11:45:55.457 回答
3

虽然其他答案没有错,但他们错过了一个非常重要的点——打算返回失败 HRESULT 的 COM 服务器必须将所有 [out] 参数设置为 NULL。这不仅仅是一个好的风格问题,它是 COM 所要求的,不遵守它可能会在涉及封送处理时导致随机崩溃。

也就是说, *pRet = 0; 在原始代码中不是多余的,而是正确和必需的。

于 2009-05-04T19:36:34.190 回答
1

我不确定我是否 100% 同意尖牙。我当然同意,对于失败的 COM 调用,您不能也不能将任何资源所有权分配给任何输出参数。这包括内存分配或添加引用 COM 对象。

但是,我认为没有错(实际上是鼓励)将纯粹的参数设置为空值,只要不转移任何资源所有权。例如,将代码设置 pRet 指向 0 从技术上讲没有任何违法之处。这不会将任何资源所有权转移给 pRet,并且只是一些没有正确检查调用成功的调用者的帮助者。

于 2009-04-01T13:20:26.463 回答