Dela via


Skrivartillägg

Viktigt!

Den moderna utskriftsplattformen är Windows bästa sätt att kommunicera med skrivare. Vi rekommenderar att du använder Microsofts IPP-drivrutin för inkorgsklass tillsammans med Print Support Apps (PSA) för att anpassa utskriftsupplevelsen i Windows 10 och 11 för enhetsutveckling av skrivare.

Mer information finns i designguiden för utskriftssupportapp v1 och v2.

Appar för skrivartillägg stöder utskriftsinställningar och skrivarmeddelanden när användare kör befintliga program på Windows-skrivbordet.

Skrivartillägg kan byggas på alla COM-kompatibla språk, men är optimerade för att skapas med Microsoft .NET Framework 4. Skrivartillägg kan distribueras med ett utskriftsdrivrutinspaket om de är XCopy-kompatibla och inte har några andra beroenden för externa körningar än de som ingår i operativsystemet, till exempel .NET. Om skrivartilläggsappen inte uppfyller dessa kriterier kan den distribueras i ett setup.exe- eller MSI-paket och annonseras i skrivarens Device Stage med hjälp av printerExtensionUrl-direktivet som anges i v4-manifestet. När en app för skrivartillägg distribueras via ett MSI-paket kan du lägga till utskriftsdrivrutinen i paketet eller utelämna den och distribuera drivrutinen separat. PrinterExtensionUrl visas i skrivarens preferensupplevelse.

IT-administratörer har några alternativ för att hantera distributionen av skrivartillägg. Om programmet är paketerat i en setup.exe eller MSI kan IT-administratörer använda standardverktyg för programvarudistribution, till exempel Microsoft Endpoint Configuration Manager, eller så kan de inkludera programmen i sin standard-OS-avbildning. IT-administratörer kan också åsidosätta PrinterExtensionUrl som anges i v4-manifestet, om de redigerar HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\<utskriftskönamn>\PrinterDriverData\PrinterExtensionUrl.

Och om ett företag väljer att helt blockera skrivartillägg kan detta göras via en grupprincip med namnet "Datorkonfiguration\Administrativa mallar\Skrivare\Tillåt inte att v4-skrivardrivrutiner visar skrivartilläggsprogram".

Skapa ett skrivartillägg

När du utvecklar ett skrivartillägg finns det sex huvudsakliga fokusområden som du måste känna till. Dessa fokusområden visas i följande lista.

  • Registrering

  • Aktivera händelser

  • OnDriverEvent-hanterare

  • Utskriftsinställningar

  • Skrivarmeddelanden

  • Hantera skrivare

Registrering

Skrivartillägg registreras med utskriftssystemet genom att ange en uppsättning registernycklar eller genom att ange programinformationen i avsnittet PrinterExtensions i v4-manifestfilen.

Det finns angivna GUID:er som stöder var och en av de olika startpunkterna för skrivartillägg. Du behöver inte använda dessa GUID:er i v4-manifestfilen, men du måste känna till GUID-värdena för att använda registerformatet för installation av v4-drivrutin. I följande tabell visas GUID-värdena för de två startpunkterna.

Startpunkt GUID (globalt unikt identifierare)
Utskriftsinställningar {EC8F261F-267C-469F-B5D6-3933023C29CC}
Skrivarmeddelanden {23BB1328-63DE-4293-915B-A6A23D929ACB}

Skrivartillägg som installeras utanför skrivardrivrutinen måste registreras med hjälp av registret. Detta säkerställer att skrivartillägg kan installeras oavsett status för bufferten eller v4-konfigurationsmodulen på klientdatorn.

När tjänsten PrintNotify startar söker den efter registernycklar under sökvägen [OfflineRoot] och bearbetar eventuella väntande registreringar eller avregistreringar. När eventuella väntande registreringar eller avregistreringar har slutförts tas registernycklarna bort i realtid. Om du använder ett skript eller en iterativ process för att placera registernycklar kan du behöva återskapa nyckeln \[PrinterExtensionID] varje gång du anger en \[PrinterDriverId]-nyckel. Ofullständiga eller felaktiga nycklar tas inte bort.

Den här registreringen är endast nödvändig vid första installationen. I följande exempel visas rätt registernyckelformat som används för att registrera skrivartillägg.

Anmärkning

[OfflineRoot] används som förkortning för HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\OfflinePrinterExtensions.

[OfflineRoot]
    \[PrinterExtensionId] {GUID}
           AppPath=[PrinterExtensionAppPath] {String}
           \[PrinterDriverId] {GUID}
                  \[PrinterExtensionReasonGuid]
(default) = ["0"|"1"] {REG_SZ 0:Unregister, 1:Register}
                  \…
                  \[PrinterExtensionReasonGuidN]
           \[PrinterDriverId2]
                  \[PrinterExtensionReasonGuid2.1]
                  \…
                  \[PrinterExtensionReasonGuid2.Z]
           …
           \[PrinterDriverIdM]
    \[PrinterExtensionId2]
    …
    \[PrinterExtensionIdT]

Följande uppsättning nycklar skulle till exempel registrera ett skrivartillägg med {PrinterExtensionIDGuid} PrinterExtensionID och en fullständigt kvalificerad sökväg till den körbara filen "C:\Program Files\Fabrikam\pe.exe" för {PrinterDriverID1Guid} och {PrinterDriverID2Guid} SkrivarDrivrutinsID:n, tillsammans med skrivarinställningar och skrivarnotifieringsorsaker.

[OfflineRoot]
    \{PrinterExtensionIDGuid}
           AppPath="C:\Program Files\Fabrikam\pe.exe"
           \{PrinterDriverID1Guid}
                 \{EC8F261F-267C-469F-B5D6-3933023C29CC}
            (default) = "1"
                 \{23BB1328-63DE-4293-915B-A6A23D929ACB}
            (default) = "1"
           \{PrinterDriverID1Guid}
                 \{EC8F261F-267C-469F-B5D6-3933023C29CC}
            (default) = "1"
                 \{23BB1328-63DE-4293-915B-A6A23D929ACB}
            (default) = "1"

Om du vill avinstallera samma skrivartillägg ska följande uppsättning nycklar anges.

[OfflineRoot]
    \{PrinterExtensionIDGuid}
           AppPath="C:\Program Files\Fabrikam\pe.exe"
           \{PrinterDriverID1Guid}
                 \{EC8F261F-267C-469F-B5D6-3933023C29CC}
            (default) = "0"
                 \{23BB1328-63DE-4293-915B-A6A23D929ACB}
            (default) = "0"
           \{PrinterDriverID1Guid}
                 \{EC8F261F-267C-469F-B5D6-3933023C29CC}
            (default) = "0"
                 \{23BB1328-63DE-4293-915B-A6A23D929ACB}
            (default) = "0"

Eftersom skrivartillägg kan köras både i en användarlanserad kontext och i en händelselanseringskontext är det bra att kunna fastställa i vilken kontext skrivartillägget används. På så sätt kan en app till exempel inte räkna upp statusen för alla köer om den har startats för en avisering eller utskriftsinställningar. Microsoft rekommenderar att skrivartillägg som installeras separat från drivrutinen (till exempel med en MSI eller setup.exe) ska använda kommandoradsväxlar antingen på genvägarna på Start-menyn eller i AppPath-posten som fylldes i i registret under registreringen. Eftersom skrivartillägg som är installerade med drivrutinen installeras i DriverStore startas inte dessa utanför utskriftsinställningarna eller skrivarmeddelandenas händelser. Därför stöds inte att ange kommandoradsväxlar i det här fallet.

När skrivartillägget registreras för det aktuella PrinterDriverID måste det innehålla PrinterDriverID i AppPath. För en skrivartilläggsapp med namnet printerextension.exeoch ett PrinterDriverID-värde för {GUID} ser [PrinterExtensionAppPath] ut som i följande exempel:

"C:\program files\fabrikam\printerextension.exe {GUID}"

Aktivera händelser

Vid körning måste skrivartillägg aktivera händelseutlösande för det aktuella PrinterDriverID. Det här är PrinterDriverID som skickades till appen via matrisen args[] och gör det möjligt för utskriftssystemet att tillhandahålla en lämplig händelsekontext av hanteringsskäl som utskriftsinställningar eller skrivarmeddelanden.

Så programmet bör skapa en ny PrinterExtensionManager för den nuvarande PrinterDriverID, registrera en delegering för att hantera händelsen OnDriverEvent och anropa metoden EnableEvents med PrinterDriverID. Följande kodfragment illustrerar den här metoden.

PrinterExtensionManager mgr = new PrinterExtensionManager();
mgr.OnDriverEvent += OnDriverEvent;
mgr.EnableEvents(new Guid(PrinterDriverID1));

Om en app inte anropar EnableEvents inom 5 sekunder överskrider Windows tidsgränsen och startar ett standardgränssnitt. För att undvika detta bör skrivartillägg följa de senaste metodtipsen för prestanda, inklusive följande:

  • Fördröj så mycket av appinitieringen som möjligt tills du har anropat EnableEvents. Därefter prioriterar du UI-svarstider med hjälp av asynkrona metoder och blockerar inte användargränssnittstråden under initieringen.

  • Använd ngen för att generera en nativ avbildning under installationen. Mer information finns i Inbyggd avbildningsgenerator.

  • Använd verktyg för prestandamätning för att hitta prestandaproblem vid inläsning. Mer information finns i Verktyg för prestandaanalys i Windows.

DriverEvent-hanterare

När en OnDriverEvent-hanterare har registrerats och händelser har aktiverats, anropas hanteraren om skrivartillägget startades för att hantera utskriftsinställningar eller skrivarmeddelanden. I föregående kodfragment registrerades en metod med namnet OnDriverEvent som händelsehanterare. I följande kodfragment är parametern PrinterExtensionEventArgs det objekt som gör att scenarier för utskriftsinställningar och skrivarmeddelanden kan skapas. PrinterExtensionEventArgs är en wrapper för IPrinterExtensionEventArgs.

static void OnDriverEvent(object sender, PrinterExtensionEventArgs eventArgs)
{
    //
    // Display the print preferences window.
    //

    if (eventArgs.ReasonId.Equals(PrinterExtensionReason.PrintPreferences))
    {
        PrintPreferenceWindow printPreferenceWindow = new PrintPreferenceWindow();
        printPreferenceWindow.Initialize(eventArgs);

        //
        // Set the caller application's window as parent/owner of the newly created printing preferences window.
        //

        WindowInteropHelper wih = new WindowInteropHelper(printPreferenceWindow);
        wih.Owner = eventArgs.WindowParent;

        //
        // Display a modal/non-modal window based on the 'WindowModal' parameter.
        //

        if (eventArgs.WindowModal)
        {
            printPreferenceWindow.ShowDialog();
        }
        else
        {
            printPreferenceWindow.Show();
        }
    }

    //
    // Handle driver events.
    //

    else if (eventArgs.ReasonId.Equals(PrinterExtensionReason.DriverEvent))
    {
        // Handle driver events here.
    }
}

För att förhindra en felaktig användarupplevelse som är associerad med krascher eller långsamma skrivartillägg implementerar Windows en tidsgräns om EnableEvents inte anropas inom en kort tid efter att appen har startats. För att aktivera felsökning inaktiveras den här tidsgränsen om det finns ett felsökningsprogram kopplat till PrintNotify-tjänsten.

I de flesta fall körs dock all apprelaterad kod som vi är intresserade av, under eller efter återanropet till OnDriverEvent. Under utvecklingen kan det också vara bra att visa en meddelanderuta innan du startar utskriftsinställningar eller skrivarmeddelanden från OnDriverEvent-återanropet. När MessageBox visas går du tillbaka till Visual Studio och väljer Felsöka>Bifoga till process och väljer namnet på din process. Gå slutligen tillbaka till Meddelandelådan och välj OK för att återuppta. Detta säkerställer att du ser undantag och träffar eventuella brytpunkter från den punkten och framåt.

Nya ReasonIds kan stödjas i framtiden. Därför måste skrivartillägg uttryckligen kontrollera ReasonID och får inte använda en "else"-instruktion för att identifiera det senast kända ReasonID. Om ett ReasonID tas emot och är okänt bör appen avslutas korrekt.

Utskriftsinställningar drivs av objektet PrintSchemaEventArgs.Ticket. Det här objektet kapslar in både PrintTicket- och PrintCapabilities-dokumenten som beskriver funktionerna och alternativen för en enhet. Den underliggande XML-koden är också tillgänglig, men objektmodellen gör det enklare att arbeta med dessa format.

I varje IPrintSchemaTicket - eller IPrintSchemaCapabilities-objekt finns funktioner (IPrintSchemaFeature) och alternativ (IPrintSchemaOption). Även om de gränssnitt som används för funktioner och alternativ är desamma oavsett ursprung, varierar beteendet något till följd av den underliggande XML-koden. Till exempel anger PrintCapabilities-dokument många alternativ per funktion, medan PrintTicket-dokument endast anger det valda alternativet (eller standardalternativet). På samma sätt anger PrintCapabilities-dokument lokaliserade visningssträngar, medan PrintTicket-dokument inte gör det.

