BUG: Class Terminate Event May Not Fire When You Use a UDT That Contains a Dynamic Array (320106)



The information in this article applies to:

  • Microsoft Visual Studio, Professional Edition 6.0
  • Microsoft Visual Studio 6.0 SP1
  • Microsoft Visual Studio 6.0 SP2
  • Microsoft Visual Studio 6.0 SP3
  • Microsoft Visual Studio 6.0 SP4
  • Microsoft Visual Studio, Enterprise Edition 6.0

This article was previously published under Q320106

SYMPTOMS

If a class has a public user-defined type (UDT) that contains a dynamic array as a property, the Terminate event of the class may fail to fire when the client accesses the dynamic array directly through the public UDT property. This may lead to memory leak.

RESOLUTION

To work around this problem, use one of the following methods:
  • Do not select Optimize for Fast Code when you compile the client project.
  • Replace the string array inside the UDT with a Variant.
  • Do not access the string array elements directly from the UDT that is returned.
For more information about how to implement these workarounds, refer to the "More Information" section.

STATUS

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

MORE INFORMATION

Steps to Reproduce Behavior

Create the Server

  1. Create an ActiveX DLL project named ReproServer in Visual Basic 6.0. A default class, Class1, is created.
  2. Add the following code to Class1:
    Public Type T
        sFields() As String
    End Type
    
    Private m_T As T
    
    Private Sub Class_Initialize()
        MsgBox "Class_Initialize"
    End Sub
    
    Public Property Get Data() As T
        ReDim m_T.sFields(1)
        m_T.sFields(0) = "Hello"
        Data = m_T
    End Property
    
    Private Sub Class_Terminate()
        MsgBox "Class_Terminate"
    End Sub
    					
  3. Add another class named Collection1.
  4. Add the following code to Collection1:
    'Local variable to hold collection.
    Private m_Col As Collection
    
    Public Function Add(sKey As String) As Class1
        'Create a new object.
        Dim objNewMember As Class1
        Set objNewMember = New Class1
    
        If Len(sKey) = 0 Then
            m_Col.Add objNewMember
        Else
            m_Col.Add objNewMember, sKey
        End If
    
        'Return the object that is created.
        Set Add = objNewMember
        Set objNewMember = Nothing
    End Function
    
    Public Property Get Item(vntIndexKey As Variant) As Class1
        'This is used when you reference an element in the collection.
        'vntIndexKey contains either the Index or Key to the collection,
        'which is why it is declared as a Variant.
        'Syntax: Set obj = x.Item(xyz) or Set obj = x.Item(5)
      Set Item = m_Col(vntIndexKey)
    End Property
    
    Public Property Get Count() As Long
        Count = m_Col.Count
    End Property
    
    Public Sub Remove(vntIndexKey As Variant)
        m_Col.Remove vntIndexKey
    End Sub
    
    Public Property Get NewEnum() As IUnknown
        'This property allows you to enumerate
        'this collection with the For...Each syntax.
        Set NewEnum = m_Col.[_NewEnum]
    End Property
    
    Private Sub Class_Initialize()
        'Create the collection when this class is created.
        Set m_Col = New Collection
    End Sub
    
    Private Sub Class_Terminate()
        'Destroy the collection when this class is terminated.
        Set m_Col = Nothing
    End Sub
    					
  5. Compile the server, ReproServer.

Create and Test the Client

  1. Create a Standard EXE project named Project1 in Visual Basic. A default form, Form1, is created.
  2. Add a CommandButton control (Command1) to Form1.
  3. Add the following code to Form1:
    Dim c As ReproServer.Collection1
    Private Sub Command1_Click()
        'Add an item (an instance of Class1) with a key named k1.
        c.Add "k1"
        dim s as String
        s = c("k1").Data.sFields(0)
        MsgBox s
        'Remove the item.
        c.Remove "k1"
    End Sub
    
    Private Sub Form_Load()
        'Create an instance of the collection.
        Set c = New ReproServer.Collection1
    End Sub
    					
  4. Press F5 to run the client application, and then click Command1. Notice that three dialog boxes appear that contain the following messages respectively:

    Class_Initialize
    Hello
    Class_Terminate

  5. On the Project menu, click Project Properties.
  6. In the Project Properties dialog box, click the Compile tab. Make sure that Compile to Native Code and Optimize for Fast Code are selected.
  7. Compile the project. Notice that Project1.exe is created.
  8. Run Project1.exe, and then click Command1. Notice that you receive only two messages:

    Class_Initialize
    Hello

    You do not receive the dialog box that contains the Class_Terminate message.

Workarounds

  • Do not select Optimize for Fast Code when you compile the client, Project1.exe.
  • Replace the string array inside the UDT with a Variant. For example, use the following code in Class1:
    Public Type T
        sFields As Variant
    End Type
    
    Private m_T As T
    
    Private Sub Class_Initialize()
        MsgBox "Class_Initialize"
    End Sub
    
    Public Property Get Data() As T
        Dim s(1) As String
        s(0) = "Hello"
        m_T.sFields = s
        Data = m_T
    End Property
    
    Private Sub Class_Terminate()
        MsgBox "Class_Terminate"
    End Sub
    					
  • Do not access the string array elements directly from the UDT that is returned. For example, use the following code in the client application:
    Private Sub Command1_Click()
        'Add an item (instance of Class1) with a key named k1.
        c.Add "k1"
        'Create a temporary structure.
        Dim sT As ReproServer.T
        sT = c("k1").Data
        'Get the field from the temporary structure rather than from that 
        'of the returned object, c("k1").Data.sField(0)
        MsgBox sT.sFields(0)
        'Remove the item.
        c.Remove "k1"
    End Sub
    					

Modification Type:MinorLast Reviewed:8/15/2005
Keywords:kbbug kbnofix KB320106