Starting with Windows CE 6.0 kernel mode drivers cannot call User Interface (UI) functions. To overcome this Windows CE 6.0 provides the UIProxy and a new function, CeCallUserProc(). Kernel mode drivers can call CeCallUserProc() to request that a user mode dll be loaded and a function in the dll be called. When writing the driver, also write the user mode dll and provide functions that will present a UI for the driver.
Luca Calligaris was nice enough to provide an example function in response to a newsgroup question (UI proxy helper dll - microsoft.public.windowsce.platbuilder):
extern "C" BOOL KMessageBox(
  LPVOID lpInBuffer,
  DWORD nInBufferSize,
  LPVOID lpOutBuffer,
  DWORD nOutBufferSize,
  PDWORD pBytesReturned
return (BOOL)MessageBox(NULL,(LPCTSTR)lpInBuffer,_T("TEST"),MB_OK);
This function is passed in a message to display in a MessageBox() and returns the results of the call. The kernel mode driver would use this function by calling CeCallUserProc() passing in the name of the user mode dll, the name of the function, and the string to display in the MessageBox(). So you might think that the call would look like:
CeCallUserProc( TEXT(“MyUI.dll”), TEXT(“KMessageBox”), TEXT(“ERROR: Driver Failed”), wcelen(TEXT(“ERROR: Driver Failed”)), NULL, 0, &BytesReturned );
That call is in red because I want to draw your attention to it. That call will fail with error 87, or invalid parameter. So let’s explore CeCallUserProc() and UIProxy more.
The function prototype for CeCallUserProc() is:
BOOL CeCallUserProc(
 LPCWSTR pszDllName,
 LPCWSTR pszFuncName,
 LPVOID lpInBuffer,
 DWORD nInBufferSize,
 LPVOID lpOutBuffer,
 DWORD nOutBufferSize,
 LPDWORD lpBytesReturned
The parameters are fairly well explained in Platform Builder Help, but there are some details that are not explained. But luckily, the details, and problems, can be found in UIProxy. The UIProxy source is available in $(WINCEROOT)\Public\Common\Oak\Drivers\UIPROXY. UIProxy includes a function that CeCallUserProc() uses to call the user mode dll. The function is LoadUserDll() which is called through a call to the IoControl() function.
Problems with UIProxy’s LoadUserDll() that you need to know about:
1.       lpOutBufferSize MUST BE greater than lpInBufferSize.   There is no logical reason for this, but if lpOutBufferSize is smaller than lpInBufferSize, the call will fail and GetLastError() will return 87 which means invalid parameter.
2.       lpInBuffer MUST BE non-NULL. Again, no logical reason for this.
3.       There is no check to confirm that lpBytesReturned is non-NULL, it blindly writes to it.
So this explains why the call to CeCallUserProc() would fail when the size of the output buffer is zero.  To actually call KMessageBox(), you will need to pass in a size for the output buffer greater than the input buffer size. 
Another problem that you could run into is that the input and output buffers cannot contain embedded pointers. This is because the user mode dll cannot map the embedded pointers to kernel space.
For an example driver and user mode dll see Windows CE 6.0: Example UIProxy User Mode DLL
For more about drivers see Windows CE: Device Driver Summary
Copyright © 2009 – Bruce Eitman
All Rights Reserved