Search code examples
windowscomatlsmart-pointerssapi

Do I have to call Release() method on CComPtr objects?


I'm working with SAPI5 API for processing text to speech. If I simplify my code looks like this (I removed error checking to simplify it as much as possible):

int main() {
    CoInitialize(NULL);
    CComPtr<ISpVoice> spVoice;
    spVoice.CoCreateInstance(CLSID_SpVoice);
    ...
    CoUninitialize();
    return 0;
}

For some weird reason my code crashes if I don't call spVoice.Release(). So the code above crashes, but this code works nicely:

int main() {
    CoInitialize(NULL);
    CComPtr<ISpVoice> spVoice;
    spVoice.CoCreateInstance(CLSID_SpVoice);
    ...
    spVoice.Release();
    CoUninitialize();
    return 0;
}

Doesn't CComPtr release the underlying object automatically as it goes out of scope?

I looked at the implementation of CComPtr and it does call Release in the destructor itself.

So I'm wondering what could have gone wrong and why is it that if I call Release myself, my code doesn't crash. But if I don't call Release then it crashes.


Solution

  • CComPtr's destructor will call Release. However, it does that when the object falls out of scope. In your above code, this is just before main returns, which is after the call to CoUninitialize.

    The following code is more correct, and guarantees the destructor runs prior to CoUninitialize.

    int main() {
        CoInitialize(NULL);
        { // Begin scope
            CComPtr<ISpVoice> spVoice;
            spVoice.CoCreateInstance(CLSID_SpVoice);
            ...
        } / End scope, spVoice's destructor runs.
        CoUninitialize();
        return 0;
    }
    

    An alternative is to create an RAII wrapper around CoInitialize/CoUninitialize. If this new object is declared prior to spVoice, it's destructor will run after spVoice's destructor, guaranteeing correct order.