Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/259.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何显示打印机属性/首选项对话框并保存更改?_C#_Winapi_Printing_Pinvoke - Fatal编程技术网

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

C# 如何显示打印机属性/首选项对话框并保存更改?,c#,winapi,printing,pinvoke,C#,Winapi,Printing,Pinvoke,编辑:我的错!我希望更改被写回默认打印机设置,而实际上只有PrinterSettings的本地实例被更改。-以下代码似乎按预期工作 我试图显示给定打印机的自定义打印机属性。我需要这作为一个自定义打印对话框,我试图写的一部分 我可以在网上找到的大多数示例都可以管理显示对话框,但是用户可能做的任何更改都丢失了,这使得它毫无用处 例如: (关于上面的页面:我试图按照BartJoy(页面上)的建议更改代码,但没有解决问题) 我还尝试了pinvoke.net页面上的示例和建议,但仍然不起作用: 根据以

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

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

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

例如:

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

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

根据以上网站,我假设问题可能只出现在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_BUFFER
      DM_OUT_BUFFER
      标志(除了
      DM_IN_PROMPT
      )设置为
      DocumentProperties
    • 您是否将
      pDevModeInput
      pDevModeOutput
      都指向了应用程序启动时初始化的
      DEVMODE
      缓冲区
    • 调用
      DocumentProperties(…DM_in_提示符…)
    • 在调用
      DocumentProperties(…DM_in_PROMPT…
      )之间,是否保留
      DEVMODE
      缓冲区的内容
见:


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


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

(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的代码将不起作用:在分配
devModeData
时,
DocumentPropreties
将始终失败,并返回
sizeNeeded
值-1,同时返回
LastError
代码13

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

int sizeNeeded = DocumentProperties(pHandle, 
                                    IntPtr.Zero, 
                                    printerSettings.PrinterName, 
                                    IntPtr.Zero, // This solves it
                                    pDevMode, 
                                    fMode);
使用
IntPtr.Zero
而不是指向DevMode结构的正确指针看起来是错误的,但是对DocumentProperties的第一次调用不会试图修改该位置的内存。调用返回的唯一数据是存储表示打印驱动程序内部参数的设备模式数据所需的内存大小

参考:


感谢您的投入。我相信我会做这些事情。我已经更新了问题并包含了我尝试过的代码。我的错!我希望将更改写回默认打印机设置,这样当我使用新的PrinterSettings()调用相同的方法时,它将反映过去的更改。-似乎随着打印机设置的正确更新,一切都正常工作。您是如何设法将更改保存回来的?此代码确实会更改printerSettings,但这些更改不会保存为默认打印机设置:(@JeffRow我刚刚尝试了您在win8 64位计算机上提供的代码,DocumentProperties返回-1,因此当调用Marshal.AllocHGlobal(sizeNeeded)时,它抛出了一个错误“内存不足,无法继续执行程序。"这是有意义的,因为sizeNeeded是-1。虽然它处于早期阶段,但这似乎是我迄今为止找到的解决我的问题的最稳定的代码。根据我之前的评论,唯一的小变化是为了获得sizeNeeded,您需要更改API调用以使用IntPtr.Zero而不是0,即Dim sizeNeeded作为整数r=DocumentProperties(Me.Handle,IntPtr.Zero,printerSettings.PrinterName,IntPtr.Zero,pDevMode,0)。感谢分享!