none
BUG: FillPolygon in combination with TextureBrush does not dispose memory!!

    Question

  • FillPolygon in combination with TextureBrush does not dispose memory correctly.
    Infact i can't make it dispose memory at all..not even GC.Collect() disposes it Sad

    inspect/try the code below and tell me how i can slove this problem.
    If you dont have some software that can see whats loaded in the memory just click on the picturebox a few times to get a outOfMemory exception.

    The code is assuming that you have a WindowsMobile Device application with a picturebox named pictureBox1 in it.



    ***********************************************
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;

    namespace TestingTexturbrushDispose
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
                _image = new Bitmap( pictureBox1.Width, pictureBox1.Height );
            }

            Bitmap _image = null;
            protected override void OnPaint( PaintEventArgs e )
            {
                Graphics gr = Graphics.FromImage( _image );
                using (Bitmap tempBitmap = new Bitmap( _image.Width, _image.Height ))
                {
                    using (Graphics tempGraphics = Graphics.FromImage( tempBitmap ))
                    {
                        //GradientFill.Fill( tempGraphics, new Rectangle( 0, 0, _image.Width, _image.Height ), Color.White, Color.Blue, GradientFill.FillDirection.TopToBottom );
                        tempGraphics.Clear( Color.Red );
                        Point[] points = new Point[] { new Point( 10, 10 ), new Point( 50, 50 ), new Point( 70, 50 ), new Point( 110, 10 ), new Point( 110, 100 ), new Point( 70, 60 ), new Point( 50, 60 ), new Point( 11, 100 ) };
                        using (Brush gradientBrush = new TextureBrush( tempBitmap ))
                        {
                            gr.FillPolygon( gradientBrush, points );
                            //gr.FillRectangle(gradientBrush, new Rectangle(0,0,50,50));
                            gradientBrush.Dispose();
                            tempGraphics.Dispose();
                            tempBitmap.Dispose();
                        }
                    }
                }
                pictureBox1.Image = _image;
                base.OnPaint( e );
            }

            private void pictureBox1_Click( object sender, EventArgs e )
            {
                for (int i = 0; i < 500; i++)
                {
                    this.Invalidate();
                    System.Windows.Forms.Application.DoEvents();
                }
            }
        }
    }
    **************************************************



    If i change the TextureBrush to a Solidbrush there is no problem.
    If i dont call "gr.FillPolygon( gradientBrush, points );" there is no problem.
    So as far as i see it has to be some bug within FillPolygon and/or TextureBrush that wont dispose the used memory

    The momory that isn't relesed is not in the application itself...its in gwes.exe that handles the userinterfaces in windows mobile.

    I have tried this on both a HTC and a Motorola MC75..same result. The OutOfMemory problem occurs much quicker on VGA devices than QVGA devices.

    Please help!

    Ohh...this is just a quick proof of concept...when i use it live i use a gradient bitmap in the texturebrush to be able to paint gradients in other things than a rectangle. But that apperently does not make a diffrence :/
    Thursday, September 11, 2008 10:30 AM

