Как показать диалоговое окно свойств / настроек принтера и сохранить изменения? - PullRequest
7 голосов
/ 13 марта 2010

РЕДАКТИРОВАТЬ: Моя вина! Я ожидал, что изменения будут записаны обратно в настройки принтера по умолчанию, когда фактически будет изменен только локальный экземпляр PrinterSettings. - Код ниже, кажется, работает как задумано

Я пытаюсь показать пользовательские свойства принтера данного принтера. Мне нужно это как часть пользовательского PrintDialog, который я пытаюсь написать.

Большинство примеров, которые я могу найти в Интернете, показывают диалоговое окно, но любые изменения, которые может внести пользователь, теряются, что делает его бесполезным.

Пример: http://www.codeproject.com/KB/system/PrinterPropertiesWindow.aspx

(относительно страницы выше: я пытался изменить код в соответствии с предложением BartJoy (на странице), но это не помогло)

Я также попробовал образец и предложения на странице pinvoke.net, но он все еще не работает:

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

На указанных выше веб-сайтах я предполагаю, что проблема может быть только в 64-битной Windows и / или если имя принтера длиннее 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);
}

Ответы [ 4 ]

7 голосов
/ 10 апреля 2013

Если вы нацелены на компиляцию x86 и запускаете с машины x64, код от Джеффа Роу не будет работать: при выделении devModeData, DocumentPropreties всегда завершится ошибкой и вернет sizeNeeded -1, а LastError код 13.

Чтобы решить проблему, убедитесь, что вы нацелились AnyCPU или просто измените вызов на DocumentPropreties на следующее:

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

Использование IntPtr.Zero вместо правильного указателя на структуру DevMode выглядит неправильно, но этот первый вызов DocumentProperties не пытается изменить память в этой позиции. Единственными данными, возвращаемыми вызовом, является объем памяти, необходимый для хранения данных режима устройства, которые представляют внутренние параметры драйвера принтера.

Справка:

7 голосов
/ 14 июля 2011

Несмотря на то, что ответ в конечном итоге проработал вопрос, я думаю, что следующее дает лучший ответ на оригинальный вопрос,

(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;
}
3 голосов
/ 16 марта 2010
  • при запуске приложения:
    • запросили ли вы драйвер принтера для правильного размера структуры DEVMODE перед его выделением?
    • Вы просили драйвер устройства инициализировать буфер DEVMODE настройками по умолчанию после его выделения?
  • когда ваше приложение открыло диалоговое окно принтера:
    • Вы установили флаги DM_IN_BUFFER и DM_OUT_BUFFER (в дополнение к DM_IN_PROMPT) в параметре fMode на DocumentProperties?
    • указали ли вы pDevModeInput и pDevModeOutput на буфер DEVMODE, который вы инициализировали при запуске приложения?
    • - это биты dmFields в буфере DEVMODE, правильно установленные до вашего вызова DocumentProperties(... DM_IN_PROMPT ...)
    • Вы сохраняете содержимое буфера DEVMODE между вызовами на DocumentProperties(... DM_IN_PROMPT ...)?

См:

0 голосов
/ 24 марта 2010

Кроме того, если вы хотите сделать это с помощью классов WPF (PrintQueue, PrintTicket), эта страница указывает вам правильное направление:

http://social.msdn.microsoft.com/Forums/en/wpf/thread/0dc695c1-578d-4da5-8f68-b2a257846c02

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...