Results 1 to 13 of 13

Thread: [RESOLVED] GDI+ GdiplusShutdown timing

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Jan 2010
    Posts
    1,103

    Resolved [RESOLVED] GDI+ GdiplusShutdown timing

    VBIDE crashed during UserControl Terminate. I investigated the problem and found GdiplusShutdown caused the crash.

    Code:
    Private Sub Dispose()
    
    If Not hMemoryGraphics = 0 Then
       GdipDeleteGraphics hMemoryGraphics
       hMemoryGraphics = 0
    End if
    
    If Not hMemoryDC = 0 Then
       SelectObject hMemoryDC, hMemoryBmpOld
       SelectObject hMemoryDC, hMemoryFontOld
       DeleteObject hMemoryFont
       DeleteObject hMemoryDC
       hMemoryDC = 0
     End If
    
    Call CleanUp(True)
    If GdipToken <> 0 Then
       Call GdiplusShutdown(GdipToken)
       GdipToken = 0
    End If
    
    End Sub
    After I move up GdiplusShutdown to top, the crash gone. What is the reason? I should Shut down GDI+ then delete GDI+ Graphics?

    Code:
    Private Sub Dispose()
    
    If GdipToken <> 0 Then
       Call GdiplusShutdown(GdipToken)
       GdipToken = 0
    End If
    
    If Not hMemoryGraphics = 0 Then
       GdipDeleteGraphics hMemoryGraphics
       hMemoryGraphics = 0
    End if
    
    If Not hMemoryDC = 0 Then
       SelectObject hMemoryDC, hMemoryBmpOld
       SelectObject hMemoryDC, hMemoryFontOld
       DeleteObject hMemoryFont
       DeleteObject hMemoryDC
       hMemoryDC = 0
     End If
    
    Call CleanUp(True)
    
    End Sub
    Last edited by Jonney; Aug 16th, 2015 at 10:53 AM.

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

    Re: GDI+ GdiplusShutdown timing

    No, I would not suggest trying to shutdown then delete. Is GDI+ startup done for each instance of your usercontrol? Or are you creating a class bound to a public variable?

    In my UC projects. I create a public variable in a module: g_Token As cTokenManager. That class starts GDI+ on initialize event and shuts down GDI+ on terminate event. Each usercontrol has a private variable: cToken As cTokenManager. That reference is released in the UC terminate event after any GDI+ object destruction is done. In the UC initialize event,
    Code:
    If g_Token Is Nothing Then Set g_Token = New cTokenManager ' initialize GDI+
    Set cToken = g_Token
    The reason I do it this way, is that as long as the UC is alive, I want to ensure the g_Token object is not released early. Theoretically, as long as VB is not suppose to release objects while they are referenced any crashes should be avoided.

    Additionally. I add a property to the cTokenManager class that lets me know if GDI+ started up correctly, i.e., didn't fail. And anywhere in my UC where I need to use GDI+ APIs, I first check to see if that property returns True else abort the routine.
    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
    Frenzied Member
    Join Date
    Jan 2010
    Posts
    1,103

    Re: GDI+ GdiplusShutdown timing

    Quote Originally Posted by LaVolpe View Post
    No, I would not suggest trying to shutdown then delete. Is GDI+ startup done for each instance of your usercontrol? Or are you creating a class bound to a public variable?

    In my UC projects. I create a public variable in a module: g_Token As cTokenManager. That class starts GDI+ on initialize event and shuts down GDI+ on terminate event. Each usercontrol has a private variable: cToken As cTokenManager. That reference is released in the UC terminate event after any GDI+ object destruction is done. In the UC initialize event,
    Code:
    If g_Token Is Nothing Then Set g_Token = New cTokenManager ' initialize GDI+
    Set cToken = g_Token
    The reason I do it this way, is that as long as the UC is alive, I want to ensure the g_Token object is not released early. Theoretically, as long as VB is not suppose to release objects while they are referenced any crashes should be avoided.

    Additionally. I add a property to the cTokenManager class that lets me know if GDI+ started up correctly, i.e., didn't fail. And anywhere in my UC where I need to use GDI+ APIs, I first check to see if that property returns True else abort the routine.
    Very good guess. The GdipToken is Private Long in UserControl. UserControl itself used GDI+ and some Classes associated with UserControl also used GDI+.

    Please examine my Code. (I will read AlphaImage again!!!):

    Code:
    Private GdipToken As Long
    Private GDIPlusAvailable As Boolean
    In userControl initialize Sub, I initialize GDI+ and set a flag called GDIPlusAvailable.
    Code:
    Private Sub InitGDIPlus()
           
        TerminateGDIPlus
        
        Dim GdipStartupInput As GdiplusStartupInput
        GdipStartupInput.GdiplusVersion = 1
        
        If (GdiplusStartup(GdipToken, GdipStartupInput) <> 0) Then
            GDIPlusAvailable = False
            GDIPlusFXAvailable = False
        Else
            GDIPlusAvailable = True
            GDIPlusFXAvailable = True
            'Next, check to see if v1.1 is available.  This allows for advanced fx work.
            Dim hMod As Long
            hMod = LoadLibrary("gdiplus.dll")
            If hMod Then
                Dim testAddress As Long
                testAddress = GetProcAddress(hMod, "GdipDrawImageFX")
                If testAddress Then GDIPlusFXAvailable = True Else GDIPlusFXAvailable = False
                FreeLibrary hMod
            End If
        End If
        
    End Sub
     
    Private Sub TerminateGDIPlus()
        If GdipToken <> 0 Then
           Call GdiplusShutdown(GdipToken)
           GdipToken = 0
           GDIPlusAvailable = False
           GDIPlusFXAvailable = False
        End If
    End Sub
    My Images Class used GDI+ (actually,more accurate, Image Class used GDI+ load images), so In UserControl:
    Code:
    Public Property Get Images() As Images
    
    'Returns the reference to the Images Collection
        oImages.IsGDIPlusAvilable = GDIPlusAvailable
        Set Images = oImages
    
    End Property
    In Images Class:
    Code:
    Private m_bIsGDIPlusAvilable As Boolean
    Public Property Get IsGDIPlusAvilable() As Boolean
    
        IsGDIPlusAvilable = m_bIsGDIPlusAvilable
    
    End Property
    
    Public Property Let IsGDIPlusAvilable(ByVal value As Boolean)
    
        m_bIsGDIPlusAvilable = value
    
    End Property
    Images Class will Call Image Class, so In Image Class:
    Code:
    Private m_bIsGDIPlusAvilable As Boolean
    Public Property Get IsGDIPlusAvilable() As Boolean
    
        IsGDIPlusAvilable = m_bIsGDIPlusAvilable
    
    End Property
    
    Public Property Let IsGDIPlusAvilable(ByVal value As Boolean)
    
        m_bIsGDIPlusAvilable = value
    
    End Property
    
    'GDI+ LoadImage
    Last edited by Jonney; Aug 16th, 2015 at 11:33 AM.

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

    Re: GDI+ GdiplusShutdown timing

    If you have multiple UCs loaded, then you would have called GDI+ startup multiple times? If so, do you get a different token for each time you called startup? If not, that is likely the problem. First UC to terminate shuts down GDI+ and subsequent UCs crash when they begin to call GDI+ APIs. If they are all different tokens, does that really mean GDI+ is running multiple instances? I don't know the answer to that question. Personally, just 1 token per project is all I would recommend.

    If that isn't the issue, maybe this is? Is it possible you did not destroy some GDI+ object you created and that is causing the crash. I'm sure you are calling GdipDisposeImage, but there are other less obvious things to destroy to: matrix, pen, brush, etc.
    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}

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

    Re: GDI+ GdiplusShutdown timing

    Sounds like a candidate for a class with a Predeclared instance, much as we get for Forms and such. The IDE doesn't give us access to this as far as I know, but you can edit the .CLS file with a text editor after creating it:

    Code:
    VERSION 1.0 CLASS
    BEGIN
      MultiUse = -1  'True
      Persistable = 0  'NotPersistable
      DataBindingBehavior = 0  'vbNone
      DataSourceBehavior  = 0  'vbNone
      MTSTransactionMode  = 0  'NotAnMTSObject
    END
    Attribute VB_Name = "MySingleton"
    Attribute VB_GlobalNameSpace = True
    Attribute VB_Creatable = False
    Attribute VB_PredeclaredId = True
    Attribute VB_Exposed = False
    That way you have an "automagic" instance to use globally and you can avoid creating more (too bad you can't make additional instances non-creatable to get a true singleton). Calls to members have the "As New penalty" just as preclared Form instances do, but that usually isn't significant.

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

    Re: GDI+ GdiplusShutdown timing

    @dilettante, just being the Devil's advocate. Is there any guarantee that the class won't be destroyed before all of the usercontrols are destroyed? If not, crashes may continue in that scenario since the usercontrols do GDI+ cleanup during their terminate event. If no guarantees, then the usercontrols may likely still need to keep a reference to that class.
    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: GDI+ GdiplusShutdown timing

    Ouch, good point of course. Probably no guarantees, at least I can't think of any regarding the teardown sequence except that deallocation should wait until the last Form unload is complete.

  8. #8

    Thread Starter
    Frenzied Member
    Join Date
    Jan 2010
    Posts
    1,103

    Re: GDI+ GdiplusShutdown timing

    Quote Originally Posted by LaVolpe View Post
    If you have multiple UCs loaded, then you would have called GDI+ startup multiple times? If so, do you get a different token for each time you called startup? If not, that is likely the problem. First UC to terminate shuts down GDI+ and subsequent UCs crash when they begin to call GDI+ APIs. If they are all different tokens, does that really mean GDI+ is running multiple instances? I don't know the answer to that question. Personally, just 1 token per project is all I would recommend.

    If that isn't the issue, maybe this is? Is it possible you did not destroy some GDI+ object you created and that is causing the crash. I'm sure you are calling GdipDisposeImage, but there are other less obvious things to destroy to: matrix, pen, brush, etc.
    Confirm that the token is different. For multiple UCs, each uc call GDI+ startup once.

    AlphaImage has center management, I know it is very good method. But I just leave it at first.

    We know .NET wrapped GDI+, If multiple same .NET application use System.Drawing class, they also create GDI+ token once only, I don't think so.

    Anyway, I move done the termination GDI+ code again, strange the crash gone when I am testing in my office. Last night, I can reproduce the crash 100%. I will test again after I come back. I suspect some other problem.

  9. #9

    Thread Starter
    Frenzied Member
    Join Date
    Jan 2010
    Posts
    1,103

    Re: GDI+ GdiplusShutdown timing

    OK. I found the crash by calling GdipDeleteGraphics hMemoryGraphics.

    A little complicated I describe:
    hMemoryGraphics is created by:
    Code:
    GdipCreateFromHDC hMemoryDC, hMemoryGraphics
    hMemoryDC is memory DC created by:
    Code:
    lhDC = GetDC(0)
    hMemoryDC  = CreateCompatibleDC(lhDC)
    ReleaseDC 0, lhDC
    Previously, I just delete the graphics one time only,when re-creating memory DC or Dispose.
    Now I create the graphics once necessary and destroy immediately after use. The price is create-use-destroy.

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

    Re: GDI+ GdiplusShutdown timing

    GDI & GDI+ interoperability, per MSDN, states that while GDI+ graphics is associated with GDI hDC, do not access the hDC (pens, brushes, bitmap, etc) until GDI+ graphics is released. Consider the hDC locked. If you accessing that hDC, maybe it is the reason for the crash.

    Otherwise if you are creating the DC then wrapping GDI+ on it and never using the DC, I don't see a reason for any crashes as long as you destroy the GDI+ graphics object, then destroy the GDI hDC in that order.
    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}

  11. #11

    Thread Starter
    Frenzied Member
    Join Date
    Jan 2010
    Posts
    1,103

    Re: GDI+ GdiplusShutdown timing

    Quote Originally Posted by LaVolpe View Post
    GDI & GDI+ interoperability, per MSDN, states that while GDI+ graphics is associated with GDI hDC, do not access the hDC (pens, brushes, bitmap, etc) until GDI+ graphics is released. Consider the hDC locked. If you accessing that hDC, maybe it is the reason for the crash.
    yes. totally agree.

    Quote Originally Posted by LaVolpe View Post
    Otherwise if you are creating the DC then wrapping GDI+ on it and never using the DC, I don't see a reason for any crashes as long as you destroy the GDI+ graphics object, then destroy the GDI hDC in that order.
    I saw someone using this technique in .NET:
    this.hMemoryDCClone = new IntPtr(this.hMemoryDC);
    this.hMemoryGraphics = Graphics.FromHdc(this.hMemoryDCClone);
    How apply the same thing in VB6?
    Code:
    Private hMemoryDCClone As Long
    hMemoryDCClone = hMemoryDC
    GdipCreateFromHDC hMemoryDCClone, hMemoryGraphics

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

    Re: GDI+ GdiplusShutdown timing

    The DC handle doesn't change as long as the DC exists. Not sure what "new IntPtr" accomplishes when it is passed to Graphics.FromHdc since the hDC handle that the IntPtr is wrapped on didn't change.

    With VB6, simply recommend destroying hGraphics when it is no longer needed. If you need to select/remove any DC objects, then destroy hMemoryGraphics, unselect/select DC objects, recreate hMemoryGraphics.

    With the AIC, I create & destroy during the rendering process because I do not own the destination DC.
    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}

  13. #13

    Thread Starter
    Frenzied Member
    Join Date
    Jan 2010
    Posts
    1,103

    Re: GDI+ GdiplusShutdown timing

    Quote Originally Posted by LaVolpe View Post
    The DC handle doesn't change as long as the DC exists. Not sure what "new IntPtr" accomplishes when it is passed to Graphics.FromHdc since the hDC handle that the IntPtr is wrapped on didn't change.
    I saw few professioner in C# doing such thing. I tried to find the code but no luck.

    With VB6, simply recommend destroying hGraphics when it is no longer needed. If you need to select/remove any DC objects, then destroy hMemoryGraphics, unselect/select DC objects, recreate hMemoryGraphics.

    With the AIC, I create & destroy during the rendering process because I do not own the destination DC.
    I will keep in mind. I am just a bit worried about the cost of "create-use-destroy". After Changed all occurrences, the speed and memory seems no heavily affected.

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