印刷ワークフローのカスタマイズ

印刷ワークフロー アプリを使用してカスタム印刷ワークフロー エクスペリエンスを作成します。

概要

印刷ワークフロー アプリは、 Microsoft Store デバイス アプリ (WSDA) の機能を拡張する UWP アプリであるため、先に進む前に WSDA について理解しておくと役立ちます。

WSDA の場合と同様に、ソース アプリケーションのユーザーが何かを印刷し、印刷ダイアログ内を移動すると、ワークフロー アプリがそのプリンターに関連付けられているかどうかがチェックされます。 その場合は、印刷ワークフロー アプリが起動します (主にバックグラウンド タスクとして、詳細については以下を参照)。 ワークフロー アプリは、印刷チケット (現在の印刷タスクのプリンター デバイス設定を構成する XML ドキュメント) と、印刷する実際の XPS コンテンツの両方を変更できます。 必要に応じて、プロセスの途中で UI を起動することで、この機能をユーザーに公開できます。 作業が完了すると、印刷コンテンツと印刷チケットがドライバーに渡されます。

背景コンポーネントとフォアグラウンド コンポーネントが関係し、機能的に他のアプリと組み合わせて使用するため、印刷ワークフロー アプリは、他のカテゴリの UWP アプリよりも実装が複雑になる可能性があります。 Workflow アプリのサンプルを調べてこのガイドを読んで、さまざまな機能を実装する方法を理解することをお勧めします。 わかりやすくするために、さまざまなエラー チェックや UI 管理などの一部の機能は、このガイドにはありません。

作業の開始

ワークフロー アプリは、適切なタイミングで起動できるように、印刷システムへのエントリ ポイントを示す必要があります。 これを行うには、UWP プロジェクトの package.appxmanifest ファイルのApplication/Extensions要素に次の宣言を挿入します。

<uap:Extension Category="windows.printWorkflowBackgroundTask"  
    EntryPoint="WFBackgroundTasks.WfBackgroundTask" />

重要

印刷のカスタマイズにユーザー入力を必要としないシナリオは多数あります。 このため、印刷ワークフロー アプリは既定でバックグラウンド タスクとして実行されます。

ワークフロー アプリが、印刷ジョブを開始したソース アプリケーションに関連付けられている場合 (この手順については後のセクションを参照)、印刷システムはそのマニフェスト ファイルのバックグラウンド タスク エントリ ポイントを調べます。

印刷チケットでバックグラウンド作業を行う

印刷システムがワークフロー アプリで最初に行うことは、バックグラウンド タスク (この場合は、WFBackgroundTasks名前空間の WfBackgroundTask クラス) をアクティブ化することです。 バックグラウンド タスクの Run メソッドでは、タスクのトリガーの詳細を PrintWorkflowTriggerDetails インスタンスとしてキャストする必要があります。 これにより、印刷ワークフローのバックグラウンド タスクに特別な機能が提供されます。 PrintWorkflowSession プロパティが公開されます。これは、PrintWorkFlowBackgroundSessionのインスタンスです。 印刷ワークフロー セッション クラス (バックグラウンドとフォアグラウンドの両方) は、印刷ワークフロー アプリの順次ステップを制御します。

次に、このセッション クラスが発生させる 2 つのイベントのハンドラー メソッドを登録します。 これらのメソッドは後で定義します。

public void Run(IBackgroundTaskInstance taskInstance) {
    // Take out a deferral here and complete once all the callbacks are done
    runDeferral = taskInstance.GetDeferral();

    // Associate a cancellation handler with the background task.
    taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);

    // cast the task's trigger details as PrintWorkflowTriggerDetails
    PrintWorkflowTriggerDetails workflowTriggerDetails = taskInstance.TriggerDetails as PrintWorkflowTriggerDetails;

    // Get the session manager, which is unique to this print job
    PrintWorkflowBackgroundSession sessionManager = workflowTriggerDetails.PrintWorkflowSession;

    // add the event handler callback routines
    sessionManager.SetupRequested += OnSetupRequested;
    sessionManager.Submitted += OnXpsOMPrintSubmitted;

    // Allow the event source to start
    // This call blocks until all of the workflow callbacks complete
    sessionManager.Start();
}

