Microsoft KB Archive/236332

= PRB: Memory Leak and Array Parameters Are Cleared When Method Object in MTS Doesn't Work =

Article ID: 236332

Article Last Modified on 6/18/2001

-

APPLIES TO


 * Microsoft Transaction Services 2.0

-



This article was previously published under Q236332



SYMPTOMS
If an object's method receives an array as a parameter and the object is configured to run in MTS, then the argument is cleared out if the object returns with an error. This only happens under the following circumstances:


 * The parameter is a Visual Basic typed array, as in ParamName as a string, or a SAFEARRAY *.
 * The method is called using early binding (using a variable declared As ClassName and not As Object).
 * The method returns an error (something other that S_OK).

The symptoms experienced vary according to the client language or environment, and as such are detailed here:
 * Visual Basic clients:

The Visual Basic array variable maintains the same structure (number and bounds of dimensions) but the elements in it changes. According to the type of the array, you see the following data in the array elements:


 * Strings: "" (zero-length strings)
 * Numbers (Long, Integer, etc.): 0 (zero)
 * Variants: Empty
 * Dates: #00:00:00#
 * Visual C++ clients:

The SafeArray pointer passed as the method argument is made null, and the information stored within it (dimensions, bounds, elements, etc.) is destroyed.


 * Memory Leak:

The array memory is lost in the MTS package, thus for each call when there is an error there is a memory leak proportional in size to the memory used by the array argument.



CAUSE
The cause is a bug in the stub provided by MTS.



Workaround
The simplest workaround is to use late binding; that is to declare the object As Object instead of the specific class type. To do this in Visual Basic, change the Dim statement for your object variable on which the method is executed. Instead of:

Dim oTest As TestArray.Test

use this: Dim oTest As Object

Note that this method avoids both the argument cleaning and the memory leak.

Note to Visual C++ clients: You have to make your call through IDispatch to use this workaround. This includes packing the method parameters in a DISPPARAMS structure and providing for all the required information in the GetIdsOfNames and Invoke calls. Please review the Platform SDK for more information on this.

If this is not a valid workaround in your scenario another solution is to pass as an argument a copy of the array, not the real array itself.

To accomplish this, make a copy of the array and use that as the call argument. If an error occurs, only the copy is destroyed and you still have the original data. However, in this case the memory leak still occurs.

NOTE: For Visual C++ clients, you need to copy SafeArray, not just alias a pointer to it. To do this use the SafeArrayCopy function in the Oleauto.h file.

If no error occurred in the method call and if you expect the server to modify the contents of this array, then copy back the contents of the argument array to the original variable and continue working as usual. If an error did occurred, use the original array in your error handling (the copy is cleared).



STATUS
Microsoft has confirmed that this is a problem in the Microsoft products that are listed at the beginning of this article.



Steps to Reproduce Behavior
 Create an ActiveX DLL. Name the project "TestArray" and name the default class provided "Test".  Add a method as follows:

Public Sub CleanMyArray(RaiseAnError As Boolean, Data As String)

If RaiseAnError Then Err.Raise 1234

End Sub  Compile the DLL and locate it within Windows Explorer. Create a new empty package in Microsoft Transaction Server and perform drag the DLL into the components right pane to register the TestArray.Test object with MTS. Create a Visual Basic 6.0 Standard EXE test client. Add a reference to the TestArray type library by selecting References from the Project menu and checking the TestArray item. This allows you to declare the object reference as belonging to the appropriate class and use early binding.  Create a button on the Form1 form and add the following code to its Click event handler:

On Error Goto ErrHandler Dim oTest As TestArray.Test Dim aData(1 to 2) As String aData(1) = "vee-bee" aData(2) = "edjez"

Set oTest = New TestArray.Test

'No error is raised. oTest.CleanMyArray False, aData

MsgBox "OK! aData(1) = """ & aData(1) & """"

'Now you raise an error: oTest.CleanMyArray True, aData

Cleanup: Set oTest = Nothing

Exit Sub ErrHandler: MsgBox "ERROR! aData(1) = """ & aData(1) & """" Resume Cleanup

</li> Run the project. Notice two message boxes; one showing the contents are acceptable and the other showing the array contents were cleared.</li></ol>

To work around this problem, use late binding by declaring the test variable As Object. Here is similar code that implements this, please note that the only modification is the Dim statement.

On Error Goto ErrHandler Dim oTest As Object 'We do this instead of TestArray.Test to use late binding Dim aData(1 to 2) As String aData(1) = "vee-bee" aData(2) = "edjez"

Set oTest = New TestArray.Test

'No error is raised: oTest.CleanMyArray False, aData

MsgBox "OK! aData(1) = """ & aData(1) & """"

'Now we raise an error oTest.CleanMyArray True, aData

Cleanup: Set oTest = Nothing

Exit Sub ErrHandler: MsgBox "ERROR! aData(1) = """ & aData(1) & """" Resume Cleanup

When you run this code, notice that the array content is preserved. If you look at the private byte count for the package MTX.exe instance using the Windows NT Performance Monitor notice that in this case there is no leak either.

<div class="references_section">