1

I'm having this strange issue with one of my bitmaps (it's a spritesheet). My game runs perfectly, but the GameStart function, which initializes bitmaps & sprites, breaks if I uncomment this line of code:

g_pPowerup100Bitmap = new Bitmap(hDC, IDB_POWERUP_100, g_hInstance);

The result is that the function just quits when it hits that line of code, but GamePaint() must be getting called because the bitmaps are blitted to the screen. I know the function isn't executing correctly because they're not sprites - just images (and the music never initializes). The sprite & music initialization are below the bitmap initialization in the same function.

What's even more frustrating is that the game actually works...sometimes. I can then play the game properly & see the new animated sprite. After attempting to debug the thing dozens of times, however, it simply doesn't build properly anymore.

What I've tried:

1) Try/catch of entire GameStart() contents. Exception is not thrown
2) Extensive breakpoint checking - it's definitely this line
3) Spell-checking variable name - exact same as in header file
4) Re-saving bitmap as 24-bit RGB instead of 32-bit
5) Cleaning & rebuilding both the solution & project file
6) Restarting Visual C++ 2008

The bitmap is 64x1536. Is that a problem? I have 8GB of RAM and a GTX 570 Classified. Here is the entire function:

void GameStart(HWND hWindow)
{
    try
  {
    // Initialize global variables
    g_iInputDelay = 0;
    g_iNumLives = 3;
    g_iScore = 0;
    g_iGameState = 1;
    g_iDifficulty = 1;

    // Seed the random number generator
    srand(GetTickCount());

    // Create the offscreen device context and bitmap
    g_hOffscreenDC = CreateCompatibleDC(GetDC(hWindow));
    g_hOffscreenBitmap = CreateCompatibleBitmap(GetDC(hWindow),
    g_pGame->GetWidth(), g_pGame->GetHeight());
    SelectObject(g_hOffscreenDC, g_hOffscreenBitmap);

    // Create and load the bitmaps
    HDC hDC = GetDC(hWindow);
    g_pHighwayBitmap = new Bitmap(hDC, IDB_HIGHWAY, g_hInstance);
    g_pChickenBitmap = new Bitmap(hDC, IDB_CHICKEN, g_hInstance);
    g_pCarBitmaps[0] = new Bitmap(hDC, IDB_CAR1, g_hInstance);
    g_pCarBitmaps[1] = new Bitmap(hDC, IDB_CAR2, g_hInstance);
    g_pCarBitmaps[2] = new Bitmap(hDC, IDB_CAR3, g_hInstance);
    g_pCarBitmaps[3] = new Bitmap(hDC, IDB_CAR4, g_hInstance);
    g_pChickenHeadBitmap = new Bitmap(hDC, IDB_CHICKENHEAD, g_hInstance);
    g_pMainMenuBitmap = new Bitmap(hDC, IDB_MAIN_MENU, g_hInstance);
    g_pHighScoresMenuBitmap = new Bitmap(hDC, IDB_HIGH_SCORES_MENU, g_hInstance);
    g_pGameOverMenuBitmap =  new Bitmap(hDC, IDB_GAME_OVER_MENU, g_hInstance);
    g_pNormalModeBtnBitmap = new Bitmap(hDC, IDB_NORMAL_MODE_BTN, g_hInstance);
    g_pHardModeBtnBitmap = new Bitmap(hDC, IDB_HARD_MODE_BTN, g_hInstance);
    g_pHighScoresBtnBitmap = new Bitmap(hDC, IDB_HIGH_SCORES_BTN, g_hInstance);
    g_pReplayBtnBitmap = new Bitmap(hDC, IDB_REPLAY_BTN, g_hInstance);
    g_pMainMenuBtnBitmap = new Bitmap(hDC, IDB_MAIN_MENU_BTN, g_hInstance);
    g_pPowerup100Bitmap = new Bitmap(hDC, IDB_POWERUP_100, g_hInstance);

    RECT    rcBounds = { 0, 0, 465, 400 };

    //Button Sprites - Main Menu
    Sprite* pBtnSprite = new Sprite(g_pNormalModeBtnBitmap, 77,228, 0,0, 1, rcBounds, BA_STOP);
    pBtnSprite->SetStateChange(0);
    g_pGame->AddSprite(pBtnSprite, 1);

    pBtnSprite = new Sprite(g_pHardModeBtnBitmap, 254,228, 0,0, 1, rcBounds, BA_STOP);
    pBtnSprite->SetStateChange(0);
    g_pGame->AddSprite(pBtnSprite, 1);

    pBtnSprite = new Sprite(g_pHighScoresBtnBitmap, 166,310, 0,0, 1, rcBounds, BA_STOP);
    pBtnSprite->SetStateChange(3);
    g_pGame->AddSprite(pBtnSprite, 1);


    //Button Sprites - Game Over Menu
    pBtnSprite = new Sprite(g_pReplayBtnBitmap, 167,249, 0,0, 1, rcBounds, BA_STOP);
    pBtnSprite->SetStateChange(0);
    g_pGame->AddSprite(pBtnSprite, 2);

    pBtnSprite = new Sprite(g_pMainMenuBtnBitmap, 82,332, 0,0, 1, rcBounds, BA_STOP);
    pBtnSprite->SetStateChange(1);
    g_pGame->AddSprite(pBtnSprite, 2);

    pBtnSprite = new Sprite(g_pHighScoresBtnBitmap, 252,332, 0,0, 1, rcBounds, BA_STOP);
    pBtnSprite->SetStateChange(3);
    g_pGame->AddSprite(pBtnSprite, 2);

    //Button Sprites - High Scores Menu
    pBtnSprite = new Sprite(g_pMainMenuBtnBitmap, 166,332, 0,0, 1, rcBounds, BA_STOP);
    pBtnSprite->SetStateChange(1);
    g_pGame->AddSprite(pBtnSprite, 3);

        // Create the chicken and car sprites
    g_pChickenSprite = new Sprite(g_pChickenBitmap, rcBounds, BA_STOP);
    g_pChickenSprite->SetPosition(4, 175);
    g_pChickenSprite->SetVelocity(0, 0);
    g_pChickenSprite->SetZOrder(1);
    g_pChickenSprite->SetNumFrames(2);
    g_pChickenSprite->SetAsInputControlled(); //stops auto-frame update
    //DEBUGGING ONLY!!!!!
    g_pChickenSprite->SetID(1);
    g_pGame->AddSprite(g_pChickenSprite, 0);

    Sprite* pSprite = new Sprite(g_pCarBitmaps[0], rcBounds, BA_WRAP);
    pSprite->SetPosition(70, 0);
    pSprite->SetVelocity(0, 6);
    pSprite->SetZOrder(2);
    g_pGame->AddSprite(pSprite, 0);
    pSprite = new Sprite(g_pCarBitmaps[1], rcBounds, BA_WRAP);
    pSprite->SetPosition(160, 0);
    pSprite->SetVelocity(0, 2);
    pSprite->SetZOrder(2);
    g_pGame->AddSprite(pSprite, 0);
    pSprite = new Sprite(g_pCarBitmaps[2], rcBounds, BA_WRAP);
    pSprite->SetPosition(239, 400);
    pSprite->SetVelocity(0, -4);
    pSprite->SetZOrder(2);
    g_pGame->AddSprite(pSprite, 0);
    pSprite = new Sprite(g_pCarBitmaps[3], rcBounds, BA_WRAP);
    pSprite->SetPosition(329, 400);
    pSprite->SetVelocity(0, -9);
    pSprite->SetZOrder(2);
    g_pGame->AddSprite(pSprite, 0);

    // Load the background music
    g_pGame->PlayMIDISong(TEXT("Music.mid"));

    getHighScores(scoreData, g_scoreTop);

    scoreData.close();

  }
  catch (int e)
  {
    cout << "An exception occurred. Exception Nr. " << e << endl;
    system("PAUSE");
  }
}

