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

EDIT: моя вина! Я ожидал, что изменения будут записаны обратно в настройки принтера по умолчанию, когда на самом деле изменяется только локальный экземпляр PrinterSettings. - Приведенный ниже код, похоже, работает по назначению

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

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

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

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

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

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

из вышеперечисленных сайтов я предполагаю, что проблема может быть только в 64-битной версии Windows и/или если имя принтера длиннее 32 символов.

Я не знаю, что я должен попробовать следующий... Я ценю любые предложения и комментарии!

EDIT: вот что я пробовал:

[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 ответов


  • при запуске приложения:
    • вы запросили драйвер принтера для правильного размера DEVMODE структура перед ее выделением?
    • вы попросили драйвер устройства инициализировать DEVMODE буфер с настройками по умолчанию после его выделения?
  • когда ваше приложение открыло диалоговое окно принтера:
    • ты DM_IN_BUFFER и DM_OUT_BUFFER флаги (в дополнение к DM_IN_PROMPT) в до DocumentProperties?
    • вы указали как pDevModeInput и pDevModeOutput до DEVMODE буфер инициализируется при запуске приложения?
    • это dmFields бит DEVMODE буфер правильно установлен до вашего вызова DocumentProperties(... DM_IN_PROMPT ...)
    • вы сохраняете содержимое DEVMODE буфер между вызовами DocumentProperties(... DM_IN_PROMPT ...)?

посмотреть:


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

(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;
}

если вы нацелены на компиляцию x86 и запускаетесь с машины x64, код от Jeff Roe не будет работать: при выделении devModeData, DocumentPropreties всегда будет терпеть неудачу и возвращает sizeNeeded -1, а с LastError код 13.

чтобы решить проблему, либо убедитесь, что вы target любой ЦП или просто измените вызов DocumentPropreties следующим образом:

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

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

ссылки:


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

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