Partilhar via


Extensões de impressora

Importante

A moderna plataforma de impressão é o meio preferido do Windows para comunicar com as impressoras. Recomendamos que utilize o controlador de classe da caixa de entrada IPP da Microsoft, juntamente com as Aplicações de Suporte de Impressão (PSA), para personalizar a experiência de impressão no Windows 10 e 11 para o desenvolvimento de dispositivos de impressora.

Para obter mais informações, consulte Guia de design do aplicativo de suporte de impressão v1 e v2.

As aplicações de extensão de impressora suportam preferências de impressão e notificações de impressora quando os utilizadores executam aplicações existentes no ambiente de trabalho do Windows.

As extensões de impressora podem ser criadas em qualquer linguagem compatível com COM, mas são otimizadas para serem criadas usando o Microsoft .NET Framework 4. As extensões de impressora podem ser distribuídas com um pacote de driver de impressão, se forem compatíveis com XCopy e não tiverem dependências em tempos de execução externos diferentes daqueles incluídos com o sistema operacional, por exemplo, .NET. Se o aplicativo de extensão de impressora não atender a esses critérios, ele poderá ser distribuído em um pacote setup.exe ou MSI e anunciado na experiência Device Stage da impressora usando a diretiva PrinterExtensionUrl especificada no manifesto v4. Quando um aplicativo de extensão de impressora é distribuído por meio de um pacote MSI, você tem a opção de adicionar o driver de impressão ao pacote ou deixá-lo de fora e distribuir o driver separadamente. O PrinterExtensionUrl é mostrado na experiência de preferências da impressora.

Os administradores de TI têm algumas opções para gerenciar a distribuição de extensões de impressora. Se o aplicativo for empacotado em um setup.exe ou MSI, os administradores de TI poderão usar ferramentas padrão de distribuição de software, como o Microsoft Endpoint Configuration Manager, ou poderão incluir os aplicativos em sua imagem padrão do sistema operacional. Os administradores de TI também podem anular o PrinterExtensionUrl especificado no manifesto v4, se editarem o nome da fila de impressão HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\<>\PrinterDriverData\PrinterExtensionUrl.

E se uma empresa optar por bloquear completamente as extensões de impressora, isso pode ser feito por meio de uma política de grupo chamada "Configuração do Computador\Modelos Administrativos\Impressoras\Não permitir que os drivers de impressora v4 mostrem aplicativos de extensão de impressora".

Construindo uma extensão de impressora

Quando você está desenvolvendo uma extensão de impressora, há seis áreas principais de foco que você deve estar ciente. Essas áreas de foco são mostradas na lista a seguir.

  • Registo

  • Habilitando eventos

  • Manipulador de Evento OnDriverEvent

  • Preferências de impressão

  • Notificações da impressora

  • Gerenciando impressoras

Registo

As extensões de impressora são registradas no sistema de impressão especificando um conjunto de chaves do Registro ou especificando as informações do aplicativo na seção PrinterExtensions do arquivo de manifesto v4.

Existem GUIDs especificados que suportam cada um dos diferentes pontos de entrada para extensões de impressora. Você não precisa usar esses GUIDs no arquivo de manifesto v4, mas você deve saber os valores GUID para usar o formato do Registro para a instalação do driver v4. A tabela a seguir mostra os valores GUID para os dois pontos de entrada.

Ponto de entrada GUID
Preferências de impressão {EC8F261F-267C-469F-B5D6-3933023C29CC}
Notificações da impressora {23BB1328-63DE-4293-915B-A6A23D929ACB}

As extensões de impressora instaladas fora do driver de impressora precisam ser registradas usando o registro. Isso garante que as extensões de impressora possam ser instaladas independentemente do status do spooler ou do módulo de configuração v4 na máquina cliente.

Assim que o serviço PrintNotify for iniciado, verificará se há chaves de registo no caminho [OfflineRoot] e processará quaisquer registos ou desregistos pendentes. Uma vez que quaisquer registros pendentes ou cancelamentos de registros são concluídos, as chaves do Registro são excluídas em tempo real. Se estiver a utilizar um script ou um processo iterativo para colocar chaves de registo, poderá ter de recriar a chave \[PrinterExtensionID] sempre que especificar uma chave \[PrinterDriverId]. Chaves incompletas ou malformadas não são excluídas.

Este registo só é necessário na primeira instalação. O exemplo a seguir mostra o formato correto da chave do Registro usado para registrar extensões de impressora.

Observação

[OfflineRoot] é usado como abreviatura para 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]

