为什么在CreateCompatibleBitmap的时候会报异常?

暗黑Zero 2015-03-25 11:00:33
我最近在用MFC写一个简单的游戏,取消了文档视图支持,然后每10毫秒发一个重绘消息,OnPaint和OnTimer是这么写的:

void CBomberManOnlineView::OnPaint()
{
CDC *pDC = GetDC();

GetClientRect(&client_rect);
cacheDC.CreateCompatibleDC(NULL);
cache_bitmap.CreateCompatibleBitmap(pDC, client_rect.Width(), client_rect.Height());
cacheDC.SelectObject(&cache_bitmap);
// TODO: 在此处添加消息处理程序代码

if(game_state == LOBBY)
{
LobbyRender(&cacheDC);
}
else if(game_state == INGAME)
{
p_game->Render(&cacheDC);
}


pDC->BitBlt(0, 0, client_rect.Width(), client_rect.Height(), &cacheDC,0,0,SRCCOPY);

ValidateRect(client_rect);
cache_bitmap.DeleteObject();
cacheDC.DeleteDC();

// 不要为绘制消息而调用 CWnd::OnPaint()
}

void CBomberManOnlineView::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值

if(nIDEvent == TIMER_RENDER)
{
OnPaint();
if(game_state == INGAME)
{
now_time = timeGetTime();
p_game->Update(now_time - last_time);
}
}
last_time = timeGetTime();
CWnd::OnTimer(nIDEvent);
}


运行程序的时候,一切都正常,人物也能移动,但是过了一段时间之后,就会突然爆出:
0x0F5079CC (mfc110ud.dll) (BomberManOnline.exe 中)处有未经处理的异常: 0xC0000005: 读取位置 0x00000004 时发生访问冲突。

查看call stack,发现是OnPaint里面的CreateCompatibleBitmap报的异常。

请问要处理这个bug,我应该做什么?这个代码中我写错了什么吗?

谢谢~
...全文
180 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
暗黑Zero 2015-03-26
  • 打赏
  • 举报
回复
引用 11 楼 lsq19871207 的回复:
我觉得是因为在创造DC的时候必须指定是针对哪个窗口创造的(不然要在哪儿画都不知道),Win32建立DC的时候也要指定当前窗口的句柄的 ------------------------------------------------------------------------------- 那为什么不直接传当前窗口的HWND,却要传个窗口指针呢? 我的意思不是考验你而显摆自己,对于这些问题,如果你真正理解Win32绘图机制,那也就不是问题了,你照样可以封一个CPaintDC出来!另外,建议还是多去看看MFC是怎么封装的那些GDI类,即使你不用Win32,也至少要知道这些MFC GDI类是如何运作的
这个我确实就不是很清楚了,有时间我会去底层看看它到底是怎么封装的; 谢谢在这个贴子里的指教!
信阳毛尖 2015-03-25
  • 打赏
  • 举报
回复
我觉得是因为在创造DC的时候必须指定是针对哪个窗口创造的(不然要在哪儿画都不知道),Win32建立DC的时候也要指定当前窗口的句柄的 ------------------------------------------------------------------------------- 那为什么不直接传当前窗口的HWND,却要传个窗口指针呢? 我的意思不是考验你而显摆自己,对于这些问题,如果你真正理解Win32绘图机制,那也就不是问题了,你照样可以封一个CPaintDC出来!另外,建议还是多去看看MFC是怎么封装的那些GDI类,即使你不用Win32,也至少要知道这些MFC GDI类是如何运作的
暗黑Zero 2015-03-25
  • 打赏
  • 举报
回复
引用 9 楼 lsq19871207 的回复:
CPaintDC的构造函数为什么要传一个当前窗口指针CPaintDC dc(this);?
我觉得是因为在创造DC的时候必须指定是针对哪个窗口创造的(不然要在哪儿画都不知道),Win32建立DC的时候也要指定当前窗口的句柄的
引用 9 楼 lsq19871207 的回复:
为什么CPaintDC dc(this) 这个栈dc对象能够控制绘制绘图并且不会造成相关的GDI泄露?它是怎么实 现控制窗口删除消息队列中的WM_PAINT消息,并使无效区域有效的?
我猜是CPaintDC在析构的时候自己把DC给Release掉了,删除消息队列当中的WM_PAINT消息应该是在EndPaint里面做的,不过EndPaint是怎么搞的我就不是很清楚了,Win32也是直接用的EndPaint…… 如有错误请多加指正!
信阳毛尖 2015-03-25
  • 打赏
  • 举报
