Использование PrintDlg в Vista x64 не работает, отлично работает в 32-битной и XP.

У нас есть приложение с некоторым устаревшим кодом «настройки» принтера, для которого мы все еще используем PrintDlg. Мы используем настраиваемый шаблон, позволяющий пользователю выбирать, какой принтер использовать для различных типов задач печати (например, отчетов или рисунков), а также ориентацию и размер/источник бумаги.

Он работает на XP и 32-битной Vista, но на Vista x64 получает CDERR_MEMLOCKFAILURE через CommDlgExtendedError(). Я пытался запустить его только с вводом голых костей в структуре PRINTDLG, но если параметры включают PD_PRINTSETUP или PD_RETURNDEFAULT, я получаю эту ошибку.

Поскольку выбор принтера/настройка страницы разделены на PageSetupDlg и PrintDlgEx, не существует очевидного простого перехода без изменения достаточного количества кода и/или полного изменения того, как мы представляем печать и настройку принтера пользователю.

Сталкивался ли кто-нибудь с этой проблемой в 64-битной Vista, и нашли ли вы обходные пути?

Примечания.
Приложение запускается от имени администратора из-за других ограничений.


person crashmstr    schedule 12.11.2008    source источник


Ответы (3)


Я нашел соответствующее сообщение на форумах Microsoft: В Vista x64 DocumentProperties не работает из процесса с повышенными правами UAC

Я проверил с помощью примера программы, что PrintDlg работает без прав администратора.

person crashmstr    schedule 17.11.2008

Я нашел сообщение на форуме сообщества Quicken: Решение проблем с печатью в Vista 64 Quicken 2008 и соответствующие часто задаваемые вопросы: Что делать, если я не могу печатать или получаю сообщение "Ошибка связи с принтером"? и рекомендации по использованию эмуляция принтера.

person crashmstr    schedule 17.11.2008

Я просто столкнулся с этой проблемой, когда добавлял печать в свое приложение. Я использовал класс PrintDialog, и он отлично работает, если он скомпилирован как 32-битное приложение, но даже не появляется при компиляции в 64-битном режиме. Никаких сообщений об ошибках, ничего. Вызов ShowDialog сразу возвращается. (Обратите внимание, что я использую 64-разрядную версию Vista.)

Я пытался использовать PrintDlg, но у него та же проблема. Я посмотрел в Интернете и нашел много людей, у которых были похожие проблемы, хотя, видимо, не все, у кого есть 64-битная Vista, видят это. В любом случае, я, наконец, решил написать свою собственную версию PrintDialog (заимствуя из кода в Интернете), но это было немного сложно (поскольку в некоторых онлайн-кодах были ошибки), и, поскольку я так и не нашел в Интернете полный пример для этого, я подумал Я бы разместил свое решение здесь.

Обратите внимание, что в моей версии несколько элементов не отображаются в диалоговом окне, например «Диапазон печати», «Копии» и «Печать в файл». Это должно быть легко добавить, но моему приложению они не нужны. Я также не мог понять, что отображалось в поле «Тип:», поэтому я его тоже пропустил.

Вот как выглядит диалог:

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;
    }
}
}
person AZDean    schedule 17.01.2009