This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| 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.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.