各位达人请入 使用GDI+绘图引起的内存未释放

cyfage 2012-08-01 08:52:21
有一个使用GDI+绘制双缓冲绘图引起的内存使用后一直不能释放的问题,导致程序占用内存持续增加,在下左查右查,百思不得其解,特求救各位达人:

这是一个动态资源链接库中的某个对话框,由于设计需要,在启动时我开启了计时器,每半秒刷新一次:

void HorizontalDlg::OnTimer(UINT nIDEvent)
{
switch (nIDEvent)
{
case 1201:
{
Invalidate(FALSE);
}
break;
}
}


然后我并没有重载OnEraseBkGnd()函数,而是在OnPaint()中写了如下双缓冲代码:

using namespace Gdiplus;
CPaintDC DC(this);

RECT Res;
GetClientRect(&Res);
int Width = Res.right - Res.left;
int Height = Res.bottom - Res.top;

CDC *pDC = GetDC();
//定义一个内存设备描述表对象(即后备缓冲区)
CDC MemDC;
//定义一个位图对象
CBitmap MemBitmap;
//建立与屏幕设备描述表(前端缓冲区)兼容的内存设备描述表句柄(后备缓冲区)
MemDC.CreateCompatibleDC(NULL);
//这时还不能绘图,因为没有位图的设备描述表是不能绘图的
//下面建立一个与屏幕设备描述表(或者内存设备描述表)兼容的位图
MemBitmap.CreateCompatibleBitmap(pDC, Width, Height);
//将位图选入到内存设备描述表
//只有选入了位图的设备描述表才有地方绘图,画到指定的位图上
CBitmap *pOldBit = MemDC.SelectObject(&MemBitmap);

/************************************************************/
Graphics graphics(MemDC.m_hDC);

SolidBrush BackroundBrush(Color(stc_SetInfo.Transparent, 255, 255, 255));
graphics.FillRectangle(&BackroundBrush, 0, 0, Width, Height);
/************************************************************/

//将后备缓冲区中的图形拷贝到前端缓冲区
pDC->BitBlt(0, 0, Width, Height, &MemDC, 0, 0, SRCCOPY);

DWORD dwExStyle = GetWindowLong(m_hWnd, GWL_EXSTYLE);
if((dwExStyle &0x80000) != 0x80000)
{
SetWindowLong(m_hWnd, GWL_EXSTYLE, dwExStyle^0x80000);
}
//不能使用客户区坐标
this->GetWindowRect(&Res);

POINT ptWinPos = {Res.left, Res.top};
SIZE sizeWindow = {Res.right - Res.left, Res.bottom - Res.top};
POINT ptSrc={0, 0};
BLENDFUNCTION Blend = {0, 0, 255, 1};
UpdateLayeredWindow(pDC, &ptWinPos, &sizeWindow, &MemDC, &ptSrc, 0, &Blend, 2);
//绘图完成后的清理
if (TRUE != MemBitmap.DeleteObject())
{
Log("DeleteObject error.");
}

if (TRUE != MemDC.DeleteDC())
{
Log("DeleteDC error.");
}

if (0 != ReleaseDC(pDC))
{
Log("ReleaseDC error.");
}


只做了一件很简单的事,把整个背景刷成白色,然后每半秒刷新一次,结果在程序启动后就在管理器中监视到内存不断被占用,每次增加4K……显然应该是典型的某处资源未被释放。但是我查了不少资料,貌似都对我的问题没有帮助。

我为了测试对比,在另外一个简单的对话框上也同样使用Invalidate(FALSE)半秒刷新一次,没有使用双缓冲,也没有绘图,结果完全没有内存问题,那问题显然就在这个绘制的代码上面了……可我怎么看代码,每次使用的句柄或对象都已经在用完后释放了呀,实在不理解……
...全文
585 28 打赏 收藏 转发到动态 举报
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
chiweiyong 2014-11-19
  • 打赏
  • 举报
回复
同样的问题,不知道帖主后来解决没?有什么方法吗?
cyfage 2012-08-06
  • 打赏
  • 举报
