How can I invoke the dialog to set printer options manually?

5.6k Views Asked by At

I'm using WPF and need to let users set some print related options like printer and printer properties (e.g. papertray, landscape/portrait, duplex, etc). I'm aware of the PrintDialog class to get a PrintQueue and PrintTicket object. However I need to create I custom solution and can not show the PrintDialog. I manage to get the available PrintQueue objects and let users select a printer. I'm struggling with the printer properties. My question is: how can I show the dialog in which a user can set the printer properties for the selected PrintQueue (the dialog that is shown when a user clicks on the Properties button in the WPF PrintDialog).

2

There are 2 best solutions below

0
On

The following code was found here (minus the Window_Loaded event). I tested it and it seems to work like a charm. Obviously you'll have to set the printer name in the PrinterSettings object before displaying the dialog.

Hope this works for you:

[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 Int32 DM_OUT_BUFFER = 14;

public void OpenPrinterPropertiesDialog(PrinterSettings printerSettings, System.IntPtr pHandle) {
    IntPtr hDevMode = printerSettings.GetHdevmode();
    IntPtr pDevMode = GlobalLock(hDevMode);
    Int32 fMode = 0;
    int sizeNeeded = DocumentProperties(pHandle, IntPtr.Zero, printerSettings.PrinterName, pDevMode, pDevMode, fMode);
    IntPtr devModeData = Marshal.AllocHGlobal(sizeNeeded);

    fMode = DM_OUT_BUFFER;

    DocumentProperties(pHandle, IntPtr.Zero, printerSettings.PrinterName, devModeData, pDevMode, fMode);
    GlobalUnlock(hDevMode);
    printerSettings.SetHdevmode(devModeData);
    printerSettings.DefaultPageSettings.SetHdevmode(devModeData);
    GlobalFree(hDevMode);
    Marshal.FreeHGlobal(devModeData);
}

private void Window_Loaded(object sender, RoutedEventArgs e) {
    OpenPrinterPropertiesDialog(new PrinterSettings(), new WindowInteropHelper(this).Handle);
}
0
On

If you target x86 compilation and run from a x64 machine, the code from Pwninstein will not work: when allocating devModeData, DocumentPropreties will always fail and returns a sizeNeeded of -1, with a LastError code 13.

To solve the problem, either make sure you target AnyCPU or just 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 a proper pointer to a DevMode structure looks wrong, but that first call to DocumentProperties does not attempt to modify the memory at that position. The only data returned by the call is the memory size needed to store the device mode data that represents the internal parameters of the print driver.

Reference: