Visual Basic Concepts

Designing Thread-Safe DLLs

Visual Basic greatly simplifies the job of authoring in-process components that can be used safely and efficiently by multithreaded clients. All in-process components created with Visual Basic use apartment-model threading, to provide synchronous access to objects.

Figure 8.4 shows how a multithreaded client uses a single-threaded in-process component.

Figure 8.4   Multithreaded client using single-threaded DLL

Note   For those who have done COM programming in C++, the client thread that handles all the calls to objects in the single-threaded in-process component will be the first thread that called OleInitialize().

Using a single-threaded DLL in this way is safe, but slow. Cross-thread marshaling is almost as slow as cross-process marshaling. You can improve the performance of an in-process component when it’s used with multithreaded clients by making the component multithreaded.

To make an ActiveX DLL project multithreaded, select the desired threading options on the General tab of the Project Properties dialog box.

Note   In-process components can only use the threads on which their clients create objects and make method calls. You cannot create additional threads within an in-process component, so the other threading options are unavailable for DLL projects.

Important   Selecting the Unattended Execution option suppresses all forms of user interaction — including message boxes and system error dialogs. This is discussed in "Event Logging for Multithreaded Components."

Marking an ActiveX DLL project for apartment-model threading provides the following benefits:

  • All of the objects a client creates on a given thread will be created in the same apartment (thread) in the DLL. Calls to these objects do not require cross-thread marshaling, making them more efficient.

  • Because an object is only accessed on the thread where it was created, calls are synchronized (serialized) so that a call is never interrupted by a call from another thread.

  • Arguments for cross-thread calls are marshaled, and the calling thread is blocked. This synchronization of data protects the calling thread’s state.

This points are illustrated in Figure 8.5.

Figure 8.5   Multithreaded client using multithreaded DLL

If Thread 2 is already making a call to one of the objects in its apartment in the DLL, a call from Thread 3 to one of Thread 2’s objects will be serialized. When Thread 2 is free, it will process the call, using the arguments marshaled from Thread 3. Thread 3 is blocked while the request is serialized.

When the call requested by Thread 3 is complete, the return value and any ByRef arguments will be marshaled back to Thread 3. This synchronization ensures that Thread 3’s data remains consistent.

Important   In an apartment-model DLL, a new apartment is created for each client thread that requests objects that the DLL provides. The first time a client thread requests such an object, Sub Main executes for the new apartment. A DLL cannot create threads of its own.

For More Information   In coding the properties and methods of your objects, it’s very important to observe reentrancy rules, as discussed in "Apartment-Model Threading in Visual Basic."

Forms and Controls in Thread-Safe DLLs

When you create an instance of a form at run time, Visual Basic follows the same rules it uses for other private objects. That is, the new form is created on the thread where the New operator is executed. If you create a form implicitly (for example, by accessing a form property using a variable that was declared As New Form1), the form is created on the thread where the code was executed.

Apartment-threaded DLLs cannot create their own threads; the first time a client thread requests an object provided by your DLL, a new apartment is created, and Sub Main executes for that apartment. All public objects requested by that client will reside in the same apartment, and will share global data. Any private objects (including forms) that are created by the public objects will also reside in the apartment.

Visual Basic does not provide any way for apartments to become aware of each other. However, a multithreaded client could obtain a reference to an object on Thread A, and pass that reference to an object on Thread B. If your DLL allows this, you should read the information on reentrancy in "Apartment Model Threading in Visual Basic."

Important   Modal forms shown by a thread do not block execution of code on other threads, and are not modal to forms on other threads. Showing a form yields control, just as calling DoEvents would, and therefore may cause the code in an object to be reentered.

Unlike forms, ActiveX controls are public objects that can be created and used by client forms. An instance of an ActiveX control always resides on the same thread as the client form that uses it.

Note   A special case of implicit form creation is the predeclared ID (for example, Form1), a hidden global form variable that Visual Basic maintains for every form class. Because Visual Basic duplicates global data for each apartment, there is a separate Form1 for every thread. To avoid confusion, you may find it helpful to avoid using predeclared IDs in apartment-threaded components, by explicitly declaring your own form variables.

Note   Duplicate global data means that a variable you declare Public in a standard module is global only to the thread; each thread has an instance of the variable. It also means that the properties of the App object, such as App.ThreadID, have separate instances for each thread.