📄 13. 使用打印机.txt
字号:
hInst = hInstance ;
hwnd = CreateWindow (szAppName, szCaption,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
void PageGDICalls (HDC hdcPrn, int cxPage, int cyPage)
{
static TCHAR szTextStr[] = TEXT ("Hello, Printer!") ;
Rectangle (hdcPrn, 0, 0, cxPage, cyPage) ;
MoveToEx (hdcPrn, 0, 0, NULL) ;
LineTo (hdcPrn, cxPage, cyPage) ;
MoveToEx (hdcPrn, cxPage, 0, NULL) ;
LineTo (hdcPrn, 0, cyPage) ;
SaveDC (hdcPrn) ;
SetMapMode (hdcPrn, MM_ISOTROPIC) ;
SetWindowExtEx (hdcPrn, 1000, 1000, NULL) ;
SetViewportExtEx (hdcPrn, cxPage / 2, -cyPage / 2, NULL) ;
SetViewportOrgEx (hdcPrn, cxPage / 2, cyPage / 2, NULL) ;
Ellipse (hdcPrn, -500, 500, 500, -500) ;
SetTextAlign (hdcPrn, TA_BASELINE | TA_CENTER) ;
TextOut (hdcPrn, 0, 0, szTextStr, lstrlen (szTextStr)) ;
RestoreDC (hdcPrn, -1) ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)
{
static int cxClient, cyClient ;
HDC hdc ;
HMENU hMenu ;
PAINTSTRUCT ps ;
switch (message)
{
case WM_CREATE:
hMenu = GetSystemMenu (hwnd, FALSE) ;
AppendMenu (hMenu, MF_SEPARATOR, 0, NULL) ;
AppendMenu (hMenu, 0, 1, TEXT ("&Print")) ;
return 0 ;
case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
return 0 ;
case WM_SYSCOMMAND:
if (wParam == 1)
{
if (!PrintMyPage (hwnd))
MessageBox (hwnd, TEXT ("Could not print page!"),
szAppName, MB_OK | MB_ICONEXCLAMATION) ;
return 0 ;
}
break ;
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;
PageGDICalls (hdc, cxClient, cyClient) ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
PRINT.C包括函数WinMain、WndProc以及一个称为PageGDICalls的函数。PageGDICalls函数接收打印机设备内容句柄和两个包含打印页面宽度及高度的变量。这个函数还负责画一个包围整个页面的矩形,有两条对角线,页中间有一个椭圆(其直径是打印机高度和宽度中较小的那个的一半),文字「Hello, Printer!」位于椭圆的中间。
处理WM_CREATE消息时,WndProc将一个「Print」选项加到系统菜单上。选择该选项将呼叫PrintMyPage,此函数的功能在程序的三个版本中将不断增强。当打印成功时,PrintMyPage传回TRUE值,如果遇到错误时则传回FALSE。如果PrintMyPage传回FALSE,WndProc就会显示一个消息框以告知使用者发生了错误。
打印的基本程序
打印程序的第一个版本是PRINT1,见程序13-5。经编译后即可执行此程序,然后从系统菜单中选择「Print」。接着,GDI将必要的打印机输出储存在一个临时文件中,然后打印队列程序将它发送给打印机。
程序13-5 PRINT1
PRINT1.C
/*---------------------------------------------------------------------
PRINT1.C -- Bare Bones Printing
(c) Charles Petzold, 1998
----------------------------------------------------------------------*/
#include <windows.h>
HDC GetPrinterDC (void) ; // in GETPRNDC.C
void PageGDICalls (HDC, int, int) ; // in PRINT.C
HINSTANCE hInst ;
TCHAR szAppName[] = TEXT ("Print1") ;
TCHAR szCaption[] = TEXT ("Print Program 1") ;
BOOL PrintMyPage (HWND hwnd)
{
static DOCINFO di = { sizeof (DOCINFO), TEXT ("Print1: Printing") } ;
BOOL bSuccess = TRUE ;
HDC hdcPrn ;
int xPage, yPage ;
if (NULL == (hdcPrn = GetPrinterDC ()))
return FALSE ;
xPage = GetDeviceCaps (hdcPrn, HORZRES) ;
yPage = GetDeviceCaps (hdcPrn, VERTRES) ;
if (StartDoc (hdcPrn, &di) > 0)
{
if (StartPage (hdcPrn) > 0)
{
PageGDICalls (hdcPrn, xPage, yPage) ;
if (EndPage (hdcPrn) > 0)
EndDoc (hdcPrn) ;
else
bSuccess = FALSE ;
}
}
else
bSuccess = FALSE ;
DeleteDC (hdcPrn) ;
return bSuccess ;
}
我们来看看PRINT1.C中的程序代码。如果PrintMyPage不能取得打印机的设备内容句柄,它就传回FALSE,并且WndProc显示消息框指出错误。如果函数成功取得了设备内容句柄,它就通过呼叫GetDeviceCaps来确定页面的水平和垂直大小(以图素为单位)。
xPage = GetDeviceCaps (hdcPrn, HORZRES) ;
yPage = GetDeviceCaps (hdcPrn, VERTRES) ;
这不是纸的全部大小,只是纸的可打印区域。呼叫后,除了PRINT1在StartPage和EndPage呼叫之间呼叫PageGDICalls,PRINT1的PrintMyPage函数中的程序代码在结构上与FORMFEED中的程序代码相同。仅当呼叫StartDoc、StartPage和EndPage都成功时,PRINT1才呼叫EndDoc打印函数。
使用放弃程序来取消打印
对于大型文件,程序应该提供使用者在应用程序行印期间取消打印任务的便利性。也许使用者只要打印文件中的一页,而不是打印全部的537页。应该要能在印完全部的537页之前纠正这个错误。
在一个程序内取消一个打印任务需要一种被称为「放弃程序」的技术。放弃程序在程序中只是个较小的输出函数,使用者可以使用SetAbortProc函数将该函数的地址传给Windows。然后GDI在打印时,重复呼叫该程序,不断地问:「我是否应该继续打印?」
我们看看将放弃程序加到打印处理程序中去需要些什么,然后检查一些旁枝末节。放弃程序一般命名为AbortProc,其形式为:
BOOL CALLBACK AbortProc (HDC hdcPrn, int iCode)
{
//其它行程序
}
打印前,您必须通过呼叫SetAbortProc来登记放弃程序:
SetAbortProc (hdcPrn, AbortProc) ;
在呼叫StartDoc前呼叫上面的函数,打印完成后不必清除放弃程序。
在处理EndPage呼叫时(亦即,在将metafile放入设备驱动程序并建立临时打印文件时),GDI常常呼叫放弃程序。参数hdcPrn是打印机设备内容句柄。如果一切正常,iCode参数是0,如果GDI模块在生成临时文件时耗尽了磁盘空间,iCode就是SP_OUTOFDISK。
如果打印作业继续,那么AbortProc必须传回TRUE(非零);如果打印作业异常结束,就传回FALSE(零)。放弃程序可以被简化为如下所示的形式:
BOOL CALLBACK AbortProc (HDC hdcPrn, int iCode)
{
MSG msg ;
while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return TRUE ;
}
这个函数看起来有点特殊,其实它看起来像是消息循环。使用者会注意到,这个「消息循环」呼叫PeekMessage而不是GetMessage。我在第五章的RANDRECT程序中讨论过PeekMessage。应该还记得,PeekMessage将会控制权返回给程序,而不管程序的
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -