windows程序设计 「PopPad3」之 POPPRNT.C 打印范例分析 笔记

/*----------------------------------------------
   POPPRNT.C -- Popup Editor Printing Functions
  ----------------------------------------------*/
 
#include <windows.h>
#include <commdlg.h>
#include "resource.h"
 
BOOL bUserAbort ;
HWND hDlgPrint ;
 
BOOL CALLBACK PrintDlgProc ( HWND hDlg , UINT msg , WPARAM wParam , LPARAM lParam)
{
     switch ( msg)
     {
     case WM_INITDIALOG :
          // 使系统菜单中的 退出项 不可选
          EnableMenuItem ( GetSystemMenu ( hDlg , FALSE ), SC_CLOSE , MF_GRAYED ) ;
          return TRUE ;
         
     case WM_COMMAND :
          bUserAbort = TRUE ;
          // 使对话框父窗口为有效,接受键盘鼠标输入
          EnableWindow ( GetParent ( hDlg ), TRUE ) ;
          // 摧毁对话框,清除对话框句柄
          DestroyWindow ( hDlg) ;
          hDlgPrint = NULL ;
          return TRUE ;
     }
     return FALSE ;
}      
   
/* 参数 hPrinterDC 是打印机设备内容句柄 ,
  如果一切正常, iCode 参数是 0,
  如果 GDI 模块在生成临时文件时耗尽了磁盘空间, iCode 就是 SP_OUTOFDISK,
  如果打印作业继续,那么 AbortProc 必须传回 TRUE (非零) ;
  如果打印作业异常结束,就传回 FALSE (零) .*/
BOOL CALLBACK AbortProc ( HDC hPrinterDC , int iCode)
{
     MSG msg ;
     // 如果用户点击 取消打印 则会退出消息检查循环 ,并返回 FALSE
     // PM_REMOVE 表示 PeekMessage 处理后,消息从队列里除掉
     while ( ! bUserAbort && PeekMessage ( & msg , NULL , 0 , 0 , PM_REMOVE ))
     {
           // 拦截非模态对话框消息
          if ( ! hDlgPrint || ! IsDialogMessage ( hDlgPrint , & msg))
          {
               TranslateMessage ( & msg) ;
               DispatchMessage ( & msg) ;
          }
     }
     return ! bUserAbort ;
}
 
