2010-03-13 228 views
7

编辑:我的错!我预料,实际上只有PrinterSettings的本地实例发生更改时,才会将更改写回默认的打印机设置。 - 下面的代码似乎按预期工作如何显示打印机属性/首选项对话框并保存更改?

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

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

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

(关于网页上面:我试图改变代码由BartJoy的建议(在页面上),但没有解决问题)

我还试图将样品和在pinvoke.net页面上的建议,但它仍然不能正常工作:

http://www.pinvoke.net/default.aspx/winspool.documentproperties

从上面的网站,我认为这个问题可能只有在64位Windows中的nd /或打印机名称是否长于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

而你怎么样设法得到chages救回来?此代码确实会更改printerSettings,但这些更改不会保存为默认打印机设置:( – Ando 2010-05-11 07:24:07

回答

3
  • 当你的应用程序启动:
    • 有你分配之前询问的DEVMODE结构的正确尺寸的打印机驱动程序它?
    • 您是否要求设备驱动程序在分配后使用默认设置初始化DEVMODE缓冲区?
  • 当你的应用程序弹出打印机的对话框:在fMode参数DocumentProperties
    • 你设置DM_IN_BUFFERDM_OUT_BUFFER标志(除DM_IN_PROMPT)?
    • 您是否已将pDevModeInputpDevModeOutput指向您在应用程序启动时初始化的DEVMODE缓冲区?
    • 都在DEVMODE缓冲的dmFields位正确设置您的通话DocumentProperties(... DM_IN_PROMPT ...)
    • 之前你保留DEVMODE缓冲区的内容在调用DocumentProperties(... DM_IN_PROMPT ...)之间?

参见:

+0

感谢您的输入。我相信我会做这些事情。我已经更新了该问题并包含了我尝试过的代码。 – 2010-03-17 00:13:37

+0

我的错!我希望将这些更改写回到默认的打印机设置,以便当我使用新的PrinterSettings()调用相同的方法时,它将反映过去的更改。 - 看起来它正在正常工作,因为printerSettings已更新正确 – 2010-03-17 00:40:32

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; 
} 
+0

@JeffRow我刚刚试过你在win8 64位机器上提供的代码,并且DocumentProperties返回-1,因此当Marshal。AllocHGlobal(sizeNeeded)被调用,它抛出一个错误“内存不足以继续执行程序”。这是有意义的,因为sizeNeeded是-1。 – Thierry 2014-03-10 15:45:16

+1

尽管它处于早期阶段,但这似乎是迄今为止我发现的最稳定的代码,可以解决我的问题。根据我以前的评论,唯一的小改变是,为了获得sizeNeeded,你需要改变API调用来使用IntPtr.Zero而不是0,即Dim sizeNeeded As Integer = DocumentProperties(Me.Handle,IntPtr.Zero ,printerSettings.PrinterName,IntPtr.Zero,pDevMode,0)。感谢分享! – Thierry 2014-03-10 16:04:49

7

如果你的目标的x86汇编和X64的机器上运行,从杰夫·罗的代码将无法正常工作:分配devModeData时,DocumentPropreties总是会失败,并返回-1 sizeNeeded,具有LastError码13

为了解决这个问题,无论是确保您指定AnyCPU或者只是改变调用DocumentPropreties到以下:

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

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

参考:

相关问题