Answers

  • Hi Anders

     

    This is a known issue in .NET CF 2.0, and has been fixed in .NET CF 3.5, as you guessed. To workaround it in the .NET CF 2.0, you can P/Invoke native calls, as shown in the following sample code:

        // Necessary Native Calls ---JesseC

        [DllImport("coredll.dll")]

        private extern static bool Polygon(IntPtr hdc, Point[] points, int

        nCount);

        [DllImport("coredll.dll")]

        private extern static IntPtr CreatePatternBrush(IntPtr hImage);

        [DllImport("coredll.dll")]

        private extern static IntPtr SelectObject(IntPtr hDC, IntPtr hBrush);

        [DllImport("coredll.dll")]

        private extern static bool DeleteObject(IntPtr hBrush);

     

        // For Native ---JesseC

        Bitmap image1;

        // Managed texture brush

        Bitmap bitmapImage = new Bitmap(@"\Program Files\DeviceApplication_Brush\microsoft-logo.jpg");

        IntPtr hDC = IntPtr.Zero;

        IntPtr hBrush = IntPtr.Zero;

        IntPtr hGDIObj = IntPtr.Zero;

        IntPtr hGDIRetObj = IntPtr.Zero;

     

        Point[] pointsDraw = {

            new Point(0, 0),

            new Point(240, 0),

            new Point(240, 294),

            new Point(0, 294)

            };

     

        public Form1()

        {

            // Set native texture brush ---JesseC

            image1 = new Bitmap(bitmapImage);

            hGDIObj = image1.GetHbitmap();

            hBrush = CreatePatternBrush(hGDIObj);

        }

     

     

        protected override void Dispose(bool disposing)

        {

            base.Dispose(disposing);

            // Proper Cleanup ---JesseC

            bitmapImage.Dispose();

            image1.Dispose();

        }

     

        protected override void OnPaint(PaintEventArgs e)

        {

            // Decalrations for P/Invoke ---JesseC

            bool bFillPolygon = false;

     

            // Need Device context ---JesseC

            hDC = e.Graphics.GetHdc();

            hGDIRetObj = SelectObject(hDC, hBrush);

     

            // P/Invoke draws a polygon ---JesseC

            bFillPolygon = Polygon(hDC, pointsDraw, pointsDraw.Length);

     

            // Release of native GDI objects --JesseC

            e.Graphics.ReleaseHdc(hDC);

     

        }

     

    Regards

    Chunsheng Tang

     

    Tuesday, September 16, 2008 2:37 AM
    Moderator

All replies

  • HI

    i think you code can improve more more efficient

    see here :

    public Form1()

            {

                InitializeComponent();

                _image = new Bitmap(pictureBox1.Width, pictureBox1.Height);

            }

     

            Bitmap _image = null;

            protected override void OnPaint(PaintEventArgs e)

            {

                using (Graphics gr = Graphics.FromImage(_image))

                {

                    using (Bitmap tempBitmap = new Bitmap(_image.Width, _image.Height))

                    {

                        using (Graphics tempGraphics = Graphics.FromImage(tempBitmap))

                        {

                            //GradientFill.Fill( tempGraphics, new Rectangle( 0, 0, _image.Width, _image.Height ), Color.White, Color.Blue, GradientFill.FillDirection.TopToBottom );

                            tempGraphics.Clear(Color.Red);

                            Point[] points = new Point[] { new Point(10, 10), new Point(50, 50), new Point(70, 50), new Point(110, 10), new Point(110, 100), new Point(70, 60), new Point(50, 60), new Point(11, 100) };

                            using (Brush gradientBrush = new TextureBrush(tempBitmap))

                            {

                                gr.FillPolygon(gradientBrush, points);

                                //gr.FillRectangle(gradientBrush, new Rectangle(0,0,50,50));                     

                            }

                        }

                    }

                }

                pictureBox1.Image = _image;

                base.OnPaint(e);

            }

     

    thank you
    Friday, September 12, 2008 4:55 AM
  • Im sorry to say that that extra dispose (the using around Graphics gr) does not help at all.
    The OutOfMemory exception still occur.
    The memory is not disposed.
    Friday, September 12, 2008 7:41 AM
  • Hi

    i got work very very strength.

    same code, do need screen shot i can post.

    thank you
    Friday, September 12, 2008 7:51 AM
  • Im trying this on a Windows Mobile 6 Professional
    .Net 2.0 Compact Framework SP1

    What are you using when you get it to work?
    Friday, September 12, 2008 12:07 PM
  • Today i tried the code on .net 3.5. The bug was not there.

    If i guess right that means that noone is going to fix the bug MS is going to say, "upgrade to .Net 3.5 to solve the problem"

    But if someone else has a solution to this problem please share Smile
    Monday, September 15, 2008 2:53 PM
  • Hi Anders

     

    This is a known issue in .NET CF 2.0, and has been fixed in .NET CF 3.5, as you guessed. To workaround it in the .NET CF 2.0, you can P/Invoke native calls, as shown in the following sample code:

        // Necessary Native Calls ---JesseC

        [DllImport("coredll.dll")]

        private extern static bool Polygon(IntPtr hdc, Point[] points, int

        nCount);

        [DllImport("coredll.dll")]

        private extern static IntPtr CreatePatternBrush(IntPtr hImage);

        [DllImport("coredll.dll")]

        private extern static IntPtr SelectObject(IntPtr hDC, IntPtr hBrush);

        [DllImport("coredll.dll")]

        private extern static bool DeleteObject(IntPtr hBrush);

     

        // For Native ---JesseC

        Bitmap image1;

        // Managed texture brush

        Bitmap bitmapImage = new Bitmap(@"\Program Files\DeviceApplication_Brush\microsoft-logo.jpg");

        IntPtr hDC = IntPtr.Zero;

        IntPtr hBrush = IntPtr.Zero;

        IntPtr hGDIObj = IntPtr.Zero;

        IntPtr hGDIRetObj = IntPtr.Zero;

     

        Point[] pointsDraw = {

            new Point(0, 0),

            new Point(240, 0),

            new Point(240, 294),

            new Point(0, 294)

            };

     

        public Form1()

        {

            // Set native texture brush ---JesseC

            image1 = new Bitmap(bitmapImage);

            hGDIObj = image1.GetHbitmap();

            hBrush = CreatePatternBrush(hGDIObj);

        }

     

     

        protected override void Dispose(bool disposing)

        {

            base.Dispose(disposing);

            // Proper Cleanup ---JesseC

            bitmapImage.Dispose();

            image1.Dispose();

        }

     

        protected override void OnPaint(PaintEventArgs e)

        {

            // Decalrations for P/Invoke ---JesseC

            bool bFillPolygon = false;

     

            // Need Device context ---JesseC

            hDC = e.Graphics.GetHdc();

            hGDIRetObj = SelectObject(hDC, hBrush);

     

            // P/Invoke draws a polygon ---JesseC

            bFillPolygon = Polygon(hDC, pointsDraw, pointsDraw.Length);

     

            // Release of native GDI objects --JesseC

            e.Graphics.ReleaseHdc(hDC);

     

        }

     

    Regards

    Chunsheng Tang

     

    Tuesday, September 16, 2008 2:37 AM
    Moderator
  •  

    Hi there,

     

    I've also found when replacing images in a picture box that its best to dispose the image first.

     

    pictureBox1.Image.Dispose

    pictureBox1.Image = _image;

    Thursday, September 25, 2008 5:52 PM
  • Hi, 

    I have tried to create a gradient button in CF 2.0.

    I noticed an OutOfMemoryException whenever I pressed the button several times, which means that it had to be redrawn, and I couldn´t control that exception for a while.

    I changed my code some times...with the "using" clause and with Chunsheng´s code...but the process gwes.exe continued growing until 25 MB approximately and then my application crashed definitely.

    Currently, I am using a Motorola MC75, the same as Anders Olsson.

    Does anyone know any other solution for the gradient button in CF 2.0??

    Thanks in advance.

    Thursday, April 15, 2010 3:22 PM
  • Hi,

    I too have the same out of memory exception after a few minutes of drawing this object on .net CF2.0 SP2,  please can someone tell me what the object that is not being disposed of correctly is, so I can do it manually. 

    I tried your solution Chunsheng TangMSFT, but your solution does not work. 

    I dispose correctly of the e.Graphics.ReleaseHdc (hDc) using all direct P/Invoke,  it draws, but you know what also.. the P/Invoke method does a black line border, whereas the .net cf2 equivalent of Polygon doesn't.  

    Please someone post the code from .net cf3.5 that works. thanks. Maybe it is not the code of the FillPolygon because it looks the same in .netcf2sp2 and .netcf3.5
    I'm guessing its a better garbage collector in the .netcf3.5 am I right?
    I can't install .netcf3.5 on my device because .netcf2 is in my device rom only.

    Anybody at Microsoft have a solution for this?

    Thanks,Hamish.

    Sunday, July 4, 2010 1:07 AM
  • perhaps he forgot to demonstrate use of DeleteObject?  its not used in the sample code.

    can I use DeleteObject(hBrush) ?

    can I use DeleteObject(hGDIRetObj) ?

    should I use DeleteObject on hBrush first before hGDIRetObj?
    Sunday, July 4, 2010 1:21 AM