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

标签 c# winapi printing pinvoke

编辑:我的错!我希望将更改写回默认打印机设置,而实际上只有 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);
}

最佳答案

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

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

关于c# - 如何显示打印机属性/首选项对话框并保存更改?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2437337/

相关文章:

c# - 刷新 Windows 窗体中的 DataGridView

c# - 将值从用户控件传递到主页 aspx

windows编程: grab audio without the driver supporting "Stereo Mix

C#:带有单引号的字符串的正则表达式(并通过双引号转义)

c# - 以编程方式向验证器添加属性

c++ - 无法将char数组写入Windows注册表项

c++ - 如何使用 Win32 操作现有桌面快捷方式的图标?

ios - 使用 AirPrint 打印 Pdf 会导致输出较小

r - 将向量打印到具有预定义列数的文件,无需回收

java - PDFBox:如何使用指定的打印机打印 pdf?