AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=869&pId=-1
Running State Machines Based Win32/WinCE Programs
page
by Jerome D
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 6779/ 10

 

Audiences

This article was written for UML users or developers of distributed, concurrent and real-time embedded systems, and computer network programmers.  If you have UML dynamic modeling experiences, it will be better.

Introduction

Many concurrent, distributed or real-time applications have to tightly co-work with other objects.  These are called service providers.  A service is formally specified by a set of primitives (operations) available to service users (applications).  These primitives describe some action or report on an action taken by a peer component/entity to be performed.  The service primitives can be classified into four categories: request, indication, response and confirm (1).

This article describes how to run state machine application framework based Win32/WinCE programs using window message hooking technology.

Why run state machines based Win32/WinCE applications?

It is natural to use state machines in modeling such applications.  An application that must sequence a series of actions or handle inputs (responses and indications) differently depending on what state it is in is often best implemented as a state machine.

State machine application framework is widely used for embedded systems development.  Embedded system developers may use Windows platform as a modeling and simulation environment so that software development and debug can commence long before a hardware prototype is available.  In a simulation environment, a developer can design the simulator using Windows program as service providers.  These simulators have the identical interface with target service providers' interface.  On target environment, the developer may require little effort to integrate state machine applications with these service providers to the real environment.

However, for Windows applications, in particular with the emerging of WinCE operation system for smart phone (PDA applications), such methodologies will become increasingly important as systems hardware and software become more complex and as the systems themselves become more connected and distributed.

Traditional State Machines implementation

A typical state machine thread works just like the following.

 

SmeRun() 
{ 
      do { 
            Wait for an external event which is posted to this running thread; 
            if ( the event is valid) 
            { Dispatch this event to active applications and trigger state transitions. 
            } else break; 
      } while(1); 
}

 

The disadvantage of this running mode is that we have to create a separate thread for state machine applications.

 

Hooking technique in Windows Environments

Hooking in programming is a technique employing so called hooks to make a chain of procedures as a handler.  Thus, after the handled event occurs, control flow follows the chain in a specific order.  New hook registers its own address as handler for the event and is expected to call the original handler at some point, usually at the end. Each hook is required to pass execution to the previous handler, eventually arriving to the default one.  Otherwise, the chain is broken.  Un-registering the hook means setting the original procedure as the event handler.

Hooking can be used for many purposes, including debugging and extending original functionality.  However, it is also misused to inject (potentially malicious) code to the event handler (2).

And since Windows-based applications are event-driven, hooking seems to be very interesting.  In fact, these applications do not make explicit function calls (such as C run-time library calls) to obtain input.  Instead, they wait for the system to pass input to them.  The system passes all input for an application to the various windows in the application.  Each window has a function, called a window procedure, which the system calls whenever it has input for the window.  If we hook the window messages when service providers (other objects) post external events (with a type of specific Windows message) to this hooked window, the state machine engine will get the event data and then dispatch them to the active state machine applications.

 In UML StateWizard (an UML dynamic model tool for concurrent, distributed real time application development) the procedure is defined as the following.

 

1. Hooking window messages

The following function MfcHookWnd() hooks a HWND object by sub-classing it; in the Windows sense, that is by inserting its own window proc ahead of whatever procedure is there currently, usually AfxWndProc (3). 

class CEgnSubclassWnd: public CSubclassWnd 
{
public:
CEgnSubclassWnd();
virtual ~CEgnSubclassWnd();
 
virtual LRESULT WindowProc(HWND hwnd, UINT msg, WPARAM wp,LPARAM lp);
LRESULT OnExtEvent(MSG& WinMsg);
};
CEgnSubclassWnd::CEgnSubclassWnd()
{
}
CEgnSubclassWnd::~CEgnSubclassWnd()
{
}
LRESULT CEgnSubclassWnd::WindowProc(HWND hwnd, UINT msg, WPARAM wp,LPARAM lp)
{
struct SME_EVENT_T* pExtEvent=NULL;
MSG WinMsg;
WinMsg.hwnd = hwnd;
WinMsg.message =msg;
WinMsg.wParam = wp;
WinMsg.lParam = lp;
LRESULT ret = 0;
switch (msg)
{
case WM_EXT_EVENT_ID:
// External event is triggered. App go to running state.
OnExtEvent(WinMsg);
break;
default:
break;
}
ret = CSubclassWnd::WindowProc(hwnd,msg,wp,lp);
return ret;
} 

 

2. Dispatching events to state machines 

Usually all applications (state machines) run at one thread only.  However, state machine engine allows applications to be divided into several groups and the applications in each group run on its separate thread at one time.  The following Figure Running State Machined Based Applications illustrates several groups of applications run at each groups’ respective thread.  At each thread the state machine engine hooks a window which runs at this thread.

Figure: Running State Machined Based Applications

When the state machine engine receives a message dedicated to state machine applications, the state machine engine translates this message to an external event and dispatches it to the destination application port if it is not null.   Otherwise, it dispatches it to all active applications which run in the same application thread context.

When the hooked window is destroyed, the window message hook is removed automatically.

 

/********************************************************************
 DESCRIPTION: Just like SmeRun(), this function dispatches external event to
 applications. 
* INPUT: 
* OUTPUT: None.
* NOTE: 
*******************************************************************/
struct SME_EVENT_T * GetEventFromQueue();
BOOL DispatchInternalEvents(SME_THREAD_CONTEXT_PT pThreadContext);
BOOL DispatchEventToApps(SME_THREAD_CONTEXT_PT pThreadContext,SME_EVENT_T *pEvent);
LRESULT CEgnSubclassWnd::OnExtEvent(MSG& WinMsg)
{
SME_APP_T *pApp;
SME_THREAD_CONTEXT_PT pThreadContext=NULL;
SME_EVENT_T *pEvent=TranslateEvent(&WinMsg);
if (pEvent==NULL)
return 0;
if (g_pfnGetThreadContext)
pThreadContext = (*g_pfnGetThreadContext)();
if (!pThreadContext) return 0;
pApp = pThreadContext->pActAppHdr;
/* Call hook function on an external event coming. */
if (pThreadContext->fnOnEventComeHook)
(*pThreadContext->fnOnEventComeHook)(SME_EVENT_ORIGIN_EXTERNAL, pEvent);
/* Dispatch it to active applications.*/
DispatchEventToApps(pThreadContext, pEvent);
DispatchInternalEvents(pThreadContext);
/* Free external event if necessary. */
if (pThreadContext->fnDelExtEvent && pEvent)
{
(*pThreadContext->fnDelExtEvent)(pEvent);
// Engine should delete this event, because
 // translation of external event will create an internal event. 
SmeDeleteEvent(pEvent); 
}
 
return 0;
}
CEgnSubclassWnd EngSubclassWnd;
BOOL MfcHookWnd(HWND hWndHooked)
{
if (hWndHooked==NULL || !IsWindow(hWndHooked)) return FALSE;
CWnd *pWnd = CWnd::FromHandle(hWndHooked);
return EngSubclassWnd.HookWindow(pWnd); 
}
BOOL MfcUnhookWnd(HWND hWndHooked)
{
if (hWndHooked==NULL || !IsWindow(hWndHooked)) return FALSE;
CWnd *pWnd = CWnd::FromHandle(hWndHooked);
return EngSubclassWnd.HookWindow(pWnd); 
} 

 

Sample

 

Suppose we have a simple player application whose state diagram looks like following.

Figure: Player State Machine Application

 

The following sample shows the way to hook dialog messages and dispatch external events to Player state machine application.  Declare an application thread context.  When the dialog opens, initialize the state machine engine in the thread context through SmeInitEngine().  In the Windows’ edition of state machine engine, this function will automatically initialize the given thread context with the following information implicitly:

 

1) SmeSetExtEventOprProc() to setup external event handling functions through Windows API GetMessage(), PostThreadMessage().

2) SmeSetMemOprProc() to setup dynamic memory management functions through new, delete operators.

3) SmeSetTlsProc() to setup thread local storage procedure functions through Windows API TlsGetValue(), TlsSetValue().

 

It then hooks the dialog messages.  Activate the Player application in the application thread.  If an external event triggers, call the MfcPostExtIntEventToWnd() function to post an external event to dialog.  This function will post WM_EXT_EVNET_ID Windows message to the dialog.

 

#define WM_EXT_EVENT_ID      (0xBFFF)

 

When the state machine engine receives this message, translate this message to an external event and dispatch it to the destination application port if it is not null.  Otherwise, dispatch it to all active applications which run in the same application thread context.

 

// The application thread context. 
SME_THREAD_CONTEXT_T g_AppThreadContext; 
// Declare the Player state machine application variable. 
SME_DEC_EXT_APP_VAR(Player); 
 
BOOL CSamplePlayerMfcDlg::OnInitDialog() 
{ 
   CDialog::OnInitDialog(); 
    
   .... 
   // Initialize engine. 
   g_AppThreadContext.nAppThreadID = 0; 
   SmeInitEngine(&g_AppThreadContext); 
   // Hook dialog message. 
   MfcHookWnd(GetSafeHwnd()); 
   SmeActivateApp(&SME_GET_APP_VAR(Player),NULL); 
} 
 
void CSamplePlayerMfcDlg::OnButtonPower() 
{ 
   MfcSendExtIntEventToWnd(EXT_EVENT_ID_POWER, 0, 0, NULL, GetSafeHwnd()); 
} 
 
void CSamplePlayerMfcDlg::OnButtonPause() 
{ 
   // TODO: Add your control notification handler code here 
   MfcSendExtIntEventToWnd(EXT_EVENT_ID_PAUSE_RESUME, 0, 0, NULL, GetSafeHwnd()); 
} 

Download Source

 

Interested by the subject

You may download more information at http://www.intelliwizard.com/ the official site -the UML IntelliWizard open source project (4).  You can get the source code for this article at /download/869/StateWizard.zip

 

References

[1] [Computer Networks, Andrew S.Tanenbaum].

[2] This entry is from Wikipedia, the leading user-contributed encyclopedia.

[3] Microsoft Systems Journal March 1997

[4] The  UML StateWizard  project is hosted by Sourceforge.net


Product Spotlight
Product Spotlight 

©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-03-28 4:36:58 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search