【问题标题】:Win32: Bitmap Instantiation Breaking FunctionWin32:位图实例化中断功能
【发布时间】:2012-12-03 10:53:08
【问题描述】:

我的一个位图(它是一个精灵表)遇到了这个奇怪的问题。我的游戏运行完美,但如果我取消注释这行代码,初始化位图和精灵的 GameStart 函数会中断:

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

结果是 函数在遇到该行代码时退出,但必须调用 GamePaint(),因为位图被传送到屏幕上。我知道该功能没有正确执行,因为它们不是精灵 - 只是图像(并且音乐永远不会初始化)。精灵和音乐初始化在同一函数中的位图初始化之下。

更令人沮丧的是,游戏实际上工作......有时。然后我可以正常玩游戏并看到新的动画精灵。然而,在尝试调试该东西数十次之后,它根本无法正确构建。

我的尝试:

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

位图为 64x1536。那是问题吗?我有 8GB 的​​ RAM 和 GTX 570 Classified。这是整个函数:

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() 在这种情况下被调用:

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

如果有人能解释为什么该位图会杀死该功能,我将不胜感激!

编辑:Hans 直击问题 - 我勾选了“Win32 Exceptions”复选框,这行代码出现内存访问错误:

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

图片是否太大?我会继续调查,但欢迎提出任何建议。

【问题讨论】:

  • 不应该等到WM_CREATE之后再使用窗口吗?
  • 这个函数只是将资源加载到内存中。 GamePaint() 是将它们传送到窗口的方法,稍后在程序中调用。
  • 它以您的窗口作为参数调用GetDC。我想这需要一个完成的窗口。检查hDC 以确保它不为空。
  • 您仍然需要多少个客户端设备上下文?您反复调用 GetDC(),尽管它应该只执行一次,即使那样,您也永远不会 ReleaseDC() 任何东西。在确保您可以在 WM_CREATE 处理程序中执行任何操作后,请练习简单的口头禅:Release what you Get; Delete 你是什么Create。比如,如果你GetDC(),你需要ReleaseDC(),如果你CreateDC()(或CreateCompatibleDC()),你迟早需要DeleteDC()
  • 嘿,对于凌乱的hDC位感到抱歉。我正在使用 Michael Morrison 的 GameEngine 类之一,还有很多我仍然难以理解的 Windows API 代码。不过,我完全看到了 GetDC 问题,并修复了它。 DC 在另一个函数中的每个刻度结束时释放。

标签: c++ winapi bitmap


【解决方案1】:

不要使用 WM_CREATE 消息来初始化您的游戏。当您在 64 位操作系统上运行 32 位代码时,会出现一个非常具体的问题,当消息处理代码中出现 SEH 异常(如访问冲突)时,它们会被吞没。 WM_CREATE 是显示问题的消息之一。 this answer 中有很多关于此行为的详细信息,尽管它非常特定于 Winforms。然而,当您使用 C 或 C++ 编写代码时,也会出现完全相同的问题。

(通常)不需要使用 WM_CREATE 来初始化东西,你最好在 CreateWindowsEx() 调用之后移动它,因为你应该只需要窗口句柄。调试器现在将再次提供帮助,并显示代码崩溃的位置。

如果您确实在 WM_CREATE 中需要它,然后使用 Debug + Exceptions 对其进行调试,请勾选 Thrown 复选框以获取 Win32 异常。也是验证此答案是否准确的方法。

【讨论】:

    【解决方案2】:

    就像我现在一样,在调用 GetDC 之后,您必须调用 ReleaseDC。 但是您拨打了三个 GetDC 电话。 留下一个电话,存储 HDC,最后释放它。

    【讨论】:

    • +1 无论 OP 是否尝试在 WM_CREATE 处理程序中执行,此答案对于正确的 DC 管理至关重要。
    猜你喜欢
    • 2011-08-08
    • 2015-01-17
    • 1970-01-01
    • 2019-07-27
    • 1970-01-01
    • 2019-06-12
    • 1970-01-01
    • 2021-12-01
    • 2014-03-17
    相关资源
    最近更新 更多