[SOLVED] Setting printer properties in PrintDialog lazarus / 31-LCL ---------------------------------------------------------------------- RayoGlauco · 2018-05-31 10:38 Hi, I want to change some printer settings before printing. (By default, my printer prints only one side and color, but I want to print double-sided and grayscale). I am using a TPrintDialog. When it is displayed, I use the properties button to access the printer properties dialog box that I attached below. Then, I modify the desired options, I accept and send my document to the printer, but the document is printed in color and on one side only, anyway. Is there something that I am forgetting? Does this dialog work correctly? This is the code (very simple): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   with PrintDialog1 do  begin    Copies := 1;    MinPage := 1;    MaxPage := doc.PageCount;    PrintRange := prAllPages;    if Execute then    begin       // Send data to Printer    end;  end;                     Edit: this test was made on Windows 8. I will test on Windows 10 and Linux, too. ---------------------------------------------------------------------- jamie · 2018-05-31 11:44 Maybe you need to refresh the printer object afterwards ? Printer.Refresh. ---------------------------------------------------------------------- RayoGlauco · 2018-05-31 12:16 No way. Same result after adding "Printer.Refresh". It seems that the changes I make in the settings are lost/forgotten after closing the TPrintDialog. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   with PrintDialog1 do  begin    Copies := 1;    MinPage := 1;    MaxPage := doc.PageCount;    PrintRange := prAllPages;    if Execute then    begin      Printer.Refresh;       // Send data to Printer    end;  end; ---------------------------------------------------------------------- wp · 2018-05-31 13:13 IIRC the printer dialogs are only provided for the user to change parameters. The settings, however, are not transferred to the printer, nor are they stored anywhere automatically. It is your responsibility to apply the settings yourself. Example: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button1Click(Sender: TObject);begin  PrintDialog1.Copies := Printer.Copies;  if PrintDialog1.Execute then begin    Printer.Copies := PrintDialog1.Copies;  end;end; ---------------------------------------------------------------------- RayoGlauco · 2018-05-31 13:35 Hello, wp Quote from: wp on May 31, 2018, 03:13:36 pmIIRC the printer dialogs are only provided for the user to change parameters. The settings, however, are not transferred to the printer, nor are they stored anywhere automatically. It is your responsibility to apply the settings yourself. Example: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button1Click(Sender: TObject);begin  PrintDialog1.Copies := Printer.Copies;  if PrintDialog1.Execute then begin    Printer.Copies := PrintDialog1.Copies;  end;end; I check and use the values in PrintDialog1 (Copies, PrintRange, FromPage, ToPage, etc), but I don't know how to access the values changed in the dialog that opens when the user clicks the button labeled "settings". Can I access these values (duplex, color printing, etc.) by checking some properties of Printer? ... If these values are not transferred to the printer, where are they stored? edit: I do not know how the temporary configuration of the printer works. What happens when the user changes the configuration using the "settings" dialog offered by TPrinterDialog? Where does Lazarus save the changed parameters? Are they automatically applied to the printout or must the programmer access them to apply them manually? ---------------------------------------------------------------------- wp · 2018-05-31 14:05 Which "Settings" button? I am no expert in printing since I try to avoid print-outs as much as possible. But I suspect that modern OS now offer more capabilities than are supported by the TPrintDialog, TPrintSetupDialog and TPageSetupDialog, as well as the TPrinter class. For example execute "Print" of MS Word and you'll see a dialog which is completely different from those provided by the LCL. And I also think that these new dialogs interact strongly with the printer driver making sure that the option "Duplex" will not be offered if the printer cannot print in duplex mode. So, the only help I can give is to recommand searching the web for OS- specific programming interface to current printer features. On https://stackoverflow.com/questions/28514198/delphi-duplex-printing, for example, you find a discussion of the duplex feature (I did not read it and don't know whether the article is helpful or not). As for the properties "PrintRange, "FromPage", "ToPage" of the TPrintDialog I think that they are not related to the printer but to your application. This means you must know how many pages your document contains and store the number of the first and last page to be printed somewhere. The PrintDialog just offers for you a possibility to provide a standardized way to change these settings. Then, when the document is printed, you send only those pages to the printer which have the corresponding page numbers. The TPrinter class does not know where these pages are. ---------------------------------------------------------------------- RayoGlauco · 2018-05-31 14:13 This "settings" button. (See attached image). My doubt is: if this settings dialog were not useful, why would it be accessible? ---------------------------------------------------------------------- wp · 2018-05-31 14:18 Quote from: RayoGlauco on May 31, 2018, 04:13:36 pmThis "settings" button. I see (I should have seen it by myself...) But this button leads to a printer-specific dialog. LCL seems to support only a few of the settings provided. As I said, you must search the web for information how to access the printer-specific and OS-specific information. But maybe somebody else knows better than me here... ---------------------------------------------------------------------- RayoGlauco · 2018-05-31 14:25 Thanks, wp Quote from: wp on May 31, 2018, 04:18:47 pmBut this button leads to a printer-specific dialog. LCL seems to support only a few of the settings provided. As I said, you must search the web for information how to access the printer-specific and OS-specific information. But maybe somebody else knows better than me here... As I said, if this settings dialog is not useful, why is it accessible? The user sees it, he/she changes things and then the programmer cannot make honour to the user's wants? Can I hide this "settings" button? Edit: I tried to access PrintDialog1.Components, to hide the "settings" button, but ComponentCount is 0. ---------------------------------------------------------------------- RayoGlauco · 2018-05-31 16:12 I made tests on Windows 8 and Windows 10 with Lazarus 1.8.4; and Linux Mint with Lazarus 1.8.2. In these 3 cases, I opened the "settings" window using the button in the PrintDialog. Then, I changed the settings to print in grayscale, accepted and printed. In all cases, the document was printed in color. ---------------------------------------------------------------------- wp · 2018-05-31 17:23 As I said, the settings made in the print dialogs are not transferred to the printer. You must send a grayscale document to the printer if you do not want to print in color. The problem is "only" how to read the color/grayscale setting from the printer driver dialog... ---------------------------------------------------------------------- jamie · 2018-05-31 23:54 Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  i, j: integer;  ppix,ppiy,mmx,mmy: integer;  XForm: TXForm;  Device, Driver, Port: array[0..80] of Char;  DevMode: THandle;  pDevmode: PDeviceMode;  insert_comm: wideString;begin  with PrintDialog1 doif Execute then  begin     with printer do      begin         GetPrinter(Device, Driver, Port, DevMode);        if Devmode <> 0 then begin           // lock it to get pointer to DEVMODE record          pDevMode := GlobalLock(Devmode);        if pDevmode <> nil then        try            with pDevmode^ do begin              dmDuplex := DMDUP_VERTICAL;              dmFields := dmFields or DM_DUPLEX;          end;             BeginDoc;             ppix:=GetDeviceCaps(Handle,LOGPIXELSX);            ppiy:=GetDeviceCaps(Handle,LOGPIXELSY);            mmx:=round(ppix/25.4);            mmy:=round(ppiy/25.4);             SetGraphicsMode(Canvas.Handle, GM_ADVANCED);   Something I've found,  You can see  how to access the DeviceMode record to set the fields this is for windows of course... I've used code similar to this in the past. ---------------------------------------------------------------------- RayoGlauco · 2018-06-01 06:51 My conclusion is that if I want to allow the user print something in a multiplatform application, I must dessign my own PrintDialog, wich will not have any "fake" and useless "settings" button. ---------------------------------------------------------------------- wp · 2018-06-01 07:12 jamie, your code seems to be for Delphi, it does not compile with Lazarus (TPrinter has no method GetPrinter in Lazarus). ---------------------------------------------------------------------- RayoGlauco · 2018-06-01 07:45 I think we are discussing about a feature not yet developed. TPrinter does not have many properties you can check (copies, orientation, and papersize, mainly): http://lazarus-ccr.sourceforge.net/docs/lcl/printers/tprinter.html Am I a fool if I think TPrinter must have some other properties like "ColorMode" or "DuplexMode"? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i // Types suggestionTColorMode = (cmColor, cmGrayscale, cmBW);TDuplexMode = (dmNone, dmLongEdge, dmShortEdge); ---------------------------------------------------------------------- wp · 2018-06-01 08:44 Quote from: RayoGlauco on June 01, 2018, 09:45:58 amAm I a fool if I think TPrinter must have some other properties like "ColorMode" or "DuplexMode"? To answer your question literally: Yes, but no offense.  I think the philosophy is that TPrinter supports only the minimum features which all (or most?) printers provide, such as page orientation, or paper size. Many models, however, have more features, like duplex mode. But the print dialogs are taken from the OS, they are no Lazarus forms, Lazarus has no possibility to change them (well, some hacker methods certainly exist). In principle, access to the additional features in the print dialogs can be made via OS-specific calls, but Lazarus does not provide this in TPrinter because it must be platform-independent and nobody, so far, has taken the burden to re-write TPrinter using a widgetset technology like in the LCL. If you want the print dialogs to show only those options supported by your program you must write your own dialogs. Apply the parameters supported by TPrinter to TPrinter directly. Accomplish other features in your application - I am thinking of color/grayscale mode here: if the user wants a grayscale print-out of an image by checking the grayscale box in the print dialog, then convert the bitmap to grayscale before sending it to the printer- you now can read the grayscale option from the dialog because it is your own form, not that of the OS. If you want features which can be accomplished by hardware only (such as duplex print) you return to the main problem again: How do you know that the user's printer can operate in duplex? Since Lazarus does not support platform-independent access to all properties or the printer driver you will have a hard life in squeezing everything out of the printer in the most general way. ---------------------------------------------------------------------- RayoGlauco · 2018-06-01 09:04 Thanks, wp. Now I am more clear about what to do. I will design my own PrintDialog, including only the options that I can really handle. ---------------------------------------------------------------------- jamie · 2018-06-01 18:03 @wp: that is Delphi code however, The GetPrinter function can be adopted from the API call of GetPrinter  I do have a couple of apps in laz that uses the printer and I did do something with some API code to get things working...  I'll take a look to see what it was I did... ---------------------------------------------------------------------- jamie · 2018-06-01 19:27 Ok,  I have had a look at an app we use at work that I converted from Delphi over laz.. I use the "DocumentProperties" along with info from the Printers Unit. I do not know why "Printers" does not support the SetPrinter and GetPrinter as did/does Delphi. On top of all that, the DocumentProperties API is not defined in the windows unit but the records are? Which is most likely the reason I had to an Define of it in my app..  For now you can use the DocumentProperties function, follow the guide lines laid out via MS.  There are many examples out there to do this with Lazarus and many posted examples of using this API with Delphi which should compile. ---------------------------------------------------------------------- wp · 2018-06-01 20:12 You want to say that the DocumentProperties function (which is declared in unit WinUtilPrn) is the way to access details on the print dialogs? As usual, the MSN  page on DocumentProperties (https://msdn.microsoft.com/en- us/library/windows/desktop/dd183576(v=vs.85).aspx) is hard reading... ---------------------------------------------------------------------- jamie · 2018-06-01 22:41 The DocumentProperties can but does not need to, present a dialog to the user. Its in the parameters.  fMode parameter indicates what to do..   DM_IN_PROMPT will present a setup dialog to the user.   DM_IN_BUFFER will read the current printers DEVMODE record which contains all of the standard settings, plus duplex mode   DM_OUT_BUFFER will output this Record back to the print driver   If you set this parameter to 0 then what happens is the function will return the size of memory needed to contain a DEVMODE record.   This is needed first to make sure you have enough space because printer venders will add to the end.  So first call is 0 for this fMode.  So after the initial call to determine needed space,  you then call it again with DM_IN_BUFFER and pass it the pointer of the buffer to the function.   This will fill the DEVMODE record Personally, I think the TPrinter Class should provide a Property that you can read and write this DEVMODE and if for some reason other targets can't provide this, have it return a blank or fill in as much of it as possible. EDIT:  Forgot to mention , the pDevModeInput will point to the incoming DEVMODE and the pDevModeOutput will be another DEVMODE that is modified and gets sent to the printer when you do the DM_OUT....  so you simply move a copy of the incoming over to the out-going and make your changes to the out going.. ---------------------------------------------------------------------- jamie · 2018-06-02 00:55 Something I came up with as an example... I don't have a printer at this location where I can test the duplex mode. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button1Click(Sender: TObject);Var  pDev:pDevmodeW; // Made a boo boo bere, needed to use the (W) version..  DevSize :integer;  PrnH:Thandle;  NS :WideString;begin   NS := Printer.PrinterName+#0#0; If  OpenPrinterW(@NS[1],@PrnH,nil) Then   Begin    DevSize := DocumentPropertiesW(Handle,PrnH,@NS[1],nil,nil,0);    GetMem(pDEV,DevSize);    DocumentPropertiesW(Handle, PrnH,@NS[1],LPDEVMODEW(pDev), nil, DM_OUT_BUFFER);    If pDev^.dmFields And DM_DUPLEX <> 0 Then   //if it supports it, try a Duplex change.      Begin       pDev^.dmFields := DM_DUPLEX;       pDev^.dmDuplex := DMDUP_HORIZONTAL;     DocumentPropertiesW(Handle,Pr nH,@NS[1],LPDEVMODEW(pDEV),LPDEVMODEw(pDEV),DM_IN_BUFFER or DM_OUT_BUFFER);     //If getLastError <> 0 Then it failed!      end;  ClosePrinter(PrnH);    FreeMem(pDev);   End;end;                         This gives example of how to Read/Write the DEVMODE of the printer. P.S.  Thanks to WP for informing me of the WinUtilPRN unit, it would have saved me some work a few months ago  And of course you must include that along with the Printer unit.. EDIT2:   I used the wrong DEVMODE, it should be DEVMODEW , sorry... ---------------------------------------------------------------------- RayoGlauco · 2018-06-05 11:14 Hello, I wrote some code to get and set some properties of the printer. I get successfully the current values, but when I call DocumentPropertiesW to change a value, I get no error, but no change is done. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i // Set new valueif DocumentPropertiesW(0, hPrinter, PWideChar(PrinterNameW), pDM, pDM, DM_IN_BUFFER or DM_OUT_BUFFER) < 0 then    error_message;if getLastError <>0 then RaiseLastOSError; // Check current valueif DocumentPropertiesW(0, hPrinter, PWideChar(PrinterNameW), pDM, nil, DM_OUT_BUFFER) < 0 then    error_message; This code runs without error, but no changes are done. I checked it, and pDM has the correct value that I want to set, before and after calling DocumentPropertiesW. But when I call again DocumentPropertiesW to chek if all is ok, I get the original value, not the new one. If I check the printer at the windows control panel, the value is not changed either. Any idea of what and why is going wrong? ---------------------------------------------------------------------- jamie · 2018-06-05 21:16 try the DM_VERTICAL instead.. Also, before calling DocumentProperties after you do a SET, check the DEVMODE record to see it overwrote original record.   Its possible you may need to split into two records, first read the record, make a copy of it and change the copy, return the copy for the DM_IN_BUFFER..  Also there is a possibility your print driver is resetting back to default when close it which can then present a problem here because the PRINTER class has a handle of its own and it is not the same, usually.   These go to the print spooler. I will check later for this.. but please try two different DEVMODE records to make sure things are not getting overlapped. ---------------------------------------------------------------------- jamie · 2018-06-05 23:25 Apparently the DocumentProperties will set the dialog properties and also verifies if the property of the printer you are attempting to set is a valid one... But it does not actually send it to the driver.. This code here sends it to the driver of the printer.... Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i // need to import SetPrinter, it is not in the WinUtilPrn file ?Function SetPrint er(hPrinter:Handle;Level:DWORD;pPrinter:PByte;Command:DWord):LongBool; StdCall; External libWinSpool name 'SetPrinterA';                                        procedure TForm1.Button3Click(Sender: TObject);Var   pPrnInFo:pPRINTER_INFO_2;   pDev:PDevMode;   H:Handle;   N:WideString;   S:Integer;begin  N := Printer.PrinterName;  OpenPrinterW(@N[1],@H, nil);//Open printer;  GetPrinter(H,2,Nil,0,@S);   //Get Size needed for Info_2;  GetMem(pPrnInfo,S);  GetPrinter(H,2,PByte(pPRNInFo),S,@s);  pDev := pPRNInfo^.pDevMode;  if pDev^.dmFields and Dm_Orientation <> 0 Then   Begin    pDev^.dmOrientation := 2;   end;  SetPrinter(H,2,PByte(pPrnInFo),0);  //Need to import the function ? Hmmmmmm  freeMem(pPrnInfo);  ClosePrinter(H);end;                                      In this example I used the PDF printer to see if I could change the orientation, it succeeded...  you can change the Duplex mode using the same approach.  You may want to add some extra error checking on each function return, I did not to shorten it up but it works on my end for the basic printer. This PC has no printer on ---------------------------------------------------------------------- RayoGlauco · 2018-06-06 09:03 Thank you, jamie, Finally it seems that all is working. I paste my code, in case someone finds it useful: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit GetAndSetPrinterSettings;   // Only for Windows {$mode objfpc}{$H+} interface uses  Printers; function SetPrinterOption(prn: tPrinter; FieldToChange: longint; ValueToSet: longint): longint;function GetPrinterOption(prn: tPrinter; FieldToGet: longint): longint; implementation uses  Classes, SysUtils, Windows, WinUtilPrn; Function SetPrinter(hPrinter:Handle;Level:DWORD;pPrinter:P Byte;Command:DWord):LongBool; StdCall; External libWinSpool name 'SetPrinterA'; function GetDevModeField(pDM: PDEVMODE; Field: longint): longint;// Only for fields whose value type and range is integer >=0// Result >= 0 ==> succeeded// Result < 0  ==> failedbegin  if (pDM^.dmFields and Field)=0 then    Result := -1   // Field not supported  else  case Field of    DM_ORIENTATION: Result := pDM^.dmOrientation;    DM_PAPERSIZE: Result := pDM^.dmPaperSize;    DM_PAPERLENGTH: Result := pDM^.dmPaperLength;    DM_PAPERWIDTH: Result := pDM^.dmPaperWidth;    DM_SCALE: Result := pDM^.dmScale;    //  DM_POSITION: Result:=pDM^.dmPosition;    DM_COPIES: Result := pDM^.dmCopies;    DM_DEFAULTSOURCE: Result := pDM^.dmDefaultSource;  DM_PRINTQUALITY: Result := pDM^.dmPrintQuality;    DM_COLOR: Result := pDM^.dmColor;    DM_DUPLEX: Result := pDM^.dmDuplex;    DM_YRESOLUTION: Result := pDM^.dmYResolution;    DM_TTOPTION: Result := pDM^.dmTTOption;    DM_COLLATE: Result := pDM^.dmCollate;    //  DM_FORMNAME: Result:=pDM^.dmFormName;    DM_LOGPIXELS: Result := pDM^.dmLogPixels;    DM_ICMMETHOD: Result := pDM^.dmICMMethod;    DM_ICMINTENT: Result := pDM^.dmICMIntent;    DM_MEDIATYPE: Result := pDM^.dmMediaType;    DM_DITHERTYPE: Result := pDM^.dmDitherType;    else      Result := -1;   // Field not supported  end;end; function SetDevModeField(pDM: PDEVMODE; Field: longint; Value: longint):longint;// Only for fields whose value type and range is integer >=0  // Result >= 0 ==> change succeeded; result = previous value (maybe we want to restore it later)  // Result < 0  ==> change failedbegin  if (pDM^.dmFields and Field)=0 then    Result := -1   // Field not supported  else  begin  Result := GetDevModeField(pDM,Field);  case Field of    DM_ORIENTATION: pDM^.dmOrientation := Value;    DM_PAPERSIZE: pDM^.dmPaperSize := Value;    DM_PAPERLENGTH: pDM^.dmPaperLength := Value;    DM_PAPERWIDTH: pDM^.dmPaperWidth := Value;    DM_SCALE: pDM^.dmScale := Value;    //  DM_POSITION: pDM^.dmPosition := Value;    DM_COPIES: pDM^.dmCopies := Value;    DM_DEFAULTSOURCE: pDM^.dmDefaultSource := Value;    DM_PRINTQUALITY: pDM^.dmPrintQuality := Value;    DM_COLOR: pDM^.dmColor := Value;    DM_DUPLEX: pDM^.dmDuplex := Value;    DM_YRESOLUTION: pDM^.dmYResolution := Value;    DM_TTOPTION: pDM^.dmTTOption := Value;    DM_COLLATE: pDM^.dmCollate := Value;    //  DM_FORMNAME: pDM^.dmFormName := Value;    DM_LOGPIXELS: pDM^.dmLogPixels := Value;    DM_ICMMETHOD: pDM^.dmICMMethod := Value;    DM_ICMINTENT: pDM^.dmICMIntent := Value;    DM_MEDIATYPE: pDM^.dmMediaType := Value;    DM_DITHERTYPE: pDM^.dmDitherType := Value;    else      Result := -1;   // Field not supported  end;  end;end;  function SetPrinterOption(prn: tPrinter; FieldToChange: longint; ValueToSet: longint): longint;  // Only for fields whose value type and range is integer >=0  // Result > 0 ==> change succeeded; result = previous value (maybe we want to restore it later)  // Result < 0 ==> change failed label  exit_with_error;const  LEVEL_2 = 2;var  hPrinter: tHandle;  pPrnInFo: pPRINTER_INFO_2;  pDev:PDevMode;  datasize: integer;  PrinterNameW: WideString;begin  pPrnInfo:=nil;  PrinterNameW := prn.PrinterName;   if not OpenPrinterW(PWideChar(PrinterNameW),@hPrinter, nil) then  //Open printer;    goto exit_with_error;   // Get Size needed for Info_2; GetPrinter returns false but it works ¿?  GetPrinter(hPrinter, LEVEL_2, Nil, 0, @datasize);   GetMem(pPrnInfo, datasize);  if pPrnInfo = nil then    goto exit_with_error;   // get config data; GetPrinter returns false but it works ¿?  GetPrinter(hPrinter, LEVEL_2 ,PByte(pPRNInFo), datasize ,@datasize);   pDev := pPRNInfo^.pDevMode;   result:= SetDevModeField(pDev, FieldToChange, ValueToSet);  if result>=0 then    SetPrinter(hPrinter, LEVEL_2, PByte(pPrnInFo), 0);  // set new config data   // clean and exit  FreeMem(pPrnInfo);         // Free allocated memory  ClosePrinter(hPrinter);    // Close printer  exit;   // ----------------- ERROR ----------------------  exit_with_error:    Result := -1;  if pPrnInfo<>nil then      // Free memory if allocated    freeMem(pPrnInfo);  if (hPrinter <> 0) then    // Close printer if opened    ClosePrinter(hPrinter);end; function GetPrinterOption(prn: tPrinter; FieldToGet: longint): longint;// Only for fields whose value type and range is integer >=0// Result >= 0 ==> succeeded// Result < 0  ==> failedlabel  exit_with_error;const  LEVEL_2 = 2;var  hPrinter: tHandle;  pPrnInFo: pPRINTER_INFO_2;  pDev:PDevMode;  datasize: integer;  PrinterNameW: WideString;begin  pPrnInfo:=nil;  PrinterNameW := prn.PrinterName;   if not OpenPrinterW(PWideChar(PrinterNameW),@hPrinter, nil) then  // Open printer;    goto exit_with_error;   // Get Size needed for Info_2; GetPrinter returns false but it works ¿?  GetPrinter(hPrinter, LEVEL_2, Nil, 0, @datasize);   GetMem(pPrnInfo, datasize);  if pPrnInfo = nil then    goto exit_with_error;   // get config data; GetPrinter returns false but it works ¿?  GetPrinter(hPrinter, LEVEL_2 ,PByte(pPRNInFo), datasize ,@datasize);   pDev := pPRNInfo^.pDevMode;   result:= GetDevModeField(pDev, FieldToGet);   // clean and exit  FreeMem(pPrnInfo);         // Free allocated memory  ClosePrinter(hPrinter);    // Close printer  exit;   // ----------------- ERROR ----------------------  exit_with_error:    Result := -1;  if pPrnInfo<>nil then      // Free memory if allocated    freeMem(pPrnInfo);  if (hPrinter <> 0) then    // Close printer if opened    ClosePrinter(hPrinter);end; end.         Examples of use: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   // uses Printers, Windows, GetAndSetPrinterSettings;  ActualValue:= GetPrinterOption(Printer, DM_DUPLEX);   // get current value  // ...  PreviousValue:= SetPrinterOption(Printer, DM_COLOR, DMCOLOR_MONOCHROME);  // Save value and then change it  // print something here (output will be monochrome)  SetPrinterOption(Printer, DM_COLOR, PreviousValue);  // Restore previous value Edit: some aesthetic changes in the code. ---------------------------------------------------------------------- RayoGlauco · 2018-06-09 10:58 I wrote a unit called GetAndSetPrinterSettings, including some more code to access printer settings in Windows. I attached the unit, in case someone finds it useful. These are the functions it provides: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function GetPrinterOption(prn: tPrinter; FieldToGet: longint): longint;function SetPrinterOption(prn: tPrinter; FieldToChange: longint; ValueToSet: longint): longint;function GetPrinterSettings(prn: tPrinter; out pDM: PDevMode): boolean;function SetPrinterSettings(prn: tPrinter; pDM: PDevMode): boolean;function PrinterSettingsDialog(prn: tPrinter; out pDM: PDevMode; ApplyNow: boolean): boolean;function PrinterSettingsDialog(prn: tPrinter): boolean;function GetDevModeField(pDM: PDEVMODE; Field: longint): longint;function SetDevModeField(pDM: PDEVMODE; Field: longint; Value: longint): longint; Examples of use: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses  Printers, Windows, GetAndSetPrinterSettings;// You must add Printer4Lazarus as a required package// Not cheching for errors to keep simple the code procedure TForm1.Button1Click(Sender: TObject);// Example 1: change printer duplex and color/monochrome modevar  pDM: PDevMode;begin  // get actual settings  GetPrinterSettings(Printer, pDM);  // alternate duplex  if GetDevModeField(pDM, DM_DUPLEX) = DMDUP_SIMPLEX then    SetDevModeField(pDM, DM_DUPLEX, DMDUP_VERTICAL)  else    SetDevModeField(pDM, DM_DUPLEX, DMDUP_SIMPLEX);  // alternate color/gray  if GetDevModeField(pDM, DM_COLOR) = DMCOLOR_COLOR then    SetDevModeField(pDM, DM_COLOR, DMCOLOR_MONOCHROME)  else    SetDevModeField(pDM, DM_COLOR, DMCOLOR_COLOR);  // apply changes to the printer  SetPrinterSettings(Printer, pDM);  // free memory  FreeMem(pDM);end; procedure TForm1.Button2Click(Sender: TObject);// Example 2: show a dialog to configure printerbegin  // ask for and apply new settings  PrinterSettingsDialog(Printer);end; procedure TForm1.Button3Click(Sender: TObject);// Example 3: show a dialog to configure a printingvar  pOriginalDM: PDevMode;begin  // save original settings  GetPrinterSettings(Printer, pOriginalDM);  // ask for and apply new settings  PrinterSettingsDialog(Printer);  // ... some printing here  Printer.BeginDoc;  Printer.Canvas.Font.Size := 14;  Printer.Canvas.Font.Color := $00ff0044;  Printer.Canvas.TextOut(200, 200, 'TESTING... ');  Printer.EndDoc;  // restore original settings  SetPrinterSettings(Printer, pOriginalDM);  // free memory  FreeMem(pOriginalDM);end; I think that printing is a common task in programming, and should be easier in Lazarus. It took me many hours to find the information (only for windows). I still do not understand why Lazarus offers a print dialog with a printer settings button, if when pressing it the user can change many things, but then the programmer can not know what changes made the user. Edit: some changes in the code & examples; I attached the GetAndSetPrinterSettings unit. ---------------------------------------------------------------------- dbox · 2018-07-18 18:13 Hi Functions working great under Windows, but.. How to save &restore printer settings, when printer settings window is "non system", for example from Oki driver? I tried save to registry as binary data pDM variable returned by GetPrinterSettings, but after restore it's not working. Any hint? I little modified your function GetPrinterSettings: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function GetPrinterSettings(prn: tPrinter; out pDM: PDevMode; out dmsize: integer): boolean; Now i can read and save as binary data ALL printer settings (DevMode+vendor data), dmsize returns properly data size to read from pDM^ @RayoGlauco Thanks for your work ---------------------------------------------------------------------- RayoGlauco · 2022-04-25 10:39 Hi, maybe toooo late, but I want to say thanks to dbox. Your contribuition is very useful !