Dela via


Designguide för utskriftssupportapp v1 och v2

Den här artikeln innehåller vägledning och exempel för oem-tillverkare och IHV:er för skrivare för att utveckla en psa-app (print support app) som kan förbättra en Windows-användares utskriftsupplevelse på flera sätt.

Viktig

Från och med lanseringen av Windows 11 SDK (22000.1) är PSA (Print Support Apps) den rekommenderade metoden för att utveckla UWP-appar för skrivare. Om du vill utveckla en utskriftssupportapp för din utskriftsenhet laddar du ned och installerar Windows 11 SDK för den Windows-version som du riktar in dig på.

Viktig

Den här artikeln innehåller avsnitt som beskriver PSA-funktioner som är tillgängliga från och med Windows 11 version 22H2. Avsnitten innehåller en anteckning som anger att den gäller för den versionen.

Mer information finns i följande artiklar:

Ämne Beskrivning
designguide för Print Support App v3 API Innehåller vägledning och exempel för skrivartillverkare och IHV:er som implementerar en v3 Print Support App (PSA) för sina enheter.
designguide för Print Support App v4 API Innehåller vägledning och exempel för skrivar-OEM:er och IHV:er som implementerar en PSA (v4 Print Support App) för deras enhet.
MSIX-manifestspecifikation för virtuell skrivare med utskriftsstöd Innehåller MSIX-manifestriktlinjer och exempel för skrivartillverkare och oberoende maskinvaruleverantörer som implementerar en virtuell skrivare med utskriftsstöd.
Stöd för appassociation för utskrift Innehåller vägledning och exempel för att associera en psa (print support app) med en skrivare.

Vissa skrivarfunktioner visas inte i utskriftsdialogrutor som visas av Windows eftersom de är specialfunktioner som behöver hjälp från en tillverkarapp för att konfigureras korrekt. De kan också vara funktioner som inte tillhandahålls i skrivarens standardfunktioner.

Skrivarspecifika funktioner kan grupperas på ett sätt som gör det enkelt för användaren att välja ett alternativ och lita på att alla funktioner som ingår i det scenariot automatiskt anges till rätt värden. Ett exempel på detta kan vara ett val mellan bläckbesparare, pappersbesparare och högsta kvalitetsläge som kan manipulera olika utskriftsfunktioner automatiskt baserat på ett val från användaren. Windows kan inte gruppera dem automatiskt eftersom det kräver att du förstår alla anpassade funktioner i varje skrivarmodell.

Det här behovet av att visa anpassade utskriftsinställningar åtgärdas av det här API:et med ett valfritt UWP-tilläggskontrakt som kan aktiveras av användaren från alla Utskriftsdialogrutor i Windows och anpassade utskriftsdialogrutor som använder API:et som tillhandahålls av Windows. Tillverkarna kan skräddarsy sitt användargränssnitt för att ge den bästa utskriftsupplevelsen för den specifika skrivare som användaren äger.

Ett annat område där skrivartillverkarna kan förbättra och särskilja är utskriftskvaliteten. Tillverkare kan förbättra utskriftskvaliteten efter återgivningen genom att optimera innehållet för den specifika skrivaren. De kan också presentera en förhandsversion med hög återgivning som bättre representerar de slutliga utdata eftersom det kan ta hänsyn till skrivarspecifika funktioner.

tidslinje för stöd av utskriftsappar

Terminologi

