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

后端 未结 4 620
日久生厌
日久生厌 2020-12-09 00:23

EDIT: My fault! I expected the changes to be written back to the default printer settings when in fact only the local instance of the PrinterSettings are changed. -

相关标签:
4条回答
  • 2020-12-09 00:34
    • when your application started up:
      • have you queried the printer driver for the correct size of the DEVMODE structure before allocating it?
      • have you asked the device driver to initialize the DEVMODE buffer with the default settings after you have allocated it?
    • when your application popped up the printer dialog:
      • have you set the DM_IN_BUFFER and DM_OUT_BUFFER flags (in addition to DM_IN_PROMPT) in the fMode parameter to DocumentProperties?
      • have you pointed both pDevModeInput and pDevModeOutput to the DEVMODE buffer you initialized at application startup?
      • are the dmFields bits in the DEVMODE buffer properly set prior to your calling DocumentProperties(... DM_IN_PROMPT ...)
      • are you preserving the contents of the DEVMODE buffer in between calls to DocumentProperties(... DM_IN_PROMPT ...)?

    See:

    • How To Modify Printer Settings with the DocumentProperties() Function
    • Using the Document Properties Dialog Box
    • the DocumentProperties Function
    0 讨论(0)
  • 2020-12-09 00:37

    Even though the answer ended up working its way into the question, I think the following provides a better answer to the original question,

    (1) Because it clearly does not modify the passed-in PrinterSettings if the user cancels.

    (2) Because it returns a DialogResult, which the caller will likely be interested in.

    [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;
    }
    
    0 讨论(0)
  • 2020-12-09 00:42

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

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

    0 讨论(0)
  • 2020-12-09 00:56

    If you target x86 compilation and run from a x64 machine, the code from Jeff Roe 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
    0 讨论(0)
提交回复
热议问题