Как открыть диалоговое окно свойств / настроек принтера и сохранить изменения?

РЕДАКТИРОВАТЬ: Моя вина! Я ожидал, что изменения будут записаны обратно в настройки принтера по умолчанию, хотя на самом деле изменяется только локальный экземпляр 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);
}

person Community    schedule 13.03.2010    source источник
comment
И как вам удалось вернуть спасенные чаги? Этот код меняет настройки принтера, но изменения не сохраняются как настройки принтеров по умолчанию :(   -  person Ando    schedule 11.05.2010


Ответы (4)


  • when your application started up:
    • have you queried the printer driver for the correct size of the DEVMODE structure before allocating it?
    • Вы просили драйвер устройства инициализировать DEVMODE буфер с настройками по умолчанию после того, как вы его разместили?
  • when your application popped up the printer dialog:
    • have you set the DM_IN_BUFFER and DM_OUT_BUFFER flags (in addition to DM_IN_PROMPT) in the fMode parameter to DocumentProperties?
    • вы указали и pDevModeInput, и pDevModeOutput на буфер DEVMODE, который вы инициализировали при запуске приложения?
    • правильно ли установлены dmFields биты в DEVMODE буфере до вашего вызова DocumentProperties(... DM_IN_PROMPT ...)
    • вы сохраняете содержимое DEVMODE буфера между вызовами DocumentProperties(... DM_IN_PROMPT ...)?

Видеть:

person vladr    schedule 16.03.2010
comment
спасибо за вклад. Я считаю, что делаю эти вещи. Я обновил вопрос и добавил код, который я пробовал. - person Patrick Klug; 17.03.2010
comment
моя вина! Я ожидал, что изменения будут записаны обратно в настройки принтера по умолчанию, чтобы при вызове того же метода с новым PrinterSettings () он отражал прошлые изменения. - Кажется, что все работает правильно, так как параметры принтера обновляются правильно. - person Patrick Klug; 17.03.2010

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

(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;
}
person Community    schedule 13.07.2011
comment
@JeffRow Я только что попробовал код, который вы предоставили на 64-битной машине win8, и DocumentProperties возвращает -1, и поэтому при вызове Marshal.AllocHGlobal (sizeNeeded) выдается ошибка. Недостаточно памяти для продолжения выполнения программы. что имеет смысл, поскольку sizeNeeded равняется -1. - person Thierry; 10.03.2014
comment
Хотя это ранняя стадия, это, по-видимому, самый стабильный код, который я нашел до сих пор, чтобы решить мою проблему. Единственное небольшое изменение, основанное на моем предыдущем комментарии, заключается в том, что для получения sizeNeeded вам необходимо изменить вызов API, чтобы использовать IntPtr.Zero вместо 0, т.е. Dim sizeNeeded As Integer = DocumentProperties (Me.Handle, IntPtr.Zero , printerSettings.PrinterName, IntPtr.Zero, pDevMode, 0). Спасибо, что поделился! - person Thierry; 10.03.2014

Если вы нацеливаетесь на компиляцию 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 не пытается изменить память в этой позиции. Единственные данные, возвращаемые вызовом, - это объем памяти, необходимый для хранения данных режима устройства, представляющих внутренние параметры драйвера печати.

Ссылка:

person Community    schedule 10.04.2013

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

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

person Community    schedule 24.03.2010