Announcement

Collapse
No announcement yet.

Memory Leak when using COM object

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Memory Leak when using COM object

    So I am using a 3rd part COM object.

    When calling methods within this COM object I get a massive memory leak. I call one method about every 10 to 50ms.

    At first I assumed it was the COM object that was the problem, but now I am not so sure because if I do the exact same thing in VB 2005 Express Edition I do not see the memory leak.

    I was wondering if I was doing anything wrong. I posted code below.

    Here is my PB test code. I kill the program by just ending it in task manager.

    Code:
    #COMPILE EXE
    
    ' Class Indentifiers
    $CLSID_Galil = GUID$("{D70CFDB0-E481-480C-A29C-6F60FC044B38}")
    $CLSID_Event_Events = GUID$("{0FEFD506-A960-448A-97CD-2DC45FCC2091}")
    
    ' Interface Indentifiers
    $IID_IGalil = GUID$("{F1F88B2C-881A-4D82-9F99-60A84691387D}")
    $IID_Int_Events = GUID$("{67E9C73D-896A-440C-88DD-D0AC82AB7569}")
    
    
    ' Interface Name  : IGalil
    ' ClassID         : $CLSID_Galil
    Interface IDBind IGalil
        Member Call libraryVersion <1610743808> () As String
        Member Let address <1610743809> (In Rhs As String<0>)
        Member Call connection <1610743810> () As String
        Member Let timeout_ms <1610743811> (In retval As Long<0>)
        Member Get timeout_ms <1610743811> () As Long
        Member Call command <1610743813> (Opt In command As String<0>, Opt In terminator As String<1>, Opt In ack As String<2>, _
            Opt In trim As Integer<3>) As String
        Member Call commandValue <1610743814> (Opt In command As String<0>) As Double
        Member Call programUpload <1610743815> () As String
        Member Call programDownload <1610743816> (Opt In program As String<0>)
        Member Call programUploadFile <1610743817> (Opt In file As String<0>)
        Member Call programDownloadFile <1610743818> (Opt In file As String<0>)
        Member Call arrayUpload <1610743819> (Opt In PB_name As String<0>) As Variant
        Member Call arrayDownload <1610743820> (In PB_array As Variant<0>, Opt In PB_name As String<1>)
        Member Call arrayUploadFile <1610743821> (Opt In file As String<0>, Opt In names As String<1>)
        Member Call arrayDownloadFile <1610743822> (Opt In file As String<0>)
        Member Call firmwareDownloadFile <1610743823> (Opt In file As String<0>)
        Member Call write <1610743824> (Opt In bytes As String<0>) As Long
        Member Call read <1610743825> () As String
        Member Call sources <1610743826> () As Variant
        Member Call recordsStart <1610743827> (Opt In period_ms As Double<0>)
        Member Call record <1610743828> (Opt In PB_method As String<0>) As Variant
        Member Call sourceValue <1610743829> (In record As Variant<0>, Opt In source As String<1>) As Double
        Member Call sourceUnits <1610743830> (Opt In source As String<0>) As String
        Member Call sourceDescription <1610743831> (Opt In source As String<0>) As String
    End Interface
    
    ' Interface Name  : Int_Events
    ' ClassID         : $CLSID_Event_Events = GUID$("{0FEFD506-A960-448A-97CD-2DC45FCC2091}")
    Class Class_IGalil_Events $CLSID_Event_Events As Event
    	instance Msg as string, Rec as variant, Sts as long
        Interface IGalil_Events $IID_Int_Events 
            Inherit IDispatch
    
            Method onInterrupt <0> (Byval PB_status As Long)
                Sts = PB_status
            End Method
    
            Method onMessage <1> (Byval message As String)
                Msg = acode$(message)
            End Method
    
            Method onRecord <2> (Byval record As Variant)
                Rec = record
            End Method
    
        End Interface
    End Class
    
    function pbmain()
    	local GalilCont as IGalil, QR as string, IP as string, GalilDataRecord as variant, uSrcName as string, SrcValue as double, GalilContEvents as IGalil_Events
    	
    	IP = ucode$("192.168.1.151")
    	
    	let GalilCont = NEWCOM CLSID $CLSID_Galil
    	
    	object let GalilCont.address = IP
    	
    	GalilContEvents = CLASS "Class_IGalil_Events"
    	EVENTS FROM GalilCont CALL GalilContEvents
    	
    	QR = ucode$("QR")
    	
    	uSrcName = ucode$("TIME")
    	
    	do
    		OBJECT CALL GalilCont.record(QR) to GalilDataRecord
    		OBJECT CALL GalilCont.sourceValue(GalilDataRecord, uSrcName) to SrcValue
    		sleep 10
    	loop
    	
    end function
    Here is in VB 2005 Express code:
    Code:
    Public Class Form1
        WithEvents g As Galil.Galil
        Private r As Object
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            g = New Galil.Galil
            g.address = ""
            Timer1.Enabled = True
        End Sub
    
        Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
            r = g.record("QR")
            Me.Text = g.sourceValue(r, "TIME").ToString
        End Sub
    End Class
    Thank you,
    Ryan M. Cross

  • #2
    How about using a timer as was done in the VB 2005 Express code.

    1) Create a window.
    2) Create a timer, for example,
    Code:
    SetTimer hWnd, %ID_Of_Timer, 50, Byval %Null
    3) Process timer event.
    Code:
      CASE %WM_TIMER
        IF wParam = %ID_Of_Timer then
      		OBJECT CALL GalilCont.record(QR) to GalilDataRecord
      		OBJECT CALL GalilCont.sourceValue(GalilDataRecord, uSrcName) to SrcValue      
        END IF
    Or

    I don't like this because the goal is to get rid off the DO LOOP.
    Code:
    	do   
    	  VariantClear byval varptr(GalilDataRecord)
    		OBJECT CALL GalilCont.record(QR) to GalilDataRecord
    		OBJECT CALL GalilCont.sourceValue(GalilDataRecord, uSrcName) to SrcValue
    		sleep 10
    	loop
    I don't remember if
    Code:
      GalilDataRecord = EMPTY
    is PB's version of VariantClear or VariantInit.
    Dominic Mitchell
    Phoenix Visual Designer
    http://www.phnxthunder.com

    Comment


    • #3
      By the way, the SDK's wParam is PB's CBWPARAM/CB.WPARAM.
      Dominic Mitchell
      Phoenix Visual Designer
      http://www.phnxthunder.com

      Comment


      • #4
        I rewrote the code as suggested to basically be as close as I could get to the VB 2005 code and I still get the same memory leak. Here is the code:

        Code:
        #COMPILE EXE
        
        #include "WIN32API.inc"
        
        global hWnd as long, GalilCont as IGalil, IP as string, GalilContEvents as IGalil_Events
        
        ' Class Indentifiers
        $CLSID_Galil = GUID$("{D70CFDB0-E481-480C-A29C-6F60FC044B38}")
        $CLSID_Event_Events = GUID$("{0FEFD506-A960-448A-97CD-2DC45FCC2091}")
        
        ' Interface Indentifiers
        $IID_IGalil = GUID$("{F1F88B2C-881A-4D82-9F99-60A84691387D}")
        $IID_Int_Events = GUID$("{67E9C73D-896A-440C-88DD-D0AC82AB7569}")
        
        ' Interface Name  : IGalil
        ' ClassID         : $CLSID_Galil
        Interface IDBind IGalil
            Member Call libraryVersion <1610743808> () As String
            Member Let address <1610743809> (In Rhs As String<0>)
            Member Call connection <1610743810> () As String
            Member Let timeout_ms <1610743811> (In retval As Long<0>)
            Member Get timeout_ms <1610743811> () As Long
            Member Call command <1610743813> (Opt In command As String<0>, Opt In terminator As String<1>, Opt In ack As String<2>, _
                Opt In trim As Integer<3>) As String
            Member Call commandValue <1610743814> (Opt In command As String<0>) As Double
            Member Call programUpload <1610743815> () As String
            Member Call programDownload <1610743816> (Opt In program As String<0>)
            Member Call programUploadFile <1610743817> (Opt In file As String<0>)
            Member Call programDownloadFile <1610743818> (Opt In file As String<0>)
            Member Call arrayUpload <1610743819> (Opt In PB_name As String<0>) As Variant
            Member Call arrayDownload <1610743820> (In PB_array As Variant<0>, Opt In PB_name As String<1>)
            Member Call arrayUploadFile <1610743821> (Opt In file As String<0>, Opt In names As String<1>)
            Member Call arrayDownloadFile <1610743822> (Opt In file As String<0>)
            Member Call firmwareDownloadFile <1610743823> (Opt In file As String<0>)
            Member Call write <1610743824> (Opt In bytes As String<0>) As Long
            Member Call read <1610743825> () As String
            Member Call sources <1610743826> () As Variant
            Member Call recordsStart <1610743827> (Opt In period_ms As Double<0>)
            Member Call record <1610743828> (Opt In PB_method As String<0>) As Variant
            Member Call sourceValue <1610743829> (In record As Variant<0>, Opt In source As String<1>) As Double
            Member Call sourceUnits <1610743830> (Opt In source As String<0>) As String
            Member Call sourceDescription <1610743831> (Opt In source As String<0>) As String
        End Interface
        
        ' Interface Name  : Int_Events
        ' ClassID         : $CLSID_Event_Events = GUID$("{0FEFD506-A960-448A-97CD-2DC45FCC2091}")
        Class Class_IGalil_Events $CLSID_Event_Events As Event
        	instance Msg as string, Rec as variant, Sts as long
            Interface IGalil_Events $IID_Int_Events 
                Inherit IDispatch
        
                Method onInterrupt <0> (Byval PB_status As Long)
                    Sts = PB_status
                End Method
        
                Method onMessage <1> (Byval message As String)
                    Msg = acode$(message)
                End Method
        
                Method onRecord <2> (Byval record As Variant)
                    Rec = record
                End Method
        
            End Interface
        End Class
        
        callback function DlgProc() as long
        	local QR as string, uSrcName as string, SrcValue as double, GalilDataRecord as variant
        	if cb.msg = %WM_TIMER then
                      if cb.wparam = 1 then
                           QR = ucode$("QR")
                           uSrcName = ucode$("TIME")
                           OBJECT CALL GalilCont.record(QR) to GalilDataRecord
                           OBJECT CALL GalilCont.sourceValue(GalilDataRecord, uSrcName) to SrcValue
                           DIALOG SET TEXT hWnd, format$(SrcValue)
                      end if
                elseif cb.msg = %WM_CLOSE then
                      KillTimer(hWnd, 1)
                end if
        end function
        
        function pbmain()
        	local IP as string
        
        	IP = ucode$("")
        	
        	let GalilCont = NEWCOM CLSID $CLSID_Galil
        	
        	object let GalilCont.address = IP
        	
        	GalilContEvents = CLASS "Class_IGalil_Events"
        	EVENTS FROM GalilCont CALL GalilContEvents
           
           DIALOG NEW PIXELS, 0, "", 100, 100, 200, 200, %WS_CLIPCHILDREN or %WS_CAPTION or %WS_SYSMENU or %WS_THICKFRAME or %WS_MINIMIZEBOX or %WS_MAXIMIZEBOX, %NULL TO hWnd
        
        	SetTimer(hWnd, 1, 10, 0)
        
           DIALOG SHOW MODAL hWnd, CALL DlgProc
        	
        end function
        Last edited by RyanCross; 19 Nov 2008, 09:00 AM.
        Thank you,
        Ryan M. Cross

        Comment


        • #5
          Yes it seems something is not getting cleaned up with the PowerBasic program. I thought maybe the VARIANT had something to do with it so I tried calling a method that does not use a VARIANT and I get the same result.

          I replaced this:
          Code:
          OBJECT CALL GalilCont.record(QR) to GalilDataRecord
          OBJECT CALL GalilCont.sourceValue(GalilDataRecord, uSrcName) to SrcValue
          With:
          Code:
          Cmd = ucode$("MGTIME")
          OBJECT CALL GalilCont.commandValue(Cmd) to SrcValue
          And I get the same result. Major memory leak. However, like I stated before if I comment these OBJECT CALLs out there is no leak.
          Thank you,
          Ryan M. Cross

          Comment


          • #6
            Well, if you are going to submit somthing to PB support on this (like code demonstrating the problem).... you might want to do them a favor and include this :
            Add Process Memory Usage Report to any program 1-12-04

            (every time I see "memory leak" here I have to wonder exactly how that was determined).
            Michael Mattias
            Tal Systems Inc. (retired)
            Racine WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              Ryan,
              Have you the ability to still rewrite for VB6???
              I would like to take a look and see if its still a fundamental leak? (Pointers not freed, or something else???)

              That and finding your problem, may actually show me the cause to my problems.

              Unfortunately...I do NOTHING with .NET till its time (and unfortunately time is LONNNNNNNNGGGGggggg overdue)
              Engineer's Motto: If it aint broke take it apart and fix it

              "If at 1st you don't succeed... call it version 1.0"

              "Half of Programming is coding"....."The other 90% is DEBUGGING"

              "Document my code????" .... "WHYYY??? do you think they call it CODE? "

              Comment


              • #8
                I did rewrite it in VB6 and there was no leak.

                So I decided to make an ActiveX DLL wrapper for it in VB6. This worked fine until I had to call a function pointer. I forgot VB6 does not have this ability. So I made a PB DLL to help VB6 call a function pointer, but for some reason if I call this function too fast it crashes my software. No idea why.

                So now I went back to VB.NET to write a wrapper for it and for some reason PB will only successfully call the first method in the COM object. It seems to fail to call anything after, it gets a "Member not found." error. This has become such a pain in the arse.
                Last edited by RyanCross; 20 Nov 2008, 11:29 AM.
                Thank you,
                Ryan M. Cross

                Comment


                • #9
                  Welp turns out the VB.NET thing was my fault I forgot to Sign the Assembly.
                  Thank you,
                  Ryan M. Cross

                  Comment


                  • #10
                    I would be interested in seeing the code in VB6. To date all my attempts work fine (As long as I do not use events, and close the program, then I crash) so I have been struggling to find 1 (just one) example that works without a crash (or even a crash that I can understand why it happend)

                    Glad you found a solution to your problem though
                    Engineer's Motto: If it aint broke take it apart and fix it

                    "If at 1st you don't succeed... call it version 1.0"

                    "Half of Programming is coding"....."The other 90% is DEBUGGING"

                    "Document my code????" .... "WHYYY??? do you think they call it CODE? "

                    Comment


                    • #11
                      Originally posted by RyanCross View Post
                      This worked fine until I had to call a function pointer. I forgot VB6 does not have this ability.
                      Not true. See AddressOff function/operator in your VB6 documentation.

                      Comment


                      • #12
                        How does AddressOf call a function pointer? I believe it only gives you the function pointer. I need an equivalent to CALL DWORD.
                        Last edited by RyanCross; 21 Nov 2008, 09:41 AM.
                        Thank you,
                        Ryan M. Cross

                        Comment


                        • #13
                          How does AddressOf call a function pointer? I believe it only gives you the function pointer.
                          I don't know VB, but if there is no "call by address" facility, you could always create a DLL with your trusty PB compiler to do it ....
                          Code:
                          ' === PB CODE ====
                          #COMPILE DLL 
                          FUNCTION VbCallDword (  BYVAL dwProc AS DWORD, [OPT] param1 as something )... EXPORT AS something
                          
                               CALL DWORD   dwProc  [USING] .... To value
                               FUNCTION = value
                          END FUNCTION
                          '======
                          ' vb-like code 
                          
                            DECLARE FUNCTION vbCallDword LIB "Mylib.dll" .....
                            X = AddressOf (somefunction) 
                            CALL vbCallDWORD (X [,params]]
                          It's a thought...

                          MCM
                          Michael Mattias
                          Tal Systems Inc. (retired)
                          Racine WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                          • #14
                            Michael I did do this, but for some odd reason if I used this function too fast it would crash my software.

                            Does anyone know if there is a CALL DWORD equivalent in VB.NET or C#?

                            This is the code I made for the DLL (AMCall.dll):
                            Code:
                            #COMPILE DLL
                            
                            #INCLUDE "WIN32API.inc"
                            
                            global hInst as long
                            
                            declare sub Dummy_CallPointer_String(byval CBID as long, byval Msg as string)
                            
                            FUNCTION LIBMAIN(BYVAL hInstance AS DWORD, BYVAL lReason AS LONG, BYVAL lReserved AS LONG) AS LONG
                            
                            	hInst = hInstance
                            
                            	SELECT CASE AS LONG lReason
                            
                            		CASE %DLL_PROCESS_ATTACH
                            			LIBMAIN = 1
                            			EXIT FUNCTION
                            
                            		CASE %DLL_PROCESS_DETACH
                            			LIBMAIN = 1
                            			EXIT FUNCTION
                            
                            		CASE %DLL_THREAD_ATTACH
                            			EXIT FUNCTION
                            
                            		CASE %DLL_THREAD_DETACH
                            			EXIT FUNCTION
                            
                            	END SELECT
                            
                            	LIBMAIN = 0
                            
                            END FUNCTION
                            
                            sub Galil_CallPointer_String alias "Galil_CallPointer_String" (byval Code_Ptr as dword, byval ControllerIndex as long, byval StrValue as string) export
                            	if Code_Ptr then call dword Code_Ptr using Dummy_CallPointer_String(ControllerIndex, StrValue)
                            end sub
                            VB6 Declare:
                            Code:
                            Public Declare Sub Galil_CallPointer_String Lib "AMCall.dll" (ByVal Code_Ptr As Long, ByVal ContIndex As Long, ByVal StrValue As String)
                            Thank you,
                            Ryan M. Cross

                            Comment


                            • #15
                              Never mind I will just use events. Still getting used to this COM thing.
                              Thank you,
                              Ryan M. Cross

                              Comment


                              • #16
                                Originally posted by RyanCross View Post
                                How does AddressOf call a function pointer? I believe it only gives you the function pointer. I need an equivalent to CALL DWORD.
                                Ah, I see. Misunderstanding on my part.

                                I thought, by "this" in your original message you were referring to a function pointer (which VB has by AddressOf), not to a means capable of calling functions by their pointer (which VB hasn't, AFIAK).

                                Comment


                                • #17
                                  if I used this function too fast it would crash my software.
                                  Code:
                                  declare sub Dummy_CallPointer_String(byval CBID as long, byval Msg as string)
                                  
                                  sub Galil_CallPointer_String alias "Galil_CallPointer_String" (byval Code_Ptr as dword,_
                                                byval ControllerIndex as long,
                                               byval StrValue as string) export
                                  	if Code_Ptr then call dword Code_Ptr using Dummy_CallPointer_String(ControllerIndex, StrValue)
                                  end sub
                                  VB DECLARE / CALL not shown, but are you sure you have those string parameters defined correctly for use from VB?

                                  MCM
                                  Michael Mattias
                                  Tal Systems Inc. (retired)
                                  Racine WI USA
                                  [email protected]
                                  http://www.talsystems.com

                                  Comment


                                  • #18
                                    So to "fix" the memory leak I wrote my own wrapper COM object in VB.NET 2008. Then I loaded that object into my PB code and obviously the wrapper handled the actual interaction with the Galil COM.

                                    This has "fixed" the issue. I don't know if its a PB issue or a Galil issue. Its just weird how PB interacts fine with the COM object I wrote and the COM object I wrote interacts with the Galil COM object fine. However there's a memory leak if I go directly from PB to Galil.
                                    Thank you,
                                    Ryan M. Cross

                                    Comment

                                    Working...
                                    X