2

I have a self-hosted WCF application where the service objects (per call, single concurrency) use embedded COM objects (referenced from the class throughout the call) built on a platform called Clarion. The COM instances are created and destroyed together with the service objects. However, underneath they access a singleton repository which takes long time to initialise (very similar to this: Startup Code for Loading COM Object for WCF Service). So a first call is very long. Hence, I must call it on startup.

If I don't instantiate the COM object on startup, everything is fine (except for the long first call). But if I do, after the garbage collection the next request will crash with access violation exception when trying to access the COM.

The service objects implement IDisposable where all the COM objects are nicely released using Marshal.ReleaseComObject. The startup call also releases the COM object.

My guess is that the startup COM gets somehow reused or recycled. I don't want it to! Can I make sure it dies forever? Or if it's not possible, can I mark it not for garbage collection? Obviously, GC.KeepAlive is irrelevant here, because these are different threads in different methods.

More details: the COM objects were made MTA-capable recently. They are thread-safe and fully concurrent, but before, when they were STA only, there was no such issue. Also, nothing bad happens outside of WCF when these guys run in separate threads.

Community
  • 1
  • 1
Vadim Berman
  • 1,932
  • 1
  • 20
  • 39
  • 1
    Try it without explicitly calling `Marshal.ReleaseComObject`. Check ["Marshal.ReleaseComObject Considered Dangerous"](http://blogs.msdn.com/b/visualstudio/archive/2010/03/01/marshal-releasecomobject-considered-dangerous.aspx). – noseratio May 02 '14 at 01:56
  • Now that's very interesting. Once I removed the call to Marshal.ReleaseComObject, it crashed outright! Without even waiting for the garbage collection. – Vadim Berman May 02 '14 at 04:46
  • Maybe there is some kind of reusability pool. May I purge or kick COM objects out of it? – Vadim Berman May 02 '14 at 04:48
  • 1
    There's no reusable pool of COM objects. You can prevent a COM object from being GC'ed with `GCHandle.Alloc`, however, that'd be wrong. I think this problem was introduced with this: *"the COM objects were made MTA-capable recently... when they were STA only, there was no such issue"*. Are those objects registered with `ThreadingModel="Free"` or `ThreadingModel="Both"`? Are you sure they don't use any other or 3rd party COM objects internally, which may still be STA? – noseratio May 02 '14 at 05:06
  • Thanks. Please elaborate on GCHandle.Alloc (as an extreme solution). Yes, of course the problem was introduced with their conversion to MTA. Yes, they are registered with ThreadingModel=Both, and it all works fine outside of WCF. – Vadim Berman May 02 '14 at 05:27
  • `GCHandle.Alloc(obj)` just prevents `obj` from being garbage collected, if it has no other strong references. But it is redundant if you do have strong references (which is normally the case). I'm not sure how else I can help without seeing actual code. – noseratio May 02 '14 at 05:38
  • Another thing is that "Both" for COM objects assumes they can be called from either STA or MTA apartment or across COM apartments. "Both" isn't the same as "Free". Judging from your descriptions, they probably should be marked as "Free". Check [this MSKB article](http://support.microsoft.com/kb/150777/en-us) for more details. – noseratio May 02 '14 at 05:57

1 Answers1

2

OK. Looks like I'm on it.

It's the bloody hidden singleton objects or, rather, a runtime library of the platform the COM is written on (SoftVelocity Clarion). It got deallocated for some reason when the startup COM got killed, probably because the reference count went down and it was time to unload the DLL itself. Although when I tweaked DllCanUnloadNow, it did not help, but I'll figure out where it's coming from.

EDIT: Clarion support of COM objects is not straightforward. The code, generated by templates, performs allocation and deallocation of the database dictionary (DctInit and DctKill respectively) and some specific classes in the main program, which ends when the main thread ends. However, in MTA COM objects end of the main thread does not mean end of the program. Hence, the easiest solution is to embed code to prevent execution of DctKill.

Also, do not forget to call AttachThreadToClarion(TRUE) in .Destruct methods, since the garbage collection thread will be different.

This issue may surface in older generation IDEs where a runtime or global objects are used extensively. Beware.

Vadim Berman
  • 1,932
  • 1
  • 20
  • 39