IUnknown ** 指针的指针 释放问题

moongoose_rmxming 2009-10-22 05:06:25
各位朋友:

我初学写COM组件,有个程序需要用DLL来传递COM对象,传递对象的接口被定义为IUnknown ** 指向指针的指针。我写了如下代码,但Release COM对象却出现了错误。关键代码如下(其他代码我已经过滤):

int32 Read_Xml_File(char * XML_Path, LStrHandle Out_Put_File, IUnknown ** lvIUnknown)
{
CComPtr<MSXML::IXMLDOMDocument> spDoc;

HRESULT hr;
IUnknown * pIUnknown;

//一般的IUnknown 指针 pIUnknown
hr = spDoc->QueryInterface(IID_IXMLDOMDocument, (void**)&pIUnknown);

//指向IUnknown 对象的指针 lvIUnknown
hr = pIUnknown->QueryInterface(IID_IXMLDOMDocument,(void**)(&(*lvIUnknown)));

//pIUnknown Release没问题
pIUnknown->Release();

//这里释放就导致程序崩溃了
(*lvIUnknown)->Release();
}

我应该怎么做才能确保Release成功?
...全文
516 34 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
34 条回复
切换为时间正序
请发表友善的回复…
发表回复
moongoose_rmxming 2009-10-24
  • 打赏
  • 举报
回复
[Quote=引用 34 楼 icosagon 的回复:]
我不是告诉你了吗!
//这句是为了返回给lvIUnknown双重指针的
//使用lvIUnknown的人要自己调用release,否则内存泄漏。
*lvIUnknown = spDoc.p;
spDoc.p = NULL;

这样就不会内存泄漏
int32 ReleasePointer(IUnknown ** vIUnknown)
{
(*vIUnknown)->Release();
::CoUninitialize();
return 0;
}

但是用vIUnknown指针的人每次调用QueryInterface去得到其它接口指针时,得到的接口指针必须要调用
Release,
而且看你的代码风格,也不是很好,内存管理很乱
[/Quote]

int32 ReleasePointer(IUnknown ** vIUnknown)
{
(*vIUnknown)->Release();
*lvIUnknown = 0;
::CoUninitialize();
return 0;
}
还得再加个零才能把内存地址清空。
多谢你对我提的建议,代码我会去好好改善:)
赵4老师 2009-10-23
  • 打赏
  • 举报
回复
《COM本质论》
icosagon 2009-10-23
  • 打赏
  • 举报
回复
把你代码全贴出来,我给你的不会有内存泄漏
moongoose_rmxming 2009-10-23
  • 打赏
  • 举报
回复
[Quote=引用 28 楼 adlay 的回复:]
如果太搅了暴力点的办法是直接检查 Release 的返回值,反复调用,直到返回 0 为止.
不过最好还是仔细跟踪吧,应该是某处多加了引用.
[/Quote]

好的来点暴力的方法看看
moongoose_rmxming 2009-10-23
  • 打赏
  • 举报
回复
[Quote=引用 26 楼 adlay 的回复:]
如果传进来的 vIUnknown 是 vIUnknown = new IUnknown*; 出来的,在不用的时候加一句 delete vIUnknown; 就可以了.
如果是通过 IUnknown* x; 去 &x 传进来的则不需要释放了.
[/Quote]

我的这个 IUnknown ** vIUnknown 是个双重指针,没有经过初始化。
是直接 *lvIUnknown = spDoc.p; 把智能指针赋值给它的。
就是在反复调用时会出现内存泄漏,加了Release也不行。
www_adintr_com 2009-10-23
  • 打赏
  • 举报
回复
如果太搅了暴力点的办法是直接检查 Release 的返回值,反复调用,直到返回 0 为止.
不过最好还是仔细跟踪吧,应该是某处多加了引用.
moongoose_rmxming 2009-10-23
  • 打赏
  • 举报
