Contents:
- 1 Thread Scheduling, Priorities, and Affinities
- 2 Suspending and Resuming a Thread
1 Thread Scheduling, Priorities, and Affinities #
2 Suspending and Resuming a Thread #
DWORD ResumeThread( HANDLE hThread );
DWORD SuspendThread( HANDLE hThread );
3 Suspending and Resuming a Process #
VOID SuspendProcess( DWORD dwProcessID, BOOL fSuspend )
{
// Get the list of threads in the system.
HANDLE hSnapshot = CreateToolhelp32Snapshot(
TH32CS_SNAPTHREAD, dwProcessID );
if( hSnapshot != INVALID_HANDLE_VALUE ) {
// Walk the list of threads.
THREADENTRY32 te = { sizeof( te ) };
BOOL fOk = Thread32First( hSnapshot, &te );
for( ; fOk; fOk = Thread32Next( hSnapshot, &te ) ) {
// Is this thread in the desired process?
if( te.th32OwnerProcessID == dwProcessID ) {
// Attempt to convert the thread ID into a handle.
HANDLE hThread = OpenThread( THREAD_SUSPEND_RESUME,
FALSE, te.th32ThreadID );
if( hThread != NULL ) {
// Suspend or resume the thread.
if( fSuspend )
SuspendThread( hThread );
else
ResumeThread( hThread );
}
CloseHandle( hThread );
}
}
CloseHandle( hSnapshot );
}
}
HANDLE OpenThread(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwThreadID
);
VOID Sleep( DWORD dwMilliseconds );
5 Switching to Another Thread #
6 A Thread's Execution Times #
// Get the current time (start time).
DWORD dwStartTime = GetTickCount();
// Perform complex algorithm here.
// Subtract start time from current time to get duration.
DWORD dwElapsedTime = GetTickCount() - dwStartTime;
BOOL GetThreadTimes(
HANDLE hThread,
PFILETIME pftCreationTime,
PFILETIME pftExitTime,
PFILETIME pftKernelTime,
PFILETIME pftUserTime
);
| Time Value | Meaning |
| Creation time | An absolute value expressed in 100-nanosecond intervals past midnight on January 1, 1601, at Greenwich, England, indicating when the thread was created. |
| Exit time | An absolute value expressed in 100-nanosecond intervals past midnight on January 1, 1601, at Greenwich, England, indicating when the thread exited. If the thread is still running, the exit time is undefined. |
| Kernel time | A relative value indicating how many 100-nanosecond intervals of CPU time the thread has spent executing operating system code. |
| User time | A relative value indicating how many 100-nanosecond intervals of CPU time the thread has spent executing application code. |
__int64 FileTimeToQuadWord( PFILETIME pft )
{
return( Int64ShllMod32( pft->dwHighDateTime, 32 ) | pft->dwLowDateTime );
}
void PerformLongOperation()
{
FILETIME ftKernelTimeStart, ftKernelTimeEnd;
FILETIME ftUserTimeStart, ftUserTimeEnd;
FILETIME ftDummy;
__int64 qwKernelTimeElapsed, qwUserTimeElapsed,
qwTotalTimeElapsed;
// Get starting times.
GetThreadTimes( GetCurrentThread(), &ftDummy, &ftDummy,
&ftKernelTimeStart, &ftUserTimeStart );
// Perform complex algorithm here.
// Get ending times.
GetThreadTimes( GetCurrentThread(), &ftDummy, &ftDummy,
&ftKernelTimeEnd, &ftUserTimeEnd );
// Get the elapsed kernel and user times by converting the start
// and end times from FILETIMEs to quad words, and then subtract
// the start times from the end times.
qwKernelTimeElapsed = FileTimeToQuadWord( &ftKernelTimeEnd ) -
FileTimeToQuadWord( &ftKernelTimeStart );
qwUserTimeElapsed = FileTimeToQuadWord( &ftUserTimeEnd ) -
FileTimeToQuadWord( &ftUserTimeStart );
// Get total time duration by adding the kernel and user times.
qwTotalTimeElapsed = qwKernelTimeElapsed + qwUserTimeElapsed;
// The total elapsed time is in qwTotalTimeElapsed.
}
BOOL GetProcessTimes(
HANDLE hProcess,
PFILETIME pftCreationTime,
PFILETIME pftExitTime,
PFILETIME pftKernelTime,
PFILETIME pftUserTime
);
BOOL QueryPerformanceFrequency( LARGE_INTEGER* pliFrequency );
BOOL QueryPerformanceCounter( LARGE_INTEGER* pliCount );
class CStopwatch {
public:
CStopwatch() { QueryPerformanceFrequency( &m_liPerfFreq ); Start(); }
void Start() { QueryPerformanceCounter( &m_liPerfStart ); }
__int64 Now() const // Returns # of milliseconds since Start was called
{
LARGE_INTEGER liPerfNow;
QueryPerformanceCounter( &liPerfNow );
return ( ( ( liPerfNow.QuadPart - m_liPerfStart.QuadPart ) * 1000 )
/ m_liPerfFreq.QuadPart );
}
private:
LARGE_INTEGER m_liPerfFreq; // Counts per second
LARGE_INTEGER m_liPerfStart; // Starting count
};
// Create a stopwatch timer (which defaults to the current time).
CStopwatch stopwatch;
// Execute the code I want to profile here.
// Get how much time has elapsed up to now.
__int64 qwElapsedTime = stopwatch.Now();
// qwElapsedTime indicates how long the profiled code
// executed in milliseconds.
7 Putting the Context in Context #
typedef struct _CONTEXT {
//
// The flags values within this flag control the contents of
// a CONTEXT record.
//
// If the context record is used as an input parameter, then
// for each portion of the context record controlled by a flag
// whose value is set, it is assumed that that portion of the
// context record contains valid context. If the context record
// is being used to modify a threads context, then only that
// portion of the threads context will be modified.
//
// If the context record is used as an IN OUT parameter to capture
// the context of a thread, then only those portions of the thread's
// context corresponding to set flags will be returned.
//
// The context record is never used as an OUT only parameter.
//
DWORD ContextFlags;
//
// This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
// set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT
// included in CONTEXT_FULL.
//
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
//
FLOATING_SAVE_AREA FloatSave;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_SEGMENTS.
//
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_INTEGER.
//
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_CONTROL.
//
DWORD Ebp;
DWORD Eip;
DWORD SegCs; // MUST BE SANITIZED
DWORD EFlags; // MUST BE SANITIZED
DWORD Esp;
DWORD SegSs;
//
// This section is specified/returned if the ContextFlags word
// contains the flag CONTEXT_EXTENDED_REGISTERS.
// The format and contexts are processor specific
//
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
BOOL GetThreadContext(
HANDLE hThread,
LPCONTEXT lpContext
);
// Create a CONTEXT structure.
CONTEXT Context;
// Tell the system that we are interested in only the
// control registers.
Context.ContextFlags = CONTEXT_CONTROL;
// Tell the system to get the registers associated with a thread.
GetThreadContext( hThread, &Context );
// The control register members in the CONTEXT structure
// reflect the thread's control registers. The other members
// are undefined.
// Tell the system that we are interested
// in the control and integer registers.
Context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
// Tell the system we are interested in the important registers.
Context.ContextFlags = CONTEXT_FULL;
| CPU Type | Definition of CONTEXT_FULL |
| x86 | CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS |
| Alpha | CONTEXT_CONTROL | CONTEXT_FLOATING_POINT | CONTEXT_INTEGER |
| CPU Type | Instruction Pointer | Stack Pointer |
| x86 | CONTEXT.Eip | CONTEXT.Esp |
| Alpha | CONTEXT.Fir | CONTEXT.IntSp |
BOOL SetThreadContext(
HANDLE hThread,
CONST CONTEXT* lpContext
);
CONTEXT Context;
// Stop the thread from running.
SuspendThread( hThread );
// Get the thread's context registers.
Context.ContextFlags = CONTEXT_CONTROL;
GetThreadContext( hThread, &Context );
// Make the instruction pointer point to the address of your choice.
// Here I've arbitrarily set the address instruction pointer to
// 0x00010000.
#if defined(_ALPHA_)
Context.Fir = 0x00010000;
#elif defined(_X86_)
Context.Eip = 0x00010000;
#else
#error Module contains CPU-specific code; modify and recompile.
#endif
// Set the thread's registers to reflect the changed values.
// It's not really necessary to reset the ControlFlags member
// because it was set earlier.
Context.ControlFlags = CONTEXT_CONTROL;
SetThreadContext( hThread, &Context );
// Resuming the thread will cause it to begin execution
// at address 0x00010000.
ResumeThread( hThread );
9 An Abstract View of Priorities #
| Priority Class | Description |
| Real-time | The threads in this process must respond immediately to events in order to execute time-critical tasks. Threads in this process also preempt operating system components. Use this priority class with extreme caution. |
| High | The threads in this process must respond immediately to events in order to execute time-critical tasks. The Task Manager runs at this class so a user can kill runaway processes. |
| Above normal | The threads in this process run between the normal and high priority classes (new in Windows 2000). |
| Normal | The threads in this process have no special scheduling needs. |
| Below normal | The threads in this process run between the normal and idle priority classes (new in Windows 2000). |
| Idle | The threads in this process run when the system is otherwise idle. This process is typically used by screensavers or background utility and statistic-gathering software. |
| Relative Thread Priority | Description |
| Time-critical | Thread runs at 31 for the real-time priority class and at 15 for all other priority classes. |
| Highest | Thread runs two levels above normal. |
| Above normal | Thread runs one level above normal. |
| Normal | Thread runs normally for the process's priority class. |
| Below normal | Thread runs one level below normal. |
| Lowest | Thread runs two levels below normal. |
| Idle | Thread runs at 16 for the real-time priority class and at 1 for all other priority classes. |
| Process Priority Class |
| Relative Thread Priority | Idle | Below Normal | Normal | Above Normal | High | Real-Time |
| Time-critical | 15 | 15 | 15 | 15 | 15 | 31 |
| Highest | 6 | 8 | 10 | 12 | 15 | 26 |
| Above normal | 5 | 7 | 9 | 11 | 14 | 25 |
| Normal | 4 | 6 | 8 | 10 | 13 | 24 |
| Below normal | 3 | 5 | 7 | 9 | 12 | 23 |
| Lowest | 2 | 4 | 6 | 8 | 11 | 22 |
| Idle | 1 | 1 | 1 | 1 | 1 | 16 |
10 Programming Priorities #
| Priority Class | Symbolic Identifiers |
| Real-time | REALTIME_PRIORITY_CLASS |
| High | HIGH_PRIORITY_CLASS |
| Above normal | ABOVE_NORMAL_PRIORITY_CLASS |
| Normal | NORMAL_PRIORITY_CLASS |
| Below normal | BELOW_NORMAL_PRIORITY_CLASS |
| Idle | IDLE_PRIORITY_CLASS |
BOOL SetPriorityClass(
HANDLE hProcess,
DWORD fdwPriority
);
BOOL SetPriorityClass(
GetCurrentProcess(),
IDLE_PRIORITY_CLASS
);
DWORD GetPriorityClass( HANDLE hProcess );
BOOL SetThreadPriority(
HANDLE hThread,
int nPriority
);
| Relative Thread Priority | Symbolic Constant |
| Time-critical | THREAD_PRIORITY_TIME_CRITICAL |
| Highest | THREAD_PRIORITY_HIGHEST |
| Above normal | THREAD_PRIORITY_ABOVE_NORMAL |
| Normal | THREAD_PRIORITY_NORMAL |
| Below normal | THREAD_PRIORITY_BELOW_NORMAL |
| Lowest | THREAD_PRIORITY_LOWEST |
| Idle | THREAD_PRIORITY_IDLE |
int GetThreadPriority( HANDLE hThread );
DWORD dwThreadID;
HANDLE hThread = CreateThread( NULL, 0, ThreadFunc, NULL,
CREATE_SUSPENDED, &dwThreadID );
SetThreadPriority( hThread, THREAD_PRIORITY_IDLE );
ResumeThread( hThread );
CloseHandle( hThread );
10.1 Dynamically Boosting Thread Priority Levels #
BOOL SetProcessPriorityBoost(
HANDLE hProcess,
BOOL DisablePriorityBoost
);
BOOL SetThreadPriorityBoost(
HANDLE hThread,
BOOL DisablePriorityBoost
);
BOOL GetProcessPriorityBoost(
HANDLE hProcess,
PBOOL pDisablePriorityBoost
);
BOOL GetThreadPriorityBoost(
HANDLE hThread,
PBOOL pDisablePriorityBoost
);
10.2 Tweaking the Scheduler for the Foreground Process #
10.3 The Scheduling Lab Sample Application #
SchedLab.cpp
/******************************************************************************
Module: SchedLab.cpp
Notices: Copyright (c) 2000 Jeffrey Richter
******************************************************************************/
#include "..\CmnHdr.H" /* See Appendix A. */
#include <windowsx.h>
#include <tchar.h>
#include <process.h> // For _beginthreadex
#include "Resource.H"
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI ThreadFunc( PVOID pvParam )
{
HANDLE hThreadPrimary = (HANDLE) pvParam;
SuspendThread( hThreadPrimary );
chMB(
"The Primary thread is suspended.\n"
"It no longer responds to input and produces no output.\n"
"Press OK to resume the primary thread & exit this secondary thread.\n"
);
ResumeThread( hThreadPrimary );
CloseHandle( hThreadPrimary );
// To avoid deadlock, call EnableWindow after ResumeThread.
EnableWindow(
GetDlgItem( FindWindow( NULL, TEXT( "Scheduling Lab" ) ), IDC_SUSPEND ),
TRUE );
return ( 0 );
}
///////////////////////////////////////////////////////////////////////////////
BOOL Dlg_OnInitDialog( HWND hwnd, HWND hwndFocus, LPARAM lParam )
{
chSETDLGICONS( hwnd, IDI_SCHEDLAB );
// Initialize process priority classes
HWND hwndCtl = GetDlgItem( hwnd, IDC_PROCESSPRIORITYCLASS );
int n = ComboBox_AddString( hwndCtl, TEXT( "High" ) );
ComboBox_SetItemData( hwndCtl, n, HIGH_PRIORITY_CLASS );
// Save our current priority class
DWORD dwpc = GetPriorityClass( GetCurrentProcess() );
if( SetPriorityClass( GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS ) )
{
// This system supports the BELOW_NORMAL_PRIORITY_CLASS class
// Restore our original priority class
SetPriorityClass( GetCurrentProcess(), dwpc );
// Add the Above Normal priority class
n = ComboBox_AddString( hwndCtl, TEXT( "Above normal" ) );
ComboBox_SetItemData( hwndCtl, n, ABOVE_NORMAL_PRIORITY_CLASS );
dwpc = 0; // Remember that this system supports below normal
}
int nNormal = n = ComboBox_AddString( hwndCtl, TEXT( "Normal" ) );
ComboBox_SetItemData( hwndCtl, n, NORMAL_PRIORITY_CLASS );
if( dwpc == 0 )
{
// This system supports the BELOW_NORMAL_PRIORITY_CLASS class
// Add the Below Normal priority class
n = ComboBox_AddString( hwndCtl, TEXT( "Below normal" ) );
ComboBox_SetItemData( hwndCtl, n, BELOW_NORMAL_PRIORITY_CLASS );
}
n = ComboBox_AddString( hwndCtl, TEXT( "Idle" ) );
ComboBox_SetItemData( hwndCtl, n, IDLE_PRIORITY_CLASS );
ComboBox_SetCurSel( hwndCtl, nNormal );
// Initialize thread relative priorities
hwndCtl = GetDlgItem( hwnd, IDC_THREADRELATIVEPRIORITY );
n = ComboBox_AddString( hwndCtl, TEXT( "Time critical" ) );
ComboBox_SetItemData( hwndCtl, n, THREAD_PRIORITY_TIME_CRITICAL );
n = ComboBox_AddString( hwndCtl, TEXT( "Highest" ) );
ComboBox_SetItemData( hwndCtl, n, THREAD_PRIORITY_HIGHEST );
n = ComboBox_AddString( hwndCtl, TEXT( "Above normal" ) );
ComboBox_SetItemData( hwndCtl, n, THREAD_PRIORITY_ABOVE_NORMAL );
nNormal = n = ComboBox_AddString( hwndCtl, TEXT( "Normal" ) );
ComboBox_SetItemData( hwndCtl, n, THREAD_PRIORITY_NORMAL );
n = ComboBox_AddString( hwndCtl, TEXT( "Below normal" ) );
ComboBox_SetItemData( hwndCtl, n, THREAD_PRIORITY_BELOW_NORMAL );
n = ComboBox_AddString( hwndCtl, TEXT( "Lowest" ) );
ComboBox_SetItemData( hwndCtl, n, THREAD_PRIORITY_LOWEST );
n = ComboBox_AddString( hwndCtl, TEXT( "Idle" ) );
ComboBox_SetItemData( hwndCtl, n, THREAD_PRIORITY_IDLE );
ComboBox_SetCurSel( hwndCtl, nNormal );
Edit_LimitText( GetDlgItem( hwnd, IDC_SLEEPTIME ), 4 ); // Maximum of 9999
return ( TRUE );
}
///////////////////////////////////////////////////////////////////////////////
void Dlg_OnCommand( HWND hwnd, int id, HWND hwndCtl, UINT codeNotify )
{
switch( id ) {
case IDCANCEL:
PostQuitMessage( 0 );
break;
case IDC_PROCESSPRIORITYCLASS:
if( codeNotify == CBN_SELCHANGE )
{
SetPriorityClass( GetCurrentProcess(), (DWORD)
ComboBox_GetItemData( hwndCtl, ComboBox_GetCurSel( hwndCtl ) ) );
}
break;
case IDC_THREADRELATIVEPRIORITY:
if( codeNotify == CBN_SELCHANGE )
{
SetThreadPriority( GetCurrentThread(), (DWORD)
ComboBox_GetItemData( hwndCtl, ComboBox_GetCurSel( hwndCtl ) ) );
}
break;
case IDC_SUSPEND:
// To avoid deadlock, call EnableWindow before creating
// the thread which calls SuspendThread.
EnableWindow( hwndCtl, FALSE );
HANDLE hThreadPrimary;
DuplicateHandle( GetCurrentProcess(), GetCurrentThread(),
GetCurrentProcess(), &hThreadPrimary,
THREAD_SUSPEND_RESUME, FALSE, DUPLICATE_SAME_ACCESS );
DWORD dwThreadID;
CloseHandle( chBEGINTHREADEX( NULL, 0, ThreadFunc,
hThreadPrimary, 0, &dwThreadID ) );
break;
}
}
///////////////////////////////////////////////////////////////////////////////
INT_PTR WINAPI Dlg_Proc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( uMsg ) {
chHANDLE_DLGMSG( hwnd, WM_INITDIALOG, Dlg_OnInitDialog );
chHANDLE_DLGMSG( hwnd, WM_COMMAND, Dlg_OnCommand );
}
return ( FALSE );
}
///////////////////////////////////////////////////////////////////////////////
int WINAPI _tWinMain( HINSTANCE hinstExe, HINSTANCE, LPTSTR pszCmdLine, int )
{
HWND hwnd =
CreateDialog( hinstExe, MAKEINTRESOURCE( IDD_SCHEDLAB ), NULL, Dlg_Proc );
BOOL fQuit = FALSE;
while( !fQuit ) {
MSG msg;
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
// IsDialogMessage allows keyboard navigation to work properly.
if( !IsDialogMessage( hwnd, &msg ) )
{
if( msg.message == WM_QUIT )
{
fQuit = TRUE; // For WM_QUIT, terminate the loop.
} else {
// Not a WM_QUIT message. Translate it and dispatch it.
TranslateMessage( &msg );
DispatchMessage( &msg );
}
} // if( !IsDialogMessage() )
} else {
// Add a number to the listbox
static int s_n = -1;
TCHAR sz[20];
wsprintf( sz, TEXT( "%u" ), ++s_n );
HWND hwndWork = GetDlgItem( hwnd, IDC_WORK );
ListBox_SetCurSel( hwndWork, ListBox_AddString( hwndWork, sz ) );
// Remove some strings if there are too many entries
while( ListBox_GetCount( hwndWork ) > 100 )
ListBox_DeleteString( hwndWork, 0 );
// How long should the thread sleep
int nSleep = GetDlgItemInt( hwnd, IDC_SLEEPTIME, NULL, FALSE );
if( chINRANGE( 1, nSleep, 9999 ) )
Sleep( nSleep );
}
}
DestroyWindow( hwnd );
return ( 0 );
}
//////////////////////////////// End of File //////////////////////////////////
SchedLab.rc
// Microsoft Developer Studio generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_SCHEDLAB DIALOGEX 0, 0, 209, 70
STYLE DS_3DLOOK | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION |
WS_SYSMENU
EXSTYLE WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE
CAPTION "Scheduling Lab"
FONT 8, "MS Sans Serif"
BEGIN
LTEXT "&Process priority class:",IDC_STATIC,4,6,68,8
COMBOBOX IDC_PROCESSPRIORITYCLASS,84,4,72,80,CBS_DROPDOWNLIST |
WS_TABSTOP
LTEXT "&Thread relative priority:",IDC_STATIC,4,20,72,8
COMBOBOX IDC_THREADRELATIVEPRIORITY,84,18,72,76,CBS_DROPDOWNLIST |
WS_TABSTOP
LTEXT "Sleep (0 to 9999 &ms):",IDC_STATIC,4,36,68,8
EDITTEXT IDC_SLEEPTIME,84,34,32,14,ES_NUMBER
PUSHBUTTON "&Suspend",IDC_SUSPEND,4,52,49,14
LISTBOX IDC_WORK,160,4,48,60,NOT LBS_NOTIFY |
LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_TABSTOP
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
IDD_SCHEDLAB, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 202
TOPMARGIN, 7
BOTTOMMARGIN, 63
END
END
#endif // APSTUDIO_INVOKED
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_SCHEDLAB ICON DISCARDABLE "SchedLab.ico"
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
BOOL SetProcessAffinityMask(
HANDLE hProcess,
DWORD_PTR dwProcessAffinityMask
);
BOOL GetProcessAffinityMask(
HANDLE hProcess,
PDWORD_PTR pdwProcessAffinityMask,
PDWORD_PTR pdwSystemAffinityMask
);
DWORD_PTR SetThreadAffinityMask(
HANDLE hThread,
DWORD_PTR dwThreadAffinityMask
);
// Thread 0 can only run on CPU 0.
SetThreadAffinityMask( hThread0, 0x00000001 );
// Threads 1, 2, 3 run on CPUs 1, 2, 3.
SetThreadAffinityMask( hThread1, 0x0000000E );
SetThreadAffinityMask( hThread2, 0x0000000E );
SetThreadAffinityMask( hThread3, 0x0000000E );
| Thread | Priority | Affinity Mask | Result |
| A | 4 | 0x00000001 | CPU 0 |
| B | 8 | 0x00000003 | CPU 1 |
| C | 6 | 0x00000002 | Can't run |
DWORD SetThreadIdealProcessor(
HANDLE hThread,
DWORD dwIdealProcessor
);
// Load the EXE into memory.
PLOADED_IMAGE pLoadedImage = ImageLoad( szExeName, NULL );
// Get the current load configuration information for the EXE.
IMAGE_LOAD_CONFIG_DIRECTORY ilcd;
GetImageConfigInformation( pLoadedImage, &ilcd );
// Change the processor affinity mask.
ilcd.ProcessAffinityMask = 0x00000003; // I desire CPUs 0 and 1
// Save the new load configuration information.
SetImageConfigInformation( pLoadedImage, &ilcd );
// Unload the EXE from memory.
ImageUnload( pLoadedImage );
usage: IMAGECFG [switches] image-names...
[-?] display this message
[-a Process Affinity mask value in hex]
[-b BuildNumber]
[-c Win32 GetVersionEx Service Pack return value in hex]
[-d decommit thresholds]
[-g bitsToClear bitsToSet]
[-h 1|0 (Enable/Disable Terminal Server Compatible bit)
[-k StackReserve[.StackCommit]
[-l enable large (>2GB) adresses
[-m maximum allocation size]
[-n bind no longer allowed on this image
[-o default critical section timeout
[-p process heap flags]
[-q only print config info if changed
[-r run with restricted working set]
[-s path to symbol files]
[-t VirtualAlloc threshold]
[-u Marks image as uniprocesor only]
[-v MajorVersion.MinorVersion]
[-w Win32 GetVersion return value in hex]
[-x Mark image as Net - Run From Swapfile
[-y Mark image as Removable - Run From Swapfile
[boot loader]
timeout=2
default=multi(0)disk(0)rdisk(0)partition(1)\WINNT
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINNT="Windows 2000 Server"
/fastdetect
multi(0)disk(0)rdisk(0)partition(1)\WINNT="Windows 2000 Server"
/fastdetect /NumProcs=1
CategoryTranslation