Condividi tramite


Estensioni della stampante

Importante

La piattaforma di stampa moderna è il mezzo preferito di Windows per comunicare con le stampanti. Ti consigliamo di usare il driver di classe IPP di Microsoft, insieme a Print Support Apps (PSA), per personalizzare l'esperienza di stampa in Windows 10 e 11 per lo sviluppo di dispositivi per stampanti.

Per altre informazioni, vedere Piattaforma di stampa moderna e guida alla progettazione dell'app di supporto per la stampa.

Le app di estensione della stampante supportano le preferenze di stampa e le notifiche della stampante quando gli utenti eseguono applicazioni esistenti sul desktop di Windows.

Le estensioni della stampante possono essere compilate in qualsiasi linguaggio compatibile con COM, ma sono ottimizzate per la compilazione con Microsoft .NET Framework 4. Le estensioni della stampante possono essere distribuite con un pacchetto driver di stampa, se sono compatibili con XCopy e non hanno dipendenze da runtime esterni diversi da quelli inclusi nel sistema operativo, ad esempio .NET. Se l'app di estensione della stampante non soddisfa questi criteri, potrebbe essere distribuita in un setup.exe o in un pacchetto MSI e pubblicizzata nell'esperienza Device Stage della stampante usando la direttiva PrinterExtensionUrl specificata nel manifesto v4. Quando un'app di estensione della stampante viene distribuita tramite un pacchetto MSI, è possibile aggiungere il driver di stampa al pacchetto o estrarlo e distribuirlo separatamente. L'oggetto PrinterExtensionUrl viene visualizzato nell'esperienza delle preferenze della stampante.

Gli amministratori IT hanno alcune opzioni per gestire la distribuzione delle estensioni della stampante. Se l'applicazione viene inserita in un pacchetto in un setup.exe o msi, gli amministratori IT possono usare strumenti di distribuzione software standard come Microsoft Endpoint Configuration Manager oppure possono includere le applicazioni nell'immagine standard del sistema operativo. Gli amministratori IT possono anche eseguire l'override di PrinterExtensionUrl specificato nel manifesto v4, se modificano HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\<print queue name>\PrinterDriverData\PrinterExtensionUrl.

Se un'azienda sceglie di bloccare completamente le estensioni della stampante, questa operazione può essere eseguita tramite un criterio di gruppo denominato "Configurazione computer\Modelli amministrativi\Stampanti\Non consentire ai driver della stampante v4 di mostrare le applicazioni di estensione della stampante".

Compilazione di un'estensione della stampante

Quando si sviluppa un'estensione della stampante, ci sono sei aree principali di interesse che è necessario tenere presente. Queste aree di interesse sono illustrate nell'elenco seguente.

  • Registrazione

  • Abilitazione di eventi

  • Gestore OnDriverEvent

  • Preferenze di stampa

  • Notifiche stampanti

  • Gestione delle stampanti

Registrazione

Le estensioni della stampante vengono registrate nel sistema di stampa specificando un set di chiavi del Registro di sistema o specificando le informazioni sull'applicazione nella sezione PrinterExtensions del file manifesto v4.

Sono disponibili GUID specificati che supportano ognuno dei diversi punti di ingresso per le estensioni della stampante. Non è necessario usare questi GUID nel file manifesto v4, ma è necessario conoscere i valori GUID per usare il formato del Registro di sistema per l'installazione del driver v4. Nella tabella seguente vengono illustrati i valori GUID per i due punti di ingresso.

Punto di ingresso GUID
Preferenze di stampa {EC8F261F-267C-469F-B5D6-3933023C29CC}
Notifiche stampanti {23BB1328-63DE-4293-915B-A6A23D929ACB}

Le estensioni della stampante installate all'esterno del driver della stampante devono essere registrate tramite il Registro di sistema. In questo modo è possibile installare le estensioni della stampante indipendentemente dallo stato dello spooler o dal modulo di configurazione v4 nel computer client.

Dopo l'avvio del servizio PrintNotify, verifica la presenza di chiavi del Registro di sistema nel percorso [OfflineRoot] ed elabora eventuali registrazioni o annullamento della registrazione in sospeso. Una volta completate le registrazioni o le registrazioni in sospeso, le chiavi del Registro di sistema vengono eliminate in tempo reale. Se si usa uno script o un processo iterativo per inserire le chiavi del Registro di sistema, potrebbe essere necessario ricreare la chiave \[PrinterExtensionID] ogni volta che si specifica una chiave \[PrinterDriverId]. Le chiavi incomplete o in formato non valido non vengono eliminate.

Questa registrazione è necessaria solo al primo installazione. Nell'esempio seguente viene illustrato il formato di chiave del Registro di sistema corretto usato per la registrazione delle estensioni della stampante.

Nota

[OfflineRoot] viene utilizzato come abbreviato per 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]

Ad esempio, il set di chiavi seguente registra un'estensione della stampante con il file eseguibile {PrinterExtensionID} PrinterExtensionID} e un percorso completo al file eseguibile "C:\Programmi\Fabrikam\pe.exe" per il file eseguibile {PrinterDriverID1Guid} e {PrinterDriverID2Guid} PrinterDriverID, con le preferenze della stampante e le notifiche della stampante.

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

Per disinstallare la stessa estensione della stampante, è necessario specificare il set di chiavi seguente.

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

Poiché le estensioni della stampante possono essere eseguite sia in un contesto avviato dall'utente che in un contesto avviato da un evento, è utile essere in grado di determinare il contesto in cui opera l'estensione della stampante. Ciò può consentire a un'app, ad esempio, di enumerare lo stato in tutte le code se è stato avviato per una notifica o le preferenze di stampa. Microsoft consiglia che le estensioni della stampante installate separatamente dal driver (ad esempio, con un'identità del servizio gestito o setup.exe) usino commutatori della riga di comando sui collegamenti menu Start o nella voce AppPath popolata nel Registro di sistema durante la registrazione. Poiché le estensioni della stampante installate con il driver vengono installate in DriverStore, queste non verranno avviate all'esterno delle preferenze di stampa o degli eventi delle notifiche della stampante. Pertanto, la specifica delle opzioni della riga di comando non è supportata in questo caso.

Quando l'estensione della stampante viene registrata per l'id PrinterDriverID corrente, deve includere PrinterDriverID in AppPath. Ad esempio, per un'app di estensione della stampante con il nome printerextension.exe e un valore PrinterDriverID pari a {GUID}, [PrinterExtensionAppPath] sarà simile all'esempio seguente:

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

Abilitazione degli eventi

In fase di esecuzione, le estensioni della stampante devono abilitare l'attivazione degli eventi per printerDriverID corrente. Questo è il PrinterDriverID passato all'app tramite la matrice args[] e consente al sistema di stampa di fornire un contesto di evento appropriato per la gestione di motivi come le preferenze di stampa o le notifiche della stampante.

L'applicazione deve quindi creare un nuovo PrinterExtensionManager per l'id PrinterDriverID corrente, registrare un delegato per gestire l'evento OnDriverEvent e chiamare il metodo EnableEvents con PrinterDriverID. Il frammento di codice seguente illustra questo approccio.

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

Se un'app non chiama EnableEvents entro 5 secondi, Windows eseguirà il timeout e avvierà un'interfaccia utente standard. Per attenuare questo problema, le estensioni della stampante devono seguire le procedure consigliate per le prestazioni più recenti, incluse le seguenti:

  • Ritardare la maggior parte possibile dell'inizializzazione dell'app fino a quando non si chiama EnableEvents. Successivamente, assegnare priorità alla velocità di risposta dell'interfaccia utente usando metodi asincroni e non bloccando il thread dell'interfaccia utente durante l'inizializzazione.

  • Usare ngen per generare un'immagine nativa durante l'installazione. Per altre informazioni, vedere Generatore di immagini native.

  • Usare gli strumenti di misurazione delle prestazioni per individuare i problemi di prestazioni durante il caricamento. Per altre informazioni, vedere Strumenti di analisi delle prestazioni di Windows.

Gestore DriverEvent

Dopo la registrazione di un gestore OnDriverEvent e gli eventi sono abilitati, se l'estensione della stampante è stata avviata per gestire le preferenze di stampa o le notifiche della stampante, il gestore verrà richiamato. Nel frammento di codice precedente un metodo denominato OnDriverEvent è stato registrato come gestore eventi. Nel frammento di codice seguente il parametro PrinterExtensionEventArgs è l'oggetto che consente di costruire le preferenze di stampa e gli scenari di notifica della stampante. PrinterExtensionEventArgs è un wrapper per 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.
    }
}

Per evitare un'esperienza utente non valida associata all'arresto anomalo o alle estensioni lente della stampante, Windows implementa un timeout se EnableEvents non viene chiamato entro un breve periodo di tempo dall'avvio dell'app. Per abilitare il debug, questo timeout è disabilitato se è presente un debugger collegato al servizio PrintNotify.

Nella maggior parte dei casi, tuttavia, tutto il codice correlato all'app a cui si è interessati, viene eseguito durante o dopo il callback OnDriverEvent. Durante lo sviluppo, può anche essere utile visualizzare un MessageBox prima di avviare le preferenze di stampa o l'esperienza delle notifiche della stampante dal callback OnDriverEvent. Quando viene visualizzato MessageBox, tornare a Visual Studio e selezionare Debug>Connetti a processo e scegliere il nome del processo. Infine, tornare a MessageBox e selezionare OK per riprendere. In questo modo verranno visualizzate le eccezioni e si raggiungeranno eventuali punti di interruzione da quel punto in poi.

I nuovi ReasonId potrebbero essere supportati in futuro. Di conseguenza, le estensioni della stampante devono controllare in modo esplicito ReasonID e non devono usare un'istruzione "else" per rilevare l'ultimo ReasonID noto. Se viene ricevuto e sconosciuto un ReasonID, l'app deve uscire normalmente.

Le preferenze di stampa sono guidate dall'oggetto PrintSchemaEventArgs.Ticket. Questo oggetto incapsula sia i documenti PrintTicket che PrintCapabilities che descrivono le funzionalità e le opzioni per un dispositivo. Anche se il codice XML sottostante è disponibile, il modello a oggetti semplifica l'uso di questi formati.

All'interno di ogni oggetto IPrintSchemaTicket o IPrintSchemaCapabilities sono disponibili funzionalità (IPrintSchemaFeature) e opzioni (IPrintSchemaOption). Anche se le interfacce usate per le funzionalità e le opzioni sono uguali indipendentemente dall'origine, il comportamento varia leggermente in seguito al codice XML sottostante. Ad esempio, i documenti PrintCapabilities specificano molte opzioni per funzionalità, mentre i documenti PrintTicket specificano solo l'opzione selezionata (o predefinita). Analogamente, i documenti PrintCapabilities specificano stringhe di visualizzazione localizzate, mentre i documenti PrintTicket non lo fanno.

Per altre informazioni sul data binding in WPF, vedere Panoramica del data binding.

Per ottimizzare le prestazioni, Microsoft consiglia di eseguire chiamate a GetPrintCapabilities solo quando è necessario aggiornare il documento PrintCapabilities.

