windows-nt/Source/XPSP1/NT/base/fs/utils/scandisk/chkdsk.c
2020-09-26 16:20:57 +08:00

620 lines
22 KiB
C

//////////////////////////////////////////////////////////////////////////////
//
// MAIN.C / ChkDskW
//
// Microsoft Confidential
// Copyright (c) Microsoft Corporation 1998
// All rights reserved
//
// 8/98 - Jason Cohen (JCOHEN)
//
//////////////////////////////////////////////////////////////////////////////
// Include file(s).
//
#include "main.h"
#include "fmifs.h"
// Internal global variable(s).
//
static BOOL g_bSuccess;
static HWND g_hProgressDlg;
static HICON g_hIconScan[3];
// External global variable(s).
//
extern HINSTANCE g_hInstance;
extern DWORD g_dwFlags;
// Internal function prototype(s).
//
static DWORD WINAPI ThreadChkdsk(LPVOID);
static BOOL CALLBACK FmifsCallback(FMIFS_PACKET_TYPE, ULONG, PVOID);
static VOID UpdateStatus(LPSTR);
static VOID SummaryDialog(HWND, LPTSTR, BOOL);
static INT_PTR CALLBACK SummaryProc(HWND, UINT, WPARAM, LPARAM);
static BOOL Summary_OnInitDialog(HWND, HWND, LPARAM);
static FARPROC LoadDllFunction(LPTSTR, LPCSTR, HINSTANCE *);
HANDLE SpawnChkdsk(HWND hDlg, DWORD dwDrives)
{
DWORD dwThreadId;
// Set that we are scanning a drive currently.
//
g_dwFlags |= SCANDISK_SCANNING;
g_dwFlags &= ~SCANDISK_CANCELSCAN;
// Set up the global variables needed.
//
g_bSuccess = FALSE;
g_hProgressDlg = hDlg;
// Load the icons for the scanning animation.
//
g_hIconScan[0] = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SCAN1));
g_hIconScan[1] = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SCAN2));
g_hIconScan[2] = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SCAN3));
// Create the thread that will run chkdsk.
//
return CreateThread(NULL, 0, ThreadChkdsk, (LPVOID)(ULONG_PTR)dwDrives, 0, &dwThreadId);
}
static DWORD WINAPI ThreadChkdsk(LPVOID dwDrives)
{
HINSTANCE hFmifsDll;
FARPROC Chkdsk;
WCHAR szwFileSystem[16],
szwDrive[] = L"A:\\";
TCHAR szBuffer[256];
INT nCount,
nIndex;
LPINT lpnSelected,
lpnIndex;
LPTSTR lpBuffer,
lpCaption,
lpCapPre;
DWORD_PTR dwMask;
BOOL bContinue,
bSurface,
bFix,
bAllHappy = FALSE;
// Load and run the Chkdsk function.
//
if ( (Chkdsk = LoadDllFunction(_T("FMIFS.DLL"), "Chkdsk", &hFmifsDll)) == NULL )
return 0;
// Get the number of selected items and allocate a buffer to hold all the indexs.
//
if ( ( (nCount = (INT)SendDlgItemMessage(g_hProgressDlg, IDC_DRIVES, LB_GETSELCOUNT, 0, 0L)) > 0 ) &&
( lpnSelected = (LPINT) MALLOC(nCount * sizeof(INT)) ) )
{
// Now get the list of selected items.
//
if ( (nCount = (INT)SendDlgItemMessage(g_hProgressDlg, IDC_DRIVES, LB_GETSELITEMS, nCount, (LPARAM) lpnSelected)) > 0 )
{
// Disable the controls.
//
EnableWindow(GetDlgItem(g_hProgressDlg, IDOK), FALSE);
EnableWindow(GetDlgItem(g_hProgressDlg, IDC_DRIVESTEXT), FALSE);
EnableWindow(GetDlgItem(g_hProgressDlg, IDC_DRIVES), FALSE);
EnableWindow(GetDlgItem(g_hProgressDlg, IDC_SURFACE), FALSE);
EnableWindow(GetDlgItem(g_hProgressDlg, IDC_AUTOFIX), FALSE);
// Change the text of the IDCANCEL button to Cancel (from Close).
//
if ( lpBuffer = AllocateString(NULL, IDS_CANCEL) )
{
SetDlgItemText(g_hProgressDlg, IDCANCEL, lpBuffer);
FREE(lpBuffer);
}
// Get the scan options.
//
bSurface = IsDlgButtonChecked(g_hProgressDlg, IDC_SURFACE);
bFix = IsDlgButtonChecked(g_hProgressDlg, IDC_AUTOFIX);
// Get the caption prefix.
//
lpCapPre = AllocateString(NULL, IDS_RESULTS);
// Loop through all the drives in the list box to see if they
// are selected.
//
lpnIndex = lpnSelected;
nIndex = 0;
bAllHappy = TRUE;
for (dwMask = 1; ((DWORD_PTR) dwDrives & ~(dwMask - 1)) && ( !(g_dwFlags & SCANDISK_CANCELSCAN) ); dwMask <<= 1)
{
// Is this drive in the list box.
//
if ( (DWORD_PTR) dwDrives & dwMask )
{
// Test to see if this item is the
// next selected one.
//
if ( *lpnIndex == nIndex )
{
//
// Ok, try and run chkdsk on this drive.
//
// Get the file system type.
//
bContinue = TRUE;
while ( bContinue && !GetVolumeInformationW(szwDrive, NULL, 0, NULL, NULL, NULL, szwFileSystem, sizeof(szwFileSystem)) )
{
bContinue = FALSE;
if ( ( GetLastError() == ERROR_NOT_READY ) &&
( lpBuffer = AllocateString(NULL, IDS_NOTREADY) ) )
{
if ( MessageBox(g_hProgressDlg, lpBuffer, NULL, MB_RETRYCANCEL | MB_ICONERROR) == IDRETRY )
bContinue = TRUE;
FREE(lpBuffer);
}
}
if ( bContinue )
{
// Now finally launch Chkdsk.
//
Chkdsk(szwDrive, szwFileSystem, FALSE, FALSE, FALSE, bSurface, NULL, FALSE, FmifsCallback);
// Make sure the user didn't cancel.
//
if ( !(g_dwFlags & SCANDISK_CANCELSCAN) )
{
// Check to see if the scan on this drive returned with errors or not.
//
if ( g_bSuccess )
{
// Get the text to use as the caption for the summary box.
//
if (lpCapPre)
lstrcpy(szBuffer, lpCapPre);
else
szBuffer[0] = _T('\0');
if ( SendDlgItemMessage(g_hProgressDlg, IDC_DRIVES, LB_GETTEXT, *lpnIndex, (LPARAM) szBuffer + (lstrlen(szBuffer) * sizeof(TCHAR))) > 0 )
lpCaption = szBuffer;
else
lpCaption = NULL;
// Display the summary message for this drive.
//
SummaryDialog(g_hProgressDlg, NULL, g_bSuccess);
}
else
{
bAllHappy = FALSE;
// If there were errors on this drive and the user
// wants to automatically fix them, we need to run
// the check disk function again with the fix error
// flag set (/F). We don't do this first because if
// the drive can't be locked it won't even scan the
// drive to see if there is an error before asking to
// check on reboot.
//
if ( bFix )
Chkdsk(szwDrive, szwFileSystem, TRUE, FALSE, FALSE, bSurface, NULL, FALSE, FmifsCallback);
}
}
// Make sure the memory for the summary dialog is freed.
//
SummaryDialog(NULL, NULL, FALSE);
// Reset the progress control.
//
SendDlgItemMessage(g_hProgressDlg, IDC_PROGRESS, PBM_SETPOS, 0, 0L);
SetDlgItemText(g_hProgressDlg, IDC_STATUS, NULLSTR);
}
lpnIndex++;
}
// Keep an index of what list box
// item this should be.
//
nIndex++;
}
// Go look at the next drive
//
szwDrive[0]++;
}
// Free the caption prefix.
//
FREE(lpCapPre);
// Renable the controls.
//
EnableWindow(GetDlgItem(g_hProgressDlg, IDOK), TRUE);
EnableWindow(GetDlgItem(g_hProgressDlg, IDC_DRIVES), TRUE);
EnableWindow(GetDlgItem(g_hProgressDlg, IDC_DRIVESTEXT), TRUE);
EnableWindow(GetDlgItem(g_hProgressDlg, IDC_SURFACE), TRUE);
EnableWindow(GetDlgItem(g_hProgressDlg, IDC_AUTOFIX), TRUE);
// Change the text of the IDCANCEL button back to Close.
//
if ( lpBuffer = AllocateString(NULL, IDS_CLOSE) )
{
SetDlgItemText(g_hProgressDlg, IDCANCEL, lpBuffer);
FREE(lpBuffer);
}
}
FREE(lpnSelected);
}
// Free the DLL library.
//
FreeLibrary(hFmifsDll);
// Reset the scaning bit.
//
g_dwFlags &= ~SCANDISK_SCANNING;
// If we are in SAGERUN mode, end the dialog now that
// we are finished scanning.
//
if ( g_dwFlags & SCANDISK_SAGERUN )
EndDialog(g_hProgressDlg, 0);
// Return the success or failure.
//
return (DWORD) bAllHappy;
}
static BOOL CALLBACK FmifsCallback(FMIFS_PACKET_TYPE PacketType, ULONG PacketLength, PVOID PacketData)
{
#ifdef _DEBUG
DWORD dwBytes;
TCHAR szDebug[1024] = NULLSTR;
HANDLE hFile = CreateFile(_T("C:\\SCANDISK.LOG"), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
switch (PacketType)
{
case FmIfsPercentCompleted:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsPercentCompleted (%d%%)\r\n"), ((PFMIFS_PERCENT_COMPLETE_INFORMATION) PacketData)->PercentCompleted);
break;
case FmIfsFormatReport:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsFormatReport\r\n"));
break;
case FmIfsInsertDisk:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsInsertDisk\r\n"));
break;
case FmIfsIncompatibleFileSystem:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsIncompatibleFileSystem\r\n"));
break;
case FmIfsFormattingDestination:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsFormattingDestination\r\n"));
break;
case FmIfsIncompatibleMedia:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsIncompatibleMedia\r\n"));
break;
case FmIfsAccessDenied:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsAccessDenied\r\n"));
break;
case FmIfsMediaWriteProtected:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsMediaWriteProtected\r\n"));
break;
case FmIfsCantLock:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsCantLock\r\n"));
break;
case FmIfsCantQuickFormat:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsCantQuickFormat\r\n"));
break;
case FmIfsIoError:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsIoError\r\n"));
break;
case FmIfsFinished:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsFinished (%s)\r\n"), ((PFMIFS_FINISHED_INFORMATION) PacketData)->Success ? _T("TRUE") : _T("FALSE"));
break;
case FmIfsBadLabel:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsBadLabel\r\n"));
break;
case FmIfsCheckOnReboot:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsCheckOnReboot\r\n"));
break;
case FmIfsTextMessage:
break;
case FmIfsHiddenStatus:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsHiddenStatus\r\n"));
break;
case FmIfsClusterSizeTooSmall:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsClusterSizeTooSmall\r\n"));
break;
case FmIfsClusterSizeTooBig:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsClusterSizeTooBig\r\n"));
break;
case FmIfsVolumeTooSmall:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsVolumeTooSmall\r\n"));
break;
case FmIfsVolumeTooBig:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsVolumeTooBig\r\n"));
break;
case FmIfsNoMediaInDevice:
wsprintf(szDebug, _T("DEBUG: FmifsCallback() - FmIfsNoMediaInDevice\r\n"));
break;
}
if ( hFile != INVALID_HANDLE_VALUE )
{
if ( szDebug[0] )
{
SetFilePointer(hFile, 0, 0, FILE_END);
WriteFile(hFile, szDebug, lstrlen(szDebug) * sizeof(TCHAR), &dwBytes, NULL);
}
CloseHandle(hFile);
}
#endif // _DEBUG
switch (PacketType)
{
case FmIfsPercentCompleted:
// Advance the current position of the progress bar
// to the percent returned.
//
SendDlgItemMessage(g_hProgressDlg, IDC_PROGRESS, PBM_SETPOS, ((PFMIFS_PERCENT_COMPLETE_INFORMATION) PacketData)->PercentCompleted, 0L);
SendDlgItemMessage(g_hProgressDlg, IDC_SCANDISK, STM_SETIMAGE, IMAGE_ICON, (LPARAM) g_hIconScan[((PFMIFS_PERCENT_COMPLETE_INFORMATION) PacketData)->PercentCompleted % 3]);
break;
case FmIfsFinished:
g_bSuccess = !((PFMIFS_FINISHED_INFORMATION) PacketData)->Success;
break;
case FmIfsTextMessage:
UpdateStatus(((PFMIFS_TEXT_MESSAGE) PacketData)->Message);
break;
case FmIfsCheckOnReboot:
((PFMIFS_CHECKONREBOOT_INFORMATION) PacketData)->QueryResult =
(MessageBox(g_hProgressDlg, _T("This drive contains errors and must be checked on startup.\n\n")
_T("Do you want this drive to be checked next time you restart you computer?"), _T("Scandisk"), MB_YESNO | MB_ICONERROR) == IDYES);
break;
}
return ( !(g_dwFlags & SCANDISK_CANCELSCAN) );
}
static VOID UpdateStatus(LPSTR lpText)
{
TCHAR szTextOut[256];
LPTSTR lpChkDsk = NULL,
lpScanDisk = NULL,
lpSearch,
lpCopy,
lpNewText;
DWORD dwLen;
HANDLE hFile = INVALID_HANDLE_VALUE;
#ifdef _UNICODE
TCHAR wcHeader = 0x0000;
TCHAR szNewText[256];
#endif // _UNICODE
#ifdef _UNICODE
if ( MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpText, -1, szNewText, sizeof(szNewText)) )
{
// If this is unicode, make sure the poitner passed in points to a unicode string.
//
lpNewText = szNewText;
// See if we need to write the header bit so that notepad will
// see the file as unicode.
//
if ( (hFile = CreateFile(_T("C:\\SCANDISK.LOG"), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE )
wcHeader = 0xFEFF;
#else // _UNICODE
lpNewText = lpText;
#endif // _UNICODE
// Write to the log file.
//
if ( ( hFile != INVALID_HANDLE_VALUE ) ||
( (hFile = CreateFile(_T("C:\\SCANDISK.LOG"), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE ) )
{
#ifdef _UNICODE
if ( wcHeader )
WriteFile(hFile, &wcHeader, sizeof(WCHAR), &dwLen, NULL);
#endif // _UNICODE
SetFilePointer(hFile, 0, 0, FILE_END);
WriteFile(hFile, (LPTSTR) lpNewText, lstrlen((LPTSTR) lpNewText) * sizeof(TCHAR), &dwLen, NULL);
CloseHandle(hFile);
}
// Remove proceeding characters we don't want (space, \r, \n, \t).
//
for (lpSearch = (LPTSTR) lpNewText; (*lpSearch == _T(' ')) || (*lpSearch == _T('\r')) || (*lpSearch == _T('\n')) || (*lpSearch == _T('\t')); lpSearch++);
if ( ISNUM(*lpSearch) )
{
// We want this info for the summary page.
//
SummaryDialog(g_hProgressDlg, lpSearch, FALSE);
}
else
{
if ( ( lpChkDsk = AllocateString(NULL, IDS_CHKDSK) ) &&
( lpScanDisk = AllocateString(NULL, IDS_SCANDISK) ) )
{
dwLen = lstrlen(lpChkDsk);
lpCopy = szTextOut;
while ( (*lpSearch != _T('\0')) && (*lpSearch != _T('\r')) )
{
if ( _tcsncmp(lpSearch, lpChkDsk, dwLen) == 0 )
{
lstrcpy(lpCopy, lpScanDisk);
lpSearch += dwLen;
lpCopy += lstrlen(lpScanDisk);
}
else
*lpCopy++ = *lpSearch++;
}
*lpCopy = _T('\0');
SetDlgItemText(g_hProgressDlg, IDC_STATUS, szTextOut);
}
FREE(lpScanDisk);
FREE(lpChkDsk);
}
#ifdef _UNICODE
}
#endif // _UNICODE
}
static VOID SummaryDialog(HWND hWndParent, LPTSTR lpText, BOOL bSuccess)
{
static LPTSTR lpSumText[16];
static DWORD dwIndex = 0;
LPTSTR lpSearch;
// First check to make sure lpText is a valid pointer. If it is NULL
// then we must be showing the summary dialog and/or freeing the memory.
//
if ( lpText )
{
// Make sure we don't already have 16 strings in our buffer.
//
if ( dwIndex < 16 )
{
//
// lpText should already point to the first digit of the number
// part of the summary.
//
// We need a pointer to the text after the number. We will search
// for the first space, which should divide the number and the text.
//
for (lpSearch = lpText; (*lpSearch) && (*lpSearch != _T(' ')); lpSearch++);
// Now that we know where the number ends, we can allocate a buffer for it
// and copy the number into it.
//
if ( lpSumText[dwIndex++] = (LPTSTR) MALLOC((size_t)((lpSearch - lpText + 1) * sizeof(TCHAR))) )
lstrcpyn(lpSumText[dwIndex - 1], lpText, (size_t)(lpSearch - lpText + 1));
// We should advance lpSearch to point to the text description, because now
// it should point to a space (unless we hit the end of the string before the space).
//
if ( *lpSearch )
lpSearch++;
// Now we need to know where the text description ends. We just want to search
// for the first new line or line feed character.
//
for (lpText = lpSearch; (*lpSearch) && (*lpSearch != _T('\r')) && (*lpSearch != _T('\n')); lpSearch++);
// Now that we know where the text ends, we can allocate a buffer for it
// also and copy the text into it.
//
if ( lpSumText[dwIndex++] = (LPTSTR) MALLOC((size_t)((lpSearch - lpText + 1) * sizeof(TCHAR))) )
lstrcpyn(lpSumText[dwIndex - 1], lpText, (size_t)(lpSearch - lpText + 1));
}
}
else
{
// We check to make sure dwIndex still isn't zero, because if it is,
// there is no text to display in the summary dialog or to free.
//
if ( dwIndex > 0 )
{
// If the hwnd is valid and the text is null, then we are
// going to display the summary box.
//
if ( hWndParent )
DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_SUMMARY), hWndParent, SummaryProc, (LPARAM) lpSumText);
//
// Now free the memory because eaither a NULL was passed in for the hwnd
// or we already displayed the dialog and now the memory needs to be freed.
//
// Loop through all the strings that may have
// been allocated by going back from where the index
// is now.
//
// Note that some of the pointers may contain NULL
// if a malloc failed, but the FREE() macro will check
// with that before freeing the memory.
//
while ( dwIndex-- > 0 )
FREE(lpSumText[dwIndex]);
// Reset the index so that it doesn't get messed up
// the next time we display a summary.
//
dwIndex = 0;
}
}
}
static INT_PTR CALLBACK SummaryProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
HANDLE_MSG(hDlg, WM_INITDIALOG, Summary_OnInitDialog);
case WM_COMMAND:
if ( (INT) LOWORD(wParam) != IDOK )
return TRUE;
case WM_CLOSE:
EndDialog(hDlg, 0);
return 0;
}
return FALSE;
}
static BOOL Summary_OnInitDialog(HWND hDlg, HWND hwndFocus, LPARAM lParam)
{
INT nSumId[] =
{
IDC_SUM1A, IDC_SUM1B, IDC_SUM2A, IDC_SUM2B,
IDC_SUM3A, IDC_SUM3B, IDC_SUM4A, IDC_SUM4B,
IDC_SUM5A, IDC_SUM5B, IDC_SUM6A, IDC_SUM6B,
IDC_SUM7A, IDC_SUM7B, IDC_SUM8A, IDC_SUM8B
};
LPTSTR *lpStrings = (LPTSTR *) lParam;
DWORD dwIndex;
for (dwIndex = 0; dwIndex < 16; dwIndex++)
SetDlgItemText(hDlg, nSumId[dwIndex], *(lpStrings++));
return FALSE;
}
static FARPROC LoadDllFunction(LPTSTR lpDll, LPCSTR lpFunction, HINSTANCE * lphDll)
{
FARPROC hFunc = NULL;
if ( (*lphDll) = LoadLibrary(lpDll) )
{
if ( (hFunc = GetProcAddress(*lphDll, lpFunction)) == NULL )
{
FreeLibrary(*lphDll);
*lphDll = NULL;
}
}
else
*lphDll = NULL;
return hFunc;
}