|
Asserts On The PocketPC
Submitted by |
Ive frequented Flipcode for over 4 years now, but never submitted anything.
Figured it was about time.
Ive been working on the PocketPC for the last 4 months and one of the things I
kept running into was a lack of a decent assert. I finally broke down and
decided to write my own collection of asserts and figured others on Flipcode
might find them useful.
First a little bit of background on the platform and its current implementation
of assert. The PocketPC lacks a large portion of the C-stdlib. Things like
abort, assert, fread, etc, dont exist in the PocketPC libraries. To further
complicate things, the PocketPC targets 3 significantly different processors,
MIPS, SHx, and StrongARM so doing a simple Assert macro of __asm{ int 3 }
doesnt work. However, Microsoft has provided a macro for inserting break
points thats platform independent, DebugBreak. This macro is defined in
winbase.h or kfuncs.h. Unfortunately DebugBreak does nothing unless a debugger
is attached.
On the PocketPC debugging is SLOW unless you have a model with a CompactFlash
slot and own a CF Ethernet adapter. I wanted something that would warn me (or a
tester) visually of asserts and stop the program if desired with out requiring
the debugger to be attached. I figured logging to a file would prove useful as
well.
To use my implementation you must first Initialize the assert module. This is
done through the INITASSERT macro. Its not necessary, but if you want a log to
be kept or your Assert message boxes to be modal then youll need to do this.
There are two macros provided for the actual assert, ASSERT and ASSERTE. ASSERT
converts the expression that was evaluated into a string and outputs that in the
message box while ASSERTE takes a second parameter for an error message to be
displayed instead of the expression.
The logging looks to see if the log file exists already. If the file exists
then it appends otherwise itll create the log file. Logging occurs regardless
of the Assert being ignored or not. Logging doesnt occur if the assert
expression doesnt evaluate to false however. If someone does decide they want
to log passed asserts it would be fairly easy to create another macro or modify
the included ones to do so.
Lastly, there is zero overhead when compiled in release mode.
There are no restrictions on how this code is used.
Comments, questions, etc are welcome.
Thanks,
-Lucas Hosed Goodwin
P.S. I KNOW theres going to be something mentioned about using C++ exception
handling, etc. Heres a nice little tidbit. PocketPCs dont support C++
Exception Handling. Its Win32 Structured Exception Handling or bust. Id love
to see a TOTD or COTD covering Win32 SEH if anyones interested ;)
|
Currently browsing [StaticAssert.zip] (3,328 bytes) - [StaticAssert.h] - (3,745 bytes)
/******************************************************************************
File Name: StaticAssert.h
Description: Header for Debug Macros and Support Functions.
Author: Lucas Goodwin (lucasg@gorge.net)
Project: Pocket PC StaticLib
Copyright (C) 2001 Goodwin Games -- All Rights Reserved
INITASSERT - Must be called before any asserts take place with in WinMain.
Recommended that it be placed immediately following main window
creation. If an invalid window handle is passed then assert
message box will not be modal.
ASSERT - If expression evaluates to false...
Breaks to debugger if attached, displays a message window
indicating the expression that failed, in what file, and what
line. If not ignored then program terminates with code -1.
The assert is appended to log file indicated by INITASSERT
regardless of ignore.
ASSERTE - Behaves same as ASSERT except expression is replaced by the
passed error message.
EX:
INITASSERT( hWnd, "/assert.log" );
ASSERT( 1 == 0 );
ASSERTE( 1 == 0, "Just testing ASSERTE" );
******************************************************************************/
#if !defined(__STATICLIB_STATICASSERT__)
#define __STATICLIB_STATICASSERT__
#include "StaticIncludes.h"
//-----------------------------------------------------------------------------
//Assert Macros
//-----------------------------------------------------------------------------
// Clear Macros
#ifdef ASSERT
#undef ASSERT
#undef ASSERTE
#undef INITASSERT
#endif //ASSERT
//Redefine Macros
#if !defined(NDEBUG)
//INITASSERT
#define INITASSERT(hwnd, logfile) SL_Assert::init_assert(hwnd, logfile)
//ASSERT - Output expression as string in Assert MsgBox
#define ASSERT(exp) \
do { \
if( !(exp) ) \
{ \
DebugBreak(); \
SL_Assert::assert(exp, TEXT(#exp), TEXT(__FILE__), \
TEXT(__TIMESTAMP__), __LINE__); \
} \
} while (0)
//ASSERTE - Output error message in Assert MsgBox
#define ASSERTE(exp, errmsg) \
do { \
if( !(exp) ) \
{ \
DebugBreak(); \
SL_Assert::assert(exp, TEXT(errmsg), TEXT(__FILE__), \
TEXT(__TIMESTAMP__), __LINE__); \
} \
} while (0)
#else
#define INITASSERT(hwnd, logfile) void(0)
#define ASSERT(exp) void(0)
#define ASSERTE(exp, errmsg) void(0)
#endif // DEBUG
//-----------------------------------------------------------------------------
// Assert Support Functions/Objects
//-----------------------------------------------------------------------------
#if !defined(NDEBUG)
namespace SL_Assert {
//---------------------------------
//Namespace Globals
//---------------------------------
static HWND g_hAssertMainWnd = NULL; //Handle to main window of app
static TCHAR g_szFileName[512]; //String holding file name
//---------------------------------
//Namespace Objects
//---------------------------------
class AssertLogFile
{
HANDLE hFile;
public:
AssertLogFile( TCHAR *FileName );
~AssertLogFile();
BOOL Append( char *szLogMessage );
};
//---------------------------------
//Namespace Functions
//---------------------------------
//init_assert - Set Main Window and Log File Name for use in assert
void init_assert( HWND hWnd, TCHAR *LogFileName );
//assert - Display error message box, log assert, and exit (if so desired)
void assert( int exp, TCHAR *errmsg, TCHAR *filename, TCHAR *timestamp,
int linenum );
//log_assert - Log assert message to log file, close File-Map object
void log_assert( TCHAR *assertmsg );
}
#endif //NDEBUG
#endif //__STATICLIB_STATICASSERT__ |
|
Currently browsing [StaticAssert.zip] (3,328 bytes) - [StaticAssert.cpp] - (4,650 bytes)
/******************************************************************************
File Name: StaticAssert.cpp
Description: Implementation for Debug Macros and Support Functions.
Author: Lucas Goodwin (lucasg@gorge.net)
Project: Pocket PC StaticLib
Copyright (C) 2001 Goodwin Games -- All Rights Reserved
See StaticAssert.h for details
******************************************************************************/
#include "StaticAssert.h"
//-----------------------------------------------------------------------------
// Assert Support Functions
//-----------------------------------------------------------------------------
#if !defined(NDEBUG)
#define ASSERT_MSG_LEN 1024
//---------------------------------
//AssertLogFile Methods
//---------------------------------
//AssertLogFile CTOR
SL_Assert::AssertLogFile::AssertLogFile( TCHAR *FileName )
{
//Open Log file
hFile = CreateFile( FileName, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL );
//A file was opened?
if( INVALID_HANDLE_VALUE == hFile )
{
return;
}
//If already exists, lets prepare to append by moving to end of file
if( ERROR_ALREADY_EXISTS == GetLastError() )
{
DWORD dwPtr = SetFilePointer( hFile, 0, NULL, FILE_END );
//Did we move properly?
if( 0xFFFFFFFF == dwPtr )
{
//Oops, something messed up, close the file and set hFile to
//invalid handle
CloseHandle( hFile );
//Closed the file, invalidate handle
hFile = INVALID_HANDLE_VALUE;
}
}
}
//AssertLogFile DTOR
SL_Assert::AssertLogFile::~AssertLogFile()
{
//If handle is valid then, append new data and close the file
if( INVALID_HANDLE_VALUE != hFile )
{
//Append data to file by extending the EOF pointer
SetEndOfFile( hFile );
//Close file
CloseHandle( hFile );
}
}
//AssertLogFile Append
BOOL SL_Assert::AssertLogFile::Append( char *szLogMessage )
{
BOOL Result = FALSE;
if( INVALID_HANDLE_VALUE != hFile )
{
DWORD BytesWritten = 0;
Result = WriteFile( hFile, (LPCVOID)szLogMessage,
strlen(szLogMessage), &BytesWritten, NULL );
}
return Result;
}
//---------------------------------
//Functions
//---------------------------------
//init_assert
void SL_Assert::init_assert( HWND hWnd, TCHAR *LogFileName )
{
g_hAssertMainWnd = hWnd;
wsprintf( g_szFileName, TEXT("%s"), LogFileName);
}
//assert
void SL_Assert::assert( int exp, TCHAR *errmsg, TCHAR *filename,
TCHAR *timestamp, int linenum )
{
if( !(exp) )
{
int Result;
TCHAR szErrMsg[ASSERT_MSG_LEN];
//Error Message for Log
wsprintf( szErrMsg, TEXT("%s\nFile: %s\nLine: %i\nCompiled: %s\n\n"),
errmsg, filename, linenum, timestamp );
//Log Assert
log_assert( szErrMsg );
//Error Message for Message Box
wsprintf( szErrMsg, TEXT("%s\n\nFile: %s\nLine: %i\n\nIgnore?"),
errmsg, filename, linenum );
//Popup Message box...
Result = MessageBox( g_hAssertMainWnd, szErrMsg,
TEXT( "ASSERTION FAILURE" ), MB_YESNO
| MB_ICONERROR | MB_SETFOREGROUND
| MB_DEFBUTTON1 | MB_APPLMODAL );
//Respond based on User input to AssertBox
switch( Result )
{
//Assert ignored, continue
case IDYES:
break;
//Assert not ignored, Exit
case IDNO:
// Terminate Application
exit(-1);
break;
}
}
}
//log_assert
void SL_Assert::log_assert( TCHAR *assertmsg )
{
AssertLogFile Log( g_szFileName );
char *ansi_log_break = "====================\n";
char ansi_assert_msg[ASSERT_MSG_LEN];
SYSTEMTIME CurSysTime;
//Append message seperator
Log.Append( ansi_log_break );
//Time stamp this entry
GetSystemTime( &CurSysTime );
sprintf( ansi_assert_msg, "%i:%i:%i - %i/%i/%i\n",
CurSysTime.wHour, CurSysTime.wMinute, CurSysTime.wSecond,
CurSysTime.wDay, CurSysTime.wMonth, CurSysTime.wYear );
Log.Append( ansi_assert_msg );
//Append message seperator
Log.Append( ansi_log_break );
//PocketPC - All strings are unicode, lets switch to Ansi
// Makes reading log file on PC easier
int Result = WideCharToMultiByte( CP_ACP, NULL, (LPCWSTR)assertmsg,
-1, (LPSTR)ansi_assert_msg,
ASSERT_MSG_LEN, NULL, NULL );
//Did I convert from Unicode to Ansi?
if( !Result )
{
//Didn't convert properly, just output unicode string
Log.Append( (char *)assertmsg );
}
else
{
//Converted properly, append converted Ansi string to Log file
Log.Append( ansi_assert_msg );
}
}
#endif //NDEBUG
|
|
The zip file viewer built into the Developer Toolbox made use
of the zlib library, as well as the zlibdll source additions.
|