January 27, 2021
Hot Topics:

Debugging Hosted Assemblies

  • By Paul Kimmel
  • Send Email »
  • More Articles »


We are well into winter in the Midwest and Northeast and that's got me thinking about sunshine and warmth. Sunshine and warmth makes me think of Las Vegas. I love Las Vegas and to play Black Jack. Generally my gambling is modest and I am a less than average player but committed to getting better. As an entertainment expense if I win $500 then I can go to a show, fly an Extra 300L at the Aerobatic Experience in Boulder City, have a great meal, visit the spa and get a massage, or go to the Richard Petty Experience on the casino's dime. So, winning a few hundred bucks means extra fun.

My other motivation for writing this particular article is that I really liked Chris Sells' Wahoo! Game on www.sellsbrothers.com/. Wahoo! is basically Tetris. You can download and play this Windows game from the Internet, and Chris uses it to talk about code access security. I liked the idea and BlackJack, so I thought I'd borrow the sentiment and create a Windows BlackJack game and use it to demonstrate how to debug a hosted assembly.

A hosted assembly is a non-executable assembly that runs in another process. COM+ server applications run in dllhost.exe. ASP.NET assemblies run in the aspnet_wp.exe process, and any assembly can be loaded and tested by NUnit test assemblies. For our purposes I elected to use a NUnit assembly and NUnit to demonstrate debugging a hosted process. The process we will be debugging is the library for my blackjack game. (As soon as I am happy with the results I will post the game, which you can download for free with source, from http://www.softconcepts.com/BlackJack.)

It is important to note that attaching to a host process and debugging your .NET assemblies is the same regardless of the host. All you need to know is the underlying name of the executable host process. (Examples are: COM+ is dllhost.exe, ASP.NET is aspnet_wp.exe, and NUnit is nunit-gui.exe.)

Building the BlackJack Game

The construction of the BlackJack took several hours to assemble. I understand the game well enough to identify some basic classes without a lot of analysis or fancy modeling; in short, I hacked the game together and refactored until I was reasonably happy with the result (see figure 1).

Click here for larger image

Figure 1: With a little practice my game might improve.

If you are familiar with the card games in general and Blackjack in particular then you won't be surprised by some of the classes implemented to support the game. Some of the classes we will need to test include BlackJack, Cards, Dealer, DealerHand, Deck, Decks, Hand, Hints, Player, PlayerCollection, PlayerHand, PlayerHandCollection, Shuffler, and Suits. (The source listing is too big to provide here, but it will be available online at http://www.softconcepts.com/BlackJack shortly.)

To permit the game to be played as a console game, Windows game, perhaps a CE game, and eventually a Web game, I implemented most of the classes in a separate class library. It is this class library (as well as the clients, but not for our purposes) that need to be tested in a host and that we will use to demonstrate host debugging in Visual Studio .NET.

Whereas some people like to build software starting with the presentation layer and finishing with the object layer, I generally build software from the object layer to presentation layer. I start with some core classes at the lowest level of complexity and then layer complexity, testing each layer as complexity is added. To this end it seemed natural to start with a card class, specific classes of cards—for example, in BlackJack Ace can have the value of 1 or 11—so it seemed suitable to subclass Card to define an Ace class as well as a class for each face value. Very quickly there were 15 classes to test—Card, Ace through King, and Deck. Because these are intrinsic classes it is useful to ensure these classes function correctly before layering complexity. Listing 1 shows supporting enumerations, and Listing 2 contains the core Card base class and the Ace class.

Listing 1: Enumerations for describing playing cards.

Imports SystemPublic Enum Face    One    Two    Three    Four    Five    Six    Seven    Eight    Nine    Ten    Jack    Queen    KingEnd EnumPublic Enum Suit  Diamond  Club  Heart  SpadeEnd Enum

Listing 2: The Card base class and an Ace subclass.

Imports SystemImports System.DrawingPublic MustInherit Class Card  Private FCardFace As Face  Private FHighFaceValue As Integer  Private FLowFaceValue As Integer  Private FCardSuit As Suit#Region "External methods and related fields"  Private width As Integer = 0  Private height As Integer = 0  Declare Function cdtInit Lib "cards.dll" (ByRef width As Integer, _    ByRef height As Integer) As Boolean  Declare Function cdtDrawExt Lib "cards.dll" (ByVal hdc As IntPtr, _    ByVal x As Integer, ByVal y As Integer, ByVal dx As Integer, _    ByVal dy As Integer, ByVal card As Integer, _    ByVal suit As Integer, ByVal color As Long) As Boolean  Declare Sub cdtTerm Lib "cards.dll" ()#End Region  Public Sub New(ByVal lowValue As Integer, ByVal highValue As Integer, _    ByVal cardSuit As Suit, ByVal cardFace As face)    cdtInit(width, height)    FHighFaceValue = highValue    FLowFaceValue = lowValue    FCardSuit = cardSuit    FCardFace = cardFace  End Sub  Public Function GetLowFacevalue() As Integer    Return FLowFaceValue  End Function  Public Function GetHighFaceValue() As Integer    Return FHighFaceValue  End Function  Public Function GetFaceValue() As Integer    Return FLowFaceValue  End Function  Public Property CardSuit() As Suit  Get    Return FCardSuit  End Get  Set(ByVal Value As Suit)    FCardSuit = Value  End Set  End Property  ' TODO: Convert various paint styles to interface  Public Sub PaintTextFace()    Console.WriteLine(GetCardValue())  End Sub  Public Sub PaintGraphicFace(ByVal g As Graphics, ByVal x As Integer, _    ByVal y As Integer, ByVal dx As Integer, ByVal dy As Integer)    Dim hdc As IntPtr = g.GetHdc()    Try      Dim Card As Integer = CType(Me.FCardFace, Integer)      cdtDrawExt(hdc, x, y, dx, dy, Card, 0, 0)    Finally      ' If Intellisense doesn't show this method unhine advanced       ' members in Tools|Options      g.ReleaseHdc(hdc)    End Try  End Sub  Public Sub PaintGraphicBack(ByVal g As Graphics, ByVal x As Integer, _    ByVal y As Integer, ByVal dx As Integer, ByVal dy As Integer)    Dim hdc As IntPtr = g.GetHdc()    Try      ' TODO: Make card style (hardcoded 61) a configurable property      cdtDrawExt(hdc, x, y, dx, dy, 61, 0, 0)    Finally      g.ReleaseHdc(hdc)    End Try  End Sub  Protected Overridable Function GetTextValue() As String    Return GetLowFacevalue().ToString()  End Function  Protected Function GetTextSuit() As String    Return FCardSuit.ToString().Chars(0).ToString  End Function  Public Overridable Function GetCardValue() As String    Return String.Format("{0}{1}", GetTextValue(), GetTextSuit())  End FunctionEnd ClassPublic Class Ace  Inherits Card  Public Sub New(ByVal cardSuit As Suit)    MyBase.New(1, 11, cardSuit, Face.One)  End Sub  Protected Overrides Function GetTextValue() As String    Return "A"  End Function End Class

Click here for larger image

Figure 2: Show advanced methods that are concealed in Intellisense by default; an example of an advanced method is the Graphics.ReleaseHdc method.

The enumerations Face and Suit use strongly typed enumerations to describe the value and suit of a card. The Card class stores properties like the face and suit values as well as the underlying value of the card. In addition, I have declared some API methods from cards.dll, which already contain capabilities for drawing graphic playing cards. (Cards.dll ships with Windows and supports games like Solitaire (sol.exe), which ships with Windows.)

Some changes I'd like to see before this code goes live are to permit the dynamic configuration of the back of the playing card—it is hard coded to 61, a value described in the documentation for cards.dll available with a Google search—and convert the Paint methods into overloaded methods or specific implementations of a graphic and text card interface.

Generally, when I get write as much code as shown in figures 1 and 2 I begin testing.

Page 1 of 2

This article was originally published on January 15, 2004

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date