Quando un utente effettua scelte usando i controlli ComboBox associati a dati, l'oggetto PrintTicket viene aggiornato automaticamente. Quando l'utente fa infine clic su OK, inizia una catena di convalida asincrona e completamento. Questo modello asincrono viene usato ampiamente per evitare che le attività a esecuzione prolungata si verifichino nei thread dell'interfaccia utente e causano blocchi nell'interfaccia utente delle preferenze di stampa o nell'app che sta stampando. Di seguito è riportato un elenco dei passaggi usati per l'elaborazione delle modifiche di PrintTicket dopo che l'utente fa clic su OK.

  1. PrintSchemaTicket viene convalidato in modo asincrono usando il metodo IPrintSchemaTicket::ValidateAsync.

  2. Al termine della convalida asincrona, Common Language Runtime (CLR) richiama il metodo PrintTicketValidateCompleted.

    1. Se la convalida ha esito positivo, chiama il metodo CommitPrintTicketAsync e CommitPrintTicketAsync chiama il metodo IPrintSchemaTicket::CommitAsync. Quando l'aggiornamento di PrintTicket viene completato correttamente, viene richiamato il metodo PrintTicketCommitCompleted, che chiama un metodo pratico che chiama il metodo PrinterExtensionEventArgs.Request.Complete per indicare che le preferenze di stampa sono complete e quindi chiude l'app.

    2. In caso contrario, presenta l'interfaccia utente all'utente per gestire la situazione del vincolo.

Se l'utente ha fatto clic su Annulla o ha chiuso direttamente la finestra delle preferenze di stampa, l'estensione della stampante chiama IPrinterExtensionEventArgs.Request.Cancel con un valore HRESULT appropriato e un messaggio per il log degli errori.

Se il processo per l'estensione della stampante è stato chiuso e non ha chiamato i metodi Complete o Cancel come descritto nei paragrafi precedenti, il sistema di stampa eseguirà automaticamente il fallback all'uso dell'interfaccia utente fornita da Microsoft.

Per recuperare le informazioni sullo stato del dispositivo, le estensioni della stampante possono usare Bidi per eseguire query sul dispositivo di stampa. Ad esempio, per visualizzare lo stato dell'input penna o altri tipi di stato sul dispositivo, le estensioni della stampante possono usare il metodo IPrinterExtensionEventArgs.PrinterQueue.SendBidiQuery per inviare query Bidi al dispositivo. Ottenere lo stato più recente di Bidi è un processo in due passaggi che prevede la configurazione di un gestore eventi per l'evento OnBidiResponseReceived e la chiamata al metodo SendBidiQuery con una query Bidi valida. Il frammento di codice seguente illustra questo processo in due passaggi.

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

Quando viene ricevuta la risposta Bidi, viene richiamato il gestore eventi seguente. Questo gestore eventi ha anche un'implementazione dello stato dell'input penna fittizia, che può essere utile per lo sviluppo quando un dispositivo non è disponibile. L'oggetto PrinterQueueEventArgs include sia una risposta HRESULT che una risposta XML Bidi. Per altre informazioni sulle risposte XML Bidi, vedere Bidi Request and Response Schemas .For more information on Bidi XML responses, see Bidi Request and Response Schemas.

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

Notifiche della stampante

Le notifiche della stampante vengono richiamate esattamente come le preferenze di stampa. Nel gestore OnDriverEvent, se IPrinterExtensionEventArgs indica che un ReasonID corrisponde al GUID DriverEvents, è possibile creare un'esperienza per gestire questo evento.

Le variabili seguenti sono più utili per gestire un'esperienza di notifica della stampante funzionale.

  • PrinterExtensionEventArgs.BidiNotification: porta il codice XML Bidi che ha causato l'attivazione dell'evento.

  • PrinterExtensionEventArgs.DetailedReasonId: contiene il GUID eventID dal file XML dell'evento driver.

L'attributo più importante dell'oggetto IPrinterExtensionEventArgs per le notifiche è la proprietà BidiNotification. Questo porta il codice XML Bidi che ha causato l'attivazione dell'evento. Per altre informazioni sulle risposte XML Bidi, vedere Bidi Request and Response Schemas .For more information on Bidi XML responses, see Bidi Request and Response Schemas.

Gestione delle stampanti

Per supportare il ruolo di un'estensione della stampante come app che può essere usata come hub per la gestione o la gestione delle stampanti, è possibile enumerare le code di stampa per le quali è registrata l'estensione della stampante corrente e ottenere lo stato per ogni coda. Questa operazione non è illustrata nel progetto PrinterExtensionSample, ma è possibile aggiungere il frammento di codice seguente nel metodo Main di App.xaml.cs per registrare un gestore eventi.

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