回复
[Quote=引用 26 楼 adlay 的回复:]
如果传进来的 vIUnknown 是 vIUnknown = new IUnknown*; 出来的,在不用的时候加一句 delete vIUnknown; 就可以了.
如果是通过 IUnknown* x; 去 &x 传进来的则不需要释放了.
[/Quote]

我的这个 IUnknown ** vIUnknown 是个双重指针,没有经过初始化。
是直接 *lvIUnknown = spDoc.p; 把智能指针赋值给它的。
就是在反复调用时会出现内存泄漏,加了Release也不行。
www_adintr_com 2009-10-23
  • 打赏
  • 举报
回复
如果传进来的 vIUnknown 是 vIUnknown = new IUnknown*; 出来的,在不用的时候加一句 delete vIUnknown; 就可以了.
如果是通过 IUnknown* x; 去 &x 传进来的则不需要释放了.
moongoose_rmxming 2009-10-23
  • 打赏
  • 举报
回复

[Quote=引用 21 楼 icosagon 的回复:]
你用了针对COM的智能指针,可以不用在调查询接口的函数了,而且你智能指针和普通COM指针混用,很容易搞错。

C/C++ codeIUnknown* pIUnknown;//lvIUnknown = new (IUnknown*); COM对象和普通对象还是不同的
ErrState= Err_NoError;//Create XML Document Object
::CoInitialize(NULL);
CComPtr<MSXML::IXMLDOMDocument> spDoc;
HRESULT hr;
hr= spDoc.CoCreateInstance(__uuidof(MSXML::DOMDocument));//Load XML File From Pathbool bSuccess=true;
bSuccess= Load_XML(XML_Path, spDoc);//不用查IXMLDOMDocument的接口了,智能指针CoCreateInstance的时候已经有了,作为模板//hr = spDoc->QueryInterface(IID_IXMLDOMDocument, (void**)&pIUnknown);//AddRef无需开发者关心,如果不用智能指针,需要调用release,否则也无需关心//这四条代码是我们讨论的地方//*lvIUnknown = pIUnknown;//(*lvIUnknown)->AddRef();//(*lvIUnknown)->Release();//pIUnknown->Release();
CComBSTR XML_File;if(SUCCEEDED(hr))
{
hr= spDoc->get_xml(&XML_File);
}


File_Content= _com_util::ConvertBSTRToString(XML_File.m_str);
iLength= strlen(File_Content);//这句是为了返回给lvIUnknown双重指针的//使用lvIUnknown的人要自己调用release,否则内存泄漏。*lvIUnknown= spDoc.p;
spDoc.p= NULL;//可有可无,但在智能指针和普通指针混用情况下要小心//spDoc.Release();::CoUninitialize();
[/Quote]

像这样Release vIUnknown 好像不行,还是会内存泄漏
不过程序已经能正常的传递地址了。
我该怎么才能释放这个 IUnknown **双重指针呢??

int32 Test(IUnknown ** vIUnknown)
{

(*vIUnknown)->Release();
*vIUnknown = NULL;
::CoUninitialize();
return 0;
}
icosagon 2009-10-23
  • 打赏
  • 举报
回复
我不是告诉你了吗!
//这句是为了返回给lvIUnknown双重指针的
//使用lvIUnknown的人要自己调用release,否则内存泄漏。
*lvIUnknown = spDoc.p;
spDoc.p = NULL;

这样就不会内存泄漏
int32 ReleasePointer(IUnknown ** vIUnknown)
{
(*vIUnknown)->Release();
::CoUninitialize();
return 0;
}

但是用vIUnknown指针的人每次调用QueryInterface去得到其它接口指针时,得到的接口指针必须要调用
Release,
而且看你的代码风格,也不是很好,内存管理很乱
moongoose_rmxming 2009-10-23
  • 打赏
  • 举报
回复
[Quote=引用 31 楼 icosagon 的回复:]
把你代码全贴出来,我给你的不会有内存泄漏
[/Quote]

