The UIProxy source code can be downloaded from: 
In Windows CE 6.0: Using UIProxy I discussed how to use CeCallUserProc() and the UIProxy. I showed some simple code for displaying a very simple MessageBox(). For this post, I thought that it would be interesting to flesh that out a little more and create a user mode DLL that wraps MessageBox() and allows the driver to do everything with MessageBox() that it might do if it was calling it directly. Almost everything, except pass a usable HWND because a kernel mode driver cannot actually have a usable HWND.
To handle the parameters needed by MessageBox(), create a data structure to hold the parameters. This is tricky though because the structure cannot have embedded pointers. So taking a page from some Microsoft defined KernelIoControl functions, the strings will be stored in the space just after the structure and the offsets to the strings will be stored in the structure:
typedef struct __MSGBOX_DATA__{
                DWORD TextOffset;
                DWORD CaptionOffset;
                DWORD Type;
} MessageBoxData;
Where:
·         TextOffset is an offset in bytes to the string to be displayed in the body of the MessageBox(). This offset will typically be sizeof( MessageBoxData ).
·         CaptionOffset is an offset in bytes string to be displayed in the title bar of the MessageBox(). This offset will typically be sizeof (MessageBoxData) plus the number of bytes in the Text string, plus the size of the NULL character.
·         Type is the uType parameter to MessageBox().
Then a user mode dll is needed that actually calls MessageBox() and passes in parameters that the kernel mode driver passes in. Note the link at the top of this page, it will allow you to download the source code, including sources and makefiles.   The function looks like:
BOOL DSUIMessageBox(LPVOID InBuffer, DWORD InBufferSize, LPVOID OutBuffer, DWORD OutBufferSize, PDWORD BytesReturned)
{
                DWORD RetVal = TRUE;
                MessageBoxData *MBData = (MessageBoxData *)InBuffer;
                TCHAR *pCaption = NULL;
                TCHAR *pText = NULL;
               
                if( ( InBuffer != NULL ) &&
                                ( InBufferSize > sizeof( MessageBoxData )) &&
                                ( OutBuffer != NULL ) &&
                                ( OutBufferSize >= sizeof( DWORD ) ) &&
                                ( BytesReturned != NULL ) )
                {
                                if( MBData->CaptionOffset != 0 )
                                                pCaption = (TCHAR *)((BYTE *)MBData + MBData->CaptionOffset);
                                if( MBData->TextOffset != 0 )
                                                pText = (TCHAR *)((BYTE *)MBData + MBData->TextOffset);
 
                                RetVal = MessageBox(NULL,pText,pCaption,MBData->Type);
 
                                *(DWORD *)OutBuffer = RetVal;
                                *BytesReturned = sizeof( DWORD );
                }
                else
                                RetVal = FALSE;
 
                return RetVal;
}
DSUIMessageBox() verifies the input parameters, then passes them to MessageBox(). On return from MessageBox(), the results are put in the OutBuffer. This function does use both the input and output buffers, so it checks that they at least appear to be valid.
Next, a kernel mode driver that calls CeCallUserProc() is needed. For this I added a new function to Windows CE: A Stream Interface Driver Shell or more specifically, I started with the most current version of the driver from Windows CE: Stream Interface Driver Power Management. The function that I added is:
DWORD UIP_MessageBox(   HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType )
{
                MessageBoxData *MBData;
                BYTE *pStr;
                DWORD MBDataSize;
                DWORD TextLength;
                DWORD CaptionLength;
                DWORD *OutBuffer;
                DWORD OutBufferSize;
                DWORD RetVal = 0;
                DWORD BytesReturned = 0;
 
                TextLength = lpText ? (wcslen( lpText ) + 1 ) : 0;
                CaptionLength = lpCaption ? (wcslen( lpCaption ) + 1 ) : 0;
                MBDataSize = sizeof( MessageBoxData ) +
                                ( TextLength * sizeof( TCHAR ) ) +
                                ( CaptionLength * sizeof( TCHAR ) );
 
                MBData = malloc( MBDataSize );
                OutBufferSize = MBDataSize + sizeof( DWORD );
                OutBuffer = malloc( OutBufferSize );
 
                if( MBData == NULL || OutBuffer == NULL )
                {
                                RETAILMSG( 1, (TEXT("Malloc failed %X %X\n"), MBData, OutBuffer ));
                                return 0;
                }
 
               
                MBData->TextOffset = lpText ? sizeof( MessageBoxData ) : 0 ;
                MBData->CaptionOffset = lpCaption ? sizeof( MessageBoxData ) + (TextLength * sizeof( TCHAR )): 0 ;
                MBData->Type = uType;
                if( lpText )
                {
                                pStr = (BYTE *)MBData + MBData->TextOffset;
                                wcscpy( pStr, lpText );
                }
                if( lpCaption )
                {
                                pStr = (BYTE *)MBData + MBData->CaptionOffset;
                                wcscpy( pStr, lpCaption );
                }
 
                if( !CeCallUserProc(DSUI, MSGBOXFN, MBData, MBDataSize, OutBuffer,  OutBufferSize, &BytesReturned ) )
                {
                                RETAILMSG( 1, (TEXT("CeCallUserProc failed (%d)\n"), GetLastError() ));
                }
                else
                {
                                RetVal = *OutBuffer;
                }
 
                free( MBData );
                free( OutBuffer );
 
                return RetVal;  
}
UIP_MessageBox() takes the same parameters as MessageBox(), although it ignores the HWND parameter. This may look complicated, but that is because it is manipulating the MessageBoxData structure. Note that it allocates the sizeof MessageBoxData plus the size of the two strings. Then because of the problems with UIProxy that I mention in Windows CE 6.0: Using UIProxy, it allocates the same size plus the size needed for the return data.
To make it easy to test the MessageBox(), I added calls to UIP_MessageBox() to the XXX_Read() function:
DWORD XXX_Read(DWORD hOpenContext, LPVOID pBuffer, DWORD Count)
{
                DWORD RetVal = 0;
                DWORD BytesReturned = 0;
 
                RetVal = UIP_MessageBox( NULL, TEXT("Geekswithblogs.net/BruceEitman XXX_Read called"), TEXT("DriverShell"), MB_ICONINFORMATION | MB_OKCANCEL );
                switch( RetVal )
                {
                                case IDOK:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDOK\n")));
                                                break;
                                case IDABORT:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDABORT\n")));
                                                break;
                                case IDCANCEL:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDCANCEL\n")));
                                                break;
                                case IDIGNORE:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDIGNORE\n")));
                                                break;
                                case IDNO:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDNO\n")));
                                                break;
                                case IDRETRY:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDRETRY\n")));
                                                break;
                                case IDYES:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDYES\n")));
                                                break;
                }
 
                RetVal = UIP_MessageBox( NULL, TEXT("Do you like Geekswithblogs?"), TEXT("DriverShell"), MB_ICONQUESTION | MB_YESNOCANCEL );
                switch( RetVal )
                {
                                case IDOK:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDOK\n")));
                                                break;
                                case IDABORT:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDABORT\n")));
                                                break;
                                case IDCANCEL:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDCANCEL\n")));
                                                break;
                                case IDIGNORE:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDIGNORE\n")));
                                                break;
                                case IDNO:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDNO\n")));
                                                break;
                                case IDRETRY:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDRETRY\n")));
                                                break;
                                case IDYES:
                                                RETAILMSG(1, (TEXT("MessageBox returned IDYES\n")));
                                                break;
                }
 
    return 0;
}
XXX_Read() calls UIP_MessageBox() twice; once as an OK/Cancel box and the other as a Yes/No/Cancel box. It doesn’t do anything glamorous with the return value, it just outputs a message associated with the value to the debug output.
So now we have a kernel mode driver that calls CeCallUserProc() and a user mode DLL. Now we just need an application that calls ReadFile(). The following is a simple test application:
int WINAPI WinMain(     HINSTANCE hInstance,
                                                                                HINSTANCE hPrevInstance,
                                                                                LPTSTR    lpCmdLine,
                                                                                int       nCmdShow)
{
                HANDLE hDriverShell;
                DWORD Data;
                DWORD BytesReturned;
 
                hDriverShell = CreateFile( TEXT("XXX1:"),
                                                GENERIC_READ|GENERIC_WRITE,
                                                0,
                                                NULL,
                                                OPEN_EXISTING,
                                                0,
                                                NULL);
 
                if( hDriverShell != INVALID_HANDLE_VALUE )
                {
                                ReadFile(hDriverShell, &Data, sizeof(Data), &BytesReturned, NULL);
                                CloseHandle( hDriverShell );
                }
}
When the test applications runs, the two message boxes will be displayed.
Tags:
Copyright © 2009 – Bruce Eitman
All Rights Reserved