I'm currently looking at a problem we are having in a .Net Framework 2.0 windows service (C#) that has X number of MTA threads running that access COM components. Each thread initializes it's own instance of the com component object. The com component object does not have any UI elements. It's simply business logic that communicates with a sql server database and a C# dll with a com interface that in turn does socket communication and access to the same sql server database.
Through my research I've found that you should not be instantiating STA COM components on an MTA thread but I can't find any specific text to say what the dangers of this are or maybe it's the fact I don't understand COM threading apartments that well.
Would there be concurrency problems with the model being described above? Even if each MTA thread is creating it's own STA COM object?
The problem we are actually running into is an object reference not set to instance of an object error in the setter of the connection string in following code block. This occurs within the C# COM object that is called by the c++ COM object:
IDbConnection connection;
//Code omitted for brevity where connection is initialized
connection.ConnectionString = myConnectionString;
Exception Type: System.NullReferenceException Message: Object reference not set to an instance of an object. Data: System.Collections.ListDictionaryInternal TargetSite: Void ConnectionString_Set(System.String) at System.Data.OracleClient.OracleConnection.ConnectionString_Set(String value) at System.Data.OracleClient.OracleConnection.set_ConnectionString(String value)
There are four basic "dangers" when making calls from the MTA:
the COM object is apartment threaded, very common, and the call must thus be marshaled to the apartment that owns the object. The technically correct wording for "STA COM". That is expensive, marshaled calls to small methods are usually 10,000x times slower.
the COM object doesn't support a proxy to marshal the call. That's very easy to find out, the call will fail with E_NOINTERFACE.
the client programmer doesn't realize that he's making a call from the MTA and forgets to marshal the interface pointer. COM cannot prevent this on calls made to an in-process COM server. You can't make this mistake in a .NET program, the CLR will always marshal, but easy to do in other runtime environments. This otherwise invokes the common wrath of making thread-unsafe calls, works when you debug the code, fails randomly in production and is impossible to debug.
the COM author has published his component to be compatible with MTA by declaring its ThreadingModel as Both or Free. But did not actually test his code thoroughly enough, writing thread-safe code is notoriously difficult. Especially dangerous in [ComVisible] .NET classes because they are automatically registered as Both and it is very easy to completely forget to test the code against that promise.
Your snippet is way too inscrutable to make the call but a possible candidate for the 4th bullet. It doesn't otherwise look like any COM is involved at all. Data providers like Oracle's are normally free threaded and tested thoroughly by hundreds of thousands of programmers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With