麻烦icosagon了:
这是一个DLL接口,用于创建一个IXMLDOMDocument 对象,并把对象传递给IUnknown ** lvIUnknown
我按照您给的方法改了,的确不会出现内存泄漏。
但是把它传递给 lvIUnknown 双重指针后就会出现内存泄漏,请看下面的DLL接口:
int32 Read_Xml_File(char * XML_Path, LStrHandle Out_Put_File, IUnknown ** lvIUnknown)
{
char * File_Content;
char * ErrDescript;

try
{
//Create XML Document Object

ErrState = Err_NoError;
::CoInitialize(NULL);
CComPtr<MSXML::IXMLDOMDocument> spDoc;
HRESULT hr;
hr = spDoc.CoCreateInstance(__uuidof(MSXML::DOMDocument));

//Load XML File From Path

bool bSuccess = true;
bSuccess = Load_XML(XML_Path, spDoc);

CComBSTR XML_File;
hr = spDoc->get_xml(&XML_File);

*lvIUnknown = spDoc.p;
spDoc.p = NULL;

File_Content = _com_util::ConvertBSTRToString(XML_File.m_str);
iLength = strlen(File_Content);


//::CoUninitialize();

if (bSuccess == false)
{
ErrDescript = "Invalid XML File,Please Check Your XML File!";
throw XML_File;
}

}
catch (...)
{
File_Content = ErrDescript;
iLength = strlen(File_Content);
ErrState = Err_LoadError;
}

Get_LV_String(File_Content, Out_Put_File, iLength);
free(Out_Put_File);
return ErrState;
}

这是释放IUnknown ** lvIUnknown的DLL接口:
这里就释放不掉内存,程序能正常运行。
我是不是智能指针没有释放对,但不知道该怎么去释放这个指针的指针
int32 ReleasePointer(IUnknown ** vIUnknown)
{

(*vIUnknown) = NULL;
::CoUninitialize();
return 0;
}
如果我不用这个IUnknown ** 这个双重是不会出现内存泄漏的问题。
但问题是其他编程语言要引用这个DLL所以只能通过双重指针的方式传递过去。一旦用了双重指针就没办法去清空内存了。头疼......
moongoose_rmxming 2009-10-22
  • 打赏
  • 举报
回复
谢谢各位朋友的提醒,我不胜感激,我会再好好的修改我的程序的,直到我成功为止。
特别谢谢icosagon adlay 以及 bourbaki.
我再加把劲好好看看。
icosagon 2009-10-22
  • 打赏
  • 举报
回复
对了还有个问题,::CoUninitialize(); 这句也去掉,否则COM支持库都没了,COM也没法用了
arong1234 2009-10-22
  • 打赏
  • 举报
回复
我怀疑两点:
1。 智能指针的出现弄乱了引用计数导致对象被多释放了一次
2。释放操作在CoUninitialize之后,导致COM库状态不正确
icosagon 2009-10-22
  • 打赏
  • 举报
回复
你用了针对COM的智能指针,可以不用在调查询接口的函数了,而且你智能指针和普通COM指针混用,很容易搞错。

IUnknown * pIUnknown;


//lvIUnknown = new (IUnknown*); COM对象和普通对象还是不同的

ErrState = Err_NoError;

//Create XML Document Object

::CoInitialize(NULL);
CComPtr <MSXML::IXMLDOMDocument> spDoc;
HRESULT hr;
hr = spDoc.CoCreateInstance(__uuidof(MSXML::DOMDocument));

//Load XML File From Path

bool bSuccess = true;
bSuccess = Load_XML(XML_Path, spDoc);

//不用查IXMLDOMDocument的接口了,智能指针CoCreateInstance的时候已经有了,作为模板

//hr = spDoc->QueryInterface(IID_IXMLDOMDocument, (void**)&pIUnknown);

//AddRef无需开发者关心,如果不用智能指针,需要调用release,否则也无需关心
//这四条代码是我们讨论的地方
//*lvIUnknown = pIUnknown;
//(*lvIUnknown)->AddRef();
//(*lvIUnknown)->Release();
//pIUnknown->Release();

CComBSTR XML_File;
if(SUCCEEDED(hr))
{
hr = spDoc->get_xml(&XML_File);
}


