PrintDocument.Print приводит к Win32Exception Операция завершена успешно

Я пытаюсь распечатать в приложении С#.NET 3.5 сетевому принтеру и получить это исключение:

Успешная операция

Что вызывает его и как его можно решить?

System.ComponentModel.Win32Exception: The operation completed successfully
   at System.Drawing.Printing.PrinterSettings.GetHdevmodeInternal()
   at System.Drawing.Printing.PrinterSettings.GetHdevmode(PageSettings pageSettings)
   at System.Drawing.Printing.PrintController.OnStartPrint(PrintDocument document, PrintEventArgs e)
   at System.Windows.Forms.PrintControllerWithStatusDialog.OnStartPrint(PrintDocument document, PrintEventArgs e)
   at System.Drawing.Printing.PrintController.Print(PrintDocument document)
   at System.Drawing.Printing.PrintDocument.Print()
  • У учетной записи есть разрешения на печать с использованием сетевого принтера. Разрешения назначаются для печати всех пользователей.
  • принтер был удален и обновлен.
  • настройка буферизации и печати непосредственно на принтер была переключена в обе стороны.
  • другие принтеры на машине работают нормально
  • другие клиенты в сети и приложения на этом же компьютере могут печатать на этом принтере без проблем.

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

enter image description here

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

Ответ 1

Сообщение о мистификации вызвано ошибкой в ​​кодах pinvoke внутри .NET Framework. Основной вызов winapi, который терпит неудачу, - это DocumentProperties() function. Объявление pinvoke для него выглядит так:

[DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
public static extern int DocumentProperties(...);

Неверное свойство SetLastError. Как вы можете сказать по ссылке MSDN, функция указывает на отказ, возвращая отрицательное значение. И не документировано, чтобы установить код ошибки, возвращаемый GetLastError().

Следствием этой ошибки является то, что структура вызовет Marshal.GetLastWin32Error() для получения кода ошибки и получит случайное значение, так как DocumentProperties() не установил его. Значение 0 маловероятно, что приводит к сообщению об исключении "Операция завершена успешно".

Итак, вам нужно игнорировать сообщение об исключении; Конечно, это очень бесполезно. К сожалению, эта функция winapi относится к категории функций, как и большинство функций GDI, которые производят код возврата "не работает". В нем нет намека на то, где искать проблему. Для этой причуды есть полупорядочная причина: сама Windows делает очень мало, когда вы вызываете DocumentProperties(); большая часть работы выполняется драйвером принтера. Для печати в winapi нет набора кодов ошибок. Все возможно: драйверы принтера - это не тонкие куски кода. Задача драйвера принтера - рассказать вам о проблемах. Они должны сделать это, открыв собственное окно. Теоретически они в любом случае; конкуренция в этом сегменте рынка не оставляет много денег, чтобы заплатить хорошую зарплату программисту в эти дни.

Это, конечно, не может работать, когда вы печатаете из службы. Нет никакого способа увидеть такое всплывающее окно, которое является основной причиной того, что Microsoft сильно обескураживает печать из службы. Ни у вас, ни у вашего клиента ИТ-персонал не дает возможности диагностировать проблемы. Прочтите этот сообщение в блоге за дополнительными примечаниями об использовании PrintDocument из службы.

Никто не любит получать совет, как это, но письмо на стене. Не делайте этого.

Ответ 2

Чтобы добавить к ответу @HansPassant, вот точный код, который вызывает исключение:

Справочный источник Microsoft, PrinterSettings.cs

private IntPtr GetHdevmodeInternal(string printer) {
    // Create DEVMODE
    int modeSize = SafeNativeMethods.DocumentProperties(NativeMethods.NullHandleRef, NativeMethods.NullHandleRef, printer, IntPtr.Zero, NativeMethods.NullHandleRef, 0);
    if (modeSize < 1) {
        throw new InvalidPrinterException(this);
    }
    IntPtr handle = SafeNativeMethods.GlobalAlloc(SafeNativeMethods.GMEM_MOVEABLE, (uint)modeSize); // cannot be <0 anyway
    IntPtr pointer = SafeNativeMethods.GlobalLock(new HandleRef(null, handle));

    //Get the DevMode only if its not cached....
    if (cachedDevmode != null) {
        Marshal.Copy(cachedDevmode, 0, pointer, devmodebytes);
    }
    else  {
        int returnCode = SafeNativeMethods.DocumentProperties(NativeMethods.NullHandleRef, NativeMethods.NullHandleRef, printer, pointer, NativeMethods.NullHandleRef, SafeNativeMethods.DM_OUT_BUFFER);
        if (returnCode < 0) {
            throw new Win32Exception();  // <--------
        }
    }

Ответ 3

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

  • Проверьте его на локальном принтере, чтобы убедиться, что приложение работает.
  • Попробуйте выполнить печать на сетевом принтере с помощью блокнота или чего-нибудь подобного
  • Двойная тройная проверка того, что пользователь работает, и имеет разрешения на печать на сетевой принтер.
  • Проверьте его на другом сетевом принтере (после выполнения 2 и 3 для этого принтера)

Ответ 4

У меня также была эта проблема. В моем случае я использовал отдельный поток для печати отчета и синхронизировал его с основным потоком через "ManualResetEvent". (Я разработал этот путь, потому что .Net-силы делят логику печати на методы "PrintPage"...).

Ошибка возникает из-за несовместимости (которой я не могу объяснить источник) между драйвером принтера и этой многопоточной средой.

Я решил проблему, изменив подпись моего метода логики печати от "void" до "IEnumerable" и прорвав результат возврата, метод, аналогичный "CoRoutines" игрового движка Unity3d. Таким образом, нет необходимости создавать несколько потоков, и мой код печати будет организован.