Por exemplo, o seguinte conjunto de chaves registraria uma extensão de impressora com o {PrinterExtensionIDGuid} PrinterExtensionID e um caminho totalmente qualificado para o executável "C:\Program Files\Fabrikam\pe.exe" para o {PrinterDriverID1Guid} e {PrinterDriverID2Guid} PrinterDriverIDs, com as preferências da impressora e os motivos das notificações da impressora.

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

Para desinstalar a mesma extensão de impressora, o seguinte conjunto de chaves deve ser especificado.

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

Como as extensões de impressora podem ser executadas em um contexto iniciado pelo usuário e em um contexto iniciado por evento, é útil poder determinar o contexto no qual a extensão da impressora está operando. Isso pode permitir que um aplicativo, por exemplo, não liste o estado de todas as filas se tiver sido iniciado para uma notificação ou configurações de impressão. A Microsoft recomenda que as extensões de impressora instaladas separadamente do driver (por exemplo, com um MSI ou setup.exe) utilizem opções de linha de comando nos atalhos do menu Iniciar ou na entrada AppPath, que foi preenchida no registo durante o registo. Como as extensões de impressora instaladas com o driver são instaladas no DriverStore, elas não serão iniciadas fora das preferências de impressão ou dos eventos de notificações da impressora. Portanto, a especificação de opções de linha de comando não é suportada neste caso.

Quando a extensão da impressora se registra para o PrinterDriverID atual, ela deve incluir o PrinterDriverID no AppPath. Por exemplo, para um aplicativo de extensão de impressora com o nome printerextension.exee um valor PrinterDriverID de {GUID}, o [PrinterExtensionAppPath] seria semelhante ao exemplo a seguir:

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

Habilitando eventos

No tempo de execução, as extensões de impressora devem habilitar o acionamento de eventos para o PrinterDriverID atual. Este é o PrinterDriverID que foi passado para o aplicativo através da matriz args[] e permite que o sistema de impressão forneça um contexto de evento apropriado para lidar com motivos como preferências de impressão ou notificações da impressora.

Portanto, o aplicativo deve criar um novo PrinterExtensionManager para o PrinterDriverID atual, registrar um delegado para manipular o evento OnDriverEvent e chamar o método EnableEvents com o PrinterDriverID. O trecho de código a seguir ilustra essa abordagem.

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

Se uma aplicação não chamar EnableEvents dentro de 5 segundos, o Windows interromperá o tempo de espera e iniciará uma interface de utilizador padrão. Para atenuar isso, as extensões de impressora devem seguir as práticas recomendadas de desempenho mais recentes, incluindo o seguinte:

  • Atrase o máximo possível a inicialização do aplicativo até depois de chamar EnableEvents. Depois disso, priorize a capacidade de resposta da interface do usuário usando métodos assíncronos e não bloqueando o thread da interface do usuário durante a inicialização.

  • Use ngen para gerar uma imagem nativa durante a instalação. Para obter mais informações, consulte Native Image Generator.

  • Use ferramentas de medição de desempenho para encontrar problemas de desempenho ao carregar. Para obter mais informações, consulte Ferramentas de Análise de Desempenho do Windows.

Manipulador DriverEvent

Depois que um manipulador OnDriverEvent for registrado e os eventos forem habilitados, se a extensão da impressora tiver sido iniciada para manipular preferências de impressão ou notificações da impressora, o manipulador será invocado. No trecho de código anterior, um método chamado OnDriverEvent foi registrado como o manipulador de eventos. No trecho de código a seguir, o parâmetro PrinterExtensionEventArgs é o objeto que permite que as preferências de impressão e os cenários de notificações de impressora sejam construídos. PrinterExtensionEventArgs é um wrapper para 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.
    }
}

Para evitar uma experiência de usuário ruim associada a falhas ou extensões de impressora lentas, o Windows implementa um tempo limite se EnableEvents não for chamado dentro de um curto período de tempo após o aplicativo ser iniciado. Para habilitar a depuração, esse tempo limite será desativado se houver um depurador anexado ao serviço PrintNotifique.

Na maioria dos casos, no entanto, todo o código relacionado com a aplicação no qual estamos interessados, é executado durante ou após o callback OnDriverEvent. Durante o desenvolvimento, também pode ser útil mostrar uma MessageBox antes de iniciar uma experiência de preferências de impressão ou notificações de impressora a partir do retorno de chamada OnDriverEvent. Quando o MessageBox aparecer, volte para o Visual Studio e selecione Depurar>Anexar ao processo e escolha o nome do seu processo. Finalmente, volte para sua MessageBox e selecione OK para retomar. Isso garantirá que você veja exceções e atinja quaisquer pontos de interrupção a partir desse ponto.

Novos ReasonIds podem ser suportados no futuro. Como resultado, as extensões de impressora devem verificar explicitamente o ReasonID e não devem usar uma instrução "else" para detetar o último ReasonID conhecido. Se um ReasonID for recebido e desconhecido, a aplicação deve encerrar sem problemas.

