One of the strongest areas of .NET is interoping which allows .NET application to take advantage of legacy code whether it is written in the form of C DLLs or COM objects.

In this post I will talk about the Runtime Callable Wrapper (RCW) which is the .NET wrapper around COM objects when they are being accessed from .NET application.

Before we’ll discuss the internals of the RCW, we should first see how can we create it.

How can a .NET application access COM?
There are 3 methods a .NET application can access a COM object:

  1. Use the CreateObject function (only available in VB.NET)
  2. Add a reference to the COM object from Visual Studio .NET which will create an interop assembly automatically for you.
  3. Manually create an interop assembly and add a reference to it.

While option 2 and 3 are similar in result there are a few side effects on strong naming and namespaces which affects certain things. I will discuss about it later on.

How does an RCW object works?

The RCW is a .NET object which is a wrapper on top of a COM object. It is the bridge between the Garbage Collection approach of .NET and the reference counted approach of COM.

It is treated as a pure .NET garbage collected object which means it will be collected only when no one is referencing it.

Since there is no effective way of marrying the reference counted way and the garbage collected way what the RCW actaully does is always hold a reference of 1 to the COM object (excluding the cases where an RCW is marshalled across AppDomains, in which case its reference count is upped by one). This means that what will determine if the COM object lives or dies is the whether its RCW is alive (which in garbage collection words means its reachable from another .NET object or it is rooted).

An RCW also has a finalizer in which the COM object is actually being dereferenced and destroyed. This means that it takes at least 2 Garbage Collections until the COM object actually dies.

Important things we should all remember about RCWs:

Having an RCW control the death of underlying COM object means that instead of having the deterministic and immediate destruction model of referencing counting we have an undeterministic garbage collection and that is something we should keep an eye on.

The RCW itself is very light so having a lot of RCWs alive will not affect the size of the GC heaps too much, but it will affect the private bytes.

How to detect RCW “leaks”?

RCWs leaks are actually RCWs that are referenced and never released. While it is similar to finding leaking .NET objects that are still being referenced it has other impacts on the system.

As I said earlier, RCWs are light, this means the underlying COM object takes up memory in the default NT heap or in some other custom heap. If its not getting freed it adds presure on the Virtual Memory, since now the native memory takes up more and may take parts in the virtual memory instead of letting the GC reserve them.

Rembmer I said there are a few methods of adding COM objects to your .NET project? The main reason I listed them was due to the fact that each method of adding them to the project will make the objects appear a bit differently in WinDbg.

Option 1 – CreateObject – will make the RCWs appear of type System.__ComObject.

Option 2 – Adding a direct reference to the COM object – will make the RCWs appear as Interop.XXX.ComClassName where XXX is the name of the COM dll and ComClassName is the name of the COM class as it appears in the type library.

Option 3- Manually creating an Interop will make them appear like Option 2 if you did not change the namespace, or as the namespace that was chosen during the type library import using tlbimp.exe utility.

So, how can we actually detect them? There are two useful methods for that.

Using WinDbg to detect RCWs leaks

This method can work on a live debug or with memory dumps. The main technique is:

  1. Perfrom some operations that allocates RCWs.
  2. Take a memory snapshot or list all objects of that type that are currently alive.
  3. Run a GC
  4. Do step 2 again.

If you took memory dumps, all that is left now is to compare the list of object.

How to list the objects? Simply use !dumpheap -type XXX where XXX is the type of RCWs you use (see the 3 options above to see the names of RCWs object you can expect).

Using LeakDiag to detect RCWs leaks

LeakDiag is a tool developed in Microsoft to detect unmanaged memory leaks. It can track and give you the exact size of the unmanaged memory leak as well as the complete stack trace of the code that allocated that object.

Do use LeakDiag you need to attach it to a running process and perform the same technique as mentioned above with WinDbg. When you click on “Log” in LeakDiag you will be able to get an XML log file that will tell you the exact call stack of the leaking memory and you will be able to identify the call stack that leads to the exact COM object.

NOTE: Make sure you have the correct symbols for your COM objects and that you set up a correct symbols path to those symbols prior to attaching LeakDiag to the process.