Termin Definition
PSA Utskriftsstödprogram. En UWP-app som använder API:et som beskrivs i den här artikeln.
MPD Dialogrutan för modern utskrift. Detta visas för användaren när en app skriver ut med hjälp av API:et Windows.Graphics.Printing.
CPD Gemensam utskriftsdialogruta. Detta visas för användaren när appen skriver ut med win32-API:et. Appar som behöver visa förhandsgranskning av utskrift utlöser inte den här dialogrutan utan implementerar sin egen version av dialogrutan. Office-appar är ett utmärkt exempel på detta.
IPP Internet Printing Protocol. Används från en klientenhet för att interagera med skrivaren för att hämta och ange utskriftsinställningar och för att skicka dokumentet som ska skrivas ut.
Utskriftsstöd associerad skrivare Skrivare som är länkad till PSA.
IPP-skrivare Skrivare som stöder IPP-protokoll.
Fler inställningar Länk som öppnar det partnerhanterade appgränssnittet i MPD. Standardinställningen är att öppna det inbyggda användargränssnittet för utskriftsinställningar när det inte finns någon PSA installerad.
Användargränssnitt för skrivarinställningar Dialogruta som används för att ange standardalternativ för skrivare som används vid utskrift. Till exempel: orientering, pappersstorlek, färg, utskrift på båda sidor och så vidare.
PDL Språk för sidbeskrivning. Formatet där ett dokument skickas till skrivaren.
Associerad PSA-skrivare Fysisk IPP-skrivare som är associerad med ett PSA-program.
Utskriftsenhetens kapacitet XML-dokumentformat för att definiera skrivarfunktioner. För mer information, se Utskriftsbiljett och utskriftsmöjlighetstekniker.
Utskriftsbiljett Samling av olika utskriftsrelaterade funktioner och deras värden som används för att avbilda användarens avsikt för ett visst utskriftsjobb.
PrintSupportExtension PSA-bakgrundsaktivitet som ansvarar för att tillhandahålla möjligheter för utökning av skrivarbegränsningar.

De här exemplen refererar till ett printsupport namnområde, som definieras som:

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

När en användare håller på att skriva ut ett dokument vill de ofta ange några inställningar som de ska skriva ut det med. De kan till exempel välja att skriva ut ett dokument i liggande orientering. De kan också dra nytta av en anpassad funktion som skrivaren stöder. Windows tillhandahåller standardgränssnittet för att visa anpassade inställningar, men användaren kanske inte förstår dem eftersom det inte finns några lämpliga ikoner eller beskrivningar. Windows kan också använda fel användargränssnittskontroll för att presentera den. En sådan anpassad funktion presenteras bäst av en app som förstår funktionen helt. Det här är motivationen bakom att erbjuda ett API som gör att skrivartillverkarna kan skapa appar som är skräddarsydda för de olika skrivarmodeller som de skapar.

Ett nytt UAP-tilläggskontrakt skapas med en ny kategori med namnet windows.printSupportSettingsUI. Appar som aktiveras med det här kontraktet får ett nytt ActivationKind med namnet PrintSupportSettingsUI. Det här kontraktet kräver ingen ny funktion.

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

Det här kontraktet anropas när användaren väljer Fler inställningar i MPD eller Inställningar i CPD. Det här kontraktet kan också anropas från utskriftsinställningar i appen Inställningar. När kontraktet aktiveras tar appen emot ett PrintSupportSettingsUISession objekt som kan användas för att hämta den aktuella PrintTicket och PrintDevice objekt. Objektet PrintDevice kan användas för kommunikation med skrivaren för att ta emot skrivar- och jobbattribut. Appen kan sedan visa användargränssnittet med lämpliga alternativ för skrivaren för användaren. När användaren gör valen och väljer OKkan programmet sedan ändra utskriftsbiljetten, validera den och sedan skicka tillbaka med hjälp av PrintSupportPrintTicketTarget objekt. Om användaren väljer att avbryta inställningsfönstret bör ändringarna ignoreras och programmet bör avslutas genom att slutföra uppskjutningen från PrintSupportSettingsUISession-objektet.

Utskriftssupportappen förväntas hantera flera samtidiga aktiveringar för olika utskriftsjobb, så en sådan app måste ha stöd för flera instanser med hjälp av SupportsMultipleInstances-elementet i filen package.appxmanifest. Om du inte gör det kan det leda till situationer där bekräftande inställningar för ett utskriftsjobb kan stänga andra inställningsfönster som kan vara öppna. Användaren måste öppna dessa inställningar igen.

Följande sekvensdiagram visar konceptet med inställningar för UI-utskriftsbiljett.

sekvensdiagram över användargränssnittets inställningar för biljettutskriftshantering

Ändra PrintTicket i inställningsgränssnittet

C#-exempelkod för aktivering av användargränssnittet för inställningar när det startas från en utskriftsdialogruta (MPD/CPD eller anpassad utskriftsdialogruta) eller från systeminställningar:

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

XAML för DefaultSettingsView-klass:

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

C#-exempelkod för att visa användargränssnittet och ändra PrintTicket:

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

Hämta skrivarattribut från skrivarenheten

WireShark-svar från en IPP-skrivare till en fråga med get-printer-attributes:

Wireshark-svar från en I P P-skrivare till en fråga om att hämta skrivarattribut

C#-exempelkod för att hämta bläcknamn och bläcknivåer från skrivaren.

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

Ange skrivarattribut på skrivaren

C#-exempelkod för att ange skrivarattribut:

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

Utöka skrivarbegränsningar

Utskriftssupportappen stöder anpassad PrintTicket-validering och definierar standardutskriftsfunktionen. I det här avsnittet beskrivs hur vi stöder dessa funktioner.

För att stödja begränsningar för skrivartillägg har en ny bakgrundsaktivitetstyp, PrintSupportExtension, implementerats. Package.appxmanifest har en utökningsbarhetspost för utskriftssupporttillägget som visas här:

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

Den här tjänsten kan köras när som helst i ett utskriftsjobb för den associerade IPP-skrivaren. Eftersom utskriftssupporttillägget aktiveras via funktionen IBackgroundTaskInstanceges en instans av IBackgroundTaskInstance till PrintSupportExtension för att ge åtkomst till klassen PrintSupportExtensionTriggerDetails runtime, som internt tillhandahåller PrintSupportExtensionSession som en egenskap. Bakgrundsklassen PrintSupportExtension kan sedan använda sessionsobjektet för att registrera sig för de händelser där den vill tillhandahålla anpassad funktionalitet.

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

    Om utskriftssupporttillägget har en egen PrintTicket- valideringsmekanism kan det registrera sig för den här händelsen. När en PrintTicket behöver verifieras, utlöser utskriftssystemet den här händelsen. PrintSupportExtension får sedan den aktuella PrintTicket som behöver valideras inom EventArgs. Den PrintSupportExtension bakgrundsklass kan sedan kontrollera PrintTicket för giltighet och ändra den för att lösa eventuella konflikter. PrintSupportExtension bakgrundsklass bör sedan ange resultatet för validering med hjälp av funktionen SetPrintTicketResult för att ange om PrintTicket- har lösts, har konflikter eller är ogiltig. Den här händelsen kan aktiveras när som helst under ett utskriftsjobbs livslängd. Om klassen PrintSupportExtension inte registreras för den här händelsen, utför utskriftssystemet sin egen validering av PrintTicket.

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

    Händelsen utlöses när utskriftssystemet uppdaterar den cachelagrade PrintDeviceCapabilities för den associerade IPP-skrivaren. När den här händelsen utlöses kan den PrintSupportExtension bakgrundsklassen inspektera den ändrade PrintDeviceCapabilities och ändra den.

Anpassad validering av utskriftsbiljett

C#-exempelkod för att tillhandahålla PrintTicket valideringstjänst:

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

Uppdaterar utskriftsenhetens kapabiliteter

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

Förbättrad utskriftskvalitet

