The information in this article applies to:
SUMMARYThis document is copied from VB5SP2.htm that is placed in the Visual Basic 5.0 directory on your machine when you install Visual Basic 5.0 Service Pack 2. Visual Basic 5.0 Service Pack 2 is an update to Visual Basic Professional, Enterprise, and Control Creation editions. MORE INFORMATIONMicrosoft Visual Basic 5.0 Service Pack 2 Release NotesVisual Basic 5.0 Service Pack 2 is an update to Visual Basic Professional, Enterprise, and Control Creation editions. It enables apartment-model threading for Visual Basic projects that contain user interface elements, such as forms and controls. It also fixes several bugs that have been reported in Visual Basic 5.0. (Search for VB5FixListSP2 on the Knowledge Base Web site at: http://msdn.microsoft.com/support.)Applying Visual Basic 5.0 Service Pack 2 to your licensed copy of Visual Basic allows you to create ActiveX controls and ActiveX documents that perform well in multithreaded clients like Internet Explorer 4.0. If you have the Professional or Enterprise Edition of Visual Basic, you can also create:
These release notes contain information supplementing the Microsoft Visual Basic 5.0 documentation in Help and Books Online. For additional information, visit the Microsoft Visual Basic Web site at: http://www.microsoft.com/vbasic/ Overview of Changes to Apartment-Model ThreadingProjects authored with Visual Basic 5.0 Service Pack 2 can take advantage of apartment-model threading without having to suppress visual elements such as forms and controls. Applying Visual Basic 5.0 Service Pack 2 makes Forms, UserControls, UserDocuments, and ActiveX Designers thread-safe:
Limitations of the Control Creation Edition, version 5.0The only parts of this document that apply to the Control Creation Edition are those that affect thread-safe ActiveX control projects.If you use the Control Creation Edition to create apartment-threaded ActiveX controls, you must use a multithreaded client such as Internet Explorer 4.0 to test the multithreaded behavior of your controls. You cannot use the Control Creation Edition to create a multithreaded test client. Selecting a Threading Model for Your ProjectVisual Basic 5.0 Service Pack 2 changes the options on the General tab of the Project Properties dialog box. After you apply Visual Basic 5.0 Service Pack 2, the Unattended Execution option is no longer coupled to the threading model for your project. You can select a setting for the Threading Model option without marking your project for Unattended Execution.To set the threading model for an ActiveX DLL, ActiveX Exe, or ActiveX Control project:
For ActiveX Exe projects, you can either specify that each new object is created on a new thread (Thread per Object), or limit your component to a fixed pool of threads. A thread pool size of one (1) makes the project single-threaded; a larger thread pool makes the project apartment-threaded. NOTE: When you change the threading model for an existing project, an error occurs if the project uses single-threaded ActiveX controls. Visual Basic prevents the use of single-threaded controls in apartment-threaded projects, as described in Converting Existing Projects. NOTE: When you specify Thread per Object (or a thread pool greater than one) for an ActiveX Exe project, only externally-created objects are created on new threads. (See "Designing Multithreaded Out-of-Process Components" in Books Online.) Thread per Object and Thread Pool are not available for ActiveX DLL and ActiveX Control projects, because thread creation is controlled by the client application. IMPORTANT: For Professional and Enterprise Edition users, the consequences of selecting Thread per Object or Thread Pool are discussed in detail in "Designing Multithreaded Out-of-Process Components" in Books Online. Visual Basic 5.0 Service Pack 2 and Unattended ExecutionVisual Basic 5.0 Service Pack 2 does not change the functioning of the Unattended Execution option. Unattended Execution still allows you to create components that can run without operator intervention on network servers. The only change made by Visual Basic 5.0 Service Pack 2 is that selecting Unattended Execution doesn't affect the threading model of your component.Limitations of the Apartment-Model Threading FeatureThe following limitations apply to all editions of Visual Basic:
Converting Existing ProjectsOnce you have applied Visual Basic 5.0 Service Pack 2, you can add apartment-threading to your existing projects by changing the Threading Model option, as described in Selecting a Threading Model for Your Project, and recompiling the project. For many projects, this is all you need to do.If an existing ActiveX DLL, ActiveX EXE, or ActiveX control project uses single-threaded constituent controls, attempting to change Threading Model to Apartment Threaded will cause an error. Because of the number and severity of problems that single-threaded ActiveX controls cause for multithreaded clients, Visual Basic does not permit them to be used in ActiveX component projects. If you have an existing project that employs a single-threaded control, contact the vendor to see whether an apartment- threaded version is available. Forcing the Use of Single-Threaded ControlsIt is possible to trick Visual Basic into using a single-threaded control in an apartment-threaded project by manually editing the .vbp file. NOTE: Do not do this. The problems that single-threaded ActiveX controls can cause include:
IMPORTANT: Single-threaded controls can cause these and other problems in any multithreaded component or application you build, using Visual Basic or any other development tool. Design Considerations for ActiveX DLL and ActiveX Control ProjectsObjects provided by in-process components are created on client threads; your DLL or OCX cannot create threads of its own. The first time a client thread creates an instance of an object provided by your component, a new apartment will be created for that thread and your Sub Main will execute for the new apartment. All of the objects your component provides for that client thread will reside in the same apartment.If you have designed your component so that all the instances of a class share some common global data, that data will now be shared only by objects that happen to be on the same thread. If you have global code that accesses a database, that access will now occur separately on each thread. For example, suppose that a multithreaded client shows one instance of your Widget control on a form on thread A and two more instances on a form on thread B. The control instances will be in two apartments, each with a copy of your global variables. Sub Main will execute once for each apartment. The two controls on thread B will share global state, separate from the global state of the control on thread A. Other design considerations are discussed in Changes to "Apartment-Model Threading in Visual Basic" and Changes to "Designing Thread-Safe DLLs" in this article, and in the corresponding topics in Visual Basic 5.0 Books Online. Design Considerations for ActiveX EXE ProjectsThe most important difference between single-threaded and apartment- threaded out-of-process components is that all global data (including the properties of the App object) is replicated for each thread. One of the implications of this is that Sub Main will be run every time a new thread is created.Other design considerations for out-of-process components are discussed in Changes to "Apartment-Model Threading in Visual Basic" and Changes to "Designing Multithreaded Out-of-Process Components" in this document, and in the corresponding topics in Visual Basic 5.0 Books Online. Changes to the Visual Basic 5.0 DocumentationThe following topics describe specific changes to the indicated topics in Visual Basic 5.0 Books Online:Changes to: "Apartment-Model Threading in Visual Basic" Changes to: "Designing Thread-Safe DLLs" Changes to: "Designing Multithreaded Out-of-Process Components" If you have the Professional or Enterprise Edition of Visual Basic, you can locate these topics in Books Online using the Contents pane: Component Tools Guide
NOTE: The Books Online browser is not included in the Control Creation Edition. The topics listed above are not included in the Visual Basic 5.0 documentation parts that can be downloaded for use with the Control Creation Edition. However, the material on reentrancy and designing thread- safe DLLs contained in the following two topics will be of interest to Control Creation Edition users. Changes to "Apartment-Model Threading in Visual Basic"The original topic in Books Online explains that Visual Basic maintains a separate copy of global data for each thread. A side effect of this, which is not mentioned in Books Online, is that Sub Main is executed for each new apartment (that is, for each new thread). Otherwise, there would be no way to initialize global data for the thread. This is discussed further in Changes to "Designing Thread-Safe DLLs" and Changes to "Designing Multithreaded Out-of-Process Components" below.Where You Can Use ThreadingIt is no longer true that components must be marked for Unattended Execution in order to use apartment-threading. Unattended Execution is now a separate option on the General tab of the Project Properties dialog box, as described in Selecting a Threading Model for Your Project, above.ReentrancyThe original topic states that when you make a cross-thread call, the method you're calling is protected from being reentered as long as the method does not yield (for example, by calling DoEvents). It is important to note that a method can also yield by making a cross-thread or cross- process method call of its own or by showing a form.Changes to "Designing Thread-Safe DLLs"The Books Online topic now applies to ActiveX Control projects, as well as ActiveX DLL projects.The procedure for making an ActiveX DLL project apartment-threaded has change, as described in Selecting a Threading Model for Your Project (above). It is no longer necessary to suppress all user interaction (the Unattended Execution option) in order to have an apartment-threaded DLL. 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. NOTE: A special case of implicit form creation is the pre-declared 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 pre-declared 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. 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. 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. Changes to "Designing Multithreaded Out-of-Process Components"It is no longer necessary to suppress all user interaction (the Unattended Execution option) in order to have a multithreaded Exe. The descriptions of Thread per Object and Thread Pool (round-robin threading) remain the same.You can make an ActiveX Exe project apartment-threaded by opening the General tab of the Project Properties dialog box and selecting Thread per Object or selecting Thread Pool and setting a pool size greater than one. Like other private objects, forms reside on the thread where they were created. Private objects cannot be created with the CreateObject function, so they cannot be used to start new threads. IMPORTANT: In a multithreaded executable, Sub Main executes independently for each new thread. Visual Basic provides no way to determine which thread was created first. Apartment-Threaded Controls for Internet Explorer 4.0Internet Explorer 4.0 displays windows using multiple threads of execution. When two different windows in Internet Explorer 4.0 display instances of the same ActiveX control, the OCX must provide an instance to each thread. If both instances are provided from the same apartment in the OCX (as is the case with Visual Basic 5.0), one of the control instances will suffer from degraded performance because of cross-thread marshaling.In addition to poor performance, a single-threaded control can cause problems with focus, tabbing, and accelerator keys when used in a multithreaded application. Compiling your OCX with Visual Basic 5.0 Service Pack 2 improves performance and removes these problems. Each control instance used by Internet Explorer 4.0 runs on the same thread as the window that displays the control, because the OCX can support a separate apartment for each client thread. Most ActiveX Controls Shipped with Visual Basic Have Been UpdatedVisual Basic 5.0 Service Pack 2 includes new versions of most of the ActiveX controls that shipped with Visual Basic 5.0. The updated controls are now apartment-model threaded and will perform equally well on single- threaded or multithreaded clients. Controls that are still single-threaded include DBGrid, MSChart, and the legacy controls that were shipped in the Tools directory for Visual Basic 5.0.ActiveX Controls Authored with Visual BasicOnce you have applied Visual Basic 5.0 Service Pack 2, new ActiveX control projects will default to apartment-model threading. You can add apartment- threading to an existing ActiveX Control project by changing the threading model, as described in Selecting a Threading Model for Your Project, and recompiling the project. For many ActiveX control projects, this is all you need to do.If your ActiveX control project uses single-threaded constituent controls, attempting to change Threading Model to Apartment Threaded will cause an error. Because of the number and severity of problems that single-threaded ActiveX controls cause for multithreaded clients, Visual Basic does not permit them to be used as constituents of apartment-threaded ActiveX controls. This is discussed in Converting Existing Projects. If you have existing ActiveX controls that employ single-threaded constituent controls, contact the vendor of the single-threaded control to see whether an apartment-threaded version is available. Other design considerations for apartment-threaded ActiveX Controls are discussed in Converting Existing Projects. Creating a Thread-Safe DLL with FormsYou can now set the Threading Model option to Apartment-Threaded in ActiveX DLL projects that contain forms.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" in Books Online, and in Changes to "Apartment-Model Threading in Visual Basic" in this document. 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 re-entered. Creating a Multithreaded Test ApplicationApplying Visual Basic 5.0 Service Pack 2 to the Professional and Enterprise Editions of Visual Basic allows you to create multithreaded test applications to exercise your multithreaded controls. The steps to create a simple multithreaded application are as follows:
Determining the Main Thread During Sub MainSub Main executes for every new thread because Visual Basic maintains a separate copy of your global data for each thread (each apartment). In order to initialize global data for the thread, Sub Main must execute. If your Sub Main loads a hidden form or displays your application's main user interface, new copies of those forms will be loaded for every new thread you create.The following code determines whether Sub Main is executing in the first thread so that you can load the hidden form only once and display the test application's main user interface only once:
IMPORTANT: This technique for identifying the first thread may not work in future versions of Visual Basic. Note that Sub Main takes no action for any thread after the first. When you add code that creates MultiUse objects (in order to start subsequent threads), be sure to include code to initialize those objects. EnumThreadWindows is used with a call-back, EnumThreadWndMain, to locate one of the hidden windows Visual Basic creates for its internal use. The window handle of this hidden window is passed to GetWindowThreadProcessId, which returns the process ID. The process ID is then used to create a unique caption for the hidden window (frmProcess) that Sub Main loads. Subsequent threads detect this window and, thus, can tell that they don't need to create the MainApp object. These gyrations are necessary because Visual Basic does not provide a way to identify the application's main thread. The MainApp class, in its Initialize event, displays the test application's main form. MainApp should pass its Me reference to the main form, so that the form keeps MainApp from terminating. From the main user interface, you can create all subsequent threads. Setting the Instancing property for MainApp to PublicNotCreatable helps you avoid displaying two main user interface forms. A simple example of a MainApp class and its associated form (steps 5 and 6, above) might look like this:
Multiple Instances of the Test ApplicationIncluding the process ID in the hidden window caption allows multiple instances of the test application to run without interfering with one another.When you call CreateObject, the instance of the public class you create will be on a thread in the current application instance. This is because CreateObject always attempts to create an object in the current application before looking for other running Exe components that might supply the object. Useful Properties for the ApartmentYou may find it useful to expose the process ID as a read-only property of the module that contains Sub Main:
This allows any object on the thread to get the process ID by calling the unqualified ProcessID property. You may also find it useful to expose a Boolean IsMainThread property in this fashion. Creating New ThreadsThe Thread per Object option causes every public object that is externally created (created using the CreateObject function) to start on a new thread. To create a new thread, simply use the programmatic ID (ProgID) of one of your MultiUse classes:
The variable "tw" now contains a reference to an object on a new thread. All calls to the properties and methods of this object that are made using "tw" will be subject to the extra overhead of cross-thread marshaling. NOTE: An object created with the New operator is not created on a new thread. It resides on the same thread where the New operator was executed. See "Designing Multithreaded Out-of-Process Components" and "How Object Creation Works in Visual Basic Components" in Books Online. To ensure that MainApp doesn't terminate until all of the other threads are finished, you can give each public class a MainApp property. When an object creates a MultiUse object on a new thread, it can pass the new object a reference to the MainApp object as part of the initialization process. (You can also pass MainApp a reference to the new object, so that MainApp has a collection of references to all objects that control threads. However, remember that this will create circular references. See "Dealing with Circular References" in Books Online.) If you want a class that controls a thread to show a form, you should provide it with an Initialize method (not to be confused with the Initialize event) or a Show method that displays the form. Don't show the form in the Class_Initialize event procedure, as this could cause timing errors when you create instances of the class. In a very simple case, the code for a MultiUse ThreadedWindow class and its form, frmThreadedWindow, might look like this:
The following code snippet shows how you might initialize the ThreadedWindow object:
If you have a number of classes that can control threads, you can make your code more generic by defining an IApartment interface to contain the Initialize method. When you implement IApartment in each class, you can provide the appropriate Initialize method code for that class. Your thread creation code might look like this:
NOTE: You can make an IXxxxApartment interface that's known only to the Multithreaded application by defining the interface in a separate type library. In the ActiveX Exe project, set a reference to the type library. Keeping References to Threaded ObjectsTo ensure proper shutdown of a multithreaded application, you must keep careful track of all references to the MultiUse objects you use to create and control threads.Define your object lifetime goals clearly. For example, consider the case of a MultiUse object that shows a form. The easiest way to manage object lifetime is to have the object pass the form a Me reference; the form then keeps the object alive. When the user closes the form, the form's Unload event must set all references to the MultiUse object to Nothing so that the object can terminate and, in turn, clean up its reference to the form. (You may find it useful to give the MultiUse object a Friend method that cleans up the reference to the form and all other internal object references; the form's Unload event can call this method.) If the object that controls a thread creates additional objects on the thread using the New operator, make sure you clean up references to those objects. The thread cannot close until all references to objects that were created on the thread have been released. Open threads consume system resources. Friend Methods Cannot Be Used Cross-ThreadBecause Friend properties and methods are not part of the public interface of a class, you cannot call them from another thread. Cross-thread calls between objects are limited to properties and methods that are declared Public.ReentrancyIf a method of an object yields by calling DoEvents, showing a modal form, or making a secondary call to an object on another thread, then the code in the method can be entered by a second caller before the first call completes. If such a method uses or changes property values or module-level variables, this could result in an invalid internal state for the object. To protect against reentrancy, you can:
Asynchronous TasksVisual Basic doesn't provide a way to fork execution (to have one thread initiate a method call on a new thread and immediately resume processing on the original thread). You can simulate this behavior in your test application by having the original method call turn on a timer and then return immediately. When the timer event occurs, you can turn the timer off and perform the asynchronous processing. This technique is discussed in "Asynchronous Call-Backs and Events" in Books Online, and is demonstrated in Books Online (see "Creating an ActiveX Exe Component") and in the Coffee sample application.Using the Multithreaded Test ApplicationYou must compile the multithreaded test application in order to test your apartment-threaded component because the Visual Basic development environment does not currently support multiple threads of execution. If you have Visual Studio, you may find it useful to compile the test application with debugging information for native code debugging so that you can use the Visual Studio debugger.Note on Safe Scripting for ActiveX ControlsTwo of the apartment-threaded controls included with Visual Basic 5.0 Service Pack 2, the Common Dialog control and the Toolbar control, now support the ActiveX IObjectSafety interface. When these controls are used in an environment where safe scripting is in force, such as Internet Explorer, they will be marked Safe for Scripting. However, certain features of these controls will be disabled in order to qualify them as safe for scripting:
Packing ListThe following files are included in Visual Basic 5.0 Service Pack 2:
Alpha systems only:
REFERENCESSearch for VB5FixListSP2 on the Knowledge Base Web site at: http://msdn.microsoft.com/support Additional query words:
Keywords : kbVBp500 kbVS97sp2fix kbvbp500sp2fix |
Last Reviewed: November 3, 1999 © 2000 Microsoft Corporation. All rights reserved. Terms of Use. |