如何显示打印机属性/首选项对话框并保存更改?
编辑:我的错!我希望将更改写回默认打印机设置,而实际上只有 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您以 x86 编译为目标并从 x64 机器运行,Jeff Roe 的代码将不起作用:分配
devModeData
时,DocumentPropreties
将始终失败并返回sizeNeeded
为 -1,LastError
代码为 13。要解决该问题,请确保您以 AnyCPU 为目标,或者只是将调用更改为
DocumentPropreties< /code> 到以下内容:
使用
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 asizeNeeded
of -1, with aLastError
code 13.To solve the problem, either make sure you target AnyCPU or just change the call to
DocumentPropreties
to the following: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:
尽管答案最终进入了问题,但我认为以下内容为原始问题提供了更好的答案,
(1)因为如果用户取消,它显然不会修改传入的 PrinterSettings。
(2) 因为它返回一个 DialogResult,调用者可能会对它感兴趣。
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.
DEVMODE
结构之前,您是否已向打印机驱动程序查询过该结构的正确大小?DEVMODE
缓冲区后,您是否要求设备驱动程序使用默认设置来初始化它?fMode
参数中的DM_IN_BUFFER
和DM_OUT_BUFFER
标志(以及DM_IN_PROMPT
)设置为文档属性
?pDevModeInput
和pDevModeOutput
都指向您在应用程序启动时初始化的DEVMODE
缓冲区?DocumentProperties(... DM_IN_PROMPT ...)
之前,DEVMODE
缓冲区中的dmFields
位是否已正确设置李>DocumentProperties(... DM_IN_PROMPT ...)
之间保留DEVMODE
缓冲区的内容?请参阅:
DEVMODE
structure before allocating it?DEVMODE
buffer with the default settings after you have allocated it?DM_IN_BUFFER
andDM_OUT_BUFFER
flags (in addition toDM_IN_PROMPT
) in thefMode
parameter toDocumentProperties
?pDevModeInput
andpDevModeOutput
to theDEVMODE
buffer you initialized at application startup?dmFields
bits in theDEVMODE
buffer properly set prior to your callingDocumentProperties(... DM_IN_PROMPT ...)
DEVMODE
buffer in between calls toDocumentProperties(... DM_IN_PROMPT ...)
?See:
另外,如果您想使用 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