![]() |
Platforms, Frameworks & Libraries »
Mobile Development »
Dialogs and Windows
Intermediate
Window Wrapper for WinCE Win32 dialog appBy Bradley ManskeThis article continues to chronicle my quest to get a Dialog Based WinCE application that I can use |
C++, eVC3.0, Windows, Mobile, Visual-Studio, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

This article continues to chronicle my quest to get a Dialog Based WinCE application that I can use objects with. This article uses the sample from my earlier project, a Win32 "Hello World" app with a wrapper object, as the starting point.
Before beginning I copied the "Hello World" app and ran one of the copies through the Visual Studio Source Code Renamer by Niek Albers.
The creation of a dialog box was demonstrated in the "Hello World" example. CreateDialogParam
is called and the final parameter gets set to the this pointer. The rest of the parameters
are the same as a standard CreateDialog call. This seemed a logical way to proceed.
A new dialog box was created in the resource editor and the call to CreateWindow was
replaced with the call to CreateDialogParam. Now several things must change because of the
differences between windows and dialogs.
This is the message pump from the WinMain procedure of the "Hello World" example:
while ( ( status = GetMessage( &msg, NULL, 0, 0 ) ) != 0 ) { if ( status == -1 ) return -1; if ( !TranslateAccelerator( msg.hwnd, hAccelTable, &msg ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } }
Now that a dialog is created,
the call to TranslateAccelerator can be removed. For the dialog messages to get processed, a
call to IsDialogMessage must be made. If the message was not for the dialog, then a call to
TranslateMessage and DispatchMessage must be made to process it. The message
pump now looks like this:
while ( ( status = GetMessage( &msg, NULL, 0, 0 ) ) != 0 ) { if ( status == -1 ) return -1; if ( !IsDialogMessage( DialogWindow.hWnd, &msg ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } }
Methodically working my way from the top to the bottom...
Since this app uses CreatDialogParam, there is no need to create a class. The API calls
for creating dialogs create their own class, so the method MyRegisterClass has been removed.
The ability to name the window class has implications in the following sections.
The next problem was in InitInstance.
To avoid multiple instances, the app wizzard generated code that used FindWindow to search
for the class name set up by the previous instance.
hWnd = FindWindow( szWindowClass, szTitle ); if ( hWnd ) { SetForegroundWindow ((HWND) (((DWORD)hWnd) | 0x01)); return 0; }
FindWindow would return a handle to the window of the previous instance to bring it to the
top. This is extreamly important in Windows CE because the easiest way to switch back to a running
program is to tap on the link that starts the program.
Unfortuneatly, the class creation has been removed from
control of the app. The easy solution would be to use FindWindow to search for the window
title. This would impose the limitation of not ever changing the title of the dialog as is sometimes done
to indicate the file open or the program state. This left me searching for another solution.
I was directed to Joseph Newcomer's article on Avoiding Multiple Instances.
His article points out the many problems with the FindWindow method and suggests some
alternatives. At his use of SendMessageTimeout I had to pause. This API call doesn't
exist in CE, so I had to do a little inventing to make it work.
CreateMutex is used as he suggests. The operating system then insures that only one instance
of the code can create a Mutex with the same name. If the creation fails, then another instance is already
running and race conditions associated with the creation of 2 instances of the app are avoided. With
the knowledge that the Mutex creation failed, the app must exit.
This leaves the problem of how to get the first instance to the top of the Z-order. I chose to broadcast a registered message to all top level windows. Since the registered message is guaranteed to be unique, only the first instance will act on it. When it sees the message, it must bring itself to the top of the Z-order.
Register a message at startup.
const UINT UWM_ARE_YOU_ME = ::RegisterWindowMessage( _T("UWM_ARE_YOU_ME-{A17001C2-2614-4406-82CE-0691BB605948}") );
Try to create the mutex to check for a running app.
HANDLE hMutexOneInstance = ::CreateMutex( NULL, FALSE,
_T("LtWtDlg-{A17001C2-2614-4406-82CE-0691BB605948}") );
AlreadyRunning = ( ::GetLastError() == ERROR_ALREADY_EXISTS ||
::GetLastError() == ERROR_ACCESS_DENIED );
if ( AlreadyRunning )
{
// PostMessage will avoid hanging waiting for a response
BOOL bresult = PostMessage( HWND_BROADCAST, UWM_ARE_YOU_ME, 0, 0 );
return FALSE; //create mutex failed so exit the app
}
Now test the message in the WndProc and if it is yours, bring yourself to the top. This works
on my Pocket PC, but not on the x86 emulator supplied with EVC++ 3.0. The emulator limits the instances, but
will not bring the first instance to the top of the Z-order.
At this point I contacted Joseph Newcomer to get his opinion. This is his response, "This is another reasonable solution; particularly on CE, where there are probably not a lot of windows that would have to reject the message, the WND_BROADCAST would be quite low overhead."
When you read the documentation for the API calls for creating the bars at the top and the bottom you'll
feel like you're in Oz. For this discussion, the Bar with the Start button on it is the Start Menu. The
other bar will may contain a menu and it will be called the Menu Bar. I'm doing this because the API
documentation refers to the Command Bar, the Task Bar, The Menu Bar, etc... Worse yet, in the call to
SHInitDialog the Menu Bar refers to the Start Menu. In SHCreateMenuBar, Menu
Bar refers to the bar with the menu in it. In the call to SHFullScreen
the Task Bar refers to the Start Menu. The app wizzard creates a function called
CreateRpCommandBar that calls CreateMenuBar to create a Menu Bar and then
removes it with the CommandBar_Destroy API call. And so on...
All of this inconsistancy can be traced back to the evolution of the Windows CE operating system. For example, version 2 had the Start Bar at the bottom and the Menu Bar at the top. Version 3 reversed them and Menu Bars became Command Bars because of the new functionality.
Given that there are 2 of these bars, all, some or none may be desireable in the dialog box app. In the
constructor for CDialogWindow is the variable dialog_view. It can take on the
values 1 to 4 for showing each possible combination of Start Bar and Menu Bar.
// the method of creating the dialog depends on if you want the // top and bottom bars to display. switch ( dialog_view ) { case 1: // CASE 1: normal - Start Bar and Menu Bar // add a simple menu SHCreateMenuBar( &mbi ); hwndCB = mbi.hwndMB; // expand the dialog to full screen shidi.dwFlags |= SHIDIF_SIZEDLGFULLSCREEN; SHInitDialog( &shidi ); SetForegroundWindow( hDlg ); SHFullScreen( hDlg, SHFS_SHOWTASKBAR | SHFS_SHOWSIPBUTTON ); break; case 2: // CASE 2: Start Bar and NO Menu Bar // expand the dialog to full screen shidi.dwFlags |= SHIDIF_SIZEDLGFULLSCREEN; SHInitDialog( &shidi ); // expand the size of the dialog to cover the bottom bar GetWindowRect( hDlg, &rc ); rc.bottom += MENU_HEIGHT; MoveWindow( hDlg, rc.left, rc.top, rc.right, rc.bottom, TRUE ); SetForegroundWindow( hDlg ); SHFullScreen( hDlg, SHFS_SHOWTASKBAR | SHFS_HIDESIPBUTTON ); break; case 3: // CASE 3: NO Start Bar and Menu Bar // add a simple menu SHCreateMenuBar( &mbi ); hwndCB = mbi.hwndMB; // expand the dialog to full screen give dialog control over // the Menu Bar area. shidi.dwFlags |= SHIDIF_FULLSCREENNOMENUBAR; SHInitDialog( &shidi ); // expand the size of the dialog to cover the top bar GetWindowRect( hDlg, &rc ); rc.top -= MENU_HEIGHT; MoveWindow( hDlg, rc.left, rc.top, rc.right, rc.bottom, TRUE ); SetForegroundWindow( hDlg ); // push Menu Bar (alias Task Bar) to the bottom of the z-order SHFullScreen( hDlg, SHFS_HIDETASKBAR | SHFS_HIDESIPBUTTON ); break; case 4: // CASE 4: NO Start Bar and NO Menu Bar // expand the dialog to full screen give dialog control over // the Menu Bar area. shidi.dwFlags |= SHIDIF_FULLSCREENNOMENUBAR; SHInitDialog( &shidi ); // expand the size of the dialog to cover the top bar GetWindowRect( hDlg, &rc ); rc.top -= MENU_HEIGHT; MoveWindow( hDlg, rc.left, rc.top, rc.right, rc.bottom, TRUE ); SetForegroundWindow( hDlg ); // push Menu Bar (alias Task Bar) to the bottom of the z-order SHFullScreen( hDlg, SHFS_HIDETASKBAR | SHFS_HIDESIPBUTTON ); break; default: // the message was not processed // indicate if the base class handled it *pbProcessed = bWasProcessed; lResult = FALSE; return lResult; }
To summarize this, if you need a menu then call SHCreateMenuBar. The next step is to gain
control over the screen areas needed by calling SHInitDialog. After getting control, resize
the dialog box, if needed, to cover the area that you just got control of. Bring the dialog to the front
with SetForegroundWindow because if the window isn't in front then the final call will have
no effect. Finally, hide those screen elements that are not needed by calling SHFullScreen.
It hides the Start Bar by moving it to the bottom of the Z-order.
The final thought in dealing with how to show a dialog deals with redrawing the page. The
WM_ACTIVATE messgae was added to the dialog procedure so that the screen elements set by
SHFullScreen are reestablished when the dialog app main window becomes active again.
case WM_ACTIVATE: switch ( dialog_view ) { case 1: // CASE 1: normal - Start Bar (top) and Menu Bar (bottom) SHFullScreen( hDlg, SHFS_SHOWTASKBAR | SHFS_SHOWSIPBUTTON ); break; case 2: // CASE 2: Start Bar and NO Menu Bar SHFullScreen( hDlg, SHFS_SHOWTASKBAR | SHFS_HIDESIPBUTTON ); break; case 3: // CASE 3: NO Start Bar and Menu Bar SHFullScreen( hDlg, SHFS_HIDETASKBAR | SHFS_SHOWSIPBUTTON ); break; case 4: // CASE 4: NO Start Bar and NO Menu Bar SHFullScreen( hDlg, SHFS_HIDETASKBAR | SHFS_HIDESIPBUTTON ); break; default: // the message was not processed // indicate if the base class handled it *pbProcessed = bWasProcessed; lResult = FALSE; return lResult; } break;
Another thing that was lost when giving up the creation of the class, was the ability to specify the icons to the class structure. Now they must be loaded when processing the WM_INITDIALOG message. At first they may not be missed at all. There is almost no place where the loaded icon shows. There are Task Manager programs that do show the loaded icons. WISbar is a good example.
In this example, the dialog app has the icon with the red number 2 on it.
Here is how to load them:
// Load the Icons for the application HICON hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_LtWtDlg ) ); if ( hIcon ) { SendMessage( hDlg, WM_SETICON, WPARAM( ICON_SMALL ), LPARAM( hIcon ) ); SendMessage( hDlg, WM_SETICON, WPARAM( ICON_BIG ), LPARAM( hIcon ) ); }
The screen shot of this app shows something a little unexpected. In the lower right hand corner the SIP (Soft Input Panel) controls still show. No command bar had been created, so why did the SIP control show up on the display? The secret is to look at the controls on the dialog. If any controls were created with the Tab Stop attribute, then CE will automatically show the SIP. Once the controls on my dialog box had the Tab Stop attribute removed, then it behaved as expected.
That about does it. This should be a suitable start for a dialog box app if you have a single dialog. Multiple dialogs present a little different problem. Destroying the main application window without exiting is a bad idea. In this case, a regular window should be created then the window can create the dialogs as necessary. If there is any interest, maybe that is where I'll go next.
1. Steve Hanov, A Lightweight Windows Wrapper, C/C++ Users Journal, Aug 2000 pg.26
2. Conduits Website, http://www.conduits.com/ce/dev/, various Win CE samples.
3. Reliable Software Website, http://www.relisoft.com/win32/index.htm,
various Win 32 tutorials.
4. Joseph Newcomer Website, http://www.pgh.net/~newcomer/mvp_tips.htm,
various tips on Win 32 programming.
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+PgUp/PgDown to switch pages.
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 11 Jul 2002 Editor: Chris Maunder |
Copyright 2002 by Bradley Manske Everything else Copyright © CodeProject, 1999-2010 Web18 | Advertise on the Code Project |