3

我们看到一个间歇性问题,在 Windows XP 下使用位图作为背景的所有者绘制的按钮显示位图不正确。将显示包含多个按钮的窗口,这些按钮对用于按钮背景的位图图像使用相同的位图文件,并且大多数按钮将是正确的,尽管在某些情况下可能有一个或两个按钮显示位图背景减少到较小的尺寸。

如果您退出应用程序然后重新启动它,您可能会看到按钮上图标显示不正确的相同行为,但它可能与以前的按钮相同,也可能不同。这种在按钮上不正确显示图标的行为也并非总是可见的。有时它显示,有时它不显示。因为一旦我们为按钮加载了一个图标,我们就保留它,一旦按钮显示不正确,它总是会显示不正确。

使用调试器,我们终于发现似乎正在发生的事情是,当GetObject()调用函数时,返回的位图大小数据有时不正确。例如,在一种情况下,位图是 75x75 像素,而返回的大小GetObject()是 13x13。由于这个尺寸被用作位图绘制的一部分,所以显示的背景成为按钮窗口上的一个小装饰。

实际源区如下。

if (!hBitmapFocus) {
    CString iconPath;
    iconPath.Format(ICON_FILES_DIR_FORMAT, m_Icon);
    hBitmapFocus = (HBITMAP)LoadImage(NULL, iconPath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
}
if (hBitmapFocus) {
    BITMAP   bitmap;
    int iNoBytes = GetObject(hBitmapFocus, sizeof(BITMAP), &bitmap);
    if (iNoBytes < 1) {
        char xBuff[128];
        sprintf (xBuff, "GetObject() failed. GetLastError = %d", GetLastError ());
        NHPOS_ASSERT_TEXT((iNoBytes > 0), xBuff);
    }
    cxSource = bitmap.bmWidth;
    cySource = bitmap.bmHeight;
    //Bitmaps cannot be drawn directly to the screen so a 
    //compatible memory DC is created to draw to, then the image is 
    //transfered to the screen
    CDC hdcMem;
    hdcMem.CreateCompatibleDC(pDC);

    HGDIOBJ  hpOldObject = hdcMem.SelectObject(hBitmapFocus);

    int xPos;
    int yPos;

    //The Horizontal and Vertical Alignment
    //For Images
    //Are set in the Layout Manager
    //the proper attribute will have to be checked against
    //for now the Image is centered on the button

    //Horizontal Alignment
    if(btnAttributes.horIconAlignment == IconAlignmentHLeft){//Image to left
        xPos = 2;
    }else if(btnAttributes.horIconAlignment == IconAlignmentHRight){//Image to right
       xPos = myRect.right - cxSource - 5;
    }else {//Horizontal center
       xPos = ((myRect.right - cxSource) / 2) - 1;
    }

    //Vertical Alignment
    if(btnAttributes.vertIconAlignment == IconAlignmentVTop){//Image to top
        yPos = 2;
    }else if(btnAttributes.vertIconAlignment == IconAlignmentVBottom){//Image to bottom
        yPos = myRect.bottom - cySource - 5;
    }else{//Vertical Center
        yPos = ((myRect.bottom - cySource) / 2) - 1;
    }

    pDC->BitBlt(xPos, yPos, cxSource, cySource, &hdcMem, 0, 0, SRCCOPY);

    hdcMem.SelectObject(hpOldObject);
}

使用调试器,我们可以看到iconPath字符串是正确的,并且加载的位图hBitmapFocus不是 NULL。接下来我们可以看到调用GetObject()了 并且返回的值iNoBytes等于 24。对于那些正确显示值bitmap.bmWidth并且正确的按钮,bitmap.bmHeight但是对于那些不正确的按钮,值太小导致绘制时的大小不正确位图。

该变量在类头中定义为

HBITMAP hBitmapFocus;

作为对此进行研究的一部分,我发现了这个堆栈溢出问题,GetObject 返回奇怪的大小,我想知道这里是否存在某种对齐问题。

bitmap调用中使用的变量是否GetObject()需要位于某种对齐边界上?当我们对一些数据使用打包时,我们使用pragma指令仅指定包含文件中需要打包在一个字节边界上的特定结构的代码的特定部分。

4

1 回答 1

0

请阅读此 Microsoft KB如何加载带有调色板信息的位图。它也有一个很好的例子。

附注:我在您的代码中没有看到您调用 ::DeleteObject(hBitmapFocus) 的任何地方。调用它非常重要,因为您可以很快用完 GDI 对象。

使用 Windows 任务管理器查看您的程序不会耗尽 GDI 资源总是一个好主意。只需在任务管理器中添加“GDI 对象”列,您的应用程序中的对象数量并没有不断增加,而是保持在预期范围内,类似于其他程序

于 2012-12-17T22:16:46.640 回答