次の方法で共有


印刷サポート アプリ v1 および v2 の設計ガイド

この記事では、Windows ユーザーの印刷エクスペリエンスをいくつかの方法で強化できる印刷サポート アプリ (PSA) を開発するための、プリンター OEM と IHV のガイダンスと例を示します。

重要

Windows 11 SDK (22000.1) のリリース以降、プリンター用の UWP アプリを開発するための推奨される方法は、印刷サポート アプリ (PSA) です。 印刷デバイス用の印刷サポート アプリを開発するには、対象の Windows バージョン用の Windows 11 SDK をダウンロードしてインストールします。

重要

この記事には、Windows 11 バージョン 22H2 以降で使用できる PSA 機能について説明するセクションが含まれています。 これらのセクションには、そのバージョンに適用されることを示すメモが含まれています。

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

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

一部のプリンター機能は、製造元アプリのヘルプを正しく構成する必要がある特別な機能であるため、Windows によって表示される印刷ダイアログには表示されません。 プリンターの既定の機能では提供されない機能である場合もあります。

プリンター固有の機能は、ユーザーがオプションを選択し、そのシナリオに関連するすべての機能が自動的に正しい値に設定されることを信頼できるようにする方法でグループ化できます。 たとえば、インクセーバー、用紙セーバー、最高品質のモードのいずれかを選択できます。これは、ユーザーからの 1 つの選択に基づいてさまざまな印刷機能を自動的に操作できます。 Windows では、すべてのプリンター モデルのすべてのカスタム機能を理解する必要がある場合、それらを自動的にグループ化することはできません。

このカスタム印刷設定を表示する必要性は、この API によって対処されます。オプションの UWP 拡張コントラクトを使用して、Windows から提供されるすべての Windows 印刷ダイアログと、Windows で提供される API を使用するカスタム印刷ダイアログからユーザーがアクティブ化できます。 製造元は、ユーザーが所有する特定のプリンターに最適な印刷エクスペリエンスを提供するように UI を調整できます。

プリンターメーカーが改善し、差別化できるもう1つの領域は、印刷品質です。 製造元は、特定のプリンターのコンテンツを最適化することで、レンダリング後の印刷品質を向上させることができます。 また、プリンター固有の機能を考慮に入れる可能性があるため、最終的な出力をより適切に表す忠実度の高いプレビューを提供することもできます。

印刷サポートアプリの印刷タイムライン

用語

用語 定義
PS4 印刷サポート アプリケーション。 この記事で説明する API を使用する UWP アプリ。
MPD モダンプリント ダイアログ。 これは、アプリが Windows.Graphics.Printing API を使用して印刷しているときにユーザーに表示されます。
CPD 一般的な印刷ダイアログ。 これは、アプリが Win32 API を使用して印刷するときにユーザーに表示されます。 印刷プレビューを表示する必要があるアプリは、このダイアログをトリガーせず、ダイアログ自体のバージョンを実装します。 Office アプリは、この主要な例です。
IPP インターネット印刷プロトコル。 クライアント デバイスからプリンターを操作して印刷設定を取得および設定し、印刷するドキュメントを送信するために使用します。
印刷サポートが関連付けられているプリンター PSA にリンクされているプリンター。
IPP プリンター IPP プロトコルをサポートするプリンター。
その他の設定 パートナーが提供するアプリ UI を MPD で開くリンク。 既定では、PSA がインストールされていない場合に、組み込みの印刷設定 UI を開きます。
プリンターの基本設定 UI 印刷時に適用される既定のプリンター オプションを設定するために使用されるダイアログ。 たとえば、向き、用紙サイズ、色、両面印刷などです。
PDL ページの説明言語。 文書がプリンターに送信される形式。
関連付けされた PSA プリンター PSA アプリケーションに関連付けられている物理 IPP プリンター。
印刷デバイスの機能 プリンター機能を定義するための XML ドキュメント形式。 詳細については、「印刷チケットと印刷機能のテクノロジ」を参照してください。
PrintTicket 特定の印刷ジョブに対するユーザーの意図をキャプチャするために使用される、さまざまな印刷関連機能とその値のコレクション。
PrintSupportExtension プリンター制約拡張機能の提供を担当する PSA バックグラウンド タスク。

これらのサンプルは、printsupport 名前空間を参照します。これは次のように定義されています。

    xmlns:printsupport="http://schemas.microsoft.com/appx/manifest/printsupport/windows10"

ユーザーが文書を印刷しようとしている場合、多くの場合、印刷に使用する設定を設定する必要があります。 たとえば、文書を横向きで印刷することを選択できます。 また、プリンターでサポートされているカスタム機能を利用することもできます。 Windows にはカスタム設定を表示する既定の UI が用意されていますが、適切なアイコンや説明がないため、ユーザーはそれらを理解できない場合があります。 Windows では、間違った UI コントロールを使用して表示している可能性もあります。 このようなカスタム機能は、その機能を完全に理解するアプリによって最適に提示されます。 これは、プリンターの製造元が作成するさまざまなプリンター モデルに合わせて調整されたアプリを作成できる API を提供する背後にある動機です。

windows.printSupportSettingsUI という名前の新しいカテゴリを使用して、新しい UAP 拡張コントラクトが作成されます。 このコントラクトでアクティブ化されたアプリは、PrintSupportSettingsUI という新しい ActivationKind を受け取ります。 このコントラクトには、新しい機能は必要ありません。

<Extensions>
    <printsupport:Extension Category="windows.printSupportSettingsUI" 
        EntryPoint="PsaSample.PsaSettingsUISample"/>
</Extensions>

このコントラクトは、ユーザーが MPD で [その他の設定] 選択するか、CPD で [基本設定] 選択したときに呼び出されます。 このコントラクトは、設定アプリの 印刷環境設定 から呼び出すこともできます。 契約がアクティブになると、アプリは、現在の PrintTicket および PrintDevice オブジェクトを取得するために使用できる、PrintSupportSettingsUISession オブジェクトを受け取ります。 PrintDevice オブジェクトは、プリンターと通信してプリンターとジョブの属性を受け取るために使用できます。 その後、アプリは、プリンターの適切なオプションを含む UI をユーザーに表示できます。 ユーザーが選択を行い、[OK]選択すると、アプリケーションは印刷チケットを変更し、検証してから、PrintSupportPrintTicketTarget オブジェクトを使用して送信し直す場合があります。 ユーザーが基本設定ウィンドウを取り消す場合は、変更を破棄し、PrintSupportSettingsUISession オブジェクトから取得した遅延を完了して、アプリケーションを終了する必要があります。

印刷サポート アプリは、異なる印刷ジョブに対して複数の同時アクティブ化を処理することが想定されているため、このようなアプリでは、package.appxmanifest ファイル内の SupportsMultipleInstances 要素を使用して複数のインスタンスをサポートする必要があります。 これを行わないと、ある印刷ジョブの環境設定を確認すると、開いている可能性のある他の環境設定ウィンドウが閉じる可能性があります。 ユーザーは、これらの環境設定ウィンドウをもう一度開く必要があります。

