Solved

How do I manually dispose of this GDI object?

Posted on 2011-03-24
16
467 Views
Last Modified: 2012-05-11
I am having a terrible time trying to manually dispose of this gdi object.  Any suggestions would be appreciated
ptStartLineAt = New Point(objLayoutDetails("Left") + 2, intWarningLineTop)

Open in new window

0
Comment
Question by:rev0304
  • 7
  • 6
  • 2
  • +1
16 Comments
 
LVL 16

Expert Comment

by:ToddBeaulieu
ID: 35210363
Any object that implements IDisposable can be disposed simply by calling the Dispose() method when you're done with it.

Could be elaborate on what the problem is?
0
 
LVL 12

Expert Comment

by:HugoHiasl
ID: 35210423
Did you try

ptStartLineAt.Dispose();


?
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 35210512
You can't Dispose() a Point structure...

You can "reset" a Point by setting it to Nothing.  Then IsEmpty() will return true:  
Dim ptStartLineAt As New Point(25, 50)
        Debug.Print(ptStartLineAt.IsEmpty & " --> " & ptStartLineAt.ToString)
        ptStartLineAt = Nothing
        Debug.Print(ptStartLineAt.IsEmpty & " --> " & ptStartLineAt.ToString)

Open in new window


Output:
False --> {X=25,Y=50}
True --> {X=0,Y=0}

Open in new window

0
 

Author Comment

by:rev0304
ID: 35216100
Good Morning -
- ToddBeaulieu: - IDisposable grants access to dispose methods in which the developer manually disposes of unmanaged objects. The problem is we don't know how to manually dispose GDI objects.  So although IDispose could offer a venue for our solution - we are still trapped by the missing piece - how to delete the GDI Object.

HugoHiasl: - as Idle_Mind mentioned there is no .dispose to a Point object

Idle_mind - Setting the variable that holds the reference to the Point object effectively causes us to lose the reference to said Point in memory.  If the GC included GDI objects then this would be sufficient.  As it stands, further in our code we do something similar by creating new objects using the same variable.  Because the GC doesn't see these GDI objects all that does is orphan the object in memory with no possible way to reach it. This is the crux of our problem.  Without being able to manually/programatically destroy the Point object, we end up with lost objects in memory causing a memory leak.

Thanks for all of your suggestions.  I'm hoping with this further detail you'll have other suggestions that will resolve this issue.   Thanks again.
0
 
LVL 16

Expert Comment

by:ToddBeaulieu
ID: 35216271
Admittedly, I've never worked wit GDI.

But, if a GDI object doesn't support IDisposable, to me that infers it's a managed object.

If that's the case, then setting the sole reference to it in your application marks it for garbage collection.

In this article on Disposing GDI objects, he breaks them out into those that support IDisposable and those that do not.

So again, anything that supports IDisposble must be disposed of when you're done with them. Anything that doesn't support it, should behave like managed references. That's what it seems like to me, anyway. Why would you release a refernce to a Point if you still need it? If you don't then it will be collected for you.

It's objects with handles that you need to worry about. The above article lists those. I really don't see the issue with non-IDisposable objects. Am I missing something?


0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 35216834
"Because the GC doesn't see these GDI objects all that does is orphan the object in memory with no possible way to reach it. This is the crux of our problem.  Without being able to manually/programatically destroy the Point object, we end up with lost objects in memory causing a memory leak."

Can you show the declaration for "ptStartLineAt"?

If it is a MANAGED .Net Point:
http://msdn.microsoft.com/en-us/library/system.drawing.point.aspx

...then it will get AUTOMATICALLY garbage collected.

If we are talking about a true GDI object, then yes, you'd have to manually dispose of it.  If this were the case, though, then you'd be using a low level windows API to both create and destroy the object.  Based on the tiny snippet supplied I don't think this applies to your situation.

Show us more code so we can narrow down what it is were dealing with here...  =)
0
 

Author Comment

by:rev0304
ID: 35216994
Thanks again for helping with this. I've attached the method that we suspect is causing the issue.

The first time I run the job It previewed 2475 records - no problem.  If I then try to do the preview a second time, it dies on record 2,470.  We've run this test several times and it always seems to work the first time but crash the second.

We are using in this code the Point, Rectangle, drawString Method and Draw Line method.  

Maybe I'm not looking in the right area - not sure.  Running the same method several times seems to crash the GDI.