File_Content = _com_util::ConvertBSTRToString(XML_File.m_str);
iLength = strlen(File_Content);

//这句是为了返回给lvIUnknown双重指针的
//使用lvIUnknown的人要自己调用release,否则内存泄漏。
*lvIUnknown = spDoc.p;
spDoc.p = NULL;

//可有可无,但在智能指针和普通指针混用情况下要小心
//spDoc.Release();
::CoUninitialize();
www_adintr_com 2009-10-22
  • 打赏
  • 举报
回复
可以这样 typedef IUnknown* UnknwonPtr 之后替换掉一个指针就好理解点了:

int32 Read_Xml_File(... , UnknownPtr* pUnknownPtr)
{
...
}

使用这个函数要么:
UnknownPtr x;
Read_Xml_File(..., &x);
或者:
UnknownPtr* px = new UnknownPtr;
Read_Xml_File(..., px);

但是如果:
UnknownPtr* px;
Read_Xml_File(..., px) // px 未初始化就使用,行为未定义。

如果是:
UnknownPtr* px;
Read_Xml_File(..., px)
{
px = new UnknownPtr; // 后面使用的已经初始化了,不会报错,但是修改的是参数的拷贝,不能传递回去的。 这时只能通过 return px; 才能传递回去。
}

如果还有障碍,把 UnknownPtr 换成是一个 int 再来看,应该可以看得明白了。
moongoose_rmxming 2009-10-22
  • 打赏
  • 举报
回复
我想试验下,我的Release能不能把内存清空,不造成内存泄露。最担心内存泄露

[Quote=引用 17 楼 bourbaki 的回复:]
你先addref,立马又release,这是没有意义的动作
[/Quote]
moongoose_rmxming 2009-10-22
  • 打赏
  • 举报
回复
谢谢,我再去了解一下。我估计是不是我释放了对象,那指向对象的指针就变成了野指针,胡乱的指向别的内存地址去了?


[Quote=引用 15 楼 bourbaki 的回复:]
addref一般实现在QueryInterface里面。release并不是真的释放内存,而是把reference count减去1,如果这个count等于0,才真的释放内存。

lz可以看看http://www.codeproject.com/KB/COM/comintro2.aspx,讲得很清楚
[/Quote]
bourbaki 2009-10-22
  • 打赏
  • 举报
回复
你先addref,立马又release,这是没有意义的动作
moongoose_rmxming 2009-10-22
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 adlay 的回复:]
引用 11 楼 renstarone 的回复:
谢谢adlay,但是我改成以下这样:
int32 Read_Xml_File(char * XML_Path, LStrHandle Out_Put_File, IUnknown ** lvIUnknown)
{
  lvIUnknown = new (IUnknown*);
  CComPtr <MSXML::IXMLDOMDocument> spDoc;
 
  HRESULT hr;
  IUnknown * pIUnknown;

      //一般的IUnknown 指针 pIUnknown
  hr = spDoc->QueryInterface(IID_IXMLDOMDocument, (void**)&pIUnknown);

  *lvIUnknown = pIUnknown;
  (*lvIUnknown)->AddRef();
  (*lvIUnknown)->Release();
}
的确程序不会崩溃,内存也不会泄漏,可我的输出的地址就全是:0x00000000了
我需要这个IUnknown ** lvIUnknown给其他程序调用,如果全是0x00000000就没办法调用啦。


你不应该在这个函数里面  new IUnknown* 啊,要在调用的地方 new 好之后传进来:
你可以这样调用:
IUnknown* p;
Read_Xml_File(...., &p);
或者:
IUnknown** pp = new IUnknown*;
Read_Xml_File(...., pp);

如果这样
IUnknown** pp;
Read_Xml_File(...., pp);
就有问题了。

这个还是对指针和 C 按值传递参数的理解了,还不好说清楚,慢慢想想吧。


[/Quote]
谢谢你,我再看看去。有点头晕。为了这个问题搞了一个下午。
加载更多回复(15)

65,186

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