Click here to Skip to main content
15,891,372 members
Articles / Programming Languages / Objective C

Running State Machine Based Win32/WinCE Programs

Rate me:
Please Sign up or sign in to vote.
4.37/5 (9 votes)
17 May 20065 min read 70.8K   1.3K   35  
This article describes how to run state machine application framework based Win32/WinCE programs using the window message hooking technology. (Open source project)
/**********************************************************************
UML StateWizard provides its software under the GPL License and 
zlib/libpng License for open source projects.

Email us at info@intelliwizard.com for any information, suggestions and 
feature requestions.

Home Page: http://www.intelliwizard.com
*************************************************************************/

/* =============================================================================
 * Filename:    sme_debug.cpp
 * 
 * Copyright  IntelliWizard Inc. www.intelliwizard.com
 * All rights reserved.
 * -----------------------------------------------------------------------------
 * General description of this file:
 *
 * State machine engine debug support global header file.
 * -----------------------------------------------------------------------------
 *                               Revision History
 * -----------------------------------------------------------------------------
 * Version   Date      Author          Revision Detail
 * 1.0.0    2004/11/26                 Initial
 *		    2004/11/26                 $ Fix memory free bug.
 *          2005/9/6                   Module Tracer
			2005/12/26				   Merge Unicode edition with ASCII edition
 * ===========================================================================*/

#include "stdafx.h"
#include "SrvAgent.h"

#include <stdio.h> 
#include <stdarg.h>
#include <memory.h>
#include "sme_debug.h"

static SME_OUTPUT_DEBUG_ASCII_STR_PROC_T g_fnOutputDebugAsciiStr = NULL; 
static SME_OUTPUT_DEBUG_UNICODE_STR_PROC_T g_fnOutputDebugUnicodeStr = NULL; 
static SME_MEM_OPR_HOOK_T g_fnMemOprHook = NULL;

#define MAX_MODULE_NUM32	4
#define MAX_MODULE_NUM		128 
static unsigned long g_ModuleToLog[MAX_MODULE_NUM32] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; 

static BOOL g_bStateTrackinTree =TRUE;
static BOOL g_bStateTrackinOutputWin =TRUE;

#ifdef _WIN32_WCE
HWND g_hWorkspaceWnd=NULL;
#else
extern HWND g_hWorkspaceWnd;
#endif

#define ID_COPYDATA_STATE_TRACK 1100
#define ID_COPYDATA_DEBUG_STR	1101

static BOOL OutputDebugAsciiStrToDS(const char * sDebugStr);
static BOOL OutputDebugUnicodeStrToDS(const unsigned short* sDebugStr);

/*******************************************************************************************
* DESCRIPTION: Turn on the debug string tracer for given module.
* INPUT:  
*  1) nModuleID: The module ID, ranging from 0 to 127 
* OUTPUT: None.
* NOTE: 
*******************************************************************************************/
void SmeTurnOnModuleTracer(int nModuleID)
{
	g_ModuleToLog[nModuleID>>5] |= (1 << (nModuleID%32));
}

/*******************************************************************************************
* DESCRIPTION: Turn off the debug string tracer of given module.
* INPUT:  
*  1) nModuleID: The module ID, ranging from 0 to 127 
* OUTPUT: None.
* NOTE: 
*******************************************************************************************/
void SmeTurnOffModuleTracer(int nModuleID)
{
	g_ModuleToLog[nModuleID>>5] &= ~(1 << (nModuleID%32));
}

/*******************************************************************************************
* DESCRIPTION: Turn on all module tracers.
* INPUT:  
*  None.
* OUTPUT: None.
* NOTE: 
*******************************************************************************************/
void SmeTurnOnAllModuleTracers()
{
	int i;
	for (i=0; i<MAX_MODULE_NUM32; i++)
		g_ModuleToLog[i] = 0xFFFFFFFF;
}

