Печать спулера apis в delphi

Я пытаюсь составить список заданий на принтере. Я пытаюсь вызвать enumjobs в первый раз, чтобы получить размер буфера, который мне нужно передать в качестве параметра во втором вызове (как рекомендуется в документации Microsoft), но при вызове enumjobs я получаю дескриптор недопустимой ошибки с enumjobs api.

Что за... я делаю не так?!

procedure showLastError();
var
pErrorText:pchar;
lastError:integer;
begin
   lastError := GetLastError();
   pErrorText := nil;

   if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_ALLOCATE_BUFFER
                 ,nil,lastError,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),@pErrorText,0,nil)<> 0) then
   begin
    showmessage(pErrorText);
   end;
end;

procedure TForm3.Button1Click(Sender: TObject);
var
handlePrinter:NativeUInt;
pJob:pointer;
sizePJob:integer;
pcbNeeded:cardinal;
pcReturned:cardinal;
cByteNeeded,cByteUsed:cardinal;

i:integer;
pJobInfo: PJobInfo1;
temp:integer;
ret:boolean;
pPrinterInfo:PPrinterInfo2W;
PPRINTER_INFO_1 : PRINTER_INFO_1;

begin
 handlePrinter := 0;
 if not OpenPrinter(nil,handlePrinter,nil) then
 begin
  showLastError();
  exit;
 end;
 if not GetPrinter(handlePrinter,3,nil,0,@cByteNeeded)then
 begin
  if (GetLastError() <> ERROR_INSUFFICIENT_BUFFER) then
  begin
   showLastError();
   exit;
  end;
 end;
 pPrinterInfo := allocMem(  cByteNeeded);
 if not GetPrinter(handlePrinter,3,pPrinterInfo,cByteNeeded,@cByteUsed)then
 begin
  if (GetLastError() <> ERROR_INSUFFICIENT_BUFFER) then
  begin
   showLastError();
   FreeMem(pPrinterInfo);
   exit;
  end;
 end;

 ret := EnumJobs(handlePrinter,0,pPrinterInfo.cJobs,2,nil,0,&pcbNeeded,&pcReturned);
 if (not ret)then
 begin
  if (GetLastError() <> ERROR_INSUFFICIENT_BUFFER) then
  begin
   showLastError();
   FreeMem(pPrinterInfo);
   exit;
  end;
 end;
 FreeMem(pPrinterInfo);
end;

2 ответа

Что за... я делаю не так?!

По сути, вы получаете неправильный дескриптор из подсистемы Spooler, и, следовательно, ERROR_INVALID_HANDLE ошибка. OpenPrinter должен открыть множество дескрипторов для объектов subsys Spooler. В частности, ваше заявление:

if not OpenPrinter(nil,handlePrinter,nil) then

открывает дескриптор для локального сервера печати, который не поддерживает ни перечисления очереди заданий, ни подробной информации о принтере (уровень 2), но, очевидно, поддерживает информацию о безопасности (уровень 3). Вы должны указать точное имя принтера, чтобы получить правильный дескриптор для принтера, например:

if not OpenPrinter('Xerox ColorQube 9301 PS', handlePrinter, nil) then

Также обратите внимание на комментарии Сертака Акьюза. Еще один, проверьте Win32Check а также SysErrorMessage служебные функции, они очень удобны при работе с WinAPI.

Другой альтернативой является использование WMI. Вы можете попробовать с классом Win32_PrintJob (выберите * из Win32_PrintJob).

Протестируйте с помощью такого кода (созданного с помощью "WMI Delphi code creator" от Rodrigo Ruz)

//-----------------------------------------------------------------------------------------------------
//     This code was generated by the Wmi Delphi Code Creator (WDCC) Version 1.8.5.0
//     http://code.google.com/p/wmi-delphi-code-creator/
//     Blog http://theroadtodelphi.wordpress.com/wmi-delphi-code-creator/
//     Author Rodrigo Ruz V. (RRUZ) Copyright (C) 2011-2014 
//----------------------------------------------------------------------------------------------------- 
//
//     LIABILITY DISCLAIMER
//     THIS GENERATED CODE IS DISTRIBUTED "AS IS". NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED.
//     YOU USE IT AT YOUR OWN RISK. THE AUTHOR NOT WILL BE LIABLE FOR DATA LOSS,
//     DAMAGES AND LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING OR MISUSING THIS CODE.
//
//----------------------------------------------------------------------------------------------------
program GetWMI_Info;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ActiveX,
  ComObj,
  Variants,
  Dialogs;



// La clase Win32_PrintJob representa un trabajo de impresión generado por una aplicación Win32. Las unidades de trabajo generadas por el comando Imprimir de una aplicación que se ejecuta en un sistema Win32 son descendientes (o miembros) de esta clase.
// Ejemplo: un documento de impresora creado por una aplicación de Office 97

procedure  GetWin32_PrintJobInfo;
const
  WbemUser            ='';
  WbemPassword        ='';
  WbemComputer        ='localhost';
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
  str:String;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2', WbemUser, WbemPassword);
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_PrintJob','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  Str := '';
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Str := Str + sLineBreak + Format('Caption           %s',[String(FWbemObject.Caption)]);// String
    Str := Str + sLineBreak + Format('DataType          %s',[String(FWbemObject.DataType)]);// String
    Str := Str + sLineBreak + Format('Description       %s',[String(FWbemObject.Description)]);// String
    Str := Str + sLineBreak + Format('Document          %s',[String(FWbemObject.Document)]);// String
    Str := Str + sLineBreak + Format('DriverName        %s',[String(FWbemObject.DriverName)]);// String
    Str := Str + sLineBreak + Format('ElapsedTime       %s',[String(FWbemObject.ElapsedTime)]);// Datetime
    Str := Str + sLineBreak + Format('HostPrintQueue    %s',[String(FWbemObject.HostPrintQueue)]);// String
    Str := Str + sLineBreak + Format('JobId             %d',[Integer(FWbemObject.JobId)]);// Uint32
    Str := Str + sLineBreak + Format('JobStatus         %s',[String(FWbemObject.JobStatus)]);// String
    Str := Str + sLineBreak + Format('Name              %s',[String(FWbemObject.Name)]);// String
    Str := Str + sLineBreak + Format('Notify            %s',[String(FWbemObject.Notify)]);// String
    Str := Str + sLineBreak + Format('Owner             %s',[String(FWbemObject.Owner)]);// String
    Str := Str + sLineBreak + Format('PagesPrinted      %d',[Integer(FWbemObject.PagesPrinted)]);// Uint32
    Str := Str + sLineBreak + Format('PrintProcessor    %s',[String(FWbemObject.PrintProcessor)]);// String
    Str := Str + sLineBreak + Format('Priority          %d',[Integer(FWbemObject.Priority)]);// Uint32
    Str := Str + sLineBreak + Format('Size              %d',[Integer(FWbemObject.Size)]);// Uint32
    Str := Str + sLineBreak + Format('Status            %s',[String(FWbemObject.Status)]);// String
    Str := Str + sLineBreak + Format('StatusMask        %d',[Integer(FWbemObject.StatusMask)]);// Uint32
    Str := Str + sLineBreak + Format('TimeSubmitted     %s',[String(FWbemObject.TimeSubmitted)]);// Datetime
    Str := Str + sLineBreak + Format('TotalPages        %d',[Integer(FWbemObject.TotalPages)]);// Uint32
    Str := Str + sLineBreak + '--------------------------------------------------------';

    MessageDlg(Str, mtInformation, [mbOK], 0);

    FWbemObject:=Unassigned;
  end;
end;


begin
 try
    CoInitialize(nil);
    try
      GetWin32_PrintJobInfo;
    finally
      CoUninitialize;
    end;
 except
    on E:EOleException do
        Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode])); 
    on E:Exception do
        Writeln(E.Classname, ':', E.Message);
 end;
 Writeln('Press Enter to exit');
 Readln;      
end.

Если вы отправляете файл на принтер и выполняете проект (это скомпилировано с Delphi 6), вы можете получить результат, подобный следующему:

С уважением.

Другие вопросы по тегам