问题
Quite a number of examples when using interfaces such as IUnknown, in this example IDocHostUIHandler but it doesn't really matter - use code similar to this:
 class TDocHostUIHandlerImpl : public IDocHostUIHandler
 {
 private:
    ULONG RefCount;
    public:      
    TDocHostUIHandlerImpl():RefCount(0){ }
    // IUnknown Method
    HRESULT __stdcall QueryInterface(REFIID riid, void **ppv) {
        if (IsEqualIID(riid,IID_IUnknown))
            {
            *ppv = static_cast<IUnknown*>(this);
            return S_OK;
            }
        else if (IsEqualIID(riid, IID_IDocHostUIHandler)) {
            *ppv = static_cast<IDocHostUIHandler*>(this);
            return S_OK;
            }
        else {
            *ppv = NULL;
            return E_NOINTERFACE;
            }
        }
    ULONG   __stdcall AddRef() {
        InterlockedIncrement((long*)&RefCount);
        return RefCount;
        }
    ULONG   __stdcall Release() {
        if (InterlockedDecrement((long*)&RefCount) == 0) delete this;
        return RefCount;
        }
My problem here is with the Release() method which deletes the interface implementation using delete this but immediately after that does return RefCount which no longer refers to a valid object in memory (it accesses deleted memory).
I would assume that it should be something like
    ULONG   __stdcall Release() {
        if (InterlockedDecrement((long*)&RefCount) == 0) { delete this; return 0; }
        return RefCount;
        }
Which also doesn't trigger resource leak tool which I use (Codeguard in C++ Builder). So why then so many examples use the first version, what am I missing here?
Or is it just the case that the "delete this" is called in another compiler like Visual Studio after the Release method ends?
A few examples:
https://www.codeproject.com/Articles/4758/How-to-customize-the-context-menus-of-a-WebBrowser
addref and release in IUnknown, what do they actually do?
https://bbs.csdn.net/topics/20135139
回答1:
Yes, you are correct that such examples are badly written. They need to be written more like you have described:
ULONG __stdcall Release()
{
    if (InterlockedDecrement((long*)&RefCount) == 0) {
        delete this;
        return 0;
    }
    return RefCount;
}
However, it is better for them to just return whatever result InterlockedDecrement returns.  As @RaymondChen pointed out in comments, this also addresses the issue of RefCount being decremented by another thread, potentially destroying this, before the return is reached, eg:
ULONG __stdcall Release()
{
    ULONG res = (ULONG) InterlockedDecrement((long*)&RefCount);
    if (res == 0) {
        delete this;
    }
    return res;
}
Same with the AddRef(), for that matter:
ULONG __stdcall AddRef()
{
    return (ULONG) InterlockedIncrement((long*)&RefCount);
}
On a side note, the QueryInterface() example you have shown is also written incorrectly, as it is not incrementing the RefCount when returning S_OK, eg:
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
{
    if (IsEqualIID(riid,IID_IUnknown)) {
        *ppv = static_cast<IUnknown*>(this);
        AddRef(); // <-- add this!
        return S_OK;
    }
    else if (IsEqualIID(riid, IID_IDocHostUIHandler)) {
        *ppv = static_cast<IDocHostUIHandler*>(this);
        AddRef(); // <-- add this!
        return S_OK;
    }
    else {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
}
Which can typically be written more easily like this instead:
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
{
    if (!ppv) {
        return E_POINTER;
    }
    if (IsEqualIID(riid, IID_IUnknown)) {
        *ppv = static_cast<IUnknown*>(this);
    }
    else if (IsEqualIID(riid, IID_IDocHostUIHandler)) {
        *ppv = static_cast<IDocHostUIHandler*>(this);
    }
    else {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    AddRef();
    return S_OK;
}
I've also seen it written like this, which accounts for bad cases when QueryInterface() is called on a NULL pointer:
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
{
    if (!ppv) {
        return E_POINTER;
    }
    if (IsEqualIID(riid, IID_IUnknown)) {
        *ppv = static_cast<IUnknown*>(this);
    }
    else if (IsEqualIID(riid, IID_IDocHostUIHandler)) {
        *ppv = static_cast<IDocHostUIHandler*>(this);
    }
    else {
        *ppv = NULL;
    }
    if (*ppv) {
        AddRef();
        return S_OK;
    }
    return E_NOINTERFACE;
}
来源:https://stackoverflow.com/questions/62885782/why-does-example-code-access-deleted-memory-in-iunknown