/*******************************************************************************************
* DESCRIPTION: Turn on all module tracers.
* INPUT:  
*  None
* OUTPUT: None.
* NOTE: 
*******************************************************************************************/
void SmeTurnOffAllModuleTracers()
{
	int i;
	for (i=0; i<MAX_MODULE_NUM32; i++)
		g_ModuleToLog[i] = 0x0;
}
/*******************************************************************************************
* DESCRIPTION: Output debug string through user defined function.
* INPUT:  
*  1) sFormat: The format of output string 
*  2) ...: The variable argument list.
* OUTPUT: None.
* NOTE: 
*******************************************************************************************/
SME_OUTPUT_DEBUG_ASCII_STR_PROC_T	SmeSetOutputDebugAsciiStrProc(SME_OUTPUT_DEBUG_ASCII_STR_PROC_T fnProc)
{
	SME_OUTPUT_DEBUG_ASCII_STR_PROC_T	fnOldProc = g_fnOutputDebugAsciiStr;
	g_fnOutputDebugAsciiStr = fnProc;
	return fnOldProc;
}

SME_OUTPUT_DEBUG_UNICODE_STR_PROC_T	SmeSetOutputDebugUnicodeStrProc(SME_OUTPUT_DEBUG_UNICODE_STR_PROC_T fnProc)
{
	SME_OUTPUT_DEBUG_UNICODE_STR_PROC_T	fnOldProc = g_fnOutputDebugUnicodeStr;
	g_fnOutputDebugUnicodeStr = fnProc;
	return fnOldProc;
}
/*******************************************************************************************
* DESCRIPTION: Output debug string through user defined function.
* INPUT:
*  1) nModuleID: Range from 0 to (32*MAX_MODULE_NUM32 - 1), which identify a module.  
*  2) sFormat: The format of output string in ASCII format. 
*  3) ...: The variable argument list.
* OUTPUT: None.
* NOTE: 
* 1) No matter, _UNICODE is defined or not, the format of output string is in ASCII format. 
* It will be easier for developer to print Unicode string. Nevertheless, the string argument 
* should be in Unicode format.
* 2) If the given module's tracer is turned on, print the debug string. If module id is -1, print it.  
*******************************************************************************************/
#define SME_MAX_STR_BUF_LEN		512

void SmeUnicodeTrace(int nModuleID, const unsigned short * wsFormat, ...)
{
	va_list   ArgList;
	static unsigned short sBuf[SME_MAX_STR_BUF_LEN + 1]; /* ASCII/Unicode string buffer */

	if (nModuleID>=MAX_MODULE_NUM) return;
	if (nModuleID!=-1 && (g_ModuleToLog[nModuleID>>5] & (1 << (nModuleID%32))) == 0) return;

	va_start(ArgList, wsFormat);

	vswprintf(sBuf, wsFormat, ArgList);
	//SME_ASSERT(vswprintf(sBuf, wsFormat, ArgList) <= SME_MAX_STR_BUF_LEN);

	if (g_fnOutputDebugUnicodeStr)
		(*g_fnOutputDebugUnicodeStr)(sBuf);
	else
		OutputDebugUnicodeStrToDS(sBuf);

	va_end(ArgList);

	return;
}

void SmeUnicodeTraceAscFmt(int nModuleID, const char * sFormat, ...)
{
	va_list   ArgList;
	static unsigned short sBuf[SME_MAX_STR_BUF_LEN + 1]; /* ASCII/Unicode string buffer */

	static unsigned short wsFormatBuf[SME_MAX_STR_BUF_LEN + 1];
	unsigned int i;

	if (nModuleID>=MAX_MODULE_NUM) return;
	if (nModuleID!=-1 && (g_ModuleToLog[nModuleID>>5] & (1 << (nModuleID%32))) == 0) return;

	va_start(ArgList, sFormat);

	//SME_ASSERT(strlen(sFormat) <= SME_MAX_STR_BUF_LEN);
	for (i = 0; i <= strlen(sFormat); i++)
		wsFormatBuf[i] = sFormat[i];

	vswprintf(sBuf, wsFormatBuf, ArgList);
	//SME_ASSERT(vswprintf(sBuf, wsFormatBuf, ArgList) <= SME_MAX_STR_BUF_LEN);

	if (g_fnOutputDebugUnicodeStr)
		(*g_fnOutputDebugUnicodeStr)(sBuf);
	else
		OutputDebugUnicodeStrToDS(sBuf);

	va_end(ArgList);

	return;
}