Dopo l'enumerazione delle code, il gestore eventi viene chiamato e le operazioni di stato possono essere eseguite. Questo evento viene generato periodicamente durante la durata dell'app per garantire che l'elenco delle code di stampa enumerate sia corrente, anche se l'utente ha installato più code dall'apertura. Di conseguenza, è importante che il gestore eventi non crei una nuova finestra ogni volta che viene eseguita e questo aspetto viene mostrato nel frammento di codice seguente.

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

Per eseguire attività di manutenzione tramite un'estensione della stampante, Microsoft consiglia di usare l'API WritePrinter legacy come descritto nello pseudocode seguente.

OpenPrinter
    StartDocPrinter
        StartPagePrinter
          WritePrinter
        EndPagePrinter
    EndDocPrinter
ClosePrinter

Procedure consigliate per le prestazioni delle estensioni della stampante

Per garantire la migliore esperienza utente, le estensioni della stampante devono essere progettate per caricare il più velocemente possibile. Il progetto di esempio di estensione della stampante è un'applicazione .NET, che significa che viene incorporata in un linguaggio intermedio (IL) che deve essere compilato in fase di esecuzione nel formato appropriato per l'architettura del processore nativo. Durante l'installazione, Microsoft consiglia di installare le estensioni della stampante in base alle procedure consigliate, per assicurarsi che l'app sia stata compilata per l'architettura di sistema nativa. Per altre informazioni sulle procedure consigliate per la compilazione e l'installazione del codice, vedere Miglioramento delle prestazioni di avvio per le applicazioni desktop.

Microsoft consiglia inoltre che le estensioni della stampante posticipino le attività di inizializzazione, ad esempio il caricamento delle risorse fino a quando non viene chiamato il metodo EnableEvents. Ciò riduce al minimo la probabilità che l'app chiami EnableEvents prima del timeout di 5 secondi per le estensioni della stampante.

Dopo la chiamata OnDriverEvent, le estensioni della stampante devono inizializzare l'interfaccia utente e disegnare il più rapidamente possibile, usando metodi asincroni, ove possibile per garantire la velocità di risposta. Le estensioni della stampante non devono dipendere dalle chiamate di rete o Bidi per creare lo stato iniziale della finestra per le preferenze di stampa o le notifiche della stampante.

Poiché l'utente effettua scelte usando l'interfaccia utente sullo schermo che influisce su PrintTicket, l'estensione della stampante deve usare il metodo IPrintSchemaTicket::ValidateAsync per convalidare le modifiche il prima possibile. Infine, l'estensione della stampante deve usare il metodo IPrintSchemaTicket::CommitAsync per eseguire il commit delle modifiche printticket.

Le estensioni della stampante vengono sempre eseguite fuori processo dal processo che li ha richiamati. È quindi necessario tenere presente il comportamento della finestra quando si sviluppa un'estensione della stampante:

  • La proprietà WindowParent di IPrinterExtensionEventArgs specifica l'handle nella finestra che ha richiamato l'app.
  • La proprietà WindowModal di IPrinterExtensionEventArgs specifica se un'estensione della stampante (in modalità preferenze di stampa) deve essere eseguita modally.

L'esempio di estensione stampante illustra come creare un'interfaccia utente in genere avviata come finestra superiore. In alcuni casi, tuttavia, l'interfaccia utente non verrà visualizzata in primo piano, ad esempio quando il processo che ha causato la chiamata dell'interfaccia utente viene eseguito a un livello di integrità diverso o quando il processo viene compilato per un'architettura del processore diversa. In questo caso, l'estensione della stampante deve chiamare FlashWindowEx per richiedere all'utente l'autorizzazione per venire in primo piano lampeggiando l'icona nella barra delle applicazioni.

Schemi di richiesta e risposta bidi

Panoramica sul data binding

Miglioramento delle prestazioni di avvio per le applicazioni desktop

Generatore di immagini native

Interfacce dello schema di stampa

Strumenti di analisi delle prestazioni di Windows