You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2426 lines
86 KiB
2426 lines
86 KiB
#include "shellprv.h"
|
|
#include "ids.h"
|
|
#include "mtpt.h"
|
|
#include "hwcmmn.h"
|
|
#pragma hdrstop
|
|
|
|
#include "apithk.h"
|
|
|
|
const static DWORD FmtaIds[] =
|
|
{
|
|
IDOK, IDH_FORMATDLG_START,
|
|
IDCANCEL, IDH_CANCEL,
|
|
IDC_CAPCOMBO, IDH_FORMATDLG_CAPACITY,
|
|
IDC_FSCOMBO, IDH_FORMATDLG_FILESYS,
|
|
IDC_ASCOMBO, IDH_FORMATDLG_ALLOCSIZE,
|
|
IDC_VLABEL, IDH_FORMATDLG_LABEL,
|
|
IDC_GROUPBOX_1, IDH_COMM_GROUPBOX,
|
|
IDC_QFCHECK, IDH_FORMATDLG_QUICKFULL,
|
|
IDC_ECCHECK, IDH_FORMATDLG_COMPRESS,
|
|
IDC_FMTPROGRESS, IDH_FORMATDLG_PROGRESS,
|
|
0,0
|
|
};
|
|
|
|
const static DWORD ChkaIds[] =
|
|
{
|
|
IDOK, IDH_CHKDSKDLG_START,
|
|
IDCANCEL, IDH_CHKDSKDLG_CANCEL,
|
|
IDC_GROUPBOX_1, IDH_COMM_GROUPBOX,
|
|
IDC_FIXERRORS, IDH_CHKDSKDLG_FIXERRORS,
|
|
IDC_RECOVERY, IDH_CHKDSKDLG_SCAN,
|
|
IDC_CHKDSKPROGRESS, IDH_CHKDSKDLG_PROGRESS,
|
|
IDC_PHASE, -1,
|
|
0,0
|
|
};
|
|
|
|
// The following structure encapsulates our calling into the FMIFS.DLL
|
|
typedef struct
|
|
{
|
|
HINSTANCE hFMIFS_DLL;
|
|
PFMIFS_FORMATEX_ROUTINE FormatEx;
|
|
PFMIFS_QSUPMEDIA_ROUTINE QuerySupportedMedia;
|
|
PFMIFS_ENABLECOMP_ROUTINE EnableVolumeCompression;
|
|
PFMIFS_CHKDSKEX_ROUTINE ChkDskEx;
|
|
PFMIFS_QUERY_DEVICE_INFO_ROUTINE QueryDeviceInformation;
|
|
} FMIFS;
|
|
|
|
typedef
|
|
HRESULT
|
|
(*PDISKCOPY_MAKEBOOTDISK_ROUTINE)(
|
|
IN HINSTANCE hInstance,
|
|
IN UINT iDrive,
|
|
IN BOOL* pfCancelled,
|
|
IN FMIFS_CALLBACK pCallback
|
|
);
|
|
|
|
// The following structure encapsulates our calling into the DISKCOPY.DLL
|
|
typedef struct
|
|
{
|
|
HINSTANCE hDISKCOPY_DLL;
|
|
PDISKCOPY_MAKEBOOTDISK_ROUTINE MakeBootDisk;
|
|
} DISKCOPY;
|
|
|
|
// This structure described the current formatting session
|
|
typedef struct
|
|
{
|
|
LONG cRef; // reference count on this structure
|
|
UINT drive; // 0-based index of drive to format
|
|
UINT fmtID; // Last format ID
|
|
UINT options; // options passed to us via the API
|
|
FMIFS fmifs; // above
|
|
DISKCOPY diskcopy; // above
|
|
HWND hDlg; // handle to the format dialog
|
|
BOOL fIsFloppy; // TRUE -> its a floppy
|
|
BOOL fIs35HDFloppy; // TRUE -> its a standard 3.5" High Density floppy
|
|
BOOL fIsMemoryStick; // TRUE -> its a memory stick (special formatting only)
|
|
BOOL fIsNTFSBlocked; // TRUE -> its a NTFS not-supported device
|
|
BOOL fEnableComp; // Last "Enable Comp" choice from user
|
|
BOOL fCancelled; // User cancelled the last format
|
|
BOOL fShouldCancel; // User has clicked cancel; pending abort
|
|
BOOL fWasFAT; // Was it FAT originally?
|
|
BOOL fFinishedOK; // Did format complete sucessfully?
|
|
BOOL fErrorAlready; // Did we put up an error dialog already?
|
|
BOOL fDisabled; // Is rgfControlEnabled[] valid?
|
|
DWORD dwClusterSize; // Orig NT cluster size, or last choice
|
|
WCHAR wszVolName[MAX_PATH]; // Volume Label
|
|
WCHAR wszDriveName[4]; // Root path to drive (eg: A:\)
|
|
HANDLE hThread; // Handle of format thread
|
|
|
|
// Array of media types supported by the device
|
|
// for NT5, we have an expanded list that includes japanese types.
|
|
FMIFS_MEDIA_TYPE rgMedia[IDS_FMT_MEDIA_J22-IDS_FMT_MEDIA_J0];
|
|
|
|
// Used to cache the enabled/disabled state of the dialog controls
|
|
BOOL rgfControlEnabled[DLG_FORMATDISK_NUMCONTROLS];
|
|
|
|
// should we create a boot disk rather than a traditional format
|
|
BOOL fMakeBootDisk;
|
|
|
|
} FORMATINFO;
|
|
|
|
//
|
|
// An enumeration to make the filesystem combo-box code more readble
|
|
//
|
|
|
|
typedef enum tagFILESYSENUM
|
|
{
|
|
e_FAT = 0,
|
|
e_NTFS,
|
|
e_FAT32
|
|
} FILESYSENUM;
|
|
|
|
#define FS_STR_NTFS TEXT("NTFS")
|
|
#define FS_STR_FAT32 TEXT("FAT32")
|
|
#define FS_STR_FAT TEXT("FAT")
|
|
|
|
//
|
|
// Private WM_USER messages we will use. For some unknown reason, USER sends
|
|
// us a WM_USER during initialization, so I start my private messages at
|
|
// WM_USER + 0x0100
|
|
//
|
|
|
|
typedef enum tagPRIVMSGS
|
|
{
|
|
PWM_FORMATDONE = WM_USER + 0x0100,
|
|
PWM_CHKDSKDONE
|
|
} PRIVMSGS;
|
|
|
|
//
|
|
// Synopsis: Loads FMIFS.DLL and sets up the function entry points for
|
|
// the member functions we are interested in.
|
|
//
|
|
HRESULT LoadFMIFS(FMIFS *pFMIFS)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Load the FMIFS DLL and query for the entry points we need
|
|
//
|
|
|
|
// SECURITY: what non-relative path do we use that will work on ia64 too?
|
|
if (NULL == (pFMIFS->hFMIFS_DLL = LoadLibrary(TEXT("FMIFS.DLL"))))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else if (NULL == (pFMIFS->FormatEx = (PFMIFS_FORMATEX_ROUTINE)
|
|
GetProcAddress(pFMIFS->hFMIFS_DLL, "FormatEx")))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else if (NULL == (pFMIFS->QuerySupportedMedia = (PFMIFS_QSUPMEDIA_ROUTINE)
|
|
GetProcAddress(pFMIFS->hFMIFS_DLL, "QuerySupportedMedia")))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else if (NULL == (pFMIFS->EnableVolumeCompression = (PFMIFS_ENABLECOMP_ROUTINE)
|
|
GetProcAddress(pFMIFS->hFMIFS_DLL, "EnableVolumeCompression")))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else if (NULL == (pFMIFS->ChkDskEx = (PFMIFS_CHKDSKEX_ROUTINE)
|
|
GetProcAddress(pFMIFS->hFMIFS_DLL, "ChkdskEx")))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else if (NULL == (pFMIFS->QueryDeviceInformation = (PFMIFS_QUERY_DEVICE_INFO_ROUTINE)
|
|
GetProcAddress(pFMIFS->hFMIFS_DLL, "QueryDeviceInformation")))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
//
|
|
// If anything failed, and we've got the DLL loaded, release the DLL
|
|
//
|
|
|
|
if (hr != S_OK && pFMIFS->hFMIFS_DLL)
|
|
{
|
|
FreeLibrary(pFMIFS->hFMIFS_DLL);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Synopsis: Loads DISKCOPY.DLL and sets up the function entry points for
|
|
// the member functions we are interested in.
|
|
//
|
|
HRESULT LoadDISKCOPY(DISKCOPY *pDISKCOPY)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Load the DISKCOPY DLL and query for the entry points we need
|
|
//
|
|
|
|
// SECURITY: what non-relative path do we use that will work on ia64 too?
|
|
if (NULL == (pDISKCOPY->hDISKCOPY_DLL = LoadLibrary(TEXT("DISKCOPY.DLL"))))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else if (NULL == (pDISKCOPY->MakeBootDisk = (PDISKCOPY_MAKEBOOTDISK_ROUTINE)
|
|
GetProcAddress(pDISKCOPY->hDISKCOPY_DLL, MAKEINTRESOURCEA(1)))) //MakeBootDisk is at ordinal 1 in diskcopy.dll
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
//
|
|
// If anything failed, and we've got the DLL loaded, release the DLL
|
|
//
|
|
|
|
if (hr != S_OK && pDISKCOPY->hDISKCOPY_DLL)
|
|
{
|
|
FreeLibrary(pDISKCOPY->hDISKCOPY_DLL);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
void AddRefFormatInfo(FORMATINFO *pFormatInfo)
|
|
{
|
|
InterlockedIncrement(&pFormatInfo->cRef);
|
|
}
|
|
|
|
void ReleaseFormatInfo(FORMATINFO *pFormatInfo)
|
|
{
|
|
ASSERT( 0 != pFormatInfo->cRef );
|
|
if (InterlockedDecrement(&pFormatInfo->cRef) == 0)
|
|
{
|
|
if (pFormatInfo->fmifs.hFMIFS_DLL)
|
|
{
|
|
FreeLibrary(pFormatInfo->fmifs.hFMIFS_DLL);
|
|
}
|
|
|
|
if (pFormatInfo->diskcopy.hDISKCOPY_DLL)
|
|
{
|
|
FreeLibrary(pFormatInfo->diskcopy.hDISKCOPY_DLL);
|
|
}
|
|
|
|
if (pFormatInfo->hThread)
|
|
{
|
|
CloseHandle(pFormatInfo->hThread);
|
|
}
|
|
|
|
LocalFree(pFormatInfo);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Thread-Local Storage index for our FORMATINFO structure pointer
|
|
//
|
|
static DWORD g_iTLSFormatInfo = 0;
|
|
static LONG g_cTLSFormatInfo = 0; // Usage count
|
|
|
|
// Synopsis: Allocates a thread-local index slot for this thread's
|
|
// FORMATINFO pointer, if the index doesn't already exist.
|
|
// In any event, stores the FORMATINFO pointer in the slot
|
|
// and increments the index's usage count.
|
|
//
|
|
// Arguments: [pFormatInfo] -- The pointer to store
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
HRESULT StuffFormatInfoPtr(FORMATINFO *pFormatInfo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Allocate an index slot for our thread-local FORMATINFO pointer, if one
|
|
// doesn't already exist, then stuff our FORMATINFO ptr at that index.
|
|
ENTERCRITICAL;
|
|
if (0 == g_iTLSFormatInfo)
|
|
{
|
|
if (0xFFFFFFFF == (g_iTLSFormatInfo = TlsAlloc()))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
g_cTLSFormatInfo = 0;
|
|
}
|
|
if (S_OK == hr)
|
|
{
|
|
if (TlsSetValue(g_iTLSFormatInfo, (void *) pFormatInfo))
|
|
{
|
|
g_cTLSFormatInfo++;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Synopsis: Decrements the usage count on our thread-local storage
|
|
// index, and if it goes to zero the index is free'd
|
|
//
|
|
// Arguments: [none]
|
|
//
|
|
// Returns: none
|
|
//
|
|
void UnstuffFormatInfoPtr()
|
|
{
|
|
ENTERCRITICAL;
|
|
if (0 == --g_cTLSFormatInfo)
|
|
{
|
|
TlsFree(g_iTLSFormatInfo);
|
|
g_iTLSFormatInfo = 0;
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
// Synopsis: Retrieves this threads FORMATINFO ptr by grabbing the
|
|
// thread-local value previously stuff'd
|
|
//
|
|
// Arguments: [none]
|
|
//
|
|
// Returns: The pointer, of course
|
|
//
|
|
FORMATINFO *GetFormatInfoPtr()
|
|
{
|
|
return (FORMATINFO*)TlsGetValue(g_iTLSFormatInfo);
|
|
}
|
|
|
|
|
|
|
|
// Synopsis: Ghosts all controls except "Cancel", saving their
|
|
// previous state in the FORMATINFO structure
|
|
//
|
|
// Arguments: [pFormatInfo] -- Describes a format dialog session
|
|
//
|
|
// Notes: Also changes "Close" button text to read "Cancel"
|
|
//
|
|
void DisableControls(FORMATINFO *pFormatInfo)
|
|
{
|
|
WCHAR wszCancel[64];
|
|
|
|
// Do this only if we haven't disabled the controls yet, otherwise
|
|
// we double-disable and our rgfControlEnabled[] array gets corrupted.
|
|
if (!pFormatInfo->fDisabled)
|
|
{
|
|
int i;
|
|
pFormatInfo->fDisabled = TRUE;
|
|
for (i = 0; i < DLG_FORMATDISK_NUMCONTROLS; i++)
|
|
{
|
|
HWND hControl = GetDlgItem(pFormatInfo->hDlg, i + DLG_FORMATDISK_FIRSTCONTROL);
|
|
pFormatInfo->rgfControlEnabled[i] = !EnableWindow(hControl, FALSE);
|
|
}
|
|
}
|
|
|
|
EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDOK), FALSE);
|
|
|
|
LoadString(HINST_THISDLL, IDS_FMT_CANCEL, wszCancel, ARRAYSIZE(wszCancel));
|
|
SetWindowText(GetDlgItem(pFormatInfo->hDlg, IDCANCEL), wszCancel);
|
|
}
|
|
|
|
// Synopsis: Restores controls to the enabled/disabled state they were
|
|
// before a previous call to DisableControls().
|
|
//
|
|
// Arguments: [pFormatInfo] -- Decribes a format dialog session
|
|
// [fReady] - If TRUE, then enable everything
|
|
// If FALSE, then enable combo boxes but leave
|
|
// buttons in limbo because there is still a format
|
|
// pending
|
|
//
|
|
// Notes: Also changes "Cancel" button to say "Close"
|
|
// Also sets focus to Cancel button instead of Start button
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
void EnableControls(FORMATINFO *pFormatInfo, BOOL fReady)
|
|
{
|
|
WCHAR wszClose[64];
|
|
int i;
|
|
HWND hwnd;
|
|
|
|
// Do this only if we have valid info in rgfControlEnabled[].
|
|
// This catches the case where we give up on a format because it is
|
|
// unstuck, and then finally it unsticks itself and tells us,
|
|
// so we go and re-enable a second time.
|
|
if (pFormatInfo->fDisabled)
|
|
{
|
|
pFormatInfo->fDisabled = FALSE;
|
|
|
|
for (i = 0; i < DLG_FORMATDISK_NUMCONTROLS; i++)
|
|
{
|
|
HWND hControl = GetDlgItem(pFormatInfo->hDlg, i + DLG_FORMATDISK_FIRSTCONTROL);
|
|
EnableWindow(hControl, pFormatInfo->rgfControlEnabled[i]);
|
|
}
|
|
}
|
|
|
|
hwnd = GetDlgItem(pFormatInfo->hDlg, IDOK);
|
|
EnableWindow(hwnd, fReady);
|
|
SendMessage(hwnd, BM_SETSTYLE, BS_PUSHBUTTON, MAKELPARAM(TRUE,0));
|
|
|
|
LoadString(HINST_THISDLL, IDS_FMT_CLOSE, wszClose, ARRAYSIZE(wszClose));
|
|
hwnd = GetDlgItem(pFormatInfo->hDlg, IDCANCEL);
|
|
SetWindowText(hwnd, wszClose);
|
|
SendMessage(hwnd, BM_SETSTYLE, BS_DEFPUSHBUTTON, MAKELPARAM(TRUE,0));
|
|
SendMessage(pFormatInfo->hDlg, DM_SETDEFID, IDCANCEL, 0);
|
|
|
|
// Shove focus only if it's on the OK button. Otherwise we end up
|
|
// yanking focus away from a user who is busy dorking with the dialog,
|
|
// or -- worse -- dorking with a completely unrelated dialog!
|
|
|
|
if (GetFocus() == GetDlgItem(pFormatInfo->hDlg, IDOK))
|
|
SetFocus(hwnd);
|
|
}
|
|
|
|
// Sets the dialog's title to "Format Floppy (A:)" or
|
|
// "Formatting Floppy (A:)"
|
|
void SetDriveWindowTitle(HWND hdlg, LPCWSTR pszDrive, UINT ids)
|
|
{
|
|
SHFILEINFO sfi;
|
|
WCHAR wszWinTitle[MAX_PATH]; // Format dialog window title
|
|
|
|
LoadString(HINST_THISDLL, ids, wszWinTitle, ARRAYSIZE(wszWinTitle));
|
|
|
|
if (SHGetFileInfo(pszDrive, FILE_ATTRIBUTE_DIRECTORY, &sfi, sizeof(sfi),
|
|
SHGFI_USEFILEATTRIBUTES | SHGFI_DISPLAYNAME))
|
|
{
|
|
StringCchCat(wszWinTitle, ARRAYSIZE(wszWinTitle), sfi.szDisplayName); // ok to truncate
|
|
}
|
|
|
|
SetWindowText(hdlg, wszWinTitle);
|
|
}
|
|
|
|
//
|
|
// Synopsis: Called when a user picks a filesystem in the dialog, this
|
|
// sets the states of the other relevant controls, such as
|
|
// Enable Compression, Allocation Size, etc.
|
|
//
|
|
// Arguments: [fsenum] -- One of e_FAT, e_NTFS, or e_FAT32
|
|
// [pFormatInfo] -- Current format dialog session
|
|
//
|
|
void FileSysChange(FILESYSENUM fsenum, FORMATINFO *pFormatInfo)
|
|
{
|
|
WCHAR wszTmp[MAX_PATH];
|
|
|
|
switch (fsenum)
|
|
{
|
|
case e_FAT:
|
|
case e_FAT32:
|
|
{
|
|
// un-check & disable the "Enable Compression" checkbox
|
|
CheckDlgButton(pFormatInfo->hDlg, IDC_ECCHECK, FALSE);
|
|
EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_ECCHECK), FALSE);
|
|
|
|
SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_RESETCONTENT, 0, 0);
|
|
|
|
LoadString(HINST_THISDLL, IDS_FMT_ALLOC0, wszTmp, ARRAYSIZE(wszTmp));
|
|
SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_ADDSTRING, 0, (LPARAM)wszTmp);
|
|
SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_SETCURSEL, 0, 0);
|
|
}
|
|
break;
|
|
|
|
case e_NTFS:
|
|
{
|
|
int i;
|
|
|
|
// un-check & disable the "Enable Compression" checkbox
|
|
EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_ECCHECK), TRUE);
|
|
CheckDlgButton(pFormatInfo->hDlg, IDC_ECCHECK, pFormatInfo->fEnableComp);
|
|
|
|
// Set up the NTFS Allocation choices, and select the current choice
|
|
SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_RESETCONTENT, 0, 0);
|
|
|
|
for (i = IDS_FMT_ALLOC0; i <= IDS_FMT_ALLOC4; i++)
|
|
{
|
|
LoadString(HINST_THISDLL, i, wszTmp, ARRAYSIZE(wszTmp));
|
|
SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_ADDSTRING, 0, (LPARAM)wszTmp);
|
|
}
|
|
|
|
switch (pFormatInfo->dwClusterSize)
|
|
{
|
|
case 512:
|
|
SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_SETCURSEL, 1, 0);
|
|
break;
|
|
|
|
case 1024:
|
|
SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_SETCURSEL, 2, 0);
|
|
break;
|
|
|
|
case 2048:
|
|
SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_SETCURSEL, 3, 0);
|
|
break;
|
|
|
|
case 4096:
|
|
SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_SETCURSEL, 4, 0);
|
|
break;
|
|
|
|
default:
|
|
SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_SETCURSEL, 0, 0);
|
|
break;
|
|
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Is this drive a GPT drive?
|
|
// GPT drive: Guid-Partition Table - a replacement for the Master Boot Record, used on some IA64 machines, can only use NTFS
|
|
BOOL IsGPTDrive(int iDrive)
|
|
{
|
|
BOOL fRetVal = FALSE;
|
|
#ifdef _WIN64
|
|
HANDLE hDrive;
|
|
TCHAR szDrive[] = TEXT("\\\\.\\A:");
|
|
|
|
ASSERT(iDrive < 26);
|
|
szDrive[4] += (TCHAR)iDrive;
|
|
|
|
hDrive = CreateFile(szDrive, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (INVALID_HANDLE_VALUE != hDrive)
|
|
{
|
|
PARTITION_INFORMATION_EX partitionEx;
|
|
DWORD cbReturned;
|
|
if (DeviceIoControl(hDrive, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, (void*)&partitionEx, sizeof(PARTITION_INFORMATION_EX), &cbReturned, NULL))
|
|
{
|
|
if (partitionEx.PartitionStyle == PARTITION_STYLE_GPT)
|
|
{
|
|
fRetVal = TRUE;
|
|
}
|
|
}
|
|
CloseHandle(hDrive);
|
|
}
|
|
#endif
|
|
return fRetVal;
|
|
}
|
|
|
|
BOOL IsDVDRAMMedia(int iDrive)
|
|
{
|
|
BOOL fRetVal = FALSE;
|
|
CMountPoint *pmtpt = CMountPoint::GetMountPoint(iDrive);
|
|
if (pmtpt)
|
|
{
|
|
DWORD dwMediaCap, dwDriveCap;
|
|
if (SUCCEEDED(pmtpt->GetCDInfo(&dwDriveCap, &dwMediaCap)))
|
|
{
|
|
fRetVal = (dwMediaCap & HWDMC_DVDRAM);
|
|
}
|
|
pmtpt->Release();
|
|
}
|
|
|
|
return fRetVal;
|
|
}
|
|
|
|
#define GIG_INBYTES (1024 * 1024 * 1024)
|
|
|
|
//
|
|
// FAT32 has some limit which prevents the number of clusters from being
|
|
// less than 65526. And minimum cluster size is 512 bytes. So minimum FAT32
|
|
// volume size is 65526*512.
|
|
|
|
#define FAT32_MIN ((ULONGLONG)65526*512)
|
|
|
|
#define FMTAVAIL_MASK_MIN 0x1
|
|
#define FMTAVAIL_MASK_MAX 0x2
|
|
#define FMTAVAIL_MASK_REQUIRE 0x3
|
|
#define FMTAVAIL_MASK_FORBID 0x4
|
|
|
|
#define FMTAVAIL_TYPE_FLOPPY 0x1
|
|
#define FMTAVAIL_TYPE_DVDRAM 0x2
|
|
#define FMTAVAIL_TYPE_GPT 0x4
|
|
#define FMTAVAIL_TYPE_MEMSTICK 0x8
|
|
#define FMTAVAIL_TYPE_NTFS_BLOCKED 0x10
|
|
|
|
typedef struct _FMTAVAIL
|
|
{
|
|
DWORD dwfs;
|
|
DWORD dwMask;
|
|
DWORD dwForbiddenTypes;
|
|
ULONGLONG qMinSize;
|
|
ULONGLONG qMaxSize;
|
|
} FMTAVAIL;
|
|
|
|
FMTAVAIL rgFmtAvail[] = {
|
|
{e_FAT, FMTAVAIL_MASK_MAX | FMTAVAIL_MASK_FORBID, FMTAVAIL_TYPE_DVDRAM | FMTAVAIL_TYPE_GPT, 0, ((ULONGLONG)2 * GIG_INBYTES) },
|
|
{e_FAT32, FMTAVAIL_MASK_MIN | FMTAVAIL_MASK_MAX | FMTAVAIL_MASK_FORBID, FMTAVAIL_TYPE_GPT | FMTAVAIL_TYPE_FLOPPY | FMTAVAIL_TYPE_MEMSTICK, FAT32_MIN, ((ULONGLONG)32 * GIG_INBYTES) },
|
|
{e_NTFS, FMTAVAIL_MASK_FORBID, FMTAVAIL_TYPE_DVDRAM | FMTAVAIL_TYPE_FLOPPY | FMTAVAIL_TYPE_MEMSTICK | FMTAVAIL_TYPE_NTFS_BLOCKED, 0, 0 }
|
|
};
|
|
|
|
// is a particular disk format available for a drive with given parameters and capacity?
|
|
BOOL FormatAvailable (DWORD dwfs, FORMATINFO* pFormatInfo, ULONGLONG* pqwCapacity)
|
|
{
|
|
BOOL fAvailable = TRUE;
|
|
DWORD dwType = 0;
|
|
|
|
if (pFormatInfo->fIsFloppy)
|
|
{
|
|
dwType |= FMTAVAIL_TYPE_FLOPPY;
|
|
}
|
|
if (IsDVDRAMMedia(pFormatInfo->drive))
|
|
{
|
|
dwType |= FMTAVAIL_TYPE_DVDRAM;
|
|
}
|
|
if (IsGPTDrive(pFormatInfo->drive))
|
|
{
|
|
dwType |= FMTAVAIL_TYPE_GPT;
|
|
}
|
|
if (pFormatInfo->fIsMemoryStick)
|
|
{
|
|
dwType |= FMTAVAIL_TYPE_MEMSTICK;
|
|
}
|
|
if (pFormatInfo->fIsNTFSBlocked)
|
|
{
|
|
dwType |= FMTAVAIL_TYPE_NTFS_BLOCKED;
|
|
}
|
|
|
|
for (int i = 0; i < ARRAYSIZE(rgFmtAvail); i++)
|
|
{
|
|
// check only entries that match the format we're looking for
|
|
if (rgFmtAvail[i].dwfs == dwfs)
|
|
{
|
|
// if a failure conditions is true, then this format is unavailable
|
|
if ((rgFmtAvail[i].dwMask & FMTAVAIL_MASK_FORBID) && (rgFmtAvail[i].dwForbiddenTypes & dwType))
|
|
{
|
|
fAvailable = FALSE;
|
|
break;
|
|
}
|
|
|
|
if ((rgFmtAvail[i].dwMask & FMTAVAIL_MASK_MIN) && (*pqwCapacity < rgFmtAvail[i].qMinSize))
|
|
{
|
|
fAvailable = FALSE;
|
|
break;
|
|
}
|
|
|
|
if ((rgFmtAvail[i].dwMask & FMTAVAIL_MASK_MAX) && (*pqwCapacity > rgFmtAvail[i].qMaxSize))
|
|
{
|
|
fAvailable = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fAvailable;
|
|
}
|
|
|
|
HRESULT GetPartitionSizeInBytes(int iDrive, ULONGLONG* pqwPartitionSize)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
HANDLE hFile;
|
|
TCHAR szDrive[] = TEXT("\\\\.\\A:");
|
|
|
|
*pqwPartitionSize = 0;
|
|
|
|
ASSERT(iDrive < 26);
|
|
szDrive[4] += (TCHAR)iDrive;
|
|
|
|
hFile = CreateFile(szDrive, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (INVALID_HANDLE_VALUE != hFile)
|
|
{
|
|
GET_LENGTH_INFORMATION LengthInfo;
|
|
DWORD cbReturned;
|
|
|
|
if (DeviceIoControl(hFile, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, (void*)&LengthInfo, sizeof(LengthInfo), &cbReturned, NULL) &&
|
|
LengthInfo.Length.QuadPart)
|
|
{
|
|
*pqwPartitionSize = LengthInfo.Length.QuadPart;
|
|
hr = S_OK;
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// this helper function adds a string to a combo box with the associated dword (dwfs) as its itemdata
|
|
void _AddFSString(HWND hwndCB, WCHAR* pwsz, DWORD dwfs)
|
|
{
|
|
int iIndex = ComboBox_AddString(hwndCB, pwsz);
|
|
if (iIndex != CB_ERR)
|
|
{
|
|
ComboBox_SetItemData(hwndCB, iIndex, dwfs);
|
|
}
|
|
}
|
|
|
|
// We only support formatting these types of devices
|
|
const FMIFS_MEDIA_TYPE rgFmtSupported[] = { FmMediaRemovable, FmMediaFixed,
|
|
FmMediaF3_1Pt44_512, FmMediaF3_120M_512, FmMediaF3_200Mb_512};
|
|
|
|
//
|
|
// Synopsis: Initializes the format dialog to a default state. Examines
|
|
// the disk/partition to obtain default values.
|
|
//
|
|
// Arguments: [hDlg] -- Handle to the format dialog
|
|
// [pFormatInfo] -- Describes current format session
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
HRESULT InitializeFormatDlg(FORMATINFO *pFormatInfo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG cMedia;
|
|
HWND hCapacityCombo;
|
|
HWND hFilesystemCombo;
|
|
HWND hDlg = pFormatInfo->hDlg;
|
|
WCHAR wszBuffer[256];
|
|
ULONGLONG qwCapacity = 0;
|
|
|
|
// Set up some typical default values
|
|
pFormatInfo->fEnableComp = FALSE;
|
|
pFormatInfo->dwClusterSize = 0;
|
|
pFormatInfo->fIsFloppy = TRUE;
|
|
pFormatInfo->fIsMemoryStick = FALSE;
|
|
pFormatInfo->fIsNTFSBlocked = FALSE;
|
|
pFormatInfo->fIs35HDFloppy = TRUE;
|
|
pFormatInfo->fWasFAT = TRUE;
|
|
pFormatInfo->fFinishedOK = FALSE;
|
|
pFormatInfo->fErrorAlready = FALSE;
|
|
pFormatInfo->wszVolName[0] = L'\0';
|
|
|
|
// Initialize the Quick Format checkbox based on option passed to the SHFormatDrive() API
|
|
Button_SetCheck(GetDlgItem(hDlg, IDC_QFCHECK), pFormatInfo->options & SHFMT_OPT_FULL);
|
|
|
|
// Set the dialog title to indicate which drive we are dealing with
|
|
PathBuildRootW(pFormatInfo->wszDriveName, pFormatInfo->drive);
|
|
SetDriveWindowTitle(pFormatInfo->hDlg, pFormatInfo->wszDriveName, IDS_FMT_FORMAT);
|
|
|
|
// Query the supported media types for the drive in question
|
|
if (!pFormatInfo->fmifs.QuerySupportedMedia(pFormatInfo->wszDriveName,
|
|
pFormatInfo->rgMedia,
|
|
ARRAYSIZE(pFormatInfo->rgMedia),
|
|
&cMedia))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
// For each of the formats that the drive can handle, add a selection
|
|
// to the capcity combobox.
|
|
if (S_OK == hr)
|
|
{
|
|
UINT olderror;
|
|
ULONG i;
|
|
ULONG j;
|
|
|
|
hCapacityCombo = GetDlgItem(hDlg, IDC_CAPCOMBO);
|
|
hFilesystemCombo = GetDlgItem(hDlg, IDC_FSCOMBO);
|
|
|
|
ASSERT(hCapacityCombo && hFilesystemCombo);
|
|
|
|
FMIFS_DEVICE_INFORMATION fmifsdeviceinformation;
|
|
BOOL fOk = pFormatInfo->fmifs.QueryDeviceInformation(
|
|
pFormatInfo->wszDriveName,
|
|
&fmifsdeviceinformation,
|
|
sizeof(fmifsdeviceinformation));
|
|
|
|
if (fOk)
|
|
{
|
|
if (fmifsdeviceinformation.Flags & FMIFS_SONY_MS)
|
|
{
|
|
pFormatInfo->fIsMemoryStick = TRUE;
|
|
}
|
|
|
|
if (fmifsdeviceinformation.Flags & FMIFS_NTFS_NOT_SUPPORTED)
|
|
{
|
|
pFormatInfo->fIsNTFSBlocked = TRUE;
|
|
}
|
|
}
|
|
|
|
// Allow only certain media types
|
|
j = 0;
|
|
for (i = 0; i < cMedia; i++)
|
|
{
|
|
for (int k = 0; k < ARRAYSIZE(rgFmtSupported); k++)
|
|
{
|
|
if (pFormatInfo->rgMedia[i] == rgFmtSupported[k])
|
|
{
|
|
pFormatInfo->rgMedia[j] = pFormatInfo->rgMedia[i];
|
|
j++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
cMedia = j;
|
|
|
|
if (0 == cMedia)
|
|
{
|
|
hr = ERROR_UNRECOGNIZED_MEDIA;
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < cMedia; i++)
|
|
{
|
|
// If we find any non-floppy format, clear the fIsFloppy flag
|
|
if (FmMediaFixed == pFormatInfo->rgMedia[i] || FmMediaRemovable == pFormatInfo->rgMedia[i])
|
|
{
|
|
pFormatInfo->fIsFloppy = FALSE;
|
|
}
|
|
|
|
// if we find any non-3.5" HD floppy format, clear the fIs35HDFloppy flag
|
|
if (FmMediaF3_1Pt44_512 != pFormatInfo->rgMedia[i])
|
|
{
|
|
pFormatInfo->fIs35HDFloppy = FALSE;
|
|
}
|
|
|
|
// For fixed media we query the size, for floppys we present
|
|
// a set of options supported by the drive
|
|
if (FmMediaFixed == pFormatInfo->rgMedia[i] || (FmMediaRemovable == pFormatInfo->rgMedia[i]))
|
|
{
|
|
DWORD dwSectorsPerCluster,
|
|
dwBytesPerSector,
|
|
dwFreeClusters,
|
|
dwClusters;
|
|
|
|
if (SUCCEEDED(GetPartitionSizeInBytes(pFormatInfo->drive, &qwCapacity)))
|
|
{
|
|
// Add a capacity desciption to the combobox
|
|
ShortSizeFormat64(qwCapacity, wszBuffer, ARRAYSIZE(wszBuffer));
|
|
}
|
|
else
|
|
{
|
|
// Couldn't get the free space... prob. not fatal
|
|
LoadString(HINST_THISDLL, IDS_FMT_CAPUNKNOWN, wszBuffer, ARRAYSIZE(wszBuffer));
|
|
}
|
|
ComboBox_AddString(hCapacityCombo, wszBuffer);
|
|
|
|
if (GetDiskFreeSpace(pFormatInfo->wszDriveName,
|
|
&dwSectorsPerCluster,
|
|
&dwBytesPerSector,
|
|
&dwFreeClusters,
|
|
&dwClusters))
|
|
{
|
|
pFormatInfo->dwClusterSize = dwBytesPerSector * dwSectorsPerCluster;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// removable media:
|
|
//
|
|
// add a capacity desciption to the combo baseed on the sequential list of
|
|
// media format descriptors
|
|
LoadString(HINST_THISDLL, IDS_FMT_MEDIA0 + pFormatInfo->rgMedia[i], wszBuffer, ARRAYSIZE(wszBuffer));
|
|
ComboBox_AddString(hCapacityCombo, wszBuffer);
|
|
}
|
|
}
|
|
|
|
|
|
// set capacity to default
|
|
ComboBox_SetCurSel(hCapacityCombo, 0);
|
|
|
|
// Add the appropriate filesystem selections to the combobox
|
|
// We now prioritize NTFS
|
|
if (FormatAvailable(e_NTFS, pFormatInfo, &qwCapacity))
|
|
{
|
|
_AddFSString(hFilesystemCombo, FS_STR_NTFS, e_NTFS);
|
|
}
|
|
|
|
if (FormatAvailable(e_FAT32, pFormatInfo, &qwCapacity))
|
|
{
|
|
|
|
_AddFSString(hFilesystemCombo, FS_STR_FAT32, e_FAT32);
|
|
}
|
|
|
|
if (FormatAvailable(e_FAT, pFormatInfo, &qwCapacity))
|
|
{
|
|
_AddFSString(hFilesystemCombo, FS_STR_FAT, e_FAT);
|
|
}
|
|
|
|
// By default, pick the 0-th entry in the _nonsorted_ combobox.
|
|
// NOTE: this can be overwritten below
|
|
ComboBox_SetCurSel(hFilesystemCombo, 0);
|
|
|
|
// If we can determine something other than FAT is being used,
|
|
// select it as the default in the combobox
|
|
olderror = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
if (GetVolumeInformation(pFormatInfo->wszDriveName,
|
|
pFormatInfo->wszVolName,
|
|
ARRAYSIZE(pFormatInfo->wszVolName),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
wszBuffer,
|
|
ARRAYSIZE(wszBuffer)))
|
|
{
|
|
// If we got a current volume label, stuff it in the edit control
|
|
if (pFormatInfo->wszVolName[0] != L'\0')
|
|
{
|
|
SetWindowText(GetDlgItem(pFormatInfo->hDlg, IDC_VLABEL), pFormatInfo->wszVolName);
|
|
}
|
|
|
|
// for non-floppies we default to keeping the FS the same as the current one
|
|
if (!pFormatInfo->fIsFloppy)
|
|
{
|
|
if (0 == lstrcmpi(FS_STR_NTFS, wszBuffer))
|
|
{
|
|
ComboBox_SelectString(hFilesystemCombo, -1, FS_STR_NTFS);
|
|
pFormatInfo->fWasFAT = FALSE;
|
|
}
|
|
else if (0 == lstrcmpi(FS_STR_FAT32, wszBuffer))
|
|
{
|
|
ComboBox_SelectString(hFilesystemCombo, -1, FS_STR_FAT32);
|
|
pFormatInfo->fWasFAT = TRUE;
|
|
pFormatInfo->dwClusterSize = 0;
|
|
}
|
|
else
|
|
{
|
|
ComboBox_SelectString(hFilesystemCombo, -1, FS_STR_FAT);
|
|
pFormatInfo->fWasFAT = TRUE;
|
|
pFormatInfo->dwClusterSize = 0;
|
|
}
|
|
}
|
|
// FEATURE - What about specialized file-systems? Don't care for now.
|
|
}
|
|
|
|
|
|
#ifndef _WIN64
|
|
// if not WIN64, enable boot-disk creation if we are a 3.5" HD floppy
|
|
EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_BTCHECK), pFormatInfo->fIs35HDFloppy);
|
|
#else
|
|
// if WIN64, hide this option, since we can't use these boot floppies on WIN64
|
|
ShowWindow(GetDlgItem(pFormatInfo->hDlg, IDC_BTCHECK), FALSE);
|
|
#endif
|
|
|
|
|
|
// restore the old errormode
|
|
SetErrorMode(olderror);
|
|
|
|
// set the state of the chkboxes properly based on the FS chosen
|
|
FileSysChange((FILESYSENUM)ComboBox_GetItemData(hFilesystemCombo, ComboBox_GetCurSel(hFilesystemCombo)), pFormatInfo);
|
|
}
|
|
}
|
|
|
|
// If the above failed due to disk not in drive, notify the user
|
|
if (FAILED(hr))
|
|
{
|
|
switch (HRESULT_CODE(hr))
|
|
{
|
|
case ERROR_UNRECOGNIZED_MEDIA:
|
|
ShellMessageBox(HINST_THISDLL,
|
|
hDlg,
|
|
MAKEINTRESOURCE(IDS_UNFORMATTABLE_DISK),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK,
|
|
NULL);
|
|
|
|
break;
|
|
|
|
case ERROR_NOT_READY:
|
|
ShellMessageBox(HINST_THISDLL,
|
|
hDlg,
|
|
MAKEINTRESOURCE(IDS_DRIVENOTREADY),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK,
|
|
pFormatInfo->wszDriveName[0]);
|
|
break;
|
|
|
|
case ERROR_ACCESS_DENIED:
|
|
ShellMessageBox(HINST_THISDLL,
|
|
hDlg,
|
|
MAKEINTRESOURCE(IDS_ACCESSDENIED),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK,
|
|
pFormatInfo->wszDriveName[0]);
|
|
break;
|
|
|
|
case ERROR_WRITE_PROTECT:
|
|
ShellMessageBox(HINST_THISDLL,
|
|
hDlg,
|
|
MAKEINTRESOURCE(IDS_WRITEPROTECTED),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK,
|
|
pFormatInfo->wszDriveName[0]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Synopsis: Called from within the FMIFS DLL's Format function, this
|
|
// updates the format dialog's status bar and responds to
|
|
// format completion/error notifications.
|
|
//
|
|
// Arguments: [PacketType] -- Type of packet (ie: % complete, error, etc)
|
|
// [PacketLength] -- Size, in bytes, of the packet
|
|
// [pPacketData] -- Pointer to the packet
|
|
//
|
|
// Returns: BOOLEAN continuation value
|
|
//
|
|
BOOLEAN FormatCallback(FMIFS_PACKET_TYPE PacketType, ULONG PacketLength, void *pPacketData)
|
|
{
|
|
UINT iMessageID = IDS_FORMATFAILED;
|
|
BOOL fFailed = FALSE;
|
|
FORMATINFO* pFormatInfo = GetFormatInfoPtr();
|
|
|
|
ASSERT(g_iTLSFormatInfo);
|
|
|
|
// Grab the FORMATINFO structure for this thread
|
|
if (pFormatInfo)
|
|
{
|
|
if (!pFormatInfo->fShouldCancel)
|
|
{
|
|
switch(PacketType)
|
|
{
|
|
case FmIfsIncompatibleFileSystem:
|
|
fFailed = TRUE;
|
|
iMessageID = IDS_INCOMPATIBLEFS;
|
|
break;
|
|
|
|
case FmIfsIncompatibleMedia:
|
|
fFailed = TRUE;
|
|
iMessageID = IDS_INCOMPATIBLEMEDIA;
|
|
break;
|
|
|
|
case FmIfsAccessDenied:
|
|
fFailed = TRUE;
|
|
iMessageID = IDS_ACCESSDENIED;
|
|
break;
|
|
|
|
case FmIfsMediaWriteProtected:
|
|
fFailed = TRUE;
|
|
iMessageID = IDS_WRITEPROTECTED;
|
|
break;
|
|
|
|
case FmIfsCantLock:
|
|
fFailed = TRUE;
|
|
iMessageID = IDS_CANTLOCK;
|
|
break;
|
|
|
|
case FmIfsCantQuickFormat:
|
|
fFailed = TRUE;
|
|
iMessageID = IDS_CANTQUICKFORMAT;
|
|
break;
|
|
|
|
case FmIfsIoError:
|
|
fFailed = TRUE;
|
|
// display a different message if we're making a boot disk
|
|
iMessageID = pFormatInfo->fMakeBootDisk ? IDS_NEEDFORMAT : IDS_IOERROR;
|
|
// FUTURE Consider showing head/track etc where error was
|
|
break;
|
|
|
|
case FmIfsBadLabel:
|
|
fFailed = TRUE;
|
|
iMessageID = IDS_BADLABEL;
|
|
break;
|
|
|
|
case FmIfsPercentCompleted:
|
|
{
|
|
FMIFS_PERCENT_COMPLETE_INFORMATION * pPercent =
|
|
(FMIFS_PERCENT_COMPLETE_INFORMATION *) pPacketData;
|
|
|
|
SendDlgItemMessage(pFormatInfo->hDlg, IDC_FMTPROGRESS,
|
|
PBM_SETPOS,
|
|
pPercent->PercentCompleted, 0);
|
|
}
|
|
break;
|
|
|
|
case FmIfsFinished:
|
|
{
|
|
// Format is done; check for failure or success
|
|
FMIFS_FINISHED_INFORMATION* pFinishedInfo = (FMIFS_FINISHED_INFORMATION*)pPacketData;
|
|
|
|
pFormatInfo->fFinishedOK = pFinishedInfo->Success;
|
|
|
|
if (pFinishedInfo->Success)
|
|
{
|
|
// fmifs will "succeed" even if we already failed, so we need to double-check
|
|
// that we haven't already put up error UI
|
|
if (!pFormatInfo->fErrorAlready)
|
|
{
|
|
// If "Enable Compression" is checked, try to enable filesystem compression
|
|
if (IsDlgButtonChecked(pFormatInfo->hDlg, IDC_ECCHECK))
|
|
{
|
|
if (pFormatInfo->fmifs.EnableVolumeCompression(pFormatInfo->wszDriveName,
|
|
COMPRESSION_FORMAT_DEFAULT) == FALSE)
|
|
{
|
|
ShellMessageBox(HINST_THISDLL,
|
|
pFormatInfo->hDlg,
|
|
MAKEINTRESOURCE(IDS_CANTENABLECOMP),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONINFORMATION | MB_OK);
|
|
}
|
|
}
|
|
|
|
// Even though its a quick format, the progress meter should
|
|
// show 100% when the "Format Complete" requester is up
|
|
SendDlgItemMessage(pFormatInfo->hDlg, IDC_FMTPROGRESS,
|
|
PBM_SETPOS,
|
|
100, // set %100 Complete
|
|
0);
|
|
|
|
// FUTURE Consider showing format stats, ie: ser no, bytes, etc
|
|
ShellMessageBox(HINST_THISDLL,
|
|
pFormatInfo->hDlg,
|
|
MAKEINTRESOURCE(IDS_FORMATCOMPLETE),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONINFORMATION | MB_OK);
|
|
}
|
|
|
|
// Restore the dialog title, reset progress and flags
|
|
SendDlgItemMessage(pFormatInfo->hDlg,
|
|
IDC_FMTPROGRESS,
|
|
PBM_SETPOS,
|
|
0, // Reset Percent Complete
|
|
0);
|
|
|
|
// Set the focus onto the Close button
|
|
pFormatInfo->fCancelled = FALSE;
|
|
}
|
|
else
|
|
{
|
|
fFailed = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (fFailed && !pFormatInfo->fErrorAlready)
|
|
{
|
|
// If we received any kind of failure information, put up a final
|
|
// "Format Failed" message. UNLESS we've already put up some nice message
|
|
ShellMessageBox(HINST_THISDLL,
|
|
pFormatInfo->hDlg,
|
|
MAKEINTRESOURCE(iMessageID),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK);
|
|
|
|
pFormatInfo->fErrorAlready = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// user hit cancel
|
|
pFormatInfo->fCancelled = TRUE;
|
|
fFailed = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no pFormatInfo? we're screwed
|
|
fFailed = TRUE;
|
|
}
|
|
|
|
return (BOOLEAN) (fFailed == FALSE);
|
|
}
|
|
|
|
//
|
|
// Synopsis: Spun off as its own thread, this ghosts all controls in the
|
|
// dialog except "Cancel", then does the actual format
|
|
//
|
|
// Arguments: [pIn] -- FORMATINFO structure pointer as a void *
|
|
//
|
|
// Returns: HRESULT thread exit code
|
|
//
|
|
DWORD WINAPI BeginFormat(void * pIn)
|
|
{
|
|
FORMATINFO *pFormatInfo = (FORMATINFO*)pIn;
|
|
HRESULT hr = S_OK;
|
|
|
|
// Save the FORAMTINFO ptr for this thread, to be used in the format
|
|
// callback function
|
|
hr = StuffFormatInfoPtr(pFormatInfo);
|
|
if (hr == S_OK)
|
|
{
|
|
HWND hwndFileSysCB = GetDlgItem(pFormatInfo->hDlg, IDC_FSCOMBO);
|
|
int iCurSel;
|
|
|
|
// Set the window title to indicate format in proress...
|
|
SetDriveWindowTitle(pFormatInfo->hDlg, pFormatInfo->wszDriveName, IDS_FMT_FORMATTING);
|
|
|
|
// Determine the user's choice of filesystem
|
|
iCurSel = ComboBox_GetCurSel(hwndFileSysCB);
|
|
|
|
if (iCurSel != CB_ERR)
|
|
{
|
|
LPCWSTR pwszFileSystemName;
|
|
FMIFS_MEDIA_TYPE MediaType;
|
|
LPITEMIDLIST pidlFormat;
|
|
BOOLEAN fQuickFormat;
|
|
|
|
FILESYSENUM fseType = (FILESYSENUM)ComboBox_GetItemData(hwndFileSysCB, iCurSel);
|
|
|
|
switch (fseType)
|
|
{
|
|
case e_FAT:
|
|
pwszFileSystemName = FS_STR_FAT;
|
|
break;
|
|
|
|
case e_FAT32:
|
|
pwszFileSystemName = FS_STR_FAT32;
|
|
break;
|
|
|
|
case e_NTFS:
|
|
pwszFileSystemName = FS_STR_NTFS;
|
|
break;
|
|
}
|
|
|
|
// Determine the user's choice of media formats
|
|
iCurSel = ComboBox_GetCurSel(GetDlgItem(pFormatInfo->hDlg, IDC_CAPCOMBO));
|
|
if (iCurSel == CB_ERR)
|
|
{
|
|
iCurSel = 0;
|
|
}
|
|
MediaType = pFormatInfo->rgMedia[iCurSel];
|
|
|
|
// Get the cluster size. First selection ("Use Default") yields a zero,
|
|
// while the next 4 select 512, 1024, 2048, or 4096
|
|
iCurSel = ComboBox_GetCurSel(GetDlgItem(pFormatInfo->hDlg, IDC_ASCOMBO));
|
|
if ((iCurSel == CB_ERR) || (iCurSel == 0))
|
|
{
|
|
pFormatInfo->dwClusterSize = 0;
|
|
}
|
|
else
|
|
{
|
|
pFormatInfo->dwClusterSize = 256 << iCurSel;
|
|
}
|
|
|
|
// Quickformatting?
|
|
fQuickFormat = Button_GetCheck(GetDlgItem(pFormatInfo->hDlg, IDC_QFCHECK));
|
|
|
|
// Clear the error state.
|
|
pFormatInfo->fErrorAlready = FALSE;
|
|
|
|
// Tell the shell to get ready... Announce that the media is no
|
|
// longer valid (so people who have active views on it will navigate
|
|
// away) and tell the shell to close its FindFirstChangeNotifications.
|
|
if (SUCCEEDED(SHILCreateFromPath(pFormatInfo->wszDriveName, &pidlFormat, NULL)))
|
|
{
|
|
SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_IDLIST | SHCNF_FLUSH, pidlFormat, 0);
|
|
SHChangeNotifySuspendResume(TRUE, pidlFormat, TRUE, 0);
|
|
}
|
|
else
|
|
{
|
|
pidlFormat = NULL;
|
|
}
|
|
|
|
if (!pFormatInfo->fMakeBootDisk)
|
|
{
|
|
// Do the format.
|
|
pFormatInfo->fmifs.FormatEx(pFormatInfo->wszDriveName,
|
|
MediaType,
|
|
(PWSTR)pwszFileSystemName,
|
|
pFormatInfo->wszVolName,
|
|
fQuickFormat,
|
|
pFormatInfo->dwClusterSize,
|
|
FormatCallback);
|
|
}
|
|
else
|
|
{
|
|
pFormatInfo->diskcopy.MakeBootDisk(pFormatInfo->diskcopy.hDISKCOPY_DLL, pFormatInfo->drive, &pFormatInfo->fCancelled, FormatCallback);
|
|
}
|
|
|
|
// Wake the shell back up.
|
|
if (pidlFormat)
|
|
{
|
|
SHChangeNotifySuspendResume(FALSE, pidlFormat, TRUE, 0);
|
|
ILFree(pidlFormat);
|
|
}
|
|
|
|
// Success or failure, we should fire a notification on the disk
|
|
// since we don't really know the state after the format
|
|
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATHW, (void *)pFormatInfo->wszDriveName, NULL);
|
|
}
|
|
else
|
|
{
|
|
// couldn't get the filesys CB selection
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
// Release the TLS index
|
|
UnstuffFormatInfoPtr();
|
|
}
|
|
|
|
// Post a message back to the DialogProc thread to let it know
|
|
// the format is done. We post the message since otherwise the
|
|
// DialogProc thread will be too busy waiting for this thread
|
|
// to exit to be able to process the PWM_FORMATDONE message
|
|
// immediately.
|
|
PostMessage(pFormatInfo->hDlg, (UINT) PWM_FORMATDONE, 0, 0);
|
|
|
|
ReleaseFormatInfo(pFormatInfo);
|
|
|
|
return (DWORD)hr;
|
|
}
|
|
|
|
BOOL_PTR CALLBACK FormatDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int iID = GET_WM_COMMAND_ID(wParam, lParam);
|
|
int iCMD = GET_WM_COMMAND_CMD(wParam, lParam);
|
|
|
|
// Grab our previously cached pointer to the FORMATINFO struct (see WM_INITDIALOG)
|
|
FORMATINFO *pFormatInfo = (FORMATINFO *) GetWindowLongPtr(hDlg, DWLP_USER);
|
|
|
|
switch (wMsg)
|
|
{
|
|
case PWM_FORMATDONE:
|
|
// Format is done. Reset the window title and clear the progress meter
|
|
SetDriveWindowTitle(pFormatInfo->hDlg, pFormatInfo->wszDriveName, IDS_FMT_FORMAT);
|
|
SendDlgItemMessage(pFormatInfo->hDlg, IDC_FMTPROGRESS, PBM_SETPOS, 0 /* Reset Percent Complete */, 0);
|
|
EnableControls(pFormatInfo, TRUE);
|
|
|
|
if (pFormatInfo->fCancelled)
|
|
{
|
|
// Don't put up UI if the background thread finally finished
|
|
// long after the user issued the cancel
|
|
if (!pFormatInfo->fShouldCancel)
|
|
{
|
|
ShellMessageBox(HINST_THISDLL,
|
|
pFormatInfo->hDlg,
|
|
MAKEINTRESOURCE(IDS_FORMATCANCELLED),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK);
|
|
}
|
|
pFormatInfo->fCancelled = FALSE;
|
|
}
|
|
|
|
if (pFormatInfo->hThread)
|
|
{
|
|
CloseHandle(pFormatInfo->hThread);
|
|
pFormatInfo->hThread = NULL;
|
|
}
|
|
break;
|
|
|
|
case WM_INITDIALOG:
|
|
// Initialize the dialog and cache the FORMATINFO structure's pointer
|
|
// as our dialog's DWLP_USER data
|
|
pFormatInfo = (FORMATINFO *) lParam;
|
|
pFormatInfo->hDlg = hDlg;
|
|
if (FAILED(InitializeFormatDlg(pFormatInfo)))
|
|
{
|
|
EndDialog(hDlg, 0);
|
|
return -1;
|
|
}
|
|
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
if (pFormatInfo && pFormatInfo->hDlg)
|
|
{
|
|
pFormatInfo->hDlg = NULL;
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
if (iCMD == CBN_SELCHANGE)
|
|
{
|
|
// User made a selection in one of the combo boxes
|
|
if (iID == IDC_FSCOMBO)
|
|
{
|
|
// User selected a filesystem... update the rest of the dialog
|
|
// based on this choice
|
|
HWND hFilesystemCombo = (HWND)lParam;
|
|
int iCurSel = ComboBox_GetCurSel(hFilesystemCombo);
|
|
|
|
FileSysChange((FILESYSENUM)ComboBox_GetItemData(hFilesystemCombo, iCurSel), pFormatInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Codepath for controls other than combo boxes...
|
|
switch (iID)
|
|
{
|
|
case IDC_BTCHECK:
|
|
pFormatInfo->fMakeBootDisk = IsDlgButtonChecked(pFormatInfo->hDlg, IDC_BTCHECK);
|
|
EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_CAPCOMBO), !pFormatInfo->fMakeBootDisk);
|
|
EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_FSCOMBO), !pFormatInfo->fMakeBootDisk);
|
|
EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_ASCOMBO), !pFormatInfo->fMakeBootDisk);
|
|
EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_VLABEL), !pFormatInfo->fMakeBootDisk);
|
|
EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_QFCHECK), !pFormatInfo->fMakeBootDisk);
|
|
break;
|
|
case IDC_ECCHECK:
|
|
pFormatInfo->fEnableComp = IsDlgButtonChecked(hDlg, IDC_ECCHECK);
|
|
break;
|
|
|
|
case IDOK:
|
|
{
|
|
// Get user verification for format, break out on CANCEL
|
|
if (IDCANCEL == ShellMessageBox(HINST_THISDLL,
|
|
hDlg,
|
|
MAKEINTRESOURCE(IDS_OKTOFORMAT),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OKCANCEL))
|
|
{
|
|
break;
|
|
}
|
|
|
|
ASSERT(pFormatInfo->hThread == NULL);
|
|
|
|
DisableControls(pFormatInfo);
|
|
pFormatInfo->fCancelled = FALSE;
|
|
pFormatInfo->fShouldCancel = FALSE;
|
|
GetWindowText(GetDlgItem(pFormatInfo->hDlg, IDC_VLABEL), pFormatInfo->wszVolName, MAX_PATH);
|
|
|
|
AddRefFormatInfo(pFormatInfo);
|
|
pFormatInfo->hThread = CreateThread(NULL,
|
|
0,
|
|
BeginFormat,
|
|
(void *)pFormatInfo,
|
|
0,
|
|
NULL);
|
|
if (!pFormatInfo->hThread)
|
|
{
|
|
// ISSUE: we should probably do something...
|
|
ReleaseFormatInfo(pFormatInfo);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
// If the format thread is running, wait for it. If not,
|
|
// exit the dialog
|
|
pFormatInfo->fShouldCancel = TRUE;
|
|
if (pFormatInfo->hThread)
|
|
{
|
|
DWORD dwWait;
|
|
|
|
do
|
|
{
|
|
dwWait = WaitForSingleObject(pFormatInfo->hThread, 10000);
|
|
}
|
|
while ((WAIT_TIMEOUT == dwWait) &&
|
|
(IDRETRY == ShellMessageBox(HINST_THISDLL,
|
|
hDlg,
|
|
MAKEINTRESOURCE(IDS_CANTCANCELFMT),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_RETRYCANCEL)));
|
|
|
|
// If the format doesn't admit to having been killed, it didn't
|
|
// give up peacefully. Just abandon it and let it clean up
|
|
// when it finally gets around to it, at which point we will
|
|
// enable the OK button to let the user take another stab.
|
|
//
|
|
// Careful: The format may have cleaned up while the dialog box
|
|
// was up, so revalidate.
|
|
if (pFormatInfo->hThread)
|
|
{
|
|
CloseHandle(pFormatInfo->hThread);
|
|
pFormatInfo->hThread = NULL;
|
|
pFormatInfo->fCancelled = TRUE;
|
|
EnableControls(pFormatInfo, FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EndDialog(hDlg, IDCANCEL);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_HELP:
|
|
WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR) (LPSTR) FmtaIds);
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR) (LPSTR) FmtaIds);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Synopsis: The SHFormatDrive API provides access to the Shell
|
|
// format dialog. This allows apps which want to format disks
|
|
// to bring up the same dialog that the Shell does to do it.
|
|
//
|
|
// NOTE that the user can format as many diskettes in the
|
|
// specified drive, or as many times, as he/she wishes to.
|
|
//
|
|
// Arguments: [hwnd] -- Parent window (Must NOT be NULL)
|
|
// [drive] -- 0 = A:, 1 = B:, etc.
|
|
// [fmtID] -- see below
|
|
// [options] -- SHFMT_OPT_FULL overrised default quickformat
|
|
// SHFMT_OPT_SYSONLY not support for NT
|
|
//
|
|
// Returns: See Notes
|
|
//
|
|
DWORD WINAPI SHFormatDrive(HWND hwnd, UINT drive, UINT fmtID, UINT options)
|
|
{
|
|
INT_PTR ret;
|
|
FORMATINFO *pFormatInfo = (FORMATINFO *)LocalAlloc(LPTR, sizeof(*pFormatInfo));
|
|
ASSERT(drive < 26);
|
|
|
|
if (!pFormatInfo)
|
|
return SHFMT_ERROR;
|
|
|
|
HRESULT hrCoInit = SHCoInitialize();
|
|
|
|
pFormatInfo->cRef = 1;
|
|
pFormatInfo->drive = drive;
|
|
pFormatInfo->fmtID = fmtID;
|
|
pFormatInfo->options = options;
|
|
|
|
// It makes no sense for NT to "SYS" a disk
|
|
if (pFormatInfo->options & SHFMT_OPT_SYSONLY)
|
|
{
|
|
ret = 0;
|
|
goto done;
|
|
}
|
|
|
|
// Load FMIFS.DLL and DISKCOPY.DLL and open the Format dialog
|
|
if (S_OK == LoadFMIFS(&pFormatInfo->fmifs) &&
|
|
S_OK == LoadDISKCOPY(&pFormatInfo->diskcopy))
|
|
{
|
|
DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_FORMATDISK),
|
|
hwnd, FormatDlgProc, (LPARAM) pFormatInfo);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0 && "Can't load FMIFS.DLL");
|
|
ret = SHFMT_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
// Since time immemorial it has been almost impossible to
|
|
// get SHFMT_CANCEL as a return code. Most of the time, you get
|
|
// SHFMT_ERROR if the user cancels.
|
|
if (pFormatInfo->fCancelled)
|
|
{
|
|
ret = SHFMT_CANCEL;
|
|
}
|
|
else if (pFormatInfo->fFinishedOK)
|
|
{
|
|
// APPCOMPAT: (stephstm) We used to say that we return the Serial
|
|
// Number but we never did. So keep on returning 0 for success.
|
|
// Furthermore, Serial number values could conflict SHFMT_*
|
|
// error codes.
|
|
ret = 0;
|
|
}
|
|
else
|
|
{
|
|
ret = SHFMT_ERROR;
|
|
}
|
|
|
|
done:
|
|
ReleaseFormatInfo(pFormatInfo);
|
|
SHCoUninitialize(hrCoInit);
|
|
return (DWORD)ret;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CHKDSK
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// This structure described the current chkdsk session
|
|
//
|
|
typedef struct
|
|
{
|
|
UINT lastpercent; // last percentage complete received
|
|
UINT currentphase; // current chkdsk phase
|
|
FMIFS fmifs; // ptr to FMIFS structure, above
|
|
BOOL fRecovery; // Attempt to recover bad sectors
|
|
BOOL fFixErrors; // Fix filesystem errors as found
|
|
BOOL fCancelled; // Was chkdsk terminated early?
|
|
BOOL fShouldCancel; // User has clicked cancel; pending abort
|
|
HWND hDlg; // handle to the chkdsk dialog
|
|
HANDLE hThread;
|
|
BOOL fNoFinalMsg; // Do not put up a final failure message
|
|
WCHAR wszDriveName[MAX_PATH]; // For example, "A:\", or "C:\folder\mountedvolume\"
|
|
LONG cRef; // reference count on this structure
|
|
} CHKDSKINFO;
|
|
|
|
void AddRefChkDskInfo(CHKDSKINFO *pChkDskInfo)
|
|
{
|
|
InterlockedIncrement(&pChkDskInfo->cRef);
|
|
}
|
|
|
|
void ReleaseChkDskInfo(CHKDSKINFO *pChkDskInfo)
|
|
{
|
|
ASSERT( 0 != pChkDskInfo->cRef );
|
|
if (InterlockedDecrement(&pChkDskInfo->cRef) == 0)
|
|
{
|
|
if (pChkDskInfo->fmifs.hFMIFS_DLL)
|
|
{
|
|
FreeLibrary(pChkDskInfo->fmifs.hFMIFS_DLL);
|
|
}
|
|
|
|
if (pChkDskInfo->hThread)
|
|
{
|
|
CloseHandle(pChkDskInfo->hThread);
|
|
}
|
|
|
|
LocalFree(pChkDskInfo);
|
|
}
|
|
}
|
|
|
|
|
|
static DWORD g_iTLSChkDskInfo = 0;
|
|
static LONG g_cTLSChkDskInfo = 0; // Usage count
|
|
|
|
//
|
|
// Synopsis: Allocates a thread-local index slot for this thread's
|
|
// CHKDSKINFO pointer, if the index doesn't already exist.
|
|
// In any event, stores the CHKDSKINFO pointer in the slot
|
|
// and increments the index's usage count.
|
|
//
|
|
// Arguments: [pChkDskInfo] -- The pointer to store
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
//
|
|
// Thread-Local Storage index for our CHKDSKINFO structure pointer
|
|
//
|
|
HRESULT StuffChkDskInfoPtr(CHKDSKINFO *pChkDskInfo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Allocate an index slot for our thread-local CHKDSKINFO pointer, if one
|
|
// doesn't already exist, then stuff our CHKDSKINFO ptr at that index.
|
|
|
|
ENTERCRITICAL;
|
|
if (0 == g_iTLSChkDskInfo)
|
|
{
|
|
g_iTLSChkDskInfo = TlsAlloc();
|
|
|
|
if (g_iTLSChkDskInfo == (DWORD)-1)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
g_cTLSChkDskInfo = 0;
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
if (TlsSetValue(g_iTLSChkDskInfo, (void *)pChkDskInfo))
|
|
{
|
|
g_cTLSChkDskInfo++;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Synopsis: Decrements the usage count on our thread-local storage
|
|
// index, and if it goes to zero the index is free'd
|
|
//
|
|
// Arguments: [none]
|
|
//
|
|
// Returns: none
|
|
//
|
|
void UnstuffChkDskInfoPtr()
|
|
{
|
|
ENTERCRITICAL;
|
|
g_cTLSChkDskInfo--;
|
|
|
|
if (g_cTLSChkDskInfo == 0)
|
|
{
|
|
TlsFree(g_iTLSChkDskInfo);
|
|
g_iTLSChkDskInfo = 0;
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
//
|
|
// Synopsis: Retrieves this threads CHKDSKINFO ptr by grabbing the
|
|
// thread-local value previously stuff'd
|
|
//
|
|
// Arguments: [none]
|
|
//
|
|
// Returns: The pointer, of course
|
|
//
|
|
CHKDSKINFO *GetChkDskInfoPtr()
|
|
{
|
|
return (CHKDSKINFO *)TlsGetValue(g_iTLSChkDskInfo);
|
|
}
|
|
|
|
//
|
|
// Synopsis: Ghosts all controls except "Cancel", saving their
|
|
// previous state in the CHKDSKINFO structure
|
|
//
|
|
// Arguments: [pChkDskInfo] -- Describes a ChkDsk dialog session
|
|
//
|
|
// Notes: Also changes "Close" button text to read "Cancel"
|
|
//
|
|
void DisableChkDskControls(CHKDSKINFO *pChkDskInfo)
|
|
{
|
|
// We disable CANCEL because CHKDSK does not
|
|
// allow interruption at the filesystem level.
|
|
EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDC_FIXERRORS), FALSE);
|
|
EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDC_RECOVERY), FALSE);
|
|
EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDOK), FALSE);
|
|
EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDCANCEL), FALSE);
|
|
}
|
|
|
|
//
|
|
// Synopsis: Restores controls to the enabled/disabled state they were
|
|
// before a previous call to DisableControls().
|
|
//
|
|
// Arguments: [pChkDskInfo] -- Decribes a chkdsk dialog session
|
|
//
|
|
void EnableChkDskControls(CHKDSKINFO *pChkDskInfo)
|
|
{
|
|
EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDC_FIXERRORS), TRUE);
|
|
EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDC_RECOVERY), TRUE);
|
|
EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDOK), TRUE);
|
|
EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDCANCEL), TRUE);
|
|
|
|
// Erase the current phase text
|
|
SetWindowText(GetDlgItem(pChkDskInfo->hDlg, IDC_PHASE), TEXT(""));
|
|
pChkDskInfo->lastpercent = 101;
|
|
pChkDskInfo->currentphase = 0;
|
|
}
|
|
|
|
//
|
|
// Synopsis: Called from within the FMIFS DLL's ChkDsk function, this
|
|
// updates the ChkDsk dialog's status bar and responds to
|
|
// chkdsk completion/error notifications.
|
|
//
|
|
// Arguments: [PacketType] -- Type of packet (ie: % complete, error, etc)
|
|
// [PacketLength] -- Size, in bytes, of the packet
|
|
// [pPacketData] -- Pointer to the packet
|
|
//
|
|
// Returns: BOOLEAN continuation value
|
|
//
|
|
BOOLEAN ChkDskCallback(FMIFS_PACKET_TYPE PacketType, ULONG PacketLength, void *pPacketData)
|
|
{
|
|
UINT iMessageID = IDS_CHKDSKFAILED;
|
|
BOOL fFailed = FALSE;
|
|
CHKDSKINFO* pChkDskInfo = GetChkDskInfoPtr();
|
|
|
|
ASSERT(g_iTLSChkDskInfo);
|
|
|
|
// Grab the CHKDSKINFO structure for this thread
|
|
if (pChkDskInfo)
|
|
{
|
|
if (!pChkDskInfo->fShouldCancel)
|
|
{
|
|
switch(PacketType)
|
|
{
|
|
case FmIfsAccessDenied:
|
|
fFailed = TRUE;
|
|
iMessageID = IDS_CHKACCESSDENIED;
|
|
break;
|
|
|
|
case FmIfsCheckOnReboot:
|
|
{
|
|
FMIFS_CHECKONREBOOT_INFORMATION * pRebootInfo = (FMIFS_CHECKONREBOOT_INFORMATION *)pPacketData;
|
|
|
|
// Check to see whether or not the user wants to schedule this
|
|
// chkdsk for the next reboot, since the drive cannot be locked
|
|
// right now.
|
|
if (IDYES == ShellMessageBox(HINST_THISDLL,
|
|
pChkDskInfo->hDlg,
|
|
MAKEINTRESOURCE(IDS_CHKONREBOOT),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONINFORMATION | MB_YESNO))
|
|
{
|
|
// Yes, have FMIFS schedule an autochk for us
|
|
pRebootInfo->QueryResult = TRUE;
|
|
pChkDskInfo->fNoFinalMsg = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Nope, just fail out with "cant lock drive"
|
|
fFailed = TRUE;
|
|
iMessageID = IDS_CHKDSKFAILED;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FmIfsMediaWriteProtected:
|
|
fFailed = TRUE;
|
|
iMessageID = IDS_WRITEPROTECTED;
|
|
break;
|
|
|
|
case FmIfsIoError:
|
|
fFailed = TRUE;
|
|
iMessageID = IDS_IOERROR;
|
|
// FUTURE Consider showing head/track etc where error was
|
|
break;
|
|
|
|
case FmIfsPercentCompleted:
|
|
{
|
|
FMIFS_PERCENT_COMPLETE_INFORMATION* pPercent = (FMIFS_PERCENT_COMPLETE_INFORMATION *)pPacketData;
|
|
|
|
SendMessage(GetDlgItem(pChkDskInfo->hDlg, IDC_CHKDSKPROGRESS),
|
|
PBM_SETPOS,
|
|
pPercent->PercentCompleted, // updatee % complete
|
|
0);
|
|
|
|
if (pPercent->PercentCompleted < pChkDskInfo->lastpercent)
|
|
{
|
|
WCHAR wszTmp[100];
|
|
WCHAR wszFormat[100];
|
|
|
|
// If this % complete is less than the last one seen,
|
|
// we have completed a phase of the chkdsk and should
|
|
// advance to the next one.
|
|
LoadString(HINST_THISDLL, IDS_CHKPHASE, wszFormat, ARRAYSIZE(wszFormat));
|
|
StringCchPrintf(wszTmp, ARRAYSIZE(wszTmp), wszFormat, ++(pChkDskInfo->currentphase)); // ok to truncate
|
|
SetDlgItemText(pChkDskInfo->hDlg, IDC_PHASE, wszTmp);
|
|
}
|
|
|
|
pChkDskInfo->lastpercent = pPercent->PercentCompleted;
|
|
}
|
|
break;
|
|
|
|
case FmIfsFinished:
|
|
{
|
|
// ChkDsk is done; check for failure or success
|
|
FMIFS_FINISHED_INFORMATION * pFinishedInfo = (FMIFS_FINISHED_INFORMATION *) pPacketData;
|
|
|
|
// ChkDskEx now return the proper success value
|
|
if (pFinishedInfo->Success)
|
|
{
|
|
// Since we're done, force the progress gauge to 100%, so we
|
|
// don't sit here if the chkdsk code misled us
|
|
SendMessage(GetDlgItem(pChkDskInfo->hDlg, IDC_CHKDSKPROGRESS),
|
|
PBM_SETPOS,
|
|
100, // Percent Complete
|
|
0);
|
|
|
|
ShellMessageBox(HINST_THISDLL,
|
|
pChkDskInfo->hDlg,
|
|
MAKEINTRESOURCE(IDS_CHKDSKCOMPLETE),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONINFORMATION | MB_OK);
|
|
|
|
SetDlgItemText(pChkDskInfo->hDlg, IDC_PHASE, TEXT(""));
|
|
|
|
SendMessage(GetDlgItem(pChkDskInfo->hDlg, IDC_CHKDSKPROGRESS),
|
|
PBM_SETPOS,
|
|
0, // reset Percent Complete
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
iMessageID = IDS_CHKDSKFAILED;
|
|
fFailed = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// If we received any kind of failure information, put up a final
|
|
// "ChkDsk Failed" message.
|
|
if (fFailed && (pChkDskInfo->fNoFinalMsg == FALSE))
|
|
{
|
|
pChkDskInfo->fNoFinalMsg = TRUE;
|
|
|
|
ShellMessageBox(HINST_THISDLL,
|
|
pChkDskInfo->hDlg,
|
|
MAKEINTRESOURCE(iMessageID),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK);
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// If the user has signalled to abort the ChkDsk, return
|
|
// FALSE out of here right now
|
|
pChkDskInfo->fCancelled = TRUE;
|
|
fFailed = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fFailed = TRUE;
|
|
}
|
|
|
|
return (BOOLEAN) (fFailed == FALSE);
|
|
}
|
|
|
|
void DoChkDsk(CHKDSKINFO* pChkDskInfo, LPWSTR pwszFileSystem)
|
|
{
|
|
TCHAR szVolumeGUID[50]; // 50: from doc
|
|
FMIFS_CHKDSKEX_PARAM param = {0};
|
|
|
|
param.Major = 1;
|
|
param.Minor = 0;
|
|
param.Flags = pChkDskInfo->fRecovery ? FMIFS_CHKDSK_RECOVER : 0;
|
|
|
|
GetVolumeNameForVolumeMountPoint(pChkDskInfo->wszDriveName,
|
|
szVolumeGUID,
|
|
ARRAYSIZE(szVolumeGUID));
|
|
|
|
// the backslash at the end means check for fragmentation.
|
|
PathRemoveBackslash(szVolumeGUID);
|
|
|
|
pChkDskInfo->fmifs.ChkDskEx(szVolumeGUID,
|
|
pwszFileSystem,
|
|
(BOOLEAN)pChkDskInfo->fFixErrors,
|
|
¶m,
|
|
ChkDskCallback);
|
|
}
|
|
|
|
|
|
//
|
|
// Synopsis: Spun off as its own thread, this ghosts all controls in the
|
|
// dialog except "Cancel", then does the actual ChkDsk
|
|
//
|
|
// Arguments: [pIn] -- CHKDSKINFO structure pointer as a void *
|
|
//
|
|
// Returns: HRESULT thread exit code
|
|
//
|
|
DWORD WINAPI BeginChkDsk(void * pIn)
|
|
{
|
|
CHKDSKINFO *pChkDskInfo = (CHKDSKINFO *)pIn;
|
|
HRESULT hr;
|
|
|
|
// Save the CHKDSKINFO ptr for this thread, to be used in the ChkDsk
|
|
// callback function
|
|
hr = StuffChkDskInfoPtr(pChkDskInfo);
|
|
if (hr == S_OK)
|
|
{
|
|
WCHAR swzFileSystem[MAX_PATH];
|
|
|
|
// Get the filesystem in use on the device
|
|
if (GetVolumeInformationW(pChkDskInfo->wszDriveName,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
swzFileSystem,
|
|
MAX_PATH))
|
|
{
|
|
// Set the window title to indicate ChkDsk in proress...
|
|
SetDriveWindowTitle(pChkDskInfo->hDlg, pChkDskInfo->wszDriveName, IDS_CHKINPROGRESS);
|
|
|
|
pChkDskInfo->fNoFinalMsg = FALSE;
|
|
|
|
// Should we try data recovery?
|
|
pChkDskInfo->fRecovery = IsDlgButtonChecked(pChkDskInfo->hDlg, IDC_RECOVERY);
|
|
|
|
// Should we fix filesystem errors?
|
|
pChkDskInfo->fFixErrors = IsDlgButtonChecked(pChkDskInfo->hDlg, IDC_FIXERRORS);
|
|
|
|
// just do it!
|
|
DoChkDsk(pChkDskInfo, swzFileSystem);
|
|
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
// Release the TLS index
|
|
UnstuffChkDskInfoPtr();
|
|
}
|
|
|
|
PostMessage(pChkDskInfo->hDlg, (UINT) PWM_CHKDSKDONE, 0, 0);
|
|
ReleaseChkDskInfo(pChkDskInfo);
|
|
|
|
return (DWORD)hr;
|
|
}
|
|
|
|
//
|
|
// Synopsis: DLGPROC for the chkdsk dialog
|
|
//
|
|
// Arguments: [hDlg] -- Typical
|
|
// [wMsg] -- Typical
|
|
// [wParam] -- Typical
|
|
// [lParam] -- For WM_INIT, carries the CHKDSKINFO structure
|
|
// pointer passed to DialogBoxParam() when the
|
|
// dialog was created.
|
|
//
|
|
BOOL_PTR CALLBACK ChkDskDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int iID = GET_WM_COMMAND_ID(wParam, lParam);
|
|
|
|
// Grab our previously cached pointer to the CHKDSKINFO struct (see WM_INITDIALOG)
|
|
CHKDSKINFO *pChkDskInfo = (CHKDSKINFO *) GetWindowLongPtr(hDlg, DWLP_USER);
|
|
|
|
switch (wMsg)
|
|
{
|
|
// done. Reset the window title and clear the progress meter
|
|
case PWM_CHKDSKDONE:
|
|
{
|
|
// chdsk is done. Reset the window title and clear the progress meter
|
|
SetDriveWindowTitle(pChkDskInfo->hDlg, pChkDskInfo->wszDriveName, IDS_CHKDISK);
|
|
|
|
SendMessage(GetDlgItem(pChkDskInfo->hDlg,
|
|
IDC_CHKDSKPROGRESS),
|
|
PBM_SETPOS,
|
|
0, // Reset Percent Complete
|
|
0);
|
|
EnableChkDskControls(pChkDskInfo);
|
|
|
|
if (pChkDskInfo->fCancelled)
|
|
{
|
|
ShellMessageBox(HINST_THISDLL,
|
|
pChkDskInfo->hDlg,
|
|
MAKEINTRESOURCE(IDS_CHKDSKCANCELLED),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK);
|
|
}
|
|
|
|
if (pChkDskInfo->hThread)
|
|
{
|
|
CloseHandle(pChkDskInfo->hThread);
|
|
pChkDskInfo->hThread = NULL;
|
|
}
|
|
|
|
EndDialog(hDlg, 0);
|
|
}
|
|
break;
|
|
|
|
case WM_INITDIALOG:
|
|
// Initialize the dialog and cache the CHKDSKINFO structure's pointer
|
|
// as our dialog's DWLP_USER data
|
|
pChkDskInfo = (CHKDSKINFO *) lParam;
|
|
pChkDskInfo->hDlg = hDlg;
|
|
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
|
|
|
|
// Set the dialog title to indicate which drive we are dealing with
|
|
SetDriveWindowTitle(pChkDskInfo->hDlg, pChkDskInfo->wszDriveName, IDS_CHKDISK);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
if (pChkDskInfo && pChkDskInfo->hDlg)
|
|
{
|
|
pChkDskInfo->hDlg = NULL;
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
switch (iID)
|
|
{
|
|
case IDC_FIXERRORS:
|
|
pChkDskInfo->fFixErrors = Button_GetCheck((HWND)lParam);
|
|
break;
|
|
|
|
case IDC_RECOVERY:
|
|
pChkDskInfo->fRecovery = Button_GetCheck((HWND)lParam);
|
|
break;
|
|
|
|
case IDOK:
|
|
{
|
|
// Get user verification for chkdsk, break out on CANCEL
|
|
DisableChkDskControls(pChkDskInfo);
|
|
|
|
pChkDskInfo->fShouldCancel = FALSE;
|
|
pChkDskInfo->fCancelled = FALSE;
|
|
|
|
AddRefChkDskInfo(pChkDskInfo);
|
|
pChkDskInfo->hThread = CreateThread(NULL,
|
|
0,
|
|
BeginChkDsk,
|
|
(void *)pChkDskInfo,
|
|
0,
|
|
NULL);
|
|
if (!pChkDskInfo->hThread)
|
|
{
|
|
// ISSUE: we should probably do something here...
|
|
ReleaseChkDskInfo(pChkDskInfo);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
{
|
|
// If the chdsk thread is running, wait for it. If not,
|
|
// exit the dialog
|
|
pChkDskInfo->fCancelled = TRUE;
|
|
pChkDskInfo->fShouldCancel = TRUE;
|
|
|
|
if (pChkDskInfo->hThread)
|
|
{
|
|
DWORD dwWait;
|
|
|
|
do
|
|
{
|
|
dwWait = WaitForSingleObject(pChkDskInfo->hThread, 10000);
|
|
}
|
|
while ((WAIT_TIMEOUT == dwWait) &&
|
|
(IDRETRY == ShellMessageBox(HINST_THISDLL,
|
|
hDlg,
|
|
MAKEINTRESOURCE(IDS_CANTCANCELCHKDSK),
|
|
NULL,
|
|
MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_RETRYCANCEL)));
|
|
|
|
// If the chkdsk doesn't admit to having been killed, it didn't
|
|
// give up peacefully. Just abandon it and let it clean up
|
|
// when it finally gets around to it, at which point we will
|
|
// enable the controls to let the user take another stab.
|
|
//
|
|
// Careful: The chkdsk may have cleaned up while the dialog box
|
|
// was up, so revalidate.
|
|
if (pChkDskInfo->hThread)
|
|
{
|
|
CloseHandle(pChkDskInfo->hThread);
|
|
pChkDskInfo->hThread = NULL;
|
|
pChkDskInfo->fCancelled = TRUE;
|
|
EnableChkDskControls(pChkDskInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EndDialog(hDlg, IDCANCEL);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_HELP:
|
|
WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPSTR)ChkaIds);
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(LPSTR)ChkaIds);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#define GET_INTRESOURCE(r) (LOWORD((UINT_PTR)(r)))
|
|
|
|
static HDPA hpdaChkdskActive = NULL;
|
|
|
|
//
|
|
// Synopsis: Same as SHChkDskDrive but takes a path rather than a drive int ID
|
|
// Call this fct for both path and drive int ID to be protected
|
|
// against chkdsk'ing the same drive simultaneously
|
|
//
|
|
// Arguments: [hwnd] -- Parent window (Must NOT be NULL)
|
|
// [pszDrive] -- INTRESOURCE: string if mounted on folder, drive
|
|
// number if mounted on drive letter (0 based)
|
|
//
|
|
STDAPI_(DWORD) SHChkDskDriveEx(HWND hwnd, LPWSTR pszDrive)
|
|
{
|
|
HRESULT hr = SHFMT_ERROR;
|
|
WCHAR szUniqueID[50]; // 50: size of VolumeGUID, which can fit "A:\\" too
|
|
|
|
CHKDSKINFO *pChkDskInfo = (CHKDSKINFO *)LocalAlloc(LPTR, sizeof(*pChkDskInfo));
|
|
|
|
if (pChkDskInfo)
|
|
{
|
|
hr = S_OK;
|
|
|
|
// We use a last percentage-complete value of 101, to guarantee that the
|
|
// next one received will be less, indicating next (first) phase
|
|
pChkDskInfo->lastpercent = 101;
|
|
pChkDskInfo->cRef = 1;
|
|
|
|
hr = StringCchCopy(pChkDskInfo->wszDriveName, ARRAYSIZE(pChkDskInfo->wszDriveName), pszDrive);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (PathAddBackslash(pChkDskInfo->wszDriveName))
|
|
{
|
|
// Prevent multiple chkdsks of the same drive
|
|
GetVolumeNameForVolumeMountPoint(pChkDskInfo->wszDriveName, szUniqueID, ARRAYSIZE(szUniqueID));
|
|
|
|
// scoping ENTERCRITICAL's var definitions to make it cooperate with other ENTERCRITICAL
|
|
{
|
|
ENTERCRITICAL;
|
|
if (!hpdaChkdskActive)
|
|
{
|
|
hpdaChkdskActive = DPA_Create(1);
|
|
}
|
|
|
|
if (hpdaChkdskActive)
|
|
{
|
|
int i, n = DPA_GetPtrCount(hpdaChkdskActive);
|
|
|
|
// Go through the DPA of currently chkdsk'ed volumes, and check if we're already
|
|
// processing this volume
|
|
for (i = 0; i < n; ++i)
|
|
{
|
|
LPWSTR pszUniqueID = (LPWSTR)DPA_GetPtr(hpdaChkdskActive, i);
|
|
|
|
if (pszUniqueID)
|
|
{
|
|
if (!lstrcmpi(szUniqueID, pszUniqueID))
|
|
{
|
|
// we're already chkdsk'ing this drive
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Looks like we're currently not chkdsk'ing this volume, add it to the DPA of currently
|
|
// chkdsk'ed volumes
|
|
if (S_OK == hr)
|
|
{
|
|
LPWSTR pszUniqueID = StrDup(szUniqueID);
|
|
if (pszUniqueID)
|
|
{
|
|
if (-1 == DPA_AppendPtr(hpdaChkdskActive, pszUniqueID))
|
|
{
|
|
LocalFree((HLOCAL)pszUniqueID);
|
|
|
|
// if can't allocate room to store a pointer, pretty useless to go on
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
|
|
}
|
|
}
|
|
|
|
// Load the FMIFS DLL and open the ChkDsk dialog
|
|
if (S_OK == hr)
|
|
{
|
|
if (S_OK == LoadFMIFS(&(pChkDskInfo->fmifs)))
|
|
{
|
|
INT_PTR ret;
|
|
INITCOMMONCONTROLSEX icc = {sizeof(icc), ICC_PROGRESS_CLASS};
|
|
InitCommonControlsEx(&icc);
|
|
|
|
ret = DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_CHKDSK),
|
|
hwnd, ChkDskDlgProc, (LPARAM) pChkDskInfo);
|
|
if (-1 == ret)
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
if (IDCANCEL == ret)
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0 && "Can't load FMIFS.DLL");
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
// We're finish for this volume, remove from the list of currently processed volumes
|
|
ENTERCRITICAL;
|
|
if (hpdaChkdskActive)
|
|
{
|
|
int i, n = DPA_GetPtrCount(hpdaChkdskActive);
|
|
|
|
for (i = 0; i < n; ++i)
|
|
{
|
|
LPWSTR pszUniqueID = (LPWSTR)DPA_GetPtr(hpdaChkdskActive, i);
|
|
if (pszUniqueID)
|
|
{
|
|
if (!lstrcmpi(szUniqueID, pszUniqueID))
|
|
{
|
|
LocalFree((HLOCAL)pszUniqueID);
|
|
|
|
DPA_DeletePtr(hpdaChkdskActive, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
// If the DPA is empty delete it
|
|
ENTERCRITICAL;
|
|
if (hpdaChkdskActive && !DPA_GetPtrCount(hpdaChkdskActive))
|
|
{
|
|
DPA_Destroy(hpdaChkdskActive);
|
|
hpdaChkdskActive = NULL;
|
|
}
|
|
LEAVECRITICAL;
|
|
|
|
ReleaseChkDskInfo(pChkDskInfo);
|
|
}
|
|
|
|
return (DWORD) hr;
|
|
}
|
|
|
|
//****************************************************************************
|
|
//
|
|
// Special hook for Win9x app compat
|
|
//
|
|
// Some Win9x apps like to WinExec("DEFRAG") or WinExec("SCANDSKW")
|
|
// even though those apps don't exist on Windows NT. When such apps
|
|
// are found, we can shim them to come here instead.
|
|
BOOL ScanDskW_OnInitDialog(HWND hdlg)
|
|
{
|
|
HICON hico;
|
|
HWND hwndList;
|
|
SHFILEINFO sfi;
|
|
HIMAGELIST himlSys;
|
|
RECT rc;
|
|
LVCOLUMN lvc;
|
|
int iDrive;
|
|
TCHAR szDrive[4];
|
|
|
|
hico = (HICON)SendDlgItemMessage(hdlg, IDC_SCANDSKICON, STM_GETICON, 0, 0);
|
|
SendMessage(hdlg, WM_SETICON, ICON_BIG, (LPARAM)hico);
|
|
SendMessage(hdlg, WM_SETICON, ICON_SMALL, (LPARAM)hico);
|
|
|
|
hwndList = GetDlgItem(hdlg, IDC_SCANDSKLV);
|
|
|
|
if (Shell_GetImageLists(NULL, &himlSys))
|
|
{
|
|
ListView_SetImageList(hwndList, himlSys, LVSIL_SMALL);
|
|
}
|
|
|
|
GetClientRect(hwndList, &rc);
|
|
|
|
lvc.mask = LVCF_WIDTH;
|
|
lvc.cx = rc.right;
|
|
lvc.iSubItem = 0;
|
|
ListView_InsertColumn(hwndList, 0, &lvc);
|
|
|
|
for (iDrive = 0; iDrive < 26; iDrive++)
|
|
{
|
|
PathBuildRoot(szDrive, iDrive);
|
|
switch (GetDriveType(szDrive))
|
|
{
|
|
case DRIVE_UNKNOWN:
|
|
case DRIVE_NO_ROOT_DIR:
|
|
case DRIVE_REMOTE:
|
|
case DRIVE_CDROM:
|
|
break; // Can't scan these drives
|
|
|
|
default:
|
|
if (SHGetFileInfo(szDrive, FILE_ATTRIBUTE_DIRECTORY, &sfi, sizeof(sfi),
|
|
SHGFI_USEFILEATTRIBUTES |
|
|
SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_DISPLAYNAME))
|
|
{
|
|
LVITEM lvi;
|
|
lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
|
|
lvi.iItem = MAXLONG;
|
|
lvi.iSubItem = 0;
|
|
lvi.pszText = sfi.szDisplayName;
|
|
lvi.iImage = sfi.iIcon;
|
|
lvi.lParam = iDrive;
|
|
ListView_InsertItem(hwndList, &lvi);
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void ScanDskW_OnOk(HWND hdlg)
|
|
{
|
|
HWND hwndList = GetDlgItem(hdlg, IDC_SCANDSKLV);
|
|
|
|
LVITEM lvi;
|
|
lvi.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
|
|
if (lvi.iItem >= 0)
|
|
{
|
|
lvi.iSubItem = 0;
|
|
lvi.mask = LVIF_PARAM;
|
|
if (ListView_GetItem(hwndList, &lvi))
|
|
{
|
|
TCHAR szDrive[4];
|
|
PathBuildRoot(szDrive, (int)lvi.lParam);
|
|
SHChkDskDriveEx(hdlg, szDrive);
|
|
}
|
|
}
|
|
}
|
|
|
|
INT_PTR CALLBACK
|
|
ScanDskW_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (wm)
|
|
{
|
|
case WM_INITDIALOG:
|
|
return ScanDskW_OnInitDialog(hdlg);
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDOK:
|
|
ScanDskW_OnOk(hdlg);
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hdlg, 0);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
LPNMHDR pnm = (LPNMHDR)lParam;
|
|
if (pnm->code == LVN_ITEMCHANGED)
|
|
{
|
|
EnableWindow(GetDlgItem(hdlg, IDOK), ListView_GetSelectedCount(GetDlgItem(hdlg, IDC_SCANDSKLV)));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Right now, we have only one app compat shim entry point (SCANDSKW)
|
|
// In the future we can add others to the command line.
|
|
|
|
STDAPI_(void) AppCompat_RunDLLW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
|
|
{
|
|
TCHAR szCmd[MAX_PATH];
|
|
LPTSTR pszArgs;
|
|
|
|
HRESULT hr = StringCchCopy(szCmd, ARRAYSIZE(szCmd), lpwszCmdLine);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pszArgs = PathGetArgs(szCmd);
|
|
PathRemoveArgs(szCmd);
|
|
|
|
|
|
if (lstrcmpi(szCmd, L"SCANDSKW") == 0) {
|
|
DialogBoxParam(g_hinst, MAKEINTRESOURCE(IDD_SCANDSKW), NULL,
|
|
ScanDskW_DlgProc, (LPARAM)pszArgs);
|
|
}
|
|
}
|
|
}
|