/*******************************************************************************************
* DESCRIPTION: Output debug string through user defined function.
* INPUT:  
*  1) sFormat: The format of output string in ASCII format. 
*  2) ...: The variable argument list. Text string should be in ASCII format. 
* OUTPUT: None.
* NOTE: 
* 1) No matter, _UNICODE is defined or not, the format of output string and text string argument should be in ASCII format. 
* It will be easier for developer to print ACSII string if _UNICODE is defined.
* 2) If the given module's tracer is turned on, print the debug string. If module id is -1, print it.  
*******************************************************************************************/
void SmeAsciiTrace(int nModuleID, const char * sFormat, ...)
{
	va_list   ArgList;
	static char sBuf[SME_MAX_STR_BUF_LEN + 1]; /* ASCII string buffer */

	if (nModuleID>=MAX_MODULE_NUM) return;
	if (nModuleID!=-1 && (g_ModuleToLog[nModuleID>>5] & (1 << (nModuleID%32))) == 0) return;

	va_start(ArgList, sFormat);

	vsprintf(sBuf, sFormat, ArgList);
	//SME_ASSERT(vsprintf(sBuf, sFormat, ArgList) <= SME_MAX_STR_BUF_LEN);

	va_end(ArgList);

	if (g_fnOutputDebugAsciiStr)
		(*g_fnOutputDebugAsciiStr)(sBuf);
	else
		OutputDebugAsciiStrToDS(sBuf);
	return;
}

/*******************************************************************************************
* The memory block information nodes will linked as a stack. The header will be g_pMemBlkList.
* The pNext property in SME_MEM_BLK_INFO_T will pointer to the previous node.
*  NULL <--- Node.pNext  <--- Node.pNext  <--- g_pMemBlkList
*******************************************************************************************/
#define SME_NO_MANS_LAND 0xFDFDFDFD
#define SME_NEW_MEM_BLK 0xCD

struct SME_MEM_BLK_INFO_T
{
	void* pUserData;
	struct SME_MEM_BLK_INFO_T *pNext;
	unsigned int nSize;
	const char * sFile;
	int nLine;
} SME_MEM_BLK_INFO_T;

static struct SME_MEM_BLK_INFO_T *g_pMemBlkList = NULL;
static int g_nMemBlkNum=0;
static int g_nTotalAllocMem=0;

/*******************************************************************************************
* DESCRIPTION:  State machine engine memory allocation function in debug version. 
* INPUT: nSize: Memory size;
* OUTPUT: Allocated memory pointer.
* NOTE: 
*   The new user data is set to SME_NEW_MEM_BLK and no man land is set to SME_NO_MANS_LAND.
*******************************************************************************************/
void * SmeMAllocDebug(unsigned int nSize, const char * sFile, int nLine)
{
	void *p;
	unsigned long *pLong;
	struct SME_MEM_BLK_INFO_T *pMemBlkInfo;
	
	/* Call pre-allocation hook function. Allocation abort if hook function returns FALSE. */
	if (g_fnMemOprHook)
		if ((*g_fnMemOprHook)(SME_HOOK_PRE_ALLOC, NULL, nSize, sFile, nLine) == FALSE)
			return NULL;

	/* Adjust to be 4-byte boundary aligned. */
	nSize = ((nSize + 3) >> 2)<<2; 
	p = SmeMAllocRelease(nSize + sizeof(unsigned long));
	if (p==NULL) return NULL;

	/* Set new user data and no man land. */
	memset(p, SME_NEW_MEM_BLK, nSize);
	pLong = (unsigned long *)((unsigned char*)p + nSize);
	*pLong = SME_NO_MANS_LAND; 

	pMemBlkInfo = (struct SME_MEM_BLK_INFO_T *)SmeMAllocRelease(sizeof(struct SME_MEM_BLK_INFO_T));
	if (pMemBlkInfo==NULL) return p;

	/* Push pMemBlkInfo to the memory block stack. */
	pMemBlkInfo->pUserData = p;
	pMemBlkInfo->nSize = nSize;
	pMemBlkInfo->sFile = sFile;
	pMemBlkInfo->nLine = nLine;
	pMemBlkInfo->pNext = g_pMemBlkList;
	g_pMemBlkList = pMemBlkInfo;
	g_nMemBlkNum++;
	g_nTotalAllocMem+=pMemBlkInfo->nSize;

	/* Call post-allocation hook function. */
	if (g_fnMemOprHook)
		(*g_fnMemOprHook)(SME_HOOK_POST_ALLOC, p, nSize, sFile, nLine);

	return p;
}