GameStart() is called in this context:

switch (msg)
  {
    case WM_CREATE:
      // Set the game window and start the game
      SetWindow(hWindow);
      GameStart(hWindow);
      return 0;

If anyone could shed some light on why that bitmap is killing the function, I would be very grateful!

EDIT: Hans hit the problem right on the head - I ticked that "Win32 Exceptions" checkbox & the memory access error appeared for this line of code:

    CopyMemory(pBitmapBits, pTempBits, pBitmapInfo->bmiHeader.biSizeImage);

Is the image too large? I'll keep investigating, but any suggestions are welcome.

KongMD
  • 171
  • 1
  • 4
  • 14
  • 1
    Shouldn't you wait until after `WM_CREATE` to use the window? – chris Dec 15 '12 at 19:51
  • This function just loads the resources into memory. GamePaint() is the one that blits them to the window, and is called later in the program. – KongMD Dec 15 '12 at 19:57
  • It calls `GetDC` with your window as an argument. I would imagine that requires a finished window. Check `hDC` to make sure it's not null. – chris Dec 15 '12 at 20:04
  • How many client device contexts do you need anyway? You're repeatedly calling GetDC(), though it should only be done once, and even then, you never ReleaseDC() anything. After ensuring you can do any of this in a WM_CREATE handler, please practice the simple mantra: `Release` what you `Get`; `Delete` what you `Create`. As in, if you `GetDC()` you need to `ReleaseDC()`, and if you `CreateDC()` (or `CreateCompatibleDC()`), you sooner-or-later need to `DeleteDC()`. – WhozCraig Dec 15 '12 at 20:31
  • Heh, sorry about the messy hDC bit. I'm using one of Michael Morrison's GameEngine classes and a lot of this Windows API code I'm still struggling to understand. I totally see the GetDC problem, though, and fixed it. The DC gets released at the end of each tick in another function. – KongMD Dec 15 '12 at 20:48

2 Answers2

3

Don't use the WM_CREATE message to initialize your game. There is a very specific problem when you run 32-bit code on a 64-bit operating system that can cause SEH exceptions (like an access violation) to get swallowed when they are raised in the message handling code. WM_CREATE is one of the messages that exhibits the problem. Lots of details about this behavior in this answer, albeit that it is very specific to Winforms. The exact same problem however exists when you write code in C or C++.

There isn't (usually) any need to use WM_CREATE to initialize stuff, you might as well move it after the CreateWindowsEx() call since you should only need the window handle. The debugger is now going to be helpful again and show you where your code crashes.

If you really do need it in WM_CREATE then debug it by using Debug + Exceptions, tick the Thrown checkbox for Win32 exceptions. Also the way to verify that this answer is accurate.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
1

As I now, after calling GetDC you must call ReleaseDC. But you make three GetDC calls. Leave one call, store HDC and at the end release it.

Serhiy
  • 1,332
  • 1
  • 16
  • 24
  • +1 regardless of whether what the OP is attempting to do is even allowed in a WM_CREATE handler, this answer is critical to proper DC management. – WhozCraig Dec 15 '12 at 20:34