Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

GDI+ memory leak problem 1

Status
Not open for further replies.

tedsmith

Programmer
Nov 23, 2000
1,762
AU
I tried the following code (courtesy of strongm) to receive 25k .jpg camera capture pictures from web cameras from 20 other computers via a local network and put in in an image box.
I send the size of the stream first then accumulate the picture up to the size of the stream.
I use indexed winsocks and streams to handle 20 computers effectively at the same time without getting any interference.
The pictures are fine BUT I find I get what looks like a memory leak of about 4k every time a stream is used to show the picture.
The computer soon "runs out of resources" as the available memory shrinks even with only one computer connected.
The winsock part doesnt leak memory (if I stop feeding the accumulated data to the stream)
I use what I presumed to be the clean up routine after every receive and the picture has been fed to the image box.
Can anybody suggest what I have missed?

The following is the guts of the code that processes the picture.
Code:
Sub Winsock_DataArrival(Index As Integer, ByVal bytesTotal As Long)
Dim StreamStats As olelib.STATSTG
Dim gdiImageTarget As Long
Dim GpInput As GdiplusStartupInput
Dim Token As Long
Dim Buffer() As Byte
Dim MessageBuffer() As String
Dim Message As String
If SentStreamSize(Index) = 0 Then
    'no data detected yet
    Winsock(Index).GetData SentStreamSize(Index), vbLong 'size of received data received in advance
Else
    'Start accumulating picture data into the stream
    ReDim Buffer(0) As Byte ' reset input buffer
    Winsock(Index).GetData Buffer, vbArray Or vbByte ' read the bytes of picture array
    myStream(Index).Seek 0, STREAM_SEEK_END
    myStream(Index).Write Buffer(0), bytesTotal ' append to a stream
    myStream(Index).Stat StreamStats, STATFLAG_NONAME
    If SentStreamSize(Index) <= StreamStats.cbSize * 10000 Then
           'we've accumulated all the data
           GpInput.GdiplusVersion = 1
           If GdiplusStartup(Token, GpInput) <> Ok Then
	         ‘Fault
                 MsgBox "Error loading GDI+!", vbCritical
                 BusPicturePresent(Index) = 0
                 Set MainForm.Image1(Index).Picture = LoadPicture("")
            Else
		 ‘show picture
                  GdipLoadImageFromStream myStream(Index),  
                  gdiImageTarget 'load GDI+ image from stream (JPEG)
                  ResetStream Index 'reset that stream for reuse
                  'working with a StdPicture object created from the GDI+ image
                  Set MainForm.ImageCamera(Index).Picture = CreatePictureFromGDIPlusImage(gdiImageTarget)           
            End if                	         
            GdipDisposeImage gdiImageTarget
            SentStreamSize(Index) = 0
      End If
End If
If Token Then GdiplusShutdown Token
End Sub


Sub ResetStream(Index As Integer)
    Dim b(0) As Byte ' declaration to get an HGlobal
    Set myStream(Index) = Nothing
    Set myStream(Index) = CreateStreamOnHGlobal(b(0), True)
End Sub

Function CreatePictureFromGDIPlusImage(gdiImage As Long) As StdPicture
    Dim lpPictDesc As olelib.PICTDESC
    Dim myUUID As olelib.UUID
    Dim hBitmap As Long
    Dim background As Long
    
    olelib.CLSIDFromString IPictureGUID, myUUID
    GdipCreateHBITMAPFromBitmap gdiImage, hBitmap, background
    
    With lpPictDesc
        .cbSizeofStruct = Len(lpPictDesc)
        .hBitmap = hBitmap
        .hpal_or_xExt = 0&
        .picType = PICTYPE_BITMAP
    End With

   Set CreatePictureFromGDIPlusImage = olelib.OleCreatePictureIndirect(lpPictDesc, myUUID, False)
    
End Function
 
A superficial review seems to suggest you've done all the necessary cleanup required by GDI+

The bad news, therefore, is that the issue may be within the wrapper library you are using (which is presumably still the one I advised some years ago). It might be worth looking to see if there has been a new version made available (although, with the slow death of Vb6 I rather suspect this unlikely)
 
Thanks for your reply.
I have painted myself into a corner in that I committed to an installation using the transfer of jpgs as I described and the 16 cameras are already installed!
I would be eternally grateful if you could advises me (with an example code) of an alternated way of decoding the stream or transmitting these pictures via winsock.
Each jpg is a frame from a webcamera sent when movement occurs in front of the camera. It's size is less than 40k and there is one received from one of 16 clients at the total rate of about one every few seconds so it is far less demanding than say a video system or the original one where I had pictures of 1 mb.

Thanks in anticipation!
 
Well, technically the True in

[tt]CreateStreamOnHGlobal(b(0), True[/tt]

means that the stream should be clearing up after itself when the final reference pointer to the stream is released, so all should be good. Which means that it is a far less likely candidate for the apparent memory leak.
 
On further testing I found that the stream part of it is not causing the memory leak. It is in the code that converts the stream to an image box.
I previously sent the contents of a picture box in a property bag but this in effect was sending a bitmap, not a jpg and was 10 times slower.
Any ideas on why the showing of the stream would cause a memory leak?
 
Ah, I think I see the problem. I suspect that you actually have a Gdi memory leak because we are not deleting a transient GDI object (hBitmap) in the CreatePictureFromGDIPlusImage function.

You can verify whether this is the case by downloading the tool found here:
And, if it is the case, then we can fix it with a simple

[tt]DeleteObject hBitmap[/tt]

call after the Set CreatePicture... line.

I leave it as an exercise for the reader to correctly declare the DeleteObject API call
 
Thanks. I'll try it when I get back home end of September. Currently I am dragging a camper trailer around Central Australia. Today visited Urulu, one of the wonders of this world and truly awe inspiring. Photos don't do justice to it.
 
Yes it works thanks to strongm again! No memory loss observed.

Of interest I had to put the DeleteObject before the Set CreatePicture... line instead of after and globally dim hBitmap otherwise it immediately deleted the object before the picture could take effect.

DeleteObject also stopped the picture from showing even when I put it after the load picture statement maybe because it takes time to fully load an image.

So it appears that it is now deleting the previous object and clearing memory for the next conversion to take place.

Is this the correct thing to do or is it likely to cause any other problem?

I declared the DeleteObject using "gdi32.dll"
Also I notice there is a DeleteObject reference in the GDI+ reference. Should I be using that but if so, as it is in a tlb file, how would I declare it?
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top