回复
为什么CPaintDC dc(this) 这个栈dc对象能够控制绘制绘图并且不会造成相关的GDI泄露?它是怎么实 现控制窗口删除消息队列中的WM_PAINT消息,并使无效区域有效的?
信阳毛尖 2015-03-25
  • 打赏
  • 举报
回复
不要怕麻烦,也不要觉得很烦,Win32直接写绘图需要的代码量是要多些,但是再多其逻辑思路是很清晰的!大型的应用程序软件绘图部分是绝对不会使用MFC封装的GDI类的!一般情况下使用MFC GDI进行绘图的,也基本只限于demo功能测试程序 当然,如果说你是为了学习,用MFC GDI进行绘图也是可以的,但我的建议是你至少是在搞清楚Win32 GDI绘图原理之后!毕竟MFC GDI类是对Win32的封装 说到这儿,我就问你一个简单的问题吧:CPaintDC的构造函数为什么要传一个当前窗口指针CPaintDC dc(this);?
暗黑Zero 2015-03-25
  • 打赏
  • 举报
回复
引用 6 楼 lsq19871207 的回复:
[quote=引用 3 楼 u011808175 的回复:] [quote=引用 1 楼 lsq19871207 的回复:] 你这个OnPaint用的不对啊! OnPaint里面要使用::BeginPaint() 和::EndPaint(),或者是MFC的CPaintDC,而不是CDC *pDC = GetDC();
这样倒是也能画图…… 我后来发现也许是因为我忘记了Release(pDC)导致的,加上这一句话之后到目前再没出现过这个问题; 是说在MFC里面最好不要使用CDC类吗?[/quote] 是说在MFC里面最好不要使用CDC类吗 --------------------------------------------------------- 不是说不可以用,只是你一定要用对,一方面,在OnPaint中使用GetDC()是错误的,GetDC()一般用于OnPaint函数之外的额外绘图,比如说我鼠标左键单击一下在原来的绘图基础之上画一条线等等,并且GetDC()与ReleaseDC是要成对出现的,否则会造成GDI泄露! 另外,能不要用MFC的GDI类就不要用,最好使用Win32系统API函数,这样你才能够理解Windows的绘图机制!不然的话,即使你MFC绘图用的很顺溜,其底层怎么实现的可能你就是是而非了 [/quote] 谢谢指教,我之前确实学过Win32的API绘图机制,干写Win32绘图是能做到的,就是感觉代码太多看着烦……我只是对MFC里面一堆DC类是怎么封装那些API的不是特清楚…… 比如有的类自动封装了BeginPaint和EndPaint,有的类封装了GetDC和ReleaseDC,稍不注意就画不出来/内存泄露了…… 所以我一直习惯用CDC然后什么都自己控制……
信阳毛尖 2015-03-25
  • 打赏
  • 举报
回复
引用 3 楼 u011808175 的回复:
[quote=引用 1 楼 lsq19871207 的回复:] 你这个OnPaint用的不对啊! OnPaint里面要使用::BeginPaint() 和::EndPaint(),或者是MFC的CPaintDC,而不是CDC *pDC = GetDC();
这样倒是也能画图…… 我后来发现也许是因为我忘记了Release(pDC)导致的,加上这一句话之后到目前再没出现过这个问题; 是说在MFC里面最好不要使用CDC类吗?[/quote] 是说在MFC里面最好不要使用CDC类吗 --------------------------------------------------------- 不是说不可以用,只是你一定要用对,一方面,在OnPaint中使用GetDC()是错误的,GetDC()一般用于OnPaint函数之外的额外绘图,比如说我鼠标左键单击一下在原来的绘图基础之上画一条线等等,并且GetDC()与ReleaseDC是要成对出现的,否则会造成GDI泄露! 另外,能不要用MFC的GDI类就不要用,最好使用Win32系统API函数,这样你才能够理解Windows的绘图机制!不然的话,即使你MFC绘图用的很顺溜,其底层怎么实现的可能你就是是而非了
信阳毛尖 2015-03-25
  • 打赏
  • 举报
