Message-Handling Procedures

Most of the message-handling functions are simple. Main_OnTimer calls a procedure to clear the list box, generate four new strings of information, and display them. The Main_OnSize function suspends all the secondary threads while the program repositions the child windows to accommodate the new size. Otherwise, the busy threads would slow down the display operation. In addition to creating threads, Main_OnCreatecreates a mutex.

/*—————————————————————————————————————————————————————————————————————
   MAIN_WNDPROC
      All messages for the main window are processed here.
—————————————————————————————————————————————————————————————————————*/
LRESULT WINAPI Main_WndProc( HWND hWnd,          // message address
                             UINT uMessage,      // message type
                             WPARAM wParam,      // message contents
                             LPARAM lParam )     // more contents
{
   switch( uMessage )
   {
      HANDLE_MSG( hWnd, WM_CREATE, Main_OnCreate );
         // create the window and the threads
      HANDLE_MSG( hWnd, WM_SIZE, Main_OnSize );
         // reposition the child windows when the main window changes
      HANDLE_MSG( hWnd, WM_TIMER, Main_OnTimer );
         // update the list box every five seconds
      HANDLE_MSG( hWnd, WM_INITMENU, Main_OnInitMenu );
         // put a check by the Use Mutex menu item if bUseMutex is TRUE
      HANDLE_MSG( hWnd, WM_COMMAND, Main_OnCommand );
         // process menu commands
      HANDLE_MSG( hWnd, WM_DESTROY, Main_OnDestroy );
         // clean up and quit
      default:
         return( DefWindowProc(hWnd, uMessage, wParam, lParam) );
   }
   return( 0L );
}

The Threads demo uses the message-cracker macro, HANDLE_MSG. As a result, the compiler may produce a series of warnings saying, “unreferenced formal parameter” for the message handlers. To avoid these, include the argsused pragma following:

#ifdef __BORLANDC__#pragma argsused#endif

The Main_OnCreate function completes the process of initializing the several threads and setting up the mutex synchronization.

/*———————————————————————————————————————————————————————————————————————
   MAIN_ONCREATE
      create the four threads and set the timer.
————————————————————————————————————————————————————————————————————————*/
BOOL Main_OnCreate( HWND hWnd, LPCREATESTRUCT lpCreateStruct )
{
   UINT uRet;
   int  iCount;
      // create the four threads, initially suspended
   for( iCount = 0; iCount < 4; iCount++ )
   {
      iRectCount[iCount] = 0;
      dwThreadData[iCount] = iCount;
      hThread[iCount] = CreateThread( NULL, 0,
             (LPTHREAD_START_ROUTINE) StartThread,
                             (LPVOID) ( & ( dwThreadData[iCount] ) ),
                                      CREATE_SUSPENDED,
                            (LPDWORD) ( & ( dwThreadID[iCount] ) ) );
      if( ! hThread[iCount] )     // was the thread created?
      {
         return( FALSE );
      }
   }
      // create a timer with a five-second period
      // the timer is used to update the list box
   uRet = SetTimer( hWnd, TIMER, 5000, NULL );
   if( ! uRet )
   {                              // unable to create the timer
      return( FALSE );
   }
      // create a mutex synchronization object
   hDrawMutex = CreateMutex( NULL, FALSE, NULL );
   if( ! hDrawMutex )
   {                              // unable to create mutex
      KillTimer( hWnd, TIMER );   // stop the timer
      return( FALSE );
   }
      // start the threads with a priority below normal 
   for( iCount = 0; iCount < 4; iCount++ )
   {
      SetThreadPriority( hThread[iCount], THREAD_PRIORITY_BELOW_NORMAL );
      iState[iCount] = ACTIVE;
      ResumeThread( hThread[iCount] );
   }
   return( TRUE );
}

The Main_OnSize function not only needs to resize the application window, but it also needs to resize—and move—all of the child windows at the same time; this is because we have multiple child windows and because the main application window is resizable.

/*———————————————————————————————————————————————————————————————————————
   MAIN_ONSIZE
      Position the list box and the four child windows.
————————————————————————————————————————————————————————————————————————*/
void Main_OnSize( HWND hWnd, UINT uState, int cxClient, int cyClient )
{
   char* szText = “No Thread Data”;
   int   iCount;
      // Suspend all active threads while the windows
      // resize and repaint themselves. This pause
      // enables the screen to update more quickly.
   for( iCount = 0; iCount < 4; iCount++ )
   {
      if( iState[iCount] == ACTIVE )
         SuspendThread( hThread[iCount] );
   }
      // Place the list box across the top 1/4 of the window.
   MoveWindow( hwndList, 0, 0, cxClient, cyClient / 4, TRUE );
      // Spread the 4 child windows across the bottom 3/4 of the
      // window. (The left border of the first one should be 0.)
   MoveWindow( hwndChild[0], 0, cyClient / 4 - 1, cxClient / 4 + 1,
               cyClient, TRUE );
   for( iCount = 1; iCount < 4; iCount++ )
   {
      MoveWindow( hwndChild[iCount], (iCount * cxClient) / 4,
                  cyClient / 4 - 1,  cxClient / 4 + 1, cyClient, TRUE );
   }
      // Add the default strings to the list box, and initialize
      // the number of figures drawn to zero.
   for( iCount = 0; iCount < 4; iCount++ )
   {
      iRectCount[iCount] = 0;
      ListBox_AddString( hwndList, szText );
   }
   ListBox_SetCurSel(hwndList, 0);
      // Reactivate the threads that were suspended while redrawing.
   for( iCount = 0; iCount < 4; iCount++ )
   {
      if( iState[iCount] == ACTIVE )
      {
         ResumeThread( hThread[iCount] );
      }
   }
   return;
}

A timer message may not be the optimum choice for updating the list box. Ideally, the operations responsible for causing a change that should be reported would direct this event. In this case, however, a timer is a simple provision accomplishing the task.

/*—————————————————————————————————————————————————————————————————————
   MAIN_ONTIMER
      Process the timer message by updating the list box.
—————————————————————————————————————————————————————————————————————*/
void Main_OnTimer( HWND hWnd, UINT uTimerID )
{
      // update the data shown in the list box
   UpdateListBox();
   return;
}

The Main_OnInitMenu function is simply used to check (or uncheck) the Use Mutex menu command.

/*—————————————————————————————————————————————————————————————————————
   MAIN_ONINITMENU
      Check or uncheck the Use Mutex menu item based on the
      value of bUseMutex.
—————————————————————————————————————————————————————————————————————*/
void Main_OnInitMenu( HWND hWnd, HMENU hMenu )
{
   CheckMenuItem( hMenu, IDM_USEMUTEX, MF_BYCOMMAND |
            (UINT)( bUseMutex ? MF_CHECKED : MF_UNCHECKED ) );
   return;
}

The Main_OnCommand function is provided to parcel out all messages that have not already been handled by message crackers. (The message crackers are found in Main_WndProc, where the HANDLE_MSG macros are used to redirect messages to the appropriate functions.)

/*—————————————————————————————————————————————————————————————————————
   MAIN_ONCOMMAND
      Respond to commands from the user.
—————————————————————————————————————————————————————————————————————*/
void Main_OnCommand( HWND hWnd, int iCmd, HWND hwndCtl, UINT uCode )
{
   switch( iCmd )
   {
      case IDM_ABOUT:      // display the About box 
         MakeAbout( hWnd );
         break;
      case IDM_EXIT:       // exit this program
         DestroyWindow( hWnd );
         break;
      case IDM_SUSPEND:    // modify the priority or state of a thread
      case IDM_RESUME:
      case IDM_INCREASE:
      case IDM_DECREASE:
         DoThread( iCmd );            // adjust a thread
         break;
      case IDM_USEMUTEX:   // toggle the use of the mutex 
         ClearChildWindows( );        // make all thread windows white
         bUseMutex = !bUseMutex;      // toggle mutex setting
         break;
      default:
         break;
   }
   return;
}

© 1998 SYBEX Inc. All rights reserved.