为什么示例代码访问IUnknown中已删除的内存? [英] Why does example code access deleted memory in IUnknown?

查看:66
本文介绍了为什么示例代码访问IUnknown中已删除的内存?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用诸如 IUnknown 之类的接口时,有许多示例,在此示例中为 IDocHostUIHandler ,但它没有真的很重要-使用类似于以下的代码:

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

我的问题是 Release()使用删除接口实现的方法删除此,但此后立即执行返回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).

我认为它应该类似于

    ULONG   __stdcall Release() {
        if (InterlockedDecrement((long*)&RefCount) == 0) { delete this; return 0; }
        return RefCount;
        }

这也不会触发我使用的资源泄漏工具(C ++ Builder中的Codeguard)。那么,为什么那么多示例使用第一个版本,我在这里错过了什么呢?

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?

还是删除此列表只是这种情况?在 Release 方法结束后在另一个类似Visual Studio的编译器中调用?

Or is it just the case that the "delete this" is called in another compiler like Visual Studio after the Release method ends?

一些示例:

https:/ /www.codeproject.com/Articles/4758/How-to-customize-the-context-menus-of-a-WebBrowser

addref并在IUnknown中释放,它们实际上是做什么的?

https://bbs.csdn.net/topics/20135139

推荐答案

是的,您正确地认为这样的例子写得不好。需要像您描述的那样编写它们:

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

但是,最好让他们只返回任何结果 InterlockedDecrement 返回。正如@RaymondChen在评论中指出的那样,这还解决了 RefCount 被另一个线程递减的问题,有可能破坏 this ,在达到返回之前,例如:

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

AddRef()相同,

ULONG __stdcall AddRef()
{
    return (ULONG) InterlockedIncrement((long*)&RefCount);
}




在侧面,您显示的QueryInterface()示例也写错了,因为返回 S_OK时,它不会增加 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;
    }
}

通常可以这样写:

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

我也看到过这样写的内容,当<$ c $在 NULL 指针上调用c> QueryInterface():

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

这篇关于为什么示例代码访问IUnknown中已删除的内存?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