Enumerating jobs in print queue

Enumerating jobs in a print queue can be done with

$SAMPLECODE$
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
* WMI
LOCAL lcPrinterName, lcComputer, loWMIService, loErr

lcPrinterName = GETPRINTER()
loLocator = CREATEOBJECT('WBEMScripting.SWBEMLocator')
loWMI = loLocator.ConnectServer()   && local machine logged on user
loWMI.Security_.ImpersonationLevel = 3   && Impersonate

lcPrinterName = STRTRAN(lcPrinterName , "\", "\\")
loPrintJobs =  loWMI.ExecQuery("Select * from Win32_PrintJob " + ;
				"  WHERE Name LIKE '" + lcPrinterName + "%'")

llOK = .T.
TRY
	FOR EACH loPrintJob in loPrintJobs
		? "---------------------------------------------"
		? "Caption", loPrintjob.Caption
		? "DataType", loPrintjob.DataType
		? "Description", loPrintjob.Description
		? "Document", loPrintjob.Document
		? "DriverName", loPrintjob.DriverName
		? "ElapsedTime", loPrintjob.ElapsedTime
		? "HostPrintQueue", loPrintjob.HostPrintQueue
		? "InstallDate", loPrintjob.InstallDate
		? "JobId", loPrintjob.JobId
		? "JobStatus", loPrintjob.JobStatus
		? "Name", loPrintjob.Name
		? "Notify", loPrintjob.Notify
		? "Owner", loPrintjob.Owner
		? "PagesPrinted", loPrintjob.PagesPrinted
		? "Parameters", loPrintjob.Parameters
		? "PrintProcessor", loPrintjob.PrintProcessor
		? "Priority", loPrintjob.Priority
		? "Size", loPrintjob.Size
		? "StartTime", loPrintjob.StartTime
		? "Status", loPrintjob.Status
		? "StatusMask", loPrintjob.StatusMask
		? "TimeSubmitted", loPrintjob.TimeSubmitted
		? "TotalPages", loPrintjob.TotalPages
		? "UntilTime", loPrintjob.UntilTime
	ENDFOR
CATCH TO loErr
	? loErr.Errorno, loErr.Message
	llOK = .F.
ENDTRY



It's quite simple to enumerate jobs in print queue using Windows API, but it is complicated to retrieve information about jobs because it's returned as C/C++ structure and VFP doesn't have built-in capabilities to work with structures. The Windows API support class is used to handle Windows API structures. The code requires VFP8 or later to run because it uses collection class to store Job info but it can be easily adapted to work under previous versions of VFP. It retrieves all info about a job stored in JOB_INFO_1 structure but date and time when job was submitted. The WinApiErrMsg function in the code below is from Retrieving Windows system error message.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
* Windows API
ooo = NEWOBJECT("EnumJobs", "EnumJobsClass.fxp")
* Tretreive Jobs for default VFP printer
IF NOT ooo.GetJobList()
	? ooo.cErrorMessage
	? ooo.cApiErrorMessage
	* Error
ENDIF

FOR i=1 TO ooo.oJobList.Count
	loOneJob = ooo.oJobList.Item(i)
	? loOneJob.JobId, loOneJob.Document
ENDFOR

ooo = Null
RETURN
*--------------------------------------------------------------------

*** EnumJobs.prg ***

DEFINE CLASS EnumJobs AS Custom
	HIDDEN hHeap
	cPrinterName = ""
	cApiErrorMessage = ""
	cErrorMessage = ""
	hHeap = 0
	oJobList = NULL
	oWas = NULL

	PROCEDURE Init()
	This.oJobList = CREATEOBJECT("Collection")
	This.oWas = NEWOBJECT("WinApiSupport", "WinApiSupport.fxp")

	This.LoadApiDlls()
	This.hHeap = HeapCreate(0, 4096, 0)
	* Use Windows default printer
	This.cPrinterName = SET("Printer",2)
	ENDPROC

	PROCEDURE Destroy
	IF This.hHeap <> 0
		HeapDestroy(This.hHeap)
	ENDIF
	ENDPROC

	PROCEDURE GetJobList(tcPrinterName)
	LOCAL lhPrinter, llSuccess, lnNeeded, lnNumberOfJobs, lnBuffer, i, loOneJob

	IF PCOUNT() > 0
		This.cPrinterName = tcPrinterName
	ENDIF

	This.ClearErrors()
	lhPrinter = 0
	IF OpenPrinter(This.cPrinterName, @lhPrinter, 0) = 0
		This.cErrorMessage = "Unable to get printer handle for '" + This.cPrinterName + "."
		This.cApiErrorMessage = WinApiErrMsg(GetLastError())
		RETURN .F.
	ENDIF

	lnNeeded = 0
	lnNumberOfJobs = 0

	* Get the size of the buffer in lnNeeded
	IF EnumJobs(lhPrinter, 0, 127, 1, 0, 0, @lnNeeded, 	@lnNumberOfJobs  ) = 0
		IF GetLastError() <> 122   && The buffer too small error
			This.cErrorMessage = "Unable to Enumerate Jobs."
			This.cApiErrorMessage = WinApiErrMsg(GetLastError())
			RETURN .F.
		ENDIF
	ENDIF
	* Allocate the buffer of required size and call EnumJobs again
	lnBuffer = HeapAlloc(This.hHeap, 0, lnNeeded)
	llSuccess = .T.
	IF EnumJobs(lhPrinter, 0, 127, 1, lnBuffer, @lnNeeded, @lnNeeded, @lnNumberOfJobs) = 0
		This.cErrorMessage = "Unable to Enumerate Jobs."
		This.cApiErrorMessage = WinApiErrMsg(GetLastError())
		llSuccess = .F.
	ENDIF

	IF llSuccess
		FOR i=1 TO lnNumberOfJobs
			loOneJob = This.OneJobObj()
			WITH loOneJob
				lnPointer = lnBuffer + (i-1) * 64
				.JobId = This.oWas.Long2NumFromBuffer(lnPointer)
				.PrinterName = This.oWas.StrZFromBuffer(lnPointer+4)
				.MachineName = This.oWas.StrZFromBuffer(lnPointer+8)
				.UserName = This.oWas.StrZFromBuffer(lnPointer+12)
				.Document = This.oWas.StrZFromBuffer(lnPointer+16)
				.Datatype = This.oWas.StrZFromBuffer(lnPointer+20)
				.StatusText = This.oWas.StrZFromBuffer(lnPointer+24)
				.Status = This.oWas.Long2NumFromBuffer(lnPointer+28)
				.Priority = This.oWas.Long2NumFromBuffer(lnPointer+32)
				.Position = This.oWas.Long2NumFromBuffer(lnPointer+36)
				.TotalPages = This.oWas.Long2NumFromBuffer(lnPointer+40)
				.PagesPrinted = This.oWas.Long2NumFromBuffer(lnPointer+44)
			ENDWITH
			This.oJobList.Add(loOneJob, TRANSFORM(loOneJob.JobId))
		ENDFOR
	ENDIF

	= HeapFree(This.hHeap, 0, lnBuffer )
	= ClosePrinter(lhPrinter)
	RETURN llSuccess

	PROCEDURE OneJobObj
	LOCAL loOneJob
	loOneJob = NEWOBJECT("Empty")
	ADDPROPERTY(loOneJob, "JobId", 0)
	ADDPROPERTY(loOneJob, "PrinterName", "")
	ADDPROPERTY(loOneJob, "MachineName", "")
	ADDPROPERTY(loOneJob, "UserName", "")
	ADDPROPERTY(loOneJob, "Document", "")
	ADDPROPERTY(loOneJob, "Datatype", "")
	ADDPROPERTY(loOneJob, "StatusText", "")
	ADDPROPERTY(loOneJob, "Status", 0)
	ADDPROPERTY(loOneJob, "Priority", 0)
	ADDPROPERTY(loOneJob, "Position", 0)
	ADDPROPERTY(loOneJob, "TotalPages", 0)
	ADDPROPERTY(loOneJob, "PagesPrinted", 0)
	*  SYSTEMTIME Submitted
	RETURN loOneJob
	ENDPROC

	PROCEDURE ClearErrors
	This.cErrorMessage = ""
	This.cApiErrorMessage = ""
	ENDPROC

	HIDDEN PROCEDURE LoadApiDlls
		DECLARE Long HeapCreate IN Win32API;
			Long dwOptions, Long dwInitialSize, Long dwMaxSize
		DECLARE Long HeapAlloc IN Win32API;
			Long hHeap, Long dwFlags, Long dwBytes
		DECLARE lstrcpy IN Win32API;
			String @lpString1, Long lpString2
		DECLARE Long HeapFree IN Win32API;
			Long hHeap, Long dwFlags, Long lpMem
		DECLARE HeapDestroy IN WIN32API Long hHeap
		DECLARE Long GetLastError IN kernel32

	ENDPROC

ENDDEFINE

*----------------------------------------------------------------------------------------------

FUNCTION OpenPrinter(tcPrinterName, thPrinter, tcDefault)
DECLARE Long OpenPrinter IN WinSpool.Drv ;
	String pPrinterName, Long@ phPrinter, String pDefault
RETURN 	OpenPrinter(tcPrinterName, @thPrinter, tcDefault)

FUNCTION ClosePrinter (thPrinter)
DECLARE Long ClosePrinter IN WinSpool.Drv Long hPrinter
RETURN ClosePrinter(thPrinter)

FUNCTION EnumJobs(thPrinter, tnFirstJob, tnNoJobs, tnLevel, ;
	tnJob, tnBuf, tnNeeded, tnReturned)

DECLARE Long EnumJobs IN WinSpool.Drv ;
	Long hPrinter, Long FirstJob, Long NoJobs, Long Level, ;
	Long pJob, Long cbBuf, Long @pcbNeeded, Long @pcReturned
RETURN EnumJobs(thPrinter, tnFirstJob, tnNoJobs, tnLevel, ;
	tnJob, tnBuf, @tnNeeded, @tnReturned)


Comments