När användaren har åtagit sig att skriva ut genom att trycka på utskriftsknappen i utskriftsdialogrutan skickas dokumentet som ska skrivas ut till utskriftsstacken från appen som skriver ut. Det här dokumentet genomgår sedan transformering (återgivning till PDL) för att göra det lämpligt för målskrivaren. Windows avgör vilken transformering som ska väljas baserat på attribut som efterfrågas från skrivaren. Det transformerade dokumentet skickas sedan till skrivaren. Detta fungerar bra för de flesta skrivare, men det finns fall där utskriftskvaliteten kan förbättras genom att låta en partnerapp delta i omvandlingen. För att underlätta detta utökas det aktuella arbetsflödes-API:et för utskrift till att omfatta anrop till appen vid ytterligare punkter från utskriftsstacken. Det här API:et stöder två nya händelser som PSA-appen kan registrera sig för. Det här är de enda startpunkterna i PSA API-ytan:

  1. Jobbstart

    • Den här händelsen utlöses när ett utskriftsjobb startas av ett program. När händelsen aktiveras kan en utskriftssupportapp välja att hoppa över systemrendering genom att anropa SetSkipSystemRenderingPrintWorkflowJobStartingEventArgs. Om hoppa över systemrendering väljs konverterar inte utskriftssystemet XPS-dokumentet till det PDL-format som krävs av skrivaren. I stället ges DEN XPS som genereras av utskriftsprogrammet direkt till PSA som sedan ansvarar för att konvertera XPS till PDL-format.
  2. PdlModificationRequested

    • Den här händelsen utlöses när Windows startar konverteringen av XPS-strömmen till det PDL-format som anges av skrivaren. Körningsklassen PrintWorkflowPdlModificationRequestedEventArgs anges som ett argument för den här händelsen. Den här händelseklassen innehåller PDL-käll- och målobjekt för att läsa och skriva utskriftsjobbets innehåll. Om appen bedömer att den behöver användarindata kan den starta användargränssnittet med hjälp av PrintWorkflowUILauncher från EventArgs. Det här API:et använder Tester-Doer-mönstret. PrintWorkflowUILauncher kan inte anropa användargränssnittet om funktionen IsUILaunchEnabled returnerar false. Den här funktionen returnerar false om PSA-sessionen körs i tyst läge (huvudlöst eller kioskläge). Utskriftssupportappen bör inte försöka starta användargränssnittet om funktionen returnerar false.

    En OutputStream är tillgänglig som en del av PrintWorkflowPdlTargetStream som returneras av funktionen GetStreamTargetAsync. Innehåll som skrivs till målet OutputStream skickas vidare till skrivaren som dokumentinnehåll.

Sekvensdiagram för PDL-ändringshändelsen:

sekvensdiagram för källströmmens P D L-ändringshändelse

PSA-förgrundsprogrammet startas när PSA-bakgrundsaktiviteten begär att användargränssnittet startas. PSA kan använda förgrundskontraktet för att hämta användarindata och/eller för att visa en förhandsversion av utskriften för användaren.

En ny printSupportWorkflow bakgrundsaktivitetstyp har definierats. Package.appxmanifest har följande utökningspost för PrintSupportWorkflow kontrakt:

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

Vid aktivering av kontraktet anges PrintWorkflowJobTriggerDetails som IBackgroundTaskInstance,>TriggerDetails. PrintWorkflowJobTriggerDetails internt tillhandahåller PrintWorkflowJobBackgroundSession som en del av dess egenskaper. Appen kan använda PrintWorkflowJobBackgroundSession för att registrera sig för händelser relaterade till olika inmatningspunkter i arbetsflödet för utskriftsjobbet. När händelseregistreringen är klar måste appen anropa PrintWorkflowJobBackgroundSession::Starta för att utskriftssystemet ska börja utlösa händelser relaterade till olika inmatningspunkter.

En ny ActivationKind med namnet PrintSupportJobUI definieras. Detta kräver ingen ny funktion.

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

Det här är ett användargränssnittskontrakt som kan startas antingen från bakgrundskontraktet för arbetsflödet för utskriftssupport eller när användaren väljer ett felmeddelande för utskriftsjobb. Vid aktivering tillhandahålls PrintWorkflowJobActivatedEventArgs, vilket har ett objekt av typen PrintWorkflowJobUISession. Med PrintWorkflowJobUISessionbör förgrundsprogrammet registrera sig för händelsen PdlDataAvailable om det vill komma åt PDL-data. Om förgrundsprogrammet vill visa anpassade felmeddelanden för eventuella fel som kan inträffa under jobbet bör det registreras för JobNotification händelse. När händelserna har registrerats ska programmet anropa funktionen PrintWorkflowJobUISession::Starta för att utskriftssystemet ska starta utlösande händelser.

Hoppar över systemrendering

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-ändringshändelse

Sekvensdiagram för PDL-ändringshändelsen:

sekvensdiagram för indataströmmens P D L-ändringshändelse

C#-exempelkod för utskriftsstödets arbetsövervakare för att läsa och skriva innehållet i utskriftsjobb:

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

