【问题标题】:Problems setting bitmaps to Static Control MFC将位图设置为静态控制 MFC 时出现问题
【发布时间】:2016-06-06 09:47:14
【问题描述】:

我目前正在使用基于对话框的 GUI,同时尝试对其进行自定义。我已经绘制了一些位图,我想在 dlg 中用作按钮和徽标。

我写了两个成员函数,一个用于将位图设置为CButtons,一个用于将位图设置为CStatics。实际上两者都在工作,当按下按钮时调用它们。但只有用于设置CButtons 的成员函数在对话框初始化期间正常工作。我的CStatics 不知何故被覆盖了。

这在OnInitDialog() 中调用时有效

void CMyDlg::setBitmapAsButton(std::string file, CButton &Button, int xPos, int yPos)
{
    CImage imgFromFile;
    CBitmap bitmap;
    std::wstring wfile(file.begin(), file.end());

    HRESULT ret = imgFromFile.Load(wfile.c_str());
    int width = imgFromFile.GetWidth();
    int height = imgFromFile.GetHeight();
    bitmap.Attach(imgFromFile.Detach());

    Button.Create(_T("My button"), WS_CHILD | WS_VISIBLE | BS_BITMAP,
    CRect(xPos, yPos, xPos + width, yPos + height), this, 1);
    Button.SetBitmap(bitmap);
}

这在OnInitDialog() 中调用时不起作用:

void CVisTrayDlg::setBitmapAsStatic(std::string file, CStatic &Static, int xPos, int yPos)
{
    CImage imgFromFile;
    CBitmap bitmap;
    std::wstring wfile(file.begin(), file.end());

    HRESULT ret = imgFromFile.Load(wfile.c_str());
    int width = imgFromFile.GetWidth();
    int height = imgFromFile.GetHeight();
    bitmap.Attach(imgFromFile.Detach());

    Static.Create(_T("My Static"), WS_CHILD | WS_VISIBLE | SS_BITMAP,
    CRect(xPos, yPos, xPos + width, yPos + height), this);
    Static.SetBitmap(bitmap);
}

CButton 和 CStatic 控件被声明为 CMyDlg 的成员。

关于如何避免这种行为的任何想法?或者有没有更好的方法在对话框中放置位图? (我尝试了CImage::BitBlt(),它也以某种方式被覆盖了。)

【问题讨论】:

  • 我相信CBitmap需要在函数返回后存在。尝试将其设为成员变量(或使用 static 关键字在此函数中快速获得结果)。
  • @Ajay 是的,你是对的。将 CBitmaps 声明为成员变量是可行的。奇怪...对于 CStatic 控件,它是必需的,但对于 CButton 控件则不是。非常感谢!
  • @Ajay 我修改了你建议的代码,女巫工作正常,但不知何故内存没有正确释放。也许你有一秒钟的时间来交叉检查它:stackoverflow.com/questions/38118923/…

标签: c++ visual-c++ bitmap mfc


【解决方案1】:

问题是所有权问题之一:在对SetBitmap 的调用返回后,单个位图实例有2 个所有者,CBitmap 对象以及CButton/CStatic1)支持>控制。当CBitmap 对象超出范围时,它会破坏单个实例,使控件具有指向无效位图的句柄。

标准解决方案是通过在将HBITMAP 传递给按钮或静态控件之前调用CImage::Detach() 来显式传递所有权:

void CVisTrayDlg::setBitmapAsStatic(std::string file, CStatic &Static, int xPos, int yPos)
{
    CImage bitmap;
    // Convert from ANSI codepage to UTF-16
    CStringW wfile(file.c_str());

    // Error handling needed. Use the SUCCEEDED() or FAILED() macros here:
    HRESULT ret = bitmap.Load(wfile);
    int width = bitmap.GetWidth();
    int height = bitmap.GetHeight();

    Static.Create(_T("My Static"), WS_CHILD | WS_VISIBLE | SS_BITMAP,
                  CRect(xPos, yPos, xPos + width, yPos + height), this);
    // Pass ownership from CImage to CStatic:
    HBITMAP hbmOld = Static.SetBitmap(bitmap.Detach());
    // SetBitmap() passes ownership of the previous image (if set).
    // We are responsible for cleanup:
    if (hbmOld != nullptr ) {
        ::DeleteObject(hbmOld);
    }
}

关于实现的几点说明:

  • 您从string 到wstring 的转换失败,除非文件 仅使用ASCII 字符。我更改了代码以处理 ANSI 代码页编码,但这可能不是源编码。您可能需要更改此设置。2)
  • 不需要从CImage 到CBitmap 的转换。 CImage 有一个 operator HBITMAP() 和一个 Detach() 成员,这就是我们真正需要的。
  • 对SetBitmap 的调用会返回先前关联位图的句柄。我们负责清理。


1)没有书面合同,按钮和静态控件的行为不同。如果它似乎适用于按钮,那么它只是巧合。这不是你应该依赖的任何东西。建议对按钮和静态控件使用相同的解决方案。

2)Windows 始终使用 UTF-16。通常最好也使用 UTF-16 作为应用程序的内部字符编码。推荐使用std::wstring。

【讨论】:

  • 我无语了,它就像魅力一样,你的解释很完美!现在一切对我来说都是有意义的!
  • 我正在使用您建议的方法,同时打开一个子对话框来加载多个图像。因为我意识到内存泄漏,所以我检查了 GDI 对象的数量,在打开子对话框时我可以看到加载的图像数量显着增加了对象,但关闭它后没有减少。我为此主题开了一个新帖子:stackoverflow.com/questions/38118923/…。知道缺少什么吗?
  • 更糟糕的是:devblogs.microsoft.com/oldnewthing/20140219-00/?p=1713。静态控件没有所有权,您还必须删除位图的卷影副本。
  • @gas:这真的很糟糕。我会看看我是否可以结合它来满足疯狂设计的要求。我需要一段时间才能完成这项工作。
【解决方案2】:

确保CBitmap 对象保留在内存中。因此,make 是你的类的成员变量。对话框类的析构函数最终会调用位图对象的析构函数,所以不用担心资源/内存泄漏。

对于按钮,它可能会在您移动/恢复窗口时显示其效果。我建议您将按钮的 CBitmap 对象也保留在内存中。

【讨论】:

  • 如果用户调用CWnd::Detach 并出于任何原因破坏HWND 之前的C++ 类,这将失败。你会找到正确的解决方案here,实现干净的所有权语义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多