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