COM objects In addition to the reference count there are ...
I. Background:
Videomanager supports real-time, requires both device information and Streamerid to pass through a set of Windows, and it needs to be set to the corresponding Videoview after incoming. So the Videomanager implements the Ideviceinfo COM object, which consists of three members, namely ivideoview* Video_view, GUID streamer_id, ideviceconfig* device_config .
C # is called as follows
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 ()); |
Two. Phenomenon:
The first two calls are normal, and the third time a crash occurs. And it's always the same. Crash exceptions and stacks in the CLR, because VS2010 simultaneously view manage and native see the stack information incomplete, so use the WinDbg command!dumpstack view, find crashes in the CLR, because there is no source code that is difficult to capture more information.
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::P Assivegetsyncblock 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::D emandpermission+0x133, calling clr! Lazymachstatecapturestate 0019e070 61455049 clr! stubhelpers::interfacemarshaler__converttonative+0x73, calling clr! Lazymachstatecapturestate |
Three. Diagnostics
In order to narrow the scope of the problem, first try to repeat the call in C # multiple times, to find that the problem occurred in Syncvideoservice.setliveconfig; Then look at this piece of C + + code.
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); } |
Continue to increase the c+ this piece of repeated calls, and finally found that the repetition of get_video_view caused the problem. You just need to call it back and forth three times to crash.
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); |
So observing Get_video_view, its internal implementation is simple, just return a pointer value by ivideoview**. Inside the breakpoint is traced the third time, the internal copy is still normal, you can return to the caller to report the exception, and the object is null.
STDMETHODIMP Clivedeviceinfo::get_video_view (ivideoview** pVal) { *pval= I_video_view_; return S_OK; } |
In C:\Program Files (x86) \microsoft Visual Studio 10.0\vc\atlmfc\include\atlcom.h
ULONG InternalAddRef ();
ULONG InternalRelease ();
A breakpoint in the code looks at the reference value of this object and finds that the reference value does not have an additional change. And the M_dwref value varies between 8~10 and is not reduced to 0. The breakpoint under the COM object's FinalRelease () is not broken.
Review some articles about the COM object reference count, and learn that you need to call AddRef () or QueryInterface () to increase the reference count for the returned object before returning a COM object from an interface. After you have finished using the object, release the reference with release ().
Modify the following:
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 (); |
After a quiz, the problem is solved.
Four. Questions:
Enter atlcom.h to view the reference count without any problems. An interface call that can be used on a reference count solves this problem. So, presumably in addition to the reference count, there should be something else in the management of this COM object.
So, in the collapse before a breakpoint opened all exception, crawl to an exception, stack as follows, see to Cstdmarshal::marshalobjref, this is the first moment of the report exception, This moment means that the intermediate process of returning a COM object may have to undergo some modification to the COM Object Management object. Cstdmarshal::marshalobjref this Windows intrinsic function to find, can not find relevant relevant information. (Please let me know if anyone knows what the bread contains here.)
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 |
Five. Conclusion:
When returning COM objects to external use, be sure to use AddRef (), or the QueryInterface () interface, to ensure that the underlying management objects associated with the COM object are set before delivery. The returned object is called release () after it is used, to prevent disclosure.
Even if you know that this time the returned pointer is definitely within the lifetime of the COM object, remember to call it, or else it will take a whole day to locate the crash like I did.
COM objects In addition to the reference count there are ...