인쇄 워크플로 앱을 사용하여 사용자 지정 인쇄 워크플로 환경을 만듭니다.
개요
인쇄 워크플로 앱은 WSDA(Microsoft Store 디바이스 앱)
WSDA의 경우와 마찬가지로 원본 애플리케이션 사용자가 인쇄를 선택하고 인쇄 대화 상자를 탐색할 때 시스템은 워크플로 앱이 해당 프린터와 연결되어 있는지 여부를 확인합니다. 이 경우 인쇄 워크플로 앱이 시작됩니다(주로 백그라운드 작업으로, 아래에서 자세히 설명). 워크플로 앱은 인쇄 티켓(현재 인쇄 작업에 대한 프린터 장치 설정을 구성하는 XML 문서)과 인쇄할 실제 XPS 콘텐츠를 모두 변경할 수 있습니다. 필요에 따라 프로세스 중간에 UI를 시작하여 사용자에게 이 기능을 노출할 수 있습니다. 작업을 수행한 후 인쇄 내용과 인쇄 티켓을 드라이버에 전달합니다.
배경 및 포그라운드 구성 요소가 포함되고 기능적으로 다른 앱과 결합되기 때문에 인쇄 워크플로 앱은 다른 범주의 UWP 앱보다 구현하기가 더 복잡할 수 있습니다. 이 가이드를 읽는 동안 워크플로 앱 샘플 검사하여 다양한 기능을 구현하는 방법을 더 잘 이해하는 것이 좋습니다. 간단한 설명을 위해 이 가이드에는 다양한 오류 검사 및 UI 관리와 같은 일부 기능이 없습니다.
시작하기
워크플로 앱은 적절한 시간에 시작할 수 있도록 인쇄 시스템에 대한 진입점을 나타내야 합니다. 이 작업은 UWP 프로젝트의 Application/Extensions
파일의 요소에 다음 선언을 삽입하여 수행됩니다.
<uap:Extension Category="windows.printWorkflowBackgroundTask"
EntryPoint="WFBackgroundTasks.WfBackgroundTask" />
중요합니다
인쇄 사용자 지정에 사용자 입력이 필요하지 않은 많은 시나리오가 있습니다. 이러한 이유로 인쇄 워크플로 앱은 기본적으로 백그라운드 작업으로 실행됩니다.
워크플로 앱이 인쇄 작업을 시작한 원본 애플리케이션과 연결된 경우(이에 대한 지침은 이후 섹션 참조) 인쇄 시스템에서 매니페스트 파일에서 백그라운드 작업 진입점을 검사합니다.
인쇄 작업지시서에서 백그라운드 작업 수행
인쇄 시스템이 워크플로 앱에서 가장 먼저 수행하는 작업은 백그라운드 작업을 활성화하는 것입니다(이 경우 WfBackgroundTask
네임스페이스의 WFBackgroundTasks
클래스). 백그라운드 작업의 Run
메서드에서 작업의 트리거 세부 정보를 PrintWorkflowTriggerDetails 인스턴스로 캐스팅해야 합니다. 이렇게 하면 인쇄 워크플로 백그라운드 작업에 대한 특수한 기능이 제공됩니다.
PrintWorkflowSession 속성은 PrintWorkFlowBackgroundSession의 인스턴스를 노출합니다. 인쇄 워크플로 세션 클래스(배경 및 전경 종류 모두)는 인쇄 워크플로 앱의 순차적 단계를 제어합니다.
그런 다음, 이 세션 클래스가 발생시킬 두 이벤트에 대한 처리기 메서드를 등록합니다. 나중에 이러한 메서드를 정의합니다.
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 메서드가 호출된 경우 인쇄 시스템은 포그라운드 애플리케이션에 대한 진입점에 대한 매니페스트 파일을 검사합니다.
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 메서드를 호출하여 수행됩니다.
비고
워크플로 앱이 인쇄 작업을 취소하는 경우 작업이 취소된 이유를 나타내는 알림 메시지를 제공하는 것이 좋습니다.
인쇄 내용에 대한 최종 백그라운드 작업 수행
UI가 PrintTaskXpsDataAvailable 이벤트에서 연기를 완료했거나 UI 단계가 건너뛰어진 경우, 인쇄 시스템은 백그라운드 작업에 대해 Submitted 이벤트를 트리거합니다. 이 이벤트의 처리기에서 워크플로 앱은 XpsDataAvailable 이벤트에서 제공하는 모든 동일한 데이터에 액세스할 수 있습니다. 그러나 이전 이벤트와 달리 제출된PrintWorkflowTarget 인스턴스를 통해 최종 인쇄 작업 콘텐츠에 쓰기 액세스 권한을 제공합니다.
최종 인쇄를 위해 데이터를 스풀하는 데 사용되는 개체는 원본 데이터가 원시 바이트 스트림으로 액세스되는지 또는 XPS 개체 모델로 액세스되는지에 따라 달라집니다. 워크플로 앱이 바이트 스트림을 통해 원본 데이터에 액세스하면 최종 작업 데이터를 쓰기 위해 출력 바이트 스트림이 제공됩니다. 워크플로 앱이 개체 모델을 통해 원본 데이터에 액세스하면 출력 작업에 개체를 쓰도록 문서 작성기가 제공됩니다. 두 경우 모두 워크플로 앱은 모든 원본 데이터를 읽고, 필요한 데이터를 수정하고, 수정된 데이터를 출력 대상에 기록해야 합니다.
백그라운드 작업이 데이터 쓰기를 마치면 해당 PrintWorkflowSubmittedOperation 개체에서 Complete을 호출하도록 해야 합니다. 워크플로 앱이 이 단계를 완료하고 제출된 이벤트 처리기가 종료되면 워크플로 세션이 닫히고 사용자가 표준 인쇄 대화 상자를 통해 최종 인쇄 작업의 상태를 모니터링할 수 있습니다.
최종 단계
프린터에 인쇄 워크플로 앱 등록
워크플로 앱은 WSDA와 동일한 형식의 메타데이터 파일 제출을 사용하여 프린터와 연결됩니다. 실제로 단일 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
로컬 사용자는 로컬 프린터에서 이 정책을 실행하거나 엔터프라이즈 구현의 경우 프린터 관리자가 인쇄 서버에서 이 정책을 실행할 수 있습니다. 그러면 정책이 모든 클라이언트 연결에 동기화됩니다. 프린터 관리자는 새 프린터를 추가할 때마다 이 정책을 사용할 수 있습니다.