友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第89部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
其中前三个参数分别是与打印机有关的信息字符串,可以从WIN。INI 的【windows 】section
中获得,各以逗号分隔,例如:
device=HP LaserJet 4P/4MP;HPPCL5E;LPT1:
代表三项意义:
Print Driver = HP LaserJet 4P/4MP
Print Type = HPPCL5E
Print Port = LPT1:
SDK 程序中对于打印所需做的努力,最低限度到此为止。显然,困难度并不高,但是其
中尚未参杂对打印机的控制,而那是比较麻烦的事儿。换句话说我们还得考虑「分页」
的问题。以文字为例,我们必须取得一页(一张纸)的大小,以及字形的高度,从而计
算扣除留白部份之后,一页可容纳几行:
TEXTMETRIC TextMetric;
int LineSpace;
int nPageSize;
int LinesPerPage;
GetTextMetrics(hPr; &TextMetric); //取得字形数据
LineSpace = TextMetric。tmHeight + TextMetric。tmExternalLeading; // 计算字高
nPageSize = GetDeviceCaps(hPr; VERTRES); // 取得纸张大小
LinesPerPage = nPageSize / LineSpace 1; // 一页容纳多少行
然后再以循环将每一行文字送往打印机:
Escape (hPr; STARTDOC; 4; 〃PrntFile text〃; (LPSTR) NULL);
CurrentLine = 1;
for (。。。) {
。。。 // 取得一行文字,放在char pLine'128' 中,长度为LineLength。
664
…………………………………………………………Page 727……………………………………………………………
第 12 章 列印與預樱А�
TextOut (hPr; 0; CurrentLine*LineSpace; (LPSTR)pLine; LineLength);
if (++CurrentLine 》 LinesPerPage ) {
CurrentLine = 1; //重设行号
IOStatus = Escape (hPr; NEWFRAME; 0; 0L; 0L); //换页
if (IOStatus 《 0 || bAbort)
break;
}
}
if (IOStatus 》= 0 && !bAbort) {
Escape (hPr; NEWFRAME; 0; 0L; 0L);
Escape (hPr; ENDDOC; 0; 0L; 0L);
}
其中的Escape 用来传送命令给打印机(打印机命令一般称为escape code ),它是一个
Windows API 函数。
打印过程中我们还应该提供一个中断机制给使用者。Modeless 对话框可以完成此一使
命,我们可以让它出现在打印过程之中。这个对话框应该在打印程序开始之前先做起来,
外形类似图12…1d:
HWND hPrintingDlgWnd; // 这就是【Printing】对话框
FARPROC lpPrintingDlg; // 【Printing】对话框的窗口函数
lpPrintingDlg = MakeProcInstance(PrintingDlg; hInst);
hPrintingDlgWnd = CreateDialog (hInst; 〃PrintingDlg〃; hWnd; lpPrintingDlg);
ShowWindow (hPrintingDlgWnd; SW_NORMAL);
负责此一中断机制的对话框函数很简单,只检查【OK 】钮有没有被按下,并据以改变
bAbort 的值:
int FAR PASCAL PrintingDlg (HWND hDlg; unsigned msg; WORD wParam; LONG lParam)
{
switch(msg) {
case WM_MAND:
return (bAbort = TRUE);
case WM_INITDIALOG:
SetFocus(GetDlgItem(hDlg; IDCANCEL));
SetDlgItemText(hDlg; IDC_FILENAME; FileName);
return (TRUE);
}
return (FALSE);
}
665
…………………………………………………………Page 728……………………………………………………………
第篇 深入 MFC 程式設計
从应用程序的眼光来看,这样就差不多了。然而资料真正送到打印机上,还有一大段曲
折过程。每一个送往打印机DC 的绘图动作,其实都只被记录为metafile (注)储存在
你的TEMP 目录中。当你调用Escape(hPr ; NEWFRAME; 。。。),打印机驱动程序(。DRV)
会把这些metafile 转换为打印机语言(control sequence 或Postscript ),然后通知GDI 模
组,由GDI 把它储存为~SPL 文件,也放在TEMP 目录中,并删除对应之metafile 。之
后,GDI 模块再送出消息给打印管理器Print Manager ,由后者调用Openm、
Writem 等低阶通讯函数(也都是Windows API 函数),把打印机命令传给打印机。
整个流程请参考图12…3。
注:metafile 也是一种图形记录规格,但它记录的是绘图动作,不像bitmap 记录的是真
正的图形资料。所以播放metafile 比播放bitmap 慢,因为多了一层绘图函数解读动作;
但它的大小比bitmap 小很多。用在有许多四形、圆形、工程几何图形上最为方便。
这个曲折过程之中就产生了一个问题。~SPL 这种文件很大,如果你的TEMP 目录空间
不够充裕,怎么办?如果Printer Manager 把积存的~SPL 内容消化掉后能够空出足够磁
碟空间的话,那么GDI 模块就可以下命令(送消息)给Printer Manager ,先把积存的
~SPL 档处理掉。问题是,在Windows 3。x 之中,我们的程序此刻正忙着做绘图动作,
GDI 没有机会送消息给Printer Manager (因为Windows 3。x 是个非强制性多任务系统) 。
解决方法是你先准备一个callback 函数,名称随你取,通常名为AbortProc :
FARPROC lpAbortProc;
lpAbortProc = MakeProcInstance(AbortProc; hInst);
Escape(hPr; SETABORTPROC; NULL; (LPSTR)(long)lpAbortProc; (LPSTR)NULL);
GDI 模块在执行Escape(hPr ; NEWFRAME。。。) 的过程中会持续调用这个callback 函数,想
办法让你的程序释放出控制权:
int FAR PASCAL AbortProc(hDC hPr; int Code)
{
MSG msg;
while (!bAbort && PeekMessage(&msg; NULL; NULL; NULL; TRUE))
if (!IsDialogMessage(hAbortDlgWnd; &msg)) {
TranslateMessage(&msg);
666
…………………………………………………………Page 729……………………………………………………………
第 12 章 列印與預樱А�
DispatchMessage(&msg);
}
return (!bAbort);
}
你可以从VC++ 4。0 所附的这个范例程序获得有关打印的极佳实例:
MSDEVSAMPLESSDKWIN32PRINTER
也可以在Charles Petzold 所着的Programming Windows 3。1 第15 章,或是其新版
Programming Windows 95 第15 章,获得更深入的资料。
Windows 程序
取得打印机DC (hPr) ,于是加载打印机驱动程序(DRV)
Escape(hPr; STARTDOC。。。) ,于是DRV 初始化并加载Print Manager
各式各样的GDI 绘图函数(储存为metafile ,~MF 档)
Escape(hPr; NEWFRAME。。。)
DRV 解读metafile ,转换为control sequences 或Postscript ,传给GDI 。
GDI 把DRV 的输出储存在~SPL 暂存盘中,并删除~MF 档。
GDI 送消息给Printer Manager ,要求打印机输出。应用程序继续进行自己的动作
Printer Manager 读~SPL 档,利用Openm、Writem 等API 函数把资料
)
(打印机命令码送往打印机,完毕后删除~SPL 档。
图12…3 Windows 程序的打印机输出动作详解
667
…………………………………………………………Page 730……………………………………………………………
第篇 深入 MFC 程式設計
以下就是SDK 程序中有关打印程序的一个实际片段。
#01 hSaveCursor = SetCursor(hHourGlass); // 把鼠标光标设为砂漏状
#02 hPr = CreateDC(〃HP LaserJet 4P/4MP〃; 〃HPPCL5E〃; 〃LPT1:〃; (LPSTR) NULL);
#03
#04 // 设定AbortProc callback 函数
#05 lpAbortProc = MakeProcInstance(AbortProc; hInst);
#06 Escape(hPr; SETABORTPROC; NULL; (LPSTR) (long) lpAbortProc; (LPSTR) NULL);
#07 bAbort = FALSE;
#08
#09 Escape(hPr; STARTDOC; 4; 〃PrntFile text〃; (LPSTR) NULL);
#10
#11 // 设定Printing 对话框及其窗口函数
#12 lpPrintingDlg = MakeProcInstance(PrintingDlg; hInst);
#13 hPrintingDlgWnd = CreateDialog(hInst; 〃PrintingDlg〃; hWnd; lpPrintingDlg);
#14 ShowWindow (hPrintingDlgWnd; SW_NORMAL);
#15 EnableWindow(hWnd; FALSE); // 令其父窗口 (也就是程序的主窗口) 除能
#16 SetCursor(hSaveCursor); // 鼠标光标形状还原
#17
#18 GetTextMetrics(hPr; &TextMetric);
#19 LineSpace = TextMetric。tmHeight + TextMetric。tmExternalLeading;
#20 nPageSize = GetDeviceCaps (hPr; VERTRES);
#21 LinesPerPage = nPageSize / LineSpace 1;
#22 dwLines = SendMessage(hEditWnd; EM_GETLINECOUNT; 0; 0L);
#23 CurrentLine = 1;
#24
#25 for (dwIndex = IOStatus = 0; dwIndex 《 dwLines; dwIndex++) {
#26 pLine'0' = 128;
#27 pLine'1' = 0;
#28 LineLength = SendMessage(hEditWnd; EM_GETLINE;
#29 (WORD)dwIndex; (LONG)((LPSTR)pLine));
#30 TextOut(hPr; 0; CurrentLine*LineSpace; (LPSTR)pLine; LineLength);
#31 if (++CurrentLine 》 LinesPerPage ) {
#32 CurrentLine = 1;
#33 IOStatus = Escape(hPr; NEWFRAME; 0; 0L; 0L);
#34 if (IOStatus= 0 && !bAbort) {
#40 Escape(hPr; NEWFRAME; 0; 0L; 0L);
#41 Escape(hPr; ENDDOC; 0; 0L; 0L);
#42 }
668
…………………………………………………………Page 731……………………………………………………………
第 12 章 列印與預樱А�
#43
#44 EnableWindow(hWnd; TRUE);
#45
#46 DestroyWindow(hPrintingDlgWnd);
#47 FreeProcInstance(lpPrintingDlg);
#48 FreeProcInstance(lpAbortProc);
#49 DeleteDC(hPr);
上述各个Escape 调用,是在Windows 3。0 下的传统作法,在Windows 3。1 以及Win32 之
中有对应的API 函数如下:
Windows 3。0 作法 Windows 3。1 作法
Escape(hPr; SETABORTPROC; 。。。) SetAbortProc(HDC hdc; ABORTPROC lpAbortProc)
Escape(hPr; STARTDOC; 。。。) StartDoc(HDC hdc; CONST DOCINFO* lpdi)
Escape(hPr; NEWFRAME; 。。。) EndPage(HDC hdc)
Escape(hPr; ENDDOC; 。。。) EndDoc(HDC hdc)
MFC 预设的打印机制
好啦,关于打印,其实有许多一成不变的动作!为什么开发工具不帮我们做掉呢?好比
说,从WIN。INI 中取得目前打印机的资料然后利用CreateDC 取得打印机DC ,又好比
说设计标准的【打印中】对话框,以及标准的打印中断函数AbortProc 。
事实上MFC 的确已经帮我们做掉了一大部份的工作。MFC 已内含打印机制,那么将
Framework 整个纳入EXE 文件中的你当然也就不费吹灰之力得到了打印功能。只要
OnDraw 函数设计好了,不但可以在屏幕上显示资料,也可以在打印机上显示资料。有
什么是我们要负担的?没有了!Framework 传给OnDraw 一个DC ,视情况的不同这个
DC 可能是显示屏DC ,也可能是打印机DC ,而你知道,Windows 程序中的图形输出
对象完全取决于DC :
669
…………………………………………………………Page 732……………………………………………………………
第篇 深入 MFC 程式設計
当你改变窗口大小,产生WM_PAINT ,OnDraw 会收到一个「显示屏DC 」。
当你选按【File/Print。。。 】,OnDraw 会收到一个「打印机DC 」。
数章之前讨论CView 时我曾经提过,OnDraw 是CView 类别中最重要的成员函数,所
有的绘图动作都应该放在其中。请注意,OnDraw 接受一个「CDC 对象指针」做为它的
参数。当窗口接受WM_PAINT 消息,Framework 就调用OnDraw 并把一个「显示屏DC 」
传过去,于是OnDraw 输出到屏幕上。
Windows 的图形装置接口(GDI )完全与硬件无关,相同的绘图动作如果送到「显示屏
DC 」,就是在屏幕上绘图,如果送到「打印机DC 」,就是在打印机上绘图。这个道理
很容易就解释了为什么您的程序代码没有任何特殊动作却具备打印功能:当使用者按下
【File/Print 】,application framework 送给OnDraw 的是一个「打印机DC 」而不再是「显
示幕DC 」。
在MFC 应用程序中,View 和application framework 分工合力完成打印工作。Application
framework 的责任是:
显示【Print 】对话框,如图12…1b。
为打印机产生一个CDC 对象。
调用CDC 对象的StartDoc 和EndDoc 两函数。
持续不断地调用CDC 对象的StartPage ,通知View 应该输出哪一页;一页打印
完毕则调用CDC 对象的EndPage 。
我们(程序员)在View 对象上的责任是:
通知application framework 总共有多少页要打印。
application framework 要求打印某特定页时,我们必须将Document 中对应的部
份输出到打印机上。
配置或释放任何GDI 资源,包括笔、刷、字形。。。等等。
如果需要,送出任何escape 码改变打印机状态,例如走纸、改变打印方向等等。
670
…………………………………………………………Page 733……………………………………………………………
第 12 章 列印與預樱А�
送出escape 码的方式是,调用CDC 对象的Escape 函数。
现在让我们看看这两组工作如何交叉在一起。为实现上述各项交互动作,CView 定义了
几个相关的成员函数,当你在AppWizard 中选择【Printing and Print Preview 】选项之后,
除了OnDraw,你的View 类别内还被加入了三个虚拟函数空壳:
// in SCRIBBLEVIEW。H
class CScribbleView : public CScrollView
{
。。。
protected:
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
virtual void OnBeginPrinting(CDC* pDC; CPrintInfo* pInfo);
virtual void OnEndPrinting(CDC* pDC; CPrintInfo* pInfo);
。。。
};
// in SCRIBBLEVIEW。CPP
BOOL CScribbleView::OnPreparePrinting(CPrintInfo* pInfo)
{
// default preparation
return DoPreparePrinting(pInfo);
}
void CScribbleView::OnBeginPrinting(CDC* /*pDC*/; CPrintInfo* /*pInfo*/)
{
// TODO: add extra initialization before
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!