Anyway, I tried another way... oh, to tell you the truth, many many different ways. At the end this is what I was able to realize.
common.pas
Code: Select all
unit common;
interface
uses Windows;
type
TJobInfoMG = record
IsValid: boolean;
JobId: cardinal;
PrinterName: array[0..MAX_PATH] of Char;
MachineName: array[0..MAX_PATH] of Char;
UserName: array[0..MAX_PATH] of Char;
Document: array[0..MAX_PATH] of Char;
NotifyName: array[0..MAX_PATH] of Char;
Datatype: array[0..MAX_PATH] of Char;
PrintProcessor: array[0..MAX_PATH] of Char;
Parameters: array[0..MAX_PATH] of Char;
DriverName: array[0..MAX_PATH] of Char;
StatusDescr: array[0..MAX_PATH] of Char;
Status: DWORD;
Priority: DWORD;
Position: DWORD;
StartTime: DWORD;
UntilTime: DWORD;
TotalPages: DWORD;
Size: DWORD;
Submitted: TSystemTime; { Time the job was spooled }
Time: DWORD; { How many seconds the job has been printing }
PagesPrinted: DWORD;
devFields: DWORD;
devCopies: SHORT;
devColor: SHORT;
devDuplex: SHORT;
devCollate: SHORT;
Collate: boolean;
Color: boolean;
Duplex: boolean;
Copies: integer;
end;
TPrintNotification = record
Process: array[0..MAX_PATH] of Char;
Api: array[0..MAX_PATH] of Char;
Params: array[0..MAX_PATH] of Char;
jobinfo: TJobInfoMG;
end;
implementation
end.Code: Select all
library hookPrint;
//{$DEFINE EXTLOG}
uses
Windows,
SysUtils,
WinSpool,
madCodeHook,
madStrings,
madRemote,
madTools,
common in '..\common.pas';
const
DLL_SpoolSs = 'spoolss.dll';
type
TPrinters = array[0..1023] of PRINTER_INFO_1;
PPrinters = ^TPrinters;
TJobs = array[0..1023] of JOB_INFO_2;
PJobs = ^TJobs;
var
prnt: TPrintNotification;
nextSetJob: function(hPrinter: THandle; JobId,Level: DWORD; pJob: Pointer; Command: DWORD): boolean; stdcall;
// ***************************************************************
procedure InitVars;
begin
prnt.jobinfo.IsValid := false;
end;
procedure NotifyApplication(api: string; params: string);
var
arrChW : array [0..MAX_PATH] of WideChar;
session: dword;
begin
GetModuleFileNameW(0, arrChW, MAX_PATH);
{$IFDEF UNICODE}
lstrcpy(prnt.Process, arrChW);
{$ELSE}
WideToAnsi(arrChW, prnt.Process);
{$ENDIF}
lstrcpy(prnt.Api, PChar(api));
lstrcpy(prnt.Params, PChar(params));
if AmSystemProcess and (GetCurrentSessionId = 0) then session := GetInputSessionId
else session := GetCurrentSessionId;
SendIpcMessage(PAnsiChar('PrintMonitor' + IntToStrEx(session)), @prnt, sizeOf(prnt));
end;
procedure Error(title, msg: string; const icon: cardinal = 0);
begin
{$IFDEF EXTLOG}
MessageBox(
GetDesktopWindow,
PChar(msg),
PChar(title),
MB_OK+icon);
{$ELSE}
NotifyApplication('ERROR '+title,msg);
{$ENDIF}
end;
// ***************************************************************
function JobToJobInfoMG(job: JOB_INFO_2): TJobInfoMG;
begin
try
with Result do begin
JobId := job.JobId;
lstrcpy(PrinterName,job.pPrinterName);
lstrcpy(MachineName,job.pMachineName);
lstrcpy(UserName,job.pUserName);
lstrcpy(Document,job.pDocument);
lstrcpy(NotifyName,job.pNotifyName);
lstrcpy(Datatype,job.pDatatype);
lstrcpy(PrintProcessor,job.pPrintProcessor);
lstrcpy(Parameters,job.pParameters);
lstrcpy(DriverName,job.pDriverName);
lstrcpy(StatusDescr,job.pStatus);
devFields := job.pDevMode^.dmFields;
devCopies := job.pDevMode^.dmCopies;
devColor := job.pDevMode^.dmColor;
devDuplex := job.pDevMode^.dmDuplex;
devCollate := job.pDevMode^.dmCollate;
Status := job.Status;
Priority := job.Priority;
Position := job.Position;
StartTime := job.StartTime;
UntilTime := job.UntilTime;
TotalPages := job.TotalPages;
Size := job.Size;
Submitted := job.Submitted;
Time := job.Time;
PagesPrinted := job.PagesPrinted;
Copies := devCopies;
if ((devFields and DM_COLOR) = DM_COLOR) then
Color := (devColor = DMCOLOR_COLOR);
if ((devFields and DM_DUPLEX) = DM_DUPLEX) then
Duplex := (devDuplex <> DMDUP_HORIZONTAL );
if ((devFields and DM_COLLATE) = DM_COLLATE) then
Collate := (devCollate = DMCOLLATE_TRUE);
IsValid := true;
end;
except
on E: Exception do Error('JobToJobInfoMG',E.Message,MB_ICONERROR);
end;
end;
procedure ShowJob(sender: string; job: TJobInfoMG); overload;
{$IFDEF EXTLOG}var s: string;{$ENDIF}
begin
{$IFDEF EXTLOG}
with job do begin
if (IsValid = false) then exit;
try
s := s+format('Printer name: %s'#13#10,[PrinterName]);
s := s+format('Machine name: %s'#13#10,[MachineName]);
s := s+format('User name: %s'#13#10,[UserName]);
s := s+format('Document: %s'#13#10,[Document]);
s := s+format('NotifyName: %s'#13#10,[NotifyName]);
s := s+format('DataType: %s'#13#10,[DataType]);
s := s+format('PrintProcessor: %s'#13#10,[PrintProcessor]);
s := s+format('Parameters: %s'#13#10,[Parameters]);
s := s+format('DriverName: %s'#13#10,[DriverName]);
s := s+format('StatusDescr: %s'#13#10,[StatusDescr]);
s := s+format('Status: $%8.8x'#13#10,[Status]);
s := s+format('Priority: %d'#13#10,[Priority]);
s := s+format('Position: %d'#13#10,[Position]);
s := s+format('StartTime: %d'#13#10,[StartTime]);
s := s+format('UntilTime: %d'#13#10,[UntilTime]);
s := s+format('TotalPages: %d'#13#10,[TotalPages]);
s := s+format('Size: %d'#13#10,[Size]);
s := s+format('Submitted: %d:%d:%d.%d'#13#10,
[Submitted.wHour,Submitted.wMinute,Submitted.wSecond,Submitted.wMilliseconds]);
s := s+format('Time: %d'#13#10,[Time]);
s := s+format('PagesPrinted: %d'#13#10,[PagesPrinted]);
Error('ShowJob 02 from '+sender,s,MB_ICONINFORMATION);
except
on E: Exception do begin
Error('ShowJob 02 from '+sender,E.Message,MB_ICONERROR);
exit;
end;
end;
end;
{$ENDIF}
end;
procedure ShowJob(sender: string; job: JOB_INFO_2); overload;
begin
ShowJob(sender,JobToJobInfoMG(job));
end;
function GetJob(id: cardinal): TJobInfoMG;
const TITLE = 'GetJob';
var
a,pcount: integer;
dwPrinters,dwNeed,dwJobs: cardinal;
printers: PPrinters;
jobs: PJobs;
job: JOB_INFO_2;
pd: TPrinterDefaults;
hPrinter: THandle;
begin
Result.IsValid := false;
with pd do begin
DesiredAccess := PRINTER_ACCESS_USE;
pDatatype := nil;
pDevMode := nil;
end;
EnumPrinters(PRINTER_ENUM_LOCAL,nil,1,nil,0,dwNeed,dwPrinters);
printers := AllocMem(dwNeed);
EnumPrinters(PRINTER_ENUM_LOCAL,nil,1,printers,dwNeed,dwNeed,dwPrinters);
for pcount := 0 to dwPrinters - 1 do begin
if OpenPrinter(printers[pcount].pName,hPrinter,@pd) then begin
dwNeed := 0; dwJobs := 0;
EnumJobs(hPrinter,0,sizeof(TJobs),2,nil,0,dwNeed,dwJobs);
jobs := AllocMem(dwNeed);
EnumJobs(hPrinter,0,sizeof(TJobs),2,jobs,dwNeed,dwNeed,dwJobs);
for a := 0 to dwJobs - 1 do begin
job := jobs[a];
if job.JobId = id then begin
Result := JobToJobInfoMG(job);
prnt.jobinfo := Result;
end;
end;
ClosePrinter(hPrinter);
end else begin
{$IFDEF EXTLOG}
Error(TITLE,format('Can''t EnumJobs for printer %s',[printers[pcount].pName]));
{$ENDIF}
end;
end;
end;
// ***************************************************************
function hookSetJob(hPrinter: THandle; JobId,Level: DWORD; pJob: Pointer; Command: DWORD): boolean; stdcall;
var job: TJobInfoMG;
begin
if Command = JOB_CONTROL_CANCEL then begin
job := GetJob(JobId);
{$IFNDEF EXTLOG}
ShowJob('SetJob',job);
{$ENDIF}
NotifyApplication('SetJob', 'JOB_CONTROL_CANCEL');
end else
NotifyApplication('SetJob', format('JobId: $%2.2x - Cmd: %x',[JobId,Command]));
Result := nextSetJob(hPrinter,JobId,Level,pJob,Command);
end;
begin
InitVars;
CollectHooks;
HookAPI(DLL_SpoolSs, 'SetJobW', @hookSetJob, @nextSetJob);
FlushHooks;
end.Well, that's all...
I repeat that this is only a test, not final release.
So it MUST be tested on servers and, finally, put in a service.
Can someone help me to complete these tasks?
Thanks a lot.