为什么将Gdiplus初始化代码移至OnPaint函数会触发访问违例报错
问题原因
这个异常是Gdiplus对象析构时机和Gdiplus资源释放顺序错误导致的,核心逻辑如下:
- Gdiplus所有的对象创建、成员方法调用、对象销毁操作,都必须发生在
GdiplusStartup调用之后、GdiplusShutdown调用之前。 - 你写的OnPaint代码中,
Graphics和Image都是栈上分配的局部对象,这类对象的析构时机是函数执行结束、准备返回前,而你的GdiplusShutdown调用写在了函数结束前的最后一行,执行顺序就变成了:- 调用
GdiplusStartup初始化Gdiplus - 创建
Graphics、Image对象 - 调用
GdiplusShutdown释放所有Gdiplus全局资源 - 函数退出前执行
Image、Graphics的析构函数,此时Gdiplus资源已经被完全释放,析构函数访问已释放的内存空间,就触发了access violation异常。
- 调用
额外说明
Gdiplus本身是进程级别的组件,全局只需要初始化、销毁各一次即可,不需要每次触发WM_PAINT消息都重复执行初始化销毁逻辑:WM_PAINT触发频率非常高(窗口移动、大小调整、被遮挡后重新显示都会触发),重复初始化不仅会严重降低绘制性能,还会增加资源冲突的概率。
修复方案
推荐方案
保持原有逻辑,将GdiplusStartup放在WinMain函数入口处执行,GdiplusShutdown放在WinMain函数退出前执行,全局仅执行一次初始化和销毁。
临时兼容方案(不推荐)
如果你确实需要在OnPaint内部完成初始化销毁,可以通过增加独立作用域的方式,让Gdiplus对象在GdiplusShutdown执行前完成析构:
void OnPaint(HDC hdc) { GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); // 独立作用域包裹Gdiplus对象 { Graphics graphics(hdc); Image theImg(L"C:/....bmp"); //graphics.DrawImage(&theImg, 0, 0); } // 执行到此处时,Graphics和Image对象已经析构完成 GdiplusShutdown(gdiplusToken); }
内容的提问来源于stack exchange,提问作者darek_911
相关产品推荐
相关产品推荐

