1

既存の ATL COM オブジェクトを変更しているときに、「The Old New Thing」ブログの「The way people mess up IUnknown::QueryInterface」という記事に出くわし、コメント セクションで議論が始まりました。回答者 (Norman Diamond) は、記事の例の 1 つで、void** へのキャストが間違っていることを指摘しました。

ただし、コードを修正してキャストを適切に実行しようとすると、メモリリークが発生します。

例は次のとおりです。

IShellFolder *psf = some object;
IUnknown *punk = NULL;
psf->QueryInterface(IID_IUnknown, (void**)&punk);

ノーマンは言った

パンクはボイドではない*. punk は IUnknown* です。

void** はユニバーサル ポインター型ではありません。void* はユニバーサル ポインター型であり、char* と相対はそのように等価になるように祖父母化されますが、void** はそうではありません。

呼び出し規則に従い、恐ろしい死を回避したい場合は、次のようにする必要があります。ボイド *パンクボイド; psf->QueryInterface(IID_IUnknown, &punkvoid); パンク = (IUnknown *) パンクボイド;

他の多くの MSDN 貢献者も同じ過ちを犯しました....現在までのすべての VC++ 実装で機能すると言う人もいるかもしれませんが、それは正しいコードにはならず、呼び出し規則にまだ違反しています。

これに照らして、古いコードを変更しました-次のとおりです。

#include <comdef.h>

...

HRESULT FinalConstruct()
{ 
    if (m_dwROTCookie != 0)
        return E_FAIL;

    //Check whether there already is an instance of the Object
    IUnknownPtr pUnk = NULL;
    if (GetActiveObject(CLSID_Object, NULL, &pUnk) == S_OK)
    {
        TRACE_WARNING("An instance of Object already exists in the current context");
        return S_OK;
    }
    HRESULT hr = QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&pUnk));

    hr = RegisterActiveObject(pUnk, CLSID_Object, ACTIVEOBJECT_WEAK, m_dwROTCookie);        
    if (FAILED(hr))
        return hr;

    hr = CoLockObjectExternal(pUnk, TRUE, TRUE);
    pUnk = NULL;
    ATLASSERT(m_dwRef == 2);
    return hr;
}

次に、次のように変更しました。

HRESULT FinalConstruct()
{ 
    if (m_dwROTCookie != 0)
        return E_FAIL;

    //Check whether there already is an instance of the Object
    IUnknownPtr pUnk = NULL;
    if (GetActiveObject(CLSID_Object, NULL, &pUnk) == S_OK)
    {
        TRACE_WARNING("An instance of Object already exists in the current context");
        return S_OK;
    }
    void* pUnkVoid = NULL;
    HRESULT hr = QueryInterface(IID_IUnknown, &pUnkVoid);

    if (SUCCEEDED(hr)
    {
        pUnk = reinterpret_cast<IUnknown*>(pUnkVoid);
        hr = RegisterActiveObject(pUnk, CLSID_Object, ACTIVEOBJECT_WEAK, m_dwROTCookie);        
        if (FAILED(hr))
            return hr;

        hr = CoLockObjectExternal(pUnk, TRUE, TRUE);
        pUnk = NULL;
    }
    ATLASSERT(m_dwRef == 2);

    return hr;

しかし今、私のアプリケーションには、この COM オブジェクトからのメモリ リークがあります。

4

2 に答える 2

0

GetActiveObject()を呼び出し、成功するとオブジェクトの参照カウントをインクリメントしますが、後でQueryInterface()呼び出して参照カウントをデクリメントしないため、メモリ リークが発生している可能性があります。Release()

于 2010-03-09T07:41:04.367 に答える
0

うーん、void* を pUnk に割り当てるのではなく、次を使用する必要があると思います。

pUnk.Attach(reinterpret_cast<IUnknown*>(pUnkVoid));
于 2010-03-08T15:51:10.780 に答える