如何显示打印机属性/首选项对话框并保存更改?

发布于 2024-08-24 14:27:40 字数 3482 浏览 8 评论 0原文

编辑:我的错!我希望将更改写回默认打印机设置,而实际上只有 PrinterSettings 的本地实例发生更改。 - 下面的代码似乎按预期工作

我试图显示给定打印机的自定义打印机属性。我需要它作为我正在尝试编写的自定义 PrintDialog 的一部分。

我可以在网上找到的大多数示例都设法显示对话框,但用户可能所做的任何更改都会丢失,这使得它毫无用处。

例子: http://www.codeproject.com/KB/system/PrinterPropertiesWindow.aspx

(关于上面的页面:我尝试按照 BartJoy(在页面上)的建议更改代码,但这并没有解决问题)

我还尝试了 pinvoke.net 页面上的示例和建议,但它仍然不起作用:

< a href="http://www.pinvoke.net/default.aspx/winspool.documentproperties" rel="noreferrer">http://www.pinvoke.net/default.aspx/winspool.documentproperties

来自上述网站我认为问题可能仅出现在 64 位 Windows 上和/或打印机名称超过 32 个字符。

我不知道下一步应该尝试什么...我感谢任何建议和评论!

编辑:这是我尝试过的:

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

我尝试使用 OpenPrinter 和 ClosePrinter 方法并在第二次调用中将 devModeData 作为输出参数传递,因为我发现来自 pinvoke.net 的原始代码没有这样做很奇怪。 (但我承认,我不知道我在做什么 - 这只是尝试和错误)。

以下是来自 pinvoke 网站的原始代码:

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

逆夏时光 2024-08-31 14:27:40

如果您以 x86 编译为目标并从 x64 机器运行,Jeff Roe 的代码将不起作用:分配 devModeData 时,DocumentPropreties 将始终失败并返回 sizeNeeded 为 -1,LastError 代码为 13。

要解决该问题,请确保您以 AnyCPU 为目标,或者只是将调用更改为 DocumentPropreties< /code> 到以下内容:

int sizeNeeded = DocumentProperties(pHandle, 
                                    IntPtr.Zero, 
                                    printerSettings.PrinterName, 
                                    IntPtr.Zero, // This solves it
                                    pDevMode, 
                                    fMode);

使用 IntPtr.Zero 而不是指向 DevMode 结构的正确指针看起来是错误的,但对 DocumentProperties 的第一次调用不会尝试修改该位置的内存。调用返回的唯一数据是存储表示打印驱动程序内部参数的设备模式数据所需的内存大小。

参考:

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:

街道布景 2024-08-31 14:27:40

尽管答案最终进入了问题,但我认为以下内容为原始问题提供了更好的答案,

(1)因为如果用户取消,它显然不会修改传入的 PrinterSettings。

(2) 因为它返回一个 DialogResult,调用者可能会对它感兴趣。

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

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;
}
南街女流氓 2024-08-31 14:27:40
  • 当您的应用程序启动时:
    • 在分配DEVMODE 结构之前,您是否已向打印机驱动程序查询过该结构的正确大小?
    • 在分配DEVMODE缓冲区后,您是否要求设备驱动程序使用默认设置来初始化它?
  • 当您的应用程序弹出打印机对话框时:
    • 您是否已将 fMode 参数中的 DM_IN_BUFFERDM_OUT_BUFFER 标志(以及 DM_IN_PROMPT)设置为文档属性
    • 您是否将 pDevModeInputpDevModeOutput 都指向您在应用程序启动时初始化的 DEVMODE 缓冲区?
    • 在调用 DocumentProperties(... DM_IN_PROMPT ...) 之前,DEVMODE 缓冲区中的 dmFields 位是否已正确设置李>
    • 您是否在调用 DocumentProperties(... DM_IN_PROMPT ...) 之间保留 DEVMODE 缓冲区的内容?

请参阅:

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

如何视而不见 2024-08-31 14:27:40

另外,如果您想使用 WPF 类(PrintQueue、PrintTicket)执行此操作,此页面将为您指明正确的方向:

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文