Microsoft KB Archive/815053

= INFO: Avoid Global Variables in Visual Basic COM+ Components =

Article ID: 815053

Article Last Modified on 5/11/2007

-

APPLIES TO


 * Microsoft Visual Basic 6.0 Professional Edition
 * Microsoft Visual Basic 6.0 Enterprise Edition
 * Microsoft Visual Basic 6.0 Learning Edition
 * Microsoft Visual Basic 5.0 Professional Edition
 * Microsoft Visual Basic 5.0 Enterprise Edition
 * Microsoft Visual Basic 5.0 Learning Edition

-



SUMMARY
When you use global variables in Visual Basic 6.0 COM+ Apartment components, data may become corrupted. Visual Basic COM+ components use single-threaded apartment (STA) threads. This may cause data to become corrupted under load because of the mechanism that COM+ uses to bind activities to its pooled STA threads. Microsoft does not recommend the use of global variables in Visual Basic COM+ components.



MORE INFORMATION
Global variables in Visual Basic are slightly different in scope from global variables in a C++ program. Instead of having one copy of a global variable that all instances of a class share, Visual Basic stores a unique copy of each global variable per thread in thread local storage (TLS). If two instances of the same Apartment class are bound to different STA threads, each STA (and, therefore, each of the two instances of that class) has its own copy of the global variable. Changes that one instance (one STA) makes to the value are not seen by the other instance (a different STA) because each TLS is unique to an STA.

When you configure a Visual Basic Apartment component in COM+, the COM+ activity model may cause a global variable to become corrupted. COM+ binds up to five activities to one of its pooled STA threads, depending on the load. Therefore, five different logical chains of execution, or five callers, can all be bound to one STA thread.

More than one thread cannot run in the same STA at the same time. When a COM method makes a call to an object that is bound to an STA, this call is packaged as a WM_USER message that is posted to the hidden window of that STA. When an outgoing apartment method call, or a cross-process remote procedure call (RPC) is made, the COM channel creates a separate thread to make the actual method call, and then enters the message loop and services the next message in the queue.

Because the STA guards data against concurrency but not against reentrancy, a second caller (activity) on that thread uses the CPU and can gain access to the global TLS data while the first caller is still in progress. This second caller has access to the same data in TLS that the preempted first call may have changed. If the data is changed while the second message is being processed, the data reflects this change immediately. When the first call returns, it finds that the data is different from what appeared previously and determines that the data has been corrupted.

For example, configure a Visual Basic Apartment component that has a class named MyVBClass in COM+. MyVBClass contains code to change the global integer variable g_MyVal. The first caller, client X, creates an instance of MyVBClass, and then calls a method to set g_MyVal to 5. In the same method, MyVBClass performs either a cross-apartment or cross-process RPC call to another COM+ component. The COM channel creates an additional thread to make the actual RPC call, and then enters the COM message pump and handles a pending call from client Y.

Client Y sets g_MyVal to 10, completes its work, and then returns. When the call of client Y is completed, the outgoing method call of client X uses the processor again. Client X's call reads g_MyVal and expects the value that it set previously (5). However, the value 10 is received. To client X, this is a corrupted value.

In any multithreaded execution environment, Visual Basic globals are unique in the TLS of each thread. However, you have little control over which STA your instances are bound to, unless you explicitly configure your component to use COM+ concurrency. However, COM+ concurrency only guarantees that all instances in the same logical thread of execution are bound to the same STA. When you start a different thread of execution, or when you do not use COM+ synchronization, your instances may not be bound to an STA where the correct global representation is stored. This is another way that you can experience problems when you use globals in Visual Basic.

Microsoft recommends that you do not use global variables in Visual Basic components that run in any multithreaded environment such as IIS and COM+. Instead, you can declare private variables in the class, and then expose them by using public properties as shown in the following sample code.

Avoid Global Variable Workaround
' Private variable for storing the value Private intMyVal As Integer

' Public property to retrieve the value Public Property Get MyValue As Integer MyValue = intMyVal End Property

' Public property to assign the value Public Property Set MyValue(myVal As Integer) intMyVal = myVal End Property

EmulateMTSBehavior Workaround
Another way to work around this problem is to set the  registry key. This setting causes an instance of an object to be created on its own thread. Therefore, you no longer experience the problem of having two instances that share the same TLS data.

Note After you reach the 100 threads with the  registry key, the behavior returns to the default setting and you can reuse the threads.

Important Depending on your architecture, setting this registry key may adversely affect the performance of your COM+ application. If you decide to set this registry key, thoroughly stress test your application to verify that the performance is acceptable. For additional information, click the following article number to view the article in the Microsoft Knowledge Base:

303071 INFO: Registry Key for Tuning COM+ Thread and Activity

For more information about how to set the  registry key, visit the following Microsoft Web site:

http://msdn2.microsoft.com/en-us/library/ms809941.aspx

Intrinsic Visual Basic Objects
In Visual Basic code, the App, the Err, and the Printer intrinsic Visual Basic objects are stored on a per-thread basis in TLS. These objects can also experience the problems that concurrent access to TLS can cause. Use these objects very carefully in a multithreaded distributed environment such as COM+ or Microsoft Internet Information Services (IIS).

App Object
The App object contains read-only information about the Visual Basic application, and also about methods to log events. By using the App.StartLogging method, a developer can change the properties of the App object to control how events are logged using the App.LogEvent method. When you call App.StartLogging to change the way that events are logged, you affect the global App object that is shared for each of your components on that thread. If your application uses the App.StartLogging method, make sure that you call this to set the correct logMode before each call to App.LogEvent.

Printer Object
When you set the properties of the global Printer object, all instances of your Visual Basic component on that thread use the new, updated data. If your Visual Basic components change the Printer properties, you must verify that the properties are set correctly immediately before you print. You must verify the Printer properties because another instance of your component on the same thread may have changed the properties between the time that they were set and the time that the document was spooled off to the printer.

Err Object
Visual Basic uses the Err object to store error information when a runtime error is raised in your Visual Basic component. Because the Err object is stored in TLS, each of your components that are on the same thread share the same copy of that Err object.

Verify that you have completed using the Err object before you make any blocking calls that may permit another Visual Basic component on the same thread to run. Do not make any other out-of-process calls, do not raise events, and do not call DoEvents from within your error handling routine until you have completed using the existing data in the Err object or you may obtain incorrect results.

Never pass the Err object outside your Visual Basic component. The Err object is also a private Visual Basic object. Do not pass it outside the component. When a second object gains access to the Err object, the Err object calls back to the original Visual Basic component on a different STA to use the data. That data may be incorrect for the current object. If you must pass error information outside your component, pass specific data such as the Err.Number and the Err.Description. For more information, visit the following Microsoft Web site:

http://msdn2.microsoft.com/en-us/library/aa716186(VS.60).aspx