回复
看起来实在解决不了了……
先结贴吧,无论如何,多谢楼上几位热心指点的。
QQ515311445 2012-08-04
  • 打赏
  • 举报
回复
//绘图完成后的清理
MemDC.SelectObject(pOldBit);
DeleteObject(MemBitmap);
MemDC.DeleteDC();
ReleaseDC(pDC);

楼主水平实在有点差,以上才是正确的写法
cyfage 2012-08-04
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 的回复:]
C/C++ code
//绘图完成后的清理
MemDC.SelectObject(pOldBit);
DeleteObject(MemBitmap);
MemDC.DeleteDC();
ReleaseDC(pDC);

楼主水平实在有点差,以上才是正确的写法
[/Quote]

多谢指点
不过似乎还是没有效果,这样写了之后还是一样不断的吃内存。
HuWenjin 2012-08-03
  • 打赏
  • 举报
回复
继楼上的

1.用clear函数试试,清屏成指定的色
2.stc_SetInfo.Transparent 这个值改成不同的值试试,我原来用那个透明窗口时碰到过这个值的问题,具体的记不清了。

cyfage 2012-08-02
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 的回复:]
HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, len);
BYTE *pmem = (BYTE*)GlobalLock(m_hMem);
memcpy(pmem, lpRsrc, len);
IStream *pstm;

CreateStreamOnHGlobal(m_hMem, FALSE, &pstm);
*pImg = Gdiplu……
[/Quote]

谢谢,不过好像也不是啊,这个函数还根本就没调用过呢。
ArcRain 2012-08-02
  • 打赏
  • 举报
回复
HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, len);
BYTE *pmem = (BYTE*)GlobalLock(m_hMem);
memcpy(pmem, lpRsrc, len);
IStream *pstm;

CreateStreamOnHGlobal(m_hMem, FALSE, &pstm);
*pImg = Gdiplus::Bitmap::FromStream(pstm);

GlobalUnlock(m_hMem);
pstm->Release();
FreeResource(lpRsrc);

没有GlobalFree?
cyfage 2012-08-02
  • 打赏
  • 举报
回复
……上面贴错了,贴成了顶层窗口的代码了,这里才是底层绘图窗口的代码:

头文件

#pragma once
#include "afxwin.h"
#include "HorizontalCtrl.h"

