locked
Increasing Memory Usage with Interop Controls in ActiveXControls RRS feed

  • Question

  • Using an interop control inside a VB6 ActiveX Control results in ever-increasing memory use as the form is loaded and unloaded.  This is part of a complex upgrade of a larger app with many forms and controls.  We cannot simply eliminate the ActiveX controls in a single step. 

    What seems to be happening is that the OCX remains loaded until the VB6 app ends.  The Terminate Event in the ActiveX control does not fire when the form is unloaded.  If the Interop control in the ActiveX control is replaced with a VB control, i.e. TextBox, the Terminate Event is fired.

    I have sample code to repro, not sure how to upload it here.

    Any help or suggestions would be greatly appreciated.  Thanks.

    Prove that memory use with the stand-alone interop control is ok
        a) In VB.Net, create an interop control, drop a textbox on the control, name the control InteropTextBox1 and build it
        b) Place InteropTextBox1 on a VB6 form.  Add code to load/unload the form in a loop for 500-1000 iterations.
        c) Build and run the VB6 app.  In task manager, note the memory used is constant or grows only slightly

    Observe the memory use increases with an ActiveX Control.
        a) Place InteropTextBox1 on a VB6 ActiveX control
        b) Add the ActiveX control to the VB6 form from step 1-b above, (replace InteropTextBox1 on the form)
        c) Build and run the VB6 app.  in task manager, note the memory used steadily increases until VB6 freezes with an out of memory error.

    Show that the Terminate Event Does not Fire when there is an Interop Control in the ActiveX control
        a) Add a message box in the UserControl_Terminate Event of the ActiveX control
        b) Add another form to the VB6 app, Form2.  Set Form2 as the Startup Object.  Add a CommandButton to Form 2.  In the Command1_Click Event, add the code:  Form2.Show, Unload Form2.
        c) Run the VB6 app.  Click Command1 3x.  Note that the Terminate event does not fire.  Close the App, note that the Terminate event fires 3x.
        d) Remove InteropTextBox1 from the ActiveX control.
        e) Run the VB6 app.  Click Command1 3x.  Note that the Terminate event fires each time Form2 is unloaded.

    • Edited by PHL_09 Tuesday, November 3, 2009 4:14 PM
    Wednesday, October 28, 2009 1:55 PM

Answers

  • Hi PHL_09,

    I think that I have fixed this issue. The key reason is that ActiveX control has the reference to underlying .NET Interop control. Because there is the reference to .NET control, GC can't collect .NET Interop control. ActiveX control is not released in this situation. Currently I dynamically add .NET Interop control in AxtiveX control(please have a look at the following code snippet). After I remove this .NET control from control collection, the ActiveX terminate event is fired. Also please refer to the following links about Controls property in VB6.

    http://msdn.microsoft.com/en-us/library/aa277578(VS.60).aspx

    frmInteropControl code

    Private Sub Form_Unload(Cancel As Integer)
      Me.UCInteropControl1.cleanup
    End Sub


    ActiveX control code

    Option Explicit
    Dim Cmd1 As InteropTextBox1
    Public MsgBoxOnTerminate As Boolean
    Dim ctl As Control

    Private Sub UserControl_Initialize()
    Set Cmd1 = Controls.Add("InteropTextBox1.InteropTextBox1", "Cmd1")
      Cmd1.Width = 2000
      Cmd1.Top = 500
      Cmd1.Left = 500
      Cmd1.Visible = True
    End Sub

    Private Sub UserControl_Terminate()
        If MsgBoxOnTerminate Then
            MsgBox "UserControl_Terminate", vbInformation, UserControl.Name
        End If
    End Sub
    Public Sub cleanup()
          Controls.Remove "Cmd1"
          Set Cmd1 = Nothing
    End Sub

    If you have any further issues, feel free to tell us.


    Best regards,
    Riquel
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Proposed as answer by Riquel_DongModerator Thursday, November 5, 2009 6:10 AM
    • Marked as answer by Riquel_DongModerator Monday, November 9, 2009 1:06 AM
    • Unmarked as answer by PHL_09 Monday, November 9, 2009 10:15 PM
    • Marked as answer by PHL_09 Tuesday, November 10, 2009 6:42 PM
    Thursday, November 5, 2009 6:10 AM
    Moderator

