Page 1 of 2 12 LastLast
Results 1 to 40 of 42

Thread: Memory Leaks and GDI

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Memory Leaks and GDI

    Who'd a thought the cleanup phase of a project could become more time consuming then the project itself!

    OK so I've developed what I thought to be a very stable GDI based UserControl, reaching a point where when testing in the IDE, it's error free and quite stable.

    Even monitoring with the Task Manager, complete with it's view expanded to include GDI objects, all looks good.

    The next step was to compile the UserControl into its ActiveX component and continue the testing. Much to my dismay, I ran into problems right away.

    Now with the UserControl being compiled, it's not releasing it's GDI objects, the number just continues to grow. No real errors are cropping up and the GUI looks good and functional, but given enough time, things will certainly implode as memory runs out.

    The second major issue which I'm not sure I should even think about now is that terminating the program results in some fairly nasty Window errors. These could be related to the memory-leak I'm seeing with GDI, so I believe I am going to ignore this problem until the first one is solved.

    So any ideas as to why a compiled UserControl isn't releasing memory even though when run in the IDE it does?

    Thanks.

  2. #2
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Memory Leaks and GDI

    In my signature is a link regarding memory leaks. If you haven't read over it yet, it's worthwhile. At least you can look at specific areas of your control that you may not have been?

    So any ideas as to why a compiled UserControl isn't releasing memory even though when run in the IDE it does?
    I'd ask just the opposite question. Why did you not see leaks during IDE? Are you sure you weren't running two VB instances and looking at the GDI count of the wrong instance?

    Regarding terminating the program and errors? Are you using a manifest for theming? There is a known problem regarding loading of shell32.dll and VB-built usercontrols. Post #12 at this thread talks about it
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  3. #3

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Re: Memory Leaks and GDI

    OK here goes - Well as typical with me, it's a bit complicated. As to why I didn't find the memory leak in the IDE was because it didn't occur there, well not at first.

    You can sort of forget about the project I'm working on as it spawned it's own little sub-project which is the root of all my heartache.

    A short-n-simple description of the problem stems from a UserControl that I created because the VB standard PictureBox didn't have all the features needed. So I set out to create my own with added functionality. In doing this I spent a great deal of time away from the main program/project, working only on the UserControl.

    My version of the PictureBox UserControl supports additional file formats along with some Graphic Primitives all in a neat self-contained little package. I realized that if I kept this UC a general purpose component, I could use it throughout my entire project.

    My UserControl, which I named Graphic PictureBox (GPB), was working so well and turned out to be so easily reused and duplicated as needed, I decided to create other UserControls using my first one developed as the base constituent control on all of my secondary UserControls.

    Think of it just as if you were creating your own UserControl for a project and placing a PictureBox on it as the primary constituent control.

    So it was off to the races, spitting out one UserControl after another, all using my base enhanced GPB UserControl at the root of each. I couldn't be happier with VB's ability to incorporate UserControls so easily into code and have them all running independently as if I had created a complicated multitasking application, but without all of the work and overhead typically involved in doing so.

    While developing my GPB, I was making use of GDI routines to manipulate images and draw selected graphic primitives. I don't care to recreate the wheel, so when it came to managing GDI, DCs and DIBs, I found and downloaded existing code from the web to incorporate into my GPB. I'm sure many here will either recognize many of these routines and could possibly even be the authors of some.

    All along the way I was checking for memory leaks as I had read that GDI was known to be a challenge when it came to the shutdown and cleanup phase. I ran into some of these problems and worked through them right up till incorporating my UC's back into the main project.

    My GPB works without memory leaks in a standalone test, both in the IDE and compiled. Then testing one of my Secondary UserControls, one for displaying an Analog Meter (AM_GPB), also worked without any memory problems both IDE and EXE versions.

    I was reaching the home stretch, starting to merge my UC's into the main project, Processing Plant Monitor (PPM). Here I was running the main project in the IDE, accessing the compiled version of my AM_GPB ActiveX which in turn was calling the compiled version of my GPB ActiveX. This is where the memory leak showed it's ugly head. And if I compile the PPM project and run it as an EXE, it crashes on Exit.

    Code:
    Main Project - Processing Plant Monitor (PPM)
    
        Calls 
    
          ActiveX Analog_Meter (AM_GPB) UserControl
    
            Calls 
    
              ActiveX Graphics PictureBox (GPB) UserControl
    I have some example code, but it's worse then the crazy novel I just wrote above.
    Last edited by stuck-n-past; Oct 16th, 2017 at 10:39 AM.

  4. #4
    Fanatic Member
    Join Date
    Aug 2013
    Posts
    806

    Re: Memory Leaks and GDI

    stuck-in-past - are you using GDI or GDI+? (Or both?) If you're not sure, can you post specific examples of the APIs you are using to accomplish tasks like...

    ...support additional file formats along with some Graphic Primitives...
    I ask because GDI+ can experience leaks in places that GDI does not, so knowing which library you're using will help narrow down potential problem spots.
    Check out PhotoDemon, a pro-grade photo editor written completely in VB6. (Full source available at GitHub.)

  5. #5

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Re: Memory Leaks and GDI

    Sorry - that's the second time I've done that, I believe it all GDI+

    I went ahead and uploaded some test code where I've stripped out as much code as possible and still have it run. I tried configuring things in a project Group but didn't get the same results, so that's why I have the files organized the way they are.

    The top directory should be PPM_TEST which has a project called PPM.vbp. The project has a UserControl that uses a second UserControl which is located in the sub-directory PPM_TEST\GPB_TEST. Before you can run this project, you will need to build the GPB ActiveX. To do this follow the notes under Step #2 - Project #2 below.

    In the GPB_TEST sub-directory there are 3 projects, again because using project groups didn't have the same results as using compiled ActiveX modules, that's why I've set things up the way they are.

    Step #1 - Project #1: GPB_DEV.vbp

    This project allows the testing of the first UserControl, which is equivalent to the real GPB UserControl. This can be run in the IDE or compiled and works without error. Staying in the IDE and going back and forth between RUN and IDLE, the GDI Object count in the Task Manager goes between 338 and 345 - stable, no growth.

    Step #2 - Project #2: GPB_BLD.vbp

    This project will build the ActiveX equivalent of the GPB UserControl. Before building, open the project and go to the UserControl and set the Public property to true.

    Step #3 - Project #3: GPB_TST.vbp

    Use this project to test the GPB UserControl as an ActiveX component, as the UserControl was just built you might have to fix things following the Rebuild notes below. This project can be run in the IDE or compiled and works without error. Staying in the IDE and going back and forth between RUN and IDLE, the GDI Object count in the Task Manager goes between 325 and 332 - stable, no growth.

    Step #4 - Project #4:

    Once the Graphic PictureBox (GPB) UserControl ActiveX is compiled, you can move up a directory and open the PPM project in the top level directory. As you have just built the GPB in Step #2, the PPM project might get mad when loading as the component list might not have the UserControl included, if this is the case you might have to rebuild things:

    Rebuild:

    Start by, removing the UserControl and Form from the project. Once these files are removed, goto components and add the UserControl and then reload the 2 files just removed and the project should run.


    Before starting the program, click on the UserControl Object to check and make sure the Green Check Mark is visible, then do the same with the Form, open the object the Forms object and see if the Green Check Mark is visible. After confirming the Check Marks are there, start the program and monitor the number of GDI objects in the Task Manager.

    With this example code the numbers aren't very spectacular, the GDI count only goes up by a couple of objects per test run. However in the actual program, I'm seeing an increase of around 800 objects each time the program is run and the IDE goes back to IDLE.

    With the GDI object count growing fast, it doen't take long before the IDE runs out of memory and crashes.

    I do apologize as this is a complicated mess and not very easy to test. And if your unwilling to jump through all of these hoops mentioned, I wouldn't blame anyone. I certainly hope that I have put the example code together properly.

    Thanks
    Attached Files Attached Files
    Last edited by stuck-n-past; Oct 16th, 2017 at 02:22 PM.

  6. #6
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Memory Leaks and GDI

    Ok, did all the above. BTW, for each of your projects, we need to remove the compiled OCX and then re-add it. This is because when we compile it, it gets a different GUID and the GUID for your ocx is a dependency listed in the frm & vbp files.

    Also, in one of the projects (forget which) I needed to re-add the png file to the FileSpec property. In the PPM project, these references were "missing" so I had to remove them also:
    Reference=*\G{5ED1647C-CEC1-4CC5-BA0A-56F71DE3931D}#1.0#0#..\..\Class Modules\DC_MEM\DC_MEM_Actx.dll#DC_MEM_Actx
    Reference=*\G{872F464B-C664-48EA-938D-5786BA05A65E}#1.0#0#D:\WINXP\System\DIB_SECTION_Actx.DLL#DIB_SECTION_Actx
    Reference=*\G{DFE58E90-2B4E-45E6-8E50-EB9CFF3E3314}#1.2#0#D:\WINXP\system\NSC.DLL#VB6 SC NEWEST
    Reference=*\G{46FE0332-94A4-4CDE-A581-AFDC92FB326D}#1.0#0#D:\WINXP\system\FILE_Actx.DLL#FILE_ActX
    In all the tests, GDI count in task manager returned to expected values whenever I ran and then unloaded the projects.

    I did not compile the AM_GPB_CTL uc nor did I compile the PPM project. Should I have? Otherwise, in short, no leaks seen.

    Edited: some other comments

    Quote Originally Posted by Tanner_H
    stuck-in-past - are you using GDI or GDI+? (Or both?)
    Tanner, if you don't get the zip projects running, the answer is yes. But GDI+ is started/stopped each time. Used to render and load an image from file then transferring bits to DIB.

    %5
    Last edited by LaVolpe; Oct 16th, 2017 at 03:43 PM.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  7. #7
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: Memory Leaks and GDI

    In a GDI-only case I have seen memory use appear to grow even though the GDI handle count stays stable. I suspect that some GDI memory gets reclaimed in a lazy manner until there is pressure to collect it.

  8. #8

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Re: Memory Leaks and GDI

    Yikes - sorry about the problems with the example projects not running properly.

    Couple of things, first the Rubber Duck Scenario is forever present and performing it's function as with this post I've learned more about some of whats happening.

    I found that running the production PPM code while monitoring the GDI Object Count, the numbers will grow and return to the starting base line number just as it should. However if before starting the program, I open the UserControl Designer and the Form Designer, and leave them open, is when the numbers grow. (Oh please let this happen for you guys as well when you test the code).

    I know leaving them open is not exactly a normal or even suggested practice before running code, but by doing this I'm hoping it will allow you guys to see the problem for yourselves.

    During development it just so happened that I typically had one of the designer windows open before I would start the code which exposed the problem.

    So with fingers crossed, and knowing the production code is much larger and complex then the example code, I still went back to retest. This time around making sure I closed all designer windows. Unfortunately it didn't help, the production code still showed a growing number of resources.

    Below are some of the numbers I recorded during testing.
    Code:
    Testing with multiple GPB UserControl Objects in IDE as an ActiveX
    
        IDE - RUN - GUI  - RUN  - GUI  - RUN  - GUI  - RUN  - IDE   
        Idle        Form          Form          Form          Idle
                    Open          Open          Open
        
    0 - 556 - 621 - 1422 - 1325 - 2118 - 2021 - 2814 - 2717 - 581     GDI Object Count From Task Manager - 1st Run
    0 - 557 - 619 - 1420 - 1323 - 2116 - 2019 - 2812 - 2715 - 579     2nd Run
    
        557 - 62  - 801  - 97   - 793  - 97   - 793  - 97   - 2136      difference in GDI Count
    Through further testing I learned that the GDI Count grows even when the running Form with the GPB UserCotntrol's closes. So not only does a Form load consume resources, but also as it closes. I figured that this is caused by the IDE switching 'Gears' from a Run-Mode to a Design-Mode. Which makes sense as when a Form closes, it stops any running UserControls, but then the IDE takes over and re-starts the UC so that a Form will be properly displayed as development continues - thus allocating resources.

    I'm starting to believe that this might be where the GDI Objects are being held, between the iterations of the IDE starting and stopping the GPB ActiveX. And when testing the un-compiled GPB where the count doesn't grow, well I'm just guessing here but perhaps my code is getting help from the IDE in releasing the GDI objects when variables are going out of scope? Heck who knows.


    Another point I wanted to make here is about the Startup and Shutdown of GDI each time it's accessed. I tried using a 'Global' GDI Token and only perform a startup once, but it turned out to be a mess with the IDE starting and stopping the UserControl between Run-Mode and Design-Mode.


    Thoughts of being an auto-mechanic are looking better and better right about now. Too bad my aging body has turned against me.
    Last edited by stuck-n-past; Oct 16th, 2017 at 09:09 PM.

  9. #9
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Memory Leaks and GDI

    First, I'd check the GDI counts with IDE MDI child forms closed, no immediate window. Otherwise, your data is pretty much pointless. The idea is to see if the GDI count returns to exactly where it was before you ran the project when the project is unloaded normally. In that "memory leak" tutorial, it clearly states that.

    Nearly 3000 GDI objects sounds pretty excessive to me. I don't know what type of app you are designing. Don't know if some sort of collection (containing created hDCs, bitmaps, fonts, etc) is being kept and released when project unloads

    Don't get GDI and GDI+ confused. You start/stop GDI+, not GDI. GDI is gdi32.dll and GDI+ is gdiplus.dll. Those 1000's of GDI objects shown in task manager are not 1000's of GDI+ objects.

    Edited: A good read regarding the GDI count
    Last edited by LaVolpe; Oct 17th, 2017 at 06:34 AM.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  10. #10
    Fanatic Member
    Join Date
    Aug 2013
    Posts
    806

    Re: Memory Leaks and GDI

    Yes, the GDI / GDI+ terminology swapping is very confusing. Then again, I'm not sure of a way to track GDI+ leaks (short of manually reference counting create/destruct events), so if the leak's being tracked via Task Manager, it must be GDI objects.

    Before returning to that - maybe others have cracked this problem, but I've encountered a lot of instability using GDI+ while in designer mode. For user controls, I generally use GDI-based fallbacks in the designer, then switch to GDI+ during runtime. Unless you need actual GDI+ features inside the designer, this may make long IDE sessions more stable (but YMMV and all that).

    As for GDI objects, tracking down leaks can be fairly straightforward. What I do is remove all direct calls to create/destroy APIs (e.g. "CreateDC","CreateCompatibleBitmap", etc), and instead, use "safe" wrapper functions placed in a module:

    Code:
    Private m_DCsCreated as Long, m_DCsDestroyed as Long
    
    Public Function SafeCreateMemoryDC() As Long
        SafeCreateMemoryDC = CreateCompatibleDC(0&)
        If (SafeCreateMemoryDC <> 0) Then
            m_DCsCreated = m_DCsCreated + 1
        Else
            Debug.Print "WARNING!  GDI.SafeGetMemoryDC() failed to create a compatible DC.  DLL Error: #" & Err.LastDllError
        End If
    End Function
    
    Public Sub SafeFreeMemoryDC(ByRef srcDC As Long)
        If (srcDC <> 0) Then
            If (DeleteDC(srcDC) <> 0) Then
                m_DCsDestroyed = m_DCsDestroyed + 1
                srcDC = 0
            Else
                Debug.Print "WARNING!  GDI.SafeFreeMemoryDC() failed to release the requested DC.  DLL Error: #" & Err.LastDllError
            End If
        End If
    End Sub
    This solves a couple of different problems. For one thing, you can keep running track of your create/destroy ratio. (Maybe print them out using a timer, or after every (n) function calls.) If you see the create count rising faster than the delete count, there are two possibilities: either you are creating more objects then you are destroying, or your delete calls are failing, perhaps because you are doing something bad like trying to delete objects that are currently selected into a DC. Debug code similar to the example above will let you know if the latter is the case, by notifying you that your delete events are being called, but they are failing for whatever reason. (I know it's tempting to not check return values from delete functions, but you'll want to start doing that while looking for leaks.)

    I believe LaVolpe covers all this in his memory leak tutorial, so my comments are likely redundant. But when thousands of GDI objects are leaking, IMO it's definitely worth the trouble to start migrating direct Create/Destroy calls to some kind of central module-level function that tracks how many objects you've created and/or destroyed, and notifies you when create/delete calls fail. If you do that, then re-test your program after swapping out each CreateXYZ GDI call for a call to your SafeCreateXYZ function instead, it should be obvious which instance(s) are contributing to the problem.

    (While here, can I ask - are user objects also leaking? Or do they look okay in task manager? Sometimes GDI leaks can indicate user objects that are also leaking, and taking created GDI objects with them.)
    Check out PhotoDemon, a pro-grade photo editor written completely in VB6. (Full source available at GitHub.)

  11. #11
    Fanatic Member
    Join Date
    Aug 2013
    Posts
    806

    Re: Memory Leaks and GDI

    Sorry, should have thought of this too - there are 3rd-party apps that can tell you what kind of GDI objects are leaking. They are not foolproof, but they can help narrow down the problem.

    I use GDIView.
    Check out PhotoDemon, a pro-grade photo editor written completely in VB6. (Full source available at GitHub.)

  12. #12
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,730

    Re: Memory Leaks and GDI

    to keep track Im using an array, each array is a type collection containing hDC, DIB, orgDIB (and more if needed, such as alpha,width,height etc)
    when "destroying" I just loop through the array, doing, for each one:
    if .hDC
    -SelectObject .hdc, .orgDIB
    -DeleteObject .DIB
    -DeleteDC .hdc
    and if you keep adding/removing, like I do, I add .hdc = 0 so I can reuse that index in that array.
    yes, like you notice I use 1 class only, and have a function to keep track of each image. i like this way more then creating multiple classes for the images.

    also, Im using CreateDIBSection when creating a hDC for the image. that is why it need to be destroyed together with the hDC.
    not sure how Tanner_H creates the image, maybe theres no need for the DIB?
    Last edited by baka; Oct 17th, 2017 at 10:19 AM.

  13. #13
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Memory Leaks and GDI

    Quote Originally Posted by baka View Post
    also, Im using CreateDIBSection when creating a hDC for the image. that is why it need to be destroyed together with the hDC.
    If I understand you correctly, you are creating a DC for each image? If so, it is unnecessary waste of GDI resources and would recommend to anyone against such a strategy. Typically, an application only needs one or, at most, a couple of DCs in single threaded apps. The DC is used for backbuffering and/or image selection. Before using BitBlt, just select the bitmap into one of those DCs, draw it or draw on it, then unselect it. Now that DC can be used again for any other image.

    Note: I am not talking about window DCs here.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  14. #14
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,730

    Re: Memory Leaks and GDI

    how? I need to load multiple images and they need to be placed somewhere otherwise how can I use GdiAlphaBlend?
    the DC I create for each image is like a picturebox, a container. to have multiple DC is like having a picturebox for each image.
    also, Im rendering at +60fps and I need all those images all the time.

    if I use a hGraphics, then its better to use gdi+ for the rendering, but gdi32 is faster and I need a DC not hGraphics.
    I used hGraphics before to store the pictures, but after testing, gdi32 is a lot faster.

    after the "clone" leak (from the other thread), I dont get more leaks. its very stable at the moment, even with many hdc.

    why is a hDC worse then hGraphics to store a bitmap?

  15. #15
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Memory Leaks and GDI

    One would have to prove to me that fps is slowed down by SelectObject calls alone. How to use a single DC for 50 images?
    Code:
    X = somevalue: Y = someValue
    SaveDC hDC  ' rarely used API
    For i = 1 To 50
         SelectObject hDC, bitmap(i)
         BitBlt targetDC, X, Y, SizeW, SizeH, hDC, 0, 0, vbSrcCopy
         X = X + someValue ' paint a horizontal strip of images
    Next
    RestoreDC hDC, -1
    Of course, that's a pretty wild example, but it shows how simple a single DC can be used, even in a loop having multiple images. In real life, typically, you'll have a routine like the following to paint the entire image:
    Code:
    Private Sub PaintMyImage(hBitmap As Long, targetDC As Long, _
              destX As Long, destY As Long, bmpWidth As Long, bmpHeight As Long)
        ' of course if you are passing a class, the hBitmap, bmpWidth, bmpHeight would be class members most likely
        Dim hPrev As Long
        hPrev = SelectObject(cachedDC, hBitmap)
        BitBlt targetDC, destX, destY, bmpWidth, bmpHeight, cachedDC, 0, 0, vbSrcCopy
        SelectObject cachedDC, hPrev
    End Sub
    I do agree that GDI+ isn't the speed demon and in a case where high fps was needed, I would create a DIB (32bpp premultiplied if alpha exists). But to need a DC for each picture you cache is overkill and a waste of resources.

    Quote Originally Posted by baka
    I need to load multiple images and they need to be placed somewhere
    A bitmap does not need to reside in a DC for the bitmap to exist

    Quote Originally Posted by baka
    why is a hDC worse then hGraphics to store a bitmap
    I probably wouldn't use GDI+ if high fps was a need. GDI+ is a good tool for loading PNG/TIFF and doing some fancy rendering (rotation, blurring, etc). However, a 32bpp premultiplied DIB prepared for AlphaBlend API would be more ideal for rendering same image often, without dynamic scaling... GDI+ used to initially render to the DIB, then released and AlphaBlend used to render to the screen whenever needed. Using GDI+ alone is not really a bottleneck but is an extra layer that can be avoided.
    Last edited by LaVolpe; Oct 17th, 2017 at 05:16 PM.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  16. #16
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Memory Leaks and GDI

    @stuck-n-past. Are you using calls to SelectObject to select items (pens, brushes, fonts, etc) into DCs? If so and you are not unselecting them before deleting them, a temporary GDI leak occurs. This is because you can't delete something selected into a DC. However, Windows has evolved to remember that the object wasn't truly deleted, in this specific scenario, and attempts to delete them later. How much later? Don't know. So, the GDI count would continue to grow & grow, but eventually return to normal once this deferred cleanup occurs. See this article by Raymond Chen. If that is the case, prevent it by unselecting before deleting.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  17. #17
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,730

    Re: Memory Leaks and GDI

    will definitely try this approach the next time im starting a project.
    or, if I get the energy, to look into the project and change all the functions to use Selectobject.
    so the DIB Im creating is enough and I can use the same dc for every image im loading into the program?
    I need that DIB because I need to have all images with alpha.

    maybe you figure it out LaVolpe, the selectobject could be the cause of the gdi "leak". if so its not really a "leak" but more of a temporary thing that cleans up on its own after some time.

  18. #18
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Memory Leaks and GDI

    Quote Originally Posted by baka View Post
    so the DIB Im creating is enough and I can use the same dc for every image im loading into the program?
    Yes. But remember to select/unselect. And also that DC must be destroyed at some point

    Quote Originally Posted by baka
    maybe you figure it out LaVolpe, the selectobject could be the cause of the gdi "leak". if so its not really a "leak" but more of a temporary thing that cleans up on its own after some time.
    Temporary, hmmm. I'd code it correctly to begin with and not rely on some behavior that may not persist in future versions/patches.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  19. #19

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Re: Memory Leaks and GDI

    Tanner - Thanks for the link to GDIView, I've been using it but didn't want to refer to it in the post worried others might not have it. With Task Manager being a given it seemed one way to simplify an already complicated post. I've looked at other GDI tools aswell, Bear and Dr. Memory, but with GDIView not needing to be installed, I chose it.

    I also wanted to thank you for your example code wrapping calls while tracking and counting functions. I wrote something close to the same in one of my attempts to located the problem myself along with a ton of messages sprinkled throughout using DebugView.

    LaVolpe - I have tripled checked making sure I have matching sets of Selecting and Un-Selecting objects. I was hoping that somewhere along the line I'd find a missing Un-Select, but no luck. I appreciated the link to Raymond Chen's article regarding DC's and the one on Debugging a GDI Resource Leak, learned a lot over the last couple of days.

    baka, I'm dealing with images and graphic primitives such as line, ellipses, arc's, etc. The images are tracked with their corresponding DIB's while the primitives are cataloged with internal array's / collections I created similar to what you spoke of in your post. This is done so that I can keep track of and modify existing primitives, size, color, angle and deletion. Within these collections I've placed debug statements to ensure proper cleanup of pens, brushed and whatnot.

    One of the reasons I went to the trouble of creating such a bizarre set of example projects/code, is that I didn't see a memory leak problem when working in my standalone Graphics PictureBox UserControl project. In this standalone version the project starts out with a base line of objects around 570, climbs to around 1,300, and then promptly returns back to the 570 when stopped. I went for weeks developing without any signs of a problem.

    At first I had thought that using my GPB in it's ActiveX configuration was exposing the problem, but now with some further testing it seems to be more prevalent when used on a Secondary form that is loaded and unload multiple times.

    Single Form Test:

    Using a compiled version of my GPB in it's ActiveX configuration, running basically an empty / clean / new project with one form and my GPB sited on this single main form, the GDI counts were as to be expected. Opening the project without running there was a GDI baseline count somewhere around 302, then a running count of 1022, and finally with the IDE back at Idle the count went back to the baseline of 302.

    Code:
     302 - IDE Idle
    1022 - IDE Running - Form Open
     302 - IDE Idle - (Back to Normal - GDI OK)
    Multiple Form Test:

    Running basically the exact same test as above, clean new project, but with two forms, the GDI count grows. The first form has a single Command Button that loads a Second form that has my GPB sited on it:

    Single Iteration

    Code:
     302 - IDE Idle
     322 - IDE Running - First Form Open
    1051 - IDE Running - First & Second Form Open with GPB
     982 - IDE Running - First Form Open & Second Form Closed (First Sign of Problem)
     302 - IDE Idle
    Several Iterations

    Code:
     322 - IDE Idle
     342 - IDE Running - 1 Form Open
    1073 - IDE Running - 2 Forms Open with GPB
     982 - IDE Running - 1 Form Open (First Sign of Problem)
    1711 - IDE Running - 2 Forms Open
    1620 - IDE Running - 1 Form Open (Continuing Problem)
    2349 - 2 Open
    2258 - 1 Open
    2987 - 2 Open
    2896 - 1 Open
    3625 - 2 Open
    3534 - 1 Open
     342 - IDE Idle
    Obviously If the 2nd form was continuously opened and closed the project will run out of memory quickly. Seeing these numbers had me look into the scope and lifetime of objects / variables thinking that perhaps a reference is being held until the main form is closed, but I don't know what that would be, or if it can be changed to stop the runaway GDI object count.
    Last edited by stuck-n-past; Oct 17th, 2017 at 10:53 PM.

  20. #20
    Fanatic Member
    Join Date
    Aug 2013
    Posts
    806

    Re: Memory Leaks and GDI

    Good morning, stuck. Ready for another day of debugging??

    If you have GDIView available, can you tell us what kind of GDI objects are leaking as you open and close that second form? That could help narrow the problem down. (e.g. a font leak requires a different fix than a pen leak)
    Check out PhotoDemon, a pro-grade photo editor written completely in VB6. (Full source available at GitHub.)

  21. #21

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Re: Memory Leaks and GDI

    Hey Tanner, well been testing and have more information but it's only served to confuse me further. Using GDIView I have the following baseline numbers beginning with the Project Open and Idle, without ever running the project.

    Code:
            Pen ExtPen   Brush Bitmap Font Palette Region DC  Meta E-Meta Other  Total
    Idle	5   0        43    163    101  1       7      115 0    0      0      706

    Next I ran several iterations of Opening and Closing the second form with the Graphic UserControl, and you can see numbers growing, with the Pen, Bitmap and Region. I've been monkeying around with my GPB, changing the number sited on the form and reducing the complexity trying to narrow down the possible areas where the leak could be. In this test, there happens to be a Control Array of 14 GPB's on the second form. I wanted a good number hoping to catch a matching growth of 14 somethings showing up in GDIView, but nothing seems to make sense.

    It's all the same code, no graphic primitives, all the same image on each of the UserControls, so if there's a leak, I would think I'd see a growth of 14 errors showing up somewhere but the numbers don't reflect a corresponding jump in values.

    Code:
            Pen ExtPen   Brush Bitmap Font Palette Region DC  Meta E-Meta Other  Total
    1 Open  5   0        43    164    101  1       7      115 0    0      0      712
    2 Open  15  0        43    182    101  1       13     122 0    0      0      778
    1 Open  13  0        43    182    101  1       13     115 0    0      0      763
    2 Open  25  0        43    206    101  1       27     121 0    0      0      834
    1 Open  23  0        43    203    101  1       27     115 0    0      0      819
    2 Open  28  0        43    224    101  1       34     121 0    0      0      890
    1 Open  28  0        43    222    101  1       34     115 0    0      0      875

    Here back to Idle the numbers are almost exactly back to base line values

    Code:
            Pen ExtPen   Brush Bitmap Font Palette Region DC  Meta E-Meta Other  Total
    Idle    5   0        43    163    101  1       5      115 0    0      0      708	1:50 pm

    What is happening when I close the main form to have all of the numbers return to baseline values? If I could duplicate whatever is happening there and do the same when the second form closes, then all would be solved.
    Last edited by stuck-n-past; Oct 18th, 2017 at 01:31 PM.

  22. #22

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Re: Memory Leaks and GDI

    Been thinking about the difference between a project with a single form and one with many. I would think not all that much happens when a secondary form is unloaded, but when a primary form closes so does the project. There must be a considerable amount of cleanup and shutdown procedures taking place, I/O streams flushed, and closed, Classes Terminated and possibly most important Global variables going out of scope and being erases/deleted causing reference counts to reach zero thus allowing allocated resources to be let go.

    Since I have been testing for all of this time using only a single form, each time that form has been closed, the IDE has been performing it's cleanup hiding any possible memory leaks. Now that I'm testing my code in a larger project where is subjected to being on a Form that's repeatedly loaded and unloaded, all of those cleanup procedures are 'missing', leaving extraneous items behind.

    I'm going to dig into the support routines I've downloaded which handle the DC's, DIB's and such to see if they are holding references active. My only guess is that they could have only been tested in projects where they were only used on primary forms so they never ran into any of the problems I'm seeing.

    Yea I know it's a long stretch, but this is the only thing I can think of as I'm running out of ideas.

  23. #23
    Fanatic Member
    Join Date
    Aug 2013
    Posts
    806

    Re: Memory Leaks and GDI

    Thanks for the update, stuck. Seeing the specific GDI object counts does help narrow the areas of concern. It looks like the GDI object climb is tied to bitmaps, with small contributions from pens and regions.

    The pens and regions could be tied to VB objects themselves, and not the support classes. VB generally creates a default pen for each user control. You can disable this behavior by setting the user control's DrawStyle property to 5-transparent. If you're not using VB's built-in draw commands, there is no penalty to this. Regions can also be created by VB to help with clipping, so I think it's worthwhile to focus on the Bitmap count, which seems to be the biggest contributor to the leak.

    Bitmaps generally come from four GDI commands: CreateBitmap, CreateBitmapIndirect, CreateCompatibleBitmap, and CreateDIBitmap. (Per MSDN.) That should give an idea of places in the support code that might be problematic.

    Personally, as a stray observation - that's a *lot* of support code if your main use-case is just loading PNGs at run-time. Much of the bulk in those support classes exists to work around Win 95-era deficiencies (like runtime discovery and hackarounds for various zlib versions). In 2017, loading and displaying PNGs is much more straightforward, and such workarounds are no longer necessary.

    For loading PNGs specifically, I believe there are codebank contributions that cover the task in about 20 lines of straightforward GDI+ code. Other basic GDI+ tasks can be similarly simple. Perhaps it would be worthwhile to investigate a "lighter" code solution for your current needs...?
    Check out PhotoDemon, a pro-grade photo editor written completely in VB6. (Full source available at GitHub.)

  24. #24

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Re: Memory Leaks and GDI

    Hey Tanner, I'm all for dumping excess code, and I had noticed all of the backwards compatible routines in some of the classes. The oldest OS that I need to support looks to be XP, which works at well since that's one of the ones I'm running.

    The c32bppDIB class certainly has code that will never be used so it's a target for cleaning / replacement. My code does perform a good deal of image manipulation through DIB's which is supported by the classes.

    I've noticed quite a few 'Set' statements on local variables without a corresponding 'Set = Nothing'. VB is probably taking care of it as routines end and the scope changes, but it's got me wondering if they could be causing trouble. I might create a tiny wrapper around the Set statement with a counter that gets incremented for a Set-Object and Decrements on a Set-Nothing. I'd include an 'ID' tag as an argument in the wrapper call so I can track where references grow.

    For now I'm sticking with the idea of the problem revolving around the difference between single form vs multiple form projects. It's the exact same Form, Code and UserControl in both projects so VB must be cleaning up GDI objects when the IDE goes from Run to Idle. That's why a single form project returns to baseline numbers, and when only unloading a secondary form it doesn't due to the IDE still being in a Run Mode. I just can't think of any other reason for the same code to behave differently.

  25. #25
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Memory Leaks and GDI

    Quote Originally Posted by stuck-n-past View Post
    Hey Tanner, I'm all for dumping excess code, and I had noticed all of the backwards compatible routines in some of the classes. The oldest OS that I need to support looks to be XP, which works at well since that's one of the ones I'm running.

    The c32bppDIB class certainly has code that will never be used so it's a target for cleaning / replacement. My code does perform a good deal of image manipulation through DIB's which is supported by the classes.
    As the author of a few of those classes, I wouldn't even use them. They don't leak, but they were designed over 10 years ago when VB usage of GDI+ was relatively new. You can even find code in there that is targeted for Win98 and older. As Tanner stated, VB usage of GDI+ is so commonplace now, I don't see any advantages of doing things the hard way, like the code in those classes.

    I've noticed quite a few 'Set' statements on local variables without a corresponding 'Set = Nothing'. VB is probably taking care of it as routines end and the scope changes, but it's got me wondering if they could be causing trouble.
    VB says it will unload (i.e., Class_Terminate) classes/ucs when the last reference is released. If those 'set' statements are on local variables, they should be released when the routine exits. If used on public/global variables, then they won't be released until the project unloads.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  26. #26

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Re: Memory Leaks and GDI

    Hey I'm not tied to any particular code and the fact that I tend to learn quickly from example, and new to GDI, I decided to search for existing projects to help get over the learning curve when all of this first started.

    The routines I decided to use were chosen because they performed the functionality needed. I had seen that the code supported some older OS's, but that seemed unimportant compared to what they brought to the project.

    The code was a complete nobrainer to implement as it dropped in without a hitch, and I figured that any unwanted routines could be removed later on.

    The routines have a nice modular design so if there’s a better solution in the code bank as Tanner spoke of, then I'm all for swapping out the old for the new. However with the exiting code working as well as it does, a complete replacement would have to show significant improvements to justify the work.

    A question about the VB linker has come to mind from writing this. If the older routines that deal with like Windows 98, are not referenced / called in the project, will they be included in the executable?
    Last edited by stuck-n-past; Oct 19th, 2017 at 06:37 PM.

  27. #27
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: Memory Leaks and GDI

    Linker? Under normal circumstances very little gets statically linked into a compiled VB6 program aside from the compiled OBJ files from your modules and precompiled modules from the VBAEXE6.LIB file.

    DLLs are dynamic, as suggested by the name. They do not get linked using a static linker and thus they are never "included."

    However at runtime "touching" one of them causes it to be loaded into memory and your program's address space. This happens for ActiveX, COM, and completely flat DLLs.

  28. #28

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Re: Memory Leaks and GDI

    One step forward - two steps back.

    I found but a single obvious problem with a created object that was not disposed of properly, but that only accounted for a small percentage of the GDI Objects in question.

    As for the bulk of the leaks, I believe I found that as well and of course it's occurring in a random and intermittent fashion making it all the more difficult to nail down. The problem is that the UserControl Terminate Event is not always firing, which I'm assuming means that there is a reference being held somewhere.

    It's a bit hard to tell, but I jury-rigged a lame workaround that allows me to manually execute the Termination code that would normally be run. When using this, the GDI Count drops within 1 or 2 objects of the baseline numbers. The part I'm not sure about is if I'm introducing additional objects which are being created due to my bogus fix. So it's quite possible that if I correct the Terminate Event issue, the leaks could all but go away.

    The search continues, but now for some reference that's holding onto the UserControl.

    Whatever is holding a reference count active, as the IDE ends execution and returns to Idle, it's able to release it. I headed straight for any Public variables thinking they would be the most likely candidate, but no luck there.

    I tried making my bogus-fix automatic so that I could move on in finishing the project, hoping either I'd stumble across the culprit by accident or perhaps it would just magically come to me. In doing so I put together something of a garbage collector which raised another question.

    Is there anyway to know which is the 'Main' form in a project?

    Thanks.

  29. #29
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Memory Leaks and GDI

    Read over this MSDN topic. While reading, interpret it in the context of your usercontrol. Don't know if it will shed any light.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  30. #30
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Memory Leaks and GDI

    Is there anyway to know which is the 'Main' form in a project?
    No. A form can be loaded and unloaded at any time. When a form that contains your UC loads, the main form may not even be shown yet. Consider the case when we used to have splash forms. Your uc could be on a splash form that is displayed only until the main form finishes its lengthy initialization. Then the splash closes while loading the main form.

    As far as the uc is concerned, its .Parent should be considered the main form. You aren't caching, directly/indirectly, a hard reference to the UC's parent, i.e., Set m_MyParent = UserControl.Parent, are you? If so, that's a circular reference that could keep your control loaded. By indirectly, I mean caching a reference to some other object that has a hard reference to the parent.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  31. #31
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: Memory Leaks and GDI

    Managing GDI objects is entirely deterministic. Resorting to by guess and by golly "heuristic garbage collection" hacks just wallpapers over program bugs.

    If you are ultimately struggling with the issue that a UserControl's Terminate event does not always get called there isn't much of a fix available except to have some explicit CleanUp method you explicitly call for any normal situations.

    Understanding Control Lifetime and Key Events is a useful topic, as is Life and Times of a UserControl Object.

  32. #32

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Re: Memory Leaks and GDI

    Thank you for the links guys, I've been doing a lot of reading and found quite a bit of information regarding GDI Leaks and Object lifetimes, both seem to be popular subjects.

    LaVolpe - Yea I was thinking about automating my 'Hack' while testing by grabbing onto the 'Main' form and tracking when it's destroyed, and from there try and kickoff my forced cleanup routine. But as you mentioned, using the .Parent object within the UC, is basically accessing the 'Main' form for all intensive purposes.

    dilettante - I'm far from giving up on the search for the leak as I can't consider a project done if it's forced to make use of a hack to survive. Although My 'garbage collector' has helped in a couple of ways, one in letting me continue to put the finishing touches on the project, as it were. And to let me know that even though I'm forcing a cleanup, its shown that the solution is within the intermittent Terminate Event not firing.


    LaVolpe - wasn't quite sure if your link would bring up a video or not -


    So... I might try and slowly reduce my entire project down to nothing, sections at a time, until I see the Terminate Event fire consistently. Just to make sure I don't miss anything during all of the resulting compilations / builds, I wrote some quick code to monitor the GDI Object Count. I'm saving the starting count and comparing it again at programs ends. It should let me know whether a leak occurred during a specific run or not.

    I'm actually planning on keeping this code around and included it in all of my projects as a safety net. For the most part it's harmless code and could point out a problem early on, before a project grows in size making it extremely difficult to locate a leak, like what I'm currently facing.

    The real problem of what I'm dealing with is the intermittent aspect of the error. I could be misled and believe I've found the problem only to realize it was just one of the times the Terminate Event decided it would play nice and fire.


    Edit: I forgot to mention that I'm not caching any pointers to the parent object that would be holding onto a reference, but good point to make as it would definitely cause a problem when attempting to shutdown. Again a disturbing fact to all of this is that it's not happening all of the time.
    Last edited by stuck-n-past; Oct 22nd, 2017 at 03:24 AM.

  33. #33

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Re: Memory Leaks and GDI

    Wow, I found some really odd stuff happening because I apparently used a reserved word in a routine name, even though the IDE and Compiler didn't detect any problem. With all of my testing, there wasn't a single error message. Typically I build things in groups until I reach the final phase where all compiled Class Modules and UserControls are pulled together with the main project code. This is the only point at which the follow error showed up:

    Code:
    Ambiguous Name Detected: ~

    It took forever to find out what was causing this, but it came down to using the name 'Release' as a routine name in my UserControl. For months of development no errors popped up, so all appeared to be OK. Even with periodically dropping out of the IDE just to make sure my code would compile into it's final form, ActiveX or whatever. I do this just to weed out errors because there's a slight difference between the IDE and the real world. And even doing this did not expose the error with the routine name I was using.

    Then with all of my code compiled, except the main project, I was loading components and references into the IDE from the project menu. As I clicked on the check mark box and then the OK button to load a UserControl, the ambiguous error showed up and stopped the project from running. And with the error message lacking in any details, I had to slowly strip the code down piece by piece until the culprit was found.

    I've only done preliminary testing, but I'm now seeing consistent firing of the Terminate event!

    Why in the world would these two issues be related, I don't know, but at this point I'm not really sure I care as long as the problem has been resolved. Time will tell, the longer things go without error, the lower my blood pressure will be! (Fingers Crossed).

  34. #34
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: Memory Leaks and GDI

    I'd guess you were making the Release call on some other interface, not your UserControl's primary interface. Normally you don't stumble into such a thing without knowing it unless you are using late binding somewhere. Probably IUnknown::Release
    Last edited by dilettante; Oct 23rd, 2017 at 10:26 AM.

  35. #35
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: Memory Leaks and GDI

    wow good find! Yeah you generally don't want any procedure names that collide with the base COM Object methods.
    All VB objects derive from IUnknown and IDispatch, so you don't want anything colliding with those method names.
    These include:
    AddRef
    QueryInterface
    Release
    GetIDsOfNames
    GetTypeInfo
    GetTypeInfoCount
    Invoke

  36. #36

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Re: Memory Leaks and GDI

    Well I've always been aware of what I would call 'typical' reserved words, but this was a bit different. Even checking with VB's reserved words https://msdn.microsoft.com/en-us/lib...(v=vs.90).aspx, I wouldn't have considered 'Release' to be an issue.

    Regardless of how it's categorized, VB certainly handled it in a bizarre fashion. First allowing its use during IDE sessions, and then even when complied. Only to have it flagged when referenced with other code was odd to say the least. And then making it all the worst, the error message itself was about as cryptic as they come. This one kicked my butt! I can only hope this is all over and done with.

  37. #37
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: Memory Leaks and GDI

    It is perfectly legal to use. I can't imagine how it could cause a problem unless misused, i.e. late-bound calls.

  38. #38
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: Memory Leaks and GDI

    Quote Originally Posted by stuck-n-past View Post
    Well I've always been aware of what I would call 'typical' reserved words, but this was a bit different. Even checking with VB's reserved words https://msdn.microsoft.com/en-us/lib...(v=vs.90).aspx, I wouldn't have considered 'Release' to be an issue.

    Regardless of how it's categorized, VB certainly handled it in a bizarre fashion. First allowing its use during IDE sessions, and then even when complied. Only to have it flagged when referenced with other code was odd to say the least. And then making it all the worst, the error message itself was about as cryptic as they come. This one kicked my butt! I can only hope this is all over and done with.
    It wouldn't fall under a reserved Keyword. Sort of like all the members of a Form or User Control Base Class, you can't also have public members with the same ambiguous name (Count or Enabled for instance). Usually the IDE will tell you the name is an issue before compiling.

    What would be really cool is if you can make an example project showing this particular issue.
    Last edited by DEXWERX; Oct 23rd, 2017 at 11:50 AM.

  39. #39

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2014
    Posts
    553

    Re: Memory Leaks and GDI

    DEX - It's a possibility, I could upload a skeleton of the full project, removing the proprietary information, leaving the ambiguous error behind. I will have to see what that would involve.

    I mentioned earlier that I had cobbled together a small routine to monitor / display GDI objects to help in my development efforts. Here is a quick snap-shot of it's output from my GPB UserControl without any further leaks. I've also implemented a global token for the GDI routines during my hunt for leaks which you can also see below. Sorry it's not formatted well, just a jumble of sloppy debug output.


    Code:
    Baseline Values
    GDI Baseline Objects: 725
    User Baseline Objects: 1100
    InitializeGDIplus ***********GDI STARTUP*****************             3:07:47 PM 
    InitializeGDIplus STARTUP TOKEN =  372055750 
     
    UserControl_Terminate       3:07:50 PM 
    REFERENCE REACHED ZERO - STOP GDI         3:07:50 PM 
    ****TRACKER GDI SHUTDOWN*******           3:07:50 PM 
    
    Completion Handle Counts:
    
           Base    High     Low     Max    Leak
    GDI:    725     786     725      61       0
    User:  1100    1174    1100      74       0
    Keeping track of the GDI objects above has actually helped out already even with it's short and limited use. I'm curious to know if there is a way to do something similar with reference counts?

    Like when unsure about the number of forms that might have been left loaded in VB, one can step through the Forms collection. Would it be possible to walk-through a collections of active references?

    I've recently aged many years hunting for possible problems relating to outstanding references. I can't begin to imagine the amount of time that could of been saved in my search for memory leaks if such code existed.
    Last edited by stuck-n-past; Oct 23rd, 2017 at 03:04 PM.

  40. #40
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,156

    Re: Memory Leaks and GDI

    Quote Originally Posted by stuck-n-past View Post
    Would it be possible to walk-through a collections of active references?
    A built-in Collection of weak references for debug builds would be awesome. I'm currently doing this manually by impl for every class/form/control a mandatory init/term code like this
    vb Code:
    1. '=========================================================================
    2. ' Base class events
    3. '=========================================================================
    4.  
    5. #If DebugMode Then
    6.     Private Sub Class_Initialize()
    7.         DebugInstanceInit STR_MODULE_NAME, m_sDebugID, Me
    8.     End Sub
    9.  
    10.     Private Sub Class_Terminate()
    11.         DebugInstanceTerm STR_MODULE_NAME, m_sDebugID
    12.     End Sub
    13. #End If
    On app termination just checking if debug Collection has less than 4 objects remaining (or other random threshold) and dump in immediate_window/custom_log all info regarding extra objects -- TypeName's, RefCount's, instance context at time of allocation, etc.

    This Collection has saved our dev team inmeasurable hours of bugs hunting, including AVs on app tear-down, mem leaks and general app stability. This and `Break on All Errors` IDE option.

    cheers,
    </wqw>

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width