Compartir a través de


Extensiones de impresora

Importante

La plataforma de impresión moderna es el medio preferido de Windows para comunicarse con impresoras. Se recomienda usar el controlador de clase de bandeja de entrada IPP de Microsoft, junto con aplicaciones de soporte técnico de impresión (PSA), para personalizar la experiencia de impresión en Windows 10 y 11 para el desarrollo de dispositivos de impresora.

Para obtener más información, consulte Plataforma de impresión moderna y la Guía de diseño de aplicaciones para compatibilidad con impresión.

Las aplicaciones de extensión de impresora admiten preferencias de impresión y notificaciones de impresora cuando los usuarios ejecutan aplicaciones existentes en el escritorio de Windows.

Las extensiones de impresora se pueden crear en cualquier lenguaje compatible con COM, pero están optimizadas para compilarse mediante Microsoft .NET Framework 4. Las extensiones de impresora se pueden distribuir con un paquete de controladores de impresión, si son compatibles con XCopy y no tienen dependencias en entornos de ejecución externos distintos de los incluidos con el sistema operativo, por ejemplo, .NET. Si la aplicación de extensión de impresora no cumple estos criterios, podría distribuirse en un setup.exe o en un paquete MSI y anunciarse en la experiencia de escenario de dispositivos de la impresora mediante la directiva PrinterExtensionUrl especificada en el manifiesto v4. Cuando una aplicación de extensión de impresora se distribuye a través de un paquete MSI, tiene la opción de agregar el controlador de impresión al paquete o dejarlo fuera y distribuirlo por separado. PrinterExtensionUrl se muestra en la experiencia de preferencias de impresora.

Los administradores de TI tienen algunas opciones para administrar la distribución de extensiones de impresora. Si la aplicación se empaqueta en un setup.exe o MSI, los administradores de TI pueden usar herramientas de distribución de software estándar como Microsoft Endpoint Configuration Manager o pueden incluir las aplicaciones en su imagen estándar del sistema operativo. Los administradores de TI también pueden invalidar PrinterExtensionUrl especificado en el manifiesto v4, si editan HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printer\<print queue name>\PrinterDriverData\PrinterExtensionUrl.

Y si una empresa decide bloquear por completo las extensiones de impresora, se puede hacer a través de una directiva de grupo denominada "Configuración del equipo\Plantillas administrativas\Impresoras\No permitir que los controladores de impresora v4 muestren aplicaciones de extensión de impresora".

Creación de una extensión de impresora

Al desarrollar una extensión de impresora, hay seis áreas principales de enfoque que debe tener en cuenta. Estas áreas de enfoque se muestran en la lista siguiente.

  • Registro

  • Habilitación de eventos

  • Controlador OnDriverEvent

  • Preferencias de impresión

  • Notificaciones de impresora

  • Administración de impresoras

Registro

Las extensiones de impresora se registran con el sistema de impresión especificando un conjunto de claves del Registro o especificando la información de la aplicación en la sección PrinterExtensions del archivo de manifiesto v4.

Hay GUID especificados que admiten cada uno de los distintos puntos de entrada para las extensiones de impresora. No es necesario usar estos GUID en el archivo de manifiesto v4, pero debe conocer los valores GUID para usar el formato del Registro para la instalación del controlador v4. En la tabla siguiente se muestran los valores GUID de los dos puntos de entrada.

Punto de entrada GUID
Preferencias de impresión {EC8F261F-267C-469F-B5D6-3933023C29CC}
Notificaciones de impresora {23BB1328-63DE-4293-915B-A6A23D929ACB}

Las extensiones de impresora instaladas fuera del controlador de impresora deben registrarse mediante el registro. Esto garantiza que las extensiones de impresora se puedan instalar independientemente del estado del administrador de trabajos de impresión o del módulo de configuración v4 en el equipo cliente.

Una vez que se inicie el servicio PrintNotify, comprobará las claves del Registro en la ruta de acceso [OfflineRoot] y procesará los registros pendientes o anulará el registro. Una vez completados los registros o anulaciones de registros pendientes, las claves del Registro se eliminan en tiempo real. Si usa un script o un proceso iterativo para colocar claves del Registro, es posible que tenga que volver a crear la clave \[PrinterExtensionID] cada vez que especifique una clave \[PrinterDriverId]. Las claves incompletas o con formato incorrecto no se eliminan.

Este registro solo es necesario en la primera instalación. En el ejemplo siguiente se muestra el formato de clave del Registro correcto que se usa para registrar extensiones de impresora.

Nota:

[OfflineRoot] se usa 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 ejemplo, el siguiente conjunto de claves registraría una extensión de impresora con el ejecutable {PrinterExtensionIDGuid} PrinterExtensionID y una ruta de acceso completa al archivo ejecutable "C:\Program Files\Fabrikam\pe.exe" para los PrinterDriverIDs {PrinterDriverID1Guid} y {PrinterDriverID2Guid}, con las preferencias de impresora y las notificaciones de impresora.

