简体   繁体   中英

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

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. - The below code seems to work as intended

I am trying to show the custom printer properties of a given printer. I need this as part of a custom PrintDialog which I am trying to write.

Most of the examples that I can find online manage to show the dialog but any changes the user might make are lost which makes it useless.

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

(regarding above page: I tried to change the code as suggested by BartJoy (on the page) but that didn't 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 might only be on 64 bit Windows and/or if a printer name is longer than 32 characters.

I don't know what I should try next... I appreciate any suggestions and comments!

EDIT: Here is what I have 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 have tried to use the OpenPrinter and ClosePrinter method and pass the devModeData as the output parameter in the second call as I found it strange that the original code from the pinvoke.net didn't do this. (but I admit, that I don't know what I am doing - this is just trial and error).

Here is the original code from the pinvoke site:

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

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:

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;
}
  • 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:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM