为什么示例代码访问IUnknown中已删除的内存? [英] Why does example code access deleted memory in 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屋!