GetPrinterJobs
Una función (escrita en C++) para saber el número de trabajos pendientes de imprimir y el número total de páginas (nuevo)



Publicado: 09/Jun/2001
Actualizado: 13/Jun/2001 (Número de páginas pendientes de imprimir)



Y yo que me quejo de las consultas...
Pues eso, que esta mañana recibí una consulta sobre cómo saber el número de trabajos pendientes de imprimir de una impresora.
Estuve buscando en la MSDN Library y estuve viendo las funciones del API y los tipos definidos que se podrían usar para obtener esa información, pero... al intentar adaptarlo al Visual Basic... nada de nada, (aparte de algún que otro desplante... por fallo de acceso a la memoria y esas cosas que suelen ocurrir cuando se accede al API)... seguí buscando y me encontré con un ejemplo en C/C++ que, entre otras cosas, mostraba cómo obtener el número de trabajos pendientes de imprimir, (que era lo que buscaba), pero hacía uso de "reserva de memoria" y otras cosas que el Visual Basic tiene vetado... Así que, me lancé a "desempolvar" algunas pruebas que hice con el CBuilder (sí, de Borland) y me puse en la tarea de adaptar el "trozo" de código C que encontré.

Y este es el resultado:

(Perdona toda esta cháchara, pero es que si no... pues iba a ser el artículo como muy corto...)

Para usar la función:

MsgBox   "Número de trabajos pendientes de la impresora " & Printer.DeviceName & _
	" es: " & GetPrinterJobs(Printer.DeviceName)

Aquí tienes el código de la función (en C/C++), así como el que habría que usar desde Visual Basic.


/*-----------------------------------------------------------------------------
    gsPrinterJobs.dll                                               (08/Jun/01)
    DLL para devolver el número de trabajos pendiente de una impresora

    Basado en un ejemplo de la MSDN Library

    ©Guillermo 'guille' Som, 2001
//-----------------------------------------------------------------------------
*/

#include <windows.h>

/*
    Devuelve el número de trabajos pendientes de la impresora indicada
    o -1 si se produce algún error.

    El parámetro será el nombre de la impresora (Printer.DeviceName)

*/

extern "C" WINBASEAPI DWORD WINAPI FAR PASCAL _export GetPrinterJobs(BSTR bstrDeviceName)
{
HANDLE 		hPrinter;
DWORD      	cByteNeeded;
PRINTER_INFO_2	*pPrinterInfo = NULL;
LPSTR 		strSrcByVal;
DWORD		Fallo = -1;

// Copia de la cadena en formato C/C++
strSrcByVal = (LPSTR)bstrDeviceName;

// Abrir la impresora
if (!OpenPrinter(strSrcByVal, &hPrinter, NULL))
	return Fallo;

// Obtener el tamaño del buffer
if (!GetPrinter(hPrinter, 2, NULL, 0, &cByteNeeded))
{
   // Si da otro error distinto del esperado...
   if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
       return Fallo;
}

// Reserva memoria para el buffer
pPrinterInfo = (PRINTER_INFO_2 *)malloc(cByteNeeded);
if (!(pPrinterInfo))
   // fallo en la reserva de memoria
   return Fallo;

// Obtener la información de la impresora
if (!GetPrinter(hPrinter,
       2,
       (LPSTR)pPrinterInfo,
       cByteNeeded,
       &cByteNeeded))
{
   // fallo en el acceso al puntero
   free(pPrinterInfo);
   pPrinterInfo = NULL;
   return Fallo;
}

// Cerrar la impresora
ClosePrinter(hPrinter);

// Devolver el valor de cJobs
return (DWORD)pPrinterInfo->cJobs;
}

Ejemplo de cómo usar esta función en Visual Basic:


'------------------------------------------------------------------------------
' Prueba para saber los trabajos pendientes de una impresora        (08/Jun/01)
' Usando una DLL escrita en CBuilder
'
' ©Guillermo 'guille' Som, 2001
'------------------------------------------------------------------------------
Option Explicit

' Declaración de la función
Private Declare Function GetPrinterJobs Lib "gsPrinterJobs.dll" _
    (ByVal sDeviceName As String) As Long

Private Sub cmdInfo_Click()
    Dim s As String
    Dim n As Long
    Const Fallo As Long = -1&
    '
    s = cboPrinters.Text
    '
    n = GetPrinterJobs(s)
    If n = Fallo Then
        List1.AddItem "Fallo al llamar a la función"
    Else
        List1.AddItem s
        List1.AddItem "Número de trabajos pendientes: " & n
    End If
End Sub

