Figure 1   TOptEx.c

 /******************************************************************************
Module name: TOptEx.c
Written by:  Kevin Hazzard and Jeffrey Richter
Purpose:     Demonstrates the critical section "feature" on 
             Windows 95 and how to fix it using the OPTEX (optimized 
             mutex) synchronization object
******************************************************************************/


#include <windows.h>
#include <stdio.h>


///////////////////////////////////////////////////////////////////////////////


// JR: The lines below were added to test the OPTEX 
#define _USE_OPTEX_INSTEAD_OF_CRITICAL_SECTIONS_
#include "OptEx.h"
// JR: The lines above were added to test the OPTEX 


///////////////////////////////////////////////////////////////////////////////


// The number of spawned threads
#define THREAD_COUNT    3

// The global critical section object
CRITICAL_SECTION g_cs;

// Prototype of thread function
DWORD WINAPI ThreadFunc(LPVOID pvParam);


///////////////////////////////////////////////////////////////////////////////


void main (void) {
   DWORD dwID, dw;
   HANDLE hThreads[THREAD_COUNT];

   // Initialize the critical section before any threads enter it
   InitializeCriticalSection(&g_cs);

   // Create a pool of threads
   for (dw = 0; dw < THREAD_COUNT; dw++) {
      hThreads[dw] = CreateThread(NULL, 0, ThreadFunc, 
                                  (LPVOID) dw, 0, &dwID);
   }

   // Wait for the spawned threads to terminate
   WaitForMultipleObjects(THREAD_COUNT, hThreads, TRUE, INFINITE);

   // Close the thread handles
   for (dw = 0; dw < THREAD_COUNT; dw++) CloseHandle(hThreads[dw]);

   printf("All worker threads shut themselves down.\n");

   // Delete the critical section after the threads have exited
   DeleteCriticalSection(&g_cs);
}


///////////////////////////////////////////////////////////////////////////////


// The thread function
DWORD WINAPI ThreadFunc (LPVOID pvParam) {
   DWORD dwThreadNum = (DWORD) pvParam;
   printf("Thread %u is waiting on the CritSec.\n", dwThreadNum);
   EnterCriticalSection(&g_cs);
   printf("Thread %u entered the CritSec.\n", dwThreadNum);
   Sleep(5000);
   printf("Thread %u is abandoning the CritSec.\n", dwThreadNum);
   return(0);
}


///////////////////////////////// End of File /////////////////////////////////

Figure 2   OPTEX

OptEx.h

 /******************************************************************************
Module name: OptEx.h
Written by:  Jeffrey Richter
Purpose:     Defines the OPTEX (optimized mutex) synchronization object
******************************************************************************/


// The opaque OPTEX data structure
typedef struct {
   LONG   lLockCount;
   DWORD  dwThreadId;
   LONG   lRecurseCount;
   HANDLE hEvent;
} OPTEX, *POPTEX;


///////////////////////////////////////////////////////////////////////////////


BOOL  OPTEX_Initialize (POPTEX poptex);
VOID  OPTEX_Delete     (POPTEX poptex);
DWORD OPTEX_Enter      (POPTEX poptex, DWORD dwTimeout);
VOID  OPTEX_Leave      (POPTEX poptex);


///////////////////////////////////////////////////////////////////////////////


#ifdef _USE_OPTEX_INSTEAD_OF_CRITICAL_SECTIONS_

#define CRITICAL_SECTION                      OPTEX
#define InitializeCriticalSection(poptex)     ((VOID) OPTEX_Initialize(poptex))
#define DeleteCriticalSection(poptex)         OPTEX_Delete(poptex)
#define EnterCriticalSection(poptex)          ((VOID) OPTEX_Enter(poptex, 
INFINITE)) #define LeaveCriticalSection(poptex) OPTEX_Leave(poptex) #endif // _USE_OPTEX_INSTEAD_OF_CRITICAL_SECTIONS_ ///////////////////////////////// End of File /////////////////////////////////

OptEx.c

 /******************************************************************************
Module name: OptEx.c
Written by:  Jeffrey Richter
Purpose:     Implements the OPTEX (optimized mutex) synchronization object
******************************************************************************/


#include <windows.h>
#include "OptEx.h"


///////////////////////////////////////////////////////////////////////////////


