2008-11-12 72 views
2

我们已经收到了一些旧版打印机“设置”代码的应用程序,我们仍在使用PrintDlg。我们使用自定义模板,允许用户选择使用哪种打印机进行各种类型的打印任务(例如报告或图纸)以及方向和纸张尺寸/来源。在Vista x64上使用PrintDlg无法正常工作,在32位和XP上正常工作

它适用于XP和32位Vista,但在Vista x64上它通过CommDlgExtendedError()得到CDERR_MEMLOCKFAILURE。我试过用PRINTDLG结构中的裸机输入来运行它,但是如果参数包括PD_PRINTSETUPPD_RETURNDEFAULT,我会得到该错误。

由于打印机选择/页面设置已拆分为PageSetupDlgPrintDlgEx,因此如果不更改相当数量的代码和/或完全更改我们向用户提供打印和打印机设置的方式,则不会出现明显的轻松转换。

有没有人在64位Vista上看到过这个问题,并且你找到了解决办法?

注:
应用程序作为管理员由于其他方面的限制

回答

0

我刚刚遇到了这个问题,因为我正在向我的应用程序添加打印。我使用的是PrintDialog类,它在编译为32位应用程序时效果很好,但在64位模式下编译时甚至不会弹出。没有错误信息,没有任何东西。对ShowDialog的调用只是立即返回。 (请注意,我正在运行64位Vista。)

我试过使用PrintDlg,但是它有同样的问题。我在网上查找,发现很多人都遇到了类似的问题,但显然不是每个拥有64位Vista的人都会看到这一点。无论如何,我终于决定编写我自己版本的PrintDialog(从在线借用代码),但这有点棘手(因为一些在线代码存在缺陷),并且因为我从未在网上找到完整的示例,所以我想我会在这里发布我的解决方案。

请注意,我的版本在对话框外留下了一些内容,如“打印范围”,“副本”和“打印到文件”。这应该很容易添加,但我的应用程序不需要它们。我也无法弄清楚“Type:”字段显示的是什么,所以我也把它丢掉了。

这里的对话框是什么样子:

alt text http://www.geocities.com/deancooper2000/PrintDialog64.jpg

这里是我的代码(我已经离开了设计师的代码了,因为它应该是很容易重新):

using System; 
using System.Collections.Generic; 
using System.Drawing; 
using System.Drawing.Printing; 
using System.Printing; 
using System.Runtime.InteropServices; 
using System.Windows.Forms; 
using Zemetrics.Diagnostics; 

namespace Utils 
{ 
/// <summary> 
/// The PrintDialog64 class replaces the standard PrintDialog with one that works in Vista x64 
/// </summary> 
public partial class PrintDialog64 : Form 
{ 
    #region Private members 
    [DllImport("winspool.drv", EntryPoint="DocumentPropertiesW")] 
    private static extern int DocumentProperties(IntPtr hWnd,IntPtr hPrinter,[MarshalAs(UnmanagedType.LPWStr)] string pDeviceName,IntPtr pDevMode,IntPtr devModeIn,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")] private static extern IntPtr GlobalLock(IntPtr hMem); 
    [DllImport("kernel32.dll")] private static extern int GlobalUnlock(IntPtr hMem); 
    [DllImport("kernel32.dll")] private static extern int GlobalFree(IntPtr hMem); 

    private const int DM_PROMPT  = 4; 
    private const int DM_OUT_BUFFER = 2; 
    private const int DM_IN_BUFFER = 8; 

    private List<PrinterItem> printers; 
    private string   printerName; 
    private string   originalName; 
    private IntPtr   hDevMode = IntPtr.Zero; 
    #endregion 

    /// <summary> 
    /// Gets or sets the printer that prints the document  
    /// </summary> 
    public PrinterSettings PrinterSettings { get; set; } 

    /// <summary> 
    /// Gets or sets a value indicating the PrintDocument used to obtain PrinterSettings.  
    /// </summary> 
    public PrintDocument Document { get; set; } 

    /// <summary> 
    /// Constructs a replacement for the standard PrintDialog with one that works in Vista x64 
    /// </summary> 
    public PrintDialog64() 
    { 
     InitializeComponent(); 
    } 

    #region PrinterItem class 
    /// <summary> 
    /// The PrinterItem class holds a reference to a PrintQueue and allows us to sort a list based on printer name 
    /// </summary> 
    private class PrinterItem : IComparable<PrinterItem> 
    { 
     #region Private members 
     private PrinterItem() {} 
     #endregion 

     /// <summary> 
     /// Construct a PrinterItem by supplying a reference to the printer's PrintQueue class 
     /// </summary> 
     /// 
     /// \param[in] printer Reference to PrintQueue class for this printer 
     public PrinterItem(PrintQueue printer) 
     { 
      Printer = printer; 
     } 

     /// <summary> 
     /// Reference to PrintQueue class for this printer 
     /// </summary> 
     public PrintQueue Printer { get; set; } 

     /// <summary> 
     /// The string for this class is simply the FullName of the printer 
     /// </summary> 
     public override string ToString() 
     { 
      return Printer.FullName; 
     } 

     #region IComparable<PrinterItem> Members 
     /// <summary> 
     /// Implements IComparable interface to allow sorting of PrinterItem classes (based on printer name) 
     /// </summary> 
     /// 
     /// \param[in] other The other PrinterItem class that we are to compare this one to 
     public int CompareTo(PrinterItem other) 
     { 
      return other.Printer.FullName.CompareTo(this.Printer.FullName); 
     } 
     #endregion 
    } 
    #endregion 

    private List<PrinterItem> GetPrinters() 
    { 
     List<PrinterItem> printers = new List<PrinterItem>(); 

     EnumeratedPrintQueueTypes[] Queue_types = {EnumeratedPrintQueueTypes.Local,EnumeratedPrintQueueTypes.Connections}; 

     try { 
      using (LocalPrintServer server = new LocalPrintServer()) 
       foreach (PrintQueue printer in server.GetPrintQueues(Queue_types)) 
        printers.Add(new PrinterItem(printer));     
      } catch {} 

     printers.Sort(); 
     return printers;     
    } 

    private void PrintDialog64_Shown(object sender, EventArgs e) 
    { 
     originalName = Document.PrinterSettings.PrinterName; 
     printers  = GetPrinters(); 
     int index=0, i=0; 

     foreach(PrinterItem printer in printers) { 
      nameComboBox.Items.Add(printer.ToString()); 

      if (printer.ToString() == originalName) index = i; 
      i++; 
      } 

     nameComboBox.SelectedIndex = index; 
    } 

    private void nameComboBox_Leave(object sender, EventArgs e) 
    { 
     string text = nameComboBox.Text; 

     foreach(Object field in nameComboBox.Items) 
      if (((string) field).ToLower().StartsWith(text.ToLower())) nameComboBox.SelectedItem = field; 

     if (nameComboBox.SelectedIndex < 0) 
      nameComboBox.SelectedIndex = 0; 
    } 

    private void nameComboBox_SelectedIndexChanged(object sender, EventArgs e) 
    { 
     PrintQueue printer = printers[nameComboBox.SelectedIndex].Printer; 

     if (hDevMode!=IntPtr.Zero) GlobalFree(hDevMode); 

     PrinterSettings.PrinterName = printerName = printer.FullName; 
     hDevMode     = PrinterSettings.GetHdevmode(Document.DefaultPageSettings);    

     statusValue .Text = printer.QueueStatus.ToString()=="None" ? "Ready" : printer.QueueStatus.ToString(); 
     whereValue .Text = printer.Location=="" ? printer.QueuePort.Name : printer.Location; 
     commentValue.Text = printer.Comment; 
    } 

    private void propertiesButton_Click(object sender, EventArgs e) 
    { 
     IntPtr handle; 
     OpenPrinter(printerName, out handle, IntPtr.Zero); 

     IntPtr pDevMode = GlobalLock(hDevMode); 
     DocumentProperties(this.Handle, handle, printerName, pDevMode, pDevMode, DM_IN_BUFFER | DM_PROMPT | DM_OUT_BUFFER); 
     GlobalUnlock(hDevMode); 

     PrinterSettings.SetHdevmode(hDevMode); 
     PrinterSettings.DefaultPageSettings.SetHdevmode(hDevMode); 
     ClosePrinter(handle); 
    } 

    private void pageDefaultsButton_Click(object sender, EventArgs e) 
    { 
     PageSetupDialog setup = new PageSetupDialog(); 
     setup.PageSettings = Document.DefaultPageSettings; 

     if (setup.ShowDialog() == DialogResult.OK) { 
      if (hDevMode!=IntPtr.Zero) GlobalFree(hDevMode); 

      hDevMode = PrinterSettings.GetHdevmode(Document.DefaultPageSettings = setup.PageSettings); 
      } 
    } 

    private void okButton_Click(object sender, EventArgs e) 
    { 
     if (hDevMode!=IntPtr.Zero) GlobalFree(hDevMode); 
    } 

    private void cancelButton_Click(object sender, EventArgs e) 
    { 
     if (hDevMode!=IntPtr.Zero) GlobalFree(hDevMode); 

     PrinterSettings.PrinterName = originalName; 
    } 
} 
}