Partilhar via


Hardcore pointer marshalling samples for Windows CE 6

This is one of those blogs where I hesitated to post it because it may be going too hard core into the guts of CE.  But from dealing with our customers who use our shared source and some of the really hard core questions I get, I'm hoping there are at least 5 super-geeks who can benefit from this.

Suppose you're writing a device driver or a service that has to do all sorts of really gross pointer marshalling across process.  For instance, consider MSMQ.  Apps get at MSMQ by calling API's like MQOpenQueue in msmqrt.dll.  This function is called from user space, but just takes the paramaters the user specified, calls DeviceIoControl(hHandleToMSMQ,...), which calls the MSMQ service to do the real heavy lifting.

In the old kernel with the flat address space this was easy (relatively).  In the new VM model it gets trickier.  Sue Loh has a good blog about this at https://blogs.msdn.com/ce_base/archive/2006/11/09/Memory-marshalling-in-Windows-CE.aspx.

Sue Loh has another good blog about the raw pointer marshal API for CE 6 at https://blogs.msdn.com/ce_base/archive/2006/11/22/marshalling-helper-apis.aspx

This API was designed so that both kernel level drivers and user mode drivers (e.g. udevice.exe & servicesd.exe) could use the same set of API's.  To make marshaling even easier, we have a set of templates in the %_WINCEROOT%\public\common\oak\inc\psl_marshaler.hxx.  This header is extensively commented along with samples showing how to make it work.  The upshot is that if you have some user-mode API that takes say 5 paramaters (some ptrs that need to be marshalled across process, others regular old DWORDs) then by using the PSL Marshaller templates you can save yourself a lot of the tedium as far as manually packing and unpacking these datatypes during the DeviceIoControl() call.

Besides the psl_marshaler code itself, some samples may help if you have to do this.

GPSID:  GPS Intermediate Driver.  This is the easiest sample (by far) I know of that uses psl_marshaler stuff in actual shipping code.

%_WINCEROOT%\public\COMMON\oak\drivers\GPSID\client - GPSAPI.dll src, link time library.  All it does is just call to the psl_marshall helpers
%_WINCEROOT%\public\COMMON\oak\drivers\GPSID\service - GPSID.dll, actual service that implements guts of this.

You can look on https://msdn.microsoft.com/library/en-us/mobilesdk5/html/mob5grfGPSIntermediateDriverFunctions.asp?frame=true to get a sense that there's just not that much going on with this API.  Actually that's intentional to keep implementation easier :).  The harder ones are code & API definitions we port from desktop Windows.  Here they have MIDL to do all the really heavy lifting for them, unlike CE where it's still psuedo-manual.  CE actually does have interprocess COM and we could rely on it & MIDL to do our IPC, but it adds 500KB to our ROM, isn't on Windows Mobile devices, and isn't as efficient in terms of CPU/memory as using the raw access routines & templates.

MSMQ: Microsoft Message Queuing.  Note you'll need to be a Platform Builder customer and install the shared source kit in order to get this code.
We're past the point of the mythical 5 people who actually need this much information.  I'm hoping you guys can figure out how to do the interprocess calls based on docs, blog entries, and the GPSID sample.  Now I'm basically targetting 1 person who actually has to marshall pointers inside structures inside arrays of VARIANTs, and to do it asyncronously at that.  My first recommendation is that if you have any control over your API, then try to keep it as simple as possible.  No embedded pointers or arrays, no asyncronous writing back of data.

However, if you're in this situation you're where I was at when getting MSMQ up on the new kernel.  In this case I'll point you at the MSMQ code in hopes that it may provide you some inspiration as to what you're trying to do.

%_WINCEROOT%\private\servers\msmq\sc\ce\client -> msmqrt.dll, runtime library apps link to to call into MSMQ.
%_WINCEROOT%\private\servers\msmq\sc\ce\driver\msmqdev.cxx - MSMQD.dll.  Especially MMQ_IOControl().

[Author: John Spaith]

Comments

  • Anonymous
    June 08, 2008
    Hi John,I successfully wrote a User Driver that makes use of psl_marshaller API. I have a question for you:1.Is there any way that the User Driver can register a Call Back function with the Kernel Driver and Kernel Driver calling the Call Back at appropriate events.

  • Anonymous
    June 10, 2008
    We used to have PerformCallBack() API prior to CE 6 that could let you pass in a fcn pointer directly for another process to call.  It turns out it was a security nightmare though and it was yanked in CE6.So you'd have to have some sort of more standard IPC mechanism - like say waiting on an event - in order to do simulate the callback.

  • Anonymous
    June 11, 2008
    Can you be more specific with an example on how waiting on an event can be used for performing the Call back? Is there a psl_marshaller API that can be used for this?

  • Anonymous
    June 12, 2008
    1 - Basically in your user-mode whatever, you need to CreateEvent() and pass that to the kernel driver.  The kernel driver needs to call DuplicateHandle() on this event.  The user-mode thing does WaitForSingleObject() and when the kernel thing does SetEvent() it causes the user-mode to wake up.  The user mode may need to call back into the kernel to get more specific information about what happens, then it would call the "callback" function itself.This is how GPS Intermediate Driver works.  App passes to GPSID a handle to be signaled when new location arrives (GPSOPenDevice).  GPSID sets the event, when app wakes up it calls GPSGetPosition() to actually retrieve the position.publicCOMMONoakdriversGPSIDservicegpsHandle.cpp shows DuplicateHandle() calls.If you're on a totally embedded device where you know no other apps will potentially be on it (or you trust the apps to be well behaved), you could skip all the DuplicateHandle() stuff and just signal a named event if your scenario allows it.  That'd be much less work.2 - No.

  • Anonymous
    June 16, 2008
    Need clarification on your statement: "The user mode may need to call back into the kernel to get more specific information about what happens, then it would call the "callback" function itself."My understanding:Let's say the Callback func is of type BOOL CallBack (DWORD *pdwretValue). When the kernel driver does SetEvent(), the UserDriver wakes up and then calls  the func Callback(&dwRetValue). This func can in turn make a PSL Call to the Kernel Mode Driver to get the value.Pl. correct my understanding.By the Way, thanks a lot for your support. I enjoy these PSL Calls. Excellent work by the developer.

  • Anonymous
    June 19, 2008
    Yes, I think you've captured what I was trying to explain.  Basically order of events would be1) USER: hEvent = CreateEvent()2) USER: CallKernelApi(hEvent);3) USER: WaitForSingleObject(hEvent);4) KERNEL: (Some other thread) SetEvent(hEvent);5) USER: Wakes up from WaitForSingleObject()6) USER: Calls PSL "GetInformation(&dwInfo)", which kernel fills out dwInfo value7) USER: Calls Callback(&dwInfo) with the value from kernel.(I guess you could swap out 6+7, so the callback itself was responsible for calling the kernel.  This is how it looks like you propose.)If there's more followup we should probably take this to a newsgroup (microsoft.public.windowsce.platbuilder is probably the best) since I only check the blog every few days, and there's more eyes to help out on problem in newsgroup.Also congrats for figuring out how to use the PSL samples.  They're not the most intuitive by a longshot, but once you understand them they've very powerful.