class HorizontalDlg : public CDialog
{
public:

HorizontalDlg(CWnd* pParent = NULL);
virtual ~HorizontalDlg();

enum {IDD = IDD_HORIZONTAL_DLG};

virtual BOOL DestroyWindow();

virtual int OpenWindow();

virtual int CloseWindow();

protected:

CButton Btn_OK;
CButton Btn_Cancel;

HorizontalCtrl *m_Ctrl;

char m_Log[1024];

virtual BOOL OnInitDialog();

virtual int LoadPNG(HINSTANCE hInstance, UINT nID, LPCTSTR sTR, Gdiplus::Bitmap **pImg);

virtual void DoDataExchange(CDataExchange* pDX);

afx_msg LRESULT OnMessage(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnNcHitTest(CPoint point);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnMove(int x, int y);
afx_msg void OnPaint();
afx_msg void OnBnClickedOk();
afx_msg void OnBnClickedCancel();

DECLARE_DYNAMIC(HorizontalDlg)
DECLARE_MESSAGE_MAP()
};


CPP文件

#pragma once
#include "stdafx.h"
#include "ShowCard.h"
#include "HorizontalDlg.h"

IMPLEMENT_DYNAMIC(HorizontalDlg, CDialog)

HorizontalDlg::HorizontalDlg(CWnd* pParent /*=NULL*/)
: CDialog(HorizontalDlg::IDD, pParent)
{
m_Ctrl = NULL;
}

HorizontalDlg::~HorizontalDlg()
{

}

BOOL HorizontalDlg::DestroyWindow()
{
if (NULL != m_Ctrl)
{
m_Ctrl->DestroyWindow();
delete m_Ctrl;
m_Ctrl = NULL;
}

return CDialog::DestroyWindow();
}

int HorizontalDlg::OpenWindow()
{
this->ShowWindow(SW_SHOW);
m_Ctrl->ShowWindow(SW_SHOW);
return 1;
}

int HorizontalDlg::CloseWindow()
{
this->ShowWindow(SW_HIDE);
m_Ctrl->ShowWindow(SW_HIDE);
return 1;
}

BEGIN_MESSAGE_MAP(HorizontalDlg, CDialog)
ON_MESSAGE(10086, OnMessage)
ON_WM_NCHITTEST()
ON_WM_TIMER()
ON_WM_MOVE()
ON_WM_PAINT()
ON_BN_CLICKED(IDOK, &HorizontalDlg::OnBnClickedOk)
ON_BN_CLICKED(IDCANCEL, &HorizontalDlg::OnBnClickedCancel)
END_MESSAGE_MAP()

BOOL HorizontalDlg::OnInitDialog()
{
CDialog::OnInitDialog();

Btn_OK.ShowWindow(SW_HIDE);
Btn_Cancel.ShowWindow(SW_HIDE);

m_Ctrl = new HorizontalCtrl;
m_Ctrl->Create(IDD_HORIZONTAL_CTRL, this);

HINSTANCE hInstance = AfxGetResourceHandle();
/*LoadPNG(hInstance, IDR_PNG_BACKROUND, _T("png"), &m_ImgBackround);*/

stc_HorizontalHandle = this->m_hWnd;
//结合当前屏幕分辨率,乘以存储的百分比,获得原本保存的坐标
this->MoveWindow(stc_SetInfo.Horizontal.x * GetSystemMetrics(SM_CXSCREEN), stc_SetInfo.Horizontal.y * GetSystemMetrics(SM_CYSCREEN), (float)HOR_DEFWIDTH * (float)stc_SetInfo.Coefficient, (float)HOR_DEFHEIGHT * (float)stc_SetInfo.Coefficient);
if (0 == stc_SetInfo.Using)
{
this->CenterWindow();
}

SetTimer(1201, 5 * 100, 0);
return TRUE;
}

int HorizontalDlg::LoadPNG(HINSTANCE hInstance, UINT nID, LPCTSTR sTR, Gdiplus::Bitmap **pImg)
{
HRSRC hRsrc = ::FindResource(hInstance, MAKEINTRESOURCE(nID), sTR);
if (!hRsrc)
{
return -1;
}

DWORD len = SizeofResource(hInstance, hRsrc);
BYTE *lpRsrc = (BYTE*)LoadResource(hInstance, hRsrc);
if (!lpRsrc)
{
return -1;
}

HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, len);
BYTE *pmem = (BYTE*)GlobalLock(m_hMem);
memcpy(pmem, lpRsrc, len);
IStream *pstm;

CreateStreamOnHGlobal(m_hMem, FALSE, &pstm);
*pImg = Gdiplus::Bitmap::FromStream(pstm);

GlobalUnlock(m_hMem);
pstm->Release();
FreeResource(lpRsrc);
return 1;
}

void HorizontalDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDOK, Btn_OK);
DDX_Control(pDX, IDCANCEL, Btn_Cancel);
}

LRESULT HorizontalDlg::OnMessage(WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
case 9778:
{
switch (lParam)
{
case 0:
{
OnBnClickedCancel();
}
break;
case 1:
{
CloseWindow();
}
break;
case 2:
{
OpenWindow();
}
}
}
break;
}

return 1;
}

LRESULT HorizontalDlg::OnNcHitTest(CPoint point)
{
LRESULT nHitTest = CDialog::OnNcHitTest(point);
return (nHitTest == HTCLIENT) ? HTCAPTION : nHitTest;
}

