4

I haven't used OLE/COM for quite some time as a developer, but I currently have a need to use some 3rd party OCX code libraries from a C# program.

The C# program uses threading (it's a TCP socket server). The OCXs are marked as Apartment threading model. From my reading, I concluded that if I was careful to create one instance of each OCX per thread, and only to use that instance from the thread that created it, I should be OK.

I did also do:-

myThread.SetApartmentState(ApartmentState.STA);

before starting each thread.

Should this be enough to ensure safe use of the OCXs?

The symptom I'm seeing is that threads can all create OCXs but on an apparently random basis, the calls to prepare and initialize the OCXs fail. They don't seem to return any useful information as to why.

Can anyone explain what I'm seeing, or give me a guide to using these OCXs safely from threaded code?

Alternatively, should I just give up and create a single instance of each and all OCXs in one thread, and send all calls to them via a threadsafe queue or similar?

JohnCC
  • 615
  • 7
  • 20
  • In case it's useful, the actual error is RPC_E_SERVERFAULT 0x80010105 "The server threw an exception." I doubt this will help, because this error is so generic. – JohnCC Feb 11 '11 at 22:08

3 Answers3

1

You are doing something that the control users probably never did before. Runtime environments like VB6, the most common place where .ocx are used, do not allow creating multiple STA threads. It is pretty likely that the code inside the control contains global variables without any of the locking required to make that safe. Random failure is the outcome.

Ditch it, .NET has excellent support for TCP with the System.Net namespace. Socket, TcpClient and TcpServer classes.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • The OCX library are probably not the socket server – dvhh Feb 11 '11 at 18:03
  • 1
    I don't mind a downvote with a reason. It does have to make some sense though. – Hans Passant Feb 11 '11 at 18:08
  • Not my downvote, but your advice is essentially to rewrite the OCX using .NET technologies, which is perhaps a bit glib if you don't know exactly what the OCX does. Unfortunately I've had to deal with 3rd party components that implement undocumented protocols over TCP/IP and a rewrite isn't always an easy option. – Joe Feb 11 '11 at 18:36
  • I think you are right Hans about the problem, but perhaps not the solution. The OCX's are a from a mapping product by MapFactor that are being used for reverse geocoding. It's some legacy stuff at my job, and I have no docs. They are probably not supported any more either. They don't relate to the TCP socket server at all, except that they are being used on data that comes into it. Ultimately we will move to another system, but I have to use this for now. – JohnCC Feb 11 '11 at 20:53
  • Well, sorry to hear that but do stay away from using it in multiple threads. These legacy controls are just not at all thread-safe, they never needed to be. – Hans Passant Feb 11 '11 at 21:00
1

Marking a component as Apartment threaded is just an assertion from the component developer. There are plenty of component developers around who don't understand threading, and even those who do understand threading sometimes get it wrong, so it's an act of faith to trust this assertion.

Personally I'm very wary of using 3rd party components in a multithreaded environment for that reason. Except those that I know have had very wide exposure in multithreaded applications, and/or for which I know I can get adequate support.

If you don't have access to support from the component developer, or source code for the component, and you absolutely need to use it, then the option of creating a single instance may be a good pragmatic solution.

Joe
  • 122,218
  • 32
  • 205
  • 338
  • I concur with your analysis. COM threading was hard to get right, and adding .NET interop is another layer of complexity. I don't have support or source code, so I agree, I think a single protected instance might be the way to go, especially as these run fine in other legacy code where only 1 instance per process is created. – JohnCC Feb 11 '11 at 20:57
0

The Apartment state should be managed by COM, so setting the apartment state only concerns your .Net code apartment state. But the apartment of your COM object should only be accessible from the thread which created your COM object (the one calling the CoInitialize). As for the failure of your calls to the COM object, they might come from underlying code. What is your OCX lib ?

dvhh
  • 4,724
  • 27
  • 33
  • The OCX's are a from a mapping product by MapFactor that are being used for reverse geocoding. They are used a lot in legacy code, single threaded, usually a single static instance per process. The fact that they don't give any problems there tell me I may just be using them in a way that was not intended. – JohnCC Feb 11 '11 at 20:56