/*******************************************************************************************
* DESCRIPTION:  State machine engine memory free function in release version. 
* INPUT: pUserData: Memory block pointer;
* OUTPUT: The result depends on user defined memory free function.
* NOTE: It will check no man land to detect whether memory overwriting occurs.
*   
*******************************************************************************************/
BOOL SmeMFreeDebug(void * pUserData, const char * sFile, int nLine)
{
	struct SME_MEM_BLK_INFO_T *pMemBlkInfo = g_pMemBlkList;
	struct SME_MEM_BLK_INFO_T *pPre = g_pMemBlkList;
	unsigned long *pLong;
	
	/* Get memory block information from stack. */
	while (pMemBlkInfo!=NULL && pMemBlkInfo->pUserData != pUserData)
	{
		pPre = pMemBlkInfo;
		pMemBlkInfo = pMemBlkInfo->pNext; 
	};

	if (pMemBlkInfo == NULL)
	{
		SME_ASCII_TRACE(-1, "Error! Try to delete an unknown or freed memory block at %s (%d).\n",
			sFile, nLine);
		return FALSE;
	};

	/* Call pre-free hook function. */
	if (g_fnMemOprHook)
		(*g_fnMemOprHook)(SME_HOOK_PRE_FREE, 
			pMemBlkInfo->pUserData, 
			pMemBlkInfo->nSize, 
			pMemBlkInfo->sFile, 
			pMemBlkInfo->nLine);

	/* Check no man land. */
	pLong = (unsigned long *)((unsigned char*)pMemBlkInfo->pUserData + pMemBlkInfo->nSize);
	if (*pLong != SME_NO_MANS_LAND)
	{
		SME_ASCII_TRACE(-1,"Error! The memory block is damaged, which is allocated at %s (%d).\n",
			pMemBlkInfo->sFile, pMemBlkInfo->nLine);
		return FALSE;
	}

	/* Call post-free hook function. */
	if (g_fnMemOprHook)
		(*g_fnMemOprHook)(SME_HOOK_POST_FREE, 
			pMemBlkInfo->pUserData, 
			pMemBlkInfo->nSize, 
			pMemBlkInfo->sFile, 
			pMemBlkInfo->nLine);

	/* Remove this memory block information node from list. */
	if (pMemBlkInfo == g_pMemBlkList)
		/* $$ What to remove is loacted in the header. */
		g_pMemBlkList = g_pMemBlkList->pNext;
	else
		pPre->pNext = pMemBlkInfo->pNext;

	g_nMemBlkNum--;
	g_nTotalAllocMem-=pMemBlkInfo->nSize;

	SmeMFreeRelease(pMemBlkInfo);
	
	return SmeMFreeRelease(pUserData);
}

/*******************************************************************************************
* DESCRIPTION:  Reset memory block information list. 
* INPUT: None;
* OUTPUT: None.
* NOTE: It will release all allocated memory blocks including user data and memory information blocks.
*   
*******************************************************************************************/
BOOL SmeDbgInit()
{
	struct SME_MEM_BLK_INFO_T *pMemBlkInfo = g_pMemBlkList;
	if (g_pMemBlkList == NULL) return FALSE;
	while (g_pMemBlkList!=NULL)
	{
		pMemBlkInfo = g_pMemBlkList;
		g_pMemBlkList = g_pMemBlkList->pNext; 

		SmeMFreeRelease(pMemBlkInfo->pUserData);
		SmeMFreeRelease(pMemBlkInfo);
	}

	g_nMemBlkNum=0;
	g_nTotalAllocMem=0;
	return TRUE;
}
/*******************************************************************************************
* DESCRIPTION: This function installs a user defined hook function and return the old hook function.
*  Hook function is called every time memory is pre-allocated, post-allocated, pre-freed, or post-freed. 
* INPUT:  
*  pMemOprHook: New memory operation hook function. 
* OUTPUT: Old memory operation hook function. 
* 
* NOTE: 
*******************************************************************************************/
SME_MEM_OPR_HOOK_T SmeSetMemOprHook(SME_MEM_OPR_HOOK_T pMemOprHook)
{
	SME_MEM_OPR_HOOK_T fnOld = g_fnMemOprHook;
	g_fnMemOprHook = pMemOprHook;
	return fnOld;
}

/*******************************************************************************************
* DESCRIPTION: This function enumerates all allocated memory blocks. The fnEnumMemCallBack 
*  function is called once per allocated memory block and is passed the information for each block.
* INPUT:  
*  SME_ENUM_MEM_CALLBACK_T fnEnumMemCallBack: Memory block enumeration call back function. 
* OUTPUT: 
* 
* NOTE: 
* typedef BOOL (*SME_ENUM_MEM_CALLBACK_T)( void *pUserData, unsigned int nSize, const unsigned char *sFileName, int nLineNumber);
* If the function returns TRUE, the enumeration will continue. If the function returns FALSE, the enumeration will stop.
*******************************************************************************************/
BOOL SmeEnumMem(SME_ENUM_MEM_CALLBACK_T fnEnumMemCallBack)
{
	struct SME_MEM_BLK_INFO_T *pMemBlkInfo = g_pMemBlkList;

	if (fnEnumMemCallBack==NULL) return FALSE;

	/* Get memory block information from stack. */
	while (pMemBlkInfo!=NULL)
	{
		if (FALSE == (*fnEnumMemCallBack)(pMemBlkInfo->pUserData, 
			pMemBlkInfo->nSize, 
			pMemBlkInfo->sFile, 
			pMemBlkInfo->nLine))
			break;
		pMemBlkInfo = pMemBlkInfo->pNext; 
	};

	return TRUE;
}

/*******************************************************************************************
* DESCRIPTION: This function print the total allocated memory block size. 
* INPUT:  
*  None. 
* OUTPUT: 
* NOTE: 
*	It will be used to detect memory leaks, if developer call it when application enter some status.
*******************************************************************************************/
void SmeAllocMemSize()
{
	SME_ASCII_TRACE(-1,"Total Allocated Memory Size:%d bytes\n", g_nTotalAllocMem);
}

/*******************************************************************************************
* DESCRIPTION: This function dumps the allocated memory block information list. 
* INPUT:  
*  None. 
* OUTPUT: 
* NOTE: 
*	It will be used to detect memory leaks, if developer call it when application enter some status.
*******************************************************************************************/
void SmeDumpMem()
{
	struct SME_MEM_BLK_INFO_T *pMemBlkInfo = g_pMemBlkList;
	SME_ASCII_TRACE(-1,"Memory dumping:\n");
	/* Get memory block information from stack. */
	while (pMemBlkInfo!=NULL)
	{
		SME_ASCII_TRACE(-1,"The memory block %X with size of %d is allocated at %s (%d).\n",
			pMemBlkInfo->pUserData, 
			pMemBlkInfo->nSize, 
			pMemBlkInfo->sFile, 
			pMemBlkInfo->nLine);
		pMemBlkInfo = pMemBlkInfo->pNext; 
	};
	SME_ASCII_TRACE(-1,"In total, %d memory block(s) is(are) allocated.\n", g_nMemBlkNum);
	return;
}

void SmeEnableStateTracking(BOOL bStateTree, BOOL bOutputWin)
{
	g_bStateTrackinTree = bStateTree;
	g_bStateTrackinOutputWin =bOutputWin;
}

