FindNextPrinterChangeNotification returns NULL for ppPrinterNotifyInfo - c++

I'm stuck on problem were I would like to ask for some help:
I have the task to print some files of different types using ShellExecuteEx with the "print" verb and need to guarantee print order of all files. Therefore I use FindFirstPrinterChangeNotification and FindNextPrinterChangeNotification to monitor the events PRINTER_CHANGE_ADD_JOB and PRINTER_CHANGE_DELETE_JOB using two different threads in the background which I start before calling ShellExecuteEx as I don't know anything about the application which will print the files etc. The only thing I know is that I'm the only one printing and which file I print. My solution seems to work well, my program successfully recognizes the event PRINTER_CHANGE_ADD_JOB for my file, I even verify that this event is issued for my file by checking what is give to me as additional info by specifying JOB_NOTIFY_FIELD_DOCUMENT.
The problem now is with the event PRINTER_CHANGE_DELETE_JOB, where I don't get any addition info about the print job, though my logic is exactly the same for both events: I've written one generic thread function which simply gets executed with the event it is used for. My thread is recognizing the PRINTER_CHANGE_DELETE_JOB event, but on each call to FindNextPrinterChangeNotification whenever this event occured I don't get any addition data in ppPrinterNotifyInfo. This works for the start event, though, I verified using my logs and the debugger. But with PRINTER_CHANGE_DELETE_JOB the only thing I get is NULL.
I already searched the web and there are some similar questions, but most of the time related to VB or simply unanswered. I'm using a C++ project and as my code works for the ADD_JOB-event I don't think I'm doing something completely wrong. But even MSDN doesn't mention this behavior and I would really like to make sure that the DELETE_JOB event is the one for my document, which I can't without any information about the print job. After I get the DELETE_JOB event my code doesn't even recognize other events, which is OK because the print job is done afterwards.
The following is what I think is the relevant notification code:
WORD jobNotifyFields[1] = {JOB_NOTIFY_FIELD_DOCUMENT};
PRINTER_NOTIFY_OPTIONS_TYPE pnot[1] = {JOB_NOTIFY_TYPE, 0, 0, 0, 1, jobNotifyFields};
PRINTER_NOTIFY_OPTIONS pno = {2, 0, 1, pnot};
HANDLE defaultPrinter = PrintWaiter::openDefaultPrinter();
HANDLE changeNotification = FindFirstPrinterChangeNotification( defaultPrinter,
threadArgs->event,
0, &pno);
[...]
DWORD waitResult = WAIT_FAILED;
while ((waitResult = WaitForSingleObject(changeNotification, threadArgs->wfsoTimeout)) == WAIT_OBJECT_0)
{
LOG4CXX_DEBUG(logger, L"Irgendein Druckereignis im Thread zum Warten auf Ereignis " << LogStringConv(threadArgs->event) << L" erkannt.");
[...]
PPRINTER_NOTIFY_INFO notifyInfo = NULL;
DWORD events = 0;
FindNextPrinterChangeNotification(changeNotification, &events, NULL, (LPVOID*) &notifyInfo);
if (!(events & threadArgs->event) || !notifyInfo || !notifyInfo->Count)
{
LOG4CXX_DEBUG(logger, L"unpassendes Ereignis " << LogStringConv(events) << L" ignoriert");
FreePrinterNotifyInfo(notifyInfo);
continue;
}
[...]
I would really appreciate if anyone could give some hints on why I don't get any data regarding the print job. Thanks!
https://forums.embarcadero.com/thread.jspa?threadID=86657&stqc=true

Here's what I think is going on:
I observe two events in two different threads for the start and end of each print job. With some debugging and logging I recognized that FindNextPrinterChangeNotification doesn't always return only the two distinct events I've notified for, but some 0-events in general. In those cases FindNextPrinterChangeNotification returns 0 as the events in pdwChange. If I print a simple text file using notepad.exe I only get one event for creation of the print job with value 256 for pdwChange and the data I need in notifyInfo to compare my printed file name against and comparing both succeeds. If I print a pdf file using current Acrobat Reader 11 I get two events, one has pdwChange as 256, but gives something like "local printdatafile" as the name of the print job started, which is obviously not the file I printed. The second event has a pdwChange of 0, but the name of the print job provided in notifyInfo is the file name I used to print. As I use FreePDF for testing pruproses, I think the first printer event is something internal to my special setup.
The notifications for the deletion of a print job create 0 events, too. This time those are sent before FindNextPrinterChangeNotification returns 1024 in pdwChange, and timely very close after the start of the print job. In this case the exactly one generated 0 event contains notifyInfo with a document name which equals the file name I started printing. After the 0 event there's exactly one additional event with pdwChange of 1024, but without any data for notifyInfo.
I think Windows is using some mechanism which provides additional notifications for the same event as 0 events after the initial event has been fired with it's real value the user notified with, e.g. 256 for PRINTER_CHANGE_ADD_JOB. On the other hand it seems that some 0 events are simply fired to provide data for an upcoming event which then gets the real value of e.g. 1024 for PRINTER_CHANGE_DELETE_JOB, but without anymore data because that has already been delivered to the event consumer with a very early 0 event. Something like "Look, there's more for the last events." and "Look, something is going to happen with the data I already provide now." Implementing such an approach my prints now seem to work as expected.
Of course what I wrote doesn't fit to what is documented for FindNextPrinterChangeNotification, but it makes a bit of sense to me. ;-)

You're not checking for overflows or errors.
The documentation for FindNextPrinterChangeNotification says this:
If the PRINTER_NOTIFY_INFO_DISCARDED bit is set in the Flags member of
the PRINTER_NOTIFY_INFO structure, an overflow or error occurred, and
notifications may have been lost. In this case, no additional
notifications will be sent until you make a second
FindNextPrinterChangeNotification call that specifies
PRINTER_NOTIFY_OPTIONS_REFRESH.
You need to check for that flag and do as described above, and you should also be checking the return code from FindNextPrinterChangeNotification.

Related

DirectX 11 Debug Layer Capture Error Strings

I have the DirectX Debug Layer working and it outputs errors and warnings to the output window in visual studio. Like this for example (not the actual issue I'm facing):
D3D11 WARNING: ID3D11DeviceContext::OMSetRenderTargets: Resource being set to OM RenderTarget slot 0 is still bound on input! [ STATE_SETTING WARNING #9: DEVICE_OMSETRENDERTARGETS_HAZARD]
I have a custom logging system that saves to files and prints in other windows. I'd like to capture the debug message strings, and display them in my own way. Is this supported? If so how do I do it?
Since I had this problem myself and found the previous answer a bit lackluster, I want to give a more detailed solution, now that I've got this working:
All API calls I will reference can be found here
One can get messages out of DirectX11 by reading them from an internal message queue, that can be accessed by calling ID3D11InfoQueue::GetMessage, which takes the index of the message to get and fills a provided buffer with a D3D11_MESSAGE struct, that contains all the wanted information (Severity, Category, ID and Text).
However, I discovered that this buffer was empty (by calling ID3D11InfoQueue::GetNumStoredMessages) when I tried to iterate over it. That seemed to be due to some filtering going on. In order for the runtime to actually fill this buffer, I first had to call ID3D11InfoQueue::PushEmptyStorageFilter, which pushes a filter that doesn't filter out any messages:
//HANDLE_HRESULT is just a macro of mine to check for S_OK return value
HANDLE_HRESULT(debug_info_queue->PushEmptyStorageFilter());
This filtering is the part that is actually discussed in the blog post that is linked in Chuck Walbourn's answer (allthough the link only directs me to the main page, the actual blog post is here). It doesn't contain any info on how to redirect the messages though.
Once the messages are generated, you can iterate over them like so:
UINT64 message_count = debug_info_queue->GetNumStoredMessages();
for(UINT64 i = 0; i < message_count; i++){
SIZE_T message_size = 0;
debug_info_queue->GetMessage(i, nullptr, &message_size); //get the size of the message
D3D11_MESSAGE* message = (D3D11_MESSAGE*) malloc(message_size); //allocate enough space
HANDLE_HRESULT(debug_info_queue->GetMessage(i, message, &message_size)); //get the actual message
//do whatever you want to do with it
printf("Directx11: %.*s", message->DescriptionByteLength, message->pDescription);
free(message);
}
debug_info_queue->ClearStoredMessages();
debug_info_queue is the ID3D11InfoQueue interface and can be obtained like so:
ID3D11InfoQueue* debug_info_queue;
device->QueryInterface(__uuidof(ID3D11InfoQueue), (void **)&debug_info_queue);
You use the ID3D11InfoQueue interface to implement your own debug message output.
using Microsoft::WRL::ComPtr;
ComPtr<ID3D11Debug> d3dDebug;
if (SUCCEEDED(device.As(&d3dDebug)))
{
ComPtr<ID3D11InfoQueue> d3dInfoQueue;
if (SUCCEEDED(d3dDebug.As(&d3dInfoQueue)))
{
See this blog post

Get job from spooler - C++

With a printer that doesn't exist, I send to the spooler different files. In my software, I try to get all files existing in the queue of the spooler. For that, I tried the following instruction:
bool t = EnumJobs(hPrinter, 0,1,3, (LPBYTE) &h, sizeof(JOB_INFO_3), &pcbNeeded, &pcReturned)
I get jobId in the field 'JobId' of the structure.
In the structure type 'JOB_INFO_3', the field 'JobId' is well filled but the field 'nextJobId' is not filled. Why?
It's the same problem when I execute the following instruction:
bool t = EnumJobs(hPrinter, 0,3,3, (LPBYTE) &h, sizeof(JOB_INFO_3), &pcbNeeded, &pcReturned)
Moreover, the field 'JobId' is not filled. Why ?
Then, I don't know how to get info(filename, state, number of pages, etc) of a particular job. I tried the following instruction but it didn't work:
GetJobA(hPrinter, h.JobId, 1, (LPBYTE) &job_info_1, sizeof(JOB_INFO_1), & nbBytes)
And my last question is: Is it possible to get all the jobs from the spooler of the printer?
Do you have any solutions?
So, I'm not sure what the rest of your code looks like, but it looks possible that you're not using the API quite correctly. The MSDN documentation suggests that you should call the EnumJobs API twice.
To determine the required buffer size, call EnumJobs with cbBuf set to zero. EnumJobs fails, GetLastError returns ERROR_INSUFFICIENT_BUFFER, and the pcbNeeded parameter returns the size, in bytes, of the buffer required to hold the array of structures and their data.
https://msdn.microsoft.com/en-us/library/windows/desktop/dd162625(v=vs.85).aspx
The flow goes like this:
Call EnumJobs for the first time to see how much memory needs to be allocated for your JOB_INFO_n array.
Allocate the memory required for your JOB_INFO_n array.
Call EnumJobs with your JOB_INFO_n array.
Looking at the call to EnumJobs where you attempt to get the first three jobs, the size of your pJob appears to be sizeof(JOB_INFO_3), where it should be three times this size in order to hold all three jobs. What is the return from EnumJobs for that call?
The reason why nextJobId is not filled in is likely a misunderstanding of the field. This field is for print jobs that have been linked together, not to find out which print job is next in the queue.
NextJobId - The print job identifier for the next print job in the linked set of print jobs.
https://msdn.microsoft.com/en-us/library/windows/desktop/dd145021(v=vs.85).aspx
As for the information about the print job, this is going to be difficult. Unfortunately, there is no way I know of to get the name/path of the file printed. There's no concept of this in the spooler APIs. Consider a print job which isn't backed by a file for example. The best you get is the print job name, which is set by the printing application.
For pages, it looks like there is a TotalPages field in the JOB_INFO_1 structure. That may be of some use to you. It looks like you're already trying to get the JOB_INFO_1 structure but having some troubles. If the API is failing, you can use GetLastError() to identify what the issue is. Does the job ID you're passed in exist?
https://msdn.microsoft.com/en-us/library/windows/desktop/ms679360(v=vs.85).aspx
For the last question about getting all print jobs from the queue. It seems that the MSDN documentation suggests the following:
To determine the number of print jobs in the printer queue, call the GetPrinter function with the Level parameter set to 2.
https://msdn.microsoft.com/en-us/library/windows/desktop/dd162625(v=vs.85).aspx
Hope this helps.

count all events in stream with babeltrace API

I have a LTTNg trace, which i am parsing using babeltrace API. So i was wondering if I could count all events in trace (or stream) without iterating over them. What functions from publilc API I can use to do that ?
The very nature of CTF makes it impossible to count the event records of a given packet in constant time. The packet's context could include an event record count field somehow, but it's not specified, so generic tools would not use it.
Thus the only way to count events is to iterate the event records, unfortunately. The easiest way is to count the number of lines that the text format of the babeltrace(1) tool prints:
babeltrace /path/to/ctf/trace/directory | wc --lines
This works as long as there's one line per printed event record, which is the case unless an event record contains a string field which has a newline (currently not escaped in the text output).
You may also wish to consider discarded event records. They are not printed to the standard output by babeltrace(1), but the tool prints a message including the count to the standard error when they are detected.
There's no way with the current babeltrace(1) tool to only print the event records which belong to the packets of a given data stream. If you need this, what I suggest is that you remove all the data stream files except the one for which you need an event record count, and run the command above again.
Also consider the Babeltrace Python bindings, for example (not tested):
import babeltrace
def count_ctf_event_records(path):
trace_collection = babeltrace.TraceCollection()
trace_collection.add_trace(path, 'ctf')
return sum(1 for event in trace_collection.events)
if __name__ == '__main__':
import sys
print(count_ctf_event_records(sys.argv[1]))
Saved as count.py, you can try this:
python3 count.py /path/to/ctf/trace/directory
Counting the event records of a specific data stream with the Python bindings is left as an exercise for the reader.
Having said this, I don't know if the Python bindings approach is faster than the babeltrace(1) one.

Implementing a printer spooler

We have an assignment in which we have to implement a printer spooler using linked lists. I have used the list from STL to implement everything. I can get the program to send the print command and maintain status of documents. The problem is I don't know how to mimic/send a dummy print command so the document gets printed. If I use Sleep() there is a delay in the whole program.while I want printing on backhand and others users to have access to the program.
Any help will be appreciated regarding my problem thanks.
In a REAL print spooler, either there are multiple threads (or in Unix, maybe a fork) or the system uses a "wait for several objects" type approach, and when there is something to print, a portion of the document is sent to the printer, set up so that when that portion is "consumed", a waitable object is "ready". Then the spooler waits for something to happen (either a new job or the printed). When the wait is completed, it determines what happened. If it's a new print job, queue it, if it's "some printing completed", it checks if that's the last of the printing and completes the job, or sends more stuff to the printer.
In pseudocdoe it may look something like this:
for(;;)
{
what = wait_for_work();
switch (what)
{
case new_job:
add_new_job();
break;
case print_write_done:
if (document_complete)
remove_current_job();
else
send_more_from_current_job();
break;
case spooler_terminate:
exit(0);
default:
print_error_message();
}
}
Obviously, without seeing your code, it's hard to say how you should implement it. But one could have a timer to simulate the "document being printed in small chunks", say, 10KB is consumed every 100ms, for example.

Win32 API Print not asking user input

We have something like Secure Print.
I go to my control panel >> Printers and Devices >> Printer >> Printer Preference Here i assign a 4 digit pin code ..
I fire a print from Word or Excel ... go to the printer >> retrieve jobs >> select my print job and enter my pin code.
Only then my job gets printed.
Now Our application has two source codes for printing
While firing print from our Application the print is instantaneously done .... I.e. it doesn't store my job in the queue and wait for pin ... but just prints immediately
Some source code
bResult = OpenPrinter(szDevice, &hPrinter, NULL);
// szDevice contains printer name
startDocPrtInfo.pDocName = msg;
startDocPrtInfo.pOutputFile = NULL;
startDocPrtInfo.pDatatype = "RAW";
bPrntErr = (StartDocPrinter(hPrinter,1,(LPBYTE)&startDocPrtInfo) == 0);
WritePrinter(hPrinter, &buffer[2], i, &cnt);
EndDocPrinter(hPrinter);
ClosePrinter(hPrinter);
The Code is printing the given document however its not waiting for user to enter his pin code
QUESTION How to make the above code wait just like its doing for other applications ? is there some setting that I am missing here ?
To give you more details .
We have another Print subroutine in the same application that works as desierd .. I.e. waits for User to enter his PIN
However the subroutine uses TextOut method instead of WritePrinter
Any idea why print is not waiting when i use the WritePrinter methods of printing?
Thanks in Anticipation CP
I resolved this.
There is NO CODE CHANGES.
In our Printer Settings, we need to UNCHECK the "Allow Escape Sequence"
Thanks to the forum tho for entertaining the question.
Will visit often with more questions and resolutions to Questions that others raise
Good Luck
A Clueless Programmer :)