As preferências de impressão são orientadas pelo objeto PrintSchemaEventArgs.Ticket. Este objeto encapsula os documentos PrintTicket e PrintCapabilities que descrevem os recursos e as opções de um dispositivo. Embora o XML subjacente também esteja disponível, o modelo de objeto facilita o trabalho com esses formatos.

Dentro de cada objeto IPrintSchemaTicket ou IPrintSchemaCapabilities, há recursos (IPrintSchemaFeature) e opções (IPrintSchemaOption). Embora as interfaces usadas para recursos e opções sejam as mesmas, independentemente da origem, o comportamento varia ligeiramente como resultado do XML subjacente. Por exemplo, os documentos PrintCapabilities especificam muitas opções por recurso, enquanto os documentos PrintTicket especificam apenas a opção selecionada (ou padrão). Da mesma forma, os documentos PrintCapabilities especificam cadeias de caracteres de exibição localizadas, enquanto os documentos PrintTicket não.

Para obter mais informações sobre vinculação de dados no WPF, consulte Visão geral da vinculação de dados .

Para maximizar o desempenho, a Microsoft recomenda que as chamadas para GetPrintCapabilities só sejam feitas quando for necessário atualizar o documento PrintCapabilities.

À medida que um usuário faz escolhas usando os controles ComboBox associados a dados, o objeto PrintTicket é atualizado automaticamente. Quando o usuário finalmente clica OK, uma cadeia de validação assíncrona e conclusão começa. Esse padrão assíncrono é usado extensivamente para evitar que tarefas de longa execução ocorram em threads da interface do usuário e causem travamentos na interface do usuário de preferências de impressão ou no aplicativo que está imprimindo. A seguir está uma lista das etapas usadas para processar as alterações do PrintTicket depois que o usuário clica OK.

  1. O PrintSchemaTicket é validado de forma assíncrona usando o métodoIPrintSchemaTicket::ValidateAsync.

  2. Quando a validação assíncrona é concluída, o Common Language Runtime (CLR) invoca o método PrintTicketValidateCompleted.

    1. Se a validação foi bem-sucedida, ele chama o método CommitPrintTicketAsync e CommitPrintTicketAsync chama o IPrintSchemaTicket::CommitAsync método. E quando a atualização PrintTicket é concluída com êxito, isso invoca o método PrintTicketCommitCompleted, que chama um método de conveniência que chama o método PrinterExtensionEventArgs.Request.Complete para indicar que as preferências de impressão estão concluídas e, em seguida, fecha o aplicativo.

    2. Caso contrário, apresenta uma interface de utilizador para gerir a situação de restrição.

Se o usuário clicou em cancelar ou fechou a janela de preferências de impressão diretamente, a extensão da impressora chamará IPrinterExtensionEventArgs.Request.Cancel com um valor HRESULT apropriado e uma mensagem para o log de erros.

Se o processo para a extensão da impressora tiver sido fechado e não tiver chamado os métodos Complete ou Cancel conforme descrito nos parágrafos anteriores, então o sistema de impressão reverterá automaticamente para usar a interface do usuário fornecida pela Microsoft.

Para recuperar informações de status do dispositivo, as extensões de impressora podem usar Bidi para consultar o dispositivo de impressão. Por exemplo, para mostrar o status da tinta ou outros tipos de status sobre o dispositivo, as extensões de impressora podem usar o método IPrinterExtensionEventArgs.PrinterQueue.SendBidiQuery para emitir consultas Bidi para o dispositivo. Obter o status Bidi mais recente é um processo de duas etapas que envolve a configuração de um manipulador de eventos para o evento OnBidiResponseReceived e a chamada do método SendBidiQuery com uma consulta Bidi válida. O trecho de código a seguir mostra esse processo de duas etapas.

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

Quando a resposta Bidi é recebida, o manipulador de eventos a seguir é invocado. Este manipulador de eventos também tem uma implementação simulada do estado da tinta, que pode ser útil durante o desenvolvimento quando um dispositivo não está disponível. O objeto PrinterQueueEventArgs inclui uma resposta HRESULT e uma resposta Bidi XML. Para obter mais informações sobre respostas XML do Bidi, consulte 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)";
}

Notificações da impressora

As notificações da impressora são invocadas exatamente da mesma forma que as preferências de impressão. No manipulador OnDriverEvent, se IPrinterExtensionEventArgs indicar que um ReasonID corresponde ao GUID DriverEvents, poderemos criar uma experiência para manipular esse evento.

