Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
361 views
in Technique[技术] by (71.8m points)

c# - 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);
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...