I have an application that uses Office interop assemblies. I am aware about the "Runtime Callable Wrapper (RCW)" managed by the runtime. But I am not very sure how the reference count gets incremented. MSDN says,
RCW keeps just one reference to the wrapped COM object regardless of the number of managed clients calling it.
If I understand it correctly, on the following example,
using Microsoft.Office.Interop.Word;
static void Foo(Application wrd)
{
/* .... */
}
static void Main(string[] args)
{
var wrd = new Application();
Foo(wrd);
/* .... */
}
I am passing the instance wrd
to another method. But this doesn't increment the internal reference count. So I am wondering on what scenarios the reference count gets incremented? Can anyone point out a scenario where the reference count gets incremented?
Also I read some blog which says avoid using double dots when programming with COM objects. Something like, wrd.ActiveDocument.ActiveWindow
. The author claims that compiler creates separate variables to hold the values which will increment the reference counter. IMHO, this is wrong and the first example proves this. Is that correct?
Any help would be great!
I have been researching this question too, working on a COM/.Net-Interop-centric application, fighting leaks, hangs and crashes.
Short answer: Every time the COM object is passed from COM environment to .NET.
Long answer:
On a side note: You should ALWAYS release COM objects as soon as you are finished using them. Leaving this work to the GC can lead to leaks, unexpected behavior and event deadlocks. This is tenfold more important if you access object not on the STA thread it was created on. [Ref 2] [Ref 3] [Painful personal experience]
I'm hope I have covered all cases, but COM is a tough cookie. Cheers.
Test 1 - reference count
private void Test1( _Application outlookApp )
{
var explorer1 = outlookApp.ActiveExplorer();
var count1 = Marshal.ReleaseComObject(explorer1);
MessageBox.Show("Count 1:" + count1);
var explorer2 = outlookApp.ActiveExplorer();
var explorer3 = outlookApp.ActiveExplorer();
var explorer4 = outlookApp.ActiveExplorer();
var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer4);
var count2 = Marshal.ReleaseComObject(explorer4);
MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals);
}
Output:
Count 1: 4
Count 2: 6, Equals: True
Test 2 - reference count cont.
private static void Test2(_Application outlookApp)
{
var explorer1 = outlookApp.ActiveExplorer();
var count1 = Marshal.ReleaseComObject(explorer1);
MessageBox.Show("Count 1:" + count1);
var explorer2 = outlookApp.ActiveExplorer();
var explorer3 = explorer2 as _Explorer;
var explorer4 = (ExplorerEvents_10_Event)explorer2;
var explorerObject = (object)explorer2;
var explorer5 = (Explorer)explorerObject;
var equals = explorer2 == explorer3 && ReferenceEquals(explorer2, explorer5);
var count2 = Marshal.ReleaseComObject(explorer4);
MessageBox.Show("Count 2:" + count2 + ", Equals: " + equals);
}
Output:
Count 1: 4
Count 2: 4, Equals: True
Sources I relay on in addition to my experience and testing:
1. Johannes Passing's - RCW Reference Counting Rules != COM Reference Counting Rules
2. Eran Sandler - Runtime Callable Wrapper Internals and common pitfalls