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;
}