次のシーケンス図は、設定 UI の印刷チケット操作の概念を表しています。

設定 U I 印刷チケット操作のシーケンス図 チケット操作を印刷する

設定 UI での PrintTicket の変更

任意の印刷ダイアログ (MPD/CPD またはカスタム印刷ダイアログ) またはシステム設定から起動したときに設定 UI をアクティブ化するための C# サンプル コード:

namespace PsaSampleApp
{
    sealed partial class App : Application
    {
        Deferral settingsDeferral;
        protected override void OnActivated(IActivatedEventArgs args)
        {
            if (args.Kind == ActivationKind.PrintSupportSettingsUI)
           {
                // Get the activation arguments
                var settingsEventArgs = args as PrintSupportSettingsActivatedEventArgs;
                PrintSupportSettingsUISession settingsSession = settingsEventArgs.Session;
                // Take deferral
                this.settingsDeferral = settingsEventArgs.GetDeferral();

                // Create root frame
                var rootFrame = new Frame();
                
        // Choose the page to be shown based upon where the application is being launched from
                switch (settingsSession.LaunchKind)
                {
                    case SettingsLaunchKind.UserDefaultPrintTicket:
                    {
                        // Show settings page when launched for default printer settings
                        rootFrame.Navigate(typeof(DefaultSettingsView), settingsSession);
                    }
                    break;
                    case SettingsLaunchKind.JobPrintTicket:
                    {
               // Show settings page when launched from printing app
                       rootFrame.Navigate(typeof(JobSettingsView), settingsSession);
                    }
                    break;
                }
                
   
                Window.Current.Content = rootFrame; 
            }
        }

        internal void ExitSettings()
        {
            settingsDeferral.Complete();
        } 
    }
}

DefaultSettingsView クラスの XAML:

<Page
    x:Class="PsaSampleApp.DefaultSettingsView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:PsaSampleApp"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0"  Orientation="Vertical" Margin="30,50,0,0">
           <ComboBox x:Name="OrientationOptions" ItemsSource="{x:Bind OrientationFeatureOptions}" SelectedItem="{x:Bind SelectedOrientationOption, Mode=TwoWay}" DisplayMemberPath="DisplayName" HorizontalAlignment="Left" Height="Auto" Width="Auto" VerticalAlignment="Top"/>
       </StackPanel>

        <StackPanel Grid.Row="1" Orientation="Horizontal">
            <Button x:Name="Ok" Content="Ok" HorizontalAlignment="Left" Margin="50,0,0,0" VerticalAlignment="Top" Click="OkClicked"/>
            <Button x:Name="Cancel" Content="Cancel" HorizontalAlignment="Left" Margin="20,0,0,0" VerticalAlignment="Top" Click="CancelClicked"/>
        </StackPanel>
    </Grid>
</Page>

UI を表示して PrintTicket を変更するための C# サンプル コード:

namespace PsaSampleApp
{
    /// <summary>
    /// Class for showing print settings to the user and allow user to modify it
    /// </summary>
    public sealed partial class DefaultSettingsView: Page
    {
        private IppPrintDevice printer;
        private PrintSupportSettingsUISession uiSession;
        private WorkflowPrintTicket printTicket;
        private App application;
        // Bound to XAML combo box
        public ObservableCollection<PrintTicketOption> OrientationFeatureOptions { get; } = new ObservableCollection<PrintTicketOption>();
        public PrintTicketOption SelectedOrientationOption { get; set; }  

        public SettingsView()
        {
            this.InitializeComponent();
            this.application = Application.Current as App;
            this.orientationFeatureOptions = new ObservableCollection<PrintTicketOption>();
        }

        internal void OnNavigatedTo(NavigationEventArgs e)
        {
            this.uiSession = = e.Parameter as PrintSupportSettingsUISession;
            this.printer = session.SessionInfo.Printer;
            this.printTicket = session.SessionPrintTicket;
            
            PrintTicketCapabilities printTicketCapabilities = this.printTicket.GetCapabilities();

            // Read orientation feature from PrintTicket capabilities
            PrintTicketFeature feature = printTicketCapabilities.PageOrientationFeature;
            // Populate XAML combo box with orientation feature options
            this.PopulateOrientationOptionComboBox(feature.Options); 

            PrintTicketOption printTicketOrientationOption = printTicket.PageOrientationFeature.GetSelectedOption();
            // Update orientation option in XAML combo box
            this.SelectedOrientationOption = this.orientationFeatureOptions.Single((option)=> (option.Name == printTicketOrientationOption.Name && option.XmlNamespace == printTicketOrientationOption.XmlNamespace));
        }

        private async void OkClicked(object sender, RoutedEventArgs e)
        {
            // Disable Ok button while the print ticket is being submitted
            this.Ok.IsEnabled = false;

            // Set selected orientation option in the PrintTicket and submit it
            PrintTicketFeature orientationFeature = this.printTicket.PageOrientationFeature;
            orientationFeature.SetSelectedOption(this.SelectedOrientationOption);
            // Validate and submit PrintTicket
            WorkflowPrintTicketValidationResult result = await printTicket.ValidateAsync();
            if (result.Validated)
            {
                // PrintTicket validated successfully – submit and exit
                this.uiSession.UpdatePrintTicket(printTicket);
                this.application.ExitSettings();
            }
            else
            {
                this.Ok.IsEnabled = true;
                // PrintTicket is not valid – show error
                this.ShowInvalidPrintTicketError(result.ExtendedError);
            }
        }

        private void CancelClicked(object sender, RoutedEventArgs e)
        {
            this.application.ExitSettings();
        }
    }
}

プリンター デバイスからプリンター属性を取得する

IPP プリンターから get-printer-attributes クエリへの WireShark 応答:

I P P プリンターからのプリンター属性取得クエリへのWiresharkの応答

プリンターからインク名とインク レベルを取得するための C# サンプル コード:

namespace PsaSampleApp
{
    /// <summary>
    /// Class for showing print settings to the user
    /// </summary>
    public sealed partial class SettingsView : Page
    { 
       IList<string> inkNames;
       IList<int> inkLevels;
        
        private async void GetPrinterAttributes()
        {
            // Read ink names and levels, along with loaded media-sizes
            var attributes = new List<string>();
            attributes.Add("marker-names");
            attributes.Add("marker-levels");
            attributes.Add("media-col-ready");
            IDictionary<string, IppAttributeValue> printerAttributes = this.printer.GetPrinterAttributes(attributes);

            IppAttributeValue inkNamesValue = printerAttributes["marker-names"];
            CheckValueType(inkNamesValue, IppAttributeValueKind.Keyword);
            this.inkNames = inkNamesValue.GetKeywordArray();
            
            IppAttributeValue inkLevelsValue = printerAttributes["marker-levels"];
            CheckValueType(inkLevelsValue, IppAttributeValueKind.Integer);
            this.inkLevels = inkLevelsValue.GetIntegerArray();
    
            // Read loaded print media sizes
        IppAttributeValue mediaReadyCollectionsValue = printerAttributes["media-col-ready"];
            foreach (var mediaReadyCollection in mediaReadyCollectionsValue.GetCollectionArray())
            {
                IppAttributeValue mediaSizeCollection;
                if (mediaReadyCollection.TryGetValue("media-size", out mediaSizeCollection))
                {
                    var xDimensionValue = mediaSizeCollection.GetCollectionArray().First()["x-dimension"];
                    var yDimensionValue = mediaSizeCollection.GetCollectionArray().First()["y-dimension"];
                    CheckValueType(xDimensionValue, IppAttributeValueKind.Integer);
                    CheckValueType(yDimensionValue, IppAttributeValueKind.Integer);
                    int xDimension = xDimensionValue.GetIntegerArray().First();
                    int yDimension = yDimensionValue.GetIntegerArray().First();
                    this.AddMediaSize(xDimension, yDimension);
                }
            }
        }