[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 la misma extensión de impresora, se debe especificar el siguiente conjunto de claves.

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

Dado que las extensiones de impresora se pueden ejecutar en un contexto iniciado por el usuario y en un contexto iniciado por eventos, resulta útil poder determinar el contexto en el que funciona la extensión de impresora. Esto puede permitir que una aplicación no enumere el estado en todas las colas si se ha iniciado para una notificación o preferencias de impresión. Microsoft recomienda que las extensiones de impresora que se instalen por separado del controlador (por ejemplo, con MSI o setup.exe) deben usar modificadores de línea de comandos en los métodos abreviados de menú Inicio o en la entrada AppPath que se rellenó en el registro durante el registro. Dado que las extensiones de impresora instaladas con el controlador se instalan en DriverStore, estas no se iniciarán fuera de las preferencias de impresión ni los eventos de notificaciones de impresora. Por lo tanto, no se admite especificar modificadores de línea de comandos en este caso.

Cuando la extensión de impresora se registra para el PrinterDriverID actual, debe incluir PrinterDriverID en AppPath. Por ejemplo, para una aplicación de extensión de impresora con el nombre printerextension.exe y un valor PrinterDriverID de {GUID}, [PrinterExtensionAppPath] tendría el siguiente aspecto:

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

Habilitación de eventos

En tiempo de ejecución, las extensiones de impresora deben habilitar el desencadenador de eventos para el PrinterDriverID actual. Este es el PrinterDriverID que se pasó a la aplicación a través de la matriz args[] y permite al sistema de impresión proporcionar un contexto de evento adecuado para controlar razones como preferencias de impresión o notificaciones de impresora.

Por lo tanto, la aplicación debe crear un nuevo PrinterExtensionManager para el PrinterDriverID actual, registrar un delegado para controlar el evento OnDriverEvent y llamar al método EnableEvents con PrinterDriverID. En el fragmento de código siguiente se muestra este método.

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

Si una aplicación no llama a EnableEvents en un plazo de 5 segundos, Windows expirará e iniciará una interfaz de usuario estándar. Para mitigar esto, las extensiones de impresora deben seguir los procedimientos recomendados de rendimiento más recientes, incluidos los siguientes:

  • Retrasar tanto como sea posible la inicialización de la aplicación, hasta después de llamar a EnableEvents. Después, priorizar la capacidad de respuesta de la interfaz de usuario mediante métodos asincrónicos y no bloquear el subproceso de la interfaz de usuario durante la inicialización.

  • Usar ngen para generar una imagen nativa durante la instalación. Para obtener más información, consulte Generador de imágenes nativas.

  • Usar herramientas de medición de rendimiento para encontrar problemas de rendimiento al cargar. Para obtener más información, consulte Windows Performance Analysis Tools.

Controlador DriverEvent

Una vez registrado un controlador OnDriverEvent y habilitados los eventos, si la extensión de impresora se inició para controlar las preferencias de impresión o las notificaciones de impresora, se invocará el controlador. En el fragmento de código anterior, se registró un método denominado OnDriverEvent como controlador de eventos. En el siguiente fragmento de código, el parámetro PrinterExtensionEventArgs es el objeto que permite construir las preferencias de impresión y los escenarios de notificaciones de impresora. PrinterExtensionEventArgs es un contenedor 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 una mala experiencia de usuario asociada a extensiones de impresora lentas o bloqueadas, Windows implementa un tiempo de espera si EnableEvents no se llama en un breve período de tiempo después de iniciar la aplicación. Para habilitar la depuración, este tiempo de espera se deshabilita si hay un depurador asociado al servicio PrintNotify.

Sin embargo, en la mayoría de los casos, todo el código relacionado con la aplicación en el que estamos interesados, se ejecuta durante o después de la devolución de llamada OnDriverEvent. Durante el desarrollo, también puede ser útil mostrar un cuadro de mensajes antes de iniciar una experiencia de preferencias de impresión o notificaciones de impresora desde la devolución de llamada OnDriverEvent. Cuando aparezca el cuadro de mensajes, vuelva a Visual Studio y seleccione Depurar>Asociar al proceso y elija el nombre del proceso. Por último, vuelva al cuadro de mensajes y seleccione Aceptar para reanudar. Esto garantizará que consulte excepciones y alcance los puntos de interrupción a partir de ese punto.

Es posible que se admitan nuevos ReasonIds en el futuro. Como resultado, las extensiones de impresora deben comprobar explícitamente ReasonID y no deben usar una instrucción "else" para detectar el último ReasonID conocido. Si se recibe un ReasonID y se desconoce, la aplicación debe salir correctamente.

Las preferencias de impresión se controlan mediante el objeto PrintSchemaEventArgs.Ticket. Este objeto encapsula tanto los documentos PrintTicket como PrintCapabilities que describen las características y las opciones de un dispositivo. Aunque el XML subyacente también está disponible, el modelo de objetos facilita el trabajo con estos formatos.

Dentro de cada objeto IPrintSchemaTicket o IPrintSchemaCapabilities hay características (IPrintSchemaFeature) y opciones (IPrintSchemaOption). Aunque las interfaces usadas para las características y las opciones son las mismas independientemente del origen, el comportamiento varía ligeramente como resultado del XML subyacente. Por ejemplo, los documentos PrintCapabilities especifican muchas opciones por característica, mientras que los documentos PrintTicket especifican solo la opción seleccionada (o predeterminada). Del mismo modo, los documentos PrintCapabilities especifican cadenas de visualización localizadas, mientras que los documentos PrintTicket no.

Para obtener más información sobre el enlace de datos en WPF, consulte Información general sobre el enlace de datos.

Para maximizar el rendimiento, Microsoft recomienda que las llamadas a GetPrintCapabilities solo se realicen cuando sea necesario actualizar el documento PrintCapabilities.

Como un usuario toma decisiones mediante los controles ComboBox enlazados a datos, el objeto PrintTicket se actualiza automáticamente. Cuando el usuario finalmente hace clic en Aceptar, comienza una cadena de validación asincrónica y finalización. Este patrón asincrónico se usa ampliamente para evitar que se produzcan tareas de larga duración en subprocesos de interfaz de usuario y provocar bloqueos en la interfaz de usuario de preferencias de impresión o en la aplicación que está imprimiendo. A continuación se muestra una lista de los pasos que se usan para procesar los cambios de PrintTicket después de que el usuario haga clic en Aceptar.

  1. PrintSchemaTicket se valida de forma asincrónica mediante el método IPrintSchemaTicket::ValidateAsync.

  2. Cuando se completa la validación asincrónica, Common Language Runtime (CLR) invoca el método PrintTicketValidateCompleted.

    1. Si la validación se realizó correctamente, llama al método CommitPrintTicketAsync y CommitPrintTicketAsync llama al método IPrintSchemaTicket::CommitAsync. Y cuando se completa correctamente la actualización de PrintTicket, esto invoca el método PrintTicketCommitCompleted, que llama a un método de conveniencia que llama al método PrinterExtensionEventArgs.Request.Complete para indicar que las preferencias de impresión están completas y, a continuación, cierra la aplicación.

    2. De lo contrario, presenta la interfaz de usuario al usuario para controlar la situación de restricción.

Si el usuario hace clic en cancelar o cierra la ventana de preferencias de impresión directamente, la extensión de impresora llama a IPrinterExtensionEventArgs.Request.Cancel con un valor HRESULT y un mensaje adecuados para el registro de errores.

Si el proceso de la extensión de impresora se ha cerrado y no ha llamado a los métodos Complete o Cancel como se describe en los párrafos anteriores, el sistema de impresión se revertirá automáticamente al uso de la interfaz de usuario proporcionada por Microsoft.

Para recuperar la información de estado del dispositivo, las extensiones de impresora pueden usar bidi para consultar el dispositivo de impresión. Por ejemplo, para mostrar el estado de entrada de lápiz u otros tipos de estado sobre el dispositivo, las extensiones de impresora pueden usar el método IPrinterExtensionEventArgs.PrinterQueue.SendBidiQuery para emitir consultas bidi al dispositivo. Obtener el estado de bidi más reciente es un proceso de dos pasos que implica configurar un controlador de eventos para el evento OnBidiResponseReceived y llamar al método SendBidiQuery con una consulta bidi válida. El siguiente fragmento de código muestra este proceso de dos pasos.

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

Cuando se recibe la respuesta bidi, se invoca al siguiente controlador de eventos. Este controlador de eventos también tiene una implementación de estado de entrada de lápiz simulada, que puede ser útil para el desarrollo cuando un dispositivo no está disponible. El objeto PrinterQueueEventArgs incluye una respuesta HRESULT y XML bidi. Para obtener más información sobre las respuestas XML bidi, consulte Esquemas de solicitud y respuesta bidi).

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

Notificaciones de impresora

Las notificaciones de impresora se invocan exactamente de la misma manera que las preferencias de impresión. En el controlador OnDriverEvent, si IPrinterExtensionEventArgs indica que un ReasonID coincide con el GUID de DriverEvents, podemos crear una experiencia para controlar este evento.

Las siguientes variables son más útiles para controlar una experiencia funcional de notificaciones de impresora.

  • PrinterExtensionEventArgs.BidiNotification: incluye el XML bidi que provocó que se desencadenara el evento.

  • PrinterExtensionEventArgs.DetailedReasonId: contiene el GUID de eventID del archivo xml del evento del controlador.

El atributo más importante del objeto IPrinterExtensionEventArgs para las notificaciones es la propiedad BidiNotification. Incluye el XML bidi que provocó que se desencadenara el evento. Para obtener más información sobre las respuestas XML bidi, consulte Esquemas de solicitud y respuesta bidi).

Administración de impresoras

Para admitir el rol de una extensión de impresora como una aplicación que se puede usar como concentrador para administrar o mantener impresoras, es posible enumerar las colas de impresión para las que está registrada la extensión de impresora actual y obtener el estado de cada cola. Esto no se muestra en el proyecto PrinterExtensionSample, pero el siguiente fragmento de código se podría agregar al método Main de App.xaml.cs para registrar un controlador de eventos.

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

