Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Внимание
Современная платформа печати — это предпочтительный способ взаимодействия с принтерами Windows. Рекомендуется использовать драйвер класса "Входящие" Microsoft IPP вместе с приложениями поддержки печати (PSA), чтобы настроить возможности печати в Windows 10 и 11 для разработки устройств принтера.
Дополнительные сведения см. в руководстве по проектированию приложения поддержки печати .
Приложения расширения принтера поддерживают параметры печати и уведомления о принтерах, когда пользователи запускают существующие приложения на рабочем столе Windows.
Расширения принтера можно создавать на любом языке с поддержкой COM, но оптимизированы для создания с помощью Microsoft платформа .NET Framework 4. Расширения принтера могут распространяться с помощью пакета драйвера печати, если они имеют возможность XCopy и не зависят от внешних сред выполнения, отличных от тех, которые включены в операционную систему, например .NET. Если приложение расширения принтера не соответствует этим критериям, оно может быть распределено в setup.exe или пакете MSI и объявлено в интерфейсе устройства принтера с помощью директивы PrinterExtensionUrl, указанной в манифесте версии 4. Если приложение расширения принтера распространяется через пакет MSI, вы можете добавить драйвер печати в пакет или оставить его и распределить драйвер отдельно. ПринтерExtensionUrl отображается в параметрах принтера.
ИТ-администраторы могут управлять распределением расширений принтера. Если приложение упаковано в setup.exe или MSI, ИТ-администраторы могут использовать стандартные средства распространения программного обеспечения, такие как Microsoft Endpoint Configuration Manager, или они могут включать приложения в свой стандартный образ ОС. ИТ-администраторы также могут переопределить ПринтерExtensionUrl, указанный в манифесте версии 4, если они редактируют HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Prints\<>PrintDriverData\PrinterExtensionUrl.
И если предприятие полностью блокирует расширения принтера, это можно сделать с помощью групповой политики "Конфигурация компьютера\Административные шаблоны\Принтеры\Не разрешать драйверам принтера версии 4 показывать приложения расширения принтера".
Создание расширения принтера
При разработке расширения принтера необходимо учитывать шесть основных областей. Эти области фокуса показаны в следующем списке.
Регистрация
Включение событий
Обработчик OnDriverEvent
Параметры печати
Уведомления о принтерах
Управление принтерами
Регистрация
Расширения принтера регистрируются в системе печати путем указания набора разделов реестра или указания сведений о приложении в разделе PrinterExtensions файла манифеста версии 4.
Указаны идентификаторы GUID, поддерживающие каждую из разных точек входа для расширений принтера. Эти идентификаторы GUID не нужно использовать в файле манифеста версии 4, но необходимо знать значения GUID, чтобы использовать формат реестра для установки драйвера версии 4. В следующей таблице показаны значения GUID для двух точек входа.
Точка входа | GUID |
---|---|
Параметры печати | {EC8F261F-267C-469F-B5D6-393023C29CC} |
Уведомления о принтерах | {23BB1328-63DE-4293-915B-A6A23D929ACB} |
Расширения принтера, установленные за пределами драйвера принтера, должны быть зарегистрированы с помощью реестра. Это гарантирует, что расширения принтера можно установить независимо от состояния spooler или модуля конфигурации версии 4 на клиентском компьютере.
После запуска службы PrintNotify он проверяет наличие разделов реестра в пути [OfflineRoot] и обрабатывает все ожидающие регистрации или отмены регистрации. После завершения всех ожидающих регистрации или отмены регистрации разделы реестра удаляются в режиме реального времени. Если вы используете скрипт или итеративный процесс для размещения разделов реестра, возможно, потребуется повторно создать ключ \[PrinterExtensionID] при каждом указании ключа \[PrinterDriverId]. Неполные или неправильные ключи не удаляются.
Эта регистрация необходима только при первой установке. В следующем примере показан правильный формат раздела реестра, используемый для регистрации расширений принтера.
Примечание.
[OfflineRoot] используется в качестве краткой инструкции для HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\OfflinePrinterExtensions.
[OfflineRoot]
\[PrinterExtensionId] {GUID}
AppPath=[PrinterExtensionAppPath] {String}
\[PrinterDriverId] {GUID}
\[PrinterExtensionReasonGuid]
(default) = ["0"|"1"] {REG_SZ 0:Unregister, 1:Register}
\…
\[PrinterExtensionReasonGuidN]
\[PrinterDriverId2]
\[PrinterExtensionReasonGuid2.1]
\…
\[PrinterExtensionReasonGuid2.Z]
…
\[PrinterDriverIdM]
\[PrinterExtensionId2]
…
\[PrinterExtensionIdT]
Например, следующий набор ключей регистрирует расширение принтера с помощью файла {PrinterExtensionIDGuid} PrinterExtensionID и полного пути к исполняемому файлу "C:\Program Files\Fabrikam\pe.exe" для файла {PrinterDriverID1Guid} и {PrinterDriverID2Guid} PrinterDriverIDs с параметрами принтера и причинами уведомлений о принтерах.
[OfflineRoot]
\{PrinterExtensionIDGuid}
AppPath="C:\Program Files\Fabrikam\pe.exe"
\{PrinterDriverID1Guid}
\{EC8F261F-267C-469F-B5D6-3933023C29CC}
(default) = "1"
\{23BB1328-63DE-4293-915B-A6A23D929ACB}
(default) = "1"
\{PrinterDriverID1Guid}
\{EC8F261F-267C-469F-B5D6-3933023C29CC}
(default) = "1"
\{23BB1328-63DE-4293-915B-A6A23D929ACB}
(default) = "1"
Чтобы удалить то же расширение принтера, необходимо указать следующий набор ключей.
[OfflineRoot]
\{PrinterExtensionIDGuid}
AppPath="C:\Program Files\Fabrikam\pe.exe"
\{PrinterDriverID1Guid}
\{EC8F261F-267C-469F-B5D6-3933023C29CC}
(default) = "0"
\{23BB1328-63DE-4293-915B-A6A23D929ACB}
(default) = "0"
\{PrinterDriverID1Guid}
\{EC8F261F-267C-469F-B5D6-3933023C29CC}
(default) = "0"
\{23BB1328-63DE-4293-915B-A6A23D929ACB}
(default) = "0"
Так как расширения принтера могут выполняться как в контексте, запущенном пользователем, так и в контексте, запущенном событиями, полезно определить контекст, в котором работает расширение принтера. Это позволяет приложению, например, не перечислять состояние во всех очередях, если оно было запущено для уведомления или настройки печати. Корпорация Майкрософт рекомендует использовать расширения принтера, которые устанавливаются отдельно от драйвера (например, с MSI или setup.exe) использовать переключатели командной строки в сочетаниях клавиш меню или в записи AppPath, заполненной в реестре во время регистрации. Так как расширения принтера, установленные с драйвером, устанавливаются в DriverStore, они не будут запускаться вне параметров печати или событий уведомлений принтера. Поэтому указание параметров командной строки не поддерживается в этом случае.
Когда расширение принтера регистрируется для текущего Объекта PrinterDriverID, оно должно включать PrinterDriverID в AppPath. Например, для приложения расширения принтера с именем printerextension.exe и значением PrinterDriverID {GUID}, [PrinterExtensionAppPath] будет выглядеть следующим образом:
"C:\program files\fabrikam\printerextension.exe {GUID}"
Включение событий
Во время выполнения расширения принтера должны включить триггер событий для текущего Объекта PrinterDriverID. Это Идентификатор PrinterDriverID, который был передан приложению через массив args[] и позволяет системе печати предоставить соответствующий контекст событий для обработки таких причин, как настройки печати или уведомления принтера.
Поэтому приложение должно создать новый PrinterExtensionManager для текущего Объекта PrinterDriverID, зарегистрировать делегат для обработки события OnDriverEvent и вызвать метод EnableEvents с помощью PrinterDriverID. Следующий фрагмент кода иллюстрирует этот подход.
PrinterExtensionManager mgr = new PrinterExtensionManager();
mgr.OnDriverEvent += OnDriverEvent;
mgr.EnableEvents(new Guid(PrinterDriverID1));
Если приложение не вызывает EnableEvents в течение 5 секунд, Windows запустит стандартный пользовательский интерфейс. Чтобы устранить эту проблему, расширения принтера должны соответствовать последним рекомендациям по производительности, включая следующие:
Задержка как можно большей части инициализации приложения до тех пор, пока не вызовете EnableEvents. После этого определите приоритет отклика пользовательского интерфейса с помощью асинхронных методов и не блокируя поток пользовательского интерфейса во время инициализации.
Используйте ngen для создания собственного образа во время установки. Дополнительные сведения см. в разделе "Генератор собственных образов".
Используйте средства измерения производительности для поиска проблем с производительностью при загрузке. Дополнительные сведения см. в статье "Средства анализа производительности Windows".
Обработчик DriverEvent
После регистрации обработчика OnDriverEvent и включения событий, если расширение принтера было запущено для обработки настроек печати или уведомлений принтера, обработчик будет вызван. В предыдущем фрагменте кода метод с именем OnDriverEvent был зарегистрирован в качестве обработчика событий. В следующем фрагменте кода параметр PrinterExtensionEventArgs — это объект, который позволяет создавать сценарии настройки печати и уведомлений принтера. PrinterExtensionEventArgs — это оболочка для IPrinterExtensionEventArgs.
static void OnDriverEvent(object sender, PrinterExtensionEventArgs eventArgs)
{
//
// Display the print preferences window.
//
if (eventArgs.ReasonId.Equals(PrinterExtensionReason.PrintPreferences))
{
PrintPreferenceWindow printPreferenceWindow = new PrintPreferenceWindow();
printPreferenceWindow.Initialize(eventArgs);
//
// Set the caller application's window as parent/owner of the newly created printing preferences window.
//
WindowInteropHelper wih = new WindowInteropHelper(printPreferenceWindow);
wih.Owner = eventArgs.WindowParent;
//
// Display a modal/non-modal window based on the 'WindowModal' parameter.
//
if (eventArgs.WindowModal)
{
printPreferenceWindow.ShowDialog();
}
else
{
printPreferenceWindow.Show();
}
}
//
// Handle driver events.
//
else if (eventArgs.ReasonId.Equals(PrinterExtensionReason.DriverEvent))
{
// Handle driver events here.
}
}
Чтобы предотвратить плохой пользовательский интерфейс, связанный с сбоем или медленными расширениями принтера, Windows реализует время ожидания, если EnableEvents не вызывается в течение короткого времени после запуска приложения. Чтобы включить отладку, это время ожидания отключено, если есть отладчик, подключенный к службе PrintNotify.
Однако в большинстве случаев весь код, связанный с приложением, в котором мы заинтересованы, выполняется во время или после обратного вызова OnDriverEvent. Во время разработки также может быть полезно показать MessageBox перед запуском параметров печати или уведомлений принтера из обратного вызова OnDriverEvent. Когда появится MessageBox, вернитесь в Visual Studio и выберите "Присоединить к процессу">и выберите имя процесса. Наконец, вернитесь в messageBox и нажмите кнопку "ОК", чтобы возобновить работу. Это обеспечит наличие исключений и попадание любых точек останова с этого момента.
Новые идентификаторы ReasonIds могут поддерживаться в будущем. В результате расширения принтера должны явным образом проверить ReasonID и не должны использовать оператор else для обнаружения последнего известного объекта ReasonID. Если объект ReasonID получен и неизвестен, приложение должно выйти из нее корректно.
Параметры печати
Параметры печати зависят от объекта PrintSchemaEventArgs.Ticket. Этот объект инкапсулирует документы PrintTicket и PrintCapabilities, описывающие функции и параметры устройства. Хотя базовый XML-код также доступен, объектная модель упрощает работу с этими форматами.
Внутри каждого объекта IPrintSchemaTicket или IPrintSchemaCapabilities есть функции (IPrintSchemaFeature) и параметры (IPrintSchemaOption). Хотя интерфейсы, используемые для функций и параметров, одинаковы независимо от источника, поведение немного меняется в результате базового XML-кода. Например, документы PrintCapabilities указывают множество параметров для каждой функции, а документы PrintTicket указывают только выбранный параметр (или по умолчанию). Аналогичным образом документы PrintCapabilities указывают локализованные строки отображения, в то время как документы PrintTicket не имеют значения.
Дополнительные сведения о привязке данных в WPF см. в разделе "Обзор привязки данных".
Чтобы повысить производительность, корпорация Майкрософт рекомендует выполнять вызовы GetPrintCapabilities только в том случае, если необходимо обновить документ PrintCapabilities.
Так как пользователь делает выбор с помощью элементов управления ComboBox с привязкой к данным, объект PrintTicket автоматически обновляется. Когда пользователь, наконец, нажимает кнопку "ОК", начинается цепочка асинхронной проверки и завершения. Этот асинхронный шаблон широко используется, чтобы предотвратить выполнение длительных задач в потоках пользовательского интерфейса и вызвать зависание в пользовательском интерфейсе параметров печати или в приложении, которое печатается. Ниже приведен список шагов, используемых для обработки изменений PrintTicket после нажатия пользователем кнопки ОК.
PrintSchemaTicket проверяется асинхронно с помощью метода IPrintSchemaTicket::ValidateAsync.
После завершения асинхронной проверки среда CLR вызывает метод PrintTicketValidateCompleted.
Если проверка выполнена успешно, метод CommitPrintTicketAsync вызывает метод IPrintSchemaTicketAsync, а метод IPrintSchemaTicket::CommitAsync. При успешном завершении обновления PrintTicket вызывается метод PrintTicketCommitCompleted, который вызывает удобный метод, который вызывает метод PrinterExtensionEventArgs.Request.Complete, чтобы указать, что параметры печати завершены, а затем закрывает приложение.
В противном случае он представляет пользовательский интерфейс пользователю для обработки ситуации ограничения.
Если пользователь щелкнул кнопку "Отменить" или закрыть окно параметров печати напрямую, расширение принтера вызывает IPrinterExtensionEventArgs.Request.Cancel с соответствующим значением HRESULT и сообщением для журнала ошибок.
Если процесс расширения принтера закрыт и не вызывает методы Complete или Cancel, как описано в предыдущих абзацах, система печати автоматически вернется к использованию пользовательского интерфейса, предоставленного корпорацией Майкрософт.
Чтобы получить сведения о состоянии устройства, расширения принтера могут использовать Bidi для запроса устройства печати. Например, чтобы отобразить состояние рукописного ввода или другие типы состояния устройства, расширения принтера могут использовать метод IPrinterExtensionEventArgs.PrinterQueue.SendBidiQuery для выдачи запросов Bidi на устройство. Получение последнего состояния Bidi — это двухэтапный процесс, включающий настройку обработчика событий для события OnBidiResponseReceived и вызов метода SendBidiQuery с допустимым запросом Bidi. В следующем фрагменте кода показан этот двухэтапный процесс.
PrinterQueue.OnBidiResponseReceived += new
EventHandler<PrinterQueueEventArgs>(OnBidiResponseReceived);
PrinterQueue.SendBidiQuery("\\Printer.consumables");
При получении ответа Bidi вызывается следующий обработчик событий. Этот обработчик событий также имеет измеченную реализацию состояния рукописного ввода, которая может оказаться полезной для разработки, если устройство недоступно. Объект PrinterQueueEventArgs включает как HRESULT, так и XML-ответ Bidi. Дополнительные сведения о xml-ответах Bidi см. в схемах запросов и ответов Bidi.
private void OnBidiResponseReceived(object sender, PrinterQueueEventArgs e)
{
if (e.StatusHResult != (int)HRESULT.S_OK)
{
MockInkStatus();
return;
}
//
// Display the ink levels from the data.
//
BidiHelperSource = new BidiHelper(e.Response);
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("BidiHelperSource"));
}
InkStatusTitle = "Ink status (Live data)";
}
Уведомления о принтерах
Уведомления принтера вызываются точно так же, как и параметры печати. В обработчике OnDriverEvent, если IPrinterExtensionEventArgs указывает, что Идентификатор ReasonID соответствует GUID DriverEvents, то мы можем создать интерфейс для обработки этого события.
Следующие переменные наиболее полезны при обработке функциональных уведомлений принтера.
PrinterExtensionEventArgs.BidiNotification — это несет XML-код Bidi, который вызвал активацию события.
PrinterExtensionEventArgs.DetailedReasonId — это идентификатор GUID eventID из xml-файла события драйвера.
Наиболее важным атрибутом объекта IPrinterExtensionEventArgs для уведомлений является свойство BidiNotification. Это несет XML-код Bidi, который вызвал активацию события. Дополнительные сведения о xml-ответах Bidi см. в схемах запросов и ответов Bidi.
Управление принтерами
Для поддержки роли расширения принтера в качестве приложения, которое можно использовать в качестве концентратора для управления и обслуживания принтеров, можно перечислить очереди печати, для которых зарегистрировано текущее расширение принтера, и получить состояние для каждой очереди. Это не показано в проекте PrinterExtensionSample, но следующий фрагмент кода можно добавить в метод Main App.xaml.cs для регистрации обработчика событий.
mgr.OnPrinterQueuesEnumerated += new EventHandler<PrinterQueuesEnumeratedEventArgs>(mgr_OnPrinterQueuesEnumerated);
После перечисления очередей вызывается обработчик событий и могут выполняться операции состояния. Это событие периодически запускается в течение времени существования приложения, чтобы убедиться, что список перечисленных очередей печати является текущим, даже если пользователь установил больше очередей после открытия. В результате важно, чтобы обработчик событий не создавал новое окно при каждом выполнении, и это отображается в следующем фрагменте кода.
static void mgr_OnPrinterQueuesEnumerated(object sender, PrinterQueuesEnumeratedEventArgs e)
{
foreach (IPrinterExtensionContext pContext in e)
{
// show status
}
}
Чтобы выполнить задачи обслуживания с помощью расширения принтера, корпорация Майкрософт рекомендует использовать устаревший API WritePrinter, как описано в следующем псевдокоде.
OpenPrinter
StartDocPrinter
StartPagePrinter
WritePrinter
EndPagePrinter
EndDocPrinter
ClosePrinter
Рекомендации по производительности расширения принтера
Чтобы обеспечить оптимальный пользовательский интерфейс, расширения принтера должны быть разработаны для максимально быстрой загрузки. Пример проекта расширения принтера — это приложение .NET, которое означает, что он встроен в промежуточный язык (IL), который должен быть скомпилирован во время выполнения в соответствующем формате для архитектуры собственного процессора. Во время установки корпорация Майкрософт рекомендует установить расширения принтера в соответствии с рекомендациями, чтобы убедиться, что приложение скомпилировано для собственной архитектуры системы. Дополнительные сведения о компиляции и установке кода см. в статье "Повышение производительности запуска для классических приложений".
Корпорация Майкрософт также рекомендует, чтобы расширения принтера откладывали задачи инициализации, такие как загрузка ресурсов, до тех пор, пока метод EnableEvents не был вызван. Это сводит к минимуму вероятность вызова приложения EnableEvents до 5-секундного ожидания расширений принтера.
После вызова OnDriverEvent расширения принтеров должны инициализировать пользовательский интерфейс и нарисовать его как можно быстрее, используя асинхронные методы, где это возможно, чтобы обеспечить скорость реагирования. Расширения принтера не должны зависеть от сетевых вызовов или Bidi, чтобы создать начальное состояние окна для настроек печати или уведомлений принтера.
Так как пользователь делает выбор с помощью пользовательского интерфейса на экране, влияющего на PrintTicket, расширение принтера должно использовать метод IPrintSchemaTicket::ValidateAsync, чтобы проверить изменения как можно раньше. Наконец, расширение принтера должно использовать метод IPrintSchemaTicket::CommitAsync , чтобы зафиксировать изменения PrintTicket.
Расширения принтера всегда выполняются вне процесса из вызываемого процесса. Поэтому при разработке расширения принтера необходимо учитывать поведение окна:
- Свойство WindowParent из IPrinterExtensionEventArgs указывает дескриптор окна, вызвавого приложение.
- Свойство WindowModal из IPrinterExtensionEventArgs указывает, следует ли модально запускать расширение принтера (в режиме печати).
В примере расширения принтера показано, как создать пользовательский интерфейс, который обычно запускается в качестве самого верхнего окна. Но в некоторых случаях пользовательский интерфейс не будет отображаться на переднем плане, например, когда процесс, который вызвал вызов пользовательского интерфейса, выполняется на другом уровне целостности или когда процесс компилируется для другой архитектуры процессора. В этом случае расширение принтера должно вызвать FlashWindowEx, чтобы запросить разрешение пользователя на переход на передний план, мигая значок на панели задач.
Связанные статьи
Общие сведения о привязке данных
Повышение производительности запуска для классических приложений