Udostępnij za pośrednictwem


Przewodnik projektowania interfejsu API aplikacji obsługi wydruku w wersji 3

Ten artykuł zawiera wskazówki i przykłady dla OEM i IHV drukarek, które implementują aplikację wsparcia drukowania wersji 3 (PSA) dla swojego urządzenia.

Terminologia

Termin Definicja
PSA Aplikacja do obsługi druku Aplikacja platformy UWP korzystająca z interfejsu API w tym dokumencie.
IPP Protokół drukowania internetowego. Używane z urządzenia klienckiego do interakcji z drukarką w celu pobierania i ustawiania preferencji drukowania oraz wysyłania dokumentu do wydrukowania.
Drukarka skojarzona z obsługą drukowania Fizyczna drukarka IPP połączona z PSA.
Drukarka IPP Drukarka obsługująca protokół IPP.
PDL Język opisu strony. Format, w którym dokument jest wysyłany do drukarki.
Drukarka PSA powiązana Fizyczna drukarka IPP skojarzona z aplikacją PSA.
FunkcjeUrządzeniaDrukującego Format dokumentu XML do definiowania możliwości drukarki.
PrintSupportExtension Zadanie w tle PSA odpowiedzialne za zapewnienie możliwości rozszerzenia ograniczeń drukarki.

Ten artykuł zawiera rozszerzenia w wersji 3 istniejącego publicznego interfejsu API aplikacji obsługi wydruku opisane w przewodniku projektowania aplikacji obsługi wydruku i Windows.Graphics.Printing.PrintSupport Namespace. Interfejs API PSA umożliwia producentom drukarek opracowywanie aplikacji do obsługi sprzętu, które mogą poprawić doświadczenia związane z drukowaniem dla użytkownika systemu Windows, korzystającego z wbudowanego sterownika klasy IPP firmy Microsoft, bez konieczności opracowywania niestandardowego sterownika. Elementy drukujące komunikują się z aplikacją PSA za pośrednictwem procesu brokera PSA.

Aby uzyskać więcej informacji, zobacz następujące artykuły:

Temat Opis
Przewodnik projektowania aplikacji obsługi wydruku Zawiera wskazówki i przykłady dla producentów OEM i niezależnych producentów sprzętu (IHV) drukarek, którzy wdrażają aplikację do obsługi wydruków (PSA) dla swoich urządzeń.
Przewodnik projektowania interfejsu API Print App w wersji 4 Zawiera wskazówki i przykłady dla producentów drukarek OEM oraz dostawców sprzętu IHV, którzy implementują aplikację wsparcia drukowania w wersji 4 (PSA) dla swoich urządzeń.
Specyfikacja manifestu MSIX dla wirtualnej drukarki wspierającej drukowanie Zawiera wskazówki i przykłady manifestu MSIX dla OEM-ów i IHV producentów drukarek, które implementują wirtualną drukarkę wspierającą drukowanie.
Obsługa wydruku — powiązanie aplikacji Zawiera wskazówki i przykłady dotyczące kojarzenia aplikacji do obsługi wydruku (PSA) z drukarką.

Istotne rozszerzenia interfejsu API są następujące:

  • Kompresja IPP — interfejs API PSA w wersji 3 dodaje funkcję, która ulepsza drukowanie IPP poprzez dodanie funkcji kompresji IPP w zadaniu drukowania dla drukarek IPP, które tę funkcję obsługują. Niektóre PSA mogą mieć niestandardową kompresję, co oznacza, że zadanie IPP jest podwójnie kompresowane, co wpływa na jego wydajność. Aby temu zapobiec, interfejs API PSA w wersji 3 wprowadza właściwość IsIppCompressionEnabled i funkcję DisableIppCompressionForJob, aby wyłączyć kompresję dla bieżącego zadania, jeśli jest to konieczne, w klasie uruchomieniowej PrintWorkflowJobStartingEventArgs (API PSA v1).

  • obsługa błędów zadań IPP i wyskakujący błąd — interfejs API PSA w wersji 3 wprowadza JobIssueDetected event w PrintWorkflowJobBackgroundSession (interfejs API PSA w wersji 1). Zdarzenie jest zgłaszane za każdym razem, gdy PSA wykryje błąd lub ostrzeżenie w zadaniu drukowania. PSA jest odpowiedzialny za wyświetlanie powiadomień o błędach użytkownikowi. Gdy PSA rejestruje się dla tego zdarzenia i ustawia właściwość SkipSystemErrorToast na prawda w PrintWorkflowJobIssueDetectedEventArgs, informuje system drukowania, aby nie wyświetlał powiadomienia o błędach systemu Windows. Interfejs API PSA w wersji 3 udostępnia również mechanizm PSA do uruchamiania UI, gdy użytkownik wchodzi w interakcję z powiadomieniem toast.

  • limity czasu komunikacji niestandardowej IPP — interfejs API PSA w wersji 3 udostępnia interfejs API, za pomocą którego PSA może nadpisać limity czasu IPP. Ponadto do klasy środowiska uruchomieniowego PrintSupportIppCommunicationConfiguration została dodana klasa PrintSupportPrintDeviceCapabilitiesChangedEventArgs w celu manipulowania limitami czasu komunikacji IPP. Ponadto interfejs API PSA w wersji 3 wprowadza zdarzenie, które jest zgłaszane w przypadku wystąpienia błędu podczas komunikacji IPP. Zdarzenie zostało wprowadzone tak, aby IHV mogło zbadać awarię i odpowiednio dostosować wartości limitu czasu.

  • Support IPPFaxOut – API PSA w wersji 3 dodaje funkcję do systemu drukowania, umożliwiając obsługę drukarek IPPFaxOut. Aby obsługiwać faks, PSA obsługuje filtr renderowania do konwersji XPS na TIFF. Ponieważ PSA może manipulować zawartością XPS przed przekonwertowaniem na TIFF, udostępnia wartość wyliczeniową XpsToTiff w PrintWorkflowPdlConversionType, dzięki czemu PSA może mieć dostęp do konwertera XPS do TIFF. Ponadto udostępnia właściwość IsIPPFaxOutPrinter dla klasy środowiska uruchomieniowego IppPrintDevice, dzięki czemu PSA może rozróżniać między standardową drukarką a drukarkami IPPFaxOut.

Wyłączanie kompresji IPP

Kompresja IPP jest pokazana w poniższym przykładzie kodu.

public sealed class PrintSupportWorkflowBackgroundTask : IBackgroundTask
{
    public BackgroundTaskDeferral TaskInstanceDeferral { get; set; }
    private PrintWorkflowJobBackgroundSession session;

    public void Run(IBackgroundTaskInstance taskInstance)
    {
        TaskInstanceDeferral = taskInstance.GetDeferral();

        if (taskInstance.TriggerDetails is PrintWorkflowJobTriggerDetails jobDetails)
        {
            session = jobDetails.PrintWorkflowJobSession;
            session.JobStarting += OnJobStarting;
            session.PdlModificationRequested += OnPdlModificationRequested;
            session.JobIssueDetected += OnJobIssueDetected;

            // Make sure to register all the event handlers before PrintWorkflowJobBackgroundSession.Start is called.
            session.Start();
        }
    }

    private void OnJobStarting(PrintWorkflowJobBackgroundSession sender, PrintWorkflowJobStartingEventArgs args)
    {
        using (args.GetDeferral())
        {
            // Skip system rendering.
            args.SetSkipSystemRendering();

            // If Ipp compression is enabled by the system, check to see if PSA does custom compression for the printer
            // and disable system compression if required.
            if (args.IsIppCompressionEnabled)
            {
                if (this.HasCustomCompression(args.Printer))
                {
                    args.DisableIppCompressionForJob();
                }
            }
        }
    }

    bool HasCustomCompression(IppPrintDevice device)
    {
        bool hasCustomCompression = false;
        // Check if the PSA does custom compression for the printer
        return hasCustomCompression;
    } 
} 

Obsługa błędów zadania IPP

W tym przykładzie pokazano, jak aplikacja PSA może rejestrować błędy zadań, wyświetlać powiadomienia o tych błędach oraz uruchamiać interfejs użytkownika, gdy użytkownik aktywuje powiadomienia.

public sealed class PrintSupportWorkflowBackgroundTask : IBackgroundTask
{
    public BackgroundTaskDeferral TaskInstanceDeferral { get; set; }
    private PrintWorkflowJobBackgroundSession session;

    public void Run(IBackgroundTaskInstance taskInstance)
    {
        TaskInstanceDeferral = taskInstance.GetDeferral();

        if (taskInstance.TriggerDetails is PrintWorkflowJobTriggerDetails jobDetails)
        {
            session = jobDetails.PrintWorkflowJobSession;
            session.JobStarting += OnJobStarting;
            session.PdlModificationRequested += OnPdlModificationRequested;
            session.JobIssueDetected += OnJobIssueDetected;

            // Make sure to register all the event handlers before PrintWorkflowJobBackgroundSession.Start is called.
            session.Start();
        }
    }

    private void OnJobIssueDetected (PrintWorkflowJobBackgroundSession sender, PrintWorkflowJobIssueDetectedEventArgs args)
    {
        // Using a deferral to exclude the background process from being suspended while PSA displays UI.
        Deferral deferral = args.GetDeferral();
        var toast = new ToastNotification(GetErrorToastXml(args.ExtendedError,
                args.JobIssueKind, args.PrinterJob, args.Configuration));
        toast.Activated += async (toastSender, e) =>
        {
            // PSA UI application 
            PrintWorkflowUICompletionStatus uiStatus = await args.UILauncher.LaunchAndCompleteUIAsync();
            // Complete deferral
            deferral.Complete();
        };
        toast.Dismissed += async (toastSender, e) => { deferral.Complete(); };
        toast.Failed += async (toastSender, e) => { deferral.Complete(); };
            
        ToastNotificationManager.CreateToastNotifier().Show(toast);
        args.SkipSystemErrorToast = true;
    }

    private XmlDocument GetErrorToastXml(Exception jobError, PrintWorkflowJobIssueKind issueKind,
        PrintWorkflowPrinterJob printerJob, PrintWorkflowConfiguration workflowConfig )
    {
        var errorToastXml = new XmlDocument();
        // Generate Toast Xml based on error information from Exception and PrintWorkflowPrinterJob.
        return errorToastXml;
    }
}

Ustawianie limitu czasu komunikacji protokołu IPP

W tym przykładzie pokazano, jak ustawić limit czasu komunikacji IPP.

public sealed class PrintSupportExtensionBackGroundTask : IBackgroundTask
{
    public BackgroundTaskDeferral TaskInstanceDeferral { get; set; }
    private PrintSupportExtensionSession session;

    public void Run(IBackgroundTaskInstance taskInstance)
    {
        taskInstance.Canceled += OnTaskInstanceCanceled;
        TaskInstanceDeferral = taskInstance.GetDeferral();

        if (taskInstance.TriggerDetails is PrintSupportExtensionTriggerDetails extensionDetails)
        {
            session = extensionDetails.Session;
            session.PrintTicketValidationRequested += OnSessionPrintTicketValidationRequested;
            session.PrintDeviceCapabilitiesChanged += OnSessionPrintDeviceCapabilitiesChanged;
            session.CommunicationErrorDetected += OnCommunicationErrorDetected;

            // Make sure to register all the event handlers before PrintSupportExtensionSession.Start is called.
            session.Start();
        }
    }

    private void OnTaskInstanceCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
    {
        session = null;
        TaskInstanceDeferral.Complete();
    }
    
    private void OnSessionPrintDeviceCapabilitiesChanged(PrintSupportExtensionSession sender, PrintSupportPrintDeviceCapabilitiesChangedEventArgs args)
    {
        // Using deferral to exclude the background process from being suspended while PSA updates the printer PDC and other configurations.
        using (args.GetDeferral())
        {
            if (args.CommunicationConfiguration.CanModifyTimeouts)
                {
                    this.UpdateAttributeTimeouts(args.CommunicationConfiguration, sender.Printer);
                    this.UpdateJobTimeouts(args.CommunicationConfiguration, sender.Printer);
                }
                // Do other operations, such as Updating PDC, PDR, and so on here.
        }
    }

    void UpdateAttributeTimeouts(PrintSupportIppCommunicationConfiguration config, IppPrintDevice device)
    {
        IppPrinterCommunicationKind communicationKind = config.CommunicationKind;
        PrintSupportIppCommunicationTimeouts currentTimeouts = config.IppAttributeTimeouts;

        // Adjust attribute timeouts based on the printer
        switch (communicationKind)
        {
            case IppPrinterCommunicationKind.Network:
                currentTimeouts.ConnectTimeout = TimeSpan.FromSeconds(10);
                currentTimeouts.SendTimeout = TimeSpan.FromSeconds(10);
                currentTimeouts.ReceiveTimeout = TimeSpan.FromSeconds(10);
                break;
            case IppPrinterCommunicationKind.UniversalPrint:
                // adjust timeout for universal printer
                break;
        }
        
    }

    void UpdateJobTimeouts(
        PrintSupportIppCommunicationConfiguration config, IppPrintDevice device)
    {
        IppPrinterCommunicationKind communicationKind = config.CommunicationKind;
        PrintSupportIppCommunicationTimeouts currentTimeouts = config.IppJobTimeouts;
        // Adjust job timeouts based on the printer and communication type
        switch (communicationKind)
        {
            case IppPrinterCommunicationKind.Network:
                currentTimeouts.ConnectTimeout = TimeSpan.FromSeconds(30);
                currentTimeouts.SendTimeout = TimeSpan.FromSeconds(30);
                currentTimeouts.ReceiveTimeout = TimeSpan.FromSeconds(30);
                break;
            case IppPrinterCommunicationKind.UniversalPrint:
                // adjust timeout for universal printer
                break;
        }
    }
}

Obsługa błędów komunikacji IPP

W tym przykładzie pokazano, jak obsługiwać błędy komunikacji IPP.

public sealed class PrintSupportExtensionBackGroundTask : IBackgroundTask
{
    public BackgroundTaskDeferral TaskInstanceDeferral { get; set; }
    private PrintSupportExtensionSession session;
    public void Run(IBackgroundTaskInstance taskInstance)
    {
        taskInstance.Canceled += OnTaskInstanceCanceled;
        TaskInstanceDeferral = taskInstance.GetDeferral();

        if (taskInstance.TriggerDetails is PrintSupportExtensionTriggerDetails extensionDetails)
        {
            session = extensionDetails.Session;
            session.PrintTicketValidationRequested += OnSessionPrintTicketValidationRequested;
            session.PrintDeviceCapabilitiesChanged += OnSessionPrintDeviceCapabilitiesChanged;
            session.CommunicationErrorDetected += OnCommunicationErrorDetected ;

            // Make sure to register all the event handlers before PrintSupportExtensionSession.Start is called.
            session.Start();
        }
    }

    private void OnTaskInstanceCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
    {
        session = null;
        TaskInstanceDeferral.Complete();
    }

    private void OnCommunicationErrorDetected(PrintSupportExtensionSession sender, PrintSupportCommunicationErrorDetectedEventArgs args)
    {
        // Using deferral to exclude the background process from being suspended while PSA updates the timeouts.
        using (args.GetDeferral())
        {

            if (args.ErrorKind == IppCommunicationErrorKind.Timeout)
            {
                PrintSupportIppCommunicationConfiguration ippConfig = args.CommunicationConfiguration;
                IppPrintDevice device = sender.Printer;
                // Update timeout based on the communication error
            }
        }
    }  
}

Drukowanie na drukarce faksowej IPP w PSA

W tym przykładzie pokazano, jak drukować na drukarce IPPFaxOut w PSA.

public sealed class PrintSupportWorkflowBackgroundTask : IBackgroundTask
{
    public BackgroundTaskDeferral TaskInstanceDeferral { get; set; }
    private PrintWorkflowJobBackgroundSession session;

    public void Run(IBackgroundTaskInstance taskInstance)
    {
        TaskInstanceDeferral = taskInstance.GetDeferral();

        if (taskInstance.TriggerDetails is PrintWorkflowJobTriggerDetails jobDetails)
        {
            session = jobDetails.PrintWorkflowJobSession;
            session.JobStarting += OnJobStarting;
            session.PdlModificationRequested += OnPdlModificationRequested;
            session.JobIssueDetected += OnJobIssueDetected;

            // Make sure to register all the event handlers before PrintWorkflowJobBackgroundSession.Start is called.
            session.Start();
        }
    }

