windows-nt/Source/XPSP1/NT/shell/cpls/system/virtual.c
2020-09-26 16:20:57 +08:00

2409 lines
63 KiB
C

/*++
Microsoft Confidential
Copyright (c) 1992-1997 Microsoft Corporation
All rights reserved
Module Name:
virtual.c
Abstract:
Implements the Change Virtual Memory dialog of the System
Control Panel Applet
Notes:
The virtual memory settings and the crash dump (core dump) settings
are tightly-coupled. Therefore, virtual.c and virtual.h have some
heavy dependencies on crashdmp.c and startup.h (and vice versa).
Author:
Byron Dazey 06-Jun-1992
Revision History:
14-Apr-93 JonPa
maintain paging path if != \pagefile.sys
15-Dec-93 JonPa
added Crash Recovery dialog
02-Feb-1994 JonPa
integrated crash recover and virtual memory settings
18-Sep-1995 Steve Cathcart
split system.cpl out from NT3.51 main.cpl
12-Jan-1996 JonPa
made part of the new SUR pagified system.cpl
15-Oct-1997 scotthal
Split out CoreDump*() stuff into separate file
09-Jul-2000 SilviuC
Allow very big page files if architecture supports it.
Allow booting without a page file.
Allow the system to scale the page file size based on RAM changes.
--*/
//==========================================================================
// Include files
//==========================================================================
// NT base apis
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntdddisk.h>
#include <help.h>
// Application specific
#include "sysdm.h"
//==========================================================================
// External Data Declarations
//==========================================================================
extern HFONT hfontBold;
//==========================================================================
// Local Definitions
//==========================================================================
#define MAX_SIZE_LEN 8 // Max chars in the Swap File Size edit.
#define MIN_FREESPACE 5 // Must have 5 meg free after swap file
#define MIN_SUGGEST 22 // Always suggest at least 22 meg
#define CCHMBSTRING 12 // Space for localizing the "MB" string.
/*
* Space for 26 pagefile info structures and 26 paths to pagefiles.
*/
#define PAGEFILE_INFO_BUFFER_SIZE MAX_DRIVES * sizeof(SYSTEM_PAGEFILE_INFORMATION) + \
MAX_DRIVES * MAX_PATH * sizeof(TCHAR)
/*
* Maximum length of volume info line in the listbox.
* A: [ Vol_label ] %d - %d
*/
#define MAX_VOL_LINE (3 + 1 + MAX_PATH + 2 + 10 + 3 + 10)
/*
* This amount will be added to the minimum page file size to determine
* the maximum page file size if it is not explicitly specified.
*/
#define MAXOVERMINFACTOR 50
#define TABSTOP_VOL 22
#define TABSTOP_SIZE 122
/*
* My privilege 'handle' structure
*/
typedef struct {
HANDLE hTok;
TOKEN_PRIVILEGES tp;
} PRIVDAT, *PPRIVDAT;
//==========================================================================
// Typedefs and Structs
//==========================================================================
// registry info for a page file (but not yet formatted).
//Note: since this structure gets passed to FormatMessage, all fields must
//be 4 bytes wide (or 8 bytes on Win64)
typedef struct
{
LPTSTR pszName;
DWORD_PTR nMin;
DWORD_PTR nMax;
DWORD_PTR chNull;
} PAGEFILDESC;
//==========================================================================
// Global Data Declarations
//==========================================================================
HKEY ghkeyMemMgt = NULL;
int gcrefMemMgt = 0;
VCREG_RET gvcMemMgt = VCREG_ERROR;
int gcrefPagingFiles = 0;
TCHAR g_szSysDir[ MAX_PATH ];
//==========================================================================
// Local Data Declarations
//==========================================================================
/*
* Virtual Memory Vars
*/
// Registry Key and Value Names
TCHAR szMemMan[] =
TEXT("System\\CurrentControlSet\\Control\\Session Manager\\Memory Management");
TCHAR szSessionManager[] = TEXT("System\\CurrentControlSet\\Control\\Session Manager");
TCHAR szPendingRename[] = TEXT("PendingFileRenameOperations");
TCHAR szRenameFunkyPrefix[] = TEXT("\\??\\");
#ifndef VM_DBG
TCHAR szPagingFiles[] = TEXT("PagingFiles");
TCHAR szPagedPoolSize[] = TEXT("PagedPoolSize");
#else
// temp values for testing only!
TCHAR szPagingFiles[] = TEXT("TestPagingFiles");
TCHAR szPagedPoolSize[] = TEXT("TestPagedPoolSize");
#endif
/* Array of paging files. This is indexed by the drive letter (A: is 0). */
PAGING_FILE apf[MAX_DRIVES];
PAGING_FILE apfOriginal[MAX_DRIVES];
// Other VM Vars
TCHAR szPagefile[] = TEXT("x:\\pagefile.sys");
TCHAR szNoPageFile[] = TEXT("TempPageFile");
TCHAR szMB[CCHMBSTRING];
DWORD dwFreeMB;
DWORD cmTotalVM;
DWORD cmRegSizeLim;
DWORD cmPagedPoolLim;
DWORD cmRegUsed;
static DWORD cxLBExtent;
static int cxExtra;
//
// Help IDs
//
DWORD aVirtualMemHelpIds[] = {
IDC_STATIC, NO_HELP,
IDD_VM_VOLUMES, NO_HELP,
IDD_VM_DRIVE_HDR, (IDH_DLG_VIRTUALMEM + 0),
IDD_VM_PF_SIZE_LABEL, (IDH_DLG_VIRTUALMEM + 1),
IDD_VM_DRIVE_LABEL, (IDH_DLG_VIRTUALMEM + 2),
IDD_VM_SF_DRIVE, (IDH_DLG_VIRTUALMEM + 2),
IDD_VM_SPACE_LABEL, (IDH_DLG_VIRTUALMEM + 3),
IDD_VM_SF_SPACE, (IDH_DLG_VIRTUALMEM + 3),
IDD_VM_ST_INITSIZE, (IDH_DLG_VIRTUALMEM + 4),
IDD_VM_SF_SIZE, (IDH_DLG_VIRTUALMEM + 4),
IDD_VM_ST_MAXSIZE, (IDH_DLG_VIRTUALMEM + 5),
IDD_VM_SF_SIZEMAX, (IDH_DLG_VIRTUALMEM + 5),
IDD_VM_SF_SET, (IDH_DLG_VIRTUALMEM + 6),
IDD_VM_MIN_LABEL, (IDH_DLG_VIRTUALMEM + 7),
IDD_VM_MIN, (IDH_DLG_VIRTUALMEM + 7),
IDD_VM_RECOMMEND_LABEL, (IDH_DLG_VIRTUALMEM + 8),
IDD_VM_RECOMMEND, (IDH_DLG_VIRTUALMEM + 8),
IDD_VM_ALLOCD_LABEL, (IDH_DLG_VIRTUALMEM + 9),
IDD_VM_ALLOCD, (IDH_DLG_VIRTUALMEM + 9),
IDD_VM_CUSTOMSIZE_RADIO,(IDH_DLG_VIRTUALMEM + 12),
IDD_VM_RAMBASED_RADIO, (IDH_DLG_VIRTUALMEM + 13),
IDD_VM_NOPAGING_RADIO, (IDH_DLG_VIRTUALMEM + 14),
0,0
};
#if 0
Plan for splitting this into propert sheets:
1. Make the VM and CC registry keys globals that are inited
to NULL (or INVALID_HANDLE_VALUE). Also make gvcVirt and
vcCore to be globals (so we can tell how the reg was opened
inside virtinit().)
1. Change all RegCloseKey's to VirtualCloseKey and CoreDumpCloseKey
2. Change VirtualOpenKey and CoreDumpOpenKey from macros to
functions that return the global handles if they are already
opened, or else opens them.
3. In the Perf and Startup pages, call VirtualOpenKey,
CoreDumpOpenKey, and VirtualGetPageFiles.
-- now we can call VirtualMemComputeAlloced() from the perf page
-- we can also just execute the CrashDump code in the startup page
4. rewrite VirtInit to not try and open the keys again, but instesd
use gvcVirt, vcCore, hkeyVM and kheyCC.
4. Write VirtualCloseKey and CoreDumpCloseKey as follows...
4.a If hkey == NULL return
4.b RegCloseKey(hkey)
4.c hkey = NULL
5. In the PSN_RESET and PSN_APPLY cases for Perf and Startup pages
call VirtualCloseKey and CoreDumpCloseKey
#endif
//==========================================================================
// Local Function Prototypes
//==========================================================================
static BOOL VirtualMemInit(HWND hDlg);
static BOOL ParsePageFileDesc(LPTSTR *ppszDesc, INT *pnDrive,
INT *pnMinFileSize, INT *pnMaxFileSize, LPTSTR *ppszName);
static VOID VirtualMemBuildLBLine(LPTSTR pszBuf, INT iDrive);
static INT GetMaxSpaceMB(INT iDrive);
static VOID VirtualMemSelChange(HWND hDlg);
static VOID VirtualMemUpdateAllocated(HWND hDlg);
int VirtualMemComputeTotalMax( void );
static BOOL VirtualMemSetNewSize(HWND hDlg);
static UINT VMGetDriveType(LPCTSTR lpszDrive);
void VirtualMemReconcileState();
void GetAPrivilege( LPTSTR pszPrivilegeName, PPRIVDAT ppd );
void ResetOldPrivilege( PPRIVDAT ppdOld );
DWORD VirtualMemDeletePagefile( LPTSTR szPagefile );
#define GetPageFilePrivilege( ppd ) \
GetAPrivilege(SE_CREATE_PAGEFILE_NAME, ppd)
#define GetRegistryQuotaPrivilege( ppd ) \
GetAPrivilege(SE_INCREASE_QUOTA_NAME, ppd)
//==========================================================================
VCREG_RET VirtualOpenKey( void ) {
DOUT("In VirtOpenKey" );
if (gvcMemMgt == VCREG_ERROR) {
gvcMemMgt = OpenRegKey( szMemMan, &ghkeyMemMgt );
}
if (gvcMemMgt != VCREG_ERROR)
gcrefMemMgt++;
DPRINTF((TEXT("SYSCPL.CPL: VirtOpenKey, cref=%d\n"), gcrefMemMgt ));
return gvcMemMgt;
}
void VirtualCloseKey(void) {
DOUT( "In VirtCloseKey" );
if (gcrefMemMgt > 0) {
gcrefMemMgt--;
if (gcrefMemMgt == 0) {
CloseRegKey( ghkeyMemMgt );
gvcMemMgt = VCREG_ERROR;
}
}
DPRINTF((TEXT("SYSCPL.CPL: VirtCloseKey, cref=%d\n"), gcrefMemMgt ));
}
LPTSTR SkipNonWhiteSpace( LPTSTR sz ) {
while( *sz != TEXT('\0') && !IsWhiteSpace(*sz))
sz++;
return sz;
}
INT TranslateDlgItemInt( HWND hDlg, int id ) {
/*
* We can't just call GetDlgItemInt because the
* string we are trying to translate looks like:
* nnn (MB), and the '(MB)' would break GetDlgInt.
*/
TCHAR szBuffer[256];
int i = 0;
if (GetDlgItemText(hDlg, id, szBuffer,
sizeof(szBuffer) / sizeof(*szBuffer))) {
i = StringToInt( szBuffer );
}
return i;
}
LPTSTR SZPageFileName (int i)
{
if (apf[i].pszPageFile != NULL) {
return apf[i].pszPageFile;
}
szPagefile[0] = (TCHAR)(i + (int)TEXT('A'));
return szPagefile;
}
LONG GetRegistryInt( HKEY hkey, LPTSTR pszValue, LONG lDefault ) {
DWORD dwType;
DWORD cbTemp;
DWORD dwVal;
cbTemp = sizeof(DWORD);
if (RegQueryValueEx (hkey, pszValue, NULL,
&dwType, (LPBYTE)&dwVal, &cbTemp) != ERROR_SUCCESS ||
dwType != REG_DWORD || cbTemp != sizeof(DWORD)) {
dwVal = (DWORD)lDefault;
}
return (LONG)dwVal;
}
BOOL SetRegistryInt( HKEY hkey, LPTSTR pszValue, LONG iValue ) {
return RegSetValueEx(hkey, pszValue, 0L, REG_DWORD, (LPBYTE)&iValue,
sizeof(iValue)) == ERROR_SUCCESS;
}
void VirtualCopyPageFiles( PAGING_FILE *apfDest, BOOL fFreeOld, PAGING_FILE *apfSrc, BOOL fCloneStrings ) {
int i;
for (i = 0; i < MAX_DRIVES; i++) {
if (fFreeOld && apfDest[i].pszPageFile != NULL) {
MemFree(apfDest[i].pszPageFile);
}
if (apfSrc != NULL) {
apfDest[i] = apfSrc[i];
if (fCloneStrings && apfDest[i].pszPageFile != NULL) {
apfDest[i].pszPageFile = CloneString(apfDest[i].pszPageFile);
}
}
}
}
/*
* VirtualMemDlg
*
*
*
*/
INT_PTR
APIENTRY
VirtualMemDlg(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
static int fEdtCtlHasFocus = 0;
switch (message)
{
case WM_INITDIALOG:
VirtualMemInit(hDlg);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDD_VM_VOLUMES:
/*
* Make edit control reflect the listbox selection.
*/
if (HIWORD(wParam) == LBN_SELCHANGE)
VirtualMemSelChange(hDlg);
break;
case IDD_VM_SF_SET:
if (VirtualMemSetNewSize(hDlg))
SetDefButton(hDlg, IDOK);
break;
case IDOK:
{
int iRet = VirtualMemPromptForReboot(hDlg);
// RET_ERROR means the user told us not to overwrite an
// existing file called pagefile.sys, so we shouldn't
// end the dialog just yet.
if (RET_ERROR == iRet) {
break;
}
VirtualMemUpdateRegistry();
VirtualMemReconcileState();
VirtualCloseKey();
#if 0
CoreDumpCloseKey();
#endif
#if 0
if (gfCoreDumpChanged)
iRet |= RET_RECOVER_CHANGE;
#endif
//
// get rid of backup copy of pagefile structs
//
VirtualCopyPageFiles( apfOriginal, TRUE, NULL, FALSE );
EndDialog(hDlg, iRet);
HourGlass(FALSE);
break;
}
case IDCANCEL:
//
// get rid of changes and restore original values
//
VirtualCopyPageFiles( apf, TRUE, apfOriginal, FALSE );
VirtualCloseKey();
#if 0
CoreDumpCloseKey();
#endif
EndDialog(hDlg, RET_NO_CHANGE);
HourGlass(FALSE);
break;
case IDD_VM_SF_SIZE:
case IDD_VM_SF_SIZEMAX:
switch(HIWORD(wParam))
{
case EN_CHANGE:
if (fEdtCtlHasFocus != 0)
SetDefButton( hDlg, IDD_VM_SF_SET);
break;
case EN_SETFOCUS:
fEdtCtlHasFocus++;
break;
case EN_KILLFOCUS:
fEdtCtlHasFocus--;
break;
}
break;
case IDD_VM_NOPAGING_RADIO:
case IDD_VM_RAMBASED_RADIO:
if( HIWORD(wParam) == BN_CLICKED )
{
EnableWindow( GetDlgItem( hDlg, IDD_VM_SF_SIZE ), FALSE );
EnableWindow( GetDlgItem( hDlg, IDD_VM_SF_SIZEMAX ), FALSE );
}
break;
case IDD_VM_CUSTOMSIZE_RADIO:
if( HIWORD(wParam) == BN_CLICKED )
{
EnableWindow( GetDlgItem( hDlg, IDD_VM_SF_SIZE ), TRUE );
EnableWindow( GetDlgItem( hDlg, IDD_VM_SF_SIZEMAX ), TRUE );
}
break;
default:
break;
}
break;
case WM_HELP: // F1
WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, HELP_FILE, HELP_WM_HELP,
(DWORD_PTR) (LPSTR) aVirtualMemHelpIds);
break;
case WM_CONTEXTMENU: // right mouse click
WinHelp((HWND) wParam, HELP_FILE, HELP_CONTEXTMENU,
(DWORD_PTR) (LPSTR) aVirtualMemHelpIds);
break;
case WM_DESTROY:
{
VirtualFreePageFiles(apf);
/*
* The docs were not clear as to what a dialog box should return
* for this message, so I am going to punt and let the defdlgproc
* doit.
*/
/* FALL THROUGH TO DEFAULT CASE! */
}
default:
return FALSE;
break;
}
return TRUE;
}
/*
* UINT VMGetDriveType( LPCTSTR lpszDrive )
*
* Gets the drive type. This function differs from Win32's GetDriveType
* in that it returns DRIVE_FIXED for lockable removable drives (like
* bernolli boxes, etc).
*
* On IA64 we don't do this, however, requiring all pagefiles be on actual
* fixed drives.
*/
TCHAR szDevice[] = TEXT("\\Device");
UINT VMGetDriveType( LPCTSTR lpszDrive ) {
UINT i;
TCHAR szDevName[MAX_PATH];
// Check for subst drive
if (QueryDosDevice( lpszDrive, szDevName, ARRAYSIZE( szDevName ) ) != 0) {
// If drive does not start with '\Device', then it is not FIXED
szDevName[ARRAYSIZE(szDevice) - 1] = '\0';
if ( lstrcmpi(szDevName, szDevice) != 0 ) {
return DRIVE_REMOTE;
}
}
i = GetDriveType( lpszDrive );
#ifndef _WIN64
if ( i == DRIVE_REMOVABLE ) {
TCHAR szNtDrive[20];
DWORD cb;
DISK_GEOMETRY dgMediaInfo;
HANDLE hDisk;
/*
* 'Removable' drive. Check to see if it is a Floppy or lockable
* drive.
*/
cb = wsprintf( szNtDrive, TEXT("\\\\.\\%s"), lpszDrive );
if ( cb != 0 && IsPathSep(szNtDrive[--cb]) ) {
szNtDrive[cb] = TEXT('\0');
}
hDisk = CreateFile(
szNtDrive,
/* GENERIC_READ */ 0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hDisk != INVALID_HANDLE_VALUE ) {
if (DeviceIoControl( hDisk, IOCTL_DISK_GET_MEDIA_TYPES, NULL,
0, &dgMediaInfo, sizeof(dgMediaInfo), &cb, NULL) == FALSE &&
GetLastError() != ERROR_MORE_DATA) {
/*
* Drive is not a floppy
*/
i = DRIVE_FIXED;
}
CloseHandle(hDisk);
} else if (GetLastError() == ERROR_ACCESS_DENIED) {
/*
* Could not open the drive, either it is bad, or else we
* don't have permission. Since everyone has permission
* to open floppies, then this must be a bernoulli type device.
*/
i = DRIVE_FIXED;
}
}
#endif
return i;
}
/*
* BOOL VirtualGetPageFiles(PAGING_FILE *apf)
*
* Fills in the PAGING_FILE array from the values stored in the registry
*/
BOOL VirtualGetPageFiles(PAGING_FILE *apf) {
DWORD cbTemp;
LPTSTR szTemp;
DWORD dwType;
INT nDrive;
INT nMinFileSize;
INT nMaxFileSize;
LPTSTR psz;
DWORD dwDriveMask;
int i;
static TCHAR szDir[] = TEXT("?:");
DPRINTF((TEXT("SYSCPL: In VirtualGetPageFile, cref=%d\n"), gcrefPagingFiles));
if (gcrefPagingFiles++ > 0) {
// Paging files already loaded
return TRUE;
}
dwDriveMask = GetLogicalDrives();
for (i = 0; i < MAX_DRIVES; dwDriveMask >>= 1, i++)
{
apf[i].fCanHavePagefile = FALSE;
apf[i].nMinFileSize = 0;
apf[i].nMaxFileSize = 0;
apf[i].nMinFileSizePrev = 0;
apf[i].nMaxFileSizePrev = 0;
apf[i].pszPageFile = NULL;
if (dwDriveMask & 0x01)
{
szDir[0] = TEXT('A') + i;
switch (VMGetDriveType(szDir))
{
case DRIVE_FIXED:
apf[i].fCanHavePagefile = TRUE;
break;
default:
break;
}
}
}
if (RegQueryValueEx (ghkeyMemMgt, szPagingFiles, NULL, &dwType,
(LPBYTE) NULL, &cbTemp) != ERROR_SUCCESS)
{
// Could not get the current virtual memory settings size.
return FALSE;
}
if ((szTemp = MemAlloc(LPTR, cbTemp)) == NULL)
{
// Could not alloc a buffer for the vmem settings
return FALSE;
}
szTemp[0] = 0;
if (RegQueryValueEx (ghkeyMemMgt, szPagingFiles, NULL, &dwType,
(LPBYTE) szTemp, &cbTemp) != ERROR_SUCCESS)
{
// Could not read the current virtual memory settings.
MemFree(szTemp);
return FALSE;
}
psz = szTemp;
while (*psz)
{
LPTSTR pszPageName;
/*
* If the parse works, and this drive can have a pagefile on it,
* update the apf table. Note that this means that currently
* specified pagefiles for invalid drives will be stripped out
* of the registry if the user presses OK for this dialog.
*/
if (ParsePageFileDesc(&psz, &nDrive, &nMinFileSize, &nMaxFileSize, &pszPageName))
{
if (apf[nDrive].fCanHavePagefile)
{
apf[nDrive].nMinFileSize =
apf[nDrive].nMinFileSizePrev = nMinFileSize;
apf[nDrive].nMaxFileSize =
apf[nDrive].nMaxFileSizePrev = nMaxFileSize;
apf[nDrive].pszPageFile = pszPageName;
}
}
}
MemFree(szTemp);
return TRUE;
}
/*
* VirtualFreePageFiles
*
* Frees data alloced by VirtualGetPageFiles
*
*/
void VirtualFreePageFiles(PAGING_FILE *apf) {
int i;
DPRINTF((TEXT("SYSCPL: In VirtualFreePageFile, cref=%d\n"), gcrefPagingFiles));
if (gcrefPagingFiles > 0) {
gcrefPagingFiles--;
if (gcrefPagingFiles == 0) {
for (i = 0; i < MAX_DRIVES; i++) {
if (apf[i].pszPageFile != NULL)
MemFree(apf[i].pszPageFile);
}
}
}
}
/*
* VirtualInitStructures()
*
* Calls VirtualGetPageFiles so other helpers can be called from the Perf Page.
*
* Returns:
* TRUE if success, FALSE if failure
*/
BOOL VirtualInitStructures( void ) {
VCREG_RET vcVirt;
BOOL fRet = FALSE;
vcVirt = VirtualOpenKey();
if (vcVirt != VCREG_ERROR)
fRet = VirtualGetPageFiles( apf );
LoadString(hInstance, IDS_SYSDM_MB, szMB, CCHMBSTRING);
return fRet;
}
void VirtualFreeStructures( void ) {
VirtualFreePageFiles(apf);
VirtualCloseKey();
}
/*
* LPTSTR BackslashTerm( LPTSTR pszPath )
*/
LPTSTR BackslashTerm( LPTSTR pszPath )
{
LPTSTR pszEnd;
pszEnd = pszPath + lstrlen( pszPath );
//
// Get the end of the source directory
//
switch( *CharPrev( pszPath, pszEnd ) )
{
case TEXT('\\'):
case TEXT(':'):
break;
default:
*pszEnd++ = TEXT( '\\' );
*pszEnd = TEXT( '\0' );
}
return( pszEnd );
}
/*
* VirtualMemInit
*
* Initializes the Virtual Memory dialog.
*
* Arguments:
* HWND hDlg - Handle to the dialog window.
*
* Returns:
* TRUE
*/
static
BOOL
VirtualMemInit(
HWND hDlg
)
{
TCHAR szTemp[MAX_VOL_LINE];
DWORD i;
INT iItem;
HWND hwndLB;
INT aTabs[2];
RECT rc;
VCREG_RET vcVirt;
SYSTEM_BASIC_INFORMATION BasicInfo;
NTSTATUS status;
unsigned __int64 TotalPhys;
HourGlass(TRUE);
//
// Load the "MB" string.
//
LoadString(hInstance, IDS_SYSDM_MB, szMB, CCHMBSTRING);
////////////////////////////////////////////////////////////////////
// List all drives
////////////////////////////////////////////////////////////////////
vcVirt = VirtualOpenKey();
if (vcVirt == VCREG_ERROR ) {
// Error - cannot even get list of paging files from registry
MsgBoxParam(hDlg, IDS_SYSDM_NOOPEN_VM_NOTUSER, IDS_SYSDM_TITLE, MB_ICONEXCLAMATION);
EndDialog(hDlg, RET_NO_CHANGE);
HourGlass(FALSE);
if (ghkeyMemMgt != NULL)
VirtualCloseKey();
return FALSE;
}
/*
* To change Virtual Memory size or Crash control, we need access
* to both the CrashCtl key and the PagingFiles value in the MemMgr key
*/
if (vcVirt == VCREG_READONLY ) {
/*
* Disable some fields, because they only have Read access.
*/
EnableWindow(GetDlgItem(hDlg, IDD_VM_SF_SIZE), FALSE);
EnableWindow(GetDlgItem(hDlg, IDD_VM_SF_SIZEMAX), FALSE);
EnableWindow(GetDlgItem(hDlg, IDD_VM_ST_INITSIZE), FALSE);
EnableWindow(GetDlgItem(hDlg, IDD_VM_ST_MAXSIZE), FALSE);
EnableWindow(GetDlgItem(hDlg, IDD_VM_SF_SET), FALSE);
}
if (!VirtualGetPageFiles(apf)) {
// Could not read the current virtual memory settings.
MsgBoxParam(hDlg, IDS_SYSDM_CANNOTREAD, IDS_SYSDM_TITLE, MB_ICONEXCLAMATION);
}
//
// Save a backup copy of the current pagefile structs
//
VirtualCopyPageFiles( apfOriginal, FALSE, apf, TRUE );
hwndLB = GetDlgItem(hDlg, IDD_VM_VOLUMES);
aTabs[0] = TABSTOP_VOL;
aTabs[1] = TABSTOP_SIZE;
SendMessage(hwndLB, LB_SETTABSTOPS, 2, (LPARAM)&aTabs);
/*
* Since SetGenLBWidth only counts tabs as one character, we must compute
* the maximum extra space that the tab characters will expand to and
* arbitrarily tack it onto the end of the string width.
*
* cxExtra = 1st Tab width + 1 default tab width (8 chrs) - strlen("d:\t\t");
*
* (I know the docs for LB_SETTABSTOPS says that a default tab == 2 dlg
* units, but I have read the code, and it is really 8 chars)
*/
rc.top = rc.left = 0;
rc.bottom = 8;
rc.right = TABSTOP_VOL + (4 * 8) - (4 * 4);
MapDialogRect( hDlg, &rc );
cxExtra = rc.right - rc.left;
cxLBExtent = 0;
for (i = 0; i < MAX_DRIVES; i++)
{
// Assume we don't have to create anything
apf[i].fCreateFile = FALSE;
if (apf[i].fCanHavePagefile)
{
VirtualMemBuildLBLine(szTemp, i);
iItem = (INT)SendMessage(hwndLB, LB_ADDSTRING, 0, (LPARAM)szTemp);
SendMessage(hwndLB, LB_SETITEMDATA, iItem, i);
// SetGenLBWidth(hwndLB, szTemp, &cxLBExtent, hfontBold, cxExtra);
cxLBExtent = SetLBWidthEx( hwndLB, szTemp, cxLBExtent, cxExtra);
}
}
SendDlgItemMessage(hDlg, IDD_VM_SF_SIZE, EM_LIMITTEXT, MAX_SIZE_LEN, 0L);
SendDlgItemMessage(hDlg, IDD_VM_SF_SIZEMAX, EM_LIMITTEXT, MAX_SIZE_LEN, 0L);
/*
* Get the total physical memory in the machine.
*/
status = NtQuerySystemInformation(
SystemBasicInformation,
&BasicInfo,
sizeof(BasicInfo),
NULL
);
if (NT_SUCCESS(status)) {
TotalPhys = (unsigned __int64) BasicInfo.NumberOfPhysicalPages * BasicInfo.PageSize;
}
else {
TotalPhys = 0;
}
SetDlgItemMB(hDlg, IDD_VM_MIN, MIN_SWAPSIZE);
// Recommended pagefile size is 1.5 * RAM size these days.
// Nonintegral multiplication with unsigned __int64s is fun!
// This will obviously fail if the machine has total RAM
// greater than 13194139533312 MB (75% of a full 64-bit address
// space). Hopefully by the time someone has such a beast we'll
// have __int128s to hold the results of this calculation.
TotalPhys >>= 20; // Bytes to MB
TotalPhys *= 3; // This will always fit because of the operation above
TotalPhys >>= 1; // x*3/2 == 1.5*x, more or less
i = (DWORD) TotalPhys; // This cast actually causes the
// algorithm to fail if the machine has
// more than ~ 3.2 billion MB of RAM.
// At that point, either the Win32 API has
// to change to allow me to pass __int64s
// as message params, or we have to start
// reporting these stats in GB.
SetDlgItemMB(hDlg, IDD_VM_RECOMMEND, max(i, MIN_SUGGEST));
/*
* Select the first drive in the listbox.
*/
SendDlgItemMessage(hDlg, IDD_VM_VOLUMES, LB_SETCURSEL, 0, 0L);
VirtualMemSelChange(hDlg);
VirtualMemUpdateAllocated(hDlg);
/*
* Show RegQuota
*/
cmTotalVM = VirtualMemComputeTotalMax();
HourGlass(FALSE);
return TRUE;
}
/*
* ParseSDD
*/
int ParseSDD( LPTSTR psz, LPTSTR szPath, INT *pnMinFileSize, INT *pnMaxFileSize) {
int cMatched = 0;
LPTSTR pszNext;
psz = SkipWhiteSpace(psz);
if (*psz) {
int cch;
cMatched++;
pszNext = SkipNonWhiteSpace(psz);
cch = (int)(pszNext - psz);
CopyMemory( szPath, psz, sizeof(TCHAR) * cch );
szPath[cch] = TEXT('\0');
psz = SkipWhiteSpace(pszNext);
if (*psz) {
cMatched++;
pszNext = SkipNonWhiteSpace(psz);
*pnMinFileSize = StringToInt( psz );
psz = SkipWhiteSpace(pszNext);
if (*psz) {
cMatched++;
*pnMaxFileSize = StringToInt( psz );
}
}
}
return cMatched;
}
/*
* ParsePageFileDesc
*
*
*
* Arguments:
*
* Returns:
*
*/
static
BOOL
ParsePageFileDesc(
LPTSTR *ppszDesc,
INT *pnDrive,
INT *pnMinFileSize,
INT *pnMaxFileSize,
LPTSTR *ppstr
)
{
LPTSTR psz;
LPTSTR pszName = NULL;
int cFields;
TCHAR chDrive;
TCHAR szPath[MAX_PATH];
/*
* Find the end of this REG_MULTI_SZ string and point to the next one
*/
psz = *ppszDesc;
*ppszDesc = psz + lstrlen(psz) + 1;
/*
* Parse the string from "filename minsize maxsize"
*/
szPath[0] = TEXT('\0');
*pnMinFileSize = 0;
*pnMaxFileSize = 0;
/* Try it without worrying about quotes */
cFields = ParseSDD( psz, szPath, pnMinFileSize, pnMaxFileSize);
if (cFields < 2)
return FALSE;
/*
* Find the drive index
*/
chDrive = (TCHAR)CharUpper((LPTSTR)*szPath);
if (chDrive < TEXT('A') || chDrive > TEXT('Z'))
return FALSE;
*pnDrive = (INT)(chDrive - TEXT('A'));
/* if the path != x:\pagefile.sys then save it */
if (lstrcmpi(szPagefile + 1, szPath + 1) != 0)
{
pszName = CloneString(szPath);
}
*ppstr = pszName;
if (cFields < 3)
{
INT nSpace;
// don't call GetDriveSpace if the drive is invalid
if (apf[*pnDrive].fCanHavePagefile)
nSpace = GetMaxSpaceMB(*pnDrive);
else
nSpace = 0;
*pnMaxFileSize = min(*pnMinFileSize + MAXOVERMINFACTOR, nSpace);
}
/*
* If the page file size in the registry is zero it means this is
* a RAM based page file.
*/
if (*pnMinFileSize == 0) {
apf[*pnDrive].fRamBasedPagefile = TRUE;
}
else {
apf[*pnDrive].fRamBasedPagefile = FALSE;
}
return TRUE;
}
/*
* VirtualMemBuildLBLine
*
*
*
*/
static
VOID
VirtualMemBuildLBLine(
LPTSTR pszBuf,
INT iDrive
)
{
TCHAR szVolume[MAX_PATH];
TCHAR szTemp[MAX_PATH];
szTemp[0] = TEXT('A') + iDrive;
szTemp[1] = TEXT(':');
szTemp[2] = TEXT('\\');
szTemp[3] = 0;
*szVolume = 0;
GetVolumeInformation(szTemp, szVolume, MAX_PATH,
NULL, NULL, NULL, NULL, 0);
szTemp[2] = TEXT('\t');
lstrcpy(pszBuf, szTemp);
if (*szVolume)
{
lstrcat(pszBuf, TEXT("["));
lstrcat(pszBuf, szVolume);
lstrcat(pszBuf, TEXT("]"));
}
if (apf[iDrive].fRamBasedPagefile)
{
if( LoadString( hInstance, 164, szTemp, ARRAYSIZE( szTemp ) ) > 0 )
{
lstrcat(pszBuf, szTemp);
}
}
else if (apf[iDrive].nMinFileSize)
{
wsprintf(szTemp, TEXT("\t%d - %d"),
apf[iDrive].nMinFileSize, apf[iDrive].nMaxFileSize);
lstrcat(pszBuf, szTemp);
}
}
/*
* SetDlgItemMB
*
*
*/
VOID SetDlgItemMB( HWND hDlg, INT idControl, DWORD dwMBValue ) {
TCHAR szBuf[32];
wsprintf(szBuf, TEXT("%d %s"), dwMBValue, szMB),
SetDlgItemText(hDlg, idControl, szBuf);
}
/*
* GetFreeSpaceMB
*
*
*
*/
DWORD
GetFreeSpaceMB(
INT iDrive
)
{
TCHAR szDriveRoot[4];
DWORD dwSectorsPerCluster;
DWORD dwBytesPerSector;
DWORD dwFreeClusters;
DWORD dwClusters;
DWORD iSpace;
DWORD iSpaceExistingPagefile;
HANDLE hff;
WIN32_FIND_DATA ffd;
szDriveRoot[0] = TEXT('A') + iDrive;
szDriveRoot[1] = TEXT(':');
szDriveRoot[2] = TEXT('\\');
szDriveRoot[3] = (TCHAR) 0;
if (!GetDiskFreeSpace(szDriveRoot, &dwSectorsPerCluster, &dwBytesPerSector,
&dwFreeClusters, &dwClusters))
return 0;
iSpace = (INT)((dwSectorsPerCluster * dwFreeClusters) /
(ONE_MEG / dwBytesPerSector));
//
// Be sure to include the size of any existing pagefile.
// Because this space can be reused for a new paging file,
// it is effectively "disk free space" as well. The
// FindFirstFile api is safe to use, even if the pagefile
// is in use, because it does not need to open the file
// to get its size.
//
iSpaceExistingPagefile = 0;
if ((hff = FindFirstFile(SZPageFileName(szDriveRoot[0] - TEXT('A')), &ffd)) !=
INVALID_HANDLE_VALUE)
{
iSpaceExistingPagefile = (INT)(ffd.nFileSizeLow / ONE_MEG);
FindClose(hff);
}
return iSpace + iSpaceExistingPagefile;
}
/*
* GetMaxSpaceMB
*
*
*
*/
static
INT
GetMaxSpaceMB(
INT iDrive
)
{
TCHAR szDriveRoot[4];
DWORD dwSectorsPerCluster;
DWORD dwBytesPerSector;
DWORD dwFreeClusters;
DWORD dwClusters;
INT iSpace;
szDriveRoot[0] = (TCHAR)(TEXT('A') + iDrive);
szDriveRoot[1] = TEXT(':');
szDriveRoot[2] = TEXT('\\');
szDriveRoot[3] = (TCHAR) 0;
if (!GetDiskFreeSpace(szDriveRoot, &dwSectorsPerCluster, &dwBytesPerSector,
&dwFreeClusters, &dwClusters))
return 0;
iSpace = (INT)((dwSectorsPerCluster * dwClusters) /
(ONE_MEG / dwBytesPerSector));
return iSpace;
}
/*
* VirtualMemSelChange
*
*
*
*/
static
VOID
VirtualMemSelChange(
HWND hDlg
)
{
TCHAR szDriveRoot[4];
TCHAR szTemp[MAX_PATH];
TCHAR szVolume[MAX_PATH];
INT iSel;
INT iDrive;
INT nCrtRadioButtonId;
BOOL fEditsEnabled;
HWND hWndEdit;
if ((iSel = (INT)SendDlgItemMessage(
hDlg, IDD_VM_VOLUMES, LB_GETCURSEL, 0, 0)) == LB_ERR)
return;
iDrive = (INT)SendDlgItemMessage(hDlg, IDD_VM_VOLUMES,
LB_GETITEMDATA, iSel, 0);
szDriveRoot[0] = TEXT('A') + iDrive;
szDriveRoot[1] = TEXT(':');
szDriveRoot[2] = TEXT('\\');
szDriveRoot[3] = (TCHAR) 0;
*szVolume = (TCHAR) 0;
GetVolumeInformation(szDriveRoot, szVolume, MAX_PATH,
NULL, NULL, NULL, NULL, 0);
szTemp[0] = TEXT('A') + iDrive;
szTemp[1] = TEXT(':');
szTemp[2] = (TCHAR) 0;
if (*szVolume)
{
lstrcat(szTemp, TEXT(" ["));
lstrcat(szTemp, szVolume);
lstrcat(szTemp, TEXT("]"));
}
//LATER: should we also put up total drive size as well as free space?
SetDlgItemText(hDlg, IDD_VM_SF_DRIVE, szTemp);
SetDlgItemMB(hDlg, IDD_VM_SF_SPACE, GetFreeSpaceMB(iDrive));
if ( apf[iDrive].fRamBasedPagefile )
{
HWND hWndEdit;
//
// Paging file size based on RAM size
//
nCrtRadioButtonId = IDD_VM_RAMBASED_RADIO;
fEditsEnabled = FALSE;
}
else
{
if ( apf[iDrive].nMinFileSize != 0 )
{
//
// Custom size paging file
//
nCrtRadioButtonId = IDD_VM_CUSTOMSIZE_RADIO;
SetDlgItemInt(hDlg, IDD_VM_SF_SIZE, apf[iDrive].nMinFileSize, FALSE);
SetDlgItemInt(hDlg, IDD_VM_SF_SIZEMAX, apf[iDrive].nMaxFileSize, FALSE);
fEditsEnabled = TRUE;
}
else
{
//
// No paging file
//
nCrtRadioButtonId = IDD_VM_NOPAGING_RADIO;
SetDlgItemText(hDlg, IDD_VM_SF_SIZE, TEXT(""));
SetDlgItemText(hDlg, IDD_VM_SF_SIZEMAX, TEXT(""));
fEditsEnabled = FALSE;
}
}
//
// Select the appropriate radio button
//
CheckRadioButton(
hDlg,
IDD_VM_CUSTOMSIZE_RADIO,
IDD_VM_NOPAGING_RADIO,
nCrtRadioButtonId );
//
// Enable/disable the min & max size edit boxes
//
EnableWindow( GetDlgItem( hDlg, IDD_VM_SF_SIZE ), fEditsEnabled );
EnableWindow( GetDlgItem( hDlg, IDD_VM_SF_SIZEMAX ), fEditsEnabled );
}
/*
* VirtualMemUpdateAllocated
*
*
*
*/
INT VirtualMemComputeAllocated( HWND hWnd , BOOL *pfTempPf)
{
BOOL fSuccess = FALSE;
static BOOL fWarned = FALSE;
ULONG ulPagefileSize = 0;
unsigned __int64 PagefileSize;
NTSTATUS result = ERROR_ACCESS_DENIED;
SYSTEM_INFO SysInfo;
PSYSTEM_PAGEFILE_INFORMATION pPagefileInfo = NULL;
PSYSTEM_PAGEFILE_INFORMATION pCurrentPagefile = NULL;
LONG lResult = ERROR_ACCESS_DENIED;
DWORD dwValueType = 0;
DWORD fTempPagefile = 0;
DWORD cbSize = sizeof(DWORD);
__try {
pCurrentPagefile = pPagefileInfo = (PSYSTEM_PAGEFILE_INFORMATION) MemAlloc(
LPTR,
PAGEFILE_INFO_BUFFER_SIZE
);
if (!pPagefileInfo) {
__leave;
} // if
// Get the page size in bytes
GetSystemInfo(&SysInfo);
// Get the sizes (in pages) of all of the pagefiles on the system
result = NtQuerySystemInformation(
SystemPageFileInformation,
pPagefileInfo,
PAGEFILE_INFO_BUFFER_SIZE,
NULL
);
if (ERROR_SUCCESS != result) {
__leave;
} // if
if (pfTempPf) {
// Check to see if the system created a temporary pagefile
lResult = RegQueryValueEx(
ghkeyMemMgt,
szNoPageFile,
NULL,
&dwValueType,
(LPBYTE) &fTempPagefile,
&cbSize
);
if ((ERROR_SUCCESS == lResult) && fTempPagefile) {
*pfTempPf = TRUE;
} // if (ERROR_SUCCESS...
else {
*pfTempPf = FALSE;
} // else
} // if (pfTempPf)
// Add up pagefile sizes
while (pCurrentPagefile->NextEntryOffset) {
ulPagefileSize += pCurrentPagefile->TotalSize;
((LPBYTE) pCurrentPagefile) += pCurrentPagefile->NextEntryOffset;
} // while
ulPagefileSize += pCurrentPagefile->TotalSize;
// Convert pages to bytes
PagefileSize = (unsigned __int64) ulPagefileSize * SysInfo.dwPageSize;
// Convert bytes to MB
ulPagefileSize = (ULONG) (PagefileSize / ONE_MEG);
fSuccess = TRUE;
} // __try
__finally {
// If we failed to determine the pagefile size, then
// warn the user that the reported size is incorrect,
// once per applet invokation.
if (!fSuccess && !fWarned) {
MsgBoxParam(
hWnd,
IDS_SYSDM_DONTKNOWCURRENT,
IDS_SYSDM_TITLE,
MB_ICONERROR | MB_OK
);
fWarned = TRUE;
} // if
if (pPagefileInfo) {
MemFree((HLOCAL) pPagefileInfo);
} // if
} // __finally
return(ulPagefileSize);
}
static VOID VirtualMemUpdateAllocated(
HWND hDlg
)
{
SetDlgItemMB(hDlg, IDD_VM_ALLOCD, VirtualMemComputeAllocated(hDlg, NULL));
}
int VirtualMemComputeTotalMax( void ) {
INT nTotalAllocated;
INT i;
for (nTotalAllocated = 0, i = 0; i < MAX_DRIVES; i++)
{
nTotalAllocated += apf[i].nMaxFileSize;
}
return nTotalAllocated;
}
/*
* VirtualMemSetNewSize
*
*
*
*/
static
BOOL
VirtualMemSetNewSize(
HWND hDlg
)
{
DWORD nSwapSize;
DWORD nSwapSizeMax;
BOOL fTranslated;
INT iSel;
INT iDrive = 2; // default to C
TCHAR szTemp[MAX_PATH];
DWORD nFreeSpace;
DWORD CrashDumpSizeInMbytes;
TCHAR Drive;
INT iBootDrive;
BOOL fRamBasedPagefile = FALSE;
//
// Initialize variables for crashdump.
//
if (GetSystemDrive (&Drive)) {
iBootDrive = tolower (Drive) - 'a';
} else {
iBootDrive = 0;
}
if ((iSel = (INT)SendDlgItemMessage(
hDlg, IDD_VM_VOLUMES, LB_GETCURSEL, 0, 0)) != LB_ERR)
{
if (LB_ERR ==
(iDrive = (INT)SendDlgItemMessage(hDlg, IDD_VM_VOLUMES,
LB_GETITEMDATA, iSel, 0)))
{
return FALSE; // failure!
}
}
CrashDumpSizeInMbytes =
(DWORD) ( CoreDumpGetRequiredFileSize (NULL) / ONE_MEG );
#if 0
CoreDumpGetValue(CD_LOG);
CoreDumpGetValue(CD_SEND);
CoreDumpGetValue(CD_WRITE);
if (acdfControls[CD_WRITE].u.fValue) {
nBootPF = -1;
} else if (acdfControls[CD_LOG].u.fValue ||
acdfControls[CD_SEND].u.fValue) {
nBootPF = MIN_SWAPSIZE;
}
if (nBootPF != 0) {
SYSTEM_BASIC_INFORMATION BasicInfo;
NTSTATUS status;
unsigned __int64 TotalPhys;
TCHAR szBootPath[MAX_PATH];
if (GetWindowsDirectory(szBootPath, MAX_PATH) == 0) {
iBootDrive = IDRV_DEF_BOOT;
} else {
iBootDrive = szBootPath[0];
if (iBootDrive > TEXT('Z'))
iBootDrive -= TEXT('a');
else {
iBootDrive -= TEXT('A');
}
}
if (nBootPF == -1) {
// Get the number of Meg in the system, rounding up
// (note that we round up a zero remainder)
status = NtQuerySystemInformation(
SystemBasicInformation,
&BasicInfo,
sizeof(BasicInfo),
NULL
);
if (NT_SUCCESS(status)) {
TotalPhys = (unsigned __int64) BasicInfo.NumberOfPhysicalPages * BasicInfo.PageSize;
}
else {
TotalPhys = 0;
}
nBootPF = (DWORD) ((TotalPhys / ONE_MEG) + 1);
}
}
#endif
if( IsDlgButtonChecked( hDlg, IDD_VM_NOPAGING_RADIO ) == BST_CHECKED )
{
//
// No paging file on this drive.
//
nSwapSize = 0;
nSwapSizeMax = 0;
fTranslated = TRUE;
}
else
{
if( IsDlgButtonChecked( hDlg, IDD_VM_RAMBASED_RADIO ) == BST_CHECKED )
{
MEMORYSTATUSEX MemoryInfo;
//
// User requested a RAM based page file. We will compute a page file
// size based on the RAM currently available so that we can benefit of
// all the verifications done below related to disk space available etc.
// The final page file specification written to the registry will contain
// zero sizes though because this is the way we signal that we
// want a RAM based page file.
//
ZeroMemory (&MemoryInfo, sizeof MemoryInfo);
MemoryInfo.dwLength = sizeof MemoryInfo;
if (GlobalMemoryStatusEx (&MemoryInfo))
{
fRamBasedPagefile = TRUE;
//
// We do not lose info because we first divide the RAM size to
// 1Mb and only after that we convert to a DWORD.
//
nSwapSize = (DWORD)(MemoryInfo.ullTotalPhys / 0x100000) + 12;
nSwapSizeMax = nSwapSize;
fTranslated = TRUE;
}
else
{
nSwapSize = 0;
nSwapSizeMax = 0;
fTranslated = TRUE;
}
}
else
{
//
// User requested a custom size paging file
//
nSwapSize = (INT)GetDlgItemInt(hDlg, IDD_VM_SF_SIZE,
&fTranslated, FALSE);
if (!fTranslated)
{
MsgBoxParam(hDlg, IDS_SYSDM_ENTERINITIALSIZE, IDS_SYSDM_TITLE, MB_ICONEXCLAMATION);
SetFocus(GetDlgItem(hDlg, IDD_VM_SF_SIZE));
return FALSE;
}
if ((nSwapSize < MIN_SWAPSIZE && nSwapSize != 0))
{
MsgBoxParam(hDlg, IDS_SYSDM_PAGEFILESIZE_START, IDS_SYSDM_TITLE, MB_ICONEXCLAMATION,
GetMaxPagefileSizeInMB(iDrive));
SetFocus(GetDlgItem(hDlg, IDD_VM_SF_SIZE));
return FALSE;
}
if (nSwapSize == 0)
{
nSwapSizeMax = 0;
}
else
{
nSwapSizeMax = (INT)GetDlgItemInt(hDlg, IDD_VM_SF_SIZEMAX,
&fTranslated, FALSE);
if (!fTranslated)
{
MsgBoxParam(hDlg, IDS_SYSDM_ENTERMAXIMUMSIZE, IDS_SYSDM_TITLE, MB_ICONEXCLAMATION,
GetMaxPagefileSizeInMB(iDrive));
SetFocus(GetDlgItem(hDlg, IDD_VM_SF_SIZEMAX));
return FALSE;
}
if (nSwapSizeMax < nSwapSize || nSwapSizeMax > GetMaxPagefileSizeInMB(iDrive))
{
MsgBoxParam(hDlg, IDS_SYSDM_PAGEFILESIZE_MAX, IDS_SYSDM_TITLE, MB_ICONEXCLAMATION,
GetMaxPagefileSizeInMB(iDrive));
SetFocus(GetDlgItem(hDlg, IDD_VM_SF_SIZEMAX));
return FALSE;
}
}
}
}
if (fTranslated && iSel != LB_ERR)
{
nFreeSpace = GetMaxSpaceMB(iDrive);
if (nSwapSizeMax > nFreeSpace)
{
MsgBoxParam(hDlg, IDS_SYSDM_PAGEFILESIZE_TOOSMALL_NAMED, IDS_SYSDM_TITLE, MB_ICONEXCLAMATION,
(TCHAR)(iDrive + TEXT('A')));
SetFocus(GetDlgItem(hDlg, IDD_VM_SF_SIZEMAX));
return FALSE;
}
nFreeSpace = GetFreeSpaceMB(iDrive);
if (nSwapSize > nFreeSpace)
{
MsgBoxParam(hDlg, IDS_SYSDM_PAGEFILESIZE_TOOSMALL, IDS_SYSDM_TITLE, MB_ICONEXCLAMATION);
SetFocus(GetDlgItem(hDlg, IDD_VM_SF_SIZE));
return FALSE;
}
if (nSwapSize != 0 && nFreeSpace - nSwapSize < MIN_FREESPACE)
{
MsgBoxParam(hDlg, IDS_SYSDM_NOTENOUGHSPACE_PAGE, IDS_SYSDM_TITLE, MB_ICONEXCLAMATION,
(int)MIN_FREESPACE);
SetFocus(GetDlgItem(hDlg, IDD_VM_SF_SIZE));
return FALSE;
}
if (nSwapSizeMax > nFreeSpace)
{
if (MsgBoxParam(hDlg, IDS_SYSDM_PAGEFILESIZE_TOOSMALL_GROW, IDS_SYSDM_TITLE, MB_ICONINFORMATION |
MB_OKCANCEL, (TCHAR)(iDrive + TEXT('A'))) == IDCANCEL)
{
SetFocus(GetDlgItem(hDlg, IDD_VM_SF_SIZEMAX));
return FALSE;
}
}
if (iDrive == iBootDrive &&
(ULONG64) nSwapSize < CrashDumpSizeInMbytes) {
DWORD Ret;
//
// The new boot drive page file size is less than we need for
// crashdump. The message notifies the user that the resultant
// dump file may be truncated.
//
// NOTE: DO NOT, turn off dumping at this point, because a valid
// dump could still be generated.
//
Ret = MsgBoxParam (hDlg,
IDS_SYSDM_DEBUGGING_MINIMUM,
IDS_SYSDM_TITLE,
MB_ICONEXCLAMATION | MB_YESNO,
(TCHAR) ( iBootDrive + TEXT ('A') ),
(DWORD) CrashDumpSizeInMbytes
);
if (Ret != IDYES) {
SetFocus(GetDlgItem(hDlg, IDD_VM_SF_SIZE));
return FALSE;
}
}
apf[iDrive].nMinFileSize = nSwapSize;
apf[iDrive].nMaxFileSize = nSwapSizeMax;
apf[iDrive].fRamBasedPagefile = fRamBasedPagefile;
// Remember if the page file does not exist so we can create it later
if (GetFileAttributes(SZPageFileName(iDrive)) == 0xFFFFFFFF &&
GetLastError() == ERROR_FILE_NOT_FOUND) {
apf[iDrive].fCreateFile = TRUE;
}
VirtualMemBuildLBLine(szTemp, iDrive);
SendDlgItemMessage(hDlg, IDD_VM_VOLUMES, LB_DELETESTRING, iSel, 0);
SendDlgItemMessage(hDlg, IDD_VM_VOLUMES, LB_INSERTSTRING, iSel,
(LPARAM)szTemp);
SendDlgItemMessage(hDlg, IDD_VM_VOLUMES, LB_SETITEMDATA, iSel,
(LPARAM)iDrive);
SendDlgItemMessage(hDlg, IDD_VM_VOLUMES, LB_SETCURSEL, iSel, 0L);
cxLBExtent = SetLBWidthEx(GetDlgItem(hDlg, IDD_VM_VOLUMES), szTemp, cxLBExtent, cxExtra);
if ( ( ! apf[iDrive].fRamBasedPagefile ) && ( apf[iDrive].nMinFileSize != 0 ) ) {
SetDlgItemInt(hDlg, IDD_VM_SF_SIZE, apf[iDrive].nMinFileSize, FALSE);
SetDlgItemInt(hDlg, IDD_VM_SF_SIZEMAX, apf[iDrive].nMaxFileSize, FALSE);
}
else {
SetDlgItemText(hDlg, IDD_VM_SF_SIZE, TEXT(""));
SetDlgItemText(hDlg, IDD_VM_SF_SIZEMAX, TEXT(""));
}
VirtualMemUpdateAllocated(hDlg);
SetFocus(GetDlgItem(hDlg, IDD_VM_VOLUMES));
}
return TRUE;
}
/*
* VirtualMemUpdateRegistry
*
*
*
*/
BOOL
VirtualMemUpdateRegistry(
VOID
)
{
LPTSTR szBuf;
TCHAR szTmp[MAX_DRIVES * 22]; //max_drives * sizeof(fmt_string)
LONG i;
INT c;
int j;
PAGEFILDESC aparm[MAX_DRIVES];
static TCHAR szNULLs[] = TEXT("\0\0");
c = 0;
szTmp[0] = TEXT('\0');
szBuf = szTmp;
for (i = 0; i < MAX_DRIVES; i++)
{
/*
* Is this a RAM based page file or
* does this drive have a pagefile specified for it?
*/
if (apf[i].fRamBasedPagefile) {
j = (c * 4);
aparm[c].pszName = CloneString(SZPageFileName(i));
aparm[c].nMin = 0;
aparm[c].nMax = 0;
aparm[c].chNull = (DWORD)TEXT('\0');
szBuf += wsprintf( szBuf, TEXT("%%%d!s! %%%d!d! %%%d!d!%%%d!c!"),
j+1, j+2, j+3, j+4);
c++;
}
else if (apf[i].nMinFileSize)
{
j = (c * 4);
aparm[c].pszName = CloneString(SZPageFileName(i));
aparm[c].nMin = apf[i].nMinFileSize;
aparm[c].nMax = apf[i].nMaxFileSize;
aparm[c].chNull = (DWORD)TEXT('\0');
szBuf += wsprintf( szBuf, TEXT("%%%d!s! %%%d!d! %%%d!d!%%%d!c!"),
j+1, j+2, j+3, j+4);
c++;
}
}
/*
* Alloc and fill in the page file registry string
*/
//since FmtMsg returns 0 for error, it can not return a zero length string
//therefore, force string to be at least one space long.
if (szTmp[0] == TEXT('\0')) {
szBuf = szNULLs;
j = 1; //Length of string == 1 char (ZTerm null will be added later).
} else {
j = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
szTmp, 0, 0, (LPTSTR)&szBuf, 1, (va_list *)&aparm);
}
for( i = 0; i < c; i++ )
MemFree(aparm[i].pszName);
if (j == 0)
return FALSE;
i = RegSetValueEx (ghkeyMemMgt, szPagingFiles, 0, REG_MULTI_SZ,
(LPBYTE)szBuf, SIZEOF(TCHAR) * (j+1));
// free the string now that it is safely stored in the registry
if (szBuf != szNULLs)
FmtFree(szBuf);
// if the string didn't get there, then return error
if (i != ERROR_SUCCESS)
return FALSE;
/*
* Now be sure that any previous pagefiles will be deleted on
* the next boot.
*/
for (i = 0; i < MAX_DRIVES; i++)
{
/*
* Did this drive have a pagefile before, but does not have
* one now?
*/
if (apf[i].nMinFileSizePrev != 0 && apf[i].nMinFileSize == 0)
{
//
// Hack workaround -- MoveFileEx() is broken
//
TCHAR szPagefilePath[MAX_PATH];
lstrcpy(szPagefilePath, szRenameFunkyPrefix);
lstrcat(szPagefilePath, SZPageFileName(i));
VirtualMemDeletePagefile(szPagefilePath);
// MoveFileEx(SZPageFileName(i), NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
}
}
return TRUE;
}
void GetAPrivilege( LPTSTR szPrivilegeName, PPRIVDAT ppd ) {
HANDLE hTok;
LUID luid;
TOKEN_PRIVILEGES tpNew;
DWORD cb;
if (LookupPrivilegeValue( NULL, szPrivilegeName, &luid ) &&
OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hTok)) {
tpNew.PrivilegeCount = 1;
tpNew.Privileges[0].Luid = luid;
tpNew.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hTok, FALSE, &tpNew, sizeof(ppd->tp),
&(ppd->tp), &cb)) {
GetLastError();
}
ppd->hTok = hTok;
} else {
ppd->hTok = NULL;
}
}
void ResetOldPrivilege( PPRIVDAT ppdOld ) {
if (ppdOld->hTok != NULL ) {
AdjustTokenPrivileges(ppdOld->hTok, FALSE, &(ppdOld->tp), 0, NULL,
NULL);
CloseHandle( ppdOld->hTok );
ppdOld->hTok = NULL;
}
}
/*
* VirtualMemReconcileState
*
* Reconciles the n*FileSizePrev fields of apf with the n*FileSize fields.
*
*/
void
VirtualMemReconcileState(
)
{
INT i;
for (i = 0; i < MAX_DRIVES; i++) {
apf[i].nMinFileSizePrev = apf[i].nMinFileSize;
apf[i].nMaxFileSizePrev = apf[i].nMaxFileSize;
} // for
}
/*
* VirtualMemDeletePagefile
*
* Hack workaround -- MoveFileEx() is broken.
*
*/
DWORD
VirtualMemDeletePagefile(
IN LPTSTR szPagefile
)
{
HKEY hKey;
BOOL fhKeyOpened = FALSE;
DWORD dwResult;
LONG lResult;
LPTSTR szBuffer = NULL;
LPTSTR szBufferEnd = NULL;
DWORD dwValueType;
DWORD cbRegistry;
DWORD cbBuffer;
DWORD cchPagefile;
DWORD dwRetVal = ERROR_SUCCESS;
__try {
cchPagefile = lstrlen(szPagefile) + 1;
lResult = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
szSessionManager,
0L,
KEY_READ | KEY_WRITE,
&hKey
);
if (ERROR_SUCCESS != lResult) {
dwRetVal = lResult;
__leave;
} // if
//
// Find out of PendingFileRenameOperations exists, and,
// if it does, how big it is
//
lResult = RegQueryValueEx(
hKey,
szPendingRename,
0L,
&dwValueType,
(LPBYTE) NULL,
&cbRegistry
);
if (ERROR_SUCCESS != lResult) {
//
// If the value doesn't exist, we still need to set
// it's size to one character so the formulas below (which are
// written for the "we're appending to an existing string"
// case) still work.
//
cbRegistry = sizeof(TCHAR);
} // if
//
// Buffer needs to hold the existing registry value
// plus the supplied pagefile path, plus two extra
// terminating NULL characters. However, we only have to add
// room for one extra character, because we'll be overwriting
// the terminating NULL character in the existing buffer.
//
cbBuffer = cbRegistry + ((cchPagefile + 1) * sizeof(TCHAR));
szBufferEnd = szBuffer = (LPTSTR) MemAlloc(LPTR, cbBuffer);
if (!szBuffer) {
dwRetVal = ERROR_NOT_ENOUGH_MEMORY;
__leave;
} // if
//
// Grab the existing value, if there is one
//
if (ERROR_SUCCESS == lResult) {
lResult = RegQueryValueEx(
hKey,
szPendingRename,
0L,
&dwValueType,
(LPBYTE) szBuffer,
&cbRegistry
);
if (ERROR_SUCCESS != lResult) {
dwRetVal = ERROR_FILE_NOT_FOUND;
__leave;
} // if
//
// We'll start our scribbling right on the final
// terminating NULL character of the existing
// value.
//
szBufferEnd += (cbRegistry / sizeof(TCHAR)) - 1;
} // if
//
// Copy in the supplied pagefile path.
//
lstrcpy(szBufferEnd, szPagefile);
//
// Add the final two terminating NULL characters
// required for REG_MULTI_SZ-ness. Yes, those indeces
// are correct--when cchPagfile was calculated above,
// we added one for its own terminating NULL character.
//
szBufferEnd[cchPagefile] = TEXT('\0');
szBufferEnd[cchPagefile + 1] = TEXT('\0');
dwValueType = REG_MULTI_SZ;
lResult = RegSetValueEx(
hKey,
szPendingRename,
0L,
dwValueType,
(CONST BYTE *) szBuffer,
cbBuffer
);
if (ERROR_SUCCESS != lResult) {
dwRetVal = lResult;
} // if
} // __try
__finally {
if (fhKeyOpened) {
RegCloseKey(hKey);
} // if
if (szBuffer) {
MemFree((HLOCAL) szBuffer);
} // if
} // __finally
return dwRetVal;
}
/*
* VirtualMemCreatePagefileFromIndex
*
*
*/
NTSTATUS
VirtualMemCreatePagefileFromIndex(
IN INT i
)
{
UNICODE_STRING us;
LARGE_INTEGER liMin, liMax;
NTSTATUS status;
WCHAR wszPath[MAX_PATH*2];
TCHAR szDrive[3];
DWORD cch;
HourGlass(TRUE);
// convert path drive letter to an NT device path
wsprintf(szDrive, TEXT("%c:"), (TCHAR)(i + (int)TEXT('A')));
cch = QueryDosDevice( szDrive, wszPath, sizeof(wszPath) /
sizeof(TCHAR));
if (cch != 0) {
// Concat the filename only (skip 'd:') to the nt device
// path, and convert it to a UNICODE_STRING
lstrcat( wszPath, SZPageFileName(i) + 2 );
RtlInitUnicodeString( &us, wszPath );
liMin.QuadPart = (LONGLONG)(apf[i].nMinFileSize * ONE_MEG);
liMax.QuadPart = (LONGLONG)(apf[i].nMaxFileSize * ONE_MEG);
status = NtCreatePagingFile ( &us, &liMin, &liMax, 0L );
}
else
{
status = STATUS_NO_SUCH_DEVICE;
}
HourGlass(FALSE);
return status;
}
/*
* VirtualMemUpdateListboxFromIndex
*
*/
void
VirtualMemUpdateListboxFromIndex(
HWND hDlg,
INT i
)
{
int j, cLBEntries, iTemp;
int iLBEntry = -1;
TCHAR szTemp[MAX_PATH];
cLBEntries = (int)SendDlgItemMessage(
(HWND) hDlg,
(int) IDD_VM_VOLUMES,
(UINT) LB_GETCOUNT,
(WPARAM) 0,
(LPARAM) 0
);
if (LB_ERR != cLBEntries) {
// Loop through all the listbox entries, looking for the one
// that corresponds to the drive index we were supplied.
for (j = 0; j < cLBEntries; j++) {
iTemp = (int)SendDlgItemMessage(
(HWND) hDlg,
(int) IDD_VM_VOLUMES,
(UINT) LB_GETITEMDATA,
(WPARAM) j,
(LPARAM) 0
);
if (iTemp == i) {
iLBEntry = j;
break;
} // if
} // for
if (-1 != iLBEntry) {
// Found the desired entry, so update it.
VirtualMemBuildLBLine(szTemp, i);
SendDlgItemMessage(
hDlg,
IDD_VM_VOLUMES,
LB_DELETESTRING,
(WPARAM) iLBEntry,
0
);
SendDlgItemMessage(
hDlg,
IDD_VM_VOLUMES,
LB_INSERTSTRING,
(WPARAM) iLBEntry,
(LPARAM) szTemp
);
SendDlgItemMessage(
hDlg,
IDD_VM_VOLUMES,
LB_SETITEMDATA,
(WPARAM) iLBEntry,
(LPARAM) i
);
SendDlgItemMessage(
hDlg,
IDD_VM_VOLUMES,
LB_SETCURSEL,
(WPARAM) iLBEntry,
0
);
if (apf[i].nMinFileSize) {
SetDlgItemInt(hDlg, IDD_VM_SF_SIZE, apf[i].nMinFileSize, FALSE);
SetDlgItemInt(hDlg, IDD_VM_SF_SIZEMAX, apf[i].nMaxFileSize, FALSE);
}
else {
SetDlgItemText(hDlg, IDD_VM_SF_SIZE, TEXT(""));
SetDlgItemText(hDlg, IDD_VM_SF_SIZEMAX, TEXT(""));
}
VirtualMemUpdateAllocated(hDlg);
} // if (-1 != iLBEntry)
} // if (LB_ERR...
return;
}
/*
* VirtualMemPromptForReboot
*
*
*
*/
int
VirtualMemPromptForReboot(
HWND hDlg
)
{
INT i, result;
int iReboot = RET_NO_CHANGE;
int iThisDrv;
UNICODE_STRING us;
LARGE_INTEGER liMin, liMax;
NTSTATUS status;
TCHAR szDrive[3];
PRIVDAT pdOld;
GetPageFilePrivilege( &pdOld );
// Have to make two passes through the list of pagefiles.
// The first checks to see if files called "pagefile.sys" exist
// on any of the drives that will be getting new pagefiles.
// If there are existing files called "pagefile.sys" and the user
// doesn't want any one of them to be overwritten, we bail out.
// The second pass through the list does the actual work of
// creating the pagefiles.
for (i = 0; i < MAX_DRIVES; i++) {
//
// Did something change?
//
if (apf[i].nMinFileSize != apf[i].nMinFileSizePrev ||
apf[i].nMaxFileSize != apf[i].nMaxFileSizePrev ||
apf[i].fCreateFile ) {
// Assume we have permission to nuke existing files called pagefile.sys
// (we'll confirm the assumption later)
result = IDYES;
if (0 != apf[i].nMinFileSize) { // Pagefile wanted for this drive
if (0 == apf[i].nMinFileSizePrev) { // There wasn't one there before
if (!(((GetFileAttributes(SZPageFileName(i)) == 0xFFFFFFFF)) || (GetLastError() == ERROR_FILE_NOT_FOUND))) {
// A file named pagefile.sys exists on the drive
// We need to confirm that we can overwrite it
result = MsgBoxParam(
hDlg,
IDS_SYSDM_OVERWRITE,
IDS_SYSDM_TITLE,
MB_ICONQUESTION | MB_YESNO,
SZPageFileName(i)
);
} // if (!((GetFileAttributes...
} // if (0 == apf[i].nMinFileSizePrev)
if (IDYES != result) {
// User doesn't want us overwriting an existing
// file called pagefile.sys, so back out the changes
apf[i].nMinFileSize = apf[i].nMinFileSizePrev;
apf[i].nMaxFileSize = apf[i].nMaxFileSizePrev;
apf[i].fCreateFile = FALSE;
// Update the listbox
VirtualMemUpdateListboxFromIndex(hDlg, i);
SetFocus(GetDlgItem(hDlg, IDD_VM_VOLUMES));
// Bail, telling the DlgProc not to end the dialog
iReboot = RET_ERROR;
goto bailout;
} // if (IDYES != result)
} // if (0 != apf[i].nMinFileSize)
} // if
} // for
for (i = 0; i < MAX_DRIVES; i++)
{
//
// Did something change?
//
if (apf[i].nMinFileSize != apf[i].nMinFileSizePrev ||
apf[i].nMaxFileSize != apf[i].nMaxFileSizePrev ||
apf[i].fCreateFile ) {
/*
* If we are strictly creating a *new* page file, or *enlarging*
* the minimum or maximum size of an existing page file, then
* we can try do it on the fly. If no errors are returned by
* the system then no reboot will be required.
*/
// assume we will have to reboot
iThisDrv = RET_VIRTUAL_CHANGE;
/*
* IF we are creating a new page file
*/
if ((0 != apf[i].nMinFileSize) && (0 == apf[i].nMinFileSizePrev)) {
status = VirtualMemCreatePagefileFromIndex(i);
if (NT_SUCCESS(status)) {
// made it on the fly, no need to reboot for this drive!
iThisDrv = RET_CHANGE_NO_REBOOT;
}
}
/*
* If we're enlarging the minimum or maximum size of an existing
* page file, we can try to do it on the fly
*/
else if ((apf[i].nMinFileSize != 0) &&
((apf[i].nMinFileSize > apf[i].nMinFileSizePrev) ||
(apf[i].nMaxFileSize > apf[i].nMaxFileSizePrev))) {
status = VirtualMemCreatePagefileFromIndex(i);
if (NT_SUCCESS(status)) {
iThisDrv = RET_CHANGE_NO_REBOOT;
}
} /* else if */
// if this drive has changed, we must reboot
if (RET_VIRTUAL_CHANGE == iThisDrv)
{
iReboot |= RET_VIRTUAL_CHANGE;
}
}
}
bailout:
ResetOldPrivilege( &pdOld );
//
// If Nothing changed, then change our IDOK to IDCANCEL so System.cpl will
// know not to reboot.
//
return iReboot;
}