Starta användargränssnittet från arbetsflödets bakgrund

C#-exempelkod för att starta användargränssnittet för utskriftssupportjobb från PSA PDL-ändring begärt händelsekontrakt:

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

Aktivering av arbetsflödesjobbsgränssnitt för PDLDataAvailable-händelse

Sekvensdiagram för aktivering av användargränssnittet för utskriftsjobb för PdlDataAvailable händelse:

sekvensdiagram för aktivering av utskriftsjobbets UI vid händelsen för tillgängliga PDL-data

C#-exempelkod för psa-jobbgränssnittets aktiveringskontrakt:

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

Hämta skrivarjobbets attribut

C#-exempelkod för att hämta jobbattribut för ett utskriftsjobb:

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

Ange attribut för skrivarjobb

C#-exempelkod, som fortsätter från avsnittet Hämta skrivarjobbattribut ovan, som visar hur du anger jobbattribut:

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

Vissa IPP-skrivare stöder inte att hämta/ange jobbattribut när jobbet har skapats. För dessa skrivare har PrintJob egenskapen JobId inställd på "0" och GetJobAttributes/SetJobAttributes misslyckas omedelbart med ett undantag.

Ge lagringsfilåtkomst till PDL-innehåll

Vissa PDL-format som PDF behöver en fullständig dataström för att kunna börja bearbetas. Därför tillhandahålls en ny metod med namnet GetContentFileAsync på klassen PrintWorkflowPdlSourceContent som returnerar en StorageFile- av källinnehållet.

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

PDL-konvertering av XPS till PDF

C#-exempelkod som visar PDL-konvertering av XPS till PDF:

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

Jobbmeddelandehändelse

Sekvensdiagram för jobbmeddelandehändelse:

sekvensdiagram för jobbmeddelandehändelsen

C#-exempelkod, som fortsätter från arbetsflödesjobbets användargränssnittsaktivering för PDLDataAvailable händelseavsnittet ovan, för att visa fel vid jobbavisering:

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

Skapa tjänst med initiala attribut

För närvarande stöder vissa IPP-skrivare inte åtgärden set-attribute. Funktionerna CreateJobOnPrinterWithAttributes och CreateJobOnPrinterWithAttributesBufferPrintWorkflowPdlDataAvailableEventArgs tillhandahålls för att lösa detta problem. Med hjälp av dessa API:er kan en PSA-utvecklare tillhandahålla jobbattribut som skickas till skrivaren när jobbet skapas på skrivaren.

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

Sekventiell XPS-bearbetning

C++/Winrt-exempelkod för bearbetning av XPS sekventiellt innan spoolingen har slutförts.

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

Visningsnamnslokalisering och PDL Passthrough API-integration

Viktig

I det här avsnittet beskrivs psa-funktioner som är tillgängliga från och med Windows 11, version 22H2.

I det här scenariot anpassar PSA utskriftsenhetens funktioner (PDC) och tillhandahåller resurser för utskriftsenheten (PDR) för lokalisering av strängar.

PSA anger även de api-innehållstyper för PDL-genomströmning som stöds (PDL-format). Om PSA inte prenumererar på händelsen eller inte uttryckligen anropar SetSupportedPdlPassthroughContentTypes, inaktiveras PDL-passagen för de skrivare som är associerade med denna PSA-app.

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

Funktionsstöd och åtgärdsattribut på sidnivå

Viktig

I det här avsnittet beskrivs psa-funktioner som är tillgängliga från och med Windows 11, version 22H2.

Scenarier för funktionsstöd och åtgärdsattribut på sidnivå grupperas eftersom de åtgärdas genom att göra ändringar på samma plats i exempelkoden.

  • Funktionsstöd för sidnivå: I det här scenariot anger PSA-programmet sidnivåattributet, som inte ska åsidosättas av ett IPP-attribut som parsas från PrintTicket.

  • Separat samling för stöd för åtgärdsattribut (PIN-utskrift): I det här scenariot anger PSA-programmet anpassade IPP-åtgärdsattribut (till exempel PIN-kod).

Följande C#-exempelkod visar nödvändiga ändringar för funktionsstöd för sidnivå och Separat samling för åtgärdsattribut scenarier.

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

