编辑:我的错!我希望在实际上只更改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);
}
答案 0 :(得分:7)
即使答案最终解决了问题,我认为以下内容为原始问题提供了更好的答案,
(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;
}
答案 1 :(得分:7)
如果您定位x86编译并从x64计算机运行,那么来自Jeff Roe的代码将无效:分配devModeData
时,DocumentPropreties
将始终失败并返回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并不会尝试修改该位置的内存。调用返回的唯一数据是存储表示打印驱动程序内部参数的设备模式数据所需的内存大小。
参考:
答案 2 :(得分:3)
DEVMODE
结构的正确大小?DEVMODE
缓冲区?DM_IN_BUFFER
参数中的DM_OUT_BUFFER
和DM_IN_PROMPT
标记(除fMode
之外)设置为DocumentProperties
?pDevModeInput
和pDevModeOutput
都指向您在应用程序启动时初始化的DEVMODE
缓冲区?dmFields
之前正确设置的DEVMODE
缓冲区中的DocumentProperties(... DM_IN_PROMPT ...)
位DEVMODE
之间保留DocumentProperties(... DM_IN_PROMPT ...)
缓冲区的内容?请参阅:
答案 3 :(得分:0)
此外,如果您想使用WPF类(PrintQueue,PrintTicket)执行此操作,此页面将指向正确的方向:
http://social.msdn.microsoft.com/Forums/en/wpf/thread/0dc695c1-578d-4da5-8f68-b2a257846c02