Una vez enumeradas las colas, se llama al controlador de eventos y se pueden realizar operaciones de estado. Este evento se desencadena periódicamente durante la vigencia de la aplicación para asegurarse de que la lista de colas de impresión enumeradas está actualizada, incluso si el usuario ha instalado más colas desde que se abrió. Como resultado, es importante que el controlador de eventos no cree una nueva ventana cada vez que se ejecute y esto se muestra en el siguiente fragmento de código.

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

Para realizar tareas de mantenimiento mediante una extensión de impresora, Microsoft recomienda que la API WritePrinter heredada se use como se describe en el siguiente pseudocódigo.

OpenPrinter
    StartDocPrinter
        StartPagePrinter
          WritePrinter
        EndPagePrinter
    EndDocPrinter
ClosePrinter

Procedimientos recomendados de rendimiento de la extensión de impresora

Para garantizar la mejor experiencia del usuario, las extensiones de impresora deben diseñarse para cargarse lo más rápido posible. El proyecto de ejemplo de extensión de impresora es una aplicación .NET, lo que significa que se integra en un lenguaje intermedio (IL) que se debe compilar en tiempo de ejecución en el formato adecuado para la arquitectura del procesador nativo. Durante la instalación, Microsoft recomienda que las extensiones de impresora se instalen de acuerdo con los procedimientos recomendados, para asegurarse de que la aplicación se ha compilado para la arquitectura del sistema nativa. Para obtener más información sobre los procedimientos recomendados de compilación e instalación de código, consulte Mejora del rendimiento de inicio para las aplicaciones de escritorio.

Microsoft también recomienda que las extensiones de impresora pospongan tareas de inicialización, como cargar recursos hasta después de llamar al método EnableEvents. Esto minimiza la probabilidad de que la aplicación llame a EnableEvents antes del tiempo de espera de 5 segundos para las extensiones de impresora.

Después de la llamada a OnDriverEvent, las extensiones de impresora deben inicializar su interfaz de usuario y dibujar lo antes posible, haciendo uso de métodos asincrónicos siempre que sea posible para garantizar la capacidad de respuesta. Las extensiones de impresora no deben tener ninguna dependencia en las llamadas de red o bidi para crear el estado inicial de la ventana para las preferencias de impresión o las notificaciones de impresora.

A medida que el usuario toma decisiones mediante la interfaz de usuario en pantalla que afecta a PrintTicket, la extensión de impresora debe usar el método IPrintSchemaTicket::ValidateAsync para validar los cambios lo antes posible. Por último, la extensión de impresora debe usar el método IPrintSchemaTicket::CommitAsync para confirmar los cambios de PrintTicket.

Las extensiones de impresora siempre se ejecutan fuera del proceso desde el proceso invocado. Por lo tanto, debe tener en cuenta el comportamiento de la ventana al desarrollar una extensión de impresora:

  • La propiedad WindowParent de IPrinterExtensionEventArgs especifica el identificador de la ventana que invocó la aplicación.
  • La propiedad WindowModal de IPrinterExtensionEventArgs especifica si se debe ejecutar una extensión de impresora (en modo de preferencias de impresión).

El ejemplo de extensión de impresora muestra cómo crear una interfaz de usuario que generalmente se inicia como ventana superior. Pero en algunos casos, la interfaz de usuario no se mostrará en primer plano, como cuando el proceso que provocó la invocación de la interfaz de usuario se ejecuta en un nivel de integridad diferente o cuando el proceso se compila para una arquitectura de procesador diferente. En este caso, la extensión de impresora debe llamar a FlashWindowEx para solicitar permiso de usuario para llegar al primer plano haciendo parpadear el icono en la barra de tareas.

Esquemas de solicitud y respuesta bidi

Información general sobre el enlace de datos

Mejora del rendimiento de inicio de las aplicaciones de escritorio

Generador de imágenes nativas

Interfaces de esquema de impresión

Windows Performance Analysis Tools