|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionI 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 Windows callback problem
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
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
hWnd = CreateWindow( szWindowClass, szTitle, WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, hInstance, (void *)this );
This
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
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
DialogBoxParam( GetInstance(), (LPCTSTR)IDD_ABOUTBOX, hWnd,
(DLGPROC)pAboutWindow->BaseWndProc, (long)pAboutWindow );
To retrieve the pointer, simply grab the . . . 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 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. InheritanceMost 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
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 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
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
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 )
{
. . .
Windows vs DialogsAnyone 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
Rules are a little different if dealing with a dialog box. The 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 */
Using the Base Window Procedure
To get the object registered as the callback function for a window, the
ATOM CMainWindow::MyRegisterClass( HINSTANCE hInstance,
LPTSTR szWindowClass )
{
WNDCLASS wc;
wc.lpfnWndProc = (WNDPROC)BaseWndProc;
. . .
To wrap a Dialog Box with an object, the CAboutWindow *pAboutWindow = new( CAboutWindow ); DialogBoxParam( GetInstance(), (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)pAboutWindow->BaseWndProc, (long)pAboutWindow ); delete pAboutWindow; ConclusionI'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. References
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||