Detect if called from P/Invoke - c#

I'm working with a managed service written in C#.
The service interacts with an unmanaged COM object using P/Invoke.
The service supplies the object with a reference to a callback interface.
The object invokes methods on the interface as and when appropriate.
My question: from any point in the service code at runtime, is it possible to determine whether the current thread is executing due to a P/Invoke callback?
In other words, I basically want to do a stack trace and check if the base call was a P/Invoke call back - without any reference to the callback interface or the names of its methods.
Hope that makes sense!
Edit:
The reason I asked the question in general terms is because I wanted to know if there was an answer to the question. I know that the design may be... questionable... but at this point I can't see any alternative solution. Here is some more context for the people that might want to offer solutions to my more specific problem.
The issue I'm dealing with boils down to apartments (STA vs MTA).
The COM object is part of an interface for interacting with hardware devices.
The service manages multiple hardware devices (ie. multiple instances of the COM object exist at runtime, using the same callback interface).
The service runs in a MTA. The COM object instances also usually run in a MTA (because the object is instanciated by the service). However, in order to interact with certain hardware drivers the COM object must sometimes run in a STA. In those cases the service uses a wrapper class with an internal STA thread to proxy interaction with the COM object instance in the STA. The wrapper uses the same interfaces as the COM object in order to keep one code path.
Most of the time the wrapper solution works fine. The problem is dealing with callbacks from the COM object, because the service needs to invoke methods on the object during the execution of the callback. If you try to proxy those method calls through the STA thread it results in deadlock, presumably because the calls get blocked on the STA message queue (which won't be processed until the callback is completed).
When the STA instances invoke callbacks on the service, P/Invoke executes these callbacks in MTA threads as we'd expect (because the service is running in a MTA). What I didn't expect was that it is possible to directly invoke methods on the COM object during the callback simply by bypassing the proxy thread. Somehow P/Invoke must realise that the thread originated in an object in a STA.
So the crux of the problem is that when dealing with STA, the same methods must be invoked in different ways depending on the context. If the service wants to invoke a method from its own managed MTA threads then it must go through the STA proxy thread; however, if the service wants to invoke that same method during a callback it must invoke the method directly. If I can distinguish between the two contexts then I have a solution for the problem.
Before you suggest...
No, it is not practical/possible to pass all the required information as parameters on the callback to avoid the need to call methods during the callback.
No, it is not possible to retrieve the required information after the callback is completed. The COM object's context is lost after the callback completes. It would be impractical to try to preserve it.

Does this help
using System.Diagnostics;
StackTrace stackTrace = new StackTrace(); // Get call stack
StackFrame[] stackFrames = stackTrace.GetFrames(); // Get method calls (frames)
// write call stack method names
foreach (StackFrame stackFrame in stackFrames)
{
Console.WriteLine(stackFrame.GetMethod().Name); // Write method name
}

Related

ThreadPool's Thread creation event

Is there a way to be notified (e.g. via an event or a callback) whenever a new ThreadPool thread is created?
I'm working on a .net console app that is using pjlib library via P/Invoke. This library requires that every thread that uses any of its APIs has to be previously registered via pj_thread_register API:
Register a thread that was created by external or native API to PJLIB. This function must be called in the context of the thread being registered. When the thread is created by external function or API call, it must be 'registered' to PJLIB using pj_thread_register(), so that it can cooperate with PJLIB's framework. During registration, some data needs to be maintained, and this data must remain available during the thread's lifetime.
When trying to call any pjlib function form an unregistered thread the library will indeed assert with the message:
Calling pjlib from unknown/external thread. You must
register external threads with pj_thread_register()
before calling any pjlib functions.
Now, the application's business logic uses async and await, and being it a console app the SynchronizationContext is null, meaning all continuations will run on ThreadPool's threads, including the P/Invokes calls to pjsip APIs, making necessary to call pj_thread_register() before every call to any of the pjsip APIs, to be sure that a newly created ThreadPool's thread won't try to use any pjsip API before registering itself.
I thought that if I could be notified (with a callback that gets called in the context of the newly created thread) whenever a new threadpool thread is created I could use this callback to register the new thread only once.
Is there a way to receive such a notification?
Alternatively I could use a SynchronizationContext (e.g. Stephen Cleary's AsyncContext or Stephen Toub's AsyncPump) on the main or on some dedicated threads (and register them as soon as I create them), but this would mean to redesign the application, maybe going towards an actor model, is there an alternative approach to face this kind of situations?
It might be possible to use profiling APIs or ETW to detect all thread pool creation events. I'm not sure how easy it would be to execute code in the context of that thread as a result of those events, though.
I would recommend using a [ThreadStatic] static bool PjInitialized; field in your interop layer, and add a check to every call at that layer. If it's not initialized, then call pj_thread_register before the actual call.

Loading COM object in C# throws exception “Unable to cast COM object of type 'System.__ComObject' to interface type ...”, but C++ or VB not

I need make COM server in unmanaged C++, and COM client in C#. I found tutorial COM Hello World in C++ ( http://antonio.cz/static/com/5.html ). Page is in Czech language. COM server shows MessageBox with text “Hello world” after call function Print() from IHello interface. Source code is here: http://antonio.cz/static/com/Hello.zip . The archive contains source code of the COM server and COM client in C++ and it works.
But my C# COM client doesn't work. It is a C# console application with reference to “Interop.Hello.dll”. I make the interop dll with command:
tlbimp Hello.tlb /out:Interop.Hello.dll
C# code:
static void Main(string[] args)
{
Interop.Hello.IHello Hello = new Interop.Hello.CHello();
Hello.Print();
}
But C# client throws exception:
Unable to cast COM object of type 'System.__ComObject' to interface type
'Interop.Hello.CHello'. This operation failed because the QueryInterface call on the
COM component for the interface with IID '{B58DF060-EAD9-11D7-BB81-000475BB5B75}'
failed due to the following error: No such interface supported (Exception from
HRESULT: 0x80004002 (E_NOINTERFACE)).
I tried load COM server from Visual Basic too. And it works. I made console application in VB with reference to “Interop.Hello.dll”.
VB code:
Module Module1
Sub Main()
Dim ic As Interop.Hello.CHello
ic = CreateObject("MyCorporation.Hello")
ic.Print()
End Sub
End Module
I debugged COM server when was loading from C# client. The method QueryInterface() return S_OK when in variable “riid” is IHello interface guid.
Any ideas why C# code doesn't work?
No such interface supported
The error message is ambiguous. Everybody will assume it is their interface that isn't supported, IHello in your case. But that's not the case and the error message doesn't make that clear enough. It is the IMarshal interface that is not supported.
COM takes care of a programming detail that .NET does not, it doesn't ignore threading. Threads are notoriously difficult to get right, there is lots of code that is not thread-safe. .NET allows you to use such code in a worker thread and won't object to you getting it wrong, usually producing a very hard to diagnose bug. The COM designers originally thought that threading was too hard to get right and should be taken care of by the smart people. And built in the infrastructure to make using code that isn't thread-safe in a worker thread safe anyway. Which works pretty well, it takes care of 95% of the typical threading problems. The last 5% however tends to give you a rather major headache. Like this one.
A COM component, like yours, can publish whether it is safe to be used from a thread in the registry. The registry value name is "ThreadingModel". A very common value, also the default when it is missing, is "Apartment". Explaining apartments is a bit beyond the scope of this answer, it really means "I'm not thread-safe". The COM infrastructure ensures that any calls on the object are made from the same thread that created the object, thus ensuring thread-safety.
That however requires some magic. Marshaling a call from one thread to a specific other thread is a very non-trivial thing to do. .NET makes it look simple with methods like Dispatcher.BeginInvoke and Control.BeginInvoke but that hides a rather large iceberg of code that's 99% under water. And COM has a hard time doing this, it is missing a .NET feature that makes this easier to implement, it doesn't directly support Reflection.
What is required, for one, is building a stack frame on the target thread so that the call can be made. Which requires knowing what the arguments for the method looks like. COM needs help with this, it doesn't know what they look like since it cannot rely on Reflection. What is required is two pieces of code, called the proxy and the stub. The proxy does know what the arguments look like and serializes the arguments of the method into a RPC packet. That code is automatically called by COM, using a dummy interface that looks exactly like the original interface but with every method making a proxy call. On the target thread, the stub code receives the RPC packet, builds the stack frame and makes the call.
This might all sound familiar in .NET terms, this is exactly the way that .NET Remoting and WCF works. Except that .NET can automatically create the proxy and stub thanks to Reflection. In COM, they need to be created by you. Two basic ways that's done, the general way is to describe the COM interfaces in a language called IDL and compile that with the midl.exe tool. Which can auto-generate the proxy and stub code from the interface descriptions in the IDL. Or there's a simple way, available when your COM server restricts itself to the Automation subset and can generate a type library. In which case you can use a proxy/stub implementation that's built into Windows, it uses the type library to figure out what the arguments look like. Which is really a lot like Reflection. With the extra step of having to register this in the registry, HKCR\Interfaces key, so COM can find the code it needs.
So what the exception message really means is that COM cannot find a way to marshal the call. It looked in the registry and could not find the registry key for the proxy/stub. It then asked your COM object "do you know how to marshal yourself?" by querying for IMarshal. The answer was No! and that was the end of it, leaving you with an exception message that is rather hard to interpret. Error reporting is COM's Achilles heel.
Next, I'll need to focus on why COM decided that it should marshal the calls to your COM server, the thing that you did not expect to happen. One basic requirement for threads that makes calls to COM objects is that it needs to tell COM what kind of support it gives for marshaling calls. Which is the second thing that's hard to do beyond building the stack frame, the call needs to be made on a very specific thread, the one that created the COM object. The code that implements the thread needs to make this possible and that is not a trivial thing to do. It requires solving the general producer/consumer problem, a generic problem in software engineering. Where the "producer" is the thread that made the call and the "consumer" is the thread that created the object.
So what the thread has to tell COM is "I implemented a solution to the producer/consumer problem, go ahead and produce at will". The universal solution to the problem is well known to most Windows programmers, it is a "message loop" that a GUI thread implements.
You tell COM about it very early, every thread that makes COM calls must call CoInitializeEx(). You can specify one of two options, you can specify COINIT_APARTMENTTHREADED (aka STA) to promise that you give a safe home to COM objects that are not thread-safe. There's the word "apartment" again. Or you can specify COINIT_MULTITHREADED (aka MTA), which basically says that you do nothing to help COM and leave it up to the COM infrastructure to sort it out.
A .NET program doesn't call CoInitializeEx() directly, the CLR makes the call for you. It also needs to know whether your thread is STA or MTA. You do so with an attribute on your Main() method for the main thread of your program, specifying either [STAThread] or [MTAThread]. MTA is the default, also the default and only option for threadpool threads. Or when you create your own Thread you specify it by calling Thread.SetApartmentState().
The combination of MTA and a COM object that is not thread-safe, or in other words the "I do nothing to help COM" scenario is part of the problem here. You force COM to give the object a safe home. The COM infrastructure will create a new thread, automatically, an STA thread. It has to, no other way to ensure the calls on the object will be thread-safe since you opted out of helping. So any call you make on the object will be marshaled. That's pretty inefficient, creating your own STA thread avoids the marshaling cost. But most significantly, COM will require the proxy and stub to make the call. You didn't implement them so that's a kaboom.
This worked in your C++ client code because it probably called CoInitialize(). Which selects STA. And it worked in your VB.NET code because the vb.net runtime support automatically selects STA, typical for the language, it does lots of things automatically to help programmers fall in the pit of success.
But that's not the C# way, it does very few things automatically. You got the kaboom because your Main() method doesn't have the [STAThread] attribute so it defaults to MTA.
Do however note that this isn't actually the technically correct solution. When you promise STA you also have to fulfill that promise. Which says that you solve the producer/consumer problem. Which requires that you pump a message loop, Application.Run() in .NET. You didn't.
Breaking that promise can have unpleasant consequences. COM will rely on your promise and will try to marshal a call when it needs to, expecting it to work. It will not work, the call won't get dispatched on your thread since you are not calling GetMessage(). You are not consuming. You can readily see this with a debugger, the thread will deadlock, the call simply never completes. Apartment threaded COM servers often also readily assume that your STA thread pumps a message loop and will use it to implement their own inter-thread marshaling, typically by calling PostMessage() from a worker thread. The WebBrowser control is a good example. The side-effect of that PostMessage() message not going anywhere is, typically, that the component will not raise an event or otherwise not perform a duty. In the case of WebBrowser, you'll never get the DocumentCompleted event for example.
Sounds like your COM server doesn't make those assumptions and that you otherwise don't make calls on a worker thread. Or you would have noticed it malfunctioning with your C++ or VB.NET client code. That's a dangerous assumption that can byte any time, but you might well get away with it.
Right C# code:
[STAThread]
static void Main(string[] args)
{
Interop.Hello.IHello Hello = new Interop.Hello.CHello();
Hello.Print();
}

Is there any way to work around OS loader lock deadlocks caused by third-party libraries?

I have an interesting problem that I haven't seen documented anywhere else (at least not this specific issue).
This issue is a combination of COM, VB6, and .NET and making them play nice.
Here's what I have:
A legacy VB6 ActiveX DLL (written by us)
A multi-threaded Windows service written in C# that processes requests from clients over the network and sends back results. It does this by creating a new STA thread to handle each request. Each request-handler thread instantiates a COM object (defined in the ActiveX DLL) to process the request and get the result (a string of XML is passed in, and it returns a string of XML back), explicitly releases the COM object, and exits. The service then sends the result back to the client.
All of the network code is handled using asynchronous networking (i.e. thread pool threads).
And yes, I know this is already a risky thing to be doing in the first place, since VB6 isn't very friendly with multi-threaded applications to begin with, but unfortunately it's what I am stuck with for the moment.
I've already fixed a number of things that were causing deadlocks in the code (for example, making sure the COM objects are actually created and called from a separate STA thread, making sure to explicitly release the COM objects before the thread exits to prevent deadlocks that were occurring between the garbage collector and the COM Interop code, etc.), but there is one deadlock scenario that I just can't seem to solve.
With some help from WinDbg, I was able to figure out what is happening, but I'm not sure how (or if) there is a way around this particular deadlock.
What's happening
If one request-handler thread is exiting, and another request-handler thread is starting at the same time, a deadlock can occur because of the way the VB6 runtime initialization and termination routines seem to work.
The deadlock occurs in the following scenario:
The new thread that is starting up is in the middle of creating a new instance of the (VB6) COM object to process an incoming request. At this point, the COM runtime is in the middle of a call to retrieve the object's class factory. The class factory implementation is in the VB6 runtime itself (MSVBVM60.dll). That is, its calling the VB6 runtime's DllGetClassObject function. This, in turn, calls an internal runtime function (MSVBVM60!CThreadPool::InitRuntime), which acquires a mutex and enters a critical section to do part of its work. At this point, it's about to call LoadLibrary to load oleaut32.dll into the process, while holding this mutex. So, now it's holding this internal VB6 runtime mutex and waiting for the OS loader lock.
The thread that is exiting is already running inside the loader lock, because it's done executing managed code and is executing inside the KERNEL32!ExitThread function. Specifically, it's in the middle of handling the DLL_THREAD_DETECH message for MSVBVM60.dll on that thread, which in turn calls a method to terminate the VB6 runtime on the thread (MSVBVM60!CThreadPool::TerminateRuntime). Now, this thread tries to acquire the same mutex that the other thread being initialized already has.
A classic deadlock. Thread A has L1 and wants L2, but Thread B has L2 and needs L1.
The problem (if you've followed me this far) is I don't have any control over what the VB6 runtime is doing in its internal thread initialization and teardown routines.
In theory, if I could force the VB6 runtime initialization code to run inside the OS loader lock, I would prevent the deadlock, because I am fairly certain the mutex the VB6 runtime is holding is specifically only used inside the initialization and termination routines.
Requirements
I can't make the COM calls from a single STA thread, because then the service won't be able to handle concurrent requests. I can't have a long-running request block other client requests either. This is why I create one STA thread per-request.
I need to create a new instance of the COM object on each thread, because I need to make sure each instance has its own copy of global variables in the VB6 code (VB6 gives each thread its own copy of all global variables).
Solutions I've tried that didn't work
Converted ActiveX DLL to ActiveX EXE
First, I tried the obvious solution and created an ActiveX EXE (out-of-process server) to handle the COM calls. Initially, I compiled it so that a new ActiveX EXE (process) was created for each incoming request, and I also tried it with the Thread Per Object compile option (one process instance is created, and it creates each object on a new thread within the ActiveX EXE).
This fixes the deadlock issue with respect to the VB6 runtime, because the VB6 runtime never gets loaded into the .NET code proper. However, this led to a different problem: if concurrent requests come into the service, the ActiveX EXE tends to fail randomly with RPC_E_SERVERFAULT errors. I assume this is because the COM marshalling and/or the VB6 runtime can't deal with concurrent object creation/destruction, or concurrent method calls, inside the ActiveX EXE.
Force the VB6 code to run inside the OS loader lock
Next, I switched back to using an ActiveX DLL for the COM class. To force the VB6 runtime to run its thread initialization code inside the OS loader lock, I created a native (Win32) C++ DLL, with code to handle DLL_THREAD_ATTACH in DllMain. The DLL_THREAD_ATTACH code calls CoInitialize and then instantiates a dummy VB6 class to force the VB6 runtime to be loaded and force the runtime initialization routine to run on the thread.
When the Windows service starts, I use LoadLibrary to load this C++ DLL into memory, so that any threads created by the service will execute that DLL's DLL_THREAD_ATTACH code.
The problem is that this code runs for every thread the service creates, including the .NET garbage collector thread and the thread-pool threads used by the async networking code, which doesn't end well (this just seems to cause the threads to never start properly, and I imagine initializing COM on the GC and thread-pool threads is in general just a very bad idea).
Addendum
I just realized why this is a bad idea (and probably part of the reason it didn't work): it isn't safe to call LoadLibrary when you are holding the loader lock. See Remarks section in this MSDN article: http://msdn.microsoft.com/en-us/library/ms682583%28VS.85%29.aspx, specifically:
Threads in DllMain hold the loader lock so no additional DLLs can be dynamically loaded or initialized.
Is there any way to workaround these issues?
So, my question is, is there any way to work around the original deadlock issue?
The only other thing I can think of is to create my own lock object and surround the code that instantiates the COM object in a .NET lock block, but then I have no way (that I know of) to put the same lock around the (operating system's) thread exit code.
Is there a more obvious solution to this issue, or am I plain out of luck here?
As long as all of your modules work in one process, you can hook Windows API by replacing some system calls with your wrappers. Then, you can wrap the calls in a single critical section to avoid deadlock.
There are several libraries and samples to achieve that, the technique is commonly known as detouring:
http://www.codeproject.com/Articles/30140/API-Hooking-with-MS-Detours
http://research.microsoft.com/en-us/projects/detours/
And of course the implementation of wrappers should be done in native code, preferably C++. .NET detours work too for high-level API functions such as MessageBox, but if you try to reimplement LoadLibrary API call in .NET then you may get a cyclic dependency issue because .NET runtime internally uses LoadLibrary function during execution and does this often.
So the solution looks like this to me: a separate .DLL module which is loaded at the very start of your application. The module fixes the deadlock problem by patching several VB and Windows API calls with your own wrappers. All wrappers do one thing: wrap the call in critical section and invoke the original API function to do the real job.
EDIT: in retrospect, I don't think this will work. The problem is that the deadlock can occur at any time that a Win32 thread exits, and since Win32 threads don't map 1:1 to .NET threads, we can't (within .NET) force Win32 threads to acquire the lock before exiting. In addition to the possibility of the .NET thread that is exiting being switched to a different OS thread, there are presumably OS threads not associated with any .NET thread (garbage collection and the like) which may start and exit at random.
The only other thing I can think of is to create my own lock object
and surround the code that instantiates the COM object in a .NET lock
block, but then I have no way (that I know of) to put the same lock
around the (operating system's) thread exit code.
That sounds like a promising approach. I gather from this that you
are able to modify the service's code, and you say each thread
explicitly releases the COM object before exiting, so presumably you
could claim a lock at this point, either just before explicitly
releasing the COM object or just after. The secret is to choose a
type of lock that is implicitly released once the thread holding it
has exited, such as a Win32 mutex.
It is likely that a Win32 mutex object does not become abandoned until
the thread has completed all DLL_THREAD_DETACH calls, although I don't
know whether this behaviour is documented. I'm not familiar with
locking in .NET but my guess is that they are unlikely to be suitable,
because even if the right kind of lock exists, it would be likely to
be considered abandoned as soon as the thread reaches the end of the
managed code section, i.e., before the calls to DLL_THREAD_DETACH.
If Win32 mutex objects don't do the trick (or if you very reasonably
prefer not to rely on undocumented behaviour) you might need to
implement the lock yourself. One way to do this would be to use
OpenThread to get a handle to the current thread and save this in your
lock object, along with an event or similar object. If the lock has
been claimed and you want to wait for it to be available, use
WaitForMultipleObjects to wait until either the thread handle or the
event is signaled. If the event is signaled this means the lock has
been explicitly released, if the thread handle is signaled it was
implicitly released by the thread exiting. Obviously implementing
this involves a lot of tricky details (for example: when a thread
explicitly releases the lock, you can't close the thread handle
because another thread might be waiting on it, so you'll have to close
it when the lock is next claimed instead) but it shouldn't be too
difficult to sort these out.
I don't see any reason why you couldn't load an extra instance of the ActiveX control in your startup code and just hang onto the reference. Presto, no more loader lock issues since the VB6 runtime never shuts down.
Since I'm still exploring my options, I wanted to still see if I could implement a solution in pure .NET code without using any native code, for the sake of simplicity. I'm not sure if this is a fool-proof solution yet, because I'm still trying to figure out whether it actually gives me the mutual exclusion I need, or if it just looks like it does.
Any thoughts or comments are welcome.
The relevant part of the code is below. Some notes:
The HandleRpcRequest method is called from a thread-pool thread when a new message is received from a remote client
This fires off a separate STA thread so that it can make the COM call safely
DbRequestProxy is a thin wrapper class around the real COM class I'm using
I used a ManualResetEvent (_safeForNewThread) to provide the mutual exclusion. The basic idea is that this event stays unsignaled (blocking other threads) if any one particular thread is about to exit (and hence potentially about to terminate the VB6 runtime). The event is only signaled again after the current thread completely terminates (after the Join call finishes). This way multiple request-handler threads can still execute concurrently unless an existing thread is exiting.
So far, I think this code is correct and guarantees that two threads can't deadlock in the VB6 runtime initialization/termination code anymore, while still allowing them to execute concurrently for most of their execution time, but I could be missing something here.
public class ClientHandler {
private static ManualResetEvent _safeForNewThread = new ManualResetEvent(true);
private void HandleRpcRequest(string request)
{
Thread rpcThread = new Thread(delegate()
{
DbRequestProxy dbRequest = null;
try
{
Thread.BeginThreadAffinity();
string response = null;
// Creates a COM object. The VB6 runtime initializes itself here.
// Other threads can be executing here at the same time without fear
// of a deadlock, because the VB6 runtime lock is re-entrant.
dbRequest = new DbRequestProxy();
// Call the COM object
response = dbRequest.ProcessDBRequest(request);
// Send response back to client
_messenger.Send(Messages.RpcResponse(response), true);
}
catch (Exception ex)
{
_messenger.Send(Messages.Error(ex.ToString()));
}
finally
{
if (dbRequest != null)
{
// Force release of COM objects and VB6 globals
// to prevent a different deadlock scenario with VB6
// and the .NET garbage collector/finalizer threads
dbRequest.Dispose();
}
// Other request threads cannot start right now, because
// we're exiting this thread, which will detach the VB6 runtime
// when the underlying native thread exits
_safeForNewThread.Reset();
Thread.EndThreadAffinity();
}
});
// Make sure we can start a new thread (i.e. another thread
// isn't in the middle of exiting...)
_safeForNewThread.WaitOne();
// Put the thread into an STA, start it up, and wait for
// it to end. If other requests come in, they'll get picked
// up by other thread-pool threads, so we won't usually be blocking anyone
// by doing this (although we are blocking a thread-pool thread, so
// hopefully we don't block for *too* long).
rpcThread.SetApartmentState(ApartmentState.STA);
rpcThread.Start();
rpcThread.Join();
// Since we've joined the thread, we know at this point
// that any DLL_THREAD_DETACH notifications have been handled
// and that the underlying native thread has completely terminated.
// Hence, other threads can safely be started.
_safeForNewThread.Set();
}
}
I had written a rather complex code using VB6,VC6 about 20 years ago and I need to port it to visual studio.net.
I simply took the functions as I had written them along with the header files corrected all the compile errors (which were MANY) and then tried to load it. got "loaderlock closed"
I then decided to redo all the files starting from those that few other files depended upon and then worked my way up and as I went I included only the header files that that particular file required. The result it loads now just fine. no more loaderlock closed.
the lesson for me is don't include any more header files in a particular cpp file than is absolutely necessary.
hope this helps
from a very happy camper!!
david

Using legacy COM component in C# multithreaded Environment

I have legacy COM component, and my mission is to write web service that wrap the COM and enable concurrent non-blocking calls.
First of all, because the COM object itself is stateless, i decided to use the [ThreadStatic] attribute so each thread will have its own instance of the COM object to prevent the use of lock { } statement and enable real concurrent processing, but it seems that all calls still procceeded synchronous.
I wrote a test code that runs a method from the COM component synchronous with for { } loop, and then added second thread that doing exactly the same but to another instance of the COM object, and i saw no changes, X calls always consume Y timespan, no matter of threads count. Its like there is a static lock or something...
In spite of that, separate processes can process each one call concurrently for real. What prevent from separate threads to behave the same?
What can i do to enable real concurrent calls to the COM component?
COM is threading aware and will honor the threading model requested by the coclass. It publishes its threading requirements with the ThreadingModel value in the registry. If it is set to "Apartment" (or is missing), COM will make sure all method calls are made from a single threaded apartment by returning a proxy for the interfaces you QI. The proxy ensures the call is marshaled to the correct thread.
You could cheat and use the interface pointer that you got when you created the coclass in an STA thread and make calls without marshaling. Given that the coclass already said it isn't capable of multi-threading, this is very unlikely to work correctly. You'll just randomly corrupt internal state.

Call VB6 DLL from a multithreaded c# windows service application?

I'm running a multithreaded windows service that need to call a VB6 dll. There's no documentation about this VB6 dll and this legacy system supports a very critical business process.
At first time (1st thread), this dll performs well. As other threads need access, it start provide wrong results.
I read one guys saying:
"Just be careful of one thing if you are using VB6. Your threading
model is going to have to change to support apartments if you are
running a multithreaded service. VB only supports multiple
single-threaded apartments, but .NET runs fully free threaded
normally. The thread that calls into the VB6 DLL needs to be
compatible with the DLL."
Another guy from team gave me the idea to put this ddl in a separated application domain. But I'm not sure.
How can we work with VB6 dll called from a multithreaded c# windows service application?
When the threads come in, are you saving objects and reusing them later on new threads? If you can, create the objects fresh for every thread. We have a situation like this with a data layer dll we use. If you create a connection on one thread, it can't be used from another. If you create a new connection on each thread, it works fine.
If it's slow to create your objects, look at the ThreadPool class and the ThreadStatic attribute. Threadpools recycle the same set of threads over and over to do work, and ThreadStatic lets you create an object that exists for one thread only. eg
[ThreadStatic]
public static LegacyComObject myObject;
As a request comes in, turn it into a job and queue it in your thread pool. When the job starts, check if the static object is initialised;
void DoWork()
{
if (myObject == null)
{
// slow intialisation process
myObject = New ...
}
// now do the work against myObject
myObject.DoGreatStuff();
}
You say
I'm running a multithreaded windows
service that need to call a VB6 dll.
There's no documentation about this
VB6 dll and this legacy system
supports a very critical business
process.
and at the same time you say
At first time (1º thread), this dll
performs well. As other threads need
access, it start provide wrong
results.
I'd make very certain that Management is aware of the failure you're seeing because the code supporting the critical business process is old and undocumented, and is being used in a way it was never intended to be used, and was never tested to be used. I bet it's also never been tested to be used from .NET before, has it?
Here's my suggestion, and this is similar to something I've actually implemented:
The VB6 DLL expects to be called on a single thread. Do not disappoint it! When your service starts, have it start up a thread of the appropriate type (I can't say, since I've deliberately forgotten all that STA/MTA stuff). Queue up requests to that thread for access to the VB6 DLL. Have all such access go through the single thread.
That way, as far as the VB6 DLL is concerned, it's running exactly as it was tested to run.
BTW, this is slightly different from what I've implemented. I had a web service, not a Windows Service. I had a C DLL, not VB6, and it wasn't COM. I just refactored all access to the thing into a single class, then put lock statements around each of the public methods.
This article on multithreading Visual Basic 6 DLL's provides some insight. It says:
To make an ActiveX DLL project
multithreaded, select the desired
threading options on the General tab
of the Project Properties dialog box.
This article says there are three possible models to choose from:
One thread of execution
Thread pool with round-robin thread assignment
Every externally created object is on its own thread
I assume that the default is one thread of execution, and that one of the other two options needs to be selected.
You might want to take a look at this: linky
And here is a snippet that caught my attention:
VB6 COM objects are STA objects, that means they must run on an STA thread.
You did create two instances of the object from two MTA threads, but the object itself will run on a single (COM (OLE) created) STA
thread, and access from the two MTA threads will be marshaled and synchronized.
So what you should do is, initialize the threads as STA so that each objects runs on his own STA thread without marshaling and you
will be fine.
Anyway, VB style COM objects are always STA. Now in order to prevent apartment marshaling and thread switching you need to create
instances in STA initialized apartments.
Note also that when you set the [MTAThread] attribute on Main, you effectively initialize the main thread as MTA, when you create
instances of STA objects from MTA threads COM will create a separate (unmanaged) thread and initialize it as STA (this is called the
default STA), all calls to STA objects from MTA threads will be marshaled (and incur thread switches), in some cases Idispatch calls
will fail due to IP marshaling failures.
So the advise is use STA (and therefore VB6) objects from compatible apartments only.

Categories

Resources