We help IT Professionals succeed at work.

Capture print details.

Jaymol
Jaymol asked
on
Hi.

Is it possible to capture information about print requests as they go to a printer?  I'm considering writing a service or systray application that keeps a log of print jobs as one of my applications is becoming a little unstable after running solidly for about 4 months.

I just want to get the info that you get when you open the printer icon from the system tray.

Thanks,

John.
Comment
Watch Question

Commented:
Take a look at the EnumJobs API function. This doesn't give you an "event" to catch a print job starting, but it will give you the information about jobs pending/printing.


God luck!!

Commented:
// The Windows print spooler regularly broadcasts a system wide
// WM_SPOOLERSTATUS message each time a job is added or deleted from the
// spooler que. The following example demonstrates trapping for this
// message.

type
  TForm1 = class(TForm)
    Label1: TLabel;
  private
    { Private declarations }
    procedure WM_SpoolerStatus(var Msg : TWMSPOOLERSTATUS); message WM_SPOOLERSTATUS;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.WM_SpoolerStatus(var Msg : TWMSPOOLERSTATUS);
begin
  Lable1.Caption := IntToStr(msg.JobsLeft)+' Jobs currenly in spooler';
  msg.Result := 0;
end;

Commented:
Sorry I think that you need name and not numbers . Try this:

uses Printers, WinSpool;

function PrinterStatusText(Status: Integer): String;
begin
  case Status of
    0:                            Result := 'Waiting';
    JOB_STATUS_PAUSED:            Result := 'Paused';
    JOB_STATUS_ERROR:             Result := 'Error';
    JOB_STATUS_DELETING:          Result := 'Deleting';
    JOB_STATUS_SPOOLING:          Result := 'Spooling';
    JOB_STATUS_PRINTING:          Result := 'Printing';
    JOB_STATUS_OFFLINE:           Result := 'Offline';
    JOB_STATUS_PAPEROUT:          Result := 'Paper Out';
    JOB_STATUS_PRINTED:           Result := 'Printed';
    JOB_STATUS_DELETED:           Result := 'Deleted';
    JOB_STATUS_BLOCKED_DEVQ:      Result := 'Blocked';
    JOB_STATUS_USER_INTERVENTION: Result := 'User Intervention';
    JOB_STATUS_RESTART:           Result := 'Restart';
  else Result := 'Status ' + IntToStr(Status);
  end;
end;

procedure GetJobs(PrinterName: String; JobList: TStrings);
const
  InfoLevel = 1;
  FirstJob = 0;
  LastJob = 19;
var
  Jobs: array [FirstJob..LastJob] of TJobInfo1;
  PrinterHandle, BytesNeeded, I, NumJobs: Integer;
begin
  if OpenPrinter(PChar(PrinterName),PrinterHandle,nil) then begin
    if
EnumJobs(PrinterHandle,FirstJob,LastJob+1,InfoLevel,@Jobs,SizeOf(Jobs),BytesNeed
ed,NumJobs) then begin
      JobList.Clear;
      for I := 0 to NumJobs-1 do
      with Jobs[I] do
        JobList.Add(Format('%s
(%s)',[StrPas(pDocument),PrinterStatusText(Status)]));
    end;
    ClosePrinter(PrinterHandle);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  GetJobs('HP Laserjet 4P',Memo1.Lines);
end;


Commented:
HERE IS A COMPLET AND WORKING SAMPLE PROJECT >
I JUST TESTED IT . SORRY FOR PREVIOUS .


unit Unit1;


interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,StdCtrls,WinSpool,Printers ;

type
TForm1 = class(TForm)
    Button1: TButton;
    ListBox1: TListBox;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
private
  { Private declarations }
  //procedure WM_SpoolerStatus(var Msg : TWMSPOOLERSTATUS);message WM_SPOOLERSTATUS;
public
  { Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

{procedure TForm1.WM_SpoolerStatus(var Msg : TWMSPOOLERSTATUS);
begin
Label1.Caption := IntToStr(msg.JobsLeft) + ' Jobs currenly in spooler';
msg.Result := 0;
end;}

procedure TForm1.Button1Click(Sender: TObject);

 const
   MaxJobs = 1024;
 type
   TArrayofJobs = array[1..1024] of TJobInfo2;
 var
   NumJobs : integer;
   pcbNeed,pcReturned:DWord;
   i : integer;
   Device : array[0..255] of char;
   Driver : array[0..255] of char;
   Port   : array[0..255] of char;
   hPrinter: THandle;
   hDeviceMode: THandle;
   Buffer: Pointer;
   TmpWinDir : array[0..255] of Char;
   Windir : string;
   Inputfile : textfile;
   line : string;

 begin
   if GetWindowsDirectory(TmpWinDir, sizeof(TmpWinDir)) <> 0 then
     Windir := Strpas(TmpWinDir) + '\SPOOL\PRINTERS\'
   else
     Windir := 'C:\WINDOWS\SPOOL\PRINTERS\';


   Printer.GetPrinter(Device, Driver, Port, hDeviceMode);
   if not WinSpool.OpenPrinter(@Device, hPrinter, nil) then exit;

   ListBox1.items.clear;
   ListBox1.items.add('Windir :='+WinDir);
   GetPrinter(hPrinter, 2, nil,0,@pcbNeed);  // return false, Don't Care
   GetMem(Buffer, pcbNeed);
   if GetPrinter(hPrinter, 2, Buffer, pcbNeed, @pcbNeed) then
     begin
       if TPrinterInfo2A(Buffer^).pServerName <> nil then

ListBox1.items.add('pServerName='+TPrinterInfo2A(Buffer^).pServerName);
       if TPrinterInfo2A(Buffer^).pPrinterName <> nil then

ListBox1.items.add('pPrinterName='+TPrinterInfo2A(Buffer^).pPrinterName);
       if TPrinterInfo2A(Buffer^).pShareName <> nil then

ListBox1.items.add('pShareName='+TPrinterInfo2A(Buffer^).pShareName);
       if TPrinterInfo2A(Buffer^).pComment <> nil then

ListBox1.items.add('pComment='+TPrinterInfo2A(Buffer^).pComment);
       if TPrinterInfo2A(Buffer^).pLocation <> nil then

ListBox1.items.add('pLocation='+TPrinterInfo2A(Buffer^).pLocation);
       if TPrinterInfo2A(Buffer^).pPortName <> nil then

ListBox1.items.add('pPortName='+TPrinterInfo2A(Buffer^).pPortName);
       if TPrinterInfo2A(Buffer^).pDriverName <> nil then

ListBox1.items.add('pDriverName='+TPrinterInfo2A(Buffer^).pDriverName);
       if TPrinterInfo2A(Buffer^).pSepFile <> nil then
       
ListBox1.items.add('pSepFile='+TPrinterInfo2A(Buffer^).pSepFile);
       if TPrinterInfo2A(Buffer^).pPrintProcessor <> nil then

ListBox1.items.add('pPrintProcessor='+TPrinterInfo2A(Buffer^).pPrintProcessor);
       if TPrinterInfo2A(Buffer^).pDatatype <> nil then

ListBox1.items.add('pDatatype='+TPrinterInfo2A(Buffer^).pDatatype);
       if TPrinterInfo2A(Buffer^).pParameters <> nil then

ListBox1.items.add('pParameters='+TPrinterInfo2A(Buffer^).pParameters);

ListBox1.items.add('Attributes='+inttostr(TPrinterInfo2A(Buffer^).Attributes
));
       if TPrinterInfo2A(Buffer^).Attributes and
PRINTER_ATTRIBUTE_QUEUED
= PRINTER_ATTRIBUTE_QUEUED then
         ListBox1.items.add('  PRINTER_ATTRIBUTE_QUEUED');
       if TPrinterInfo2A(Buffer^).Attributes and
PRINTER_ATTRIBUTE_QUEUED
= PRINTER_ATTRIBUTE_QUEUED then
         ListBox1.items.add('  PRINTER_ATTRIBUTE_QUEUED');
       if TPrinterInfo2A(Buffer^).Attributes and
PRINTER_ATTRIBUTE_DIRECT
= PRINTER_ATTRIBUTE_DIRECT then
         ListBox1.items.add('  PRINTER_ATTRIBUTE_DIRECT');
       if TPrinterInfo2A(Buffer^).Attributes and
PRINTER_ATTRIBUTE_DEFAULT
= PRINTER_ATTRIBUTE_DEFAULT then
         ListBox1.items.add('  PRINTER_ATTRIBUTE_DEFAULT');
       if TPrinterInfo2A(Buffer^).Attributes and
PRINTER_ATTRIBUTE_SHARED
= PRINTER_ATTRIBUTE_SHARED then
         ListBox1.items.add('  PRINTER_ATTRIBUTE_SHARED');
       if TPrinterInfo2A(Buffer^).Attributes and
PRINTER_ATTRIBUTE_NETWORK
= PRINTER_ATTRIBUTE_NETWORK then
         ListBox1.items.add('  PRINTER_ATTRIBUTE_NETWORK');
       if TPrinterInfo2A(Buffer^).Attributes and
PRINTER_ATTRIBUTE_HIDDEN
= PRINTER_ATTRIBUTE_HIDDEN then
         ListBox1.items.add('  PRINTER_ATTRIBUTE_HIDDEN');
       if TPrinterInfo2A(Buffer^).Attributes and
PRINTER_ATTRIBUTE_LOCAL =
PRINTER_ATTRIBUTE_LOCAL then
         ListBox1.items.add('  PRINTER_ATTRIBUTE_LOCAL');
       if TPrinterInfo2A(Buffer^).Attributes and
PRINTER_ATTRIBUTE_ENABLE_DEVQ = PRINTER_ATTRIBUTE_ENABLE_DEVQ then
         ListBox1.items.add('  PRINTER_ATTRIBUTE_ENABLE_DEVQ');
       if TPrinterInfo2A(Buffer^).Attributes and
PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS = PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS
then
         ListBox1.items.add('  PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS');
       if TPrinterInfo2A(Buffer^).Attributes and PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST = PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST
then
         ListBox1.items.add('PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST');
       if TPrinterInfo2A(Buffer^).Attributes and PRINTER_ATTRIBUTE_WORK_OFFLINE = PRINTER_ATTRIBUTE_WORK_OFFLINE

then
         ListBox1.items.add('  PRINTER_ATTRIBUTE_WORK_OFFLINE');
       if TPrinterInfo2A(Buffer^).Attributes and PRINTER_ATTRIBUTE_ENABLE_BIDI = PRINTER_ATTRIBUTE_ENABLE_BIDI

then
         ListBox1.items.add('  PRINTER_ATTRIBUTE_ENABLE_BIDI');

ListBox1.items.add('Priority='+inttostr(TPrinterInfo2A(Buffer^).Priority));

ListBox1.items.add('DefaultPriority='+inttostr(TPrinterInfo2A(Buffer^).DefaultPriority));

ListBox1.items.add('StartTime='+inttostr(TPrinterInfo2A(Buffer^).StartTime))
;

ListBox1.items.add('UntilTime='+inttostr(TPrinterInfo2A(Buffer^).UntilTime))
;

ListBox1.items.add('Status='+inttostr(TPrinterInfo2A(Buffer^).Status));
       if TPrinterInfo2A(Buffer^).Status and PRINTER_STATUS_PAUSED =
PRINTER_STATUS_PAUSED then
         ListBox1.items.add('  PRINTER_STATUS_PAUSED');
       if TPrinterInfo2A(Buffer^).Status and PRINTER_STATUS_ERROR =
PRINTER_STATUS_ERROR then
         ListBox1.items.add('  PRINTER_STATUS_ERROR');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_PENDING_DELETION = PRINTER_STATUS_PENDING_DELETION then
         ListBox1.items.add('  PRINTER_STATUS_PENDING_DELETION');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_PAPER_JAM =
PRINTER_STATUS_PAPER_JAM then
         ListBox1.items.add('  PRINTER_STATUS_PAPER_JAM');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_PAPER_OUT =
PRINTER_STATUS_PAPER_OUT then
         ListBox1.items.add('  PRINTER_STATUS_PAPER_OUT');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_MANUAL_FEED =
PRINTER_STATUS_MANUAL_FEED then
         ListBox1.items.add('  PRINTER_STATUS_MANUAL_FEED');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_PAPER_PROBLEM
= PRINTER_STATUS_PAPER_PROBLEM then
         ListBox1.items.add('  PRINTER_STATUS_PAPER_PROBLEM');
       if TPrinterInfo2A(Buffer^).Status and PRINTER_STATUS_OFFLINE
=
PRINTER_STATUS_OFFLINE then
         ListBox1.items.add('  PRINTER_STATUS_OFFLINE');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_IO_ACTIVE =
PRINTER_STATUS_IO_ACTIVE then
         ListBox1.items.add('  PRINTER_STATUS_IO_ACTIVE');
       if TPrinterInfo2A(Buffer^).Status and PRINTER_STATUS_BUSY =
PRINTER_STATUS_BUSY then
         ListBox1.items.add('  PRINTER_STATUS_BUSY');
       if TPrinterInfo2A(Buffer^).Status and PRINTER_STATUS_PRINTING
=
PRINTER_STATUS_PRINTING then
         ListBox1.items.add('  PRINTER_STATUS_PRINTING');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_OUTPUT_BIN_FULL = PRINTER_STATUS_OUTPUT_BIN_FULL then
         ListBox1.items.add('  PRINTER_STATUS_OUTPUT_BIN_FULL');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_NOT_AVAILABLE
= PRINTER_STATUS_NOT_AVAILABLE then
         ListBox1.items.add('  PRINTER_STATUS_NOT_AVAILABLE');
       if TPrinterInfo2A(Buffer^).Status and PRINTER_STATUS_WAITING
=
PRINTER_STATUS_WAITING then
         ListBox1.items.add('  PRINTER_STATUS_WAITING');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_PROCESSING =
PRINTER_STATUS_PROCESSING then
         ListBox1.items.add('  PRINTER_STATUS_PROCESSING');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_INITIALIZING =
PRINTER_STATUS_INITIALIZING then
         ListBox1.items.add('  PRINTER_STATUS_INITIALIZING');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_WARMING_UP =
PRINTER_STATUS_WARMING_UP then
         ListBox1.items.add('  PRINTER_STATUS_WARMING_UP');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_TONER_LOW =
PRINTER_STATUS_TONER_LOW then
         ListBox1.items.add('  PRINTER_STATUS_TONER_LOW');
       if TPrinterInfo2A(Buffer^).Status and PRINTER_STATUS_NO_TONER
=
PRINTER_STATUS_NO_TONER then
         ListBox1.items.add('  PRINTER_STATUS_NO_TONER');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_PAGE_PUNT =
PRINTER_STATUS_PAGE_PUNT then
         ListBox1.items.add('  PRINTER_STATUS_PAGE_PUNT');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_USER_INTERVENTION = PRINTER_STATUS_USER_INTERVENTION
then
         ListBox1.items.add('  PRINTER_STATUS_USER_INTERVENTION');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_OUT_OF_MEMORY
= PRINTER_STATUS_OUT_OF_MEMORY then
         ListBox1.items.add('  PRINTER_STATUS_OUT_OF_MEMORY');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_DOOR_OPEN =
PRINTER_STATUS_DOOR_OPEN then
         ListBox1.items.add('  PRINTER_STATUS_DOOR_OPEN');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_SERVER_UNKNOWN
= PRINTER_STATUS_SERVER_UNKNOWN then
         ListBox1.items.add('  PRINTER_STATUS_SERVER_UNKNOWN');
       if TPrinterInfo2A(Buffer^).Status and
PRINTER_STATUS_POWER_SAVE =
PRINTER_STATUS_POWER_SAVE then
         ListBox1.items.add('  PRINTER_STATUS_POWER_SAVE');

ListBox1.items.add('cJobs='+inttostr(TPrinterInfo2A(Buffer^).cJobs));
Label1.Caption := inttostr(TPrinterInfo2A(Buffer^).cJobs);

ListBox1.items.add('AveragePPM='+inttostr(TPrinterInfo2A(Buffer^).AveragePPM
));
     end;
   FreeMem(Buffer, pcbNeed);

   EnumJobs(hPrinter, 0, NumJobs, 2, nil, 0, pcbNeed, pcReturned);
   GetMem(Buffer, pcbNeed);
   if EnumJobs(hPrinter, 0, NumJobs, 2, Buffer, pcbNeed, pcbNeed,
pcReturned) then
     begin
       ListBox1.items.add('enumjobs ='+inttostr(pcReturned));
       for i := 1 to pcReturned do
         begin
           ListBox1.items.add('----------------------------------');

ListBox1.items.add('JobId='+inttostr(TArrayofJobs(buffer^)[i].JobId));
           if TarrayofJobs(buffer^)[i].pPrinterName <> nil then

ListBox1.items.add('pPrinterName='+TarrayofJobs(buffer^)[i].pPrinterName);
           if TarrayofJobs(buffer^)[i].pMachineName <> nil then

ListBox1.items.add('pMachineName='+TarrayofJobs(buffer^)[i].pMachineName);
           if TarrayofJobs(buffer^)[i].pUserName <> nil then

ListBox1.items.add('pUserName='+TarrayofJobs(buffer^)[i].pUserName);
           if TarrayofJobs(buffer^)[i].pDocument <> nil then

ListBox1.items.add('pDocument='+TarrayofJobs(buffer^)[i].pDocument);
           if TarrayofJobs(buffer^)[i].pDatatype <> nil then

ListBox1.items.add('pDatatype='+TarrayofJobs(buffer^)[i].pDatatype);
           if TarrayofJobs(buffer^)[i].pStatus <> nil then

ListBox1.items.add('pStatus='+TarrayofJobs(buffer^)[i].pStatus);
           if TarrayofJobs(buffer^)[i].pStatus <> nil then

ListBox1.items.add('pStatus='+TarrayofJobs(buffer^)[i].pStatus);

ListBox1.items.add('Size='+Inttostr(TarrayofJobs(buffer^)[i].Size));

ListBox1.items.add('Status='+Inttostr(TarrayofJobs(buffer^)[i].Status));

ListBox1.items.add('Priority='+Inttostr(TarrayofJobs(buffer^)[i].Priority));

ListBox1.items.add('Position='+Inttostr(TarrayofJobs(buffer^)[i].Position));

ListBox1.items.add('TotalPages='+Inttostr(TarrayofJobs(buffer^)[i].TotalPages));

ListBox1.items.add('PagesPrinted='+Inttostr(TarrayofJobs(buffer^)[i].PagesPrinted));
           with TArrayofJobs(buffer^)[i].Submitted do
             begin
               ListBox1.items.add('Submited wYear='+Inttostr(wYear));
               ListBox1.items.add('Submited wMonth='+Inttostr(wMonth));
               ListBox1.items.add('Submited wDayOfWeek='+Inttostr(wDayOfWeek));
               ListBox1.items.add('Submited wDay='+Inttostr(wDay));
               ListBox1.items.add('Submited wHour='+Inttostr(wHour));
               ListBox1.items.add('Submited wMinute='+Inttostr(wMinute));
               ListBox1.items.add('Submited wMinute='+Inttostr(wSecond));
               ListBox1.items.add('Submited wMilliseconds='+Inttostr(wMilliseconds));
             end;

           ListBox1.items.add(Windir +
format('%-0.5d.SPL',[TArrayofJobs(buffer^)[i].JobId]));
         
ListBox1.items.add('=======================================');
           assignfile(inputfile,Windir +
format('%-0.5d.SPL',[TArrayofJobs(buffer^)[i].JobId]));
           reset(inputfile);
           repeat
             readln(inputfile,line);
             ListBox1.items.add(line);
           until eof(inputfile);
           closefile(inputfile);

ListBox1.items.add('=======================================');

           SetJob(hPrinter, TArrayofJobs(buffer^)[i].JobId, 0, nil,
JOB_CONTROL_CANCEL)
         end;

     end;
   FreeMem(Buffer, pcbNeed);

   WinSpool.ClosePrinter(hPrinter);
 end;  



end.
Mohammed NasmanSoftware Developer
CERTIFIED EXPERT

Commented:
Take a look at, some links for the printing

http://homepages.borland.com/efg2lab/Library/Delphi/Printing/

Author

Commented:
What can I say?  Thanks for the code.

John.

Author

Commented:
One thing though - your code very nicely tells me everything that's going on at the click of a button, but it doesn't capture the print event.  I need this info to be captured automatically.  Can you please give me code to complete the question fully.

Thanks,

John.

Commented:
I will try .
Untill then you can replace the click event with a timer.

Author

Commented:
Adding a timer's a bit lame.

I need to capture every single print job - not just the ones that last longer than my timer interval.

John.

Explore More ContentExplore courses, solutions, and other research materials related to this topic.