Click here to Skip to main content
15,867,308 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.5K   1.3K   35   15
This article describes how to run state machine application framework based Win32/WinCE programs using the window message hooking technology. (Open source project)

Introduction

Many concurrent, distributed and real-time applications have to tightly co-work with other objects, which 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 the window message hooking technology.

Why Run State Machines Based Win32/WinCE Applications?

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

The 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 debugging can commence long before a hardware prototype is available. In a simulation environment, a developer can design some simulators using Windows programs as service providers. These simulators will have identical interfaces with the target service providers' interfaces. On the target environment, a developer may take little effort to integrate state machine applications with these service providers in real environment.

However, for Windows applications, in particular with the emerging of the WinCE operating system for smart phones and 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 the 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. A new hook registers its own address as a 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 the execution to the previous handler, eventually arriving to the default one, otherwise the chain is broken. Unregistering the hook means setting the original procedure as the event handler.

Hooking can be used for many purposes including debugging and extending the original functionality, but also can be 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 the input. Instead, they wait for the system to pass input to them. The system passes all the input for an application to the various windows in the application. Each window has a function, called a window procedure, that 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 specific type of Windows message) to this hooked window, the state machine engine will get the event data and then dispatches them to the active state machine applications.

In UML StateWizard (a UML dynamic model tool for concurrent, distributed real time application development), the procedure is defined as follows:

1. Hooking Window Messages

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

C++
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, a state machine engine allows applications to be divided to some groups, and the applications in each group run on separate threads at a time. The following figure, Run State Machines with MFC, illustrates some groups of applications running at each group's respective thread. At each thread, the state machine engine hooks a window which runs on this thread.

Sample screenshot

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.

C++
/*****************************************************
* 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 is as follows:

Sample screenshot

The following sample shows how to hook dialog messages and dispatch external events to the 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 the state machine engine, this function will automatically initialize the given thread context, following the information implicitly:

  1. SmeSetExtEventOprProc() to setup external event handling functions through the Windows APIs GetMessage(), PostThreadMessage().
  2. SmeSetMemOprProc() to setup dynamic memory management functions through the new and delete operators.
  3. SmeSetTlsProc() to setup thread local storage procedure functions through Windows APIs TlsGetValue(), TlsSetValue().

and then hooks the dialog messages. Activate the Player application in the application thread. If an external event triggers, it calls the MfcPostExtIntEventToWnd() function to post an external event to dialog. This function will post the WM_EXT_EVNET_ID Windows message as below to the dialog:

C++
#define WM_EXT_EVENT_ID (0xBFFF)

When the state machine engine receives this message, it 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.

C++
// 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()); 
}

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

Interested in the Subject?

You may download more information at the official site the UML StateWizard open source project.[4]

Notes and References

  1. Computer Networks, Andrew S.Tanenbaum
  2. Wikipedia, the leading user-contributed encyclopedia
  3. Microsoft Systems Journal, March 1997
  4. The UML StateWizard project is hosted by Sourceforge.net

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


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

Comments and Discussions

 
GeneralUpdated software license to LGPL for linking into non-free programs [modified] Pin
Jerome_D5-Jun-06 15:51
Jerome_D5-Jun-06 15:51 
Generalwhere has the article gone Pin
Jonathan [Darka]17-May-06 21:26
professionalJonathan [Darka]17-May-06 21:26 
GeneralRe: where has the article gone Pin
Jerome_D17-May-06 23:51
Jerome_D17-May-06 23:51 
GeneralRe: where has the article gone Pin
Jonathan [Darka]17-May-06 23:56
professionalJonathan [Darka]17-May-06 23:56 
GeneralRe: where has the article gone Pin
Jonathan [Darka]17-May-06 23:56
professionalJonathan [Darka]17-May-06 23:56 
GeneralRe: where has the article gone Pin
Jerome_D18-May-06 16:38
Jerome_D18-May-06 16:38 
QuestionDoes it support multithread? Pin
francis_c15-May-06 3:56
francis_c15-May-06 3:56 
AnswerRe: Does it support multithread? Pin
Jerome_D16-May-06 3:56
Jerome_D16-May-06 3:56 
GeneralNice ,but how to register the Add-in Pin
cynew13-May-06 3:18
cynew13-May-06 3:18 
GeneralRe: Nice ,but how to register the Add-in Pin
Jerome_D14-May-06 2:46
Jerome_D14-May-06 2:46 
GeneralOpen Addin Source Code Pin
Jerome_D20-Apr-06 19:19
Jerome_D20-Apr-06 19:19 
GeneralAlmost auto plagiarized :/ Pin
Kochise10-Apr-06 5:44
Kochise10-Apr-06 5:44 
GeneralRe: Almost auto plagiarized :/ Pin
Jerome_D10-Apr-06 15:50
Jerome_D10-Apr-06 15:50 
GeneralRe: Almost auto plagiarized :/ Pin
Aryasheel20-Apr-06 2:48
Aryasheel20-Apr-06 2:48 
GeneralPort to pocket PC Pin
Jerome_D20-Apr-06 19:17
Jerome_D20-Apr-06 19:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.