Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Drawing Cards with Cards.dll

0.00/5 (No votes)
18 Sep 2003 1  
An article on drawing cards using the cards.dll supplied with windows

Introduction

Article shows how to use cards.dll that ships with windows, turn it into a class, and use it in a simple project that draws the reverse side of each of the 16 types of card backing.

Windows 2000 card backs

Windows XP card backs.

Background

In my programming life, I have always fancied writing a card game. Up until recently, this has been a daunting task, but I decided to bite the bullet and have a go. Over the last six months I have been teaching myself C#, so I decided that this would be the language to write the game in.

Presented below is part of the interface to cards.dll. I have mainly used two articles as my reference material that I found on the web, written originally for C. One is an MSDN article from 1996 by Dave Edson, the other comes from a site called catch22 by James Brown. I have tried to amalgamate these two articles to give a clearer overview of the cards.dll, while providing the code for C#.

Cards.dll

Cards.dll exports five functions which you can use to draw card images. Each of these is listed below, with an overview of each function and a detailed breakdown of what the arguments do. You cannot get directly to the bitmaps held within the dll, however in order to draw them, you supply a graphics device context to be drawn upon.

cdtInit

[DllImport("cards.dll")]
private static extern bool cdtInit( ref int width, ref int height );

cdtInit initializes the cards.dll library. As you can see the method takes two arguments which are stored as the width and height of the card in pixels. The return value is either TRUE for when the cards.dll has been initialized, or FALSE if an error has occurred. The constructor for the cardsdll class will throw an exception if there is an error. This is shown in the snippet below.

if( !cdtInit( ref width, ref height ) )
   throw new Exception ( "cards.dll did not load" );

Although, you can supply a height and width, I have found that by changing these numbers, the card actually stays the same size.

cdtTerm

[DllImport("cards.dll")]
private static extern void cdtTerm();

cdtTerm is the uninitialization method. It cleans up the card resources. This is called in the Dispose() method of the cardsdll class. It is a one time function which needs to be called just before the application finishes.

cdtDraw

[DllImport("cards.dll")]
private static extern bool cdtDraw( IntPtr hdc, int x, int y, 
    int card, int mode, long color );

cdtDraw draws a card. This method takes a number of parameters listed below;

  • IntPtr hdc - Pass in the device context from Graphics.GetHdc().
  • int x - upper left corner of the card.
  • int y - upper left corner of the card.
  • int card - card to draw, (this is dependant upon the mode selected).
  • int mode - mode
    • mdFaceUp - draw face up card
    • mdFaceDown - draw face down card
    • mdHilite - draw face up card inverted
    • mdGhost - draw a ghost card, the card parameter is ignored
    • mdRemove - draw rectangle of background color at x, y
    • mdDeckX - draw an X
    • mdDeckO - draw an O
  • long color - table background color (only required for mdGhost and mdRemove) or when drawing the cross hatch card back.

The return value is TRUE if the draw was successful, and FALSE otherwise.

cdtDrawExt

[DllImport("cards.dll")]
private static extern bool cdtDrawExt( IntPtr hdc, int x, int y, 
    int dx, int dy, int card, int type, long color );

cdtDrawExt also draws a card. The difference is that by supplying arguments dx and dy, the size of the card can be varied.

cdtAnimate

cdtAnimate animates the backs of certain cards. In windows 2000 and previous the following cards animate.

  • ROBOT - Meter moves over 4 frames
  • CASTLE - Bats flapping around castle over 2 frames
  • ISLAND - Sun sticks tongue out over 4 frames
  • CARDHAND - Cards running up and down sleeve over 4 frames

Call cdtAnimate every 250 ms for proper animation speed. The return value is TRUE if the animation was successful, and FALSE when the animation has finished.

[DllImport("cards.dll")]
private static extern bool cdtAnimate( IntPtr hdc, int cardback, 
    int x, int y, int frame );

Bitmap numbering

One final point to note about the dll is the numbering of the bitmaps within it. The lowest card (zero) is the Ace of Clubs, next card is the Ace of Diamonds, etc. What happens is that the 52 playing cards rotate from Clubs, Diamonds, Hearts to Spades and back to Clubs again, starting at the Ace and working up to the King. I have provided 2 enumerations for the suit and rank of card.

  public enum eSUIT : int
  {
    CLUBS   = 0,
    DIAMOND = 1,
    HEARTS  = 2,
    SPADES  = 3
  }

  public enum eRank : int
  {
    ACE   = 0,
    TWO   = 1,
    THREE = 2,
    FOUR  = 3,
    FIVE  = 4,
    SIX   = 5,
    SEVEN = 6,
    EIGHT = 7,
    NINE  = 8,
    TEN   = 9,
    JACK  = 10,
    QUEEN = 11,
    KING  = 12
  }

A card value can be calculated using the simple formula

card = rank + suit * 4;

For example, the 4 of Diamonds is

card = ( (int)eRank.FOUR + (int)eSUIT.DIAMOND ) * 4;

Giving the value to card of 13.

The 16 card back designs as shown in the images are cards 53 through to 68. Again, I have provided an enumeration for this.

  public enum eBACK : int
  {
    CROSSHATCH = 53, /* XP = CROSSHATCH */ 
    WEAVE1     = 54, /* XP = SKY */
    WEAVE2     = 55, /* XP = MINERAL */
    ROBOT      = 56, /* XP = FISH */
    FLOWERS    = 57, /* XP = FROG */
    VINE1      = 58, /* XP = MOONFLOWER */
    VINE2      = 59, /* XP = ISLAND */
    FISH1      = 60, /* XP = SQUARES */
    FISH2      = 61, /* XP = MAGENTA */
    SHELLS     = 62, /* XP = SANDDUNES */
    CASTLE     = 63, /* XP = SPACE */
    ISLAND     = 64, /* XP = LINES */
    CARDHAND   = 65, /* XP = TOYCARS */
    UNUSED     = 66, /* XP = UNUSED */
    THE_X      = 67, /* XP = THE_X */
    THE_O      = 68  /* XP = THE_0 */
  }

I have not investigated whether the cards in XP animate or not. (Maybe another article!)

cardsdll class

In my code example, each of the interfaces to the dll are private, so in order to use them I have made some public functions. The main one of these I have used is to draw the backs of the cards.

public bool drawCardBack( IntPtr hdc, int x, int y, eBACK back )

Inside the actual function is a call to cdtDraw(. . .) with the last two arguments fixed at mdFaceDown and 0. mdFaceDown is a constant declaration in the class as one of the card types.

Similarly, each way of drawing a card has a public interface.

public bool drawCard( IntPtr hdc, int x, int y, int card, 
    int mode, long color )
public bool drawCardBack( IntPtr hdc, int x, int y, eBACK back )
public bool drawAnimatedBack( IntPtr hdc, int x, int y, 
    int card, int frame )
public bool drawInvertedCard( IntPtr hdc, int x, int y, int card )
public bool drawEmptyCard( IntPtr hdc, int x, int y, long color )
public bool drawExtrudedCard( IntPtr hdc, int x, int y, int dx, 
    int dy, int card, int mode, long color )

Using the code

As an example, in order to draw the bottom right corner card, the following call is made.

cardHandle.drawCardBack( hdc, 250, 310, eBACK.THE_O );

Where cardHandle is an instance of the cardsdll class.

Conclusion

I didn't realize how easy drawing a card could be till I looked at cards.dll. I hope people find this, my first article useful.

History

  • 15.09.03 - Initial Publication.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here