        private void CheckValueType(IppAttributeValue value, IppAttributeValueKind expectedKind)
        {
            if (value.Kind != expectedKind)
            {
                throw new Exception(string.Format("Non conformant type found: {0}, expected: {1}", value.Kind, expectedKind));
            }
        }
    }
}

プリンターでのプリンター属性の設定

プリンター属性を設定するための C# サンプル コード:

int defaultResolutionX = 1200;
int defaultResolutionY = 1200;
string pdlFormat = "image/pwg-raster";
private async void SetPrinterAttributes()
{
    var attributes = new Dictionary<string, IppAttributeValue>();
    attributes.Add("document-format-default", IppAttributeValue.CreateKeyword(this.pdlFormat));
    var resolution = new IppResolution(this.defaultResolutionX, this.defaultResolutionY, IppResolutionUnit.DotsPerInch);
    attributes.Add("printer-resolution-default", IppAttributeValue.CreateResolution(resolution));
            
    var result = this.printer.SetPrinterAttributes(attributes);
    if (!result.Succeeded)
    {
        foreach (var attributeError in result.AttributeErrors)
        {
            var attributeName = attributeError.Key;
            switch (attributeError.Value.Reason)
            {
            case IppAttributeErrorReason.AttributeValuesNotSupported:
                var values = attributeError.Value.GetUnsupportedValues().First();
                this.LogUnSupportedValues(attributeName, values);
                break;
            case IppAttributeErrorReason.AttributeNotSettable:
                this.LogAttributeNotSettable(attributeName);
                break;
            case IppAttributeErrorReason.AttributeNotSupported:
                this.LogAttributeNotSupported(attributeName);
                break;
            case IppAttributeErrorReason.RequestEntityTooLarge:
                this.LogAttributeNotEntityTooLarge(attributeName);
                break;
            case IppAttributeErrorReason. ConflictingAttributes:
                this.LogConflictingAttributes(attributeName);
                break;
            }
        }
    }
}

プリンター制約の拡張

印刷サポート アプリでは、カスタム PrintTicket 検証と既定の PrintTicket の定義がサポートされています。 このセクションでは、これらの機能をサポートする方法について説明します。

プリンター拡張機能の制約をサポートするために、新しいバックグラウンド タスクの種類である PrintSupportExtension が実装されました。 Package.appxmanifest には、次に示すように、印刷サポート拡張機能の機能拡張エントリがあります。

<Extensions>
    <printsupport:Extension Category="windows.printSupportExtension" 
        EntryPoint="PsaBackgroundTasks.PrintSupportExtension"/>
</Extensions>

このサービスは、関連付けられている IPP プリンターの印刷ジョブ内の任意の時点で実行できます。 IBackgroundTaskInstance関数によって印刷サポート拡張機能がアクティブ化されると、IBackgroundTaskInstance のインスタンスが PrintSupportExtensionExtension に与えられ、PrintSupportExtensionTriggerDetails ランタイム クラスへのアクセスが提供されます。このランタイム クラスは、内部的に PrintSupportExtensionSession をプロパティとして提供します。 PrintSupportExtension バックグラウンド クラスは、セッション オブジェクトを使用して、カスタム機能を提供するイベントに登録できます。

  1. event Windows.Foundation.TypedEventHandler<PrintSupportExtensionSession, PrintSupportPrintTicketValidationRequestedEventArgs>; PrintTicketValidationRequested;

    Print サポート拡張機能が独自の PrintTicket 検証メカニズムを提供する場合は、このイベントに登録できます。 PrintTicket を検証する必要があるときはいつでも、印刷システムではこのイベントが発生します。 PrintSupportExtension は、EventArgs 内で検証する必要がある現在の PrintTicket を取得します。 PrintSupportExtension バックグラウンド クラスは、PrintTicket の有効性を確認し、競合を解決するように変更できます。 PrintSupportExtension バックグラウンド クラスは、PrintTicket が解決されたか、競合しているか、無効であるかを示すために、SetPrintTicketResult 関数 使用して検証の結果を設定する必要があります。 このイベントは、印刷ジョブの有効期間中にいつでも発生させることができます。 PrintSupportExtension クラスがこのイベントに登録されない場合、印刷システムは PrintTicket の独自の検証を実行します。

  2. event Windows.Foundation.TypedEventHandler<PrintSupportExtensionSession, PrintSupportPrintDeviceCapabilitiesChangedEventArgs>; PrintDeviceCapabilitiesChanged;

    印刷システムが、関連付けられている IPP プリンターのキャッシュされた PrintDeviceCapabilities を更新した後、このイベントが発生します。 このイベントが発生すると、PrintSupportExtension バックグラウンド クラスは、変更された PrintDeviceCapabilities を検査して変更できます。

印刷チケットのカスタム検証

C# サンプルコードは PrintTicket 検証サービスを提供するためにあります。

public void Run(IBackgroundTaskInstance taskInstance)
{
    // Take task deferral
    this.taskDeferral = taskInstance.GetDeferral();
    // Associate a cancellation handler with the background task
    taskInstance.Canceled += OnTaskCanceled;

    var psaTriggerDetails = taskInstance.TriggerDetails as PrintSupportExtensionTriggerDetails;

    var serviceSession = psaTriggerDetails.Session as PrintSupportExtensionSession;

    this.ippPrintDevice = serviceSession.Printer;
    serviceSession.PrintTicketValidationRequested += this.OnPrintTicketValidationRequested;
    serviceSession.PrinterDeviceCapabilitiesChanged += this.OnPdcChanged;
    serviceSession.Start();
}

private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
    // Complete the deferral
    this.taskDeferral.Complete();
}

private void OnPrintTicketValidationRequested(PrintSupportExtensionSession session, PrintSupportPrintTicketValidationRequestedEventArgs args)
{
    using (args.GetDeferral())
    {
        // Get PrintTicket that needs needs to be validated and resolved   
        var printTicket = args.PrintTicket;
                
        // Validate and resolve PrintTicket
        WorkflowPrintTicketValidationStatus validationStatus = this.ValidateAndResolvePrintTicket(printTicket);
        args.SetPrintTicketValidationStatus(validationStatus);
    }
}

PrintDeviceCapabilities の更新

