#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) { 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)) { lstrcat(wszWinTitle, sfi.szDisplayName); } 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, sizeof(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; iMessageID = 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) { 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)); wsprintf(wszTmp, wszFormat, ++(pChkDskInfo->currentphase)); 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; lstrcpyn(pChkDskInfo->wszDriveName, pszDrive, ARRAYSIZE(pChkDskInfo->wszDriveName)); 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; } // 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; lstrcpyn(szCmd, lpwszCmdLine, ARRAYSIZE(szCmd)); pszArgs = PathGetArgs(szCmd); PathRemoveArgs(szCmd); if (lstrcmpi(szCmd, L"SCANDSKW") == 0) { DialogBoxParam(g_hinst, MAKEINTRESOURCE(IDD_SCANDSKW), NULL, ScanDskW_DlgProc, (LPARAM)pszArgs); } }