Förbättra utskriftsdialogrutan med PSA

Viktig

I det här avsnittet beskrivs psa-funktioner som är tillgängliga från och med Windows 11, version 22H2.

I det här scenariot möjliggör utskriftsdialogrutan med PSA-integrering följande åtgärder:

  • Få en återuppringning när markeringen ändras i MPD för skrivaren som är associerad med PSA

  • Visa ett AdaptiveCard med stöd för openUrl- åtgärd

  • Visa anpassade funktioner och parametrar i utskriftsdialogrutan

  • Ändra PrintTicket, vilket ändrar valet för funktionsalternativ som visas i utskriftsdialogrutan

  • Hämta Windows.ApplicationModel.AppInfo för utskriftsappen, och öppna sedan utskriftsdialogrutan

Följande C#-exempel illustrerar de här förbättringarna av utskriftsdialogrutan:

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-konvertering med värdbaserade bearbetningsflaggor

Viktig

I det här avsnittet beskrivs psa-funktioner som är tillgängliga från och med Windows 11, version 22H2.

Det aktuella API:et för PDL-konvertering, PrintWorkflowPdlConverter.ConvertPdlAsync, utför värdbaserad bearbetning som standard. Det innebär att värd-/utskriftsdatorn utför rotationen, sidordningen och så vidare, så att skrivaren inte behöver utföra dessa åtgärder. Skrivar-IHV:er kanske dock vill ha PDL-konvertering utan värdbaserad bearbetning eftersom skrivaren kan göra detta bättre. Funktionen ConvertPdlAsync använder värdbaserade bearbetningsflaggor för att uppfylla detta krav. PSA kan hoppa över all värdbaserad bearbetning eller en viss värdbaserad bearbetningsåtgärd med den här flaggan.

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
}

Ställ in uppdateringspolicy för utskriftsenheters kapabiliteter (PDC)

Viktig

I det här avsnittet beskrivs psa-funktioner som är tillgängliga från och med Windows 11, version 22H2.

Skrivar-IHV:er kan ha olika krav på när utskriftsenhetens funktioner (PDC) måste uppdateras. För att uppfylla dessa krav kan PrintSupportPrintDeviceCapabilitiesUpdatePolicy ange en uppdateringspolicy för PDC:n. PSA kan ange PDC-uppdateringsprincipen baserat på tid eller antalet utskriftsjobb med hjälp av det här API:et.

Ange PDC-uppdateringsprincip baserat på antalet jobb

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

Ange PDC-uppdateringsprincip baserat på TimeOut

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

Designvägledning för allmän utskriftssupportapp (PSA)

När du utformar en utskriftssupportapp är det viktigt att ta med dessa aspekter i designen:

  • Både förgrunds- och bakgrundskontrakt bör markeras som stöd för flera instanser, till exempel SupportsMultipleInstance ska finnas i paketmanifestet. Detta för att säkerställa att kontraktens livslängd kan hanteras på ett tillförlitligt sätt för flera samtidiga jobb.

  • Behandla startgränssnittet för PDL-ändring som ett valfritt steg. Gör det bästa för att slutföra utskriftsjobbet även om det inte var tillåtet att starta användargränssnittet. Utskriftsjobb bör bara avbrytas om det inte finns något sätt att slutföra dem utan användaringripande vid förändring av PDL. Överväg att skicka PDL omodifierat i sådana fall.

  • När du lanserar UI för ändringar av PDL, anropa IsUILaunchEnabled innan du anropar LaunchAndCompleteUIAsync. Detta är för att säkerställa att scenarier som inte kan visa användargränssnittet vid den aktuella tidpunkten fortsätter att skrivas ut korrekt. Dessa scenarier kan vara på en huvudlös enhet eller en enhet som för närvarande är i kioskläge eller stör ej-läge.

Serviceplan upphör för skrivardrivrutiner från tredje part i Windows

IPP-specifikation (Internet Printing Protocol)

Stöd för appassociation för utskrift

Windows.Devices.Printers

Windows.Graphics.Printing.PrintSupport

Windows.Graphics.Printing.Workflow