    private async void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession sender, PrintWorkflowPdlModificationRequestedEventArgs args)
    {
        using (args.GetDeferral())
        {
            IppPrintDevice printer = args.PrinterJob.Printer;
            IInputStream xpsContent = args.SourceContent.GetInputStream();
            WorkflowPrintTicket printTicket = args.PrinterJob.GetJobPrintTicket();

            string documentFormat = this.GetPrinterDocumentFormat(printer);
            PrintWorkflowPdlTargetStream targetStream = args.CreateJobOnPrinter(documentFormat);
            IInputStream xpsSourceContent = xpsContent;
            if (printer.IsIPPFaxOutPrinter)
            {                    
                // Add cover page to XPS source document   
                xpsSourceContent = this.AddCoverPageToXpsContent(xpsContent);
            }
            
            PrintWorkflowPdlConverter pdlConverter;
            switch (documentFormat.ToLowerInvariant())
            {
                case "image/pwg-raster":
                    pdlConverter = args.GetPdlConverter(PrintWorkflowPdlConversionType.XpsToPwgr);
                    break;
                case "application/pclm":
                    pdlConverter = args.GetPdlConverter(PrintWorkflowPdlConversionType.XpsToPclm);
                    break;
                case "application/pdf":
                    pdlConverter = args.GetPdlConverter(PrintWorkflowPdlConversionType.XpsToPdf);
                    break;
                case "image/tiff":
                    pdlConverter = args.GetPdlConverter(PrintWorkflowPdlConversionType.XpsToTiff);
                    break;
                default:
                    // This should not happen, aborting workflow if PSA does not identify the supported PDLs
                    args.Configuration.AbortPrintFlow(PrintWorkflowJobAbortReason.JobFailed);
                    return;
                }
                // Use pdlConverter to convert the source XPS stream to printer's document format and send it to the printer using targetStream.
                await pdlConverter.ConvertPdlAsync(printTicket, xpsSourceContent, targetStream.GetOutputStream());

                targetStream.CompleteStreamSubmission(PrintWorkflowSubmittedStatus.Succeeded);
            }
        }

    private IInputStream AddCoverPageToXpsContent(IInputStream xpsStream)
    {
        var coverPageXps = new InMemoryRandomAccessStream();
        // Add cover page to XPS content and write to coverPageXps stream
        return coverPageXps;
    }
}

Wyłączanie interfejsu użytkownika faksu dla numeru telefonu z PSA

W przypadku obsługi drukarki faksu IPP ten przykład przedstawia interfejs użytkownika, który prosi użytkownika o wprowadzenie numeru faksu podczas drukowania na drukarce faksu. Ale PSA może chcieć pokazać swój własny interfejs użytkownika z więcej informacji i opcji. Ponieważ posiadanie dwóch interfejsów użytkownika dla faksu może być mylące dla użytkownika, ten przykład pokazuje opcję dla PSA, która umożliwia wyłączenie interfejsu użytkownika systemu, gdy chce pokazać swój interfejs użytkownika faksu.

W poniższym przykładzie pokazano użycie interfejsu API.

public sealed class PrintSupportWorkflowBackgroundTask : IBackgroundTask
{

    private void OnJobStarting(PrintWorkflowJobBackgroundSession sender, PrintWorkflowJobStartingEventArgs args)
    {
        using (args.GetDeferral())
        {
            // If the job is printing to an Ipp fax printer,
            // check whether PSA has a custom UI and disable system UI for getting the fax number.
            if (args.IsIPPFaxOutPrinter)
            {
                if (this.HasCustomUIForFax(args.Printer))
                {
                    args.SkipSystemFaxUI = true;
                }
            }
        }
    }

    bool HasCustomUIForFax(IppPrintDevice device)
    {
        bool hasCustomUIForFax = false;
        // Check if the PSA does custom UI for the given fax printer.
        return hasCustomUIForFax;
    }  
}

Uwagi

Przykłady w tym artykule są oparte na przykładach interfejsów API PSA w wersji 1 i 2 w przewodniku projektowania aplikacji Print z założeniem, że deweloper zna przepływ pracy interfejsu API PSA.

Ten artykuł zawiera rozszerzenia istniejącego publicznego interfejsu API Aplikacji Wsparcia Druku, opisanego w przewodniku projektowania aplikacji wsparcia druku i Windows.Graphics.Printing.PrintSupport Przestrzeni nazw. Interfejs API PSA umożliwia producentom drukarek tworzenie aplikacji platformy UWP w celu usprawnienia środowiska drukowania użytkowników systemu Windows podczas korzystania ze sterownika klasy IPP firmy Microsoft, bez konieczności opracowywania niestandardowego sterownika.

Składniki drukowania komunikują się z aplikacją PSA za pośrednictwem procesu brokera PSA.

WyłączKompresjęIppDlaZadania

koniec planu obsługi sterowników drukarek innych firm w systemie Windows

IsIppCompressionEnabled

IsIPPFaxOutPrinter

IppPrintDevice

ProblemWykrytyZadanie

PrintSupportIppCommunicationConfiguration

PrintSupportPrintDeviceCapabilitiesChangedEventArgs

PrintWorkflowJobBackgroundSession

PrintWorkflowJobIssueDetectedEventArgs

PrintWorkflowJobStartingEventArgs

PrintWorkflowPdlConversionType

SkipSystemErrorToast

Windows.Graphics.Print.PrintSupport