Enumerating jobs in print queue

Enumerating jobs in print queue can be done with

This is sample code. Add error handling and adjust to your requirements as necessary.

&& 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.

&& 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)