Mer information om databindning i WPF finns i Översikt över databindning.

För att maximera prestanda rekommenderar Microsoft att anrop till GetPrintCapabilities endast görs när det är nödvändigt att uppdatera PrintCapabilities-dokumentet.

När en användare gör val med hjälp av de databundna ComboBox-kontrollerna uppdateras PrintTicket-objektet automatiskt. När användaren slutligen klickar på OK börjar en kedja med asynkron validering och slutförande. Det här asynkrona mönstret används i stor utsträckning för att förhindra att långvariga processer förekommer på UI-trådar och orsakar att användargränssnittet för utskriftsinställningar eller den utskrivande appen hänger sig. Följande är en lista över de steg som används för att bearbeta PrintTicket-ändringarna när användaren klickar på OK.

  1. PrintSchemaTicket verifieras asynkront med hjälp av metoden IPrintSchemaTicket::ValidateAsync .

  2. När den asynkrona valideringen är klar anropar COMMON Language Runtime (CLR) metoden PrintTicketValidateCompleted.

    1. Om valideringen lyckades anropas metoden CommitPrintTicketAsync och CommitPrintTicketAsync anropar metoden IPrintSchemaTicket::CommitAsync . När uppdateringen av PrintTicket har slutförts anropas metoden PrintTicketCommitCompleted, som anropar en bekvämlighetsmetod som anropar metoden PrinterExtensionEventArgs.Request.Complete för att indikera att utskriftsinställningarna är slutförda och sedan stänger appen.

    2. Annars visas användargränssnittet för användaren för att hantera villkorssituationen.

Om användaren klickade på avbryt eller stängde fönstret för utskriftsinställningar direkt anropar skrivartillägget IPrinterExtensionEventArgs.Request.Cancel med ett lämpligt HRESULT-värde och meddelande för felloggen.

Om processen för skrivartillägget har stängts och inte anropat metoderna Slutför eller Avbryt enligt beskrivningen i föregående stycken, återgår utskriftssystemet automatiskt till att använda användargränssnittet från Microsoft.

För att hämta information om enhetsstatus kan skrivartillägg använda Bidi för att fråga utskriftsenheten. Om du till exempel vill visa pennanteckningsstatus eller andra typer av status för enheten kan skrivartillägg använda metoden IPrinterExtensionEventArgs.PrinterQueue.SendBidiQuery för att skicka Bidi-frågor till enheten. Att hämta den senaste Bidi-statusen är en tvåstegsprocess där du konfigurerar en händelsehanterare för händelsen OnBidiResponseReceived och anropar metoden SendBidiQuery med en giltig Bidi-fråga. Följande kodfragment visar den här tvåstegsprocessen.

PrinterQueue.OnBidiResponseReceived += new
EventHandler<PrinterQueueEventArgs>(OnBidiResponseReceived);
PrinterQueue.SendBidiQuery("\\Printer.consumables");

När Bidi-svaret tas emot anropas följande händelsehanterare. Den här händelsehanteraren har också en simulerad implementering av pennanteckningsstatus, vilket kan vara användbart för utveckling när en enhet inte är tillgänglig. Objektet PrinterQueueEventArgs innehåller både ett HRESULT- och ett Bidi XML-svar. Mer information om Bidi XML-svar finns i Bidi-begärande- och svarsscheman.

private void OnBidiResponseReceived(object sender, PrinterQueueEventArgs e)
{
    if (e.StatusHResult != (int)HRESULT.S_OK)
    {
        MockInkStatus();
        return;
    }

    //
    // Display the ink levels from the data.
    //

    BidiHelperSource = new BidiHelper(e.Response);
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs("BidiHelperSource"));
    }
    InkStatusTitle = "Ink status (Live data)";
}

Skrivarmeddelanden

Skrivarmeddelanden anropas på exakt samma sätt som utskriftsinställningar. Om IPrinterExtensionEventArgs i OnDriverEvent-hanteraren anger att ett ReasonID matchar DriverEvents GUID kan vi skapa en upplevelse för att hantera den här händelsen.

Följande variabler är mest användbara när det gäller att hantera en funktionsduglig skrivaravisering.

  • PrinterExtensionEventArgs.BidiNotification – Detta bär den Bidi XML som gjorde att händelsen utlöstes.

  • PrinterExtensionEventArgs.DetailedReasonId – Detta innehåller eventID GUID från xml-filen för drivrutinshändelsen.

Det viktigaste attributet från IPrinterExtensionEventArgs-objektet för meddelanden är egenskapen BidiNotification. Detta bär den Bidi XML som gjorde att händelsen utlöstes. Mer information om Bidi XML-svar finns i Bidi-begärande- och svarsscheman.

Hantera skrivare

För att stödja rollen för ett skrivartillägg som en app som kan användas som hubb för att hantera/underhålla skrivare är det möjligt att räkna upp de utskriftsköer som det aktuella skrivartillägget är registrerat för och få status för varje kö. Detta visas inte i projektet PrinterExtensionSample, men följande kodfragment kan läggas till i main-metoden för App.xaml.cs för att registrera en händelsehanterare.

mgr.OnPrinterQueuesEnumerated += new EventHandler<PrinterQueuesEnumeratedEventArgs>(mgr_OnPrinterQueuesEnumerated);

När köerna har räknats upp anropas händelsehanteraren och statusåtgärder kan utföras. Den här händelsen utlöses regelbundet under appens livslängd för att säkerställa att listan över uppräknade utskriftsköer är aktuell, även om användaren har installerat fler köer sedan den öppnades. Därför är det viktigt att händelsehanteraren inte skapar ett nytt fönster varje gång det körs, och detta visas i följande kodfragment.

static void mgr_OnPrinterQueuesEnumerated(object sender, PrinterQueuesEnumeratedEventArgs e)
{
    foreach (IPrinterExtensionContext pContext in e)
    {
        // show status
    }
}

För att utföra underhållsaktiviteter med hjälp av ett skrivartillägg rekommenderar Microsoft att det äldre WritePrinter-API:et används enligt beskrivningen i följande pseudokod.

OpenPrinter
    StartDocPrinter
        StartPagePrinter
          WritePrinter
        EndPagePrinter
    EndDocPrinter
ClosePrinter

Metodtips för prestanda för skrivartillägg

För att säkerställa bästa användarupplevelse bör skrivartillägg utformas för att läsas in så snabbt som möjligt. Projektet Exempel på skrivartillägg är ett .NET-program, vilket innebär att det är inbyggt i ett mellanliggande språk (IL) som måste kompileras vid körning till lämpligt format för den interna processorarkitekturen. Under installationen rekommenderar Microsoft att skrivartillägg installeras enligt bästa praxis för att säkerställa att appen har kompilerats för den interna systemarkitekturen. Mer information om metodtips för kodkompilering och installation finns i Förbättra startprestanda för dina skrivbordsprogram.

Microsoft rekommenderar också att skrivartillägg skjuter upp initieringsuppgifter som att läsa in resurser tills metoden EnableEvents har anropats. Detta minimerar sannolikheten för att appen anropar EnableEvents före tidsgränsen på 5 sekunder för skrivartillägg.

Efter OnDriverEvent-anropet bör skrivartillägg initiera användargränssnittet och rita så snabbt som möjligt och använda asynkrona metoder där det är möjligt för att säkerställa svarstider. Skrivartillägg bör inte ha något beroende av nätverksanrop eller Bidi för att skapa det inledande fönstertillståndet för utskriftsinställningar eller skrivarmeddelanden.

Eftersom användaren gör val med hjälp av användargränssnittet på skärmen som påverkar PrintTicket bör skrivartillägget använda metoden IPrintSchemaTicket::ValidateAsync för att verifiera ändringar så tidigt som möjligt. Slutligen bör skrivartillägget använda metoden IPrintSchemaTicket::CommitAsync för att kunna genomföra PrintTicket-ändringarna.

Skrivartillägg körs alltid utanför processen från den process som anropade dem. Därför måste du ha fönsterbeteendet i åtanke när du utvecklar ett skrivartillägg:

Exemplet på skrivartillägg visar hur du skapar ett användargränssnitt som vanligtvis startas som det översta fönstret. Men i vissa fall visas inte användargränssnittet i förgrunden, till exempel när processen som gjorde att användargränssnittet anropades körs på en annan integritetsnivå eller när processen kompileras för en annan processorarkitektur. I det här fallet bör skrivartillägget anropa FlashWindowEx för att begära användarbehörighet för att komma till förgrunden genom att blinka ikonen i aktivitetsfältet.

Bidi-scheman för begäran och svar

Översikt över databindning

Förbättra startprestanda för dina skrivbordsprogram

Inbyggd avbildningsgenerator

Utskriftsschemagränssnitt

Verktyg för prestandaanalys i Windows