Opening printer queus, managing printer queues

hi!

I am looking for a way to open my printer queue from delphi. same as opening the printer from explorer...


also looking for a way to manage printerqueu from my own application... i.e. see the queue.. and delete from a specified printer
LVL 2
joepeztAsked:
Who is Participating?
 
twinsoftConnect With a Mentor Commented:
Hi, this a component that i use. It has all the info that you need in order to access the printer jobs, delete one, etc.

Have fun...


unit XSPrinterStatus;

interface

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

const
  imaxPrintJobs = 10000;

type
  EPrintError = class(Exception);

  TPrinterState = (psPAUSED, psERROR, psDELETING, psSPOOLING,
                   psPRINTING, psOFFLINE, psPAPEROUT, psPRINTED, psDELETED,
                   psBLOCKED_DEVQ, psUSER_INTERVENTION, psRESTART, psUNKNOWN, psOK);

  TPrinterStates = set of TPrinterState;

  TArrayJobInfo2 = array[0..iMaxPrintJobs] of TJobInfo2;

  TXSPrinterStatus = class(TComponent)
  private
    FDefaultPrinter: boolean;
    FSpoolerErrorID: integer;
    FSpoolerErrorStr: string;
    FErrorStart: integer;
    FstrPrinterName: string;
   	FhPrinter: THAndle;
	   FpPrinterInfo2: ^TPrinterInfo2;
	   FpJobStorage: ^TArrayJobInfo2;
    FNrOfJobs: DWORD;
    FErrorStates: TPrinterStates;

    function StorePrinterName: boolean;
    function GetSpoolerReady: boolean;
    function GetJobs: Boolean;
    function GetJobStatus(Index: integer): TPrinterStates;
    function GetStatusDesc(Index: integer): string;
    function QueueStatus: DWORD;
    procedure SetPrinterName(Value: string);
    function GetJobCount: integer;
    procedure SetDefaultPrinter(Value: boolean);
    function GetJobInfo(Index: integer): TJobInfo2;
  protected
  public
    property JobCount: integer read GetJobCount;
    property JobStatus[Index: Integer]: TPrinterStates read GetJobStatus;
    property StatusDesc[Index: Integer]: string read GetStatusDesc;
    property SpoolerReady: boolean read GetSpoolerReady;
    property SpoolerErrorStr: string read FSpoolerErrorStr;
    property SpoolerErrorID: integer read FSpoolerErrorID;
    property JobInfo[Index: Integer]: TJobInfo2 read GetJobInfo;
    property PrinterHandle: THandle read FhPrinter;

    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure CancelPrintJob(aJobID: Integer);
    function FirstJobInError: Integer;
    function NextJobInError: Integer;
    function FindJob(Title: string): Integer;

  published
    property PrinterName: string read FstrPrinterName write SetPrinterName stored StorePrinterName;
    property ErrorStates: TPrinterStates read FErrorStates write FErrorStates default [psERROR, psOFFLINE];
    property DefaultPrinter: boolean read FDefaultPrinter write SetDefaultPrinter default true;
  end;

procedure Register;

implementation

procedure Register;
begin
 RegisterComponents('System', [TXSPrinterStatus]);
end;

constructor TXSPrinterStatus.Create(AOwner: TComponent);
begin
 inherited Create(AOwner);

 FSpoolerErrorStr := '';
 FpPrinterInfo2 := nil;
 FpJobStorage := nil;
 FErrorStates := [psERROR, psOFFLINE];

 FDefaultPrinter := true;

 SetPrinterName('');
end;

destructor TXSPrinterStatus.Destroy;
begin
 ClosePrinter(FhPrinter);
 if FpPrinterInfo2 <> nil then
  dispose(FpPrinterInfo2);

 if FpJobStorage <> nil then
		dispose(FpJobStorage);

 inherited Destroy;
end;

function TXSPrinterStatus.GetJobCount: integer;
begin
 Result := -1;

 if QueueStatus = 0 then
  Result := FNrOfJobs;
end;

function TXSPrinterStatus.QueueStatus: DWORD;
var
 cbBuf: DWORD;
 pcbBuf: pointer;
 Needed: DWORD;
 pcbNeeded: LPDWORD;
begin
 pcbBuf := @cbBuf;

 if not GetPrinter(FhPrinter, 2, Nil, 0, pcbBuf) then
  if GetLastError <> ERROR_INSUFFICIENT_BUFFER then
   begin
    FSpoolerErrorStr := Name + ': GetPrinterQueueStatus Error: ' + IntToStr(GetLastError);
    FSpoolerErrorID := GetLastError;

    raise EPrintError.Create(FSpoolerErrorStr);
   end;

 if FpPrinterInfo2 <> nil then
  dispose(FpPrinterInfo2);

 GetMem(FpPrinterInfo2,cbBuf);
 fillChar(FpPrinterInfo2^, cbBuf, 0);

 pcbNeeded := @Needed;

 if not GetPrinter(FhPrinter, 2, FpPrinterInfo2, cbBuf, pcbNeeded) then
  begin
	 	FSpoolerErrorStr := Name + ': GetPrinterQueueStatus Error: ' + IntToStr(GetLastError);
 		FSpoolerErrorID := GetLastError;

	 	raise EPrintError.Create(FSpoolerErrorStr);
  end;

 Result := FpPrinterInfo2^.status;
 FNrOfJobs := FpPrinterInfo2^.cJobs;
end;

procedure TXSPrinterStatus.SetPrinterName(Value: string);
begin
 if (Value <> FstrPrinterName) or (Value = '') then
  begin
   FErrorStart := -1;
   if FstrPrinterName <> '' then
    ClosePrinter(FhPrinter);
   if Trim(Value) = '' then
    with Printer do
     begin
      FDefaultPrinter := true;
      Value := Printers[PrinterIndex];
     end
    else
     FDefaultPrinter := false;

   if not Openprinter(PChar(Value), FhPrinter, nil) then
    begin
   		FSpoolerErrorStr := 'OpenPrinter Error: ' + IntToStr(GetLastError);
   		FSpoolerErrorID := GetLastError;

   		raise EPrintError.Create(FSpoolerErrorStr);
    end;

   FstrPrinterName := Value;
  end;
end;

function TXSPrinterStatus.StorePrinterName: boolean;
begin
 Result := not FDefaultPrinter;
end;

function TXSPrinterStatus.GetJobStatus(Index: integer): TPrinterStates;
var
 Status: DWORD;
begin
 result := [];

 if (Index >= JobCount) or (Index < 0) then
  Exit;

 if not GetJobs then
  Exit;

 Status := FpJobStorage^[Index].status;

 if Status = 0 then
  Result := [psOK]
 else
  begin
   if (JOB_STATUS_PAUSED and Status) = JOB_STATUS_PAUSED then
    Result := Result + [psPAUSED];
   if (JOB_STATUS_ERROR and Status) = JOB_STATUS_ERROR then
    Result := Result + [psERROR];
   if (JOB_STATUS_DELETING and Status) = JOB_STATUS_DELETING then
    Result := Result + [psDELETING];
   if (JOB_STATUS_SPOOLING and Status) = JOB_STATUS_SPOOLING then
    Result := Result + [psSPOOLING];
   if (JOB_STATUS_PRINTING and Status) = JOB_STATUS_PRINTING then
    Result := Result + [psPRINTING];
   if (JOB_STATUS_OFFLINE and Status) = JOB_STATUS_OFFLINE then
    Result := Result + [psOFFLINE];
   if (JOB_STATUS_PAPEROUT and Status) = JOB_STATUS_PAPEROUT then
    Result := Result + [psPAPEROUT];
   if (JOB_STATUS_PRINTED and Status) = JOB_STATUS_PRINTED then
    Result := Result + [psPRINTED];
   if (JOB_STATUS_DELETED and Status) = JOB_STATUS_DELETED then
    Result := Result + [psDELETED];
   if (JOB_STATUS_BLOCKED_DEVQ and Status) = JOB_STATUS_BLOCKED_DEVQ then
    Result := Result + [psBLOCKED_DEVQ];
   if (JOB_STATUS_USER_INTERVENTION and Status) = JOB_STATUS_USER_INTERVENTION then
    Result := Result + [psUSER_INTERVENTION];
   if Result = [] then
    Result := [psUNKNOWN];
  end;
end;

function TXSPrinterStatus.GetJobs: Boolean;
var
 cbBuf: DWORD;
 Needed: DWORD;
begin
 GetJobs := false;

 if JobCount >= 0 then
  begin
   if not EnumJobs(FhPrinter, 0, FpPrinterInfo2^.cJobs, 2, FpJobStorage, 0, cbBuf, Needed) then
    if GetLastError <> ERROR_INSUFFICIENT_BUFFER then
     begin
    		FSpoolerErrorStr := 'Get Jobs Error: ' + IntToStr(GetLastError);
    		FSpoolerErrorID := GetLastError;

    		raise EPrintError.Create(FSpoolerErrorStr);
     end;

   if FpJobStorage <> nil then
    dispose(FpJobStorage);

   GetMem(FpJobStorage,cbBuf);
   fillChar(FpJobStorage^, cbBuf, 0);

   if not EnumJobs(FhPrinter, 0, FpPrinterInfo2^.cJobs, 2, FpJobStorage, cbBuf, Needed, cbBuf) then
    begin
  			FSpoolerErrorStr := 'EnumJobs Error: ' + IntToStr(GetLastError);
  			FSpoolerErrorID := GetLastError;

  			raise EPrintError.Create(FSpoolerErrorStr);
    end;

   GetJobs := true;
  end;
end;

function TXSPrinterStatus.GetStatusDesc(Index: Integer): string;
var
 Status: TPrinterStates;
begin
 Status := GetJobStatus(Index);

 Result := '';

	if (psPAUSED in Status) then Result := Result + ' PAUSED';
	if (psERROR in Status) then Result := Result + ' ERROR';
	if (psDELETING in Status) then Result := Result + ' DELETING';
	if (psSPOOLING in Status) then Result := Result + ' SPOOLING';
	if (psPRINTING in Status) then Result := Result + ' PRINTING';
	if (psOFFLINE in Status) then Result := Result + ' OFFLINE';
	if (psPAPEROUT in Status) then Result := Result + ' PAPEROUT';
	if (psPRINTED in Status) then Result := Result + ' PRINTED';
	if (psDELETED in Status) then Result := Result + ' DELETED';
	if (psBLOCKED_DEVQ in Status) then Result := Result + ' BLOCKED';
	if (psUSER_INTERVENTION in Status) then Result := Result + ' USERINTERVENTION';
end;

function TXSPrinterStatus.FirstJobInError: Integer;
begin
 FErrorStart := 0;
 Result := NextJobInError;
end;

function TXSPrinterStatus.NextJobInError: Integer;
var
 i: Integer;
begin
 if FErrorStart = -1 then
  raise Exception.Create(Name + ': FirstJobInError not executed before NextJobInError');

 Result := -1;
 for i := FErrorStart to JobCount - 1 do
  if (GetJobStatus(i) * ErrorStates) <> [] then
   begin
    Result := i;
    Break;
   end;

 if Result = -1 then
  FErrorStart := JobCount+1
 else
  FErrorStart := Result + 1;
end;

function TXSPrinterStatus.GetSpoolerReady: boolean;
begin
 FSpoolerErrorStr := '';
 try
  Result := (QueueStatus = 0) and (GetJobs);
 except
  on e: EPrintError do
   Result := false;
  on e: Exception do
   raise
 end;
end;

procedure TXSPrinterStatus.SetDefaultPrinter(Value: boolean);
begin
 if Value <> FDefaultPrinter then
  begin
   FDefaultPrinter := Value;
   if FDefaultPrinter then
    SetPrinterName('');
  end;
end;

function TXSPrinterStatus.GetJobInfo(Index: integer): TJobInfo2;
begin
 if (Index < 0) or (Index >= JobCount) then
  Exit;

 Result := FpJobStorage^[Index];
end;

function TXSPrinterStatus.FindJob(Title: string): Integer;
var
 i: Integer;
begin
 Result := -1;
 if GetJobs then
  for i := 0 to JobCount - 1 do
   if (JobInfo[i].pDocument <> '') and (JobInfo[i].pDocument = Title) then
    begin
     Result := i;
     Break;
    end;
end;

procedure TXSPrinterStatus.CancelPrintJob(aJobID: Integer);
var
 aJob: TJobInfo2;
begin
 aJob := GetJobInfo(aJobID);
 SetJob(FhPrinter, aJob.JobId, 0, nil, JOB_CONTROL_DELETE);
end;

end.

Open in new window

0
 
joepeztAuthor Commented:
wow really, this is usefull too, if you can figure out how to also open my printer in explorer I double points :-)
0
 
twinsoftCommented:
What do you mean ?

"how to also open my printer in explorer"
0
 
joepeztAuthor Commented:
in explorer, you can go to printers, there you see all the installed printers.. you double click on it, and can open the queue from there also..

I just want to open this programmically
0
 
twinsoftCommented:
Hi, call this function with the name on the printer...

And please give some more points. As you can see from other threads, a question like this gets at least 500 points....

Uses ShlObj, ActiveX, ShellAPI;

function TForm1.OpenThisPrinter(PrinterName: String): Boolean;
var
 Allocator: IMalloc;
 PrinterItemIDList:  pItemIDList;
 ShellExecuteInfo :  TShellExecuteInfo;
begin
 Result := False;
 if CoGetMalloc(MEMCTX_TASK, Allocator) = S_OK then
  begin
   PrinterItemIDList := GetThisPrinter(PrinterName, Allocator);
   try
    if PrinterItemIDList = nil then
     Result := False
    else
     begin
      ZeroMemory(@ShellExecuteInfo, SizeOf(TShellExecuteInfo));
      with ShellExecuteInfo do
       begin
        cbSize := SizeOf(TShellExecuteInfo);
        fMask := SEE_MASK_INVOKEIDLIST OR SEE_MASK_FLAG_NO_UI;
        lpIDList := PrinterItemIDlist;
        nShow := SW_SHOWDEFAULT;
       end;

      ShellExecuteEx(@ShellExecuteInfo);
      Result := True;
     end;
   finally
    Allocator.Free(PrinterItemIDList)
   end;
  end;
end;
0
All Courses

From novice to tech pro — start learning today.