C++(MFC)非模态调用默认打印机无法修改打印方向问题
问题
环境:Windows 10、Visual Studio 2017,使用C++(MFC)开发程序。
程序收到硬件触发信号时执行打印操作,当前打印功能正常,但需要实现检查页面方向,若为纵向则强制修改为横向的逻辑。
现有修改方向的代码片段:
CPrintDialog PrnDlg(FALSE); if (PrnDlg.GetDefaults()) { DEV_MODE *pDevMode; pDevMode = (DEVMODE *)::GlobalLock(PrnDlg.m_pd.hDevMode); if (pDevMode->dmOrientation != DMORIENT_LANDSCAPE) { pDevMode->dmOrientation = DMORIENT_LANDSCAPE; } ::GlobalUnlock(PrnDlg.m_pd.hDevMode); // 其他未展示代码 }
问题:修改后默认打印机仍以纵向打印,换成PrnDlg.DoModal()则功能正常,请问遗漏了什么步骤?
补充的默认打印机打印代码(未包含所有修改方向的尝试):
DOCINFO DocInfo; CPrintInfo PrintInfo; PRINTDLG pd; delete PrintInfo.m_pPD; // 构造自定义CLPrintDlg实例并赋值 PrintInfo.m_pPD = new CLPrintDlg(this, snPageSetupPaperOrientation, snPageSetupPaperSize, snPageSetupPaperSource); PrintInfo.m_bDocObject = FALSE; PrintInfo.m_dwFlags = 0; PrintInfo.m_bDirect = FALSE; PrintInfo.m_bPreview = FALSE; CPrintDialog PrnDlg(FALSE); if (PrnDlg.GetDefaults()) { CString StrTitle = _T("HISTOGRAM PRINTOUT"); memset(&DocInfo, 0, sizeof(DOCINFO)); DocInfo.cbSize = sizeof(DOCINFO); DocInfo.lpszDocName = StrTitle; DocInfo.lpszOutput = NULL; CDC dcPrint; dcPrint.Attach(PrnDlg.m_pd.hDC); dcPrint.m_bPrinting = TRUE; dcPrint.StartDoc(&DocInfo); int nYRes = dcPrint.GetDeviceCaps(VERTRES); int nXRes = dcPrint.GetDeviceCaps(HORZRES); dcPrint.SetMapMode(MM_HIMETRIC); CSize size; size.cx = nPageSetupLeftMargin; size.cy = nPageSetupTopMargin; dcPrint.HIMETRICtoDP(&size); int nLeftMarg = size.cx; int nTopMarg = size.cy; size.cx = nPageSetupRightMargin; size.cy = nPageSetupBottomMargin; dcPrint.HIMETRICtoDP(&size); int nRightMarg = size.cx; int nBottomMarg = size.cy; int nXAvailable = nXRes - nLeftMarg - nRightMarg; int nYAvailable = nYRes - nTopMarg - nBottomMarg; dcPrint.SetMapMode(MM_ANISOTROPIC); dcPrint.StartPage(); int nHeaderHeight = (int)(nYAvailable * HEADER_HEIGHT); int nMargBetweenHeaderAndHistogram = (int)(nYAvailable * MARG_BETWEEN_HEADER_AND_HISTOGRAM); int nHistogramHeight = (int)(nYAvailable * HISTOGRAM_HEIGHT); int nMargBetweenHistogramAndLineReel = (int)(nYAvailable * MARG_BETWEEN_HISTOGRAM_AND_LINE_REEL); int nLineReelHeight = (int)(nYAvailable * LINE_REEL_HEIGHT); int nMargBetweenLineReelAndData = (int)(nYAvailable * MARG_BETWEEN_LINE_REEL_AND_DATA); int nDataHeight = (int)(nYAvailable * DATA_HEIGHT); int nMargBetweenDataAndFooter = (int)(nYAvailable * MARG_BETWEEN_DATA_AND_FOOTER); int nFooterHeight = (int)(nYAvailable * FOOTER_HEIGHT); int nYOffset = nTopMarg; dcPrint.SetWindowExt(nXAvailable, nHeaderHeight); dcPrint.SetViewportExt(nXAvailable, nHeaderHeight); dcPrint.SetViewportOrg(nLeftMarg, nYOffset); pReelHeaderWnd->Print(dcPrint); nYOffset += nHeaderHeight + nMargBetweenHeaderAndHistogram; dcPrint.SetWindowExt(nXAvailable, nHistogramHeight); dcPrint.SetViewportExt(nXAvailable, nHistogramHeight); dcPrint.SetViewportOrg(nLeftMarg, nYOffset); pReelViewWnd->ShowPileWidthButtons(false); pReelViewWnd->Print(dcPrint); pReelViewWnd->ShowPileWidthButtons(true); nYOffset += nHistogramHeight + nMargBetweenHistogramAndLineReel; dcPrint.SetWindowExt(nXAvailable, nLineReelHeight); dcPrint.SetViewportExt(nXAvailable, nLineReelHeight); dcPrint.SetViewportOrg(nLeftMarg, nYOffset); pLineReelInfoWnd->Print(szLineName, szReelId, dcPrint); nYOffset += nLineReelHeight + nMargBetweenLineReelAndData; dcPrint.SetWindowExt(nXAvailable, nDataHeight); dcPrint.SetViewportExt(nXAvailable, nDataHeight); dcPrint.SetViewportOrg(nLeftMarg, nYOffset); CRect rect(0, 0, nXAvailable, nDataHeight); //dcPrint.Rectangle( rect ); // Debug pReelDataWnd->Print(dcPrint); nYOffset += nDataHeight + nMargBetweenDataAndFooter; dcPrint.SetWindowExt(nXAvailable, nFooterHeight); dcPrint.SetViewportExt(nXAvailable, nFooterHeight); dcPrint.SetViewportOrg(nLeftMarg, nYOffset); //dcPrint.Rectangle( 0,0, nXAvailable, nFooterHeight ); // Debug pReelFooterWnd->Print(CLConfig::szCompanyName, dcPrint); dcPrint.EndPage(); dcPrint.EndDoc(); }
解决方案
遗漏的核心步骤是修改DEVMODE后,没有标记字段变更,也未将DEVMODE的修改同步到打印DC。DoModal()内部会自动处理DEVMODE到DC的同步流程,但GetDefaults()仅初始化默认参数,需手动补充以下操作:
- 修改DEVMODE的
dmOrientation后,必须设置dmFields标记,表明dmOrientation字段已被修改 - 调用
ResetDC()将修改后的DEVMODE应用到打印对话框的DC上
修改后的完整方向调整代码:
CPrintDialog PrnDlg(FALSE); if (PrnDlg.GetDefaults()) { DEVMODE* pDevMode = reinterpret_cast<DEVMODE*>(::GlobalLock(PrnDlg.m_pd.hDevMode)); if (pDevMode) { if (pDevMode->dmOrientation != DMORIENT_LANDSCAPE) { pDevMode->dmOrientation = DMORIENT_LANDSCAPE; // 标记方向字段已修改 pDevMode->dmFields |= DM_ORIENTATION; } // 将修改后的DEVMODE同步到打印DC ::ResetDC(PrnDlg.m_pd.hDC, pDevMode); ::GlobalUnlock(PrnDlg.m_pd.hDevMode); } // 后续打印代码... }
额外注意事项:
- 确保
PrnDlg.m_pd.hDC有效(GetDefaults()成功后会初始化该句柄) - 修改完成后必须调用
GlobalUnlock()释放全局内存块,避免内存泄漏
内容的提问来源于stack exchange,提问作者Kap
相关产品推荐
相关产品推荐