BOOL PopPrntPrintFile ( HINSTANCE hInst , HWND hwnd , HWND hwndEdit ,
                       PTSTR szTitleName)
{
     static DOCINFO     di = { sizeof ( DOCINFO) } ;
     static PRINTDLG    pd ;
     BOOL               bSuccess ;
     int                yChar , iCharsPerLine , iLinesPerPage , iTotalLines ,
                        iTotalPages , iPage , iLine , iLineNum ;
     PTSTR              pstrBuffer ;
     TCHAR              szJobName [ 64 + MAX_PATH ] ;
     TEXTMETRIC         tm ;
     WORD               iColCopy , iNoiColCopy ;
    
     // 指定这个结构的大小
     pd . lStructSize         = sizeof ( PRINTDLG) ;

     pd . hwndOwner           = hwnd ;    // 指向对话框所有者窗口的句柄

     /* 包含打印机的设备与环境信息的 DEVMODE 结构的句柄。
       如果 hDevMode 不为 NULL ,则必须分配 DEVMODE 结构可移动的内存块,并初始化它的成员。
       PrintDlg 函数使用输入数据以初始化对话框中的控件。
       当 PrintDlg 返回时, DEVMODE 的成员显示用户的输入。
       如果 hDevMode 输入为 NULL , PrintDlg 为 DEVMODE 结构分配内存,初始化它的成员。
       以指示用户的输入,并返回一个句柄,标识它。 */
     pd . hDevMode            = NULL ;

      /* 包含驱动器名、打印机名和输出端口名的设备名结构 DEVNAMES 的句柄。
       如果 hDevNames 不为 NULL ,你必须分配 DEVNAMES 结构可移动的内存块,并初始化它的成员。
       PrintDlg 函数使用输入数据以初始化对话框中的控件。
       当 PrintDlg 返回时, DEVNAMES 成员包含由用户选择的打印机的信息。
       可以使用此信息来创建一个设备上下文或信息上下文。
       当 hDevNames 成员是 NULL ,在这种情况下, PrintDlg 为 DEVNAMES 结构分配内存,初始化它的成员。
       以指示用户的输入,并返回一个句柄,标识它。 */
     pd . hDevNames           = NULL ;

     /* 一个设备上下文句柄或一个信息上下文,这取决于是否指定旗标成员 PD_RETURNDC 或 PC_RETURNIC 标志。
     如果没有指定标志,这个成员的值是不确定的。如果指定了这两个标志, PD_RETURNDC 优先。 */
     pd . hDC                 = NULL ;

      /* 初始化打印对话框。当对话框返回时,它将会设置这些标志,以指示用户的输入。
       PD_ALLPAGES  默认的标志,指示所有单选按钮的最初选择。
        此标志作为一个占位符,表示 PD_PAGENUMS 和 PD_SELECTION 标志不指定。
       PD_COLLATE  如果这个标志被设置,副本逐份打印 复选框在初始时被选中。
       如果这个标志被 PrintDlg 函数返回时设置,应用程序必须模拟逐份打印方式。
       PD_RETURNDC 告诉 PrintDlg 自动建立打印机的装置或资讯的內容。
       并要求返回一个用户在对话框中选择的设备上下文的句柄。
       PD_NOSELECTION  禁止选择单选按钮。 */
     pd . Flags               = PD_ALLPAGES | PD_COLLATE |
                              PD_RETURNDC | PD_NOSELECTION ;

      /* 保存并初始化起始页面的值 */
     pd . nFromPage           = 0 ;

     /* 保存并初始化结束页面的值 */
     pd . nToPage             = 0 ;

     /* 保存并初始化页面的最低范围值 */
     pd . nMinPage            = 0 ;

     /* 保存并初始化页面的最高范围值 */
     pd . nMaxPage            = 0 ;

     /* 保存并初始化需要打印的份数 */
     pd . nCopies             = 1 ;

      /* 如果 PD_ENABLEPRINTTEMPLATE 或 PD_ENABLESETUPTEMPLATE 标志在 flags 成员设置,
       hInstance 的是一个自定义对话框模板资源被载入内存后的句柄,
       该句柄包含对话框模板由 lpPrintTemplateName 或 lpSetupTemplateName 成员命名。 */
     pd . hInstance           = NULL ;

     /* 应用程序定义的数据,该数据传递到钩子程序由 lpfnPrintHook 或 lpfnSetupHook 成员确定。
       当系统发送 WM_INITDIALOG 消息到钩子程序,该消息的 lParam 参数是一个对话框创建时 PRINTDLG 结构的指针。
       钩子程序可以使用该指针来获取 lCustData 价值。 */
     pd . lCustData           = 0L ;

     /* 一个对 PrintHookProc 钩子程序,处理打印对话框的消息列。
       这个成员被忽略,除非 PD_ENABLEPRINTHOOK 标志在 flags 成员设置。
       如果挂钩函数处理 WM_CTLCOLORDLG 信息,挂钩函数必须返回一个刷子句柄,此刷子用来刷控制背景 */
     pd . lpfnPrintHook       = NULL ;

      /* 一个对 SetupHookProc 钩子程序,处理打印设置对话框消息列。
       这个成员被忽略,除非 PD_ENABLESETUPHOOK 标志在 flags 成员设置。
       如果挂钩函数处理 WM_CTLCOLORDLG 信息,挂钩函数必须返回一个刷子句柄,此刷子用来刷控制背景 */
     pd . lpfnSetupHook       = NULL ;

      /* 指向一个以空字符结束的字符串,字符串是对话框模板资源的名字,
       资源保存在能被 hInstance 成员识别的模块中。此模板替换默认的打印对话框模板。 */
     pd . lpPrintTemplateName = NULL ;

      /* 指向一个以空字符结束的字符串,字符串是对话框模板资源的名字,
       资源保存在能被 hInstance 成员识别的模块中。此模板替换默认的打印设置对话框模板。 */
     pd . lpSetupTemplateName = NULL ;

     /* 如果 PD_ENABLEPRINTTEMPLATEHANDLE 标志在 flags 成员集,
       hPrintTemplate 是一个句柄内存对象,它包含一个对话框模板。此模板替换默认的打印对话框模板。 */
     pd . hPrintTemplate      = NULL ;

     /* 如果 PD_ENABLESETUPTEMPLATEHANDLE 标志在 flags 成员集,
       hSetupTemplate 是一个句柄内存对象,它包含一个对话框模板。此模板替换默认打印设置对话框模板。 */
     pd . hSetupTemplate      = NULL ;
    
      /* 激活打印对话框
        如果用户点击 OK 按钮,返回值为非零值。
       如果用户取消或关闭 Print 或 PrinterSetup 对话框或错误出现,返回值为零 */
     if ( ! PrintDlg ( & pd))
          return TRUE ;

               // 取得编辑控件中文字的行数
     if ( 0 == ( iTotalLines = SendMessage ( hwndEdit , EM_GETLINECOUNT , 0 , 0)))
          return TRUE ;
 
     // 取得当前打印机的字体信息
     GetTextMetrics ( pd . hDC , & tm) ;
     yChar = tm . tmHeight + tm . tmExternalLeading ;
     // 当前打印机每页打印宽度 除以 小写字体的宽度 得到 打印机每行可打印字符数
     iCharsPerLine = GetDeviceCaps ( pd . hDC , HORZRES ) / tm . tmAveCharWidth ;
     // 当前打印机每页打印高度 除以 字体的高度 得到 打印机每页可打印行数
     iLinesPerPage = GetDeviceCaps ( pd . hDC , VERTRES ) / yChar ;
     // 计算得到需要打印的总页数 ( 取整 )
     iTotalPages   = ( iTotalLines + iLinesPerPage - 1) / iLinesPerPage ;
 
     // 为准备打印的一行文本分配一个缓冲区
     pstrBuffer = malloc ( sizeof ( TCHAR) * ( iCharsPerLine + 1)) ;
 
     // 使整个父窗口无效,不接受键盘和鼠标输入
     EnableWindow ( hwnd , FALSE ) ;
    
     bSuccess     = TRUE ;
     bUserAbort   = FALSE ;
    
     // 创建 取消打印 非模态对话框
     hDlgPrint = CreateDialog ( hInst , TEXT ( " PrintDlgBox " ),
                                hwnd , PrintDlgProc) ;
                              
     // 设置取消打印对话框中的 IDC_FILENAME 控件显示文本为打开的文件名及扩展名
     SetDlgItemText ( hDlgPrint , IDC_FILENAME , szTitleName) ;

     /* 注册放弃打印程序。
       在处理 EndPage 呼叫时 , 亦即 , 在将 metafile 放入设备驱动程序并建立临时打印文件时 ,
       GDI 常常呼叫该 放弃打印程序 。 */
     SetAbortProc ( pd . hDC , AbortProc ) ;
 
     // 取得父窗口标题栏文字
     GetWindowText ( hwnd , szJobName , sizeof ( szJobName)) ;
     // 存储到打印信息结构中,用于显示在打印队列中
     di . lpszDocName = szJobName ;
    
     if ( StartDoc ( pd . hDC , & di) > 0)
     {
          for ( iColCopy = 0 ;
                /* pd.Flags & PD_COLLATE
                  如果 副本逐份打印 复选框被选择 , 则循环打印实际设置的份数,
                  否则循环一次 , 内层循环中先将当前页打印需要的份数 , 再更新需打印的数据 .*/
               iColCopy < (( WORD) pd . Flags & PD_COLLATE ? pd . nCopies : 1) ;
               iColCopy ++)
          {
               // 实现打印文件翻页,更新打印数据
               for ( iPage = 0 ; iPage < iTotalPages ; iPage ++)
               {
                    for ( iNoiColCopy = 0 ;
                         /* pd.Flags & PD_COLLATE
                            如果 副本逐份打印 复选框被选择 , 则打印当前页 1 份;
                            否则将当前页打印实际设置的份数 , 再更新需打印的数据 .*/
                         iNoiColCopy < ( pd . Flags & PD_COLLATE ? 1 : pd . nCopies);
                         iNoiColCopy ++)
                     {
                         // 如果开始新的一页失败,则设置标记,跳出内层循环
                         if ( StartPage ( pd . hDC) < 0)
                         {
                              bSuccess = FALSE ;
                              break ;
                          }
                              //iLinesPerPage 为打印机每页可打印行数
                         for ( iLine = 0 ; iLine < iLinesPerPage ; iLine ++)
                         {
                              // 计算当前应该读取编辑控件中的第几行
                              iLineNum = iLinesPerPage * iPage + iLine ;
                             
                               // 如果读取超过了编辑控件中的文字行数 , 则跳出内层循环
                              if ( iLineNum > iTotalLines)
                                   break ;
                                   
                               /*iCharsPerLine 为打印机每行可打印字符数 .
                                该语句是为后面发送 EM_GETLINE 消息做准备 ,
                                pstrBuffer 的第一个字节需要标明该缓冲区的大小 .*/
                              *( int *) pstrBuffer = iCharsPerLine ;
                             
                              // 读取编辑控件中一行的文字 , 发送到打印机设备中
                              TextOut ( pd . hDC , 0 , yChar * iLine , pstrBuffer ,
                                       /* 向编辑控件发送 EM_GETLINE 行复制消息 ,
                                          将 iLineNum 行的文字复制到 pstrBuffer 缓冲区 ,
                                         要复制的字符数在 pstrBuffer 缓冲区的第一个字节中指明 ,
                                         返回值为行的字符长度 .*/
                                       ( int) SendMessage ( hwndEdit , EM_GETLINE ,
                                       ( WPARAM) iLineNum , ( LPARAM) pstrBuffer));
                          }
                         /* 结束当前的一页 . 如果失败,则设置标记,跳出内层循环
                           如果 放弃打印程序 的传回值是 FALSE ,则 EndPage 不会传回错误。
                           由于这个原因,在下一页开始之前,要直接测试 bUserAbort .*/
                         if ( EndPage ( pd . hDC) < 0)
                         {
                              bSuccess = FALSE ;
                              break ;
                         }
                         // 如果用户点击 取消打印,则跳出内层循环 .
                         if ( bUserAbort)
                               break ;
                    }
                    // 用户取消 或 打印指令错误 则跳出内层循环
                    if ( ! bSuccess || bUserAbort)
                         break ;
               }
               // 用户取消 或 打印指令错误 则跳出内层循环。从此跳出了所有打印循环
               if ( ! bSuccess || bUserAbort)
                    break ;
          }
     }
     else
          bSuccess = FALSE ;
    
     if ( bSuccess)   // 如果打印指令在执行时一切正常,则进行 EndDoc 呼叫 .
          EndDoc ( pd . hDC) ;
    
     if ( ! bUserAbort// 如果 AbortProc 消息检查完毕,证明打印指令执行结束
     {
           EnableWindow ( hwnd , TRUE ) ;  // 使整个父窗口有效,重新接受键盘和鼠标输入
          DestroyWindow ( hDlgPrint) ;     // 摧毁对话框
     }
    
     free ( pstrBuffer) ;
     DeleteDC ( pd . hDC) ;    // 删除打开的打印机设备句柄
 
     // 逻辑与 用户未取消打印并且打印指令未出错时,返回 TRUE
     return bSuccess && ! bUserAbort ;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值