Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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).

like image 722
FromTheMountain Avatar asked Apr 07 '09 19:04

FromTheMountain


2 Answers

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);
}
like image 54
Mark Carpenter Avatar answered Sep 30 '22 08:09

Mark Carpenter


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:

  • PInvoke.Net page on DocumentProperties
like image 25
Renaud Bompuis Avatar answered Sep 30 '22 10:09

Renaud Bompuis