How to show printer properties / settings dialog and save changes?

EDITOR: My fault! I expected the changes to revert to the default printer settings if only the local instance of PrinterSettings was actually changed. - The code below works as expected

I am trying to show custom printer properties for this printer. I need this as part of the custom PrintDialog I'm trying to write.

Most of the examples that I can find on the Internet can display a dialog box, but any changes that the user can make are lost, which makes it useless.

Example: http://www.codeproject.com/KB/system/PrinterPropertiesWindow.aspx

(regarding the above page: I tried to change the code as suggested by BartJoy (on the page), but this did not fix it)

I also tried the sample and suggestions on the pinvoke.net page, but it still doesn't work:

http://www.pinvoke.net/default.aspx/winspool.documentproperties

From the above websites, I assume that the problem can only be on 64-bit Windows and / or if the printer name is longer than 32 characters.

I do not know what I should try next ... I appreciate any suggestions and comments!

EDIT: Here is what I tried:

[DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesW", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, [MarshalAs(UnmanagedType.LPWStr)] string pDeviceName, IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode); [DllImport("winspool.drv")] private static extern int OpenPrinter(string pPrinterName, out IntPtr hPrinter, IntPtr pDefault); [DllImport("winspool.drv")] private static extern int ClosePrinter(IntPtr phPrinter); [DllImport("kernel32.dll")] static extern IntPtr GlobalLock(IntPtr hMem); [DllImport("kernel32.dll")] static extern bool GlobalUnlock(IntPtr hMem); [DllImport("kernel32.dll")] static extern bool GlobalFree(IntPtr hMem); private const int DM_PROMPT = 4; private const int DM_OUT_BUFFER = 2; private const int DM_IN_BUFFER = 8; private void OpenPrinterPropertiesDialog() { var printerSettings = new System.Drawing.Printing.PrinterSettings(); var printerName = printerSettings.PrinterName; IntPtr handle; OpenPrinter(printerName, out handle, IntPtr.Zero); IntPtr hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings); IntPtr pDevMode = GlobalLock(hDevMode); int sizeNeeded = DocumentProperties(this.Handle, handle, printerName, pDevMode, pDevMode, 0); IntPtr devModeData = Marshal.AllocHGlobal(sizeNeeded); DocumentProperties(this.Handle, handle, printerName, devModeData, pDevMode, DM_IN_BUFFER | DM_PROMPT | DM_OUT_BUFFER); ClosePrinter(handle); GlobalUnlock(hDevMode); printerSettings.SetHdevmode(devModeData); printerSettings.DefaultPageSettings.SetHdevmode(devModeData); GlobalFree(hDevMode); Marshal.FreeHGlobal(devModeData); } 

I tried using the OpenPrinter and ClosePrinter methods and passing devModeData as the output parameter in the second call, as it was strange to me that the source code from pinvoke.net did not. (but I admit that I do not know what I'm doing - it's just a trial version and a mistake).

Here is the source code from pinvoke:

 private void OpenPrinterPropertiesDialog(PrinterSettings printerSettings) { IntPtr hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings); IntPtr pDevMode = GlobalLock(hDevMode); int sizeNeeded = DocumentProperties(this.Handle, IntPtr.Zero, printerSettings.PrinterName, pDevMode, pDevMode, 0); IntPtr devModeData = Marshal.AllocHGlobal(sizeNeeded); DocumentProperties(this.Handle, IntPtr.Zero, printerSettings.PrinterName, IntPtr.Zero, pDevMode, 14); GlobalUnlock(hDevMode); printerSettings.SetHdevmode(devModeData); printerSettings.DefaultPageSettings.SetHdevmode(devModeData); GlobalFree(hDevMode); Marshal.FreeHGlobal(devModeData); } 
+7
c # winapi printing pinvoke
source share
4 answers
  • when starting the application:
    • Have you requested a printer driver for the correct size of the DEVMODE structure before distributing it?
    • Did you ask the device driver to initialize the DEVMODE buffer with the default settings after you allocated it?
  • when your application appears in the printer dialog box:
    • Have you set the DM_IN_BUFFER and DM_OUT_BUFFER (in addition to DM_IN_PROMPT ) in the fMode parameter on DocumentProperties ?
    • Did you point both pDevModeInput and pDevModeOutput to the DEVMODE buffer that you initialized when you started the application?
    • represents the dmFields bit in the DEVMODE buffer correctly set before your call to DocumentProperties(... DM_IN_PROMPT ...)
    • Do you save the contents of the DEVMODE buffer between calls to DocumentProperties(... DM_IN_PROMPT ...) ?

Cm:

+3
source share

Despite the fact that the answer turned out to be in this question, I think the following gives the best answer to the original question,

(1) Because it does not explicitly change the passed PrinterSettings settings if the user cancels.

(2) Because it returns a DialogResult, which is likely to be interesting to the caller.

 [DllImport("kernel32.dll")] static extern IntPtr GlobalLock(IntPtr hMem); [DllImport("kernel32.dll")] static extern bool GlobalUnlock(IntPtr hMem); [DllImport("kernel32.dll")] static extern bool GlobalFree(IntPtr hMem); [DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesW", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, [MarshalAs(UnmanagedType.LPWStr)] string pDeviceName, IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode); private const int DM_PROMPT = 4; private const int DM_OUT_BUFFER = 2; private const int DM_IN_BUFFER = 8; private DialogResult EditPrinterSettings(PrinterSettings printerSettings) { DialogResult myReturnValue = DialogResult.Cancel; IntPtr hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings); IntPtr pDevMode = GlobalLock(hDevMode); int sizeNeeded = DocumentProperties(this.Handle, IntPtr.Zero, printerSettings.PrinterName, pDevMode, pDevMode, 0); IntPtr devModeData = Marshal.AllocHGlobal(sizeNeeded); long userChoice = DocumentProperties(this.Handle, IntPtr.Zero, printerSettings.PrinterName, devModeData, pDevMode, DM_IN_BUFFER | DM_PROMPT | DM_OUT_BUFFER); long IDOK = (long)DialogResult.OK; if (userChoice == IDOK) { myReturnValue = DialogResult.OK; printerSettings.SetHdevmode(devModeData); printerSettings.DefaultPageSettings.SetHdevmode(devModeData); } GlobalUnlock(hDevMode); GlobalFree(hDevMode); Marshal.FreeHGlobal(devModeData); return myReturnValue; } 
+7
source share

If you target x86 compilation and run it from an x64 machine, the code from Jeff Roe will not work: when devModeData distributed, DocumentPropreties will always fail and returns sizeNeeded from -1 using LastError code 13.

To solve the problem, make sure you target AnyCPU or simply change the call to DocumentPropreties to the following:

 int sizeNeeded = DocumentProperties(pHandle, IntPtr.Zero, printerSettings.PrinterName, IntPtr.Zero, // This solves it pDevMode, fMode); 

Using IntPtr.Zero instead of the correct pointer to the DevMode structure does not look right, but this first call to DocumentProperties does not try to change the memory at this position. The only data returned by the call is the amount of memory needed to store device mode data that represents the internal parameters of the print driver. A.

Link:

+7
source share

Also, if you want to do this using WPF classes (PrintQueue, PrintTicket), this page points you in the right direction:

http://social.msdn.microsoft.com/Forums/en/wpf/thread/0dc695c1-578d-4da5-8f68-b2a257846c02

0
source share

All Articles