Private Sub Form_Load()
    ' Enumerar las impresoras disponibles
    Dim tPrinter As Printer
    '
    ' Añadir las impresoras disponibles
    For Each tPrinter In Printers
        cboPrinters.AddItem tPrinter.DeviceName
    Next
    ' Asignar la variable de la impresora seleccionada
    Set tPrinter = Printer
    If cboPrinters.ListCount > 0 Then
        cboPrinters.Text = tPrinter.DeviceName
    End If
End Sub

Este es el aspecto del formulario:

 

Espero que te pueda ser de utilidad.

Nos vemos.
Guillermo
P.S.
Aquí tienes el código C/C++ y del ejemplo en VB: PrinterJobs.zip 59.6KB (actualizado el 13/Jun/2001)


13/Jun/2001: Número de páginas pendientes de imprimir.

Le he añadido una nueva función a la librería, en este caso es para saber también el número de páginas que quedan por imprimir (además del número de trabajos pendientes)
He de avisar de que no todas las impresoras tienen esta característica, la he probado en una HP 840C y funciona bien, (como podrás comprobar en la imagen).


La nueva función se llama: GetNumPages y se declara de la siguiente forma:
fíjate que el segundo parámetro es ByRef, para que se devuelva en la variable el número de trabajos pendientes.


Private Declare Function GetNumPages Lib "gsPrinterJobs.dll" _
    (ByVal sDeviceName As String, ByRef NumJobs As Long) As Long

Para usarla:


    Dim s As String
    Dim n As Long
    Const Fallo As Long = -1&
    Dim p As Long
    '
    s = cboPrinters.Text
    '
    ' Número de trabajos pendientes y número de páginas             (11/Jun/01)
    '
    n = GetNumPages(s, p)
    If n = Fallo Then
        List1.AddItem "Fallo al llamar a la función"
    Else
        List1.AddItem s
        List1.AddItem "Número de trabajos pendientes: " & p & ", páginas: " & n
    End If

 

Aquí tienes el código en C/C++ de la nueva función:


/*
    Función para leer el número de páginas pendientes de imprimir   (11/Jun/01)
    Basado en un ejemplo de la MSDN Library:
    How To Call Win32 Spooler Enumeration APIs Properly
*/
// Devuelve el número de páginas por imprimir y en el segundo parámetro,
// (por referencia) el número de trabajos pendientes
extern "C" WINBASEAPI DWORD WINAPI FAR PASCAL _export
	GetNumPages(BSTR bstrDeviceName, DWORD *NumJobs)
{
HANDLE      hPrinter;
DWORD       dwNeeded, dwReturned, i;
JOB_INFO_1  *pJobInfo;
LPTSTR      szPrinterName;
DWORD       Fallo = -1;
DWORD       TotalPaginas=0;

// Copia de la cadena en formato C/C++
szPrinterName = (LPSTR)bstrDeviceName;

// You need a printer handle, open the printer
if( ! OpenPrinter( szPrinterName, &hPrinter, NULL ) )
	return Fallo;

// First you call EnumJobs() to find out how much memory you need
if( ! EnumJobs( hPrinter, 0, 0xFFFFFFFF, 1, NULL, 0, &dwNeeded,
	     &dwReturned ) )
{
	// It should have failed, but if it failed for any reason other
	// than "not enough memory", you should bail out
	if( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
	{
 	    ClosePrinter( hPrinter );
 	    return Fallo;
	}
}
// Allocate enough memory for the JOB_INFO_1 structures plus
// the extra data - dwNeeded from the previous call tells you
// the total size needed
if( (pJobInfo = (JOB_INFO_1 *)malloc( dwNeeded )) == NULL )
{
	ClosePrinter( hPrinter );
	return Fallo;
}
// Call EnumJobs() again and let it fill out our structures
if( ! EnumJobs( hPrinter, 0, 0xFFFFFFFF, 1, (LPBYTE)pJobInfo,
	     dwNeeded, &dwNeeded, &dwReturned ) )
{
	ClosePrinter( hPrinter );
	free( pJobInfo );
	return Fallo;
}
// You're done with the printer handle, close it
ClosePrinter( hPrinter );

// dwReturned tells how many jobs there are
// Here, you'll simply display the number of jobs found
*NumJobs = dwReturned;

TotalPaginas = 0;

// It's easy to loop through the jobs and access each one
for(i=0;i<dwReturned;i++)
{
	// pJobInfo[i] is a JOB_INFO_1 struct for that job
	// so here you could do whatever you want for each job
        TotalPaginas = TotalPaginas + pJobInfo[i].TotalPages;
}

// Clean up
free( pJobInfo );
return (DWORD)TotalPaginas;
}

Puedes usar el link de arriba, ya que el fichero está actualizado.

¡Que lo disfrutes!
Nos vemos.
Guillermo


ir al índice