13.2 打印图形和文字
(1)注册打印异常终止过程 SetAbortProc(hdcPrn,AbortProc);//在StartDoc前注册
(2)异常终止过程——取消打印
①调用时间:当调用EndPage之前,程序每次调用一个GDI函数时,GDI模块会把另一个记录追加到磁盘上的图元文件。当调用EndPage时(也就是把图元文件送设备驱动程序和创建临时打印文件时),GDI会频繁地调用异常终止过程。(如生成临时文件导致磁盘空间不足,会调用该过程,并传入iCode为SP_OUTOFDISK的参数)
②异常终止过程
BOOL CALLBACK AbortProc(HDC hdcPrn, int iCode) { MSG msg; while(!bUserAbort && PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { if(!hDlgPrint || !IsDialogMessage(hDlgPrint,&msg)) { TranslateMessage(&msg);//本例主窗口被Disable,收不到键盘和鼠标消息,该句可省略 DispatchMessage(&msg); } } return !bUserAbort; }
说明: A、因本例开始打印时,会出现产生自定义的“取消对话框”(非模态的),为了在GDI模块将图元文件送给设备驱动程序时,用户能随时点击“取消”按钮终止打印,得在异常终止过程中用“消息循环”(因为这时程序己进入异常中止过程的处理,要在这个过程中能处理对话框的消息,这时就必须从消息队列中获取消息,并分配出去!),如果用户点击了“取消”,则会把全局变量bUserAbort设为TRUE,从而退出打印。 B、PeekMessage查看消息,可立即返回,而不用等待消息的出现。 C、异常处理过程中当返回TRUE时,继续打印,返回FALSE时终止打印。 D、为防止用户在打印作业开始以后,又选择主菜单中的“Print”菜单项,甚至选择退 |
③打印结束不必去除异常终止过程
【获取打印机设备环境——程序】
/*---------------------------------------------------------------------------------- GETPNTDC.C ——GetPrinterDC function ----------------------------------------------------------------------------------*/ //#pragma warning(disable: 4996) //win8.1以上GetVersion己过时,加上这句关闭句 #include <windows.h> #include <VersionHelpers.h> //VS2013用来判断系统版本的头文件 HDC GetPrinterDC(void) { DWORD dwNeeded, dwReturned; HDC hdc; PRINTER_INFO_4 *pinfo4; PRINTER_INFO_5 *pinfo5; if (!IsWindowsXPOrGreater()) // if (GetVersion() && 0x80000000) //Windows98 { EnumPrinters(PRINTER_ENUM_DEFAULT, NULL, 5, NULL, 0, &dwNeeded, &dwReturned); //获取所需缓冲区的字节数和所需结构体的数量 pinfo5 = malloc(dwNeeded); //分配所需的字节数 EnumPrinters(PRINTER_ENUM_DEFAULT, NULL, 5, (PBYTE)pinfo5, dwNeeded, &dwNeeded, &dwReturned); hdc = CreateDC(NULL, pinfo5->pPrinterName, NULL, NULL); //pinfo5为打1个打印机,pinfo5+1为第2个打印机 free(pinfo5); } else { EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwNeeded, &dwReturned); pinfo4 = malloc(dwNeeded); EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, (PBYTE)pinfo4, dwNeeded, &dwNeeded, &dwReturned); hdc = CreateDC(NULL, pinfo4->pPrinterName, NULL, NULL); free(pinfo4); } return hdc; } //#pragma warning (default : 4996)
【打印程序框架】
/*------------------------------------------------------------ PRINT.C -- Common routines for Print1,Print2,and Print3 (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL PrintMyPage(HWND); extern HINSTANCE hInst; extern TCHAR szAppName[]; extern TCHAR szCaption[]; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HWND hwnd; MSG msg; WNDCLASSEX wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.cbSize = sizeof(WNDCLASSEX); wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(hInstance, szAppName); wndclass.hIconSm = LoadIcon(hInstance, szAppName); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClassEx(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); return 0; } hInst = hInstance; hwnd = CreateWindow(szAppName, // window class name szCaption, // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters 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);//各向同性 //将坐标系改为坐标系原点在客户区中心,y向上为正,x、y均为范围(-1000,1000) SetWindowExtEx(hdcPrn, 1000, 1000, NULL); SetViewportExtEx(hdcPrn, cxPage / 2, -cyPage / 2, NULL); //y轴向上为正 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) { HDC hdc; PAINTSTRUCT ps; static int cxClient, cyClient; HMENU hMenu; switch (message) { case WM_CREATE: //在系统菜单中增加"Print"菜单项,菜单ID为1; hMenu = GetSystemMenu(hwnd, FALSE); AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); AppendMenu(hMenu, 0, 1, TEXT("&Print")); //菜单ID=1; return 0; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_SYSCOMMAND: //wParam为菜单ID 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); }
【Print1程序】
/*------------------------------------------------------------------ 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"), TEXT("Print1.prn") }; 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 TRUE; }
【Print2程序】
/*------------------------------------------- PRINT2.C -- Printing with Abort Procedure (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("Print2"); TCHAR szCaption[] = TEXT("Print Program 2(Abort Procedure)"); BOOL CALLBACK AbortProc(HDC hdcPrn, int iCode) { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } return TRUE; } BOOL PrintMyPage(HWND hwnd) { static DOCINFO di = { sizeof(DOCINFO), TEXT("Print2:Printing") }; HDC hdcPrn; BOOL bSuccess = TRUE; short xPage, yPage; if (NULL == (hdcPrn = GetPrinterDC())) { return FALSE; } xPage = GetDeviceCaps(hdcPrn, HORZRES); yPage = GetDeviceCaps(hdcPrn, VERTRES); EnableWindow(hwnd, FALSE); SetAbortProc(hdcPrn, AbortProc); 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; EnableWindow(hwnd, TRUE); DeleteDC(hdcPrn); return bSuccess; }
【print3程序】
/*------------------------------------------- PRINT3.C -- Printing with Dialog Box (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("Print3"); TCHAR szCaption[] = TEXT("Print Program 3(Dialog Box)"); BOOL bUserAbort; HWND hDlgPrint; BOOL CALLBACK PrintDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: SetWindowText(hDlg, szAppName); 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; } BOOL CALLBACK AbortProc(HDC hdcPrn, int iCode) { MSG msg; //消息循环——在异常处理过程,分配“取消”对话框的消息。 while (!bUserAbort && PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (!hDlgPrint || !IsDialogMessage(hDlgPrint, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return !bUserAbort; } BOOL PrintMyPage(HWND hwnd) { static DOCINFO di = { sizeof(DOCINFO), TEXT("Print3:Printing"), TEXT("Print3.prn") }; HDC hdcPrn; BOOL bSuccess = TRUE; short xPage, yPage; if (NULL == (hdcPrn = GetPrinterDC())) { return FALSE; } xPage = GetDeviceCaps(hdcPrn, HORZRES); yPage = GetDeviceCaps(hdcPrn, VERTRES); bUserAbort = FALSE; hDlgPrint = CreateDialog(hInst, TEXT("PrintDlgBox"), hwnd, PrintDlgProc); //“取消”对话框(非模态) EnableWindow(hwnd, FALSE); SetAbortProc(hdcPrn, AbortProc); 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; if (!bUserAbort) { EnableWindow(hwnd, TRUE); DestroyWindow(hDlgPrint); } DeleteDC(hdcPrn); return bSuccess && !bUserAbort; }
13.3 增加打印功能的POPPAD程序
(1)PRINTDLG结构体
字段 |
含义 |
lStructSize |
结构大小(字节数) |
hwndOwner |
父窗口句柄(可为NULL) |
hDevMode |
包含打印机设备与环境信息的DEVMODE结构句柄。 ①如果 hDevMode 不为 NULL ,则必须分配 DEVMODE 结构可移动的内存块,并初始化它的成员。 PrintDlg 函数使用输入数据以初始化对话框中的控件。 当PrintDlg 返回时, DEVMODE 的成员显示用户的输入。 ②如果 hDevMode 输入为 NULL , PrintDlg 为 DEVMODE 结构分配内存,初始化它的成员。以指示用户的输入,并返回一个句柄,标识它。 |
hDevNames |
包含驱动器名、打印机名和输出端口名的设备名结构DEVNAMES句柄。 ①如果 hDevNames 不为 NULL ,你必须分配 DEVNAMES 结构可移动的内存块,并初始化它的成员。 PrintDlg 函数使用输入数据以初始化对话框中的控件。 当PrintDlg 返回时, DEVNAMES 成员包含由用户选择的打印机的信息。 ②当 hDevNames 成员是 NULL ,在这种情况下, PrintDlg 为 DEVNAMES 结构分配内存,初始化它的成员。 以指示用户的输入,并返回一个句柄,标识它。 ③可以使用此信息来创建一个设备上下文或信息上下文。 |
hDC |
确定DC或IC(information context),由Flags是否设置PD_RETURNDC或PC_RETURNIC标志来决定。如果没有指定标志,这个成员的值是不确定的。如果指定了这两个标志, PD_RETURNDC 优先 |
Flags |
初始化打印对话框。当对话框返回时,它将会设置这些标志,以指示用户的输入。 PD_ALLPAGES 默认的标志,选“打印所有页单”的单选按钮。 PD_PAGENUMS 和 PD_SELECTION:选中“页码”、“范围”单选按钮。 PD_COLLATE:副本逐份打印 复选框在初始时被选中。 逐份打印:如1、2、3,1、2、3 非逐份打印:如1、1、2、2、3、3 PD_RETURNDC:返回一个用户在对话框中选择的设备环境句柄。 PD_NOSELECTION:禁止选择单选按钮 |
nFromPage |
打印开始页码 |
nToPage |
打印结束页码 |
nMinPage |
开始/结束页码编辑控件的页码范围的最小值 |
nMaxPage |
开始/结束页码编辑控件的页码范围的最大值 |
nCopies |
打印的份数 |
(2)获取编辑框中某行的文本
SendMessage( hWnd, EM_GETLINE,(WPARAM) wParam,(LPARAM) lParam);
①wParam:为编辑框中的某行
②lParam:指向接收字符的缓冲区(pstrBuffer),其中发送消息时,要事先将pstrBuffer的第一个字设为要读取的字符个数。读出该行后,这个字位置会被返回的文字所覆盖。
③返回值为实际读取的字符数
(3)AbortDoc函数很少用到(注意不是EndDoc)
(4)多页打印时函数的调用顺序
【PopPad4 程序】
//PopPad4.c
/*------------------------------------------------------------ POPPAD4.C -- Popup Editor Version4 (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> #include "resource.h" #include "CommFunc.h" #define ID_EDIT 1 #define UNTITLED TEXT("(untitled)") static TCHAR szAppName[] = TEXT("PopPad4"); static HWND hDlgModeless; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HACCEL hAccel; HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, szAppName); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = szAppName; wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppName, // window class name TEXT("edit4"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); hAccel = LoadAccelerators(hInstance, szAppName); while (GetMessage(&msg, NULL, 0, 0)) { if (hDlgModeless == NULL || !IsDialogMessage(hDlgModeless, &msg)) { //处理键盘加速键 if (!TranslateAccelerator(hwnd, hAccel, &msg)) { //非键盘加速键消息的处理 TranslateMessage(&msg); DispatchMessage(&msg); } } } return msg.wParam; } void OkMessage(HWND hwnd, TCHAR* szMessage, TCHAR* szTitleName) { TCHAR szBuffer[64 + MAX_PATH]; wsprintf(szBuffer, szMessage, szTitleName[0] ? szTitleName : UNTITLED); MessageBox(hwnd, szBuffer, szAppName, MB_OK | MB_ICONEXCLAMATION); } int AskConfirmation(HWND hwnd) { return MessageBox(hwnd, TEXT("Really Want to close PopPad3?"), szAppName, MB_YESNO | MB_ICONQUESTION); } short AskAboutSave(HWND hwnd, TCHAR* szTitleName) { TCHAR szBuffer[64 + MAX_PATH]; int iRet; wsprintf(szBuffer, TEXT("Save current changes in %s?"), szTitleName[0] ? szTitleName : UNTITLED); iRet = MessageBox(hwnd, szBuffer, szAppName, MB_YESNOCANCEL | MB_ICONQUESTION); if (iRet == IDYES) { if (!SendMessage(hwnd, WM_COMMAND, IDM_FILE_SAVE, 0)) //IDM_FILE_SAVE返回0为失败,1成功 iRet = IDCANCEL; } return iRet; } //设置标题 void DoCaption(HWND hwnd, TCHAR* szTitleName) { TCHAR szCaption[64 + MAX_PATH]; wsprintf(szCaption, TEXT("%s - %s"), szAppName, szTitleName[0] ? szTitleName : UNTITLED); SetWindowText(hwnd, szCaption); } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndEdit; int iSelect, iEnable; static int iOffset; static TCHAR szFileName[MAX_PATH], szTitleName[MAX_PATH]; static bNeedSave = FALSE; static UINT messageFindReplace; LPFINDREPLACE pfr; static HINSTANCE hInst; switch (message) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lParam)->hInstance; hwndEdit = CreateWindow(TEXT("edit"), NULL, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_NOHIDESEL | //ES_NOHIDESEL,编辑框在没有输入焦点时被选择的文字仍然被加亮 ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0, 0, 0, 0, hwnd, (HMENU)ID_EDIT, hInst, NULL); //限制编辑框的文本最大长度 SendMessage(hwndEdit, EM_LIMITTEXT, 32000, 0L); PopFileInitialize(hwnd); PopFontInitialize(hwndEdit); messageFindReplace = RegisterWindowMessage(FINDMSGSTRING); //申请获取“查找”、“替换”发出的特殊消息的ID DoCaption(hwnd, szTitleName); return 0; case WM_SETFOCUS: SetFocus(hwndEdit); return 0; case WM_INITMENUPOPUP: //lParam:item position and indicator switch (lParam) { case 1: //Undo菜单项 EnableMenuItem((HMENU)wParam, IDM_EDIT_UNDO, SendMessage(hwndEdit, EM_CANUNDO, 0, 0) ? MF_ENABLED : MF_GRAYED); //Paste菜单项 EnableMenuItem((HMENU)wParam, IDM_EDIT_PASTE, IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED); iSelect = SendMessage(hwndEdit, EM_GETSEL, 0, 0); if (HIWORD(iSelect) == LOWORD(iSelect)) iEnable = MF_GRAYED; else iEnable = MF_ENABLED; EnableMenuItem((HMENU)wParam, IDM_EDIT_CUT, iEnable); EnableMenuItem((HMENU)wParam, IDM_EDIT_COPY, iEnable); EnableMenuItem((HMENU)wParam, IDM_EDIT_CLEAR, iEnable); break; case 2: //“查找”菜单项 //如果非模态对话框==NULL时,激活菜单 iEnable = hDlgModeless == NULL ? MF_ENABLED : MF_GRAYED; EnableMenuItem((HMENU)wParam, IDM_SEARCH_FIND, iEnable); EnableMenuItem((HMENU)wParam, IDM_SEARCH_NEXT, iEnable); EnableMenuItem((HMENU)wParam, IDM_SEARCH_REPLACE, iEnable); break; } return 0; case WM_COMMAND: if (lParam && LOWORD(wParam == ID_EDIT)) //控件消息 { switch (HIWORD(wParam)) //控件通知码 { case EN_UPDATE: bNeedSave = TRUE; return 0; case EN_ERRSPACE: case EN_MAXTEXT: MessageBox(hwnd, TEXT("Edit Control out of space."), szAppName, MB_OK | MB_ICONSTOP); return 0; } break; }; switch (LOWORD(wParam))//加速键ID或菜单ID,这里两个ID相等 { //菜单消息 case IDM_FILE_NEW: //保存旧文件,如果保存时失败,则什么都不做,直接返回。 if (bNeedSave&& IDCANCEL == AskAboutSave(hwnd, szTitleName)) return 0; SetWindowText(hwndEdit, TEXT("\0")); //清除编辑框内容 szFileName[0] = '\0'; szTitleName[0] = '\0'; DoCaption(hwnd, szTitleName); bNeedSave = FALSE; return 0; case IDM_FILE_OPEN: //保存旧文件,如果保存时失败,则什么都不做,直接返回。 if (bNeedSave && IDCANCEL == AskAboutSave(hwnd, szTitleName)) return 0; if (PopFileOpenDlg(hwnd, szFileName, szTitleName)) { if (!PopFileRead(hwndEdit, szFileName)) { OkMessage(hwnd, TEXT("Could not read file %s!"), szTitleName); szFileName[0] = '\0'; szTitleName[0] = '\0'; } } DoCaption(hwnd, szTitleName); bNeedSave = FALSE; return 0; case IDM_FILE_SAVE: if (szFileName[0]) { if (PopFileWrite(hwndEdit, szFileName)) { bNeedSave = FALSE; return 1; } else { OkMessage(hwnd, TEXT("Could not write file %s"), szTitleName); return 0; } } //如果是UNTITLE,则弹出保存对话框 case IDM_FILE_SAVE_AS: if (PopFileSaveDlg(hwnd, szFileName, szTitleName)) { DoCaption(hwnd, szTitleName); if (PopFileWrite(hwndEdit, szTitleName)) { bNeedSave = FALSE; return 1; } else { OkMessage(hwnd, TEXT("Could not write file %s"), szTitleName); return 0; } } return 0; case IDM_FILE_PRINT: if (!PopPrntPrintFile(hInst, hwnd, hwndEdit, szTitleName)) OkMessage(hwnd, TEXT("Could not print file %s"), szTitleName); MessageBeep(0); return 0; case IDM_FORMAT_FONT: if (PopFontChooseFont(hwnd)) { PopFontSetFont(hwndEdit); } return 0; case IDM_SEARCH_FIND: //将查找将从iOffset位置开始(首获取选中文本后面的位置iOffset) SendMessage(hwndEdit, EM_GETSEL, 0, (LPARAM)&iOffset); hDlgModeless = PopFindFindDlg(hwnd); return 0; case IDM_SEARCH_NEXT: //查找将从iOffset位置开始(首获取选中文本后面的位置iOffset) SendMessage(hwndEdit, EM_GETSEL, 0, (LPARAM)&iOffset); if (PopFindValidFind()) PopFindNextText(hwndEdit, &iOffset); else hDlgModeless = PopFindFindDlg(hwnd); return 0; case IDM_SEARCH_REPLACE: //查找将从iOffset位置开始(首获取选中文本后面的位置iOffset) SendMessage(hwndEdit, EM_GETSEL, 0, (LPARAM)&iOffset); hDlgModeless = PopFindReplaceDlg(hwnd); return 0; case IDM_APP_EXIT: SendMessage(hwnd, WM_CLOSE, 0, 0); return 0; case IDM_EDIT_UNDO: SendMessage(hwndEdit, WM_UNDO, 0, 0); return 0; case IDM_EDIT_CUT: SendMessage(hwndEdit, WM_CUT, 0, 0); return 0; case IDM_EDIT_COPY: SendMessage(hwndEdit, WM_COPY, 0, 0); return 0; case IDM_EDIT_PASTE: SendMessage(hwndEdit, WM_PASTE, 0, 0); return 0; case IDM_EDIT_CLEAR: SendMessage(hwndEdit, WM_CLEAR, 0, 0); return 0; case IDM_EDIT_SELECT_ALL: SendMessage(hwndEdit, EM_SETSEL, 0, -1); return 0; case IDM_HELP_HELP: MessageBox(hwnd, TEXT("Help not yet implement!"), szAppName, MB_OK | MB_ICONEXCLAMATION); return 0; case IDM_APP_ABOUT: MessageBox(hwnd, TEXT("POPPAD3(c) Charles Petzold 1998!"), szAppName, MB_OK | MB_ICONINFORMATION); return 0; } break; case WM_SIZE: MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); return 0; case WM_CLOSE: //选择窗口关闭按钮时收到该消息 if (!bNeedSave || IDCANCEL != AskAboutSave(hwnd, szTitleName)) DestroyWindow(hwnd); return 0; case WM_QUERYENDSESSION: //系统关机或注销时收到该消息 if (!bNeedSave || IDCANCEL != AskAboutSave(hwnd, szTitleName)) return 1; return 0; case WM_DESTROY: PopFontDeinitialize(); PostQuitMessage(0); return 0; default: //处理“查找”、“替换”发送的特殊消息 if (message == messageFindReplace) { pfr = (LPFINDREPLACE)lParam; //用户点击了“取消”按钮 if (pfr->Flags & FR_DIALOGTERM) hDlgModeless = NULL; //用户点击了“查找下一个”按钮 if (pfr->Flags& FR_FINDNEXT) { if (!PopFindFindText(hwndEdit, &iOffset, pfr)) OkMessage(hwnd, TEXT("Text not Find!"), TEXT("\0")); } //用户点击了“替换全部” if (pfr->Flags & FR_REPLACEALL) while (PopFindReplaceText(hwndEdit, &iOffset, pfr)); return 0; } break; } return DefWindowProc(hwnd, message, wParam, lParam); }
//PopPrnt.c
#pragma once #include "CommFunc.h" #include <windows.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; } 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; TEXTMETRIC tm; PTSTR pstrBuffer; TCHAR szJobName[64 + MAX_PATH]; BOOL bSuccess = TRUE; int yChar, iCharsPerLine, iLinesPerPage, iTotalLines, iTotalPages; WORD iColCopy, iNoiColCopy; int iLine, iLineNum, iLineChars; //调用打话通用对话框 pd.lStructSize = sizeof(PRINTDLG); pd.hwndOwner = hwnd; pd.hDevMode = NULL; pd.hDevNames = NULL; pd.hDC = NULL; pd.Flags = PD_ALLPAGES | PD_COLLATE | PD_RETURNDC | PD_NOSELECTION; pd.nFromPage = 0; pd.nToPage = 0; pd.nMaxPage = 0; pd.nMinPage = 0; pd.nCopies = 1; pd.hInstance = NULL; pd.lCustData = 0L; pd.lpfnPrintHook = NULL; pd.lpfnSetupHook = NULL; pd.lpPrintTemplateName = NULL; pd.lpSetupTemplateName = NULL; pd.hPrintTemplate = NULL; pd.hSetupTemplate = NULL; if (!PrintDlg(&pd)) return TRUE; //获取文本总行数 iTotalLines = SendMessage(hwndEdit, EM_GETLINECOUNT, 0, 0); if (0 == iTotalLines) return TRUE; //计算文件所需的各种度量 GetTextMetrics(pd.hDC, &tm); yChar = tm.tmHeight + tm.tmExternalLeading; iCharsPerLine = GetDeviceCaps(pd.hDC, HORZRES) / tm.tmAveCharWidth; iLinesPerPage = GetDeviceCaps(pd.hDC, VERTRES) / yChar; /* 因为隐式转换的问题:如取整3.14 =3,但我们要的是向上取整,即取4, 所以(iTotalLines +iLinesPerpage)/iLinesPerPage,可以向上取值。 又因为如果iTotalLines=iLinesPerPage时,即总行数刚好一页时,此时 只须一页,但根据上式取整后得到的是2页,为了防止这种情况出现。分子-1, 即iTotalLines + iLinesPerPage - 1。 */ iTotalPages = (iTotalLines + iLinesPerPage - 1) / iLinesPerPage; //分配一个可以容纳一行文本的内存(含\0) pstrBuffer = malloc(sizeof(TCHAR)*(iCharsPerLine + 1)); //显示取消对话框——非模态 EnableWindow(hwnd, FALSE); //使主窗口不能接受键盘和鼠标消息 bUserAbort = FALSE; hDlgPrint = CreateDialog(hInst, TEXT("PrintDlgBox"), hwnd, PrintDlgProc); SetDlgItemText(hDlgPrint, IDC_FILENAME, szTitleName); SetAbortProc(pd.hDC, AbortProc); //开始打印作业 GetWindowText(hwnd, szJobName, sizeof(szJobName)); di.lpszDocName = szJobName;//在打印作业队列中显示的名称 if (StartDoc(pd.hDC, &di) > 0) { //逐份打印时, 则循环打印实际设置的份数, //否则循环一次, 内层循环中先将当前页打印出需要的份数。 for (iColCopy = 0; iColCopy < (WORD)(pd.Flags & PD_COLLATE ? pd.nCopies : 1); iColCopy++) { //从第1页,打印到最后一项 for (int iPage = 0; iPage < iTotalPages; iPage++) { //如果是逐份打印,则打印当前页1份,否则打印实际设置的份数。 for (iNoiColCopy = 0; 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; //将每行的字符个数存到pstrBuffer的第一个字中。这是EM_GETLINE的要求,要求在 //SendMessage中指定要读取的行数(wParam),和该行要读取的字符数(存于lParam所指 //向的缓冲区的第一个字中)。 *(int*)pstrBuffer = iCharsPerLine; //读取控件第iLineNum的文字 iLineChars = (int)SendMessage(hwndEdit, EM_GETLINE, (WPARAM)iLineNum, (LPARAM)pstrBuffer); TextOut(pd.hDC, 0, yChar*iLine, pstrBuffer, iLineChars); } //结束当前的一页,如果用户“取消”打印 ,则 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(pd.hDC); } //如果用户没有取消,至此,打印完全正确 if (!bUserAbort) { EnableWindow(hwnd, TRUE); DestroyWindow(hDlgPrint); } free(pstrBuffer); DeleteDC(pd.hDC); //删除打开的打印机设备句柄 return bSuccess && !bUserAbort; }
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 PopPad.rc 使用 // #define IDC_FILENAME 1000 #define IDM_FILE_NEW 40001 #define IDM_FILE_OPEN 40002 #define IDM_FILE_SAVE 40003 #define IDM_FILE_SAVE_AS 40004 #define IDM_FILE_PRINT 40005 #define IDM_APP_EXIT 40006 #define IDM_EDIT_UNDO 40007 #define IDM_EDIT_CUT 40008 #define IDM_EDIT_COPY 40009 #define IDM_EDIT_PASTE 40010 #define IDM_EDIT_CLEAR 40011 #define IDM_EDIT_SELECT_ALL 40012 #define IDM_HELP_HELP 40013 #define IDM_APP_ABOUT 40014 #define IDM_SEARCH_FIND 40015 #define IDM_SEARCH_NEXT 40016 #define IDM_SEARCH_REPLACE 40017 #define IDM_FORMAT_FONT 40018 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 106 #define _APS_NEXT_COMMAND_VALUE 40051 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
//PopPad4.rc
// Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // 中文(简体,中国) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Menu // POPPAD4 MENU BEGIN POPUP "&File" BEGIN MENUITEM "&New", IDM_FILE_NEW MENUITEM "&Open", IDM_FILE_OPEN MENUITEM "&Save", IDM_FILE_SAVE MENUITEM "Save &As...", IDM_FILE_SAVE_AS MENUITEM SEPARATOR MENUITEM "&Print", IDM_FILE_PRINT MENUITEM SEPARATOR MENUITEM "E&xit", IDM_APP_EXIT END POPUP "&Edit" BEGIN MENUITEM "&Undo\tCtrl+Z", IDM_EDIT_UNDO MENUITEM SEPARATOR MENUITEM "Cu&t\tCtrl+X", IDM_EDIT_CUT MENUITEM "&Copy\tCtrl+C", IDM_EDIT_COPY MENUITEM "&Paste\tCtrl+V", IDM_EDIT_PASTE MENUITEM "De&lete\tDel", IDM_EDIT_CLEAR MENUITEM SEPARATOR MENUITEM "&Select All", IDM_EDIT_SELECT_ALL END POPUP "&Search" BEGIN MENUITEM "&Find...\tCtrl+F", IDM_SEARCH_FIND MENUITEM "Find &Next\tF3", IDM_SEARCH_NEXT MENUITEM "&Replace...\tCtrl+R", IDM_SEARCH_REPLACE END POPUP "F&ormat" BEGIN MENUITEM "&Font...", IDM_FORMAT_FONT END POPUP "&Help" BEGIN MENUITEM "&Help...", IDM_HELP_HELP MENUITEM "&About PopPad...", IDM_APP_ABOUT END END ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. POPPAD ICON "POPPAD.ICO" ///////////////////////////////////////////////////////////////////////////// // // Accelerator // POPPAD ACCELERATORS BEGIN VK_DELETE, IDM_EDIT_CLEAR, VIRTKEY, NOINVERT VK_INSERT, IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT VK_DELETE, IDM_EDIT_CUT, VIRTKEY, SHIFT, NOINVERT VK_INSERT, IDM_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT VK_BACK, IDM_EDIT_UNDO, VIRTKEY, ALT, NOINVERT VK_F1, IDM_HELP_HELP, VIRTKEY, NOINVERT "^C", IDM_EDIT_COPY, ASCII, NOINVERT "^V", IDM_EDIT_PASTE, ASCII, NOINVERT "^X", IDM_EDIT_CUT, ASCII, NOINVERT "^Z", IDM_EDIT_UNDO, ASCII, NOINVERT END ///////////////////////////////////////////////////////////////////////////// // // Dialog // PRINTDLGBOX DIALOGEX 0, 0, 161, 107 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PopPad" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN PUSHBUTTON "Cancel", IDCANCEL, 54, 81, 50, 14 CTEXT "Sending", IDC_STATIC, 63, 12, 26, 8 CTEXT "", IDC_FILENAME, 11, 33, 139, 8 CTEXT "to print spooler.", IDC_STATIC, 54, 57, 52, 8 END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN "PRINTDLGBOX", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 154 TOPMARGIN, 7 BOTTOMMARGIN, 100 END END #endif // APSTUDIO_INVOKED #endif // 中文(简体,中国) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
//CommFunc.h
#pragma once #include<windows.h> //Function in POPFILE.C void PopFileInitialize(HWND); BOOL PopFileOpenDlg(HWND, PTSTR, PTSTR); BOOL PopFileSaveDlg(HWND, PTSTR, PTSTR); BOOL PopFileRead(HWND, PTSTR); BOOL PopFileWrite(HWND, PTSTR); //Function in POPFIND.C HWND PopFindFindDlg(HWND); HWND PopFindReplaceDlg(HWND); BOOL PopFindFindText(HWND, int*, LPFINDREPLACE); BOOL PopFindNextText(HWND, int*); BOOL PopFindReplaceText(HWND, int*, LPFINDREPLACE); BOOL PopFindValidFind(void); //Functions in POPFONT.C void PopFontInitialize(HWND); BOOL PopFontChooseFont(HWND); void PopFontSetFont(HWND); void PopFontDeinitialize(void); //Functions in POPPRINT.C BOOL PopPrntPrintFile(HINSTANCE, HWND, HWND, PTSTR);
//PopFile.C、PopFind.C、PopFont.C等文件见第11章 PopPad3程序