2008-11-12 71 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; 
    } 
} 
}