BOOL OPTEX_Initialize (POPTEX poptex) {

   poptex->lLockCount = -1;   // No threads have enterred the OPTEX
   poptex->dwThreadId = 0;    // The OPTEX is unowned
   poptex->lRecurseCount = 0; // The OPTEX is unowned
   poptex->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
   return(poptex->hEvent != NULL);  // TRUE if the event is created
}


///////////////////////////////////////////////////////////////////////////////


VOID OPTEX_Delete (POPTEX poptex) {

   // No in-use check
   CloseHandle(poptex->hEvent);  // Close the event
}


///////////////////////////////////////////////////////////////////////////////


DWORD OPTEX_Enter (POPTEX poptex, DWORD dwTimeout) {

   DWORD dwThreadId = GetCurrentThreadId();  // The calling thread's ID

   // Assume that the thread waits successfully
   DWORD dwRet = WAIT_OBJECT_0;  

   if (InterlockedIncrement(&poptex->lLockCount) == 0) {

      // ---> No thread owns the OPTEX
      poptex->dwThreadId = dwThreadId; // We own it
      poptex->lRecurseCount = 1;       // We own it once

   } else {

      // ---> Some thread owns the OPTEX
      if (poptex->dwThreadId == dwThreadId) {

         // ---> We already own the OPTEX
         poptex->lRecurseCount++;     // We own it again

      } else {

         // ---> Another thread owns the OPTEX
         // Wait for the owning thread to release the OPTEX
         dwRet = WaitForSingleObject(poptex->hEvent, dwTimeout);
         if (dwRet != WAIT_TIMEOUT) {

            // ---> We got ownership of the OPTEX
            poptex->dwThreadId = dwThreadId; // We own it now
            poptex->lRecurseCount = 1;       // We own it once
         }
      }
   }

   // Return why we continue execution
   return(dwRet);
}


///////////////////////////////////////////////////////////////////////////////


VOID OPTEX_Leave (POPTEX poptex) {

   if (--poptex->lRecurseCount > 0) {

      // We still own the OPTEX
      InterlockedDecrement(&poptex->lLockCount);

   } else {

      // We don't own the OPTEX
      poptex->dwThreadId = 0;
      if (InterlockedDecrement(&poptex->lLockCount) >= 0) {
         // Other threads are waiting, wake one on them
         SetEvent(poptex->hEvent);
      }
   }
}


///////////////////////////////// End of File /////////////////////////////////

Figure 3   OPTEX_Enter Return Values

Return Value

Description

WAIT_OBJECT_0

The thread successfully gained ownership of the OPTEX (or incremented its recur-sion count)

WAIT_TIMEOUT

The thread could not gain ownership of the OPTEX in the specified time

Figure 4   DEVMODE's Relevant Members for Screen Settings

DEVMODE Member

Description

Example

dmBitsPerPel

Indicates the display's color resolution

4 bits for 16 colors

8 bits for 256 colors
16 bits for 65536 colors

dmPelsWidth

Indicates the width of the display

640 pixels

dmPelsHeight

Indicates the height of the display

480 pixels

dmDisplayFlags

Indicates the display's mode

DM_GRAYSCALE indicates that the display does not support color
DM_INTERLACED indicates that the display mode is interlaced

dmDisplayFrequency

Indicates the refresh frequency of the display (Windows 95 always returns 0)

60 Hz

Figure 5   Change Display Settings Flags

Flag

Description

0

Change the display settings now

CDS_UPDATEREGISTRY

Change the display settings now and make these settings the default by saving them in the registry under HKEY_CURRENT_USER

CDS_TEST

Just test to see if the requested settings are valid

CDS_FULLSCREEN

This undocumented flag tells the system that the calling application wants to enter/leave full screen mode-this prevents the system from repositioning other windows to keep them visible

Figure 6   Change Display Settings Return Values

Return value

Description

DISP_CHANGE_SUCCESSFUL

The display's settings have changed immediately-Windows NT 4.0 cannot change settings dynamically, and always returns DISP_CHAGE_RESTART instead

DISP_CHANGE_RESTART

The display's settings have been changed but require the computer to be rebooted before they can take affect

DISP_CHANGE_BADFLAGS

An invalid flag was passed

DISP_CHANGE_FAILED

The display driver failed to change the settings

DISP_CHANGE_BADMODE

The requested settings are not supported by the display

DISP_CHANGE_NOTUPDATED

The settings are not changed because they couldn't also be saved in the registry-this only happens on Windows NT if access to the registry key is denied by the administrator