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

会有一股神秘感。 提交于 2019-12-03 13:36:56

问题


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


回答1:


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);
}



回答2:


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


来源:https://stackoverflow.com/questions/727328/how-can-i-invoke-the-dialog-to-set-printer-options-manually

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!