![]() |
Platforms, Frameworks & Libraries »
Mobile Development »
Dialogs and Windows
Intermediate
Window Wrapper for WinCE Win32 "Hello World" appBy Bradley ManskeIn this article I take the standard Windows CE "Hello World" Win 32 application and wrap it with a class that can be inherited from. |
C++, eVC3.0, Windows, Mobile, Visual-Studio, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

I created a dialog based app to run on Windows wih MFC. The heart of this program was a few classes that didn't depend on MFC and I took care to make them easily portable, because I knew I wanted to try it on CE. I started Embedded Visual C 3.0 and looked for an App Wizard to help me create a Win 32 based dialog app. Since there was none to be found, I created a new project using the Win32 App Wizard. Just like the Win32 App Wizard in Visual Studio, there wasn't a class in sight.
I was about to suffer through the lack of objects when I happened to see an article by Steve Hanov in the C/C++ Users Journal that explained wrapping Windows with a class. So I set out to make 2 starter projects. The first a Win 32 "Hello World" app wrapped in an object and a Win 32 Dialog app wrapped in an object.
In this article I take the standard Windows CE "Hello World" Win 32 application and wrap it with a class that can be inherited from.
The root problem in creating a wrapper is the Windows Callback function. This function must be
a static procedure so the address can be supplied to the RegisterClassEx function.
To solve this the function must be a callback procedure.
LRESULT CALLBACK CBaseWindow::BaseWndProc( HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam )
Now that the Windows Procedure is part of a class, the problem is getting to the other members
the class. Static member functions can't access non-static member functions without a
this pointer. To supply the needed pointer to the window it can be included in the
CREATESTRUCT. The structure is automatically filled in when you make the
CreateWindow call (note the this pointer in the final argument).
hWnd = CreateWindow( szWindowClass, szTitle, WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, hInstance, (void *)this );
This CREATESTRUCT is availible in the callback procedure during the
WM_CREATE message as the lParam. The pointer is retrieved from the
structure and stored as user data using the Windows function SetWindowLong.
LRESULT CALLBACK CBaseWindow::BaseWndProc( HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam )
{
if ( msg == WM_CREATE )
{
CBaseWindow *pObj = reinterpret_cast<CBaseWindow *>
((long)((LPCREATESTRUCT)lParam)->lpCreateParams);
::SetWindowLong( hwnd, GWL_USERDATA,
(LONG)((LPCREATESTRUCT)lParam)->lpCreateParams );
. . .
On all subsequent messages from this window, a call to GetWindowLong will retrieve
the this pointer. Now you are free to access your entire class.
This method can be used when creating Dialog boxes also. In the case of a modal dialog box, the
windows callback procedure is specified in the DialogBox call. To get the object
pointer to the callback procedure use DialogBoxParam. The object pointer will be
the final parameter.
DialogBoxParam( GetInstance(), (LPCTSTR)IDD_ABOUTBOX, hWnd,
(DLGPROC)pAboutWindow->BaseWndProc, (long)pAboutWindow );
To retrieve the pointer, simply grab the lParam of the call to the base window procedure
during the WM_INITDIALOG message. Now save the pointer as you did before.
. . . if ( msg == WM_INITDIALOG ) { CBaseWindow *pObj = reinterpret_cast<CBaseWindow *>(lParam); ::SetWindowLong( hwnd, GWL_USERDATA, lParam ); . . .
A difference between a desktop version of Windows and the CE version is in the window creation
process. On the desktop WM_NCCREATE is the first message so the this
pointer should be captured during the its processing. In Windows CE this message
doesn't exist. The first message is WM_CREATE so it must be used.
Although this method is a little complex, it saves declaring static data structures that map window handles to pointers that point to the correct objects. As each window is created, it stores its own pointer to the object that owns it.
Most windows are created as more specialized cases of existing windows. Using C++, objects can utilize this relationship. The base window class provides a static function that calls a virtual function. Here is a look at the base window class declaration:
class CBaseWindow { public: CBaseWindow(){hInst=0;} ~CBaseWindow(){} HWND hWnd; // The main window handle BOOL DlgFlag; // True if object is a dialog window static LRESULT CALLBACK BaseWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ); protected: HINSTANCE hInst; // The current instance HWND hwndCB; // The command bar handle HINSTANCE GetInstance () const { return hInst; } virtual LRESULT WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, PBOOL pbProcessed ) { *pbProcessed = FALSE; return NULL; } };
Here you see the static function BaseWndProc that windows will use as a callback and the
virtual function WndProc that will get called as the message processor for the window.
In the case of the function for the base window, WndProc indicates that no messages are
are processed and returns.
Here is where the fun begins. The Base Class can't be used for a window by itself, but each window in
the program can now inherit the Base Class in the declaration of its own class. From here on out, all
of the details of managing this pointers to associate a code object to a window are hidden.
Here the main window class inherits from the base class.
class CMainWindow : public CBaseWindow { public: CMainWindow(){} ~CMainWindow(){} BOOL InitInstance( HINSTANCE hInstance, int nCmdShow ); protected: HWND CreateRpCommandBar( HWND ); ATOM MyRegisterClass( HINSTANCE, LPTSTR ); virtual LRESULT WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, PBOOL pbProcessed ); };
As the window needs become more specialized, you create a new class based on an old
class and change the behaviors to suit the new requirements. For example, the WndProc
of the CMainWindow class replaces the function by the same name in the
CBaseWindow class.
When you override the old function with
a new virtual function you should call the inherited class first to get the inherited traits then
change the processing in the way you need. Here the WndProc of the main window calls the
WndProc of the CBaseWindow class before doing its message processing.
LRESULT CMainWindow::WndProc( HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam,
PBOOL pbProcessed )
{
int wmId, wmEvent;
// call the base class first
LRESULT lResult = CBaseWindow::WndProc( hWnd, message,
wParam, lParam,
pbProcessed );
switch ( message )
{
. . .
Anyone who has looked at a standard Win32 program and has seen an ordinary Windows callback procedure knows to expect this line at the end of the case table.
lResult = DefWindowProc( hwnd, msg, wParam, lParam );
If the message is processed by one of the virtual functions in the chain then the
function will return FALSE and the DefWindowProc will not be called.
Rules are a little different if dealing with a dialog box. The DefWindowProc, if needed,
is called automatically for a dialog. If the message is processed by one of the functions in the chain
then TRUE is returned.
This is an annoyance and a source of errors (IMHO). So I have attempted to level the window and dialog box playing field a little by compensating in my base class. To do this I set a flag to remind myself if this is for a dialog or not. This way all of the window callbacks can be coded the same. Here is the static base window callback function.
LRESULT CALLBACK CBaseWindow::BaseWndProc( HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam )
{
// check to see if a copy of the 'this' pointer needs to be saved
if ( msg == WM_CREATE )
{
CBaseWindow *pObj = reinterpret_cast<CBaseWindow *>
((long)((LPCREATESTRUCT)lParam)->lpCreateParams);
::SetWindowLong( hwnd, GWL_USERDATA,
(LONG)((LPCREATESTRUCT)lParam)->lpCreateParams );
pObj->hWnd = hwnd;
pObj->DlgFlag = FALSE;
if ( pObj->hInst == 0 )
pObj->hInst = MainWindow.hInst;
}
if ( msg == WM_INITDIALOG )
{
CBaseWindow *pObj = reinterpret_cast<CBaseWindow *>(lParam);
::SetWindowLong( hwnd, GWL_USERDATA, lParam );
pObj->hWnd = hwnd;
pObj->DlgFlag = TRUE;
pObj->hInst = MainWindow.hInst;
}
BOOL bProcessed = FALSE;
LRESULT lResult;
// Retrieve the pointer
CBaseWindow *pObj = WinGetLong<CBaseWindow *>( hwnd, GWL_USERDATA );
// call the virtual window procedure for the object stored in the
// windows user data
if ( pObj )
lResult = pObj->WndProc( hwnd, msg, wParam, lParam, &bProcessed );
else
return ( pObj->DlgFlag ? FALSE : TRUE ); // message not processed
if ( pObj->DlgFlag )
return bProcessed; // processing a dialog message return TRUE
// if processed
else
if ( !bProcessed )
// If message was unprocessed and not a dialog, send it back to Windows.
lResult = DefWindowProc( hwnd, msg, wParam, lParam );
return lResult; // processing a window message return FALSE
// if processed
} /* BaseWndProc */
To get the object registered as the callback function for a window, the BaseWndProc
is set as the Windows callback function pointer when the class is registered.
ATOM CMainWindow::MyRegisterClass( HINSTANCE hInstance,
LPTSTR szWindowClass )
{
WNDCLASS wc;
wc.lpfnWndProc = (WNDPROC)BaseWndProc;
. . .
To wrap a Dialog Box with an object, the BaseWndProc is sent as the Windows callback
function pointer in the call to DialogBoxParam.
CAboutWindow *pAboutWindow = new( CAboutWindow ); DialogBoxParam( GetInstance(), (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)pAboutWindow->BaseWndProc, (long)pAboutWindow ); delete pAboutWindow;
I've covered how to get the code for encapsulating Windows messages into objects at a basic level on a Windows CE device. Most of this should apply to the desktop versions of Windows also. This doesn't wrap everything into an object, but associating a window with an object is one of the harder and most common problems. As a result, I have the first starter application I wanted. A standard Windows CE "Hello World" Win 32 application wrapped in a class.
I plan to continue by telling how I used this project as the starting point to get a dialog based application.
One handy tip: Look for the Visual Studio Project Renamer by Niek Albers here on CodeProject. It works well on Embedded Visual C++ 3.0 projects also.
| 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 Web19 | Advertise on the Code Project |