You can download LeakDiag from here. Although this is not the latest version, it is still handy.

There is another handy utility (which is written in .NET) that parses the XML log files of LeakDiag called LDGrapher that you can use to view a little bit more easily the LeakDiag logs.

You can download LDGrapher from here (this is also not the latest version).

I plan to write in greater detail about LeakDiag and LDGrapher in a future post.

I’ll try and see if I can convine some of the guys at MS to release LeakDiag and LDGrapher like they released DebugDiag. I’ll keep you posted on those efforts here in this blog.

  • Anonymous

    Nice Man! Nice Explanation about RCW. It gives indepth details abt RCW.

  • Vijayaragavan

    Nice Man! Nice Explanation about RCW. It gives indepth details abt RCW.

  • You are welcome.

  • Hi,

    We are hosting VB6 ocx control in a .net Application (winforms), while closing the applicaiton we are getting an memory exception.

    Also please let us know how to debugg the ocx control from .net

    regards

    mano

  • Manoharan,

    What is the exact error you are getting?

    Can you provide me a sample code of how you create (and possibly destroy) your OCX?

    Do you pass .NET objects or data into the OCX other than strings and integers? Do you get unmanaged data buffer from your OCX back to your .NET application?

    Do you create and destroy multiple times the OCX object?

    Are you creating timers or threads and use the OCX from them?

    The most common problem in these cases is usually a memory leak in the OCX control.

  • Hi Eran,

    We are getting the following error while closing the application :

    Unhandled exception in explorer.exe (MSVBVM60.DLL) 0xC0000005, Access violation error”

    The Main application is in VB6 (exe project) which referres some other Activex (ocx) control projects.

    We have upgraded only the main application to .net 2005 using .net upgrade wizard. The wizard crated the necessary interop & wrappers for the referred ocx/ddls. The application successfully upgraded to .net and running but while closing the upgraded .net application we are getting the above referred memory exception. We are not creating or destroying the object since the code is generated by the upgrade wizard.

    Also please clarify my how to debugg the referred ocx controls (interop/wrapper) from VB.Net

    Thanks & Regards

    Mano

  • Manoharan, this seems like a bug (or more exactly, a memory leak) in your VB6 code (either in your control or the one you are referring to from within your VB6 project).

    In any case, to debug your VB6 project, you can debug it like any other VB6 OCX that you are debugging, but either setting your .NET program as the program to run while debugging your VB6 project, or run it and attach the VB6 debugger to the process of the .NET app.

    This way you’ll be able to step through your VB6 code and, hopefully, figure out where the exception is coming from. You cannot debug VB6 code within VB.NET.

    If you are unable to do that, you might need to resort to WinDbg, in which case you’ll need to run your application, run WinDbg and attach it to your .NET application and upon closing it an exception should be thrown which might help you figure out from which DLL it actually happened.

  • Hi Eran,

    Thanks for your reply.

    I try to catch the exception in the form_closing & form_closed event at that stage there is no exception occurs after that only the following memory exception occurs.

    Unhandled exception in DBCenter.exe (MSVBVM60.DLL) 0xC0000005, Access violation error”

    How to debugging the OCX control in VB6 from my VB.net application I tried the same way I ocule not able to debugg the ocx control. If your can suggest some squence of steps it will be very helpful to me.

    I loaded the microsoft symbol files and run the applicaiton, then the exceptions shows the “MSVBVM60.dll ”

    Thanks & regards

    Mano

  • Mano,

    I suspect the error occurs when the application unload. More specifically, probably one of your components in VB6 is unloading and its destory/unload code has a bug that is causing this.

    It seems as though it is coming from MSVBVM60.DLL because VB6 freaks out in its runtime library.

    I’m afraid it would be quite difficult to debug this over blog comments.

    Basically what you need to do is run your application, hook up WinDbg and close your application. WinDbg should break due to the exception and you should write this:

    ~kv250

    This will show you the call stack from which the error is coming from and should point you to the offending code.

  • Hi Eran,

    I ran the application and try to attach the WinDbg I am getting the following error :

    “An attamep to set processes DebugPort or ExceptionPort was made, but a port already exists in the process”

    How to resolve this error.

    regards

    Mano

  • Mano,

    Don’t run your .NET application from the debugger. You can only attach one debugger to a windows process.

  • Hi Eran,

    I have created a sepate .Net application & used only the control & initialize that control and compile that application. ( I ran the application from the obj\Debug directory)

    I attached that application to the WinDbg & close the application I got Stack unwind information not available Message.

    I am sending herewith the following stacktacce I got from the WinDbg for your reference:

    0:006> g
    (dac.940): Access violation – code c0000005 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=00fe3f9c ebx=00fef3cc ecx=77d4b96b edx=00000003 esi=00feca24 edi=00000133
    eip=6604b4cc esp=0012f57c ebp=0012f5a8 iopl=0 nv up ei ng nz na po nc
    cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010282
    *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\MSVBVM60.DLL –
    MSVBVM60!IID_IVbaHost+0x1cbdc:
    6604b4cc 8b4074 mov eax,dword ptr [eax+74h] ds:0023:00fe4010=????????

    0:000> ~kv250
    ChildEBP RetAddr Args to Child
    WARNING: Stack unwind information not available. Following frames may be wrong.
    0012f5a8 660488a8 00fef3cc 000305fc 00000133 MSVBVM60!IID_IVbaHost+0x1cbdc
    *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\tabctl32.ocx –
    0012f5d4 212f20bf 00fef4c0 000305fc 00000133 MSVBVM60!IID_IVbaHost+0x19fb8
    *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\USER32.dll –
    0012f658 77d48734 00000000 00000133 1f011086 tabctl32+0x20bf
    0012f684 77d48816 212f206a 000305fc 00000133 USER32!GetDC+0x6d
    0012f6ec 77d4b89b 00000000 212f206a 000305fc USER32!GetDC+0x14f
    0012f728 77d5f3e3 006800a0 00680008 1f011086 USER32!GetParent+0x16c
    *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\MSCOMCTL.OCX –
    0012f748 27585065 000305fc 00000133 1f011086 USER32!SendMessageA+0x49
    0012f7a0 2759575e 1f011086 00000200 001a9ae0 MSCOMCTL+0x5065
    00000000 00000000 00000000 00000000 00000000 MSCOMCTL!DllGetClassObject+0x7725

    Please provide me your comments on this.

    Thanks & Regards

    Mano

  • Hi Mano,

    I need a bit more information to figure this out. Can you please do the following things to create a dump file that you can send me and I’ll take a look?

    Connect a debugger and do everything I’ve told you before, but instead of running “~kv250” run the following command “.dump /ma c:\\dump.dmp” (without the double quotes). This will create a dump file named dump.dmp on your root C: drive.

    Zip it and email it to me to:
    [my first name] @ sandler.co.il

    Replace [my first name] with my first name.

    Eran

  • Manoharan

    Hi Eran,

    Sorry for the delayed reply, I was on leave.

    I have generated the dump file the zip size is 40 MB I don’t know how to send it.

    regards

    Mano

  • Mano, you can use YouSendIt (http://www.yousendit.com/).

  • Srikanth

    Hi,
    I’m using a COM component for word automation. I’m creating the objects of the Word Document and Word Application using Document and Application classes of the COM component and I’m doing some operations, when I’m trying to close the object of Document it’s closing successfully and quit, the problem is with the object of the Application, it’s neither closing not returing any error, it’s simply appearing as a running process in the Task Manager, being working on a very important server I couldn’t leave the process like that I must kill it when the application is closing, if you can give me any idea it’d be of great help to me. My code for closing the objects is

    //–Closing the Word Document after editing
    oWordDoc.Close(ref oTrue,ref oMissing,ref oFalse);

    //oWordDoc.ActiveWindow.Close(ref oTrue,ref oMissing);
    if(oWord!=null)
    {
    oWord.Quit(ref oTrue,ref oMissing,ref oFalse);
    GC.Collect();
    GC.WaitForPendingFinalizers();
    oWordDoc=null;
    oWord=null;
    }

    Also please tell me how can I access the Marshal object in my Program. Do i need to include any name space for it? Becoz I’m not able to access the Marshal object.

  • Hello Srikanth,

    There are numerous reasons why the Word processes stay in memory. Word is an out of process COM server which means activating it creates the Word process. If there is any other reference to the object from any other program it will keep the process alive.

    In addition to that, from my experience with out of process COM servers, it’s not very easy to make sure these little bugger close when you finish using them.

    Generally it is NOT recommend for you to use Word in that way on a server. If you need to create/update/edit/process a Word document, there are plenty of 3rd party components that does that on the server side in a thread safe (and multi threaded supported) manner.

    You may want to use Marshal.ReleaseComObject (you can access the Marshal class by including “System.Runtime.InteropServices”.

    Though I’m not sure that will actually do the trick if there is a slight chance that someone else on that server is accessing the Word COM server.

  • Korry

    Hi
    I have been searching for a solution to a RCW problem that I have.
    The app I have written is in vb.net and it accesses a com dll.
    The app can work fine just as I want and would expect but it will
    crash or hang occasionally when it accesses the com object.

    The more common of these calls/methods is for example
    iRet = oAccessMxSB.MFWFireMinor(strMinorCode, strFields1to10)
    iRet is a return code for errors etc
    strMinorCode is a string
    strFields1to10 is an array of strings

    The call works fine most of the time but occasionally the data in the string
    array gets corrupted. Such as StrFields(0) should be “CS00”, but somehow
    the value the com object gets is nothing or just “CS”.
    When this happens the com executes correctly but returns an error code which
    is returned in iRet.
    Other methods in the com call are less kind and just cause the software to hang.

    I have been looking at your web page on RCWs and wonder if it
    is what I should be looking at. I have tried lots of different
    things but am not getting any closer to a solution. Perhaps its not the com
    call thats the problem, and maybe its something else in my own .net code.
    I have also coded up try catch exceptions but this doesnt catch anything

    Any pointers, ideas etc would be welcome as I have been trying to find
    a solution off and on for the last couple of years.

    Regards Korry

  • There are two possible problems:

    1) I’m assuming the string you are passing is from .NET to the COM DLL, there might be some memory leak or a problem in the COM DLL that might cause memory corruption after a while.

    2) You might be having some threading issues between the COM DLL and .NET. Do you know the threading model of the COM DLL? Do you know in what language it was written?

  • Korry

    Hi Eran
    Thanks for reply
    To 1 yes I am passing the string array from vb.net to the Com.
    To 2 the com.dll is a 3rd party DLL supplied through Bentley Cad software.
    I dont know the threading model or what software it was written in. I do know that it was written pre .net days.
    I have asked Bentley for help but they just arent interested.
    This week I have installed the App on a Desktop PC and have had no problems, I have also installed it on a Laptop(Dell M6300) and again no problems

    Regards Korry

  • Yuhong Bao

    “*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\MSVBVM60.DLL”
    Unfortunately, not all versions of MSVBVM60.DLL has symbols on the MS symbol server.

  • Pingback: Use SAD to view RCWs and CCWs | Code & Debug()

  • Ekhawa

    Hello Eran,
    This is Bimz here , i am facing a same issue as manohar faced earlier , how did you resolve it earlier in case of manohar.please mail me the soln in my email id [email protected].I am also instatiating VB control in .Net UI using AXHost Control

  • Ron Inbar

    Hi Eran,

    sos.dll has a command called RCWCleanupList, which gives the following output:

    0:000> !rcwcleanuplist 00165798
    RuntimeCallableWrappers (RCW) to be cleaned:
    RCW CONTEXT THREAD Apartment
    774ec284 774ec2a8 774ec2c4 STA
    774fd6d5 774ec2a8 774ec2c4 STA
    MTA Interfaces to be released: 0
    STA Interfaces to be released: 2

    How (if at all) can I use this output to figure out which coclasses are being referenced by the RCWs?

  • It’s been quite a while since I debugged .NET/COM issues (let alone .NET at all đŸ˜‰ ).

    Just to understand a bit better – are you trying to figure out leaking objects? If so, did you use leakdiag and LDGrapher?

    In any case, these addresses seems like addresses on the stack of the STA thread. My guess is that the address under the RCW column maybe the place in the stack that may contain the address of the object itself so you might want to “dds 774ec284” and try “!do” on some of the addresses there. They might contain the address of the object and you’ll be able to know which RCW it is.

    But these are mostly guesses.

    What are you trying to find out?

  • Ron Inbar

    Hi Eran,

    Thanks for your quick reply!

    It’s a blocked finalizer thread situation. The finalizer thread looks like this:

    0:000> ~5kb
    ChildEBP RetAddr Args to Child

    0a3efa04 7a0c04b2 00168660 00000000 00000000 mscorwks!CtxEntry::EnterContextOle32BugAware+0x2b
    0a3efb24 79ffb8a1 79f877e2 0a3efb80 deff45a1 mscorwks!CtxEntry::EnterContext+0x322
    0a3efb58 79ffb929 00165798 deff4551 00000001 mscorwks!RCWCleanupList::ReleaseRCWListInCorrectCtx+0xc4
    0a3efba8 79f8770b deff4515 00194380 00194380 mscorwks!RCWCleanupList::CleanupAllWrappers+0xdb

    The argument I gave to RCWCleanupList was taken from the arguments to RCWCleanupList::ReleaseRCWListInCorrectCtx (just tried them one by one until I got something out of the command).

    0:000> dds 774ec284
    774ec284 7758b866 ole32!CObjectContext::QueryInterface
    774ec288 7751ab02 ole32!CObjectContext::AddRef
    774ec28c 7751ab66 ole32!CObjectContext::Release
    774ec290 7751b48b ole32!CObjectContext::GetUnmarshalClass
    774ec294 7751b4ab ole32!CObjectContext::GetMarshalSizeMax
    774ec298 7751b4ef ole32!CObjectContext::MarshalInterface
    774ec29c 7758bcc3 ole32!CObjectContext::UnmarshalInterface
    774ec2a0 7751cf30 ole32!CObjectContext::ReleaseMarshalData
    774ec2a4 77532866 ole32!CBindStatusCallback::OnLowResource

    This looks like the vtable for an object of type CObjectContext, but I don’t know what to make of this information. If it was one of my own types, I would know what to look for.

    As for the 2nd RCW in the output from !RCWCleanupList, it doesn’t even look like a real address because it’s not divisible by 4 or even by 2 (it’s a 32-bit machine). I tried to round it down but to no avail. Here is the output:

    0:000> dds 774fd6d5
    774fd6d5 8b55ff8b
    774fd6d9 758b56ec
    774fd6dd 468d5708
    774fd6e1 15ff5004
    774fd6e5 774e1224 ole32!_imp__InterlockedDecrement
    774fd6e9 ff85f88b
    774fd6ed 2852840f
    774fd6f1 c78b0000
    774fd6f5 c25d5e5f

  • If you suspect the finalizer thread is blocked, you should try some of the things I wrote about here: http://dotnetdebug.net/2005/06/22/blocked-finalizer-thread/

    In general, try to find the thread ID of the finalizer thread, and do !clrstack on it (or ‘kv’ for unmanaged stack) to see if what is blocking it is your code.

  • Ron Inbar

    I already did that and I know the STA thread ID. What I still don’t understand is how that thread became responsible for a COM object since it doesn’t seem to be running a Windows message loop.

  • The finalizer thread is NOT an STA thread. However, since the object you are about to finalize is an STA COM object it means that when the finalizer threads decrement the ref count of the underlying COM object, the COM infrastructure now needs to perform a context switch to the STA thread in which the COM object resides.

    If you are certain your finalizer thread is being blocked (i.e. the finalization queue is ever increasing) the methods mentioned above will tell you which object it is, but not what’s going on there.

    You need to find the STA thread (again, it is not the finalizer thread) and see the call stack there to figure out why is it blocking.

    Btw, the block may also be due to the nature of STA threads allowing only a single caller inside that thread in a given time, so due to context switching between other threads and objects in that STA thread the finalizer simply isn’t capable of handling the amount of objects it needs to release in a given time because the rest of the app keeps on switching into that STA thread.

  • Pingback: The Art of Dev()