Thanks again.    
Private Sub PrintWarningLine(ByVal e As Graphics, ByVal objPrintArea As Rectangle, ByVal objLayoutDetails As DataRow)
'************************************************************************
        ' Procedure/Function: PrintWarningLine()
        ' Description:
        '       Prints the WarningLine
        '************************************************************************

        '***************************************
        ' Initialize Variables
        '***************************************
        Dim objStringFormat As New StringFormat
        Dim sngTextWidth As Single
        Dim sngTextHeight As Single
        Dim intWarningLineLength As Integer
        Dim intWarningLineTop As Integer
        Dim ptStartLineAt As Point
        Dim ptEndLineAt As Point

        '***************************************
        ' Set String Alignment
        '***************************************
        With objStringFormat
            .Alignment = StringAlignment.Center
            .LineAlignment = StringAlignment.Center
        End With

        '***************************************
        ' Draw Warning String
        '***************************************
        e.DrawString("DO NOT WRITE ABOVE THIS LINE", Me._objFontWarning, Brushes.Black, objPrintArea, objStringFormat)

        '***************************************
        ' Calculate Line Positions
        '***************************************
        With e.MeasureString("DO NOT WRITE ABOVE THIS LINE", Me._objFontWarning)
            sngTextWidth = .Width
            sngTextHeight = .Height
        End With
        intWarningLineLength = (objLayoutDetails("Width") - sngTextWidth - 24) / 2
        intWarningLineTop = objLayoutDetails("Top") + ((objLayoutDetails("Height") - 4) / 2)

        '***************************************
        ' Print Left Top Line
        '***************************************
        ptStartLineAt = New Point(objLayoutDetails("Left") + 2, intWarningLineTop)
        ptEndLineAt = New Point(ptStartLineAt.X + intWarningLineLength, intWarningLineTop)
        e.DrawLine(Pens.Black, ptStartLineAt, ptEndLineAt)
        'deleteobject(New IntPtr(&ptStartLineAt))
        'releaseobject(ptStartLineAt)
        'ptStartLineAt.finalize()
        ptStartLineAt = Nothing
        ptEndLineAt = Nothing

        '***************************************
        ' Print Left Bottom Line
        '***************************************
        ptStartLineAt = New Point(objLayoutDetails("Left") + 2, intWarningLineTop + 2)
        ptEndLineAt = New Point(ptStartLineAt.X + intWarningLineLength, intWarningLineTop + 2)
        e.DrawLine(Pens.Black, ptStartLineAt, ptEndLineAt)
        ptStartLineAt = Nothing
        ptEndLineAt = Nothing

        '***************************************
        ' Print Right Top Line
        '***************************************
        ptStartLineAt = New Point(objLayoutDetails("Left") + 2 + intWarningLineLength + 10 + sngTextWidth + 10, intWarningLineTop)
        ptEndLineAt = New Point(ptStartLineAt.X + intWarningLineLength, intWarningLineTop)
        e.DrawLine(Pens.Black, ptStartLineAt, ptEndLineAt)
        ptStartLineAt = Nothing
        ptEndLineAt = Nothing

        '***************************************
        ' Print Right Bottom Line
        '***************************************
        ptStartLineAt = New Point(objLayoutDetails("Left") + 2 + intWarningLineLength + 10 + sngTextWidth + 10, intWarningLineTop + 2)
        ptEndLineAt = New Point(ptStartLineAt.X + intWarningLineLength, intWarningLineTop + 2)
        e.DrawLine(Pens.Black, ptStartLineAt, ptEndLineAt)
        ptStartLineAt = Nothing
        ptEndLineAt = Nothing
    End Sub

Open in new window

0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 35217019
Where is the Graphics "e"  being passed into this method coming from?

That's probably where the problem lies...

Show us how this puppy gets called...and how often is it called?
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 

Author Comment

by:rev0304
ID: 35217086
e is a parameter coming from a method that handles the event OnPagePrint. (see code for prototype)

The below method is not called directly, it's called with each page print.  This varies with number of records that the user selects.  In this particular case, it's 2,475 times.

Thinking about what you just said -- we just added an e.Graphics.Dispose at the end of this method - I'm testing that right now - I'll update as soon as the test finishes

Thanks!
Private Sub _objPrinterInterface_OnPagePrint(ByRef e As System.Drawing.Printing.PrintPageEventArgs) Handles _objPrinterInterface.OnPagePrint

Open in new window

0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 35217161
For Paint() type events, you are not supposed to call Dispose() on default supplied graphics objects.

You should only be disposing of Graphics that YOU create, as with CreateGraphics().

At any rate, the Point() is simply a STRUCTURE that holds two values.  It doesn't have a GDI handle associated with it.

What is the actual error you're getting?
0
 

Author Comment

by:rev0304
ID: 35217197
Here it is.   Thanks again
Error.jpg
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 35217301
Ok...can you show us more code around the printing process?
0
 

Author Comment

by:rev0304
ID: 35217454
We used code from www.codeproject.com to give the users an alternative print preview. I just went back to the site to get the code to send to you and see that there is now a note that addresses this very problem.

 "If the document contains several thousand pages, caching all those images may cause problems. Windows has a limit of 10,000 GDI objects, and each page image represents at least one. If you use too many GDI objects, your application may crash, or cause other apps to crash. Not nice..."

The author suggests that we convert the page images into streams, store the stread and the create the images on demand.  What do you think of this?  This sounds like a big change to me.  Now I'm wondering if the problem is just with this "enhanced dialog".
(http://www.codeproject.com/KB/printing/CoolPrintPreviewDialog.aspx?display=Mobile&fid=1544884&df=90&mpp=10&noise=3&sort=Position&view=None&fr=71)

Any suggestions would be appreciated but if you want to bail out at this point I totally understand.
0
 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 500 total points
ID: 35217533
I haven't played with it...but the author seems to imply that you can simply replace the normal List<Image> with his newer List<PageImageList>.

Does the downloadable source not have the "fix" in it?
0
 

Author Comment

by:rev0304
ID: 35231308
I found that alternate code - it is commented out in the download.  I'm going to try and play with that.  Thanks so much for all of your help.
0
 

Author Closing Comment

by:rev0304
ID: 35231317
Thanks so much for all of your help
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Article by: jpaulino
XML Literals are a great way to handle XML files and the community doesn’t use it as much as it should.  An XML Literal is like a String (http://msdn.microsoft.com/en-us/library/system.string.aspx) Literal, only instead of starting and ending with w…
Calculating holidays and working days is a function that is often needed yet it is not one found within the Framework. This article presents one approach to building a working-day calculator for use in .NET.
This video discusses moving either the default database or any database to a new volume.
This tutorial demonstrates a quick way of adding group price to multiple Magento products.

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now