次の方法で共有


印刷サポート アプリケーション v3 API 設計ガイド

この記事では、デバイスの v3 印刷サポート アプリ (PSA) を実装しているプリンター OEM および IHV のガイダンスと例を示します。

用語

用語 定義
PS4 印刷サポート アプリケーション。 このドキュメントの API を使用する UWP アプリ。
IPP インターネット印刷プロトコル。 クライアント デバイスからプリンターを操作して印刷設定を取得および設定し、印刷するドキュメントを送信するために使用します。
印刷サポートが関連付けられているプリンター PSA にリンクされている物理 IPP プリンター。
IPP プリンター IPP プロトコルをサポートするプリンター。
PDL ページの説明言語。 文書がプリンターに送信される形式。
関連付けされた PSA プリンター PSA アプリケーションに関連付けられている物理 IPP プリンター。
印刷デバイスの機能 プリンター機能を定義するための XML ドキュメント形式。
プリントサポートエクステンション プリンター制約拡張機能の提供を担当する PSA バックグラウンド タスク。

この記事には、「印刷サポート アプリの設計ガイド」および「Windows.Graphics.Printing.PrintSupport 名前空間の 」で説明されている既存の印刷サポート アプリケーションパブリック API に対する v3 拡張機能が含まれています。 PSA API を使用すると、プリンターの製造元は、カスタム ドライバーを開発しなくても、受信トレイ Microsoft IPP クラス ドライバーを使用しながら Windows ユーザーの印刷エクスペリエンスを強化できるハードウェア サポート アプリを開発できます。 印刷コンポーネントは、PSA ブローカー プロセスを通じて、PSA アプリと通信します。

詳細については、次の記事を参照してください。

トピック 説明
印刷サポート アプリの設計ガイド デバイスの印刷サポート アプリ (PSA) を実装しているプリンター OEM および IHV のガイダンスと例を示します。
印刷サポート アプリ v4 API 設計ガイド デバイスの v4 印刷サポート アプリ (PSA) を実装しているプリンター OEM および IHV のガイダンスと例を示します。
"印刷サポート仮想プリンタ用MSIXマニフェスト仕様" 印刷サポート仮想プリンターを実装しているプリンター OEM および IHV の MSIX マニフェスト ガイダンスと例を提供します。
印刷サポート アプリの関連付け 印刷サポート アプリ (PSA) をプリンターに関連付けるガイダンスと例を示します。

API の重要な拡張機能は次のとおりです。

  • IPP 圧縮 -PSA v3 API は、この機能をサポートする IPP プリンターの印刷ジョブに IPP 圧縮機能を追加することで、IPP 印刷を強化する機能を追加します。 一部のPSAはカスタム圧縮を持つ可能性があり、これはIPPジョブがパフォーマンスに影響を与える二重圧縮を有することを意味する。 これを軽減するために、PSA v3 API では、PrintWorkflowJobStartingEventArgs (PSA v1 API) ランタイム クラスに、IsIppCompressionEnabled プロパティと DisableIppCompressionForJob 関数 (必要に応じて現在のジョブの圧縮を無効にする) が導入されています。

  • IPP ジョブ エラー処理とエラー トースト - PSA v3 API では、PrintWorkflowJobBackgroundSession (PSA v1 API) ランタイム クラスに JobIssueDetected イベントが導入されています。 このイベントは、印刷ジョブでPSAがエラー/警告を検出するたびに発生します。 PSAは、その後、エラー トーストをユーザーに表示する役割を担います。 PSA がこのイベントに登録し、の SkipSystemErrorToast プロパティを で true に設定すると、PrintWorkflowJobIssueDetectedEventArgsで Windows 印刷システムトーストを表示しないように印刷システムに指示します。 また、PSA v3 API は、ユーザーがトーストを操作するときに、PSA が UI を起動するためのメカニズムも提供します。

  • カスタム IPP 通信タイムアウト - PS v3 API は、PSA が IPP タイムアウトをオーバーライドできる API を提供します。 さらに、PrintSupportIppCommunicationConfiguration ランタイム クラスが、IPP 通信タイムアウトを操作するための PrintSupportPrintDeviceCapabilitiesChangedEventArgs に追加されます。 さらに、PSA v3 API では、IPP 通信でエラーが発生したときに発生するイベントが導入されます。 IHV が障害を調査し、それに応じてタイムアウト値を調整できるように、イベントが導入されました。

  • サポート IPPFaxOut - PSA v3 API は、IPPFaxOut プリンターをサポートする機能を印刷システムに追加します。 FAX をサポートするために、PSA は XPS を Tiff に変換するレンダリング フィルターをサポートしています。 TIFF に変換する前に XPS コンテンツを操作する場合があるため、PrintWorkflowPdlConversionType で XpsToTiff 列挙値を提供し、XPS から TIFF へのコンバーターにアクセスできるようにします。 さらに、PSA が標準プリンターと IPPFaxOut プリンターを区別できるように、ISIPPFaxOutPrinter プロパティを IPpPrintDevice ランタイム クラスに提供します。

IPP 圧縮の無効化

IPP 圧縮を次のコード サンプルに示します。

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

IPP ジョブエラー処理

このサンプルでは、PSA アプリでジョブ エラーを登録し、ジョブ エラーのトーストを表示し、ユーザーがトーストをアクティブ化したときに UI を起動する方法を示します。

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

IPP 通信タイムアウトの設定

このサンプルでは、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;
        }
    }
}

IPP 通信エラーの処理

このサンプルでは、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
            }
        }
    }  
}

PSA で IPP FAX 出力プリンターに印刷する

このサンプルでは、PSA の IPPFaxOut プリンターに印刷する方法を示します。

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

PSAで電話番号のFAX UIを無効にする

IPP FAX プリンターのサポートにより、このサンプルでは、FAX プリンターに印刷するときにユーザーに FAX 番号の入力を要求する UI を示します。 しかし、PSAは、より多くの情報とオプションを持つ独自のUIを表示したいかもしれません。 ユーザーが混乱する可能性があるため、FAX 用の UI が2つ存在する場合、このサンプルでは、FAX 用 UI を表示する際にシステム UI を無効にするオプションを示しています。

次の例では、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;
    }  
}

Remarks

この記事のサンプルは、Print サポート アプリ設計ガイドのPSA v1 および v2 API のサンプルに基づいて構築されています。これは、開発者がPSA API ワークフローに精通していることを前提に されています。

この記事には、「印刷サポート アプリの設計ガイド」および「Windows.Graphics.Printing.PrintSupport 名前空間 説明されている既存のパブリック印刷サポート アプリケーション API の拡張機能が含まれています。 PSA API を使用すると、プリンターの製造元は、カスタム ドライバーを開発することなく、受信トレイ Microsoft IPP クラス ドライバーを使用しながら Windows ユーザーの印刷エクスペリエンスを強化できる UWP アプリを開発できます。

印刷コンポーネントは、PSA ブローカー プロセスを通じて、PSA アプリと通信しています。

DisableIppCompressionForJob

Windows でのサード パーティ製プリンター ドライバーのサービス終了プラン

IsIppCompressionEnabled

IsIPPFaxOutPrinter

IppPrintDevice

JobIssueDetected

PrintSupportIppCommunicationConfiguration

PrintSupportPrintDeviceCapabilitiesChangedEventArgs

PrintWorkflowJobBackgroundSession

PrintWorkflowJobIssueDetectedEventArgs

PrintWorkflowJobStartingEventArgs

PrintWorkflowPdlConversionType

SkipSystemErrorToast

Windows.Graphics.Printing.PrintSupport