回复

void CBomberManOnlineView::OnPaint() 
{
	PAINTSTRUCT wndps = {0};
	::BeginPaint(m_hWnd,&wndps);
	HDC hDC = CreateCompatibleDC(wndps.hdc);
	GetClientRect(&client_rect);
	HBITMAP bmpCompatible = CreateCompatibleBitmap(wndps.hdc,client_rect.Width(), client_rect.Height());
	HGDIOBJ oldCompatible = SelectObject(hDC,bmpCompatible);
	SetBkMode(hDC,TRANSPARENT);

	if(game_state == LOBBY)
	{
		LobbyRender(&cacheDC);
	}
	else if(game_state == INGAME)
	{
		p_game->Render(&cacheDC);
	}

	::BitBlt(wndps.hdc,0,0,client_rect.Width(), client_rect.Height(),hDC,0,0,SRCCOPY);

	ValidateRect(client_rect);
	SelectObject(hDC,oldCompatible);
	DeleteObject(bmpCompatible);
	DeleteDC(hDC);
	::EndPaint(m_hWnd,&wndps);
}

void CBomberManOnlineView::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	if(nIDEvent == TIMER_RENDER)
	{
		::PostMessage(m_hWnd,WM_PAINT,0,0);	//Invalidate(FALSE);
		if(game_state == INGAME)
		{
			now_time = timeGetTime();
			p_game->Update(now_time - last_time);
		}
	}
	last_time = timeGetTime();
	CWnd::OnTimer(nIDEvent);
}

暗黑Zero 2015-03-25
  • 打赏
  • 举报
回复
引用 2 楼 worldy 的回复:
void CBomberManOnlineView::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if(nIDEvent == TIMER_RENDER) { //OnPaint(); Invalidate();//或者Update(); if(game_state == INGAME) { now_time = timeGetTime(); p_game->Update(now_time - last_time);//p_game是什么? } } last_time = timeGetTime(); CWnd::OnTimer(nIDEvent);//这个不需要向下传递,不用了 }
谢谢,我一直想知道Invalidate()和直接调用OnPaint()到底有什么不一样…… p_game是指向游戏类的指针,那句话的意思是更新游戏里面的数据,比如人物的坐标之类的 最后那句话是MFC自动生成的,可以删掉是吧?
暗黑Zero 2015-03-25
  • 打赏
  • 举报
回复
引用 1 楼 lsq19871207 的回复:
你这个OnPaint用的不对啊! OnPaint里面要使用::BeginPaint() 和::EndPaint(),或者是MFC的CPaintDC,而不是CDC *pDC = GetDC();
这样倒是也能画图…… 我后来发现也许是因为我忘记了Release(pDC)导致的,加上这一句话之后到目前再没出现过这个问题; 是说在MFC里面最好不要使用CDC类吗?
worldy 2015-03-25
  • 打赏
  • 举报
回复
void CBomberManOnlineView::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if(nIDEvent == TIMER_RENDER) { //OnPaint(); Invalidate();//或者Update(); if(game_state == INGAME) { now_time = timeGetTime(); p_game->Update(now_time - last_time);//p_game是什么? } } last_time = timeGetTime(); CWnd::OnTimer(nIDEvent);//这个不需要向下传递,不用了 }
信阳毛尖 2015-03-25
  • 打赏
  • 举报
回复
你这个OnPaint用的不对啊! OnPaint里面要使用::BeginPaint() 和::EndPaint(),或者是MFC的CPaintDC,而不是CDC *pDC = GetDC();
洗洗睡去 2015-03-25
  • 打赏
  • 举报
回复
学习了

16,490

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • AIGC Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

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