As variáveis a seguir são mais úteis para lidar com uma experiência de notificações de impressora funcional.

  • PrinterExtensionEventArgs.BidiNotification – Carrega o XML Bidi que fez com que o evento fosse acionado.

  • PrinterExtensionEventArgs.DetailedReasonId – Contém o GUID eventID do arquivo xml do evento do driver.

O atributo mais importante do objeto IPrinterExtensionEventArgs para notificações é a propriedade BidiNotification. Isso carrega o XML Bidi que fez com que o evento fosse acionado. Para obter mais informações sobre respostas XML do Bidi, consulte Bidi Request and Response Schemas.

Gestão de impressoras

Para suportar a função de uma extensão de impressora como um aplicativo que pode ser usado como um hub para gerenciar/manter impressoras, é possível enumerar as filas de impressão para as quais a extensão de impressora atual está registrada e obter o status de cada fila. Isso não é demonstrado no projeto PrinterExtensionSample, mas o trecho de código a seguir pode ser adicionado ao método Main de App.xaml.cs registrar um manipulador de eventos.

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

Depois que as filas são enumeradas, o manipulador de eventos é chamado e as operações de status podem ocorrer. Esse evento é acionado periodicamente durante o tempo de vida do aplicativo para garantir que a lista de filas de impressão enumeradas seja atual, mesmo que o usuário tenha instalado mais filas desde que foi aberta. Como resultado, é importante que o manipulador de eventos não crie uma nova janela cada vez que é executado, e isso é mostrado no trecho de código a seguir.

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

Para executar tarefas de manutenção usando uma extensão de impressora, a Microsoft recomenda que a API WritePrinter herdada seja usada conforme descrito pelo pseudocódigo a seguir.

OpenPrinter
    StartDocPrinter
        StartPagePrinter
          WritePrinter
        EndPagePrinter
    EndDocPrinter
ClosePrinter

Práticas recomendadas de desempenho de extensão de impressora

A fim de garantir a melhor experiência do usuário, as extensões de impressora devem ser projetadas para carregar o mais rápido possível. O projeto Printer Extension Sample é um aplicativo .NET, o que significa que ele é incorporado em uma linguagem intermediária (IL) que deve ser compilada em tempo de execução no formato apropriado para a arquitetura de processador nativa. Durante a instalação, a Microsoft recomenda que as extensões de impressora sejam instaladas de acordo com as práticas recomendadas, para garantir que o aplicativo tenha sido compilado para a arquitetura nativa do sistema. Para obter mais informações sobre as práticas recomendadas de compilação e instalação de código, consulte Improving Launch Performance for Your Desktop Applications.

A Microsoft também recomenda que as extensões de impressora adiem tarefas de inicialização, como carregar recursos, até que o método EnableEvents tenha sido chamado. Isso minimiza a probabilidade de o aplicativo chamar EnableEvents antes do tempo limite de 5 segundos para extensões de impressora.

Após a chamada OnDriverEvent, as extensões de impressora devem inicializar sua interface do usuário e desenhar o mais rápido possível, fazendo uso de métodos assíncronos sempre que possível para garantir a capacidade de resposta. As extensões de impressora não devem depender de chamadas de rede ou Bidi para criar o estado inicial da janela para preferências de impressão ou notificações de impressora.

À medida que o usuário faz escolhas usando a interface do usuário na tela que afeta o PrintTicket, a extensão da impressora deve usar o método IPrintSchemaTicket::ValidateAsync para validar as alterações o mais cedo possível. Finalmente, a extensão da impressora deve usar o método IPrintSchemaTicket::CommitAsync para confirmar as alterações do PrintTicket.

As extensões de impressora são sempre executadas fora do processo que as invoca. Portanto, você deve ter em mente o comportamento da janela ao desenvolver uma extensão de impressora:

  • A propriedade WindowParent de IPrinterExtensionEventArgs especifica o identificador para a janela que invocou o aplicativo.
  • A propriedade WindowModal de IPrinterExtensionEventArgs especifica se uma extensão de impressora (no modo de preferências de impressão) deve ser executada modalmente.

O exemplo de extensão de impressora demonstra como criar uma interface do usuário que geralmente é iniciada como a janela mais alta. Mas, em alguns casos, a interface do usuário não será mostrada em primeiro plano, como quando o processo que causou a chamada da interface do usuário está sendo executado em um nível de integridade diferente ou quando o processo é compilado para uma arquitetura de processador diferente. Nesse caso, a extensão da impressora deve chamar FlashWindowEx para solicitar permissão do usuário para vir para o primeiro plano piscando o ícone na barra de tarefas.

Esquemas Bidi de Solicitação e Resposta

Visão geral da vinculação de dados

Melhorar o desempenho de inicialização para seus aplicativos de desktop

Gerador de Imagem Nativa

Interfaces de esquema de impressão

Ferramentas de Análise de Desempenho do Windows