Problem With Checking Printer Status

limva
limva used Ask the Experts™
on
I need to check the printer status, whether it is ready, offline, out of paper, paper jam, etc.

I've gone through several GetPrinter API samples but I just could not get it!  I have trouble when it comes to API calls.  I need someone to provide me with the code that I'll need to simply plug into my module.

I'm using Win98 and VB6.  There is only 1 printer installed which is always set as the default.

Please help!
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Also don't forget to check current number of queued print jobs. I think some versions of Windows (including Win98) limit you to a maximum of 100 queued print jobs.

But simply you don't really need to check any of these things as they are handled by the windows spooler.  So first check that spooling has been turned on and spool file format is RAW. (If not RAW you start to get random SPOOL32 GP Faults after about 2000 pages.)

However, when printing you will get the odd error but there is a simple way of handling these errors and normally the printer spooler is just telling you to slow down for a bit.

Whenever you touch the printer you NEED to set an error trap because you will get the odd error.

For example:

On Error Goto PrinterError
Try=0
Printer.NewPage
On Error Goto OtherError

' Place pleanty of DoEvents in you DO/For loops else the printer spooler cant work so well.


' Handle Printer Faults
PrinterError:

' Many errors will clear themselves
' so just give the printer some more time

Try = Try + 1
If Try > 10 Then
 
   ' Err.Description will contain printer error info.
   ' Whatever it is.
   ' e.g. some kind of printerfault.
   ok=MsgBox(Err.Description,VbRetryCancel,"Printer Error")
   if ok=vbCancel Then
       ' Abandon Job
       On error resume next
       printer.killdoc
       GoTo AbortJob (or Exit Function/Sub)
   End IF
   Try=0
Else
   ' give printer some time say 2 seconds. Problem normally/may correct itself.
   Sleep 2000
End IF

Resume ' Control will return to the line that caused the error and retry the operation

' Execution never gets here.

' Declaration for the sleep API call place in a module
' or make Private Declare to place in a Form declaration.
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)


If you realy want to get cute you never use the printer object at all.

You put all your code in a class module which talks to the printer for you.

Dim MYPrinter as New zPrinterControler

MYPrinter.Preview=False ' you can also handle printer preview too.

Ok=MYPrinter.ActionOK("NP") ' Print New page
If Not OK Then
  OK=MYPrinter.ActionOK("NP")
  End ' handle operator abandon
End If

In you class module zPrinterControler you need some code like:

Public Function ActionOK(ActionCode as string)
Dim PMode as string
Dim StartTime as date

PPMode=ucase(ActionCode)
StartTime = Now
Do
    On Error Resume Next
    Err = 0
    Select Case pmode$
    Case Is = "NP"
        Printer.NewPage
    Case Is = "KD"
        Printer.KillDoc
    Case Is = "ED"
        Printer.EndDoc
    Case Else
        Printer.Print dt$;
    End Select
 
    If Err = 0 Then Exit Do
   
    If DateDiff("S",Now,StartTime)>20 Then  

        ok = MsgBox("Printer error: " + Str$(Err) + " - "  Error$ + CrLf + CrLf + "Retry or Cancel", vbRetryCancel + vbExclamation, "Printer Error")
        If ok <> vbRetry Then
            ActionOK=False
            Exit Function
        End If
    Else
        Sleep 2000
    End If
Loop

ActionOK = True

On Error GoTo 0

End Sub


It takes several weeks to put togeter a realy comprehensive printer handler, so a printer error trap is the quick and filthy way to get your app. done.


Author

Commented:
Thanks for your comment, inthedark.  But my ap cannot send printing data to the printer.  It should only check the printer status.  So far as I've seen, the only way to check status is through Windows API GetPrinter.
Ensure you’re charging the right price for your IT

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden using our free interactive tool and use it to determine the right price for your IT services. Start calculating Now!

Author

Commented:
No other comments!

Author

Commented:
No other comments!
I have looked for something that will help and the problem is that you can't talk to the printer you can only talk to the spooler.

Sorry, I though I pasted some links whch confirmed this. I've had a busy week.

http://support.microsoft.com/default.aspx?scid=kb;EN-US;q160129

So you can get the status of the printer only by looking at the printer port and talking to the printer directly.  Each Make and Model of printer speaks a differnt language. That is one of the success stories of windows - in the old days you had to write a differnt software driver for each make of printer.  Now the manufacturer writes just one driver and the windows sppoler talks to the printer via this driver.

In the manual for the printer it may describe what each of the wire in the cable are meant to indicate.  You can peek at these values, but I suspect that you will just get off/on line.

Where you need to go from here is to get the iEEE inteface spec. Accessing printer ports.
Here is the code you need from:

http://delphi.about.com/gi/dynamic/offsite.htm?site=http%3A%2F%2Fwww.undu.com%2FArticles%2F990228a.html

Its in assembler....

function TestPrinterStatus(LPTPort: word): byte;
var
  Status: byte;
  CheckLPT: word;
begin
  Status := 0;
  if (LPTPort >= 1) and (LPTPort <= 3) then
    begin
      CheckLPT := LPTPort -1;
      asm
        mov dx, CheckLPT;
        mov al, 0;
        mov ah, 2;
        int 17h;
        mov &Status, ah;
      end;
    end;
  Result := Status;
end;

Pass in the LPT port number you want to check & get the



following back:
01h - Timeout
08h - I/O Error
10h - Printer selected
20h - Out of paper
40h - Printer acknowledgement
80h - Printer not busy (0 if busy)


I have seen and example of calling interrupt and from vb.
We can create a DLL that can do this.

Back later.....
You need VBINT.DLL and VBINT.BAS.

Load VBINT.bas as a module.

Function GetPrinterStatus(PrinterPort as long) as Long

DIM Regs as vbregs
Dim Rtn as integer

rtn=printerport-1

'  mov dx, CheckLPT;
REGS.DX=rtn '0=lpt1,2=lpt2,etc.

'       mov al, 0;
'       mov ah, 2;
regs.ax=2*256+0

'       int 17h;
Rtn% = VBInt(&H17, Regs, Regs)

GetPrinterStatus=int(regs.ax/256)

End Function


I have not tested it but it should work,

Job done!

Author

Commented:
Hi inthedark.  I thought my question was dead. Thanks for your comments!  I'll examine and try them and I'll let you know after the weekend.

Thanks again!

Author

Commented:
Sorry it took me a long time to get back to you, inthedark.  Actually, the solution I needed is for more than 60 computers installed in several locations, a few of them are too far for me to get to.  I think the easiest way for me is to just use the GetPrinter API and monitor the Jobs Count.  If there is a printer jam or if it is out of paper, the jobs will be qeued and then reported in Jobs Count.  So if Jobs Count is not 0, that means the printer is not clearing them.  I would know this because printing is only 1 at a time and done only once in a while.

I'm sure your solution works though I have not tried it.

Thanks again!
If you have a busy office be carefull in Win98 you can only have 100 print jobs in the printer queue.

Another no coding way would be to have a Win2K server which hanldes all of the print jobs, it can be made to spool to the remote printers, any problem on any printer would cause the 2K server to pop-up a message.

Thanks for the points.
mh2

Commented:
Hi,

I have a similar problem to you.
My application printing thru a hidden WEBBROWSER control can lockup and thro a javascript error if the printer is off or out of paper (of course, i can't simulate it!)

Unfortunatly I have not come up with a solution, I'm beggining to feel it is not possible, cos of the driver architecture in windows this is hidden from you.

There is nothing (to my knowledge) that can be done before the spooler has detected an error on the current job. Once a job has found an error (about a 1.5 minutes.. anyone know why so long?) you can detect an "error" status. Be ideal if we could reduce the timeout to say 5 seconds, cos many print jobs could be sent in 1.5 mins.

Here some code i got from the msdn, its in C++, i had to write a c++ dll to do this, as i could not get setprinter to work from vb, but my vb knowledge is not very good, so it may be a simple task to translate.

Just export IsPrinterSpoolerError()
In my VB app, I have...

If IsPrinterSpoolerError() Then
  SendToPrinter = FALSE
  Exit Function
End If

Here the code....

BOOL GetJobs(HANDLE hPrinter,        /* Handle to the printer. */
             JOB_INFO_2 **ppJobInfo, /* Pointer to be filled.  */
             int *pcJobs,            /* Count of jobs filled.  */
             DWORD *pStatus)         /* Print Queue status.    */

{

   DWORD cByteNeeded,
         nReturned,
         cByteUsed;
   JOB_INFO_2       *pJobStorage = NULL;
   PRINTER_INFO_2   *pPrinterInfo = NULL;

   /* Get the buffer size needed. */
   if (!GetPrinter(hPrinter, 2, NULL, 0, &cByteNeeded))
   {
     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
         return FALSE;
   }

   pPrinterInfo = (PRINTER_INFO_2 *)malloc(cByteNeeded);
   if (!(pPrinterInfo))
     /* Failure to allocate memory. */
     return FALSE;

   /* Get the printer information. */
   if (!GetPrinter(hPrinter,
         2,
         (unsigned char*)(LPSTR)pPrinterInfo,
         cByteNeeded,
         &cByteUsed))
   {
     /* Failure to access the printer. */
     free(pPrinterInfo);
     pPrinterInfo = NULL;
     return FALSE;
   }

   /* Get job storage space. */
   if (!EnumJobs(hPrinter,
         0,
         pPrinterInfo->cJobs,
         2,
         NULL,
         0,
         (LPDWORD)&cByteNeeded,
         (LPDWORD)&nReturned))
   {
     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
     {
         free(pPrinterInfo);
         pPrinterInfo = NULL;
         return FALSE;
     }
   }

   pJobStorage = (JOB_INFO_2 *)malloc(cByteNeeded);
   if (!pJobStorage)
   {
     /* Failure to allocate Job storage space. */
     free(pPrinterInfo);
     pPrinterInfo = NULL;
     return FALSE;
   }

   ZeroMemory(pJobStorage, cByteNeeded);

   /* Get the list of jobs. */
   if (!EnumJobs(hPrinter,
         0,
         pPrinterInfo->cJobs,
         2,
         (LPBYTE)pJobStorage,
         cByteNeeded,
         (LPDWORD)&cByteUsed,
         (LPDWORD)&nReturned))
   {
     free(pPrinterInfo);
     free(pJobStorage);
     pJobStorage = NULL;
     pPrinterInfo = NULL;
     return FALSE;
   }

   /*
   *  Return the information.
   */
   *pcJobs = nReturned;
   *pStatus = pPrinterInfo->Status;
   *ppJobInfo = pJobStorage;
   free(pPrinterInfo);

   return TRUE;

}

bool WINAPI IsPrinterSpoolerError()
{
   JOB_INFO_2  *pJobs;
   int         cJobs,
             i;
   DWORD       dwPrinterStatus;
   HANDLE hPrinter;
   PRINTER_DEFAULTS def;
   def.DesiredAccess = PRINTER_ALL_ACCESS;
   def.pDevMode = NULL;
   def.pDatatype = NULL;

   if ( !OpenPrinter("Brother HL-1450 series",&hPrinter,&def) )
   {
      return true;
   }

   /*
   *  Get the state information for the Printer Queue and
   *  the jobs in the Printer Queue.
   */
   if (!GetJobs(hPrinter, &pJobs, &cJobs, &dwPrinterStatus))
     return FALSE;

   /*
   *  If the Printer reports an error, believe it.
   */
   if (dwPrinterStatus &
     (PRINTER_STATUS_ERROR |
     PRINTER_STATUS_PAPER_JAM |
     PRINTER_STATUS_PAPER_OUT |
     PRINTER_STATUS_PAPER_PROBLEM |
     PRINTER_STATUS_OUTPUT_BIN_FULL |
     PRINTER_STATUS_NOT_AVAILABLE |
     PRINTER_STATUS_NO_TONER |
     PRINTER_STATUS_OUT_OF_MEMORY |
     PRINTER_STATUS_OFFLINE |
     PRINTER_STATUS_DOOR_OPEN))
   {
     return TRUE;
   }

   /*
   *  Find the Job in the Queue that is printing.
   */
   for (i=0; i < cJobs; i++)
   {
     if (pJobs[i].Status & JOB_STATUS_PRINTING)
     {
         /*
          *  If the job is in an error state,
          *  report an error for the printer.
          *  Code could be inserted here to
          *  attempt an interpretation of the
          *  pStatus member as well.
          */
         if (pJobs[i].Status &
             (JOB_STATUS_ERROR |
             JOB_STATUS_OFFLINE |
             JOB_STATUS_PAPEROUT |
             JOB_STATUS_BLOCKED_DEVQ))
         {
             return TRUE;
         }
     }
   }

   /*
   *  No error condition.
   */
   return FALSE;

}



Commented:
hey inthedark i am facing a problem with the soln u passed on my problem is that it gives file not found error fro vbint whenever i pass the value of port
do help me as early as possible
thank you

Commented:
and yes i have copied the vbint.dll to both sysytem and system32 folder
Post the code and declarations you are using

Commented:
i am using the same thing as u have ssaid above any wayz here they are

this code goes in to the form
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Option Explicit

Private Sub Command1_Click()
Dim ret As Long
ret = GetPrinterStatus(0)
End Sub
Function GetPrinterStatus(PrinterPort As Long) As Long

Dim Regs As VBREGS
Dim Rtn As Integer

Rtn = PrinterPort - 1

'  mov dx, CheckLPT;
Regs.dx = Rtn '0=lpt1,2=lpt2,etc.

'       mov al, 0;
'       mov ah, 2;
Regs.ax = 2 * 256 + 0

'       int 17h;
Rtn% = VBInt(&H17, Regs, Regs)

GetPrinterStatus = Int(Regs.ax / 256)

End Function

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
this one is in the module
Option Explicit

Type VBREGS
  ax As Integer
  bx As Integer
  cx As Integer
  dx As Integer
  si As Integer
  di As Integer
  cflag As Integer
  ds As Integer
  es As Integer
End Type

Declare Function VBInt% Lib "VBINT.DLL" Alias "#1" (ByVal servNr%, inRegs As VBREGS, outRegs As VBREGS)
Declare Function GetSegment% Lib "VBINT.DLL" Alias "#2" (ByVal stringVar$)
Declare Function GetOffset% Lib "VBINT.DLL" Alias "#3" (ByVal stringVar$)

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
and vbint.dll is copied in to system  folder

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial