呵呵,一个引用计数的小问题,请达人明示

springlie 2009-07-01 10:54:53
初初接触DirectShow,稍微明白点,又不明白,还请达人指教。

在一个视频源捕获的类中,有一个方法:


void CVideoCaptureFilter::SetResolution(void)
{
if (mDevice && mFilter)
{
IAMAnalogVideoDecoder * pDecoder = GetAnalogDecoder();
if (pDecoder) pDecoder->put_TVFormat(mDevice->GetVideoResolution());
}
}


其中当中的GetAnalogDecoder()如下:

IAMAnalogVideoDecoder * CVideoCaptureFilter::GetAnalogDecoder(void)
{
IAMAnalogVideoDecoder * pDecoder = NULL;
if (mFilter)
{
mFilter->QueryInterface(IID_IAMAnalogVideoDecoder, (void**)&pDecoder);
if (pDecoder)
{
// keep no outstanding reference count

pDecoder->Release();
return pDecoder;
}
}
return NULL;
}


SetResolution方法中用到了由GetAnalogDecoder方法得到的decoder。
而在GetAnalogDecoder中,也明确释放了由QueryInterface累加进来的outstanding reference count。
但是,这样返回的pDecoder不是为NULL了吗?

IUnknown::QueryInterface:
Remarks:
If the application does not need to use the interface retrieved by a call to this method,
it must call the IUnknown::Release method for that interface to free it.
The IUnknown::QueryInterface method makes it possible to extend objects without interfering with functionality.


显然得到的接口在另外的函数中用到了。以我的认识,应该不在GetAnalogDecoder中release,而是在SetResolution中用完再release,这样也符合提供接口者count++,使用接口者count--的原则。

但是,上面那两段代码,为何在实际中竟然可以用?是我对所谓的 outstanding reference count 的概念理解错误了吗?
请达人明示,谢谢!
...全文
131 12 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
springlie 2009-07-03
  • 打赏
  • 举报
回复
另外,对应于上例:
IUnknown::QueryInterface
Determines whether the object supports a particular COM interface. If it does, the system increases the object's reference count, and the application can use that interface immediately.

这里的object应该指的就是mFilter(准确说应该是*mFilter?)
If the application does not need to use the interface retrieved by a call to this method, it must call the IUnknown::Release method for that interface to free it.

这里的interface应该就是得到的pDecoder。

MSDN里确实隐晦说明了,二者是维护了同一引用计数。
springlie 2009-07-03
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 bottlebox 的回复:]
引用 8 楼 springlie 的回复:
我的理解是,增加的引用计数是加在返回的接口上的,而不是调用QueryInterface的接口上。
也就是说,我认为,即使mFilter不存在了,只要…

如果你找一个Filter的源代码,可以做下测试,使用new的方式建立该Filter,当调用该Filter的QueryInterface()函数时,
一般情况下,你会发现是该Filter的引用计数加一。
实际上在dshow中的Filter的一个接口指针如上面的pDecoder执行Addref()时,调用到的…
[/Quote]

您的说法是正确的,看来我走入了一个很大的误区。
下面的代码说明了我以前的错误所在:

#include <Strmif.h>
#include <assert.h>
#include <dshow.h>

#define null 0

int main(int argc, char *argv[])
{

IGraphBuilder * mFilter = NULL;
IBaseFilter * pDecoder = NULL;

CoInitialize(NULL);

assert(SUCCEEDED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_ALL, IID_IGraphBuilder, (void **)&mFilter)));

assert(2 == mFilter->AddRef());
assert(3 == mFilter->AddRef());
assert(2 == mFilter->Release());

assert(SUCCEEDED(mFilter->QueryInterface(IID_IBasicVideo, (void**)&pDecoder)));

assert(4 == mFilter->AddRef());
assert(5 == pDecoder->AddRef());
assert(6 == mFilter->AddRef());
assert(5 == mFilter->Release());
assert(4 == pDecoder->Release());
assert(3 == mFilter->Release());
assert(2 == mFilter->Release());
assert(1 == pDecoder->Release());
assert(null == pDecoder->Release());
//assert(1 == mFilter->AddRef()); ///< Failed

