C++打印池任务获取问题:JOB_INFO_3字段异常及任务信息查询求助
Let’s break down each of your questions and walk through practical, actionable solutions tailored to working with non-existent printer queues:
1. Why isn't nextJobId populated in JOB_INFO_3 when calling EnumJobs?
The nextJobId field in JOB_INFO_3 is built for active print servers or functioning physical printers that maintain a linked chain of jobs in their queue. When you send jobs to a non-existent printer, the Windows spooler stores these jobs locally but skips building the full job chain metadata. It only populates essential fields like JobId to track the job for potential retry, but doesn’t waste resources maintaining a linked list when there’s no active printer to process the sequence.
Also, even if you have multiple jobs, requesting only 1 job (the third parameter set to 1) means the spooler has no reason to populate nextJobId—it’s only used when linking to subsequent jobs in a multi-job request.
2. Why aren't JobId fields populated when requesting 3 jobs with EnumJobs?
This is almost certainly a buffer size mismatch. When you pass sizeof(JOB_INFO_3) as the buffer size, you’re only allocating space for one JOB_INFO_3 structure—but you’re asking for 3 jobs. The EnumJobs function will return FALSE in this case, and pcbNeeded will tell you the total bytes required to store all 3 job entries. If you don’t check the return value and just look at the uninitialized buffer, the JobId fields will appear empty or contain garbage data.
A secondary possibility: jobs in a non-existent printer’s queue are often in an error state, which can cause the spooler to restrict full multi-job enumeration—but the buffer size issue is the far more likely culprit here.
3. How to fix failed GetJobA calls for job details?
Let’s troubleshoot this step by step:
First, check return values and error codes: After calling
GetJobA, runGetLastError()to get specific failure details. Common issues include:ERROR_INSUFFICIENT_BUFFER:sizeof(JOB_INFO_1)is too small.JOB_INFO_1contains variable-length strings (likepDocument) that are stored in the same buffer. You need to first callGetJobAwith a buffer size of0to get the requiredpcbNeededvalue, then allocate a buffer of that size and callGetJobAagain.ERROR_INVALID_JOB: TheJobIdyou’re using is invalid. Double-check that it came from a successfulEnumJobscall (confirmEnumJobsreturnedTRUEandpcReturned> 0).ERROR_PRINTER_NOT_FOUND: Ensure yourhPrinterhandle is valid. Even for non-existent printers,OpenPrinterAshould return a valid handle if you use the correct printer name and have sufficient access rights (e.g.,PRINTER_ACCESS_USE).
Example fix code:
DWORD nbBytes = 0; // First call to get required buffer size GetJobA(hPrinter, jobId, 1, NULL, 0, &nbBytes); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { // Handle error return; } // Allocate buffer for job data JOB_INFO_1* jobInfo = (JOB_INFO_1*)malloc(nbBytes); if (!jobInfo) { // Handle allocation failure return; } // Second call to retrieve actual job details if (GetJobA(hPrinter, jobId, 1, (LPBYTE)jobInfo, nbBytes, &nbBytes)) { // Access jobInfo->pDocument, jobInfo->Status, jobInfo->TotalPages, etc. } else { // Handle failure, check GetLastError() for specifics } free(jobInfo);
4. Can I retrieve all jobs in a non-existent printer's pool? Yes—here's a reliable workflow
Even for non-existent printers, the Windows spooler retains the job queue, so you can enumerate all jobs with the right approach:
Step-by-Step Solution:
Open a valid printer handle:
UseOpenPrinterAwith your printer’s name and appropriate access rights. Even if the printer doesn’t exist, this will open a handle to the local spool queue.HANDLE hPrinter = NULL; if (!OpenPrinterA("YourNonExistentPrinterName", &hPrinter, NULL)) { // Handle error, check GetLastError() return; }Get the required buffer size for enumeration:
CallEnumJobsonce with a buffer size of0to getpcbNeeded—this tells you how much memory you need to store all job entries. UseJOB_INFO_1instead ofJOB_INFO_3; it’s more reliable for invalid queues and includes all core details (JobId, status, document name, pages, etc.).DWORD pcbNeeded = 0, pcReturned = 0; EnumJobs(hPrinter, 0, 1, 1, NULL, 0, &pcbNeeded, &pcReturned); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { // Handle error ClosePrinter(hPrinter); return; }Allocate buffer and enumerate all jobs:
Allocate the buffer usingpcbNeeded, then callEnumJobsagain to retrieve all jobs. Request a large enough job count (e.g.,1000) to cover all possible entries in the queue.JOB_INFO_1* jobList = (JOB_INFO_1*)malloc(pcbNeeded); if (!jobList) { ClosePrinter(hPrinter); return; } if (EnumJobs(hPrinter, 0, 1000, 1, (LPBYTE)jobList, pcbNeeded, &pcbNeeded, &pcReturned)) { // Iterate over all returned jobs for (DWORD i = 0; i < pcReturned; i++) { DWORD jobId = jobList[i].JobId; LPCSTR docName = jobList[i].pDocument; DWORD status = jobList[i].Status; // Use GetJobA as shown earlier to fetch more details if needed } } else { // Handle enumeration failure }Clean up resources:
Free the allocated buffer and close the printer handle when you’re done.free(jobList); ClosePrinter(hPrinter);
Key tips:
- Stick to
JOB_INFO_1orJOB_INFO_2for non-existent printers—JOB_INFO_3’s linked list functionality doesn’t work well with invalid queues. - Always validate return values and check
GetLastError()for every spooler API call—this is critical for debugging issues with unresponsive or non-existent printers.
内容的提问来源于stack exchange,提问作者user2274060