void HorizontalDlg::OnMove(int x, int y)
{
if (NULL != m_Ctrl)
{
//用户有可能在使用后调整分辨率,导致显示框飞到屏幕外面,因此使用坐标和当前屏幕分辨率像素的百分比
//进行存储,这样换算虽然麻烦点,但可以保证不管怎么调整分辨率都保持原样
CRect Ret;
GetWindowRect(&Ret);
m_Ctrl->MoveWindow(&Ret);

stc_SetInfo.Horizontal.x = (float)Ret.left / (float)GetSystemMetrics(SM_CXSCREEN);
stc_SetInfo.Horizontal.y = (float)Ret.top / (float)GetSystemMetrics(SM_CYSCREEN);

::PostMessage(stc_RouteHandle, 10086, 9900, 1);
if (1 == stc_SetInfo.Model)
{
::PostMessage(stc_SetHandle, 10086, 9900, 1);
}
}
}

void HorizontalDlg::OnTimer(UINT nIDEvent)
{
switch (nIDEvent)
{
case 1201:
{
Invalidate(FALSE);
}
break;
}
}

void HorizontalDlg::OnPaint()
{
using namespace Gdiplus;
CPaintDC DC(this);

RECT Res;
GetClientRect(&Res);
int Width = Res.right - Res.left;
int Height = Res.bottom - Res.top;

CDC *pDC = GetDC();
//定义一个内存设备描述表对象(即后备缓冲区)
CDC MemDC;
//定义一个位图对象
CBitmap MemBitmap;
//建立与屏幕设备描述表(前端缓冲区)兼容的内存设备描述表句柄(后备缓冲区)
MemDC.CreateCompatibleDC(NULL);
//这时还不能绘图,因为没有位图的设备描述表是不能绘图的
//下面建立一个与屏幕设备描述表(或者内存设备描述表)兼容的位图
MemBitmap.CreateCompatibleBitmap(pDC, Width, Height);
//将位图选入到内存设备描述表
//只有选入了位图的设备描述表才有地方绘图,画到指定的位图上
CBitmap *pOldBit = MemDC.SelectObject(&MemBitmap);

/************************************************************/
Graphics graphics(MemDC.m_hDC);

SolidBrush BackroundBrush(Color(stc_SetInfo.Transparent, 150, 100, 100));
graphics.FillRectangle(&BackroundBrush, 0, 0, Width, Height);
/************************************************************/

//将后备缓冲区中的图形拷贝到前端缓冲区
pDC->BitBlt(0, 0, Width, Height, &MemDC, 0, 0, SRCCOPY);

DWORD dwExStyle = GetWindowLong(m_hWnd, GWL_EXSTYLE);
if((dwExStyle &0x80000) != 0x80000)
{
SetWindowLong(m_hWnd, GWL_EXSTYLE, dwExStyle^0x80000);
}
//不能使用客户区坐标
this->GetWindowRect(&Res);

POINT ptWinPos = {Res.left, Res.top};
SIZE sizeWindow = {Res.right - Res.left, Res.bottom - Res.top};
POINT ptSrc={0, 0};
BLENDFUNCTION Blend = {0, 0, 255, 1};
UpdateLayeredWindow(pDC, &ptWinPos, &sizeWindow, &MemDC, &ptSrc, 0, &Blend, 2);
//绘图完成后的清理
MemBitmap.DeleteObject();
MemDC.DeleteDC();
ReleaseDC(pDC);
}

void HorizontalDlg::OnBnClickedOk()
{

}

void HorizontalDlg::OnBnClickedCancel()
{

}
cyfage 2012-08-02
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 的回复:]
代码是不是没上全啊。
感觉没什么问题呀。
每次4KB啊?BoundsChecker查查看?
[/Quote]

实际上这是一个双窗体中的底层,底层做绘制图片之类的工作,上一层窗体则放控件等等,所以这个底层窗体除了绘图之外基本上没什么别的事情。

这是这个对话框的全部代码:

头文件:

#pragma once
#include "afxwin.h"
#include "resource.h"

class HorizontalCtrl : public CDialog
{
public:

HorizontalCtrl(CWnd* pParent = NULL);
virtual ~HorizontalCtrl();

enum {IDD = IDD_HORIZONTAL_CTRL};

virtual BOOL DestroyWindow();

protected:

CButton Btn_OK;
CButton Btn_Cancel;

CBrush m_Brush;

char m_Log[1024];

bool m_Clicked;

virtual BOOL OnInitDialog();

virtual int LoadPNG(HINSTANCE hInstance, UINT nID, LPCTSTR sTR, Gdiplus::Bitmap **pImg);

virtual void DoDataExchange(CDataExchange* pDX);

afx_msg LRESULT OnMessage(WPARAM wParam, LPARAM lParam);
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnPaint();
afx_msg void OnBnClickedOk();
afx_msg void OnBnClickedCancel();

DECLARE_DYNAMIC(HorizontalCtrl)
DECLARE_MESSAGE_MAP()
};


CPP文件:

#pragma once
#include "stdafx.h"
#include "ShowCard.h"
#include "HorizontalCtrl.h"

IMPLEMENT_DYNAMIC(HorizontalCtrl, CDialog)

HorizontalCtrl::HorizontalCtrl(CWnd* pParent /*=NULL*/)
: CDialog(HorizontalCtrl::IDD, pParent)
{

}

HorizontalCtrl::~HorizontalCtrl()
{

}

BOOL HorizontalCtrl::DestroyWindow()
{
return CDialog::DestroyWindow();
}

BEGIN_MESSAGE_MAP(HorizontalCtrl, CDialog)
ON_MESSAGE(10086, OnMessage)
ON_WM_CTLCOLOR()
ON_WM_TIMER()
ON_WM_PAINT()
ON_BN_CLICKED(IDOK, &HorizontalCtrl::OnBnClickedOk)
ON_BN_CLICKED(IDCANCEL, &HorizontalCtrl::OnBnClickedCancel)
END_MESSAGE_MAP()

BOOL HorizontalCtrl::OnInitDialog()
{
CDialog::OnInitDialog();

Btn_OK.ShowWindow(SW_HIDE);
Btn_Cancel.ShowWindow(SW_HIDE);

m_Brush.CreateSolidBrush(RGB(255, 0, 255));
DWORD dwExStyle = GetWindowLong(m_hWnd,GWL_EXSTYLE);
::SetWindowLong(m_hWnd, GWL_EXSTYLE, dwExStyle|0x80000);
::SetLayeredWindowAttributes(this->GetSafeHwnd(), 0xff00ff, 0, 1);

HINSTANCE hInstance = AfxGetResourceHandle();
/*LoadPNG(hInstance, IDR_PNG_BACKROUND, _T("png"), &m_ImgBackround);*/
return TRUE;
}

int HorizontalCtrl::LoadPNG(HINSTANCE hInstance, UINT nID, LPCTSTR sTR, Gdiplus::Bitmap **pImg)
{
HRSRC hRsrc = ::FindResource(hInstance, MAKEINTRESOURCE(nID), sTR);
if (!hRsrc)
{
return -1;
}

DWORD len = SizeofResource(hInstance, hRsrc);
BYTE *lpRsrc = (BYTE*)LoadResource(hInstance, hRsrc);
if (!lpRsrc)
{
return -1;
}

HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, len);
BYTE *pmem = (BYTE*)GlobalLock(m_hMem);
memcpy(pmem, lpRsrc, len);
IStream *pstm;

CreateStreamOnHGlobal(m_hMem, FALSE, &pstm);
*pImg = Gdiplus::Bitmap::FromStream(pstm);

GlobalUnlock(m_hMem);
pstm->Release();
FreeResource(lpRsrc);
return 1;
}

void HorizontalCtrl::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDOK, Btn_OK);
DDX_Control(pDX, IDCANCEL, Btn_Cancel);
}

LRESULT HorizontalCtrl::OnMessage(WPARAM wParam, LPARAM lParam)
{
return 1;
}

HBRUSH HorizontalCtrl::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

if(nCtlColor=CTLCOLOR_DLG)
{
return m_Brush;
}

return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
}

void HorizontalCtrl::OnTimer(UINT nIDEvent)
{

}

void HorizontalCtrl::OnPaint()
{
CDialog::OnPaint();
}

void HorizontalCtrl::OnBnClickedOk()
{

}

