Delphi – Monitor de impresiones.

Organizando mis proyectos más viejos me encontré con un programa que hice allá por el 2004 y me ha parecido que puede ser útil para alguien más, por ello me he decidido a compartirlo en el blog.

Haciendo uso de la API WinSpool encapsulada en la unidad WinSpool.pas podemos consultar los trabajos procesados o en proceso de las impresoras instaladas en Windows y de esa manera crear un monitor de impresiones. Es importante mencionar que en las versiones más recientes de Windows será necesario ejecutar el programa como administrador para poderlo ver funcionar. Si deseas conocer más acerca de esta Api puedes encontrar la documentación más reciente en el siguiente enlace: Print Spooler API Reference.

¿Cómo Funciona?

Este programa hace uso de la función EnumPrinters para obtener la lista de impresoras registradas en el sistema y de esa manera poder consultar su estatus con relación a los trabajos de impresión ejecutados o en proceso. Debido a que para las operaciones de consulta será necesario conocer el nombre de las impresoras instaladas lo primero que hace al iniciar el programa es invocar a la función GetPrinters definida de la siguiente manera en el formulario principal:

Procedure TfrmMonitor.GetPrinters;
Var
  Needed, i: Cardinal;
begin
  If not enumprinters(PRINTER_ENUM_LOCAL, nil, 5, nil, 0, Needed, NumPrinters)
    then
    ppi5 := GetMemory(Needed);
  if assigned(ppi5) then
    enumprinters(PRINTER_ENUM_LOCAL, nil, 5, ppi5, Needed, Needed, NumPrinters);
  SetLength(APrinterInfo, NumPrinters);
  For i := 0 To NumPrinters - 1 Do
  begin
    APrinterInfo[i].Name := ppi5.pPrinterName;
    APrinterInfo[i].Pos := i;
    Inc(ppi5);
    if Length(ppi5.pPrinterName) > 0 then
      ListView1.Items.Add.Caption := ppi5.pPrinterName;
  End;
  ListView1.Columns.Items[0].Width := 300;
End;

Y ya que se obtienen las impresoras instaladas aprovecha para agregarlas al listado en el formulario como puede verse en la siguiente imagen:

 

ListaImpresoras

 

Una vez que obtiene el listado de impresoras el programa debe obtener el Handle de cada una de ellas para poder establecer comunicación con cada dispositivo.

Importante** ¿Qué es un Handle? En Windows, todos los recursos del sistema (archivos, dispositivos, ventanas, objetos, etc) tienen un identificador único que brinda información acerca del recurso por lo que toda comunicación a través de la API con ellos necesariamente deberá pasar por obtener su Handle. Si deseas conocer más acerca de esto te recomiendo leer el siguiente artículo: Handles and Objects.

Para obtener el Handle de cada impresora el programa hace uso de la función OpenPrinter como podemos ver en el siguiente código:

Function TfrmMonitor.GetHandlePrinter(IndexPrinter: Integer): Thandle;
const
  Defaults: TPrinterDefaults = (pDatatype: nil; pDevMode: nil;
    DesiredAccess: PRINTER_ACCESS_USE or PRINTER_ACCESS_ADMINISTER);
begin
  if not OpenPrinter(Pchar(APrinterInfo[IndexPrinter].Name), Result, @Defaults)
    then
    RaiseLastWin32Error;
End;

Como puedes notar se requiere acceso a nivel de administrador por lo que si no ejecutas el programa con este nivel de seguridad obtendrás el mensaje “System Error. Code: 5. Acceso Denegado” como se puede ver en la siguiente imagen:

 

systemerror5

 

Si todo ha salido bien hasta este punto, el programa se ejecutará y aprovechando un componente TTimer consultará en un intervalo de 1 milisegundo el estado de las impresoras.  Debido a que el procedimiento de consulta bloqueaba el programa decidí colocarlo en un Thread quedando la clase de la siguinte manera :

  MonitorPrinter = class(TThread)
  private
    { Private declarations }
    PGrid: TStringGrid;
    PName: PrinterName;
    PHandle: PrinterHandle;
    procedure ProcesaInfo;
  protected
    Function SizeJobs(HPrinter: Thandle): DWord;
    Procedure CargaFilaDatos(Fila: TStrings; PTrabajo: PrinterTrabajos;
      Pos: Integer);
    procedure Execute; override;
  Public
    constructor Create(PMGrid: TStringGrid);
    property PrName: String Write PName;
    property PrHandle: PrinterHandle Write PHandle;
  end;

De esta Clase el método clave es ProcesaInfo pues hace uso de la función EnumJobs que como indica la documentación de la API nos devuelve información detallada del estado del dispositivo permitiéndonos obtener el siguiente resultado:

PrinterMonitor

 

El código fuente del programa podas descargarlo en el siguiente enlace y allí encontrarás el código aquí esbozado junto con todo el código necesario para el funcionamiento de este pequeño programa. Espero sea de utilidad.

 

 

6 Comments

  1. saludos desde Sucre – Bolivia, queria Felicitarte por este aporte, pero es posible agregar el costo de impresion por hoja de color y blanco y negro, para asi tener un resultado en unidades monetarias y asi controlar mejor el uso de las impresoras??????????????

    • Si amigo, es posible. solo hay que agregar una base de datos al sistema para establecer opciones de costo por impresión y además que lleve un registro de impresiones monitoreadas. Supongo que te gustaria ver algo así integrado en este pequeño programa….

  2. hola, esta muy bueno tu programa pero queria saber si se puede hacer que todas las impresiones pasen pausadas y despues poder escojer cuales imprimir y cuales cancelar

    gracias

Deja un comentario