#ifdef _WIN32_WCE
extern BOOL EST_StateTrack(struct SME_APP_T *pDestApp, SME_STATE_T nNewState);
#endif
/*******************************************************************************************
* DESCRIPTION: Send state update information to DS. 
* INPUT:  
*  None. 
* OUTPUT: 
* NOTE: Format of Data: New State (4 bytes); Application name (char array);
* 1) If new state is -1, the given application is inactive.
* 2) If application name is "", the application thread ends.

If an application runs at multiple threads, only ONE instance will only to run. 
*******************************************************************************************/
SME_THREAD_CONTEXT_PT GetThreadContext();

BOOL StateTrack(struct SME_EVENT_T *pEvent, 
			   struct SME_APP_T *pDestApp, 
			   SME_STATE_T nNewState)
{
#if (defined(DEBUG) || defined(_DEBUG))
/* Debug edition */

	// Print state tracking in output window:
	static BOOL bFirst = TRUE;
	if (bFirst)
	{
		bFirst = FALSE;
		OutputDebugAsciiStrToDS("\nStart state tracking:\n(Application, New State[, Event]) \n");
	}

	if (g_bStateTrackinOutputWin && pDestApp)
	{
		char sOutput[256];
		// State Transition (
		if (pEvent)
			_snprintf(sOutput, sizeof(sOutput)-1, "(%s, %d, %d)\n", 
				pDestApp->sAppName, pDestApp->nAppState, pEvent->nEventID);
		else
			_snprintf(sOutput, sizeof(sOutput)-1, "(%s, %d)\n", 
				pDestApp->sAppName, pDestApp->nAppState);

		OutputDebugAsciiStrToDS(sOutput);
	}

	if (!g_bStateTrackinTree)
		return TRUE;

	//// State Tracking in state tree.
#ifdef _WIN32_WCE
	return EST_StateTrack(pDestApp, nNewState);
#else
	unsigned char byBuf[4+256]={0};
	long * pLong = (long *)byBuf;
	int nLen=0; 
	COPYDATASTRUCT cds;

	if (g_hWorkspaceWnd == NULL) return FALSE;

	if (pDestApp!=NULL && pDestApp->sAppName!=NULL)
		nLen = strlen(pDestApp->sAppName);
	else nLen = 0;

	if (nLen > 255) return FALSE;

	*pLong = (long)nNewState;
	if (nLen > 0)
		strcpy((char*)(&byBuf[4]), pDestApp->sAppName);

	cds.dwData = ID_COPYDATA_STATE_TRACK;
	cds.cbData = 4+nLen+1;
	cds.lpData = byBuf;
	SendMessage(g_hWorkspaceWnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);
	return TRUE;
#endif
#else
/* Release edition */
	return TRUE;
#endif
}

/*******************************************************************************************
* DESCRIPTION: Output debug string to DS. 
* INPUT:  
*  sDebugStr: NULL-terminated text string.
*	nSizeInByte: the total size of text string in Bytes including NULL terminal. 
* OUTPUT: 
* NOTE: 
*******************************************************************************************/
BOOL OutputDebugAsciiStrToDS(const char* sDebugStr)
{
	COPYDATASTRUCT cds;

	if (g_hWorkspaceWnd == NULL) return FALSE;

	cds.dwData = ID_COPYDATA_DEBUG_STR;
	cds.cbData = strlen(sDebugStr)+1;
	cds.lpData = (void*)sDebugStr;
	SendMessage(g_hWorkspaceWnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);
	return TRUE;
}

BOOL OutputDebugUnicodeStrToDS(const unsigned short* sDebugStr)
{
	COPYDATASTRUCT cds;

	if (g_hWorkspaceWnd == NULL) return FALSE;

	cds.dwData = ID_COPYDATA_DEBUG_STR;
	cds.cbData = (wcslen(sDebugStr)+1)<<1;
	cds.lpData = (void*)sDebugStr;
	SendMessage(g_hWorkspaceWnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);
	return TRUE;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.


Written By
Web Developer
China China
Jerome. (Free to speak, free to use.)

Comments and Discussions