VS debugger stops without exception when a thread uses a COM class - vb.net

I have a VB.NET application which starts a thread and the thread instantiate a COM class, from a C++ COM in-proc module, using a PIA, and uses its methods. Been working for years.
Just converted the COM server to VB.NET an ran into the following problem with the VS debugger: stepping in the code of the thread, the line of code which refers to the VB.NET COM server, trying to step over, execution would stop, no exception, debugger would end, thread ends.
I just found that the problem was caused by the fact that I did not specify an apartment model for the thread. Specifiying STAThread would fix it.
Is there a way to configure VS debugger to get more than an abort during this kind of bug? In Debugger/Exception, turning on all "stop if exception is raised" did not do the job.

Related

Does VBA use a Global Interpreter Lock? Do all interpreted languages need one?

It is possible to pass a VBA class (COM object) from one hosted VBA process to another (e.g. one instance of Excel.exe to another) using the Application.Run method. That means the second process (thread 2) will have access to code defined in the first process (thread 1).
It is therefore possible that the second thread would invoke a method on the object from thread 1 which runs using the first thread's interpreter, so thread 1's interpreter is being invoked from thread 1 and thread 2 simultaneously.
That's probably not surprising to anyone, but it was to me, since VBA is implemented as a single threaded language (it all runs in the host application's UI thread!) and doesn't give you syntax to write multithreaded code. Based on that I thought surely the VBA interpreter wouldn't be thread safe and would not allow execution from other threads. But apparently you can, and I've also passed VBA objects to python libraries in the past (which I imagine run on a separate thread with the python interpreter).
So how is it done, does VBA have a GIL like Python - meaning the interpreter can only ever be run by a single thread at a time, or is there something else going on? Is there any way to check?
To control what threads can access methods on a COM Object (thread affinity), COM uses apartments. An apartment is a place for objects having the same thread affinity. Apartments are specific to a single process.
VBA Objects have high thread affinity and they “live” in an STA (single threaded apartment). They will receive calls from a single thread which is always the same thread. In VBA there is one and only one thread in the STA. STA is designed to protect objects against concurrency. A hidden window is created when a thread enters an STA (CoInitializeEx is called). When a call to an object living in the STA is made, a window message is posted to the hidden window which then makes the thread in the apartment to execute the call. If two threads call methods on an object living in an STA, then the hidden window will receive two messages and only one will be handled at a time meaning that the second is blocked until the first call has completed. In short, objects living in an STA do not have concurrency.
A call from another process is not a direct call. Different apartments and threads. COM simulates a synchronous call instead. The call in the calling process is paused and then replayed in the process where the actual object lives. COM is designed to work with objects even from different computers. The simulation (or interception) involves a proxy and a stub and the COM transport.
For example, if a VBA thread (T1) in process 1 uses an object created in a VBA thread in process 2 (T2), then the object’s interface is exported from the apartment of T2 to the apartment of T1 (the interface is marshalled). When COM marshals the interface, it creates a stub living in the apartment of T2. The stub represents the calling side - T1. The marshalled information is transported and then used to create (unmarshall) the object’s interface (the proxy) in the apartment of T1. Communication is set between the proxy and the stub. So, T1 receives a proxy, not the true object (which lives in a totally different memory space). When T1 calls a method on the proxy, COM forwards the call to the real object via the stub, waits until the call is done and returns the result.

Wait for a thread inside a C++ static object

I have a static object that needs to initialize an imaging API. The allocated resources of this imaging API need to be released by the same thread.
So I'm starting a thread in my static object that initializes everything and then waits for a counter to reach zero. When this happens the thread cleans all up and finishes.
This is an unmanaged class inside a managed library, so I can't use System::Threading::Thread (needs a managed static member function) or std::thread (compiler error, not supported with /clr).
So I have to start my thread like:
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&Initialize, this, 0, 0);
All works fine, the init is done and the API functions work. But when I close the application I see that the usage counter of my static object reaches zero but the clean up function is never called by the thread, as if the thread was killed. Is there a way to make sure the thread will continue to exist and execute until its end?
After turning this around in all possible ways and adding events etc I guess this is not possible so I'll have to change the structure of my code and encapsulate the non managed class inside a managed class, and add the thread to the managed class.
I think you could proceed in one of two ways:
Wrap the resources in RAII-style classes, and refactor to have the objects' lifetimes be on the stack of your created thread, ensuring their destructors get called when the thread loop exits without having to call any additional cleanup. If there is no issue with the thread returning correctly when your counter reaches 0, this should be the simplest and cleanest way of addressing this.
I'm thinking you could intercept the WM_CLOSE message using window procedures, process necessary cleanup and then pass the message on, effectively "stalling" it until you are ready to close. Note that even though you are in a DLL you can still set up a window procedure and message pump system, you don't need a GUI to do that. I am however not 100% sure on whether you'll receive the WM_CLOSE message that concerns the application that "owns" your DLL, it's not something I've tried out yet.
You will have to implement some form of messaging through events within your thread's loop however, as the WindowProc will be called on a different thread, so you know when to call the cleanup procedure.
I also am not very familiar with CLR, so there might be a simpler way of interacting with those APIs than with raw C++ calls and handles.

TcpClient/Listener: Do I have to call Sockets.Close when my program ends?

I'm using the TcpClient and TcpListener classes to establish a connection between two computers. I've read documentation and questions online, but can't seem to find the answer to this question:
Is it necessary to call Sockets.Close() before the program ends, or will VB close all sockets which were opened by the program for me before it ends? In addition, what if the program crashes?
From what I've seen, the suggested implementation of the TcpClient and TcpListener classes suggest closing the sockets in the Finally block so that they are closed even if any exceptions occur.
However, this finally block is not necessarily called if I just simply close the program by calling End, and I'm not entirely sure if it is called if a crash outside of the try block or on another thread occurs.

Access violation in WCF with COM object after garbage collection

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.
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.

Hooking thread exit

Is there a way for me to hook the exit of managed threads (i.e. run some code on a thread, just before it exits?)
I've developed a mechanism for hooking thread exit that works for some threads. Step 1: develop a 'hook' STA COM class that takes a callback function and calls it in its destructor. Step 2: create a ThreadStatic instance of this object on the thread I want to hook, and pass the object a managed delegate converted to an unmanaged function pointer. The delegate then gets called on thread exit (since the CLR calls IUnknown::Release on all STA COM RCWs as part of thread exit).
This mechanism works on, for example, worker threads that I create in code using the Thread class.
However, it doesn't seem to work for the application's main thread (be it a console or windows app). The 'hook' COM object seems to be deleted too late in the shutdown process and the attempt to call the delegate fails.
(The reason I want to implement this facility is so I can run some native COM code on the exiting thread that works with STA COM objects that were created on the thread, before it's 'too late' (i.e. before the thread has exited, and it's no longer possible to work with STA COM objects on that thread.))
Do you control thread creation? It's probably simplest to simply wrap the thread's code in a try...finally clause, and put your code in the finally.
Note that when the AppDomain shuts down, you can never be sure that your code will be called since the shutdown may be less than graceful.
When you say "Destructor" I presume you mean a C++/CLI destructor - i.e. the .Dispose() method?
If you need to support hooking before the process exits, you could try the AppDomain.CurrentDomain.ProcessExit event - which doesn't seem to fire on an unhandled exception, or AppDomain.CurrentDomain.UnhandledException which seems to fire only on an unhandled exception.
None of this looks particularly robust, mind you...