RegisterForPrintAsyncNotifications

I write this in hopes of saving you some time, if you're looking at MSFT's new RegisterForPrintAsyncNotifications API (new in Vista and Server 2008).

At first glance (and second, and third) glance, this API doesn't seem to work. It promises to provide asynchronous notifications of state changes to the print objects in the spooler, but how the $#@^ does it work? Let's look at it more closely. (It's defined int he file prnasnot.h, if you've got your sdk handy.


HRESULT RegisterForPrintAsyncNotifications(
LPCWSTR pName,
PrintAsyncNotificationType *pSchema,
PrintAsyncNotifyUserFilter filter,
PrintAsyncNotifyConversationStyle directionality,
IPrintAsyncNotifyCallback *pCallback,
HANDLE *pRegistrationHandler
);


pName is a pointer to the name of the print object you want to monitor. If you want alerts from all objects, you can pass NULL here (or so I've observed!)

pSchema is a pointer to a schema that the client expects. This is the hinge of the whole mess. More about that in a moment. For now, take it on faith that this has to be the right "Magic String" to get you what you want.

filter. Where this notification will be sent. can be 'kPerUser' or 'kAllUsers'.

directionality. Wonderful, meaningless name, isn't it? What this means is, is the channel specified in 'pSchema' biDirectional or uniDirectional. That is, will the code that's listening for events sent thing back to the other end of the pipe. Values are 'kBiDirectional' and 'kUniDirectional'. The 2008 Server DDK includes a bidirectional example in 'src\print\asyncnotify', but I've just been using kUniDirectional.

pCallback. This is a pointer to your code that the server will call-back to when there's an event. In O-O, this is the 'observer' object. It HAS to be a pointer to an object on the heap (learned that the hard way).

pRegistrationHandler This is your handle back to the registration when you're done listening, you unregister by calling Unregister like this:

::UnRegisterForPrintAsyncNotifications(hRegistration);


Still will me? 'Kay, here's a snippet showing how this all ties together:



HANDLE hRegistration;
HANDLE unregisterForNotificationsEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

IPrintAsyncNotifyCallback* pCallback = new CPrintAsyncNotifyCallback( unregisterForNotificationsEvent);

HRESULT hr = ::RegisterForPrintAsyncNotifications( argv[1],
const_cast(&PRINT_APP_BIDI_NOTIFY_CHANNEL),
kAllUsers,
kUniDirectional,
pCallback,
&hRegistration
);


Couple of things to note:
- that PRINT_APP_BIDI_NOTIFY_CHANNEL is the undocumented magic you really need. It's defined in prnasnot.h, but documented nowhere else. I stumbled across it in desperation, but it's just what you need to listen in on the same bidiSchema updates that the spooler is receiving.
- pCallBack derives from IUnknown, so yes, you're implementing a COM object. Mainly, that's just so the interface can reference-count and allow the object to clean-up.
- The IPrintAsyncNotifyCallback implementation is kludgy, but if you follow the example in the ddk, it's not too bad. What you end-up getting back is XML that conforms to the Microsoft BidiSchema.

I swore for 2 months that this interface was non-functional, but once I found the PRINT_APP_BIDI_NOTIFY_CHANNEL guid, then it started working.

Enjoy :-)

Popular posts from this blog

Monday Mope

Review: The Southeast Christian Church Easter Pageant