All replies

  • Hi PHL_09,

    Whether you explicitly set InteropUsercontrol instance variable to Nothing in VB6 code when you decide to upload VB6 ActiveX control. VB.NET and VB6 use the different mechanism to collect unused object. Because InteropUsercontrol is one .NET object, we need to explicitly set its referenced variable to Nothing in this situation. Please let me know the result. If you have further issues, feel free to discuss it here with us.



    Best regards,
    Riquel
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Monday, November 2, 2009 8:02 AM
    Moderator
  • Thank you for the reply.  I don't understand the suggestion.  How would we set the referenced variable to Nothing for a control added at design-time?  Could you provide a snippet of code?  I can send you the code if you'd like to test your suggestion.

    Here are some related posts from this forum:

    http://social.msdn.microsoft.com/Forums/en-US/vbinterop/thread/fad7e9bf-5531-4d94-b9d5-5dbcad78126a

    http://social.msdn.microsoft.com/Forums/en-US/vbinterop/thread/5a159b7a-95a8-4265-82de-d285b4bc806a/

    http://social.msdn.microsoft.com/Forums/en-US/vbinterop/thread/5f7b3d85-5cb9-4aaf-8c91-1cbfd656b1fe/
    Monday, November 2, 2009 2:40 PM
  • Hi PHL_09,

    Please let me know whether you have handled Form unload to set VB6 ActiveX control variable to Nothing. Also in this event handler call one custom method of VB6 ActiveX control to set its child control Interop control variable to Nothing. Try this. If you have any further issues, discuss it here or you can send your project to me via email(see it in my profile).


    Best regards,
    Riquel
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Tuesday, November 3, 2009 7:43 AM
    Moderator
  • An error is raised, "Compile Error: Invalid use of property" when setting the control to Nothing in the Form_Terminate or Form_Unload Event.  This is the expected behavior for standard VB controls, i.e. TextBox, as well as ActiveX controls.

    I tried setting the Interop control variable to Nothing on the Form_Unload, and also tried Dispose.  There was no change in behavior.

    I will email you the sample code for the above examples.

    Tuesday, November 3, 2009 2:36 PM
  • Hi PHL_09,

    In this situation memory leak occurs when we host one Interop control in VB6 ActiveX control. I recommend that you don't use ActiveX control to wrap Interop control to fix this issue. I will research this question to see how to clean up ActiveX control.

    Best regards,
    Riquel
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Wednesday, November 4, 2009 10:47 AM
    Moderator
  • Hi PHL_09,

    Please have a look at this article about CCW. Although it is related to compact framework, the theory is identical. We can know that the culprit is CCW which is not released here. 
    http://blogs.msdn.com/netcfteam/archive/2005/07/24/442612.aspx

    I think that ActiveX control should have reference to CCW for the underlying .NET Interop control, so .NET GC doesn't collect this .NET object. Finally ActiveX control is alive too. I will figure out which method we can use to handle this situation.


    Best regards,
    Riquel
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Wednesday, November 4, 2009 11:46 AM
    Moderator
  • Hi PHL_09,

    I think that I have fixed this issue. The key reason is that ActiveX control has the reference to underlying .NET Interop control. Because there is the reference to .NET control, GC can't collect .NET Interop control. ActiveX control is not released in this situation. Currently I dynamically add .NET Interop control in AxtiveX control(please have a look at the following code snippet). After I remove this .NET control from control collection, the ActiveX terminate event is fired. Also please refer to the following links about Controls property in VB6.

    http://msdn.microsoft.com/en-us/library/aa277578(VS.60).aspx

    frmInteropControl code

    Private Sub Form_Unload(Cancel As Integer)
      Me.UCInteropControl1.cleanup
    End Sub


    ActiveX control code

    Option Explicit
    Dim Cmd1 As InteropTextBox1
    Public MsgBoxOnTerminate As Boolean
    Dim ctl As Control

    Private Sub UserControl_Initialize()
    Set Cmd1 = Controls.Add("InteropTextBox1.InteropTextBox1", "Cmd1")
      Cmd1.Width = 2000
      Cmd1.Top = 500
      Cmd1.Left = 500
      Cmd1.Visible = True
    End Sub

    Private Sub UserControl_Terminate()
        If MsgBoxOnTerminate Then
            MsgBox "UserControl_Terminate", vbInformation, UserControl.Name
        End If
    End Sub
    Public Sub cleanup()
          Controls.Remove "Cmd1"
          Set Cmd1 = Nothing
    End Sub

    If you have any further issues, feel free to tell us.


    Best regards,
    Riquel
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Proposed as answer by Riquel_DongModerator Thursday, November 5, 2009 6:10 AM
    • Marked as answer by Riquel_DongModerator Monday, November 9, 2009 1:06 AM
    • Unmarked as answer by PHL_09 Monday, November 9, 2009 10:15 PM
    • Marked as answer by PHL_09 Tuesday, November 10, 2009 6:42 PM
    Thursday, November 5, 2009 6:10 AM
    Moderator
  • We've tested the workaround of dynamically adding and removing the interop control.  It does cause the UserControl_Terminate Event to fire properly.  For a simple control, it seems to work well.  For a more complex control, not having the design-time control is a significant limitation.  We've also found that the memory use still grows, each time the Form is loaded/unloaded.  We're testing now to assess stability in light of the ever-increasing memory usage.

    [Note: Do not do this in a production app!]  We experimented with calling the System.Runtime.InteropServices.Marshal.ChangeWrapperHandleStrength method, when the Form unloads.  This causes the UserControl to terminate properly.  However, the memory usage is only reduced slightly and if the interop control is in use elsewhere, it is, of course, unstable.  This is interesting, because it seems to hint that decrementing the CCW reference count, so the UserControl will unload, may be the real solution.  We are looking into this further.  If you can shed any light on this, it would be much appreciated.  Thank you.
    Monday, November 9, 2009 10:40 PM
  • They are different technologies. At least you can dynamically add/remove the Interop control in ActiveX control to implement your requirement. If you think that this is one limitation, I recommend that you directly use Interop Usercontrol to replace ActiveX control in VB6. 

    It is best to give more and detailed information to new question so that I can discuss it with you.


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Tuesday, November 10, 2009 6:31 AM
    Moderator
  • I agree that replacing the ActiveX controls with Interop controls is probably the best solution.  Unfortunately, it can't be done now, for this particular upgrade project.  However, I would recommend this approach to anyone facing the issue.  We tried using Marshal.Release to decrement the ccw reference count, hoping this would allow the UserControl to terminate normally.  It did not work.

    Tuesday, November 10, 2009 7:14 PM
  • Hi PHL_09,

    Marshal.Release looks like one promising method. MSDN document shows: "You can also use these methods and the Release method on managed objects to release the COM interfaces represented by the managed object's COM callable wrapper", but I am curious how to get Intptr parameter for CCW in .NET code. Please let me know how you use this method in your situation.
    Best regards,
    Riquel
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Wednesday, November 11, 2009 8:41 AM
    Moderator
  • Dim ptr As System.IntPtr = System.Runtime.InteropServices.Marshal.GetIUnknownForObject(Me)
    
    Wednesday, November 11, 2009 2:35 PM
  • Hi PHL_09,

    You can check when you call the Marshal.Release method, whether the Interop control can be used in VB6 application or not. This will make us to know whether this method can release the COM interfaces in VB6.

    Best regards,
    Riquel
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Thursday, November 12, 2009 8:10 AM
    Moderator
  • Its not 100% clear what test you had in mind.  I put an Interop control on a VB6 form and called Marshal.Release from a Button Click.  Adding Me.Dispose and GC.Collect after the Marshal.Release, makes the control disappear from the VB6 Form.  However, VBForm.Controls.Count does not decrease, and I can see the control properties in the watch window.

    Thursday, November 12, 2009 3:51 PM
  • PHL_09, did you ever get a solution to this?

    I have a very similar problem:
    1) I want to write an ActiveX in C#, to be used in a legacy C++ app
    2) I can do that ok, see the ActiveX in the C++ app and send data to it.
    3) But when I raise an event in the C# app I get an error.  I have tried for several days to fix this, but no joy.
    4) But the C# ActiveX works ok in a VB6 app
    5) And a native VB6 ActiveX works ok in the C++ app!
    6) So, my solution is a VB6 wrapper ActiveX that contains the C# ActiveX


    7) Which all works, except now I have your memory problem!

     

     

    Wednesday, July 21, 2010 3:48 PM