Использование PrintDlg в Vista x64 не работает, отлично работает на 32-битной и XP - PullRequest
2 голосов
/ 13 ноября 2008

У нас есть приложение с устаревшим кодом «установки» принтера, для которого мы до сих пор используем <a href="http://msdn.microsoft.com/en-us/library/ms646940(VS.85).aspx" rel="nofollow noreferrer">PrintDlg</a>. Мы используем пользовательский шаблон, чтобы позволить пользователю выбрать, какой принтер использовать для различных типов задач печати (например, отчетов или чертежей), а также ориентацию и размер бумаги / источник.

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

Поскольку выбор принтера / настройка страницы были разделены на <a href="http://msdn.microsoft.com/en-us/library/ms646937(VS.85).aspx" rel="nofollow noreferrer">PageSetupDlg</a> и <a href="http://msdn.microsoft.com/en-us/library/ms646942(VS.85).aspx" rel="nofollow noreferrer">PrintDlgEx</a>, очевидного простого перехода не будет без изменения значительного количества кода и / или полного изменения способа представления пользователю настроек печати и принтера .

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

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

Ответы [ 3 ]

2 голосов
/ 17 ноября 2008

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

В примере программы я подтвердил, что PrintDlg работает без прав администратора.

0 голосов
/ 17 января 2009

Я только что столкнулся с этой проблемой, когда добавлял печать в свое приложение. Я использовал класс 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;
    }
}
}
0 голосов
/ 17 ноября 2008

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

...