6

I'm developing a BlackJack program which shows a BlackJack Table, cards, etc. The plan is that it'll be playing thousands of hands one after another with an automated strategy.

I have a PlayerSeat UserControl which contains an ItemsControl bound to a ObservableCollection. This CardInHand class contains a BitmapSource named CardImage. When the instance is crated it loads the card image from resources using the following code:

[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);

private BitmapSource GenerateCardImage() {
        Stream TempStream = this.GetType().Assembly.GetManifestResourceStream("BlackJack.Resources.CardImages.Card_" + m_Card.ShortTitle + ".gif");
        System.Drawing.Bitmap sourceBMP = new System.Drawing.Bitmap(TempStream);
        BitmapSource tempBitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
            sourceBMP.GetHbitmap(),
            IntPtr.Zero,
            System.Windows.Int32Rect.Empty,
            BitmapSizeOptions.FromWidthAndHeight(sourceBMP.Width, sourceBMP.Height)
        );
        TempStream.Dispose();
        DeleteObject(sourceBMP.GetHbitmap());
        return tempBitmapSource;
}

The problem is that after I run through ~500 rounds (~4000 hands or ~10000 cards) I end up with a GDI+ error and the application taking up ~400MB of RAM. This grows quickly and is related to the number of hands that have been played.

DeleteObject() is something I had found on another site which said that this is the best way to release the resources from the Bitmap. It's MIGHT be having a small affect, but not what I'm looking for. I've also tried Dispose().

Another site said it had to do with ItemsSource binding. I removed the binding and memory still grew. Inversely I left the binding and removed the code that generates the bitmap. It played 40,000 rounds and did not grow substantially (maybe +20MB over the 40min it was running).

The ObservableCollection is Clear()ed after every round. I've tried nulling the collection, the CardInHand, and the BitmapSource propery, to no avail.

How can I allow these images to display on the screen but also make sure their objects are propery destroyed after they're no longer needed?

Thank you for your time.

Caesar Kabalan
  • 753
  • 1
  • 8
  • 18

1 Answers1

6

So first off, you only have 52 cards. Just create the images up front and keep them around for the life of the application. It is a Black Jack game after all; it is safe to assume that each card will be needed at one point or another.

That said, there is an issue with creating BitmapSource objects from streams. The byte[] held by the stream is not being freed when the stream is disposed. See my own question here. The only reason I didn't vote to close as a duplicate is because I think you should really just create the cards once and be done with it instead of creating these images 10,000+ times.

Community
  • 1
  • 1
Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • This makes sense. If I generate a BitmapSource on application startup I should just be able to reference that correct? It'll just store a reference to the original BitmapSource in the CardInHand class, and not duplicate the data, right? – Caesar Kabalan Aug 28 '11 at 02:54
  • @Caesar: Yes, just create them at start up and store them in a Dictionary or something. Map a card type to the image using an enum or manage them however you like, just don't create a new one every time it is needed. – Ed S. Aug 28 '11 at 03:39
  • 2
    Classic example of the [Flyweight Pattern](http://en.wikipedia.org/wiki/Flyweight_pattern) here. – Cameron MacFarland Aug 28 '11 at 04:41
  • Thanks everyone. This worked perfectly. I ended up doing as Ed S. said. Thank you again! – Caesar Kabalan Aug 28 '11 at 09:14
  • Maybe I am missing something here, but it seems that in the question, the `BitmapSource` is *not* created based on a stream, but based on a bitmap handle retrieved from a `Bitmap` object. – O. R. Mapper Mar 09 '15 at 20:15