Objref Moniker issue. How to release?

Posted on August 20, 2007. Filed under: Uncategorized |

We were using ObjRef moniker to represent instance of an object in our product. Idea is to get monikers for two instances and pass them into a function. After this function returns we get the objects represented by the monikers and call Release() on them. To get objref moniker we use API CreateObjRefMoniker() and finally call IMoniker::GetDisplayName() interface method. GetDisplayName() method increments the ref-count and returns which we finally release it with obj->Release() call as said above.

Our product was crashing when we retrieve monikers, use them and release() them during second operation. One operation constitutes retrieving two monikers of two instances, use them and call Release() on them. Second operation is similar with two new instances, two monikers, use them and Release() on them.

When I debugged, I saw the following pattern (addresses are fake, just for example. This is first operation):

Obj1 – this pointer- 0x00aaaaaa – ref_count =1

Get IUnknown and IMoniker pointers (not considering increment in ref_count values here for simplicity. Anyway, autoptr releases them before function returns)

Call GetDisplayName() on these objects. Immediately after calling GetDisplayName() we have:

Obj1 – this pointer- 0x00aaaaaa – ref_count =2

Call Release() on the created instance (we have moniker string anyway, object is living)

Obj1 – this pointer- 0x00aaaaaa – ref_count =1

Same steps as above for Object 2.

Obj2 – this pointer – 0x00bbbbbb – ref_count=1

Obj2 – this pointer – 0x00bbbbbb – ref_count=2

Obj2 – this pointer – 0x00bbbbbb – ref_count=1

Use the moniker strings and finally call Release() on the objects represented by the Monikers.

MonikerObj1->Release() – 0x00aaaaaa – ref_count=0

MonikerObj2->Release() – 0x00bbbbbb – ref_count=0

Both objects are destroyed. Everything ok.

During Second Operation, when we create new object, COM is creating the object at the same location as first time (perhaps, reusing the memory location?):

Second_Obj1 – this pointer – 0x00aaaaaa – ref_count=1

Get IUnknown and IMoniker pointers (not considering increment in ref_count values here for simplicity. Anyway, autoptr releases them before function returns)

Call GetDisplayName() on these objects. Immediately after calling GetDisplayName() we have:

Obj1 – this pointer- 0x00aaaaaa – ref_count =1 [HERE IS THE DIFFERENCE. GetDisplayName() did not increment the ref_count.]

Call Release() on the created instance. We assumed Moniker String is present and object will be living.

Obj1->Release() — 0x00aaaaaa – ref_count=0 [OBJECT IS DESTROYED. MONIKER STRING WE HAVE POINTS TO NON-LIVING OBJECT]

Now, we create another instance. Since previous one is destroyed, COM yet again creates new instance at the same location!!!

Obj2 – this pointer – 0x00aaaaaa – ref_count=1

Call GetDisplayName(). This time too it does not increment. So we finally we ended up with moniker string representing non-living object during second operation. During final release() we end up with crash.

So, What is causing GetDisplayName() not to increment ref_count during second operation? I searched the net and couldn’t find an answer. So I decided to disassemble Ole32.dll and analyze it.

I’ll skip the details. Here is the crux: Internally, this pointer gets marshaled, Ole32 maintains some kind of PIDTable. Some kind of hash is performed and “this” pointer gets cached. During GetDisplayName(), a lookup is performed with “this” pointer. If it is not found, a new CIDObject() is created and ref_count is incremented on the “this” pointer. It is then cached. Next time, if same “this” pointer comes along, GetDisplayName() simply returns from the cache. I believe this is default implementation to speed up. During this time, “this” ref_count is NOT incremented.

To solve this issue, we need to somehow clear this cache. I’ve set access breakpoint in this table with WinDbg and seen that during CoUninitialize() IMarshal::DisconnectObject() method is called which tries to call release on the cached “This” pointer and clears the cache. Gotcha! All I need now is to know if Ole32 exposes any method which internally calls IMarshal::DisconnectObject(). A simple MSDN search gave me promising API ::CoDisconnectObject(). Still, documentation is not clear on whether it clears the cache I’m looking at. Fire PEBrowsePro, disassemble Ole32!CoDisconnectObject(). Preliminary analysis showed that lookup is performed on PIDTable. Due to virtual method calls, it not clear from disassembly what exactly happens.

Anyway, to test it out, I called ::CoDisconnectObject( &monikerobj1, 0); and followed it in WinDbg. I see that the cache is indeed cleared and the object is correctly destroyed.

So, this is what I learned. To release ObjRef Moniker correctly, one must call ::CoDisconnectObject(), instead of calling Release() on it.

Advertisements

Make a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Liked it here?
Why not try sites on the blogroll...

%d bloggers like this: