Solved Report Sent to Printer?

Would this be of any use?
Indeed it would. It includes the comparatively easy part of fetching print job info from the print queue. I added something similar as a fallback because the print spooler events API function behave somewhat unpredictably in regards to the data the can be retrieved with their events.

This is always going to come back to this question: "What - and where - is the measurable property?" If you cannot get feedback from the printer such as notification for advanced printers, there is no way to answer this question.
The tangible information is in the print queue (print spooler). Luckily it's not really the printer itself that needs to be interrogated for that information. It is in the windows print spooler already. This is very fortunate, because there is no need for "advanced printers". Basically every printer that has a Windows driver should be good enough. However, some printer drivers (e.g., "Microsoft Print to PDF") don't support the notifications from the spooler.

Meanwhile I built a proof of concept using mainly the Printer Change Notification Functions. These are a bunch of unwieldy beasts, but I finally managed to tame them. Currently I only got a couple of somewhat isolated code fragments implementing parts of the solution. I haven't integrated and tested them with an actual Access report yet. I also haven't had the opportunity to test with a print server on the network yet.
Nonetheless, it looks very promising. - I will report back, once I got a complete, integrated solution.
 
OK, I'm impressed. My only question is, given some remote installations, can you see the printer queues from the remote place that is printing (or vice versa, the remote printer queues from the local place that is printing)?
 
Your question and desire contradict each other.
Sorry. That was a foreign language issue. I only noticed now that "is printed" is likely being read as "completed printing" while a literal translation to my language would be "is in the process of being printed".

Anyway, send print jobs to the spooler. Your Access app can call WinAPI's to get a status from the printer server as to whether the job is queued for printing, currently printing, or cancelled.
In theory you are right. The problem is that I need to know *when* to get the status from the print queue. Too frequent polling would waste resources and block the Access UI, while too infrequent polling might miss the report being in the queue.
To solve this problem, I'm utilizing the Change Notification functions.
 
I only noticed now that "is printed" is likely being read as "completed printing" while a literal translation to my language would be "is in the process of being printed".
The problem is that I need to know *when* to get the status from the print queue.
The answer to the modified question is, start asking immediately after you open the report object. Check for it in the appropriate print queue on a one-second timer loop until you see the job or you reach a time-out point when the report object has been closed for at least 10-15 seconds. Your comments about using CTRL/P to trigger printing from the Preview screen means you might not have a clear-cut event for when it gets sent out for printing. Therefore, you have to have that loop running during the time it COULD be requested by command or keyboard shortcut.

I don't care how many pages are in the job. Once it is in the queue, it will take at least a second or two to stage the job and feed the first page, so a one-second timer loop should be adequate. If the job is IN the print queue in ANY state except Error or Suspended or something like that, you made it to the point where it is committed to being printed. Unless you even want to catch cases where it gets canceled before it actually starts.
 
Additional item you may want to check into. What happens if a user prints your report to a PDF then sends the PDF to the printer? Is that out of scope of what you are trying to catch?
 
Looking through the thread, I realize I don't know the end game. Are you using this as a way of telling a user something? Are you keeping track of usage stats? Do you actually CARE about someone opening a report in Print Preview and then closing it without printing? Where is this going?
 
Are you keeping track of usage stats? Do you actually CARE about someone opening a report in Print Preview and then closing it without printing?
The data in the report is some sort of contract. As long as it wasn't printed, it is allowed that users or the application automatically make changes to the data. Once it is printed, changes are no longer allowed. I need to know when it is printed to mark the data as "printed".
(Once they intentionally print, users have a strong incentive to make sure the printer output is complete and readable. So I don't need to track success of printing.)
If report data is accidentally marked as printed but actually wasn't, it's a minor nuisance. However, if the report is printed but the data not marked as such, it may become a major issue.

What happens if a user prints your report to a PDF then sends the PDF to the printer? Is that out of scope of what you are trying to catch?
That scenario is unlikely to happen. Nonetheless, creating a PDF from the report also qualifies as "printing" in the context of the process, regardless of whether the PDF is eventually printed on paper or not.
If users print to a PDF printer, that will also be caught by my intended monitoring of the print spooler. I didn't investigate direct PDF export using DoCmd.OutputTo PDF yet. Considering the even greater unlikeliness of this happening, I might ignore this loophole.
 
So why not just track the contract's print status within the Access app
Yikes!
That's what this thread is all about.
Now tell me how to detect printing from report preview.

General info: I think my approach using the print spooler API is going to work. Unfortunately the client came along with a couple of other high priority tasks and I had to put this report printing work on hold for a couple of days.
 
In the real world, a contract is not legally binding until it has been signed by all parties and returned to the party that created the contract. Perhaps you should obtain proof the contract has been signed by all parties and then updating the status? Just because a contract has been printed does not mean the contract cannot be amended or cancelled.
Of course, Adobe Sign or Docusign could be integrated in a comprehensive manner. :cool:
 
Use a timer to poll the status every 500 ticks (half second).You can set a limit of 10 seconds, (20 polls) using a loop. Play with the polling frequency and total time until you feel it's right.
I reply to this because that's the potential(!) flaw with your code suggested in #34.
My gut feeling (haven't had time to evaluate) is that half a second is too long an interval to reliably catch a document in the print queue of a PDF printer. At the same time half a second might be shorter than a remote print queue takes to respond to the query. So, this polling either might fail or completely block the application.
(While printing to PDF is not my primary concern at the moment, it is something I would like to have covered when creating a generic solution.)

In the real world, a contract is not legally binding until it has been signed by all parties and returned to the party that created the contract.
That's true of course. But printing the report creates a Schrödinger's contract from the application's point of view. It is a disconnected copy of the data that might be signed or might be shredded. The safe bet is to assume it is signed. If the contract needs to be cancelled of amended than users can do that in the application.
Perhaps you should obtain proof the contract has been signed by all parties and then updating the status?
This is a million times more likely to be forgotten by users than their instruction to not print from print preview, which already gets forgotten often enough for this task to have ended up on my todo list.
 
I know you rejected by previous suggestion to write the report to PDF and then present that to the user, but from the discussion I think that may be your best option. One of your issues with the PDF is that it is different from other reports, but this one is already different because you a requiring the user to remember never to print from preview. If you change the process to a complete and export action, the recording of the action becomes simple. It also supplies the user with the option to see the contract as it was when completed. Also if you want to further lock down the PDF, Windows folders can be configured to be write only.

I use this process for both purchase order and invoicing reports. All purchase orders are emailed, when generated an export tag is added to the data and end of report that can be referenced to set the PO Receive by vendor, Acknowledged, Approved flags.
 
I'm with RonPaii on the idea that this is ALSO an employee education problem. The tech solution to after-the-fact capture the act of printing starts to require you to "split hairs" over how the file was prepared and how quickly your queue sampling code got to it. Instead of hoping and praying you can catch a transitory event, consider providing a command button that "officially" commits the report to a printer and (behind the scenes) also marks that action in order to lock in the changes. Then perhaps lock in the form that opens the report so that it always and only opens the report in a non-printing mode.

I.e. make the whole decision to "lock and commit" really easy to do by building in the feature. Don't TELL them what to do. GUIDE them through doing it the right way.
 
An online service that provides a work flow for distributing documents, signing, and return could be integrated via API's to automate the status update in your Access app.
In theory it could.
In practice it makes very little sense because the customer supposed to sign the contract is physically there, in person, expecting to get a paper copy of the contract just signed.
 
If detecting Preview > Print is difficult, one new idea is to not have a Preview button. Perhaps the DATA can be previewed (in some form / subforms, not trying to faithfully show what would be printed), but the only way to get a printout is to click the Print button, which then allows user to select the output destination, and it goes there, and the data can be marked as "cannot modify anymore".
 
one new idea is to not have a Preview button.
I don't like this idea for reasons I will explain in the post after the next (#51).
But, this just gave me an idea that would have been a super simple solution...
I don't want to rob the users of the possibility to see the report in preview. However, I could show the user a preview but at the same time make this preview unsuitable for printing the final report. E.g., by putting a large "DRAFT" watermark in the background of the report when opened in preview. - This is not the perfect solution, but considering the extreme difficulty of detecting the print of the preview, it would be acceptable.

Luckily, I don't think I'm going to need this. But it will be my fallback if the current solution fails in the target environment.
 
Last edited:
then I think you're over complicating the need to know if they received the paper copy, when your app can just change the status of the contract to 'printed' so your users cannot edit the contract.
I do not care whether the customer received their copy.
I can change the status to printed anytime. But when do I do that? You leave this question unanswered and this was the core question right from the start of this thread.
 
One of your issues with the PDF is that it is different from other reports, but this one is already different because you a requiring the user to remember never to print from preview.
Well, the requirement to not print from preview was the status quo ante. The objective of this thread's underlying question was to get rid of this requirement.

and how quickly your queue sampling code got to it.
That was a problem with the polling of the printer queue suggested by others. Polling the queue is indeed problematic and for exactly this reason I rejected those suggestions. - My solution (outlined before and below) does not suffer from this problem.

Try the code below that uses Windows Management Instrumentation through the GetObject function to detect the status of remote print jobs.
This was that type of unverified AI output I asked not to post.
The QueueStatus property does not exist and the translation of numeric status to text does not make sense to me with any of the actually existing status properties one could get from the WMI printer object.
But the fatal problem of this approach is something else...
Use a timer to poll the status every 500 ticks (half second).You can set a limit of 10 seconds, (20 polls) using a loop. Play with the polling frequency and total time until you feel it's right.
The code you posted takes on average 850 ticks to run when querying the queue of a local printer already. I couldn't test it, but I'm convinced it will take longer for a remote print queue.
850 ticks is already much too long, to reliably catch a document in the queue of a PDF printer (which admittedly was/is not my primary concern).

I'm with RonPaii on the idea that this is ALSO an employee education problem.
I find it hard already to justify to educate users to not use an innocent keyboard shortcut like [CTRL]+[P]. - I personally hate it, when applications do not support generic standard shortcuts of the OS.

The idea to "guide" users by taking away well-known sensible features/functionality because the application cannot cope with them is a an absolute last resort to me. I strongly dislike the idea to make user's lives harder or more uncomfortable to make my work easier. This often is an easy approach to solve problems such as this one, but it is also a declaration of UX design bankruptcy by the developer.
Of course, pouring this amount of effort into this problem is highly questionable from an economic point of view. My client would have never approved this in advance and would be extremely upset if I would charge them for this in full without prior approval. - I won't.

I'm going to mark this thread as resolved now.
I pieced together all my individual proof-of-concept code snippets based on the Printer Change Notification Functions API.
My solution sketched:
When the report is opened in preview, I create a change notification object using FindFirstPrinterChangeNotification monitoring the print spooler of the report's printer for new jobs being added to the queue.
Luckily I discovered the RegisterWaitForSingleObject function, which allows to wait for an Windows event (signal) and then have the OS create a new thread invoking a callback function in my VBA code.
Unfortunately, the FindNextPrinterChangeNotification intended to retrieve additional information about the event behaves not entirely predictable. So I query the print spooler using the GetJob API to check whether the print job raising the event matches my report.
If yes, the report was printed.

My solution works reliably during runtime in my local environment. I will deploy this into the client's environment in the next couple of days and then will see if it also works reliably there.
I'm not sure yet if I will publish the source code for this. Certainly not before the client confirmed it working for them.

Of course, querying an external entity such as the print spooler to know if and when my report was printer is not the ideal solution. However, after my initial research and the discussions in this thread, I believe it is the only way this can be reliably achieved.

Thank you all for your valuable contributions to this thread and for all your suggestions.
 
Last edited:
How often have customers not received a copy?
I do not know and I do not care.
The problem is the other way round. Customers showed up with their genuine signed copy of the contract that according to the application was never printed out.
I can't say how often this happened but often enough for my client to present this as a problem to be resolved to me.
 

Users who are viewing this thread

Back
Top Bottom