The NPC three students, self-study Windows program design has two or three months, I was to see the Fish C Studio launched the introduction of Windows programming video, this video collection is not particularly many, currently only the previous nine chapters of the video content, but the Little turtle teacher explain the book content is very detailed, nuanced, Can let us learn a lot of knowledge. The environment in which I develop Win32 is VS2013.
First, the printer working mechanism
When you use a printer in Windows, you actually start a complex interactive process between a series of modules, including the GDI32 module, the printer device driver module, the Windows Spooler, and other modules.
The application wants to start using the printer, first calling the CreateDC function to get the printer device environment handle, which must need to know the printer device name, so you need to wait for the EnumPrinters function to get the printer device name. Note that when the CREATEDC function is called, the corresponding printer device driver for the parameter is loaded into memory. The application then calls the StartDoc function to start a new document, which is processed by the GDI module, and the GDI module calls the control function that was just called into the in-memory printer device driver, telling the device driver to prepare for printing. Then call the StartPage function to start a new page, end this page with the EndPage function, and notice that the GDI function is started drawing on the page between the StartPage and EndPage functions, and the GDI module first stores the GDI drawing functions on the metafile on the hard disk. Well, after calling the EndPage function to end this page, then the real print work begins, and the printer device driver converts the metafile on the hard disk into the output for the printer, specifically how we don't care about the conversion. Then, these converted printer output will be stored in the GDI module to another temporary file, so far, all the work on this page has been completed, then the next page of printing, how to tell the print spooler to print a new page? The GDI module uses inter-process calls to tell the spooler that the new job is ready, that the application should process the next page, loop over and over again ... Once all the pages have been printed, you can call the EndDoc function to indicate that the print job is complete.
Second, get the printer device environment handle
We know that to use a printer, you must first get the printer device environment handle, generally by calling the CreateDC function to get the printer device environment handle, but the problem is that parameter 2 of the function requires the name of the printer device to be specified. How do I get the name of the printer device? As we all know, a computer can connect multiple printers at the same time, regardless of how many printers are connected, the default printer is only one, the default printer is the user's most recently selected printer. So, we can get the name of the default printer device, get the name of the default printer by calling the EnumPrinters function, and then use that name as a parameter to the CREATEDC function. The following is a complete code example for getting a printer device environment handle:
HDC GetPrinterDC (void) {DWORD dwneeded, dwreturned; HDC hdc; Printer_info_4 * PINFO4; Printer_info_5 * PINFO5; if (GetVersion () & 0x80000000)//Windows 98 {//The function is called for the first time to get the desired structure size enumprinters (printer_enum_d Efault, NULL, 5, NULL, 0, &dwneeded, &dwreturned); Pinfo5 = (Printer_info_5 *) malloc (dwneeded);//The function is called the second time to actually populate the structure enumprinters (Printer_enum_default, NULL, 5, (pbyte) Pinfo5, dwneeded, &dwneeded, &dwreturned);//Will get the pPrinterName member of the struct as the parameter of the CREATEDC function hdc = CreateDC (NULL, pinfo5->pprintername, NULL, NULL); Free (PINFO5); } else//Windows NT {//under the same enumprinters (Printer_enum_local, NULL, 4, NULL, 0, &dwneeded, &dwreturned); Pinfo4 = (Printer_info_4 *) malloc (dwneeded); EnumPrinters (Printer_enum_local, NULL, 4, (pbyte) Pinfo4, dwneeded, &dwneeded, &dwreturned); HDC = CREATEDC (null, pinfo4->pprintername, NULL, NULL); Free (PINFO4); }//Return to the printer device environment handle return HDC;}
Third, print graphics and text
How do we use it when we have a hard time getting a handle to the printer device environment in the code above? No hurry, let's put it first. We'll start by creating an application window that shows what we're going to print in the client area of the window (GDI draw function calls), and a menu item that adds a print function to the System menu, and the print function is performed when the user taps the Print menu item. Let's put the code example of the application window first:
#include <windows.h>lresult CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); BOOL printmypage (HWND); extern hinstance hInst;//This is a global variable that declares another file extern TCHAR szappname[];//Here is a global variable that declares another file extern Tcha R szcaption[];//Here is a global variable declaring another file int WINAPI WinMain (hinstance hinstance, hinstance hprevinstance, PS TR szcmdline, int icmdshow) {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, idi_application); Wndclass.hcursor = LoadCursor (NULL, Idc_arrow); Wndclass.hbrbackground = (hbrush) getstockobject (White_brush); Wndclass.lpszmenuname = NULL; Wndclass.lpszclassname = Szappname; if (! RegisterClass (&wndclass)) {MessageBox (NULL, TEXT ("This program requires Windows NT! "), Szappname, Mb_iconerror); return 0; } hInst = hinstance; hwnd = CreateWindow (Szappname, Szcaption, Ws_overlappedwindow, Cw_usede FAULT, Cw_usedefault, Cw_usedefault, cw_usedefault, NULL, NULL, HINSTANC E, NULL); ShowWindow (hwnd, icmdshow); UpdateWindow (HWND); while (GetMessage (&msg, NULL, 0, 0)) {translatemessage (&msg); DispatchMessage (&MSG); } return Msg.wparam;} In the print page or customer area (why is there also drawing in the customer area?) You'll know later. Draw graphics and text void Pagegdicalls (HDC hdcprn, int cxpage, int cypage) {static TCHAR sztextstr[] = text ("Hello, Printe R! "); Rectangle (hdcprn, 0, 0, cxpage, cypage);//along the cxpage width, the cypage height of the printed page to draw the rectangle//Draw diagonally on the printed page movetoex (hdcprn, 0, 0, NU LL); LineTo (HDCPRN, Cxpage, cypage); Movetoex (hdcprn, cxpage, 0, NULL); LineTo (hdcprn, 0, cypage); Save current device environment, since the mapping pattern needs to be changed, the ellipse is drawn and the text SaveDC (HDCPRN) is displayed in the center; Setmapmode (HDCPRN, mm_isotropic); Setwindowextex (HDCPRN, +, +, 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)); Restore to the original device environment, then the mapping mode just set up and so on are not effective 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://Get system menu Handle HMenu = GetSystemMenu (hwnd, FALSE); Add a Print menu item to the System menu 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://When the user clicks on the Print menu item, the Printmypage function is executed to print, and the Printmypage function return value is to determine if the print is successful, and if it fails, an error dialog box pops up if (wPa Ram = = 1) {if (! Printmypage (HWND)) MessageBox (hwnd, TEXT ("Could not print page!"), SZ AppName, MB_OK | Mb_iconexclamation); return 0; } break; Case WM_PAINT:HDC = BeginPaint (hwnd, &PS); Look, we all know that when the window is generated, the entire client area is invalid, then a WM_PAINT message is emitted, then the Pagegdicalls function is called, and the content that needs to be printed is plotted in the client area pagegdicalls (HDC, Cxclient, Cyclient); EndPaint (hwnd, &PS); return 0; Case Wm_close:if (IDOK = = MessageBox (hwnd), TEXT ("Do you want to exit? "), TEXT (" dialog box "), Mb_okcancel | Mb_defbutton1 | mb_iconquestion)) {DestroyWindow (HWND); } else {return 0; } Case Wm_destroy:postquitmEssage (0); return 0; } return DefWindowProc (HWND, message, WParam, LParam);}
Iv. implementation of the printing function (i.e. the implementation of the Printmypage function)
Here, we have done most of the functions, the last function of the printing function, that is, the Printmypage function.
Put the code up first, and then analyze it:
#include <windows.h>hdc getprinterdc (void); void Pagegdicalls (HDC, int, int); HINSTANCE HInst; TCHAR szappname[] = text ("Print1");//define global variables, have references in previous file TCHAR szcaption[] = text ("Print program 1");//define global variables in the previous File has a reference to the BOOL Printmypage (HWND hwnd) {//DOCINFO structure, the first field indicates the size of the structure, and the second field is a string static value of text ("Print1:printing") Docinf O di = {sizeof (docinfo), TEXT ("print1:printing") of the string}; BOOL bsuccess = TRUE; HDC hdcprn; int xPage, ypage;//The length and width of the printing paper if (NULL = = (Hdcprn = GETPRINTERDC ()))//Get printer device environment return FALSE; XPage = GetDeviceCaps (hdcprn, horzres); Ypage = GetDeviceCaps (hdcprn, vertres); /* Only the STARTDOC, StartPage, endpage functions are successful, that is, the return value is greater than 0 o'clock to be able to invoke the EndDoc end document */if (StartDoc (HDCPRN, &di) > 0)//Start new Document {if (StartPage (HDCPRN) > 0)//Start new Page {//gdi Draw command, GDI module stores GDI drawing command in hard The metafile on the disk pagegdicalls (HDCPRN, XPage, Ypage); if (EndPage (HDCPRN) > 0)//After calling the EndPage function, the printer device program converts the metafile to printout, and finally stores the printout as another temporary file EndDoc (HDCPRN);//Print End else bsuccess = FALSE; }} else bsuccess = FALSE; DeleteDC (HDCPRN); return bsuccess;}
V. Canceling printing with an abnormal termination process
Well, so far, the whole function has been basically realized. There is a problem, if a document is very large, the user wants to print a page, but accidentally press the wrong way, to print hundreds of pages, then how to stop printing? Therefore, when the application is still printing, the program should provide a convenient way for the user to cancel the print job. So, we need to modify the code of the print function file. If you need to cancel a print job, call an "abort process", which is a function. The programmer can pass the address of this function as an argument to the SetAbortProc function (in fact, the process is to register an "abnormal termination process"), whenever you print, call the EndPage function, will call the "abnormal termination process" in advance to determine whether to continue printing. OK, here's the code first.
#include <windows.h>hdc getprinterdc (void); In GETPRNDC. Cvoid pagegdicalls (HDC, int, int); In PRINT. Chinstance HInst; TCHAR szappname[] = TEXT ("Print2"); TCHAR szcaption[] = TEXT ("Print program 2 (Abort Procedure)");//add content, the definition of an abort procedure function, that is, the function that executes when the EndPage function is called bool CALLBACK A Bortproc (HDC hdcprn, int iCode)//Parameter 1 is the printer device environment handle, if everything is OK, parameter 2 is 0, if the GDI module generates a temporary print output file resulting in insufficient disk space, the parameter 2 is sp_outofdisk{msg msg; Look, it's like a message loop. Yes, this is the message loop, but the function that gets the message is the Peedmessage function, and we all know that if the message queue has a message waiting to be processed, it returns true, and False if there is no message. We can notice that regardless of how the function is handled, and finally always returns true, indicating that the print job can continue, then it does not seem to achieve our expected effect (according to the user's actions, the manual cancellation of printing), we will continue to refine, add a Print dialog box to implement user interaction with the program. 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")}; BOOL bsuccess = TRUE; HDC hdcprn; Short XPage, Ypage ; if (NULL = = (Hdcprn = GETPRINTERDC ())) return FALSE; XPage = GetDeviceCaps (hdcprn, horzres); Ypage = GetDeviceCaps (hdcprn, vertres); Prevents Windows from receiving mouse and keyboard messages, avoiding duplicate printing of 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; Enable window to receive mouse keyboard message EnableWindow (hwnd, TRUE); DeleteDC (HDCPRN); return bsuccess;}
VI. Add a Print dialog box (to enable user interaction with the program)
We know that there is a problem with the improvement of the previous code, first it does not directly show whether it is printed and when it ends, only when you move the program with the mouse and discover that the program is not responding, you are sure that it is still working on the Printmypage routine, which is still in the process of printing. We can provide a non-modal dialog box, as well as a maintenance dialog process. When the user clicks the Cancel button on the dialog, the user wants to cancel printing, so the program terminates the print operation. This dialog box is often referred to as the "terminating dialog", which is often referred to as the "Terminate dialog process". Now, put on the improved code:
#include <windows.h>hdc getprinterdc (void); In GETPRNDC. Cvoid pagegdicalls (HDC, int, int); In PRINT. Chinstance HInst; TCHAR szappname[] = TEXT ("Print3"); TCHAR szcaption[] = TEXT ("Print program 3 (Dialog box)"); BOOL Buserabort; HWND hdlgprint;//Print dialog box handler bool CALLBACK Printdlgproc (HWND hdlg, UINT message, WPARAM WPARAM , LPARAM LPARAM) {switch (message) {case WM_INITDIALOG://Set Window caption SetWindowText (hdlg, Szapp Name); Deactivate the System menu close option EnableMenuItem (GetSystemMenu (hdlg, FALSE), Sc_close, mf_grayed); return TRUE; Case WM_COMMAND://Press the Cancel button after//global variable, TRUE to identify the Cancel button press Buserabort = TRUE; EnableWindow (GetParent (hdlg), TRUE); Start the main window DestroyWindow (HDLG); Close dialog box Hdlgprint = NULL; Set to NULL to prevent calling IsDialogMessage return TRUE in the message loop; } return FALSE;} Discard handler, used to stop printing bool CALLBACK Abortproc (HDC HDCPRN, int iCode) {MSG msg; while (!buserabort && peekmessage (&msg, NULL, 0, 0, pm_remove)) {//IsDialogMessage function is used to send messages to non-system modal dialog box if (!hdlgprint | |! IsDialogMessage (Hdlgprint, &msg)) {translatemessage (&msg); DispatchMessage (&MSG); }}//Return true identity to continue printing return!buserabort;} BOOL Printmypage (HWND hwnd) {static DocInfo di = {sizeof (docinfo), TEXT ("print3:printing")}; BOOL bsuccess = TRUE; HDC hdcprn; int XPage, ypage; if (NULL = = (Hdcprn = GETPRINTERDC ())) return FALSE; XPage = GetDeviceCaps (hdcprn, horzres); Ypage = GetDeviceCaps (hdcprn, vertres); EnableWindow (hwnd, FALSE); First set the user cancel state to false buserabort = false; Set Popup callback function Hdlgprint = Createdialog (HInst, TEXT ("Printdlgbox"), hwnd, PRINTDLGPROC) ; Set the Discard handler callback function 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) {//If the user does not cancel printing, re-enable the main window and clear the Print dialog box EnableWindow (hwnd, TRUE); DestroyWindow (Hdlgprint); } DeleteDC (HDCPRN); Buserabort can tell you whether the user has terminated the print job//bsuccess will tell you if there is a failure return bsuccess &&!buserabort;}
Windows Programming Core summary (printer -2018.5.5)