void HorizontalCtrl::OnBnClickedCancel()
{

}
傻X 2012-08-02
  • 打赏
  • 举报
回复
代码是不是没上全啊。
感觉没什么问题呀。
每次4KB啊?BoundsChecker查查看?
cyfage 2012-08-02
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 的回复:]
排除法,,,一点一点来吧 逐渐增加,,我很多时候都是这样找出问题的
[/Quote]

谢谢……可我就是一步步排除,一直到绘制背景这一步没办法继续检测下去了

SolidBrush BackroundBrush(Color(stc_SetInfo.Transparent, 255, 255, 255));
graphics.FillRectangle(&BackroundBrush, 0, 0, Width, Height);


不会就没有内存增加,绘制了就增加……
cyfage 2012-08-02
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 的回复:]
/************************************************************/
Graphics graphics(MemDC.m_hDC);

SolidBrush BackroundBrush(Color(stc_SetInfo.Transparent, 255, 255, 255));
graphics.Fil……
[/Quote]
我试着这样用了下,看起来还是一样在吃内存……请问老兄说的函数调用到底是啥……


/************************************************************/
DrawBackround(&MemDC);
/************************************************************/



int HorizontalDlg::DrawBackround(CDC *memDC)
{
using namespace Gdiplus;

Graphics graphics(memDC->m_hDC);
SolidBrush BackroundBrush(Color(stc_SetInfo.Transparent, 150, 100, 100));
graphics.FillRectangle(&BackroundBrush, 0, 0, (int)((float)HOR_DEFWIDTH * stc_SetInfo.Coefficient), (int)((float)HOR_DEFHEIGHT * stc_SetInfo.Coefficient));

return 1;
}
wangwangtongtong 2012-08-02
  • 打赏
  • 举报
回复
排除法,,,一点一点来吧 逐渐增加,,我很多时候都是这样找出问题的
HuWenjin 2012-08-02
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 的回复:]

本人在WinCE下玩GDI好多年了,可以负责任的告诉你,必须在MemBitmap.DeleteObject()之前把pOldBit用SelectObject设置回去.
[/Quote]

GDI资源在删除前是必须要还原的,你好像没有把原来的选回去

不过,你说内存增加,GDI没有增,搞不明白了

耐心的看自己的代码,然后调试吧
cyfage 2012-08-02
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 的回复:]
本人在WinCE下玩GDI好多年了,可以负责任的告诉你,必须在MemBitmap.DeleteObject()之前把pOldBit用SelectObject设置回去.
[/Quote]

谢谢,我现在就是这么写的,不过看起来好像没什么帮助:

//绘图完成后的清理
pOldBit = MemDC.SelectObject(&MemBitmap);
DeleteObject(MemBitmap);
MemDC.DeleteDC();
ReleaseDC(pDC);
cyfage 2012-08-02
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 的回复:]
每次增加4K  有没一直增加?
这个4K值很怀疑是系统自身的,我的机器里一个空程序,点击它,切换它,也是这个4K增加
加到一定值就不加了,有时最小化后,就释放了。
[/Quote]

是一直增加
我试过最长十分钟里一直在增加,内存占用多出了几个MB出来,再久就不知道了。

不过使用Visual Leak Detector检测不到内存泄露。
pig357 2012-08-02
  • 打赏
  • 举报
回复
cdc和bitmap释放有顺序的吧,把二者的顺序换换
HuWenjin 2012-08-02
  • 打赏
  • 举报
回复
每次增加4K  有没一直增加?

这个4K值很怀疑是系统自身的,我的机器里一个空程序,点击它,切换它,也是这个4K增加
加到一定值就不加了,有时最小化后,就释放了。

QQ515311445 2012-08-02
  • 打赏
  • 举报
回复
本人在WinCE下玩GDI好多年了,可以负责任的告诉你,必须在MemBitmap.DeleteObject()之前把pOldBit用SelectObject设置回去.
FrankieWang008 2012-08-01
  • 打赏
  • 举报
回复
显卡坏啦??
加载更多回复(8)

19,469

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 图形处理/算法
社区管理员
  • 图形处理/算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