private void OnPdcChanged(PrintSupportExtensionSession session, PrintSupportPrintDeviceCapabilitiesChangedEventArgs args)
{
    using (args.GetDeferral())
    {
        var pdc = args.GetCurrentPrintDeviceCapabilities();

        // Check current PDC and make changes according to printer device capabilities
        XmlDocument newPdc = this.CheckAndUpdatePrintDeviceCapabilities(pdc);
        args.UpdatePrintDeviceCapabilities(newPdc);
    }
}

印刷品質の向上

ユーザーが印刷ダイアログの印刷ボタンを押して印刷をコミットすると、印刷するドキュメントが印刷中のアプリから印刷スタックに送信されます。 このドキュメントでは、ターゲット プリンターに適した変換 (PDL へのレンダリング) が行われます。 Windows は、プリンターから照会された属性に基づいて、選択する変換を決定します。 その後、変換されたドキュメントがプリンターに送信されます。 これはほとんどのプリンターに適していますが、パートナー アプリが変換に参加できるようにすることで、印刷の品質が向上する可能性がある場合があります。 これを容易にするために、現在の印刷ワークフロー API が拡張され、印刷スタックから追加のポイントでアプリへの呼び出しが含まれます。 この API は、PSA アプリが登録できる 2 つの新しいイベントをサポートします。 PS API サーフェスへの唯一のエントリ ポイントは次のとおりです。

  1. JobStarting

    • このイベントは、任意のアプリケーションによって印刷ジョブが開始されたときに発生します。 イベントが発生すると、印刷サポートアプリは、PrintWorkflowJobStartingEventArgs上で SetSkipSystemRendering を呼び出すことにより、システムレンダリングをスキップすることを選択できます。 システムレンダリングのスキップを選択した場合、印刷システムは XPS ドキュメントをプリンターで必要な PDL 形式に変換しません。 代わりに、印刷アプリケーションによって生成された XPS は直接、XPS から PDL 形式への変換を担当する PSA に渡されます。
  2. PdlModificationRequested

    • このイベントは、Windows がプリンターによって示される PDL 形式への XPS ストリームの変換を開始するときに発生します。 PrintWorkflowPdlModificationRequestedEventArgs ランタイム クラスは、このイベントの引数として提供されます。 このイベント クラスは、印刷ジョブの内容を読み書きするための PDL ソース オブジェクトとターゲット オブジェクトを提供します。 ユーザー入力が必要であるとアプリが判断した場合は、EventArgs から printWorkflowUILauncher 使用して UI を起動できます。 この API では、Tester-Doer パターンが使用されます。 関数 IsUILaunchEnabled が false を返した場合、PrintWorkflowUILauncher は UI を呼び出すことができません。 この関数は、PSA セッションがサイレント モード (ヘッドレス モードまたはキオスク モード) で実行されている場合に false を返します。 関数が false を返した場合、印刷サポート アプリは UI の起動を試みることはできません。

    OutputStream は、関数 GetStreamTargetAsync によって返される PrintWorkflowPdlTargetStream の一部として使用できます。 ターゲット OutputStream に書き込まれたコンテンツは、ドキュメント コンテンツとしてプリンターに渡されます。

PDL 変更イベントのシーケンス図:

ソース ストリーム P D L 変更イベント の シーケンス図

PSA フォアグラウンド アプリケーションは、PS バックグラウンド タスクが UI の起動を要求したときに起動されます。 PSA は、フォアグラウンド コントラクトを使用してユーザー入力を取得したり、プレビュー印刷プレビューをユーザーに表示したりできます。

新しい printSupportWorkflow バックグラウンド タスクの種類が定義されました。 Package.appxmanifest には、PrintSupportWorkflow コントラクトの次の機能拡張エントリがあります。

<Extensions>
    <printsupport:Extension Category="windows.printSupportWorkflow" 
        EntryPoint="PsaBackgroundTasks.PrintSupportWorkflowSample"/>
</Extensions>

コントラクトのアクティブ化時に、PrintWorkflowJobTriggerDetails は IBackgroundTaskInstance-TriggerDetailsとして指定されます。 PrintWorkflowJobTriggerDetails は、そのプロパティの一部として PrintWorkflowJobBackgroundSession を内部的に提供します。 アプリでは、PrintWorkflowJobBackgroundSession を使用して、印刷ジョブワークフローのさまざまな挿入ポイントに関連するイベントを登録できます。 イベントの登録が完了した後、アプリでは PrintWorkflowJobBackgroundSession::Start を呼び出して、印刷システムがさまざまな挿入ポイントに関連するイベントの発生を開始する必要があります。

新しい ActivationKind である PrintSupportJobUI が定義されています。 これには新しい機能は必要ありません。

<Extensions>
    <printsupport:Extension Category="windows.printSupportJobUI" 
        EntryPoint="PsaSample.PrintSupportJobUISample"/>
</Extensions>

これは、印刷サポート ワークフローのバックグラウンド コントラクトから、またはユーザーが印刷ジョブ エラー トーストを選択したときに起動できる UI コントラクトです。 アクティブ化時 PrintWorkflowJobActivatedEventArgs が提供され、PrintWorkflowJobUISession オブジェクトが含まれます。 PrintWorkflowJobUISessionを使用すると、PDL データにアクセスする場合は、フォアグラウンド アプリケーションが PdlDataAvailable イベントに登録する必要があります。 フォアグラウンド アプリケーションは、ジョブ中に発生する可能性があるエラーのカスタム エラー メッセージを表示する場合は、JobNotification イベントに登録する必要があります。 イベントが登録されると、アプリケーションは printWorkflowJobUISession::Start 関数 呼び出して、印刷システムでイベントの発生を開始する必要があります。

システムレンダリングのスキップ

namespace PsaBackground
{
    class PrintSupportWorkflowBackgroundTask : IBackgroundTask
    {
        BackgroundTaskDeferral taskDeferral;
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            // Take Task Deferral            
            taskDeferral = taskInstance.GetDeferral();

            var jobTriggerDetails = taskInstance.TriggerDetails as PrintWorkflowJobTriggerDetails;

            var workflowBackgroundSession = jobTriggerDetails.PrintWorkflowJobSession as PrintWorkflowJobBackgroundSession;
            // Register for events
            workflowBackgroundSession.JobStarting += this.OnJobStarting;
            workflowBackgroundSession.PdlModificationRequested += this.OnPdlModificationRequested;
            // Start Firing events
            workflowBackgroundSession.Start();
        }
    
        private void OnJobStarting(PrintWorkflowJobBackgroundSession session, PrintWorkflowJobStartingEventArgs args)
        {
            using (args.GetDeferral())
            {
                // Call SetSkipSystemRendering to skip conversion for XPS to PDL, so that PSA can directly manipulate the XPS file.
                args.SetSkipSystemRendering();
            }
        }
     }
}

PDL 変更イベント

PDL 変更イベントのシーケンス図:

入力ストリーム P D L 変更イベント のシーケンス図

印刷サポート ジョブ モニターの印刷ジョブ コンテンツの読み取りと書き込みの C# サンプル コード:

private void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession session, PrintWorkflowPdlModificationRequestedEventArgs args)
{
    using (args.GetDeferral())
    {
        IInputStream pdlContent = args.SourceContent.GetInputStream();
        // Specify the Content type of stream that will be written to target that is passed to printer accordingly.
        PrintWorkflowPdlTargetStream streamTarget = args.CreateJobOnPrinter(args.SourceStream.ContentType);
        IOutputStream outputStream = streamTarget.GetOutputStream();

        using (var inputReader = new Windows.Storage.Streams.DataReader(pdlContent))
        {
            inputReader.InputStreamOptions = InputStreamOptions.Partial;
            using (var outputWriter = new Windows.Storage.Streams.DataWriter(outputStream))
            {
                // Write the updated Print stream from input stream to the output stream
                uint chunkSizeInBytes = 256 * 1024; // 256K chunks
                
                uint lastAllocSize = 0;
                byte[] contentData = new byte[chunkSize];
                while(this.ReadChunk(inputReader, ref contentData))
                {
                    
                    // Make any changes required to the input data
                    // ...                        
                    // Write out the modified content
                    outputWriter.WriteBytes(contentData);
                    await outputWriter.StoreAsync();
                }
            }
        }
        streamTarget.CompleteStreamSubmission(PrintWorkflowSubmittedStatus.Succeeded);
        this.taskDeferral.Complete();
        }
    }
}

ワークフローの背景から UI を起動する

PSPDL 変更要求イベント コントラクトから印刷サポート ジョブ UI を起動するための C# サンプル コード:

private async void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession session, PrintWorkflowPdlModificationRequestedEventArgs args)
{
    IInputStream pdlContent = args.SourceContent.GetInputStream();
    WorkflowPrintTicket printTicket = args.PrinterJob.GetJobPrintTicket();

    bool uiRequired = this.IsUIRequired(pdlContent, printTicket);
    if (!uiRequired)
    {
        // Specify the Content type of content that will be written to target that is passed to printer accordingly.
        PrintWorkflowPdlTargetStream streamTarget = args.CreateJobOnPrinter (args.SourceStream.ContentType);
        // Process content directly if UI is not required
        this.ProcessContent(pdlContent, streamTarget);
    }
    else if (args.UILauncher.IsUILaunchEnabled())
    {
        // LaunchAndCompleteUIAsync will launch the UI and wait for it to complete before returning 
        PrintWorkflowUICompletionStatus status = await args.UILauncher.LaunchAndCompleteUIAsync();
        if (status == PrintWorkflowUICompletionStatus.Completed)
        {
            PrintWorkflowPdlTargetStream streamTarget = args.CreateJobOnPrinter(args.SourceStream.ContentType);
            this.ProcessContent(pdlContent, streamTarget);
        }
        else
        {
            if (status == PrintWorkflowUICompletionStatus.UserCanceled)
            {
                // Log user cancellation and cleanup here.
                this.taskDeferral.Complete();
            }
            else
            {
                // UI launch failed, abort print job.
                args.Configuration.AbortPrintFlow(PrintWorkflowAbortReason.JobFailed);
                this.taskDeferral.Complete();
            }
        }
    }
    else
    {
        // PSA requires to show UI, but launching UI is not supported at this point because of user selection.
        args.Configuration.AbortPrintFlow(PrintWorkflowAbortReason.JobFailed);
        this.taskDeferral.Complete();
    }
}

PDLDataAvailable イベントのワークフロー ジョブ UI のアクティブ化

PdlDataAvailable イベント時の印刷ジョブ UI の起動のシーケンス図:

P D L データが利用可能なイベントの印刷ジョブ U I のアクティブ化に関するシーケンス図 の印刷ジョブ U I アクティブ化のシーケンス図

PSAジョブUI活性化契約のC#サンプルコード:

namespace PsaSampleApp
{
    sealed partial class App : Application
    {
        protected override void OnActivated(IActivatedEventArgs args)
        {
            if (args.Kind == ActivationKind.PrintSupportJobUI)
            {
                var rootFrame = new Frame();
        
                rootFrame.Navigate(typeof(JobUIPage));
                Window.Current.Content = rootFrame;
        
                var jobUI = rootFrame.Content as JobUIPage;

                // Get the activation arguments
                var workflowJobUIEventArgs = args as PrintWorkflowJobActivatedEventArgs;

                PrintWorkflowJobUISession session = workflowJobUIEventArgs.Session;
                session.PdlDataAvailable += jobUI.OnPdlDataAvailable;
                session.JobNotification += jobUI.OnJobNotification;
                // Start firing events
                session.Start(); 
            }
        }
    }
}

namespace PsaSampleApp
{
    public sealed partial class JobUIPage : Page    
    {
        public JobUIPage()
        {
            this.InitializeComponent();
        }

        public string WorkflowHeadingLabel;

        public void OnPdlDataAvailable(PrintWorkflowJobUISession session, PrintWorkflowPdlDataAvailableEventArgs args)
        {
            using (args.GetDeferral())
            {
                string jobTitle = args.Configuration.JobTitle;
                string sourceApplicationName = args.Configuration.SourceAppDisplayName;            
                string printerName = args.Printer.PrinterName;
                this.WorkflowHeadingLabel = string.Format(this.formatHeading, jobTitle, sourceApplicationName, printerName);

                // Get pdl stream and content type
                IInputStream pdlContent = args.SourceContent.GetInputStream();
                string contentType = args.SourceContent.ContentType;
                this.ShowPrintPreview(pdlContent, contentType);
            }
        }
    }
}

プリンター ジョブ属性を取得する

印刷ジョブのジョブ属性を取得するための C# サンプル コード:

namespace PsaBackground
{
    class PrintSupportWorkflowBackgroundTask : IBackgroundTask
    {
        private async void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession session, 
                             PrintWorkflowPdlModificationRequestedEventArgs args)
        {
            using (args.GetDeferral())
            {
                string colorMode = this.GetJobColorMode(args.PrinterJob);
                if (colorMode != "monochrome")
                {
                    this.SetJobColorModeToMonochrome(args.PrinterJob);
                } 
            }
        }

        private string GetJobColorMode(PrintWorkflowPrinterJob printerJob)
        {
            var attributes = new List<string>();
            attributes.Add("print-color-mode");
             // Gets the IPP attributes from the current print job
            IDictionary<string, IppAttributeValue> printerAttributes = printerJob.GetJobAttributes(attributes);

            var colorModeValue =  printerAttributes["print-color-mode"];
            this.CheckValueType(colorModeValue, IppAttributeValueKind.Keyword);

            return colorModeValue.GetKeywordArray().First();
        }
    }
} 

プリンター ジョブの属性を設定する

C# サンプル コード。前述の「プリンター ジョブ属性の取得」セクション 進み、ジョブ属性の設定を示します。

private async void SetJobColorModeToMonochrome(PrintWorkflowPrinterJob printerJob)
{
    var attributes = new Dictionary<string, IppAttributeValue>();
    attributes.Add("print-color-mode", IppAttributeValue.CreateKeyword("monochrome"));

    var result = PrinterJob.SetJobAttributes(attributes);
    if (!result.Succeeded)
    {
        this.LogSetAttributeError(result.AttributeErrors);
    }
}

一部の IPP プリンターは、ジョブの作成後にジョブ属性の取得/設定をサポートしていません。 これらのプリンターの場合、PrintJob には JobId プロパティが "0" に設定されており、GetJobAttributes/SetJobAttributes は例外ですぐに失敗します。

PDL コンテンツへのストレージ ファイル アクセスの提供

PDF などの一部の PDL 形式では、処理を開始するために完全なストリームを使用できる必要があります。 そのため、GetContentFileAsync という名前の新しいメソッドは、ソース コンテンツの StorageFile を返す PrintWorkflowPdlSourceContent クラスに提供されます。

public sealed partial class JobUIPage : Page
{
    public async void OnPdlDataAvailable(PrintWorkflowJobUISession session, PrintWorkflowPdlDataAvailableEventArgs args)
    {
        using (args.GetDeferral())
        {
            if (String.Equals(args.SourceContent.ContentType, "application/pdf", StringComparison.OrdinalIgnoreCase))
            {
                // Wait for all PDL data to be available
                StorageFile sourceFile == await args.SourceContent.GetContentFileAsync();
                IRandomAccessStream sourceStream = await sourceFile.OpenReadAsync();

                PdfDocument pdfDocument = await PdfDocument.LoadFromStreamAsync(sourceStream);

                for (uint i = 0; i < pdfDocument.PageCount; i++)
                {
                    PdfPage page = pdfDocument.GetPage(i);
                    var pageImage = new InMemoryRandomAccessStream();
                    await page.RenderToStreamAsync(pageImage);
                    this.AddImageToPreviewImageList(pageImage);
                }
            }
        }
    }
}    

XPS から PDF への PDL 変換

XPS から PDF への PDL 変換を示す C# サンプル コード:

private async void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession session, PrintWorkflowPdlModificationRequestedEventArgs args)
{
    using (args.GetDeferral())
    {
        if (String.Equals(args.SourceContent.ContentType, "application/oxps", StringComparison.OrdinalIgnoreCase))
        {
            var xpsContent = args.SourceContent.GetInputStream();

            var printTicket = args.PrinterJob.GetJobPrintTicket();
            PrintWorkflowPdlTargetStream streamTarget = args.CreateJobOnPrinter("application/pdf");

            // Modify XPS stream here to make the needed changes 
            // for example adding a watermark

            PrintWorkflowPdlConverter pdlConverter = args.GetPdlConverter(PrintWorkflowPdlConversionType.XpsToPdf);
            await pdlConverter.ConvertPdlAsync(printTicket, xpsContent, streamTarget.GetOutputStream());

            streamTarget.CompleteStreamSubmission(PrintWorkflowSubmittedStatus.Succeeded);
        }
        else
        {
            // We except source content to be XPS in this case, abort the session if it is not XPS.
            args.Configuration.AbortPrintFlow(PrintWorkflowAbortReason.JobFailed);
        }
    }
    this.taskDeferral.Complete();
}

ジョブ通知イベント

ジョブ通知イベントのシーケンス図:

ジョブ通知イベント のための シーケンス図

上記の PDLDataAvailable イベント セクションのワークフロー ジョブ UI アクティブ化から続行して、ジョブ通知にエラーを表示する C# サンプル コード:

public sealed partial class JobUIPage : Page    
{
    public void OnJobNotification(PrintWorkflowJobUISession session, PrintWorkflowJobNotificationEventArgs args)
    {
        using (args.GetDeferral())
        {
            PrintWorkflowPrinterJobStatus jobStatus = args.PrintJob.GetJobStatus();

            switch (jobStatus)
            {
                case PrintWorkflowPrinterJobStatus::Error:
                    // Show print job error to the user
                    Frame->Navigate(JobErrorPage::typeid, this);
                break;
                case PrintWorkflowPrinterJobStatus::Abort:
                    // Show message that print job has been aborted.
                    Frame->Navigate(JobAbortPage::typeid, this);
                break;
                case PrintWorkflowPrinterJobStatus::Completed:
                    // Show job successfully completed message to the user.
                    Frame->Navigate(JobCompletedPage::typeid, this);
                break;
            }
        }
    }    
}

初期ジョブ属性を使用してジョブを作成する

現在、一部の IPP プリンターでは set-attribute 操作がサポートされていません。 この問題を軽減するために、CreateJobOnPrinterWithAttributes 関数 と CreateJobOnPrinterWithAttributesBuffer 関数 PrintWorkflowPdlDataAvailableEventArgs が用意されています。 これらの API を使用して、PSA 開発者は、プリンターでジョブが作成されるときにプリンターに渡されるジョブ属性を提供できます。

public sealed partial class JobUIPage : Page
{
    public async void OnPdlDataAvailable(PrintWorkflowJobUISession session, PrintWorkflowPdlDataAvailableEventArgs args)
    {
       var attributes = new Dictionary<string, IppAttributeValue>();
       attributes.Add("print-color-mode", IppAttributeValue.CreateKeyword("monochrome"));
       // Create job on printer with initial job attributes
       PrintWorkflowPdlTargetStream streamTarget = args.CreateJobOnPrinterWithAttributes(attributes, "application/pdf");
        // Write data to target stream
    }
}

シーケンシャル XPS 処理

スプールが完了する前に XPS を順次処理するための C++/Winrt サンプル コード。

namespace winrt
{
    struct WorkflowReceiver : public winrt::implements<WorkflowReceiver, IPrintWorkflowXpsReceiver2>
    {
        STDMETHODIMP SetDocumentSequencePrintTicket(_In_ IStream* documentSequencePrintTicket) noexcept override
        {
            // process document sequence print ticket
            return S_OK;
        }

        STDMETHODIMP SetDocumentSequenceUri(PCWSTR documentSequenceUri) noexcept override
        {
            // process document sequence URI
        }

        STDMETHODIMP AddDocumentData(UINT32 documentId, _In_ IStream* documentPrintTicket,
            PCWSTR documentUri) noexcept override
        {
            // process document URI and print ticket
            return S_OK;
        }

        STDMETHODIMP AddPage(UINT32 documentId, UINT32 pageId,
            _In_ IXpsOMPageReference* pageReference, PCWSTR pageUri)  noexcept override
        {
            // process XPS page
            return S_OK;
        }

        STDMETHODIMP Close() noexcept override
        {
            // XPS processing finished
            return S_OK;
        }

        STDMETHODIMP Failed(HRESULT XpsError) noexcept override
        {
            // XPS processing failed, log error and exit
            return S_OK;
        }
    };

    void PsaBackgroundTask::OnPdlModificationRequested(PrintWorkflowJobBackgroundSession session,
        PrintWorkflowPdlModificationRequestedEventArgs args)
    {
    auto contentType = args.SourceContent().ContentType();
        if (contentType == L"application/oxps")
        {
                    auto xpsContent = args.SourceContent().GetInputStream();
                    PrintWorkflowObjectModelSourceFileContent xpsContentObjectModel(xpsContent);
                    com_ptr<IPrintWorkflowObjectModelSourceFileContentNative> xpsContentObjectModelNative;
                    check_hresult(winrt::get_unknown(xpsContentObjectModel)->QueryInterface( 
                                                        IID_PPV_ARGS(xpsContentObjectModelNative.put())));
        
                    auto xpsreceiver = make_self<WorkflowReceiver>();
                    check_hresult(xpsContentObjectModelNative->StartXpsOMGeneration(xpsreceiver.get()));
        }
    }
}

表示名のローカライズと PDL パススルー API の統合

重要

このセクションでは、Windows 11 バージョン 22H2 以降で使用できるPSA機能について説明します。

このシナリオでは、PSA は印刷デバイス機能 (PDC) をカスタマイズし、文字列ローカライズ用の印刷デバイス リソース (PDR) を提供します。

サポートされている PDL パススルー API コンテンツの種類 (PDL 形式) も設定します。 PSA がイベントをサブスクライブしないか、明示的に SetSupportedPdlPassthroughContentTypes を呼び出さない場合、この PSA アプリに関連付けられたプリンターの PDL パススルーは無効になります。

// Event handler called every time PrintSystem updates PDC or BindPrinter is called
 private void OnPdcChanged(PrintSupportExtensionSession session, PrintSupportPrintDeviceCapabilitiesChangedEventArgs args)
{
    using (args.GetDeferral())
    {
        XmlDocument pdc = args.GetCurrentPrintDeviceCapabilities();
        XmlDocument pdr = args.GetCurrentPrintDeviceResources();
        
        // Check current PDC and make changes according to printer device capabilities 
        XmlDocument newPdc = this.CheckAndUpdatePrintDeviceCapabilities(pdc);
        // Get updated printer devices resources, corresponding to the new PDC 
        XmlDocument newPdr = this.GetPrintDeviceResourcesInfo(newPdc, pdr, args.ResourceLanguage);

        // Update supported PDL formats 
        args.SetSupportedPdlPassthroughContentTypes(GetSupportedPdlContentTypes());
        
        args.UpdatePrintDeviceCapabilities(newPdc);
        args.UpdatePrintDeviceResources(newPdr);
    }
}

ページ レベル機能のサポートと操作属性

重要

このセクションでは、Windows 11 バージョン 22H2 以降で使用できるPSA機能について説明します。

ページ レベル機能のサポートと操作属性のシナリオは、サンプル コード内の同じ場所に変更を加えることで対処されるため、グループ化されます。

  • ページ レベル機能のサポート: このシナリオでは、PSA アプリケーションはページ レベル属性を指定します。これは、PrintTicket から解析された IPP 属性によってオーバーライドされるべきではありません。

  • 操作属性のサポート (PIN 印刷) の個別のコレクション: このシナリオでは、PSA アプリケーションはカスタム IPP 操作属性 (PIN など) を指定します。

次の C# サンプル コードは、ページレベル機能のサポートや、操作属性のための個別コレクションが必要となる や の各シナリオに対して、必要な変更点を示しています。

private void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession session, PrintWorkflowPdlModificationRequestedEventArgs args)
{
    using (args.GetDeferral())
    {
        IInputStream pdlContent = args.SourceContent.GetInputStream();
    
        // Custom job attributes to add to the printJob
        IDictionary<string, IppAttributeValue> jobAttributes = LocalStorageUtil.GetCustomIppJobAttributes();
        // Custom operation attributes to add to printJob
        IDictionary<string, IppAttributeValue> operationAttributes = LocalStorageUtil.GetCustomIppOperationAttributes();
        
        // PSA has an option to select preferred PDL format
        string documentFormat = GetDocumentFormat(args.PrinterJob.Printer);
    
        // Create PrintJob with specified PDL and custom attributes
        PrintWorkflowPdlTargetStream targetStream = args.CreateJobOnPrinterWithAttributes(jobAttributes, documentFormat  , operationAttributes,
           PrintWorkflowAttributesMergePolicy  .DoNotMergeWithPrintTicket /*jobAttributesMergePolicy*/, PrintWorkflowAttributesMergePolicy.MergePreferPsaOnConflict /*operationAttributesMergePolicy*/);
    
        // Adding a watermark to the output(targetStream) if source payload type is XPS
        this.ModifyPayloadIfNeeded(targetStream, args, documentFormat, deferral);
    
        // Marking the stream submission as Succeeded.
        targetStream.CompleteStreamSubmission(PrintWorkflowSubmittedStatus.Succeeded);
    
        this.taskDeferral.Complete();
    }
}

PSA による印刷ダイアログの拡張

重要

このセクションでは、Windows 11 バージョン 22H2 以降で使用できるPSA機能について説明します。

このシナリオでは、印刷ダイアログとPSA統合を使用すると、次のアクションが可能になります。

  • MPD で選択が変更されたときに、PSA に関連付けられているプリンターにコールバックを取得する

  • openUrl アクションをサポートする 1 つの AdaptiveCard を表示する

  • 印刷ダイアログにカスタム機能とパラメーターを表示する

  • PrintTicket を変更し、印刷ダイアログに表示される機能オプションの選択を変更する

  • 印刷アプリの Windows.ApplicationModel.AppInfo を取得し、印刷ダイアログを開きます

次の C# サンプルは、これらの印刷ダイアログの機能強化を示しています。

public BackgroundTaskDeferral TaskInstanceDeferral { get; set; }

public void Run(IBackgroundTaskInstance taskInstance)
{
    // Take task deferral 
    TaskInstanceDeferral   = taskInstance.GetDeferral();
    // Associate a cancellation handler with the background task 
    taskInstance.Canceled += OnTaskCanceled;

    if (taskInstance.TriggerDetails is PrintSupportExtensionTriggerDetails extensionDetails)
    {
         PrintSupportExtensionSession session = extensionDetails.Session;
         session.PrintTicketValidationRequested += OnSessionPrintTicketValidationRequested;
         session.PrintDeviceCapabilitiesChanged += OnSessionPrintDeviceCapabilitiesChanged;
         session.PrinterSelected += this.OnPrinterSelected;
    }
}

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

// Event handler called when the PSA Associated printer is selected in Print Dialog
private void OnPrinterSelected(PrintSupportExtensionSession session, PrintSupportPrinterSelectedEventArgs args)
{
    using (args.GetDeferral())
    {
        // Show adaptive card in the Print Dialog (generated based on Printer and Printing App) 
        args.SetAdaptiveCard  (GetCustomAdaptiveCard(session.Printer, args.SourceAppInfo));

        // Request to show Features and Parameters in the Print Dialog if not shown already
        const string xmlNamespace = "\"http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords\"";
        var additionalFeatures= new List<PrintSupportPrintTicketElement> { new PrintSupportPrintTicketElement { LocalName = "PageMediaType", NamespaceUri = xmlNamespace } };                  
        var additionalParameters = new List<PrintSupportPrintTicketElement> { new PrintSupportPrintTicketElement { LocalName = "JobCopiesAllDocuments", NamespaceUri = xmlNamespace } };

        if ((featuresToShow.Count + parametersToShow.Count) <= args.AllowedCustomFeaturesAndParametersCount)
        {
            args.SetAdditionalFeatures(additionalFeatures);
            args.SetAdditionalParameter(additionalParameters);
        }
        else
        {
            // Cannot show that many additional features and parameters, consider reducing the number
            // of additional features and parameters by selecting only the most important ones
        }
    }
}

// Create simple AdaptiveCard to show in MPD
public IAdaptiveCard GetCustomAdaptiveCard(IppPrintDevice ippPrinter, AppInfo appInfo)
{
    return AdaptiveCardBuilder.CreateAdaptiveCardFromJson($@"
        {{""body"": [
                {{ 
                    ""type"": ""TextBlock"",
                    ""text"": ""Hello {appInfo.DisplayInfo.DisplayName} from {ippPrinter.PrinterName}!""
                }}
              ],
              ""$schema"": ""http://adaptivecards.io/schemas/adaptive-card.json"",
            ""type"": ""AdaptiveCard"",
            ""version"": ""1.0""
        }}");
}

ホスト ベースの処理フラグを使用した PDL 変換

重要

このセクションでは、Windows 11 バージョン 22H2 以降で使用できるPSA機能について説明します。

現在の PDL 変換 API PrintWorkflowPdlConverter.ConvertPdlAsyncは、既定でホスト ベースの処理を行います。 これは、プリンターがこれらの操作を実行する必要がないように、ホスト/印刷コンピューターが回転やページの順序などを実行することを意味します。 ただし、プリンターの IHV は、ホストベースの処理なしで PDL 変換を望むかもしれません。なぜなら、他の方法よりもプリンター自身がこれを効率的に行える可能性があるためです。 ConvertPdlAsync 関数は、この要件に対処するためにホスト ベースの処理フラグを受け取ります。 PSA は、このフラグを使用して、すべてのホストベースの処理または特定のホストベースの処理操作をスキップできます。

class HostBaseProcessingRequirements
{
    public bool CopiesNeedsHostBasedProcessing = false;
    public bool PageOrderingNeedsHostBasedProcessing = false;
    public bool PageRotationNeedsHostBasedProcessing = false;
    public bool BlankPageInsertionNeedsHostBasedProcessing = false;
}

private async void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession sender, PrintWorkflowPdlModificationRequestedEventArgs args)
{
    using (args.GetDeferral())
    {
        var targetStream = args.CreateJobOnPrinter("application/pdf");
        var pdlConverter = args.GetPdlConverter(PrintWorkflowPdlConversionType.XpsToPdf);

        var hostBasedRequirements = this.ReadHostBasedProcessingRequirements(args.PrinterJob.Printer);
            
        PdlConversionHostBasedProcessingOperations hostBasedProcessing = PdlConversionHostBasedProcessingOperations.None;
        if (hostBasedRequirements.CopiesNeedsHostBasedProcessing)
        {
            hostBasedProcessing |= PdlConversionHostBasedProcessingOperations.Copies;
        }

        if (hostBasedRequirements.PageOrderingNeedsHostBasedProcessing)
        {
            hostBasedProcessing |= PdlConversionHostBasedProcessingOperations.PageOrdering;
        }

        if (hostBasedRequirements.PageRotationNeedsHostBasedProcessing)
        {
            hostBasedProcessing |= PdlConversionHostBasedProcessingOperations.PageRotation;
        }

        if (hostBasedRequirements.BlankPageInsertionNeedsHostBasedProcessing)
        {
            hostBasedProcessing |= PdlConversionHostBasedProcessingOperations.BlankPageInsertion;
        }

        await pdlConverter.ConvertPdlAsync(args.PrinterJob.GetJobPrintTicket(), args.SourceContent.GetInputStream(), targetStream.GetOutputStream(), hostBasedProcessing);
    }
}

private HostBaseProcessingRequirements ReadHostBasedProcessingRequirements(IppPrintDevice printDevice)
{
    // Read Host based processing requirements for the printer
}

印刷デバイス機能 (PDC) 更新ポリシーの設定

重要

このセクションでは、Windows 11 バージョン 22H2 以降で使用できるPSA機能について説明します。

プリンターの IHV は、印刷デバイス機能 (PDC) を更新する必要がある場合に異なる要件があります。 これらの要件に対処するために、PrintSupportPrintDeviceCapabilitiesUpdatePolicy の更新ポリシーを PDC に設定できます。 この API を使用して、時間または印刷ジョブの数に基づいて PDC 更新ポリシーを設定できます。

ジョブの数に基づいて PDC 更新ポリシーを設定する

// Event handler called every time PrintSystem updates PDC
private void OnPdcChanged(PrintSupportExtensionSession session, PrintSupportPrintDeviceCapabilitiesChangedEventArgs args)
{
    using (args.GetDeferral())
    {
        // Set update policy to update the PDC on bind printer of every print job.
        var updatePolicy = PrintSupportPrintDeviceCapabilitiesUpdatePolicy.CreatePrintJobRefresh(1);
        args.SetPrintDeviceCapabilitiesUpdatePolicy(updatePolicy);      
    }
}

TimeOut に基づいて PDC 更新ポリシーを設定する

// Event handler called every time PrintSystem updates PDC
private void OnPdcChanged(PrintSupportExtensionSession session, PrintSupportPrintDeviceCapabilitiesChangedEventArgs args)
{
    using (args.GetDeferral())
    {
        // Set update policy to update the PDC on bind printer of every print job.
        var updatePolicy = PrintSupportPrintDeviceCapabilitiesUpdatePolicy.CreatePrintJobRefresh(1);
        args.SetPrintDeviceCapabilitiesUpdatePolicy(updatePolicy);      
    }
}

一般的な印刷サポート アプリ (PSA) の設計ガイダンス

印刷サポート アプリを設計するときは、次の点を設計に含める必要があります。

  • フォアグラウンド コントラクトとバックグラウンド コントラクトの両方が複数のインスタンスをサポートするようにマークする必要があります。たとえば、パッケージ マニフェストに supportsMultipleInstance 存在する必要があります。 これは、複数の同時ジョブに対してコントラクトの有効期間を確実に管理できるようにするためです。

  • PDL 変更の起動 UI を省略可能な手順として扱います。 UI の起動が許可されていない場合でも、印刷ジョブを正常に完了するように最善を尽くしてください。 印刷ジョブは、PDL の変更中にユーザー入力なしで正常に完了する方法がない場合にのみ中止する必要があります。 このような場合は、変更されていない PDL を送信することを検討してください。

  • PDL 変更用の UI を起動するときは、先に IsUILaunchEnabled を呼び出してから、LaunchAndCompleteUIAsyncを呼び出します。 これは、現在の時点で UI を表示できないシナリオが引き続き正しく印刷されるようにするためです。 これらのシナリオは、ヘッドレス デバイス、あるいは現在キオスク モードまたは応答不可モードになっているデバイスで考えられます。

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

インターネット印刷プロトコル (IPP) の仕様

印刷サポート アプリの関連付け

Windows.Devices.Printers

Windows.Graphics.Printing.PrintSupport

Windows.Graphics.Printing.Workflow