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

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

我试图显示给定打印机的自定义打印机属性。 我需要这个作为我想写的自定义PrintDialog的一部分。

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

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

(关于上面的页面:我试图改变BartJoy建议的代码(在页面上)但是没有修复它)

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

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

  • 当您的应用程序启动时:
    • 你有没有在分配之前查询打印机驱动程序的DEVMODE结构的正确大小?
    • 你有没有要求设备驱动程序在分配后用默认设置初始化DEVMODE缓冲区?
  • 当您的应用程序弹出打印机对话框时:
    • 你有没有将fMode参数中的DM_IN_BUFFERDM_OUT_BUFFER标志(除了DM_IN_PROMPTfModeDocumentProperties
    • 你有没有将pDevModeInputpDevModeOutput指向你在应用程序启动时初始化的DEVMODE缓冲区?
    • 是在调用DocumentProperties(... DM_IN_PROMPT ...)之前正确设置的DEVMODE缓冲区中的dmFieldsDocumentProperties(... DM_IN_PROMPT ...)
    • 你是否在对DocumentProperties(... DM_IN_PROMPT ...)调用之间保留了DEVMODE缓冲区的内容?

看到:

  • 如何使用DocumentProperties()函数修改打印机设置
  • 使用“文档属性”对话框
  • DocumentProperties函数

即使答案最终成功解决问题,我认为以下内容为原始问题提供了更好的答案,

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

如果你定位x86编译并从x64机器运行,那么来自Jeff Roe的代码将不起作用:在分配devModeDataDocumentPropreties将始终失败并返回sizeNeeded为-1,其LastError代码为13。

要解决此问题,请确保将目标设为AnyCPU,或者只是将对DocumentPropreties的调用更改为以下内容:

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

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

参考:

  • DocumentProperties上的PInvoke.Net页面

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

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