Search code examples
c#disposeobjectdisposedexceptionwin32exception

System.ComponentModel.Win32Exception: 'Error creating window handle.'


My problem is:

System.ComponentModel.Win32Exception: 'Error creating window handle'.

I know I can solve this problem with Dispose(), but when I use it in the program, I'm displaying another error:

System.ObjectDisposedException: 'Can not access a disposed object. Object name: 'PictureBox'. '

I use the following code:

private void SetUpPuzzle_Click(int parts)
{
    Panel P = new Panel
    {
        Size = new Size(200, 200),
        Location = new Point(394, 62),
    };

    Controls.Add(P);
    Control board = P;
    int total = parts * parts;
    var PB = new PictureBox[total];
    var imgarray = new Image[total];
    var img = User_Image.Image;
    int W = img.Width / (int.Parse(Math.Sqrt(double.Parse(parts.ToString())).ToString()));
    int H = img.Height / (int.Parse(Math.Sqrt(double.Parse(parts.ToString())).ToString()));
    int size = 200 / (int.Parse(Math.Sqrt(double.Parse(parts.ToString())).ToString()));

    for (int x = 0; x < parts; x++)
    {
        for (int y = 0; y < parts; y++)
        {
            var index = x * parts + y;

            imgarray[index] = new Bitmap(W, H);
            using (Graphics graphics = Graphics.FromImage(imgarray[index]))
                graphics.DrawImage(img, new Rectangle(0, 0, W, H),
                                   new Rectangle(x * W, y * H, W, H), GraphicsUnit.Pixel);

            PB[index] = new PictureBox
            {
                Name = "P" + index,
                Size = new Size(size, size),
                Location = new Point(x * size, y * size),
                Image = imgarray[index],
                SizeMode = PictureBoxSizeMode.StretchImage
            };

            PB[index].MouseEnter += Images_M_E;
            PB[index].MouseLeave += Images_M_L;
            PB[index].MouseClick += Form_MouseClick;
            *PB[index].Dispose();
            *board.Controls.Add(PB[index]);
        }
    }
}

When I want to create 10,000 objects

This error is displayed.


Solution

  • My problem is:

    System.ComponentModel.Win32Exception: 'Error creating window handle'.

    Indeed. You are creating way too many controls for a Winforms application.

    And disposing of them doesn't really help because you can't use a disposed object any longer..

    To have this kind of large puzzle (10k pieces) you need to change from using PictureBoxes (or any other Controls) to display the puzzle pieces to a different approach. This has been suggested in the original question but then you only wanted to have 100 pieces, remember?

    The most common approach is this: Keep a list of images (when they are <= 256x256 pixels do put them into an ImageList!) and draw them in the board's Paint event. This will get rid of all the overhead involved with PictureBoxes.

    (Aside: One may think this will not be performant with all the DrawImage calls. But all those PictureBoxes also need to draw all the pixels on all their surfaces, so that is no issue. But they also have to carry the overhead of being (under the hood) fully functional windows (see the error message!), which is why the system can only have a limited number of them; always try to keep the number of controls < 1k!)

    You will have to move the placement logic to the board's Paint event and will also have to change the event model..:

    Instead of having each PictureBox respond to its own events you will have to find a way to do all the work in the board's events. This will have to be diffenrent, depending on the event.

    Since we don't know which event you have and what they do and which data they need for their work, it is hard to give all the necessary details, so I'll just point out a few things..:

    • There will not be a Enter or Leave event you can use. Instead you need to detect entering an area of a piece by testing for it in the MouseMove event. If you keep a List<Rectangle> you can use Rectangle.Contains(e.Location) for this test.

    • You can detect a MouseClick but then will have to find out which area was clicked. If your Enter and Leave logic from the MouseMove is working you can use its result to know where the Click went.

    Similar ideas can be used for all other events; some are simple, some need a little calculation but they will all be fast and pretty easy to implement..

    To optimize performance try to make the image n the right size and use Format32bppPArgb as the pixel format, because it is faster to display.

    Another option is to pull the pixel data right from the original image in the Paint event with the same calculations you use now to create them. (There is a DrawImage overlay that uses two Rectangles, one to determine the target and one for the source area..) This saves GDI handles, at least if you can't use an ImageList.

    Always plan for growth! For a better implementation do create a Piece class. It should hold a Rectangle and an integer index into the ImageList's Images collection. It could also have a method Switch(Piece otherPiece) which would either switch the Rectangles or the indices.

    Good luck :-)