VS2008转VS2022应用调用CDC::StartDoc打印崩溃(预览正常)
问题背景
从VS2008移植到VS2022的MFC应用,打印预览功能正常,但调用CDC::StartDoc()时触发崩溃,调试输出报0x80070490(元素未找到)错误,代码未做任何修改,初步怀疑缺失头文件或库文件。
调试输出信息:
onecoreuap\printscan\print\workflow\printsupport\dll\printsupportutil.cpp(573)\Print.PrintSupport.Source.dll!7AC3BA60: (caller: 7AC26A2B) Exception(1) tid(7094) 80070490 Element not found.
Exception thrown at 0x75A37402 in IPC2000.exe: Microsoft C++ exception: wil::ResultException at memory location 0x06B6E4AC.
Exception thrown at 0x75A37402 in IPC2000.exe: Microsoft C++ exception: wil::ResultException at memory location 0x06B6DA50.
Exception thrown at 0x75A37402 in IPC2000.exe: Microsoft C++ exception: wil::ResultException at memory location 0x0018BA70.
Exception thrown at 0x75A37402 in IPC2000.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000.
Print.PrintSupport.Source.dll!7AC19ED8: ReturnHr(1) tid(6770) 80070490 Element not found.
Msg:[onecoreuap\printscan\print\workflow\printsupport\dll\printsupportutil.cpp(573)\Print.PrintSupport.Source.dll!7AC3BA60: (caller: 7AC26A2B) Exception(1) tid(6770) 80070490 Element not found.
]
Print.PrintSupport.Source.dll!7AC151F7: LogHr(1) tid(6770) 80070490 Element not found.
Exception thrown at 0x75A37402 (KernelBase.dll) in IPC2000.exe: WinRT originate error - 0x80070490 : 'Element not found.'.
Exception thrown at 0x75A37402 in IPC2000.exe: Microsoft C++ exception: winrt::hresult_error at memory location 0x0018BBC8.
解决方案
1. 修正DOCINFO结构体初始化
VS2022绑定的新版Windows SDK对DOCINFO的字段校验更严格,旧代码可能省略了cbSize的设置,导致系统无法正确识别结构体。必须显式初始化该字段:
DOCINFO di = {0}; di.cbSize = sizeof(DOCINFO); // 关键:必须设置,旧VS2008代码可能遗漏 di.lpszDocName = _T("打印任务");
2. 禁用WinRT打印兼容层
调试输出中的WinRT异常表明系统尝试用新打印框架处理旧MFC逻辑,添加编译宏禁用WinRT支持:
- 打开项目属性 → C/C++ → 预处理器 → 预处理器定义,添加
_DISABLE_WINRT_SUPPORT
3. 显式链接旧版打印核心库
VS2022默认链接新版打印组件,可强制链接旧版winspool.lib(VS2008时代的打印核心库):
- 项目属性 → 链接器 → 输入 → 附加依赖项,添加
winspool.lib
4. 排除打印机驱动问题
打印预览正常但StartDoc崩溃,可能是第三方打印机驱动与新版打印框架不兼容。先切换到系统默认的Microsoft Print to PDF测试,若能正常打印,说明需更新打印机驱动。
5. 增加错误捕获调试
在StartDoc()调用前后添加错误日志,便于定位更细节的问题:
// 调用StartDoc前获取错误码 DWORD preErr = GetLastError(); TRACE(_T("StartDoc前错误码:%lu\n"), preErr); int ret = pDC->StartDoc(&di); if (ret == SP_ERROR) { DWORD err = GetLastError(); CString errMsg; errMsg.Format(_T("StartDoc失败,错误码:%lu"), err); AfxMessageBox(errMsg); }
内容的提问来源于stack exchange,提问作者KokoCa

