为何通过CPrintDialog::GetPrinterDC()获取的DC调用CDC::IsPrinting()返回FALSE?
你遇到的这个问题其实是MFC的CDC::IsPrinting()方法的设计逻辑导致的,先结合你的代码来拆解原因:
CPrintDialog pd ( TRUE , PD_RETURNDC ); if ( pd.DoModal () == IDOK ) { HDC hDC = pd.GetPrinterDC(); CDC printDC; printDC.Attach ( hDC ); /*...调用drawSomething(printDC)...*/ }
在drawSomething里调用DC.IsPrinting()却返回FALSE,核心原因如下:
原因分析
CDC::IsPrinting()本质是返回其内部的m_bPrinting成员变量值,这个变量默认是FALSE。只有当CDC对象是通过MFC打印框架初始化时(比如OnPrint函数中传递的CDC、或者调用CDC::StartDoc启动打印作业后),MFC才会自动把m_bPrinting设为TRUE。
而你通过CPrintDialog::GetPrinterDC()拿到的是一个原始的打印机HDC,手动Attach到空的CDC对象上时,MFC并不会自动设置m_bPrinting为TRUE——因为这个CDC只是简单封装了打印机句柄,并没有关联到MFC的打印上下文流程里。
解决办法
有两种实用的解决思路:
方法1:手动标记打印状态(派生CDC类)
因为m_bPrinting是CDC的protected成员,你可以派生一个自定义CDC类,添加方法来修改这个变量:
class CMyPrintDC : public CDC { public: void SetPrinting(bool bPrinting) { m_bPrinting = bPrinting; } };
然后替换原有代码使用:
if ( pd.DoModal () == IDOK ) { HDC hDC = pd.GetPrinterDC(); CMyPrintDC printDC; printDC.Attach ( hDC ); printDC.SetPrinting(TRUE); // 手动设置打印状态 drawSomething(printDC); }
这样IsPrinting()就会返回TRUE了。
方法2:直接判断DC是否为打印机设备
如果不想派生类,也可以跳过IsPrinting(),直接通过Windows API判断设备类型:
void drawSomething ( CDC& DC ) { int deviceTech = DC.GetDeviceCaps(TECHNOLOGY); if (deviceTech == DT_RASPRINTER) { //仅针对打印机的特殊绘图 } //标准绘图 }
GetDeviceCaps(TECHNOLOGY)会返回设备类型,DT_RASPRINTER代表打印机设备,这种判断更直接,也不依赖MFC内部状态。
额外提示
其实CPrintDialog在PD_RETURNDC标志下,m_pd.hDC也持有这个打印机DC,你直接用pd.m_pd.hDC来Attach效果和GetPrinterDC()一致,但本质问题还是一样的,需要手动处理打印状态或用API判断。
内容的提问来源于stack exchange,提问作者prompt