getchar();
}


谢谢,明天结贴,如果没人还有其他说法的话。
顺便问一下,除了QueryInterface以外,还有什么其他方法也能导致两个接口是处于“同生共死”的状态?
WaistCoat17 2009-07-02
  • 打赏
  • 举报
回复
mFilter->QueryInterface(IID_IAMAnalogVideoDecoder, (void**)&pDecoder);
if (pDecoder)
{
// keep no outstanding reference count

pDecoder->Release(); // Release完pDecoder就无效了
return pDecoder;
}
瓶盒 2009-07-02
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 springlie 的回复:]
我的理解是,增加的引用计数是加在返回的接口上的,而不是调用QueryInterface的接口上。
也就是说,我认为,即使mFilter不存在了,只要…
[/Quote]
如果你找一个Filter的源代码,可以做下测试,使用new的方式建立该Filter,当调用该Filter的QueryInterface()函数时,
一般情况下,你会发现是该Filter的引用计数加一。
实际上在dshow中的Filter的一个接口指针如上面的pDecoder执行Addref()时,调用到的最初代码都是GetOwner()->AddRef();
GetOwner()得到的就是所有者的IUknown指针m_pUnknown,这个指针是在对象初始化时赋的值,一般情况下,一个Filter中的输入输出pin对象及Filter的其它对外接口的所有者都是这同一个m_pUnknown的值。
spring203 2009-07-02
  • 打赏
  • 举报
回复
mFilter->QueryInterface(IID_IAMAnalogVideoDecoder, (void**)&pDecoder);
楼主,首先个人意见,你这样用不合适, 简单分析下,由于mFilter是成员变量,对于IID_IAMAnalogVideoDecoder类型来说,每QueryInterface一次就是引用计数+1,Release-1,对应当计数为0时才创建或者释放其值,所以即使你释放了也并不一定代表不能用了,因为计数不一定是0了。
ArcRain 2009-07-02
  • 打赏
  • 举报
回复
QueryInterface如果拿到了接口指针,会在QueryInterface里面进行AddRef,所以GetAnalogDecoder返回前进行Release,保持了引用计数不变
ArcRain 2009-07-02
  • 打赏
  • 举报
回复
你的想法我觉得是对的.

不过,从这两个函数上看它的写法也不错,按照COM接口指针引用的规则,一般入口时AddRef,出口前Release,保持引用计数不变
GetAnalogDecoder完成了这么个规则,所以SetResolution里拿到接口指针时也不需要AddRef,Release了

GetAnalogDecoder拿到接口指针后并没有进行使用,所以返回接口指针前把它Release减掉,保持引用计数不变,
由于IAMAnalogVideoDecoder的实例存在,所以Release时只是保持引用计数不变,接口对象并没有释放
这样外层调用者获取到接口指针后就可以直接进行使用了,不需要再进行AddRef, Release
fengrx 2009-07-02
  • 打赏
  • 举报
回复
引用计数一般是封装好的,在程序中使用完时直接释放对象就可以了,Release就是释放对象,不是引用减1。
blueink_200451 2009-07-02
  • 打赏
  • 举报
回复
楼主问问题看得出来好辛苦,一边寻求答案还在一边总结。
我说不上什么。不过帮顶。辛苦了。
楼主也很出力,这是一个精品贴。收藏了。
springlie 2009-07-02
  • 打赏
  • 举报
回复
to 7L:

您的发言很有趣,但是我不能同意您的观点。

MSDN:

IUnknown::QueryInterface:
Remarks:
If the application does not need to use the interface retrieved by a call to this method,
it must call the IUnknown::Release method for that interface to free it.

我的理解是,增加的引用计数是加在返回的接口上的,而不是调用QueryInterface的接口上。
也就是说,我认为,即使mFilter不存在了,只要我们保留pDecoder的引用计数,pDecoder还可以使用。
我是初学,不太明白您提到的“聚合”的意思,但我认为IAMAnalogVideoDecoder和IFilter(这是mFilter的接口)可能在层次上有区别,一个包含另外一个,
但是在本质上是没有区别的。IFilter只是找到IAMAnalogVideoDecoder接口的一种手段。脱离了IFilter,IAMAnalogVideoDecoder照样可以用。

我是以一个门外汉的想法写出来,大家多多指正我,谢谢。
瓶盒 2009-07-02
  • 打赏
  • 举报
回复
对于为什么Release()后仍然可用,我的理解是IAMAnalogVideoDecoder接口对象是聚合在mFilter中的,mFIlter是所有者。
mFilter->QueryInterface(IID_IAMAnalogVideoDecoder, (void**)&pDecoder);
这句执行后,最终是mFIlter的引用计数加一,而 pDecoder->Release();也是mFIlter的引用计数减一,pDecoder指向的是一个虚函数表,只要mFilter没释放,这里面的内空是不变的。
不过这样的调用确实是不合规范。
springlie 2009-07-02
  • 打赏
  • 举报
回复
谢谢各位耐心回答,我是提问者。以下是我根据各位的发言的启发后的一些思考,写出来,有什么错误请大家指点。我现在还是倾向于将pDecoder->Release()放在SetResolution。请大家指点。

to 1楼:

确实,如您所言,在您注释的release处,已经减去了由QueryInterface潜在加上的引用计数(顺便纠正下2L,release是引用计数-1,而不是释放)。此时的IAMAnalogVideoDecode接口的引用计数并没有发生变化,但这并不是说得到的pDecoder无效,因为无效的情况只有一种:指针所指的地方被释放掉了。而接口被释放掉的唯一原因就是它的引用计数为0。我们的CVideoCaptureFilter函数保证了引用计数不变,但并没有保证引用计数为0,因为很可能在程序的其他地方也许还保留有IAMAnalogVideoDecode接口的引用计数——如果真的引用计数不为0,那pDecoder就还能用,还可以取道它想要取到的东西。但是这样是不安全的,因为本身并不保留引用计数,一旦其他地方的引用计数不再保留,使得引用计数归0,接口自动销毁,而pDecoder也会悄无声息变成悬浮指针。

to 2楼:

com接口不需要释放,当检测其引用计数为0时会自动销毁,这一切是系统在做。

to 3楼:

注意到了您的精彩发言。如上所说,我认为您所说的那种写法是不安全的(除非您确信在用pDecoder的时候,肯定有其他代码保证其引用计数不为0)。这两段代码,在实际情况中确实可行,我认为就是我说的这种原因(虽然我还没找到是其他什么地方保留了引用计数而没有是释放)。
因为多余的引用计数总比悬浮指针还可以接受,至少程序暂时能跑起来。所以我见过在一些COM程序中,为了保证不出现悬浮指针,在代码段的开始就给所有即将用到的接口增加一个引用计数,而在代码段的结尾再都减去一个引用计数。所以我觉得应该是保留pDecoder的引用计数一直到SetResolution中使用pDecoder后。

to 4楼:

请看to 3楼

to 5楼:

谢谢您的回答。我认为原则是这样:由某个实例保留的引用计数,就应该由这个实例最终再释放掉。如果每个实例都是严格按照这样的规定,那么所有的引用计数都会被正确地+1,也都会被正确地-1,最终接口会顺利地销毁。这里的方法里的release只是用来释放当前对象保留的这个接口的引用计数,至于释放后引用计数是不是为0了?接口被销毁没有,这都不是我们所关心的。单个的做好了,最后总算账,肯定是对的。

3,248

社区成员

发帖
与我相关
我的任务
社区描述
ATL,Active Template Library活动(动态)模板库,是一种微软程序库,支持利用C++语言编写ASP代码以及其它ActiveX程序。
社区管理员
  • ATL/ActiveX/COM社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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