Start メソッドが呼び出されると、セッション マネージャーは最初に SetupRequested イベントを発生させます。 このイベントは、印刷タスクに関する一般的な情報と印刷チケットを公開します。 この段階では、印刷チケットをバックグラウンドで編集できます。

private void OnSetupRequested(PrintWorkflowBackgroundSession sessionManager, PrintWorkflowBackgroundSetupRequestedEventArgs printTaskSetupArgs) {
    // Take out a deferral here and complete once all the callbacks are done
    Deferral setupRequestedDeferral = printTaskSetupArgs.GetDeferral();

    // Get general information about the source application, print job title, and session ID
    string sourceApplicationName = printTaskSetupArgs.Configuration.SourceAppDisplayName;
    string jobTitle = printTaskSetupArgs.Configuration.JobTitle;
    string sessionId = printTaskSetupArgs.Configuration.SessionId;

    // edit the print ticket
    WorkflowPrintTicket printTicket = printTaskSetupArgs.GetUserPrintTicketAsync();

    // ...

重要なのは、 SetupRequested の処理において、フォアグラウンド コンポーネントを起動するかどうかをアプリが決定することです。 これは、以前にローカル ストレージに保存された設定や、印刷チケットの編集中に発生したイベント、または特定のアプリの静的設定によって異なります。

// ...

if (UIrequested) {
    printTaskSetupArgs.SetRequiresUI();

    // Any data that is to be passed to the foreground task must be stored the app's local storage.
    // It should be prefixed with the sourceApplicationName string and the SessionId string, so that
    // it can be identified as pertaining to this workflow app session.
}

// Complete the deferral taken out at the start of OnSetupRequested
setupRequestedDeferral.Complete();

印刷ジョブでフォアグラウンド作業を行う (省略可能)

SetRequiresUI メソッドが呼び出された場合、印刷システムは、フォアグラウンド アプリケーションへのエントリ ポイントのマニフェスト ファイルを調べます。 package.appxmanifest ファイルのApplication/Extensions要素には、次の行が必要です。 EntryPointの値をフォアグラウンド アプリの名前に置き換えます。

<uap:Extension Category="windows.printWorkflowForegroundTask"  
    EntryPoint="MyWorkFlowForegroundApp.App" />

次に、印刷システムは、指定されたアプリ エントリ ポイントに対して OnActivated メソッドを呼び出します。 App.xaml.cs ファイルの OnActivated メソッドで、ワークフロー アプリはアクティブ化の種類を確認して、それがワークフローのアクティブ化であることを確認する必要があります。 その場合、ワークフロー アプリはアクティブ化引数を PrintWorkflowUIActivatedEventArgs オブジェクトにキャストできます。このオブジェクトは、 PrintWorkflowForegroundSession オブジェクトをプロパティとして公開します。 このオブジェクトには、前のセクションで対応するバックグラウンドオブジェクトと同様に、印刷システムによって発生するイベントが含まれており、これらにハンドラーを割り当てることができます。 この場合、イベント処理機能は、 WorkflowPageと呼ばれる別のクラスに実装されます。

最初に、 App.xaml.cs ファイルで次の手順を実行します。

protected override void OnActivated(IActivatedEventArgs args){

    if (args.Kind == ActivationKind.PrintWorkflowForegroundTask) {

        // the app should instantiate a new UI view so that it can properly handle the case when
        // several print jobs are active at the same time.
        Frame rootFrame = new Frame();
        if (null == Window.Current.Content)
        {
            rootFrame.Navigate(typeof(WorkflowPage));
            Window.Current.Content = rootFrame;
        }

        // Get the main page
        WorkflowPage workflowPage = (WorkflowPage)rootFrame.Content;

        // Make sure the page knows it's handling a foreground task activation
        workflowPage.LaunchType = WorkflowPage.WorkflowPageLaunchType.ForegroundTask;

        // Get the activation arguments
        PrintWorkflowUIActivatedEventArgs printTaskUIEventArgs = args as PrintWorkflowUIActivatedEventArgs;

        // Get the session manager
        PrintWorkflowForegroundSession taskSessionManager = printTaskUIEventArgs.PrintWorkflowSession;

        // Add the callback handlers - these methods are in the workflowPage class
        taskSessionManager.SetupRequested += workflowPage.OnSetupRequested;
        taskSessionManager.XpsDataAvailable += workflowPage.OnXpsDataAvailable;

        // start raising the print workflow events
        taskSessionManager.Start();
    }
}

UI にイベント ハンドラーがアタッチされ、 OnActivated メソッドが終了すると、印刷システムは UI が処理する SetupRequested イベントを起動します。 このイベントは、印刷ジョブ情報や印刷チケット ドキュメントなど、バックグラウンド タスクのセットアップ イベントが提供したのと同じデータを提供しますが、追加の UI の起動を要求する機能はありません。 WorkflowPage.xaml.cs ファイルで次の手順を実行します。

internal void OnSetupRequested(PrintWorkflowForegroundSession sessionManager, PrintWorkflowForegroundSetupRequestedEventArgs printTaskSetupArgs) {
    // If anything asynchronous is going to be done, you need to take out a deferral here,
    // since otherwise the next callback happens once this one exits, which may be premature
    Deferral setupRequestedDeferral = printTaskSetupArgs.GetDeferral();

    // Get information about the source application, print job title, and session ID
    string sourceApplicationName = printTaskSetupArgs.Configuration.SourceAppDisplayName;
    string jobTitle = printTaskSetupArgs.Configuration.JobTitle;
    string sessionId = printTaskSetupArgs.Configuration.SessionId;
    // the following string should be used when storing data that pertains to this workflow session
    // (such as user input data that is meant to change the print content later on)
    string localStorageVariablePrefix = string.Format("{0}::{1}::", sourceApplicationName, sessionID);

    try
    {
        // receive and store user input
        // ...
    }
    catch (Exception ex)
    {
        string errorMessage = ex.Message;
        Debug.WriteLine(errorMessage);
    }
    finally
    {
        // Complete the deferral taken out at the start of OnSetupRequested
        setupRequestedDeferral.Complete();
    }
}

次に、印刷システムは UI の XpsDataAvailable イベントを発生させます。 このイベントのハンドラーでは、ワークフロー アプリはセットアップ イベントで使用できるすべてのデータにアクセスでき、さらに、生バイトのストリームまたはオブジェクト モデルとして XPS データを直接読み取ることができます。 XPS データにアクセスすると、UI は印刷プレビュー サービスを提供し、ワークフロー アプリがデータに対して実行する操作に関する追加情報をユーザーに提供できます。

このイベント ハンドラーの一部として、ワークフロー アプリはユーザーと対話し続ける場合に遅延オブジェクトを取得する必要があります。 遅延がない場合、印刷システムは、 XpsDataAvailable イベント ハンドラーが終了したとき、または非同期メソッドを呼び出すときに、UI タスクが完了したと見なします。 アプリは、ユーザーの UI との対話から必要なすべての情報を収集したら、印刷システムが進むことができるように遅延を完了する必要があります。

internal async void OnXpsDataAvailable(PrintWorkflowForegroundSession sessionManager, PrintWorkflowXpsDataAvailableEventArgs printTaskXpsAvailableEventArgs)
{
    // Take out a deferral
    Deferral xpsDataAvailableDeferral = printTaskXpsAvailableEventArgs.GetDeferral();

    SpoolStreamContent xpsStream = printTaskXpsAvailableEventArgs.Operation.XpsContent.GetSourceSpoolDataAsStreamContent();

    IInputStream inputStream = xpsStream.GetInputSpoolStream();

    using (var inputReader = new Windows.Storage.Streams.DataReader(inputStream))
    {
        // Read the XPS data from input stream
        byte[] xpsData = new byte[inputReader.UnconsumedBufferLength];
        while (inputReader.UnconsumedBufferLength > 0)
        {
            inputReader.ReadBytes(xpsData);
            // Do something with the XPS data, e.g. preview
            // ...
        }
    }

    // Complete the deferral taken out at the start of this method
    xpsDataAvailableDeferral.Complete();
}

さらに、イベント引数によって公開される PrintWorkflowSubmittedOperation インスタンスには、印刷ジョブを取り消すか、ジョブが成功しても出力印刷ジョブは必要ないことを示すオプションが用意されています。 これを行うには、PrintWorkflowSubmittedStatus 値を使用して Complete メソッドを呼び出します。

Note

ワークフロー アプリが印刷ジョブを取り消す場合は、ジョブが取り消された理由を示すトースト通知を提供することを強くお勧めします。

印刷コンテンツに対して最終的なバックグラウンド作業を行う

UI が PrintTaskXpsDataAvailable イベント (または UI ステップがバイパスされた場合) で遅延が完了すると、印刷システムはバックグラウンド タスクの Submitted イベントを発生させます。 このイベントのハンドラーでは、ワークフロー アプリは、 XpsDataAvailable イベントによって提供されるすべての同じデータにアクセスできます。 ただし、前のイベントとは異なり、Submitted は、PrintWorkflowTarget インスタンスを介して最終的な印刷ジョブコンテンツに書き込みアクセスも提供します。

最終的な印刷のためにデータをスプールするために使用されるオブジェクトは、ソース データに生バイト ストリームとしてアクセスするか、XPS オブジェクト モデルとしてアクセスするかによって異なります。 ワークフロー アプリがバイト ストリームを介してソース データにアクセスすると、最終的なジョブ データを書き込む出力バイト ストリームが提供されます。 ワークフロー アプリがオブジェクト モデルを介してソース データにアクセスすると、出力ジョブにオブジェクトを書き込むドキュメント ライターが提供されます。 どちらの場合も、ワークフロー アプリは、すべてのソース データを読み取り、必要なデータを変更し、変更されたデータを出力ターゲットに書き込む必要があります。

バックグラウンド タスクは、データの書き込みが完了したら、対応する PrintWorkflowSubmittedOperation オブジェクトに対して Complete を呼び出す必要があります。 ワークフロー アプリがこの手順を完了し、 Submitted イベント ハンドラーが終了すると、ワークフロー セッションが閉じられ、ユーザーは標準の印刷ダイアログで最終的な印刷ジョブの状態を監視できます。

最終ステップ

印刷ワークフロー アプリをプリンターに登録する

ワークフロー アプリは、WSDA の場合と同じ種類のメタデータ ファイルの送信を使用してプリンターに関連付けられています。 実際、1 つの UWP アプリケーションは、ワークフロー アプリと、印刷タスク設定機能を提供する WSDA の両方として機能できます。 メタデータの関連付けを作成するには、対応する WSDA の手順に従います

違いは、WSDA はユーザーに対して自動的にアクティブ化されますが (そのユーザーが関連付けられているデバイスで印刷すると、アプリは常に起動します)、ワークフロー アプリは起動しないということです。 個別のポリシーを設定する必要があります。

ワークフロー アプリのポリシーを設定する

ワークフロー アプリ ポリシーは、ワークフロー アプリを実行するデバイス上の Powershell コマンドによって設定されます。 ワークフロー ポリシーを設定できるように、Set-Printer、Add-Printer (既存のポート)、および Add-Printer (新しい WSD ポート) コマンドが変更されます。

  • Disabled: ワークフロー アプリはアクティブ化されません。
  • Uninitialized: ワークフロー DCA がシステムにインストールされている場合、ワークフロー アプリがアクティブになります。 アプリがインストールされていない場合でも、印刷は続行されます。
  • Enabled: ワークフロー DCA がシステムにインストールされている場合、ワークフロー コントラクトがアクティブになります。 アプリがインストールされていない場合、印刷は失敗します。

次のコマンドを実行すると、指定したプリンターでワークフロー アプリが必要になります。

Set-Printer –Name "Microsoft XPS Document Writer" -WorkflowPolicy Enabled

ローカル ユーザーは、ローカル プリンターでこのポリシーを実行することも、エンタープライズ実装の場合はプリンター管理者がプリント サーバーでこのポリシーを実行することもできます。 その後、ポリシーはすべてのクライアント接続に同期されます。 プリンター管理者は、新しいプリンターが追加されるたびにこのポリシーを使用できます。

関連項目

ワークフロー アプリのサンプル

Windows.Graphics.Printing.Workflow 名前空間