/*----------------------------------------------
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 ;
}
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 ;
}