COM对象除了引用计数还有...

COM对象除了引用计数还有…

一.   背景:

VideoManager支持实时, 需要同时传入一组窗口的设备信息和StreamerID, 并且传入之后需要设置给相对应的VideoView. 所以在VideoManager实现了IDeviceInfo的COM对象, 包含三个成员分别是IVideoView* video_view, GUID streamer_id, IDeviceConfig* device_config.

C#的调用如下


var list_deviceInfo = new List<LiveDeviceInfo>();

foreach (var videoViewPort in videoViewPorts)

{

videoViewPort.PlayMode = VideoPlayMode.Live;

var videoViewWrapper = videoService.Get(videoViewPort);

if (videoViewWrapper != null)

{

videoViewWrapper.PlayMode = VideoPlayMode.Live;

}

var liveDevice = new LiveDeviceInfo();

liveDevice.streamer_id = videoViewPort.PlayingDevice.Id;

liveDevice.video_view = (VideoView)videoViewWrapper.VideoView;

liveDevice.device_info = videoViewPort.PlayingDevice;

list_deviceInfo.Add(liveDevice);

}

SyncVideoService.SetLiveConfig(Layout, list_deviceInfo.ToArray());

二.   现象:

调用的前两次都正常, 到第三次出现了崩溃. 而且每次都是如此. 崩溃的异常和堆栈在CLR中, 由于VS2010同时查看Manage和Native看到的堆栈信息不全, 所以使用Windbg的命令!dumpstack查看, 发现崩溃在clr中, 由于没有源代码很难捕捉到更多信息.


ChildEBP RetAddr  Caller, Callee

0019df28 6144d5ed clr!SafeAddRef+0x53

0019df3c 6144d625 clr!RCW::GetComIPForMethodTableFromCache+0x25f, calling clr!SafeAddRef

0019df4c 612e826c clr!ObjHeader::GetSyncBlock+0x33, calling clr!ObjHeader::PassiveGetSyncBlock

0019df70 612f1c0c clr!JIT_GetSharedGCThreadStaticBase+0x28, calling clr!GetThread

0019df88 6144d431 clr!RCW::GetComIPFromRCW+0x2d, calling clr!RCW::GetComIPForMethodTableFromCache

0019df98 61388bc9 clr!GetComIPFromObjectRef+0x1e4, calling clr!RCW::GetComIPFromRCW

0019dff8 6145510b clr!MarshalObjectToInterface+0x3a, calling clr!GetComIPFromObjectRef

0019e008 6145509f clr!StubHelpers::InterfaceMarshaler__ConvertToNative+0xd8, calling clr!MarshalObjectToInterface

0019e030 613738ad clr!StubHelpers::DemandPermission+0x133, calling clr!LazyMachStateCaptureState

0019e070 61455049 clr!StubHelpers::InterfaceMarshaler__ConvertToNative+0x73, calling clr!LazyMachStateCaptureState

三.   诊断

为了缩小问题的范围, 先尝试将C#中的调用多次重复操作, 发现问题发生在SyncVideoService.SetLiveConfig; 于是查看这一块的C++代码.


for (int i=low_bound; i<= high_bound; i++) {

LPUNKNOWN ptr_unknown = safearray_deviceinfos.GetAt(i);

if (FAILED(ptr_unknown->QueryInterface(IID_ILiveDeviceInfo, (void**)(&live_device_info)))) {

break;

}

VARIANT_BOOL rst;

IVideoView* video_view = NULL;

live_device_info->get_video_view(&video_view);

live_device_info->get_device_info(&device_config);

live_device_info->get_streamer_id(&streamer_id);

video_view->SetLiveVideoConfig(streamer_id, device_config, &rst);

}

继续增加C+这一块的重复调用, 终于发现get_video_view的重复导致的问题. 只需要对它反复调用三次就会崩溃.


IVideoView* video_view = NULL;

live_device_info->get_video_view(&video_view);

live_device_info->get_video_view(&video_view);

live_device_info->get_video_view(&video_view);

于是观察get_video_view, 它的内部实现很简单, 只是通过IVideoView**返回一个指针值. 在内部下断点跟踪第三次, 内部复制依然正常, 可返回到调用方就报异常, 并且对象为NULL.


STDMETHODIMP CLiveDeviceInfo::get_video_view(IVideoView** pVal)

{

*pVal= i_video_view_;

return S_OK;

}

在c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\atlmfc\include\atlcom.h

ULONG InternalAddRef();

ULONG InternalRelease();

代码下断点查看这个对象的引用值, 发现引用值没有发生额外的变化. 并且m_dwRef数值在8~10之间变化, 并没有被减到0.  在该COM对象的FinalRelease()下断点, 也没有断下来.

查看了一些关于COM对象引用计数的文章, 了解到对于从某接口返回COM对象之前, 需要调用AddRef(), 或QueryInterface()来为返回的对象增加引用计数. 在使用完该对象之后, 使用Release()释放引用.

修改如下:


STDMETHODIMP CLiveDeviceInfo::get_video_view(IVideoView** pVal)

{

i_video_view_->QueryInterface(IID_IVideoView, (void**)pVal);

return S_OK;

}


live_device_info->get_video_view(&video_view);

video_view->Release();

经过测验, 问题得到解决.

四.   疑问:

进入atlcom.h内部查看引用计数没有任何问题. 可关于引用计数的接口调用却可以解决这个问题. 所以, 推测除了引用计数, 应该还有其它东西在管理这COM对象.

于是, 在崩溃前一刻下断点开启全部exception, 抓取到一个异常, 堆栈如下, 查看到CStdMarshal::MarshalObjRef, 这是报异常的第一刻, 这一刻意味着返回COM对象的中间过程可能要经过一些对COM对象管理对象的修改. 对CStdMarshal::MarshalObjRef这个windows内部函数进行查找, 可没有找到相关的有效的相关信息.(如果谁知道这里面包含什么, 请告知我)


Current frame: KERNELBASE!RaiseException+0x58

ChildEBP RetAddr  Caller, Callee

004ecc10 761fc41f KERNELBASE!RaiseException+0x58, calling ntdll!RtlRaiseException

004ecc34 77cfe023 ntdll!RtlFreeHeap+0x105, calling ntdll!RtlpLowFragHeapFree

004ecc4c 76f6f18c ole32!operator delete+0x16, calling ntdll!RtlFreeHeap

004ecc58 75b35c93 rpcrt4!RpcpRaiseException+0x7b, calling kernel32!RaiseExceptionStub

004ecc74 76f94387 ole32!CStdMarshal::MarshalObjRef+0x11e, calling rpcrt4!RpcRaiseException

004eccb8 75b31cf1 rpcrt4!NdrpPointerMarshall+0x90

004eccdc 75b26b24 rpcrt4!NdrPointerMarshall+0x30, calling rpcrt4!NdrpPointerMarshall

004ecd18 75b26b24 rpcrt4!NdrPointerMarshall+0x30, calling rpcrt4!NdrpPointerMarshall

004ecd5c 75bc06b8 rpcrt4!NdrStubCall2+0x402, calling rpcrt4!NdrpServerMarshal

004ecd8c 77d340b3 ntdll!RtlpFindGuidInSection+0xac, calling ntdll!__security_check_cookie

004ecdb8 76f88e72 ole32!NdrpOleAllocate

五.   结论:

将COM对象返回给外部使用时, 一定要使用AddRef(), 或QueryInterface()接口, 确保传递前对COM对象相关底层管理对象都有设置好.返回的对象在使用完之后, 调用Release(), 防止泄露.

即使明知道这一次返回的指针肯定在该COM对象的生命周期内, 也一定要记得调用, 否则就会像我一样花一整天的时间定位崩溃.

时间: 2024-11-03 22:03:10

COM对象除了引用计数还有...的相关文章

Andorid Binder进程间通信---Binder本地对象,实体对象,引用对象,代理对象的引用计数

本文参考<Android系统源代码情景分析>,作者罗升阳. 一.Binder库(libbinder)代码: ~/Android/frameworks/base/libs/binder ----BpBinder.cpp ----Parcel.cpp ----ProcessState.cpp ----Binder.cpp ----IInterface.cpp ----IPCThreadState.cpp ----IServiceManager.cpp ----Static.cpp ~/Androi

Python 对象的引用计数和拷贝

Python 对象的引用计数和拷贝 Python是一种面向对象的语言,包括变量.函数.类.模块等等一切皆对象. 在python中,每个对象有以下三个属性: 1.id,每个对象都有一个唯一的身份标识自己,可通过内建函数id(obj)查看. 2.type,对象的类型决定了该对象可以保存什么类型的值,可用内建函数type(obj)查看: 3.value,即对象的值. 下面是一个例子: >>> str = "hello world" >>> type(str

ARC下查看对象的引用计数

网上各种文章.问答给出的结论都是"ARC下无法打印对象的引用计数值". 确实,ARC禁止直接查看Objective-C对象的引用计数,但是Objective-C对象不是还可以转化为Core Foundation对象么?于是动手做了一个小实验: id obj = [[NSObject alloc]init]; printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(obj))); 需要注意

对象的引用计数

在OC中所有的引用类型都被声明为指针类型,指针类型在内存中占用若干地址空间,如果对象的内存使用不当,则会造成内存溢出,甚至程序崩溃的严重后果. 在其他语言中有内存自动回收的, 例如:Java语言,自动释放的. 手动管理内存:C++ , OC 2.0之前. 在OC 2.0之后,提供了内存自动个管理机制ARC,我们很少关系内存管理,但是了解内存内管是必要的! 在OC中如何判断一个对象何时分配内存,有何时释放内存呢? OC提供了对象的引用计数法则. 对于每个对象都有一个retainCount属性,当属

QPointer,QSharedPointer,QWeakPointer的区别与使用例子(QSharedPointer类似Delphi里的引用计数,是强引用,而QWeakPointer是弱引用,不影响原始对象的引用计数,相当于是在暗中观察对象,但保持联系,需要的时候就会出现)

QPointer is a template class that provides guarded pointers to Qt objects and behaves like a normal C++ pointer except that it is automatically set to 0 when the referenced object is destroyed and no "dangling pointers" are produced.QSharedPoint

你相信吗??Python把数字也当做对象!@@@对象,名称绑定,引用计数

本文学习自:http://blog.csdn.net/yockie/article/details/8474408 1.对象 Python中, 万物皆对象,包括12345等int常量.不信吗??用dir()命令看一看就知道 当然了,既然他们都叫做对象.那么肯定有共同点了!所有的对象都有下面的三个特征 * 都有唯一的标识码 id() * 都有确定的类型 * 有内容(或称为值) 一旦对象被创建,标识码就不能更改,对象类型也是不可更改的,内容可以改变(可变对象如dict.list .恒定对象如int.

手工引用计数中规则

使用设值方法为属性赋值时 assign.retain.copy三个特性的实现 self.property = newValue; assign的特性会是这样: property = newValue; retain特性会是这样 if (property!=0) { [property release]; property = [newValue retain]; } copy的特性会是这样 if (property!=0) { [property release]; property = [ne

引用计数

在引用计数中,每一个对象负责维护对象所有引用的计数值.当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减.当引用计数到零时,该对象就将释放占有的资源.中文名引用计数原 因程序调试原 理每一个对象负责维护对象所有引用的计数值类 型最直观的垃圾收集策略目录1简介2引用计数的使用? 原因? 规则? 接口? 调试? 优化? 规则1简介编辑 最直观的垃圾收集策略是引用计数.引用计数很简单,但是需要编译器的重要配合,并且增加了赋值函数 (mutator) 的开销(这个术语是针对用户

智能指针的实现--使用引用计数实现以及原理

一.智能指针 在C++语言编程时,当类中有指针成员时,一般有两种方式来管理指针成员:一是采用值型的方式管理,每个类对象都保留一份指针指向的对象的拷贝:另一种更优雅的方式是使用智能指针,从而实现指针指向的对象的共享. 智能指针(smart pointer)的一种通用实现技术是使用引用计数(reference count).智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针. 每次创建类的新对象时,初始化指针并将引用计数置为1:当对象作为另一对象的副本而创建时,拷贝