Source code of Windows XP (NT5)
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.

2412 lines
82 KiB

  1. #include "shellprv.h"
  2. #include "ids.h"
  3. #include "mtpt.h"
  4. #include "hwcmmn.h"
  5. #pragma hdrstop
  6. #include "apithk.h"
  7. const static DWORD FmtaIds[] =
  8. {
  9. IDOK, IDH_FORMATDLG_START,
  10. IDCANCEL, IDH_CANCEL,
  11. IDC_CAPCOMBO, IDH_FORMATDLG_CAPACITY,
  12. IDC_FSCOMBO, IDH_FORMATDLG_FILESYS,
  13. IDC_ASCOMBO, IDH_FORMATDLG_ALLOCSIZE,
  14. IDC_VLABEL, IDH_FORMATDLG_LABEL,
  15. IDC_GROUPBOX_1, IDH_COMM_GROUPBOX,
  16. IDC_QFCHECK, IDH_FORMATDLG_QUICKFULL,
  17. IDC_ECCHECK, IDH_FORMATDLG_COMPRESS,
  18. IDC_FMTPROGRESS, IDH_FORMATDLG_PROGRESS,
  19. 0,0
  20. };
  21. const static DWORD ChkaIds[] =
  22. {
  23. IDOK, IDH_CHKDSKDLG_START,
  24. IDCANCEL, IDH_CHKDSKDLG_CANCEL,
  25. IDC_GROUPBOX_1, IDH_COMM_GROUPBOX,
  26. IDC_FIXERRORS, IDH_CHKDSKDLG_FIXERRORS,
  27. IDC_RECOVERY, IDH_CHKDSKDLG_SCAN,
  28. IDC_CHKDSKPROGRESS, IDH_CHKDSKDLG_PROGRESS,
  29. IDC_PHASE, -1,
  30. 0,0
  31. };
  32. // The following structure encapsulates our calling into the FMIFS.DLL
  33. typedef struct
  34. {
  35. HINSTANCE hFMIFS_DLL;
  36. PFMIFS_FORMATEX_ROUTINE FormatEx;
  37. PFMIFS_QSUPMEDIA_ROUTINE QuerySupportedMedia;
  38. PFMIFS_ENABLECOMP_ROUTINE EnableVolumeCompression;
  39. PFMIFS_CHKDSKEX_ROUTINE ChkDskEx;
  40. PFMIFS_QUERY_DEVICE_INFO_ROUTINE QueryDeviceInformation;
  41. } FMIFS;
  42. typedef
  43. HRESULT
  44. (*PDISKCOPY_MAKEBOOTDISK_ROUTINE)(
  45. IN HINSTANCE hInstance,
  46. IN UINT iDrive,
  47. IN BOOL* pfCancelled,
  48. IN FMIFS_CALLBACK pCallback
  49. );
  50. // The following structure encapsulates our calling into the DISKCOPY.DLL
  51. typedef struct
  52. {
  53. HINSTANCE hDISKCOPY_DLL;
  54. PDISKCOPY_MAKEBOOTDISK_ROUTINE MakeBootDisk;
  55. } DISKCOPY;
  56. // This structure described the current formatting session
  57. typedef struct
  58. {
  59. LONG cRef; // reference count on this structure
  60. UINT drive; // 0-based index of drive to format
  61. UINT fmtID; // Last format ID
  62. UINT options; // options passed to us via the API
  63. FMIFS fmifs; // above
  64. DISKCOPY diskcopy; // above
  65. HWND hDlg; // handle to the format dialog
  66. BOOL fIsFloppy; // TRUE -> its a floppy
  67. BOOL fIs35HDFloppy; // TRUE -> its a standard 3.5" High Density floppy
  68. BOOL fIsMemoryStick; // TRUE -> its a memory stick (special formatting only)
  69. BOOL fIsNTFSBlocked; // TRUE -> its a NTFS not-supported device
  70. BOOL fEnableComp; // Last "Enable Comp" choice from user
  71. BOOL fCancelled; // User cancelled the last format
  72. BOOL fShouldCancel; // User has clicked cancel; pending abort
  73. BOOL fWasFAT; // Was it FAT originally?
  74. BOOL fFinishedOK; // Did format complete sucessfully?
  75. BOOL fErrorAlready; // Did we put up an error dialog already?
  76. BOOL fDisabled; // Is rgfControlEnabled[] valid?
  77. DWORD dwClusterSize; // Orig NT cluster size, or last choice
  78. WCHAR wszVolName[MAX_PATH]; // Volume Label
  79. WCHAR wszDriveName[4]; // Root path to drive (eg: A:\)
  80. HANDLE hThread; // Handle of format thread
  81. // Array of media types supported by the device
  82. // for NT5, we have an expanded list that includes japanese types.
  83. FMIFS_MEDIA_TYPE rgMedia[IDS_FMT_MEDIA_J22-IDS_FMT_MEDIA_J0];
  84. // Used to cache the enabled/disabled state of the dialog controls
  85. BOOL rgfControlEnabled[DLG_FORMATDISK_NUMCONTROLS];
  86. // should we create a boot disk rather than a traditional format
  87. BOOL fMakeBootDisk;
  88. } FORMATINFO;
  89. //
  90. // An enumeration to make the filesystem combo-box code more readble
  91. //
  92. typedef enum tagFILESYSENUM
  93. {
  94. e_FAT = 0,
  95. e_NTFS,
  96. e_FAT32
  97. } FILESYSENUM;
  98. #define FS_STR_NTFS TEXT("NTFS")
  99. #define FS_STR_FAT32 TEXT("FAT32")
  100. #define FS_STR_FAT TEXT("FAT")
  101. //
  102. // Private WM_USER messages we will use. For some unknown reason, USER sends
  103. // us a WM_USER during initialization, so I start my private messages at
  104. // WM_USER + 0x0100
  105. //
  106. typedef enum tagPRIVMSGS
  107. {
  108. PWM_FORMATDONE = WM_USER + 0x0100,
  109. PWM_CHKDSKDONE
  110. } PRIVMSGS;
  111. //
  112. // Synopsis: Loads FMIFS.DLL and sets up the function entry points for
  113. // the member functions we are interested in.
  114. //
  115. HRESULT LoadFMIFS(FMIFS *pFMIFS)
  116. {
  117. HRESULT hr = S_OK;
  118. //
  119. // Load the FMIFS DLL and query for the entry points we need
  120. //
  121. // SECURITY: what non-relative path do we use that will work on ia64 too?
  122. if (NULL == (pFMIFS->hFMIFS_DLL = LoadLibrary(TEXT("FMIFS.DLL"))))
  123. {
  124. hr = HRESULT_FROM_WIN32(GetLastError());
  125. }
  126. else if (NULL == (pFMIFS->FormatEx = (PFMIFS_FORMATEX_ROUTINE)
  127. GetProcAddress(pFMIFS->hFMIFS_DLL, "FormatEx")))
  128. {
  129. hr = HRESULT_FROM_WIN32(GetLastError());
  130. }
  131. else if (NULL == (pFMIFS->QuerySupportedMedia = (PFMIFS_QSUPMEDIA_ROUTINE)
  132. GetProcAddress(pFMIFS->hFMIFS_DLL, "QuerySupportedMedia")))
  133. {
  134. hr = HRESULT_FROM_WIN32(GetLastError());
  135. }
  136. else if (NULL == (pFMIFS->EnableVolumeCompression = (PFMIFS_ENABLECOMP_ROUTINE)
  137. GetProcAddress(pFMIFS->hFMIFS_DLL, "EnableVolumeCompression")))
  138. {
  139. hr = HRESULT_FROM_WIN32(GetLastError());
  140. }
  141. else if (NULL == (pFMIFS->ChkDskEx = (PFMIFS_CHKDSKEX_ROUTINE)
  142. GetProcAddress(pFMIFS->hFMIFS_DLL, "ChkdskEx")))
  143. {
  144. hr = HRESULT_FROM_WIN32(GetLastError());
  145. }
  146. else if (NULL == (pFMIFS->QueryDeviceInformation = (PFMIFS_QUERY_DEVICE_INFO_ROUTINE)
  147. GetProcAddress(pFMIFS->hFMIFS_DLL, "QueryDeviceInformation")))
  148. {
  149. hr = HRESULT_FROM_WIN32(GetLastError());
  150. }
  151. //
  152. // If anything failed, and we've got the DLL loaded, release the DLL
  153. //
  154. if (hr != S_OK && pFMIFS->hFMIFS_DLL)
  155. {
  156. FreeLibrary(pFMIFS->hFMIFS_DLL);
  157. }
  158. return hr;
  159. }
  160. //
  161. // Synopsis: Loads DISKCOPY.DLL and sets up the function entry points for
  162. // the member functions we are interested in.
  163. //
  164. HRESULT LoadDISKCOPY(DISKCOPY *pDISKCOPY)
  165. {
  166. HRESULT hr = S_OK;
  167. //
  168. // Load the DISKCOPY DLL and query for the entry points we need
  169. //
  170. // SECURITY: what non-relative path do we use that will work on ia64 too?
  171. if (NULL == (pDISKCOPY->hDISKCOPY_DLL = LoadLibrary(TEXT("DISKCOPY.DLL"))))
  172. {
  173. hr = HRESULT_FROM_WIN32(GetLastError());
  174. }
  175. else if (NULL == (pDISKCOPY->MakeBootDisk = (PDISKCOPY_MAKEBOOTDISK_ROUTINE)
  176. GetProcAddress(pDISKCOPY->hDISKCOPY_DLL, MAKEINTRESOURCEA(1)))) //MakeBootDisk is at ordinal 1 in diskcopy.dll
  177. {
  178. hr = HRESULT_FROM_WIN32(GetLastError());
  179. }
  180. //
  181. // If anything failed, and we've got the DLL loaded, release the DLL
  182. //
  183. if (hr != S_OK && pDISKCOPY->hDISKCOPY_DLL)
  184. {
  185. FreeLibrary(pDISKCOPY->hDISKCOPY_DLL);
  186. }
  187. return hr;
  188. }
  189. void AddRefFormatInfo(FORMATINFO *pFormatInfo)
  190. {
  191. InterlockedIncrement(&pFormatInfo->cRef);
  192. }
  193. void ReleaseFormatInfo(FORMATINFO *pFormatInfo)
  194. {
  195. if (InterlockedDecrement(&pFormatInfo->cRef) == 0)
  196. {
  197. if (pFormatInfo->fmifs.hFMIFS_DLL)
  198. {
  199. FreeLibrary(pFormatInfo->fmifs.hFMIFS_DLL);
  200. }
  201. if (pFormatInfo->diskcopy.hDISKCOPY_DLL)
  202. {
  203. FreeLibrary(pFormatInfo->diskcopy.hDISKCOPY_DLL);
  204. }
  205. if (pFormatInfo->hThread)
  206. {
  207. CloseHandle(pFormatInfo->hThread);
  208. }
  209. LocalFree(pFormatInfo);
  210. }
  211. }
  212. //
  213. // Thread-Local Storage index for our FORMATINFO structure pointer
  214. //
  215. static DWORD g_iTLSFormatInfo = 0;
  216. static LONG g_cTLSFormatInfo = 0; // Usage count
  217. // Synopsis: Allocates a thread-local index slot for this thread's
  218. // FORMATINFO pointer, if the index doesn't already exist.
  219. // In any event, stores the FORMATINFO pointer in the slot
  220. // and increments the index's usage count.
  221. //
  222. // Arguments: [pFormatInfo] -- The pointer to store
  223. //
  224. // Returns: HRESULT
  225. //
  226. HRESULT StuffFormatInfoPtr(FORMATINFO *pFormatInfo)
  227. {
  228. HRESULT hr = S_OK;
  229. // Allocate an index slot for our thread-local FORMATINFO pointer, if one
  230. // doesn't already exist, then stuff our FORMATINFO ptr at that index.
  231. ENTERCRITICAL;
  232. if (0 == g_iTLSFormatInfo)
  233. {
  234. if (0xFFFFFFFF == (g_iTLSFormatInfo = TlsAlloc()))
  235. {
  236. hr = HRESULT_FROM_WIN32(GetLastError());
  237. }
  238. g_cTLSFormatInfo = 0;
  239. }
  240. if (S_OK == hr)
  241. {
  242. if (TlsSetValue(g_iTLSFormatInfo, (void *) pFormatInfo))
  243. {
  244. g_cTLSFormatInfo++;
  245. }
  246. else
  247. {
  248. hr = HRESULT_FROM_WIN32(GetLastError());
  249. }
  250. }
  251. LEAVECRITICAL;
  252. return hr;
  253. }
  254. // Synopsis: Decrements the usage count on our thread-local storage
  255. // index, and if it goes to zero the index is free'd
  256. //
  257. // Arguments: [none]
  258. //
  259. // Returns: none
  260. //
  261. void UnstuffFormatInfoPtr()
  262. {
  263. ENTERCRITICAL;
  264. if (0 == --g_cTLSFormatInfo)
  265. {
  266. TlsFree(g_iTLSFormatInfo);
  267. g_iTLSFormatInfo = 0;
  268. }
  269. LEAVECRITICAL;
  270. }
  271. // Synopsis: Retrieves this threads FORMATINFO ptr by grabbing the
  272. // thread-local value previously stuff'd
  273. //
  274. // Arguments: [none]
  275. //
  276. // Returns: The pointer, of course
  277. //
  278. FORMATINFO *GetFormatInfoPtr()
  279. {
  280. return (FORMATINFO*)TlsGetValue(g_iTLSFormatInfo);
  281. }
  282. // Synopsis: Ghosts all controls except "Cancel", saving their
  283. // previous state in the FORMATINFO structure
  284. //
  285. // Arguments: [pFormatInfo] -- Describes a format dialog session
  286. //
  287. // Notes: Also changes "Close" button text to read "Cancel"
  288. //
  289. void DisableControls(FORMATINFO *pFormatInfo)
  290. {
  291. WCHAR wszCancel[64];
  292. // Do this only if we haven't disabled the controls yet, otherwise
  293. // we double-disable and our rgfControlEnabled[] array gets corrupted.
  294. if (!pFormatInfo->fDisabled)
  295. {
  296. int i;
  297. pFormatInfo->fDisabled = TRUE;
  298. for (i = 0; i < DLG_FORMATDISK_NUMCONTROLS; i++)
  299. {
  300. HWND hControl = GetDlgItem(pFormatInfo->hDlg, i + DLG_FORMATDISK_FIRSTCONTROL);
  301. pFormatInfo->rgfControlEnabled[i] = !EnableWindow(hControl, FALSE);
  302. }
  303. }
  304. EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDOK), FALSE);
  305. LoadString(HINST_THISDLL, IDS_FMT_CANCEL, wszCancel, ARRAYSIZE(wszCancel));
  306. SetWindowText(GetDlgItem(pFormatInfo->hDlg, IDCANCEL), wszCancel);
  307. }
  308. // Synopsis: Restores controls to the enabled/disabled state they were
  309. // before a previous call to DisableControls().
  310. //
  311. // Arguments: [pFormatInfo] -- Decribes a format dialog session
  312. // [fReady] - If TRUE, then enable everything
  313. // If FALSE, then enable combo boxes but leave
  314. // buttons in limbo because there is still a format
  315. // pending
  316. //
  317. // Notes: Also changes "Cancel" button to say "Close"
  318. // Also sets focus to Cancel button instead of Start button
  319. //
  320. //--------------------------------------------------------------------------
  321. void EnableControls(FORMATINFO *pFormatInfo, BOOL fReady)
  322. {
  323. WCHAR wszClose[64];
  324. int i;
  325. HWND hwnd;
  326. // Do this only if we have valid info in rgfControlEnabled[].
  327. // This catches the case where we give up on a format because it is
  328. // unstuck, and then finally it unsticks itself and tells us,
  329. // so we go and re-enable a second time.
  330. if (pFormatInfo->fDisabled)
  331. {
  332. pFormatInfo->fDisabled = FALSE;
  333. for (i = 0; i < DLG_FORMATDISK_NUMCONTROLS; i++)
  334. {
  335. HWND hControl = GetDlgItem(pFormatInfo->hDlg, i + DLG_FORMATDISK_FIRSTCONTROL);
  336. EnableWindow(hControl, pFormatInfo->rgfControlEnabled[i]);
  337. }
  338. }
  339. hwnd = GetDlgItem(pFormatInfo->hDlg, IDOK);
  340. EnableWindow(hwnd, fReady);
  341. SendMessage(hwnd, BM_SETSTYLE, BS_PUSHBUTTON, MAKELPARAM(TRUE,0));
  342. LoadString(HINST_THISDLL, IDS_FMT_CLOSE, wszClose, ARRAYSIZE(wszClose));
  343. hwnd = GetDlgItem(pFormatInfo->hDlg, IDCANCEL);
  344. SetWindowText(hwnd, wszClose);
  345. SendMessage(hwnd, BM_SETSTYLE, BS_DEFPUSHBUTTON, MAKELPARAM(TRUE,0));
  346. SendMessage(pFormatInfo->hDlg, DM_SETDEFID, IDCANCEL, 0);
  347. // Shove focus only if it's on the OK button. Otherwise we end up
  348. // yanking focus away from a user who is busy dorking with the dialog,
  349. // or -- worse -- dorking with a completely unrelated dialog!
  350. if (GetFocus() == GetDlgItem(pFormatInfo->hDlg, IDOK))
  351. SetFocus(hwnd);
  352. }
  353. // Sets the dialog's title to "Format Floppy (A:)" or
  354. // "Formatting Floppy (A:)"
  355. void SetDriveWindowTitle(HWND hdlg, LPCWSTR pszDrive, UINT ids)
  356. {
  357. SHFILEINFO sfi;
  358. WCHAR wszWinTitle[MAX_PATH]; // Format dialog window title
  359. LoadString(HINST_THISDLL, ids, wszWinTitle, ARRAYSIZE(wszWinTitle));
  360. if (SHGetFileInfo(pszDrive, FILE_ATTRIBUTE_DIRECTORY, &sfi, sizeof(sfi),
  361. SHGFI_USEFILEATTRIBUTES | SHGFI_DISPLAYNAME))
  362. {
  363. lstrcat(wszWinTitle, sfi.szDisplayName);
  364. }
  365. SetWindowText(hdlg, wszWinTitle);
  366. }
  367. //
  368. // Synopsis: Called when a user picks a filesystem in the dialog, this
  369. // sets the states of the other relevant controls, such as
  370. // Enable Compression, Allocation Size, etc.
  371. //
  372. // Arguments: [fsenum] -- One of e_FAT, e_NTFS, or e_FAT32
  373. // [pFormatInfo] -- Current format dialog session
  374. //
  375. void FileSysChange(FILESYSENUM fsenum, FORMATINFO *pFormatInfo)
  376. {
  377. WCHAR wszTmp[MAX_PATH];
  378. switch (fsenum)
  379. {
  380. case e_FAT:
  381. case e_FAT32:
  382. {
  383. // un-check & disable the "Enable Compression" checkbox
  384. CheckDlgButton(pFormatInfo->hDlg, IDC_ECCHECK, FALSE);
  385. EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_ECCHECK), FALSE);
  386. SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_RESETCONTENT, 0, 0);
  387. LoadString(HINST_THISDLL, IDS_FMT_ALLOC0, wszTmp, ARRAYSIZE(wszTmp));
  388. SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_ADDSTRING, 0, (LPARAM)wszTmp);
  389. SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_SETCURSEL, 0, 0);
  390. }
  391. break;
  392. case e_NTFS:
  393. {
  394. int i;
  395. // un-check & disable the "Enable Compression" checkbox
  396. EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_ECCHECK), TRUE);
  397. CheckDlgButton(pFormatInfo->hDlg, IDC_ECCHECK, pFormatInfo->fEnableComp);
  398. // Set up the NTFS Allocation choices, and select the current choice
  399. SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_RESETCONTENT, 0, 0);
  400. for (i = IDS_FMT_ALLOC0; i <= IDS_FMT_ALLOC4; i++)
  401. {
  402. LoadString(HINST_THISDLL, i, wszTmp, ARRAYSIZE(wszTmp));
  403. SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_ADDSTRING, 0, (LPARAM)wszTmp);
  404. }
  405. switch (pFormatInfo->dwClusterSize)
  406. {
  407. case 512:
  408. SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_SETCURSEL, 1, 0);
  409. break;
  410. case 1024:
  411. SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_SETCURSEL, 2, 0);
  412. break;
  413. case 2048:
  414. SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_SETCURSEL, 3, 0);
  415. break;
  416. case 4096:
  417. SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_SETCURSEL, 4, 0);
  418. break;
  419. default:
  420. SendDlgItemMessage(pFormatInfo->hDlg, IDC_ASCOMBO, CB_SETCURSEL, 0, 0);
  421. break;
  422. }
  423. }
  424. break;
  425. }
  426. }
  427. //
  428. // Is this drive a GPT drive?
  429. // GPT drive: Guid-Partition Table - a replacement for the Master Boot Record, used on some IA64 machines, can only use NTFS
  430. BOOL IsGPTDrive(int iDrive)
  431. {
  432. BOOL fRetVal = FALSE;
  433. #ifdef _WIN64
  434. HANDLE hDrive;
  435. TCHAR szDrive[] = TEXT("\\\\.\\A:");
  436. ASSERT(iDrive < 26);
  437. szDrive[4] += (TCHAR)iDrive;
  438. hDrive = CreateFile(szDrive, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  439. if (INVALID_HANDLE_VALUE != hDrive)
  440. {
  441. PARTITION_INFORMATION_EX partitionEx;
  442. DWORD cbReturned;
  443. if (DeviceIoControl(hDrive, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, (void*)&partitionEx, sizeof(PARTITION_INFORMATION_EX), &cbReturned, NULL))
  444. {
  445. if (partitionEx.PartitionStyle == PARTITION_STYLE_GPT)
  446. {
  447. fRetVal = TRUE;
  448. }
  449. }
  450. CloseHandle(hDrive);
  451. }
  452. #endif
  453. return fRetVal;
  454. }
  455. BOOL IsDVDRAMMedia(int iDrive)
  456. {
  457. BOOL fRetVal = FALSE;
  458. CMountPoint *pmtpt = CMountPoint::GetMountPoint(iDrive);
  459. if (pmtpt)
  460. {
  461. DWORD dwMediaCap, dwDriveCap;
  462. if (SUCCEEDED(pmtpt->GetCDInfo(&dwDriveCap, &dwMediaCap)))
  463. {
  464. fRetVal = (dwMediaCap & HWDMC_DVDRAM);
  465. }
  466. pmtpt->Release();
  467. }
  468. return fRetVal;
  469. }
  470. #define GIG_INBYTES (1024 * 1024 * 1024)
  471. //
  472. // FAT32 has some limit which prevents the number of clusters from being
  473. // less than 65526. And minimum cluster size is 512 bytes. So minimum FAT32
  474. // volume size is 65526*512.
  475. #define FAT32_MIN ((ULONGLONG)65526*512)
  476. #define FMTAVAIL_MASK_MIN 0x1
  477. #define FMTAVAIL_MASK_MAX 0x2
  478. #define FMTAVAIL_MASK_REQUIRE 0x3
  479. #define FMTAVAIL_MASK_FORBID 0x4
  480. #define FMTAVAIL_TYPE_FLOPPY 0x1
  481. #define FMTAVAIL_TYPE_DVDRAM 0x2
  482. #define FMTAVAIL_TYPE_GPT 0x4
  483. #define FMTAVAIL_TYPE_MEMSTICK 0x8
  484. #define FMTAVAIL_TYPE_NTFS_BLOCKED 0x10
  485. typedef struct _FMTAVAIL
  486. {
  487. DWORD dwfs;
  488. DWORD dwMask;
  489. DWORD dwForbiddenTypes;
  490. ULONGLONG qMinSize;
  491. ULONGLONG qMaxSize;
  492. } FMTAVAIL;
  493. FMTAVAIL rgFmtAvail[] = {
  494. {e_FAT, FMTAVAIL_MASK_MAX | FMTAVAIL_MASK_FORBID, FMTAVAIL_TYPE_DVDRAM | FMTAVAIL_TYPE_GPT, 0, ((ULONGLONG)2 * GIG_INBYTES) },
  495. {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) },
  496. {e_NTFS, FMTAVAIL_MASK_FORBID, FMTAVAIL_TYPE_DVDRAM | FMTAVAIL_TYPE_FLOPPY | FMTAVAIL_TYPE_MEMSTICK | FMTAVAIL_TYPE_NTFS_BLOCKED, 0, 0 }
  497. };
  498. // is a particular disk format available for a drive with given parameters and capacity?
  499. BOOL FormatAvailable (DWORD dwfs, FORMATINFO* pFormatInfo, ULONGLONG* pqwCapacity)
  500. {
  501. BOOL fAvailable = TRUE;
  502. DWORD dwType = 0;
  503. if (pFormatInfo->fIsFloppy)
  504. {
  505. dwType |= FMTAVAIL_TYPE_FLOPPY;
  506. }
  507. if (IsDVDRAMMedia(pFormatInfo->drive))
  508. {
  509. dwType |= FMTAVAIL_TYPE_DVDRAM;
  510. }
  511. if (IsGPTDrive(pFormatInfo->drive))
  512. {
  513. dwType |= FMTAVAIL_TYPE_GPT;
  514. }
  515. if (pFormatInfo->fIsMemoryStick)
  516. {
  517. dwType |= FMTAVAIL_TYPE_MEMSTICK;
  518. }
  519. if (pFormatInfo->fIsNTFSBlocked)
  520. {
  521. dwType |= FMTAVAIL_TYPE_NTFS_BLOCKED;
  522. }
  523. for (int i = 0; i < ARRAYSIZE(rgFmtAvail); i++)
  524. {
  525. // check only entries that match the format we're looking for
  526. if (rgFmtAvail[i].dwfs == dwfs)
  527. {
  528. // if a failure conditions is true, then this format is unavailable
  529. if ((rgFmtAvail[i].dwMask & FMTAVAIL_MASK_FORBID) && (rgFmtAvail[i].dwForbiddenTypes & dwType))
  530. {
  531. fAvailable = FALSE;
  532. break;
  533. }
  534. if ((rgFmtAvail[i].dwMask & FMTAVAIL_MASK_MIN) && (*pqwCapacity < rgFmtAvail[i].qMinSize))
  535. {
  536. fAvailable = FALSE;
  537. break;
  538. }
  539. if ((rgFmtAvail[i].dwMask & FMTAVAIL_MASK_MAX) && (*pqwCapacity > rgFmtAvail[i].qMaxSize))
  540. {
  541. fAvailable = FALSE;
  542. break;
  543. }
  544. }
  545. }
  546. return fAvailable;
  547. }
  548. HRESULT GetPartitionSizeInBytes(int iDrive, ULONGLONG* pqwPartitionSize)
  549. {
  550. HRESULT hr = E_FAIL;
  551. HANDLE hFile;
  552. TCHAR szDrive[] = TEXT("\\\\.\\A:");
  553. *pqwPartitionSize = 0;
  554. ASSERT(iDrive < 26);
  555. szDrive[4] += (TCHAR)iDrive;
  556. hFile = CreateFile(szDrive, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  557. if (INVALID_HANDLE_VALUE != hFile)
  558. {
  559. GET_LENGTH_INFORMATION LengthInfo;
  560. DWORD cbReturned;
  561. if (DeviceIoControl(hFile, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, (void*)&LengthInfo, sizeof(LengthInfo), &cbReturned, NULL) &&
  562. LengthInfo.Length.QuadPart)
  563. {
  564. *pqwPartitionSize = LengthInfo.Length.QuadPart;
  565. hr = S_OK;
  566. }
  567. CloseHandle(hFile);
  568. }
  569. return hr;
  570. }
  571. // this helper function adds a string to a combo box with the associated dword (dwfs) as its itemdata
  572. void _AddFSString(HWND hwndCB, WCHAR* pwsz, DWORD dwfs)
  573. {
  574. int iIndex = ComboBox_AddString(hwndCB, pwsz);
  575. if (iIndex != CB_ERR)
  576. {
  577. ComboBox_SetItemData(hwndCB, iIndex, dwfs);
  578. }
  579. }
  580. // We only support formatting these types of devices
  581. const FMIFS_MEDIA_TYPE rgFmtSupported[] = { FmMediaRemovable, FmMediaFixed,
  582. FmMediaF3_1Pt44_512, FmMediaF3_120M_512, FmMediaF3_200Mb_512};
  583. //
  584. // Synopsis: Initializes the format dialog to a default state. Examines
  585. // the disk/partition to obtain default values.
  586. //
  587. // Arguments: [hDlg] -- Handle to the format dialog
  588. // [pFormatInfo] -- Describes current format session
  589. //
  590. // Returns: HRESULT
  591. //
  592. HRESULT InitializeFormatDlg(FORMATINFO *pFormatInfo)
  593. {
  594. HRESULT hr = S_OK;
  595. ULONG cMedia;
  596. HWND hCapacityCombo;
  597. HWND hFilesystemCombo;
  598. HWND hDlg = pFormatInfo->hDlg;
  599. WCHAR wszBuffer[256];
  600. ULONGLONG qwCapacity = 0;
  601. // Set up some typical default values
  602. pFormatInfo->fEnableComp = FALSE;
  603. pFormatInfo->dwClusterSize = 0;
  604. pFormatInfo->fIsFloppy = TRUE;
  605. pFormatInfo->fIsMemoryStick = FALSE;
  606. pFormatInfo->fIsNTFSBlocked = FALSE;
  607. pFormatInfo->fIs35HDFloppy = TRUE;
  608. pFormatInfo->fWasFAT = TRUE;
  609. pFormatInfo->fFinishedOK = FALSE;
  610. pFormatInfo->fErrorAlready = FALSE;
  611. pFormatInfo->wszVolName[0] = L'\0';
  612. // Initialize the Quick Format checkbox based on option passed to the SHFormatDrive() API
  613. Button_SetCheck(GetDlgItem(hDlg, IDC_QFCHECK), pFormatInfo->options & SHFMT_OPT_FULL);
  614. // Set the dialog title to indicate which drive we are dealing with
  615. PathBuildRootW(pFormatInfo->wszDriveName, pFormatInfo->drive);
  616. SetDriveWindowTitle(pFormatInfo->hDlg, pFormatInfo->wszDriveName, IDS_FMT_FORMAT);
  617. // Query the supported media types for the drive in question
  618. if (!pFormatInfo->fmifs.QuerySupportedMedia(pFormatInfo->wszDriveName,
  619. pFormatInfo->rgMedia,
  620. ARRAYSIZE(pFormatInfo->rgMedia),
  621. &cMedia))
  622. {
  623. hr = HRESULT_FROM_WIN32(GetLastError());
  624. }
  625. // For each of the formats that the drive can handle, add a selection
  626. // to the capcity combobox.
  627. if (S_OK == hr)
  628. {
  629. UINT olderror;
  630. ULONG i;
  631. ULONG j;
  632. hCapacityCombo = GetDlgItem(hDlg, IDC_CAPCOMBO);
  633. hFilesystemCombo = GetDlgItem(hDlg, IDC_FSCOMBO);
  634. ASSERT(hCapacityCombo && hFilesystemCombo);
  635. FMIFS_DEVICE_INFORMATION fmifsdeviceinformation;
  636. BOOL fOk = pFormatInfo->fmifs.QueryDeviceInformation(
  637. pFormatInfo->wszDriveName,
  638. &fmifsdeviceinformation,
  639. sizeof(fmifsdeviceinformation));
  640. if (fOk)
  641. {
  642. if (fmifsdeviceinformation.Flags & FMIFS_SONY_MS)
  643. {
  644. pFormatInfo->fIsMemoryStick = TRUE;
  645. }
  646. if (fmifsdeviceinformation.Flags & FMIFS_NTFS_NOT_SUPPORTED)
  647. {
  648. pFormatInfo->fIsNTFSBlocked = TRUE;
  649. }
  650. }
  651. // Allow only certain media types
  652. j = 0;
  653. for (i = 0; i < cMedia; i++)
  654. {
  655. for (int k = 0; k < ARRAYSIZE(rgFmtSupported); k++)
  656. {
  657. if (pFormatInfo->rgMedia[i] == rgFmtSupported[k])
  658. {
  659. pFormatInfo->rgMedia[j] = pFormatInfo->rgMedia[i];
  660. j++;
  661. break;
  662. }
  663. }
  664. }
  665. cMedia = j;
  666. if (0 == cMedia)
  667. {
  668. hr = ERROR_UNRECOGNIZED_MEDIA;
  669. }
  670. else
  671. {
  672. for (i = 0; i < cMedia; i++)
  673. {
  674. // If we find any non-floppy format, clear the fIsFloppy flag
  675. if (FmMediaFixed == pFormatInfo->rgMedia[i] || FmMediaRemovable == pFormatInfo->rgMedia[i])
  676. {
  677. pFormatInfo->fIsFloppy = FALSE;
  678. }
  679. // if we find any non-3.5" HD floppy format, clear the fIs35HDFloppy flag
  680. if (FmMediaF3_1Pt44_512 != pFormatInfo->rgMedia[i])
  681. {
  682. pFormatInfo->fIs35HDFloppy = FALSE;
  683. }
  684. // For fixed media we query the size, for floppys we present
  685. // a set of options supported by the drive
  686. if (FmMediaFixed == pFormatInfo->rgMedia[i] || (FmMediaRemovable == pFormatInfo->rgMedia[i]))
  687. {
  688. DWORD dwSectorsPerCluster,
  689. dwBytesPerSector,
  690. dwFreeClusters,
  691. dwClusters;
  692. if (SUCCEEDED(GetPartitionSizeInBytes(pFormatInfo->drive, &qwCapacity)))
  693. {
  694. // Add a capacity desciption to the combobox
  695. ShortSizeFormat64(qwCapacity, wszBuffer, ARRAYSIZE(wszBuffer));
  696. }
  697. else
  698. {
  699. // Couldn't get the free space... prob. not fatal
  700. LoadString(HINST_THISDLL, IDS_FMT_CAPUNKNOWN, wszBuffer, sizeof(wszBuffer));
  701. }
  702. ComboBox_AddString(hCapacityCombo, wszBuffer);
  703. if (GetDiskFreeSpace(pFormatInfo->wszDriveName,
  704. &dwSectorsPerCluster,
  705. &dwBytesPerSector,
  706. &dwFreeClusters,
  707. &dwClusters))
  708. {
  709. pFormatInfo->dwClusterSize = dwBytesPerSector * dwSectorsPerCluster;
  710. }
  711. }
  712. else
  713. {
  714. // removable media:
  715. //
  716. // add a capacity desciption to the combo baseed on the sequential list of
  717. // media format descriptors
  718. LoadString(HINST_THISDLL, IDS_FMT_MEDIA0 + pFormatInfo->rgMedia[i], wszBuffer, ARRAYSIZE(wszBuffer));
  719. ComboBox_AddString(hCapacityCombo, wszBuffer);
  720. }
  721. }
  722. // set capacity to default
  723. ComboBox_SetCurSel(hCapacityCombo, 0);
  724. // Add the appropriate filesystem selections to the combobox
  725. // We now prioritize NTFS
  726. if (FormatAvailable(e_NTFS, pFormatInfo, &qwCapacity))
  727. {
  728. _AddFSString(hFilesystemCombo, FS_STR_NTFS, e_NTFS);
  729. }
  730. if (FormatAvailable(e_FAT32, pFormatInfo, &qwCapacity))
  731. {
  732. _AddFSString(hFilesystemCombo, FS_STR_FAT32, e_FAT32);
  733. }
  734. if (FormatAvailable(e_FAT, pFormatInfo, &qwCapacity))
  735. {
  736. _AddFSString(hFilesystemCombo, FS_STR_FAT, e_FAT);
  737. }
  738. // By default, pick the 0-th entry in the _nonsorted_ combobox.
  739. // NOTE: this can be overwritten below
  740. ComboBox_SetCurSel(hFilesystemCombo, 0);
  741. // If we can determine something other than FAT is being used,
  742. // select it as the default in the combobox
  743. olderror = SetErrorMode(SEM_FAILCRITICALERRORS);
  744. if (GetVolumeInformation(pFormatInfo->wszDriveName,
  745. pFormatInfo->wszVolName,
  746. ARRAYSIZE(pFormatInfo->wszVolName),
  747. NULL,
  748. NULL,
  749. NULL,
  750. wszBuffer,
  751. ARRAYSIZE(wszBuffer)))
  752. {
  753. // If we got a current volume label, stuff it in the edit control
  754. if (pFormatInfo->wszVolName[0] != L'\0')
  755. {
  756. SetWindowText(GetDlgItem(pFormatInfo->hDlg, IDC_VLABEL), pFormatInfo->wszVolName);
  757. }
  758. // for non-floppies we default to keeping the FS the same as the current one
  759. if (!pFormatInfo->fIsFloppy)
  760. {
  761. if (0 == lstrcmpi(FS_STR_NTFS, wszBuffer))
  762. {
  763. ComboBox_SelectString(hFilesystemCombo, -1, FS_STR_NTFS);
  764. pFormatInfo->fWasFAT = FALSE;
  765. }
  766. else if (0 == lstrcmpi(FS_STR_FAT32, wszBuffer))
  767. {
  768. ComboBox_SelectString(hFilesystemCombo, -1, FS_STR_FAT32);
  769. pFormatInfo->fWasFAT = TRUE;
  770. pFormatInfo->dwClusterSize = 0;
  771. }
  772. else
  773. {
  774. ComboBox_SelectString(hFilesystemCombo, -1, FS_STR_FAT);
  775. pFormatInfo->fWasFAT = TRUE;
  776. pFormatInfo->dwClusterSize = 0;
  777. }
  778. }
  779. // FEATURE - What about specialized file-systems? Don't care for now.
  780. }
  781. #ifndef _WIN64
  782. // if not WIN64, enable boot-disk creation if we are a 3.5" HD floppy
  783. EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_BTCHECK), pFormatInfo->fIs35HDFloppy);
  784. #else
  785. // if WIN64, hide this option, since we can't use these boot floppies on WIN64
  786. ShowWindow(GetDlgItem(pFormatInfo->hDlg, IDC_BTCHECK), FALSE);
  787. #endif
  788. // restore the old errormode
  789. SetErrorMode(olderror);
  790. // set the state of the chkboxes properly based on the FS chosen
  791. FileSysChange((FILESYSENUM)ComboBox_GetItemData(hFilesystemCombo, ComboBox_GetCurSel(hFilesystemCombo)), pFormatInfo);
  792. }
  793. }
  794. // If the above failed due to disk not in drive, notify the user
  795. if (FAILED(hr))
  796. {
  797. switch (HRESULT_CODE(hr))
  798. {
  799. case ERROR_UNRECOGNIZED_MEDIA:
  800. ShellMessageBox(HINST_THISDLL,
  801. hDlg,
  802. MAKEINTRESOURCE(IDS_UNFORMATTABLE_DISK),
  803. NULL,
  804. MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK,
  805. NULL);
  806. break;
  807. case ERROR_NOT_READY:
  808. ShellMessageBox(HINST_THISDLL,
  809. hDlg,
  810. MAKEINTRESOURCE(IDS_DRIVENOTREADY),
  811. NULL,
  812. MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK,
  813. pFormatInfo->wszDriveName[0]);
  814. break;
  815. case ERROR_ACCESS_DENIED:
  816. ShellMessageBox(HINST_THISDLL,
  817. hDlg,
  818. MAKEINTRESOURCE(IDS_ACCESSDENIED),
  819. NULL,
  820. MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK,
  821. pFormatInfo->wszDriveName[0]);
  822. break;
  823. case ERROR_WRITE_PROTECT:
  824. ShellMessageBox(HINST_THISDLL,
  825. hDlg,
  826. MAKEINTRESOURCE(IDS_WRITEPROTECTED),
  827. NULL,
  828. MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK,
  829. pFormatInfo->wszDriveName[0]);
  830. break;
  831. }
  832. }
  833. return hr;
  834. }
  835. // Synopsis: Called from within the FMIFS DLL's Format function, this
  836. // updates the format dialog's status bar and responds to
  837. // format completion/error notifications.
  838. //
  839. // Arguments: [PacketType] -- Type of packet (ie: % complete, error, etc)
  840. // [PacketLength] -- Size, in bytes, of the packet
  841. // [pPacketData] -- Pointer to the packet
  842. //
  843. // Returns: BOOLEAN continuation value
  844. //
  845. BOOLEAN FormatCallback(FMIFS_PACKET_TYPE PacketType, ULONG PacketLength, void *pPacketData)
  846. {
  847. UINT iMessageID = IDS_FORMATFAILED;
  848. BOOL fFailed = FALSE;
  849. FORMATINFO* pFormatInfo = GetFormatInfoPtr();
  850. ASSERT(g_iTLSFormatInfo);
  851. // Grab the FORMATINFO structure for this thread
  852. if (pFormatInfo)
  853. {
  854. if (!pFormatInfo->fShouldCancel)
  855. {
  856. switch(PacketType)
  857. {
  858. case FmIfsIncompatibleFileSystem:
  859. fFailed = TRUE;
  860. iMessageID = IDS_INCOMPATIBLEFS;
  861. break;
  862. case FmIfsIncompatibleMedia:
  863. fFailed = TRUE;
  864. iMessageID = IDS_INCOMPATIBLEMEDIA;
  865. break;
  866. case FmIfsAccessDenied:
  867. fFailed = TRUE;
  868. iMessageID = IDS_ACCESSDENIED;
  869. break;
  870. case FmIfsMediaWriteProtected:
  871. fFailed = TRUE;
  872. iMessageID = IDS_WRITEPROTECTED;
  873. break;
  874. case FmIfsCantLock:
  875. fFailed = TRUE;
  876. iMessageID = IDS_CANTLOCK;
  877. break;
  878. case FmIfsCantQuickFormat:
  879. fFailed = TRUE;
  880. iMessageID = IDS_CANTQUICKFORMAT;
  881. break;
  882. case FmIfsIoError:
  883. fFailed = TRUE;
  884. iMessageID = IDS_IOERROR;
  885. // FUTURE Consider showing head/track etc where error was
  886. break;
  887. case FmIfsBadLabel:
  888. fFailed = TRUE;
  889. iMessageID = IDS_BADLABEL;
  890. break;
  891. case FmIfsPercentCompleted:
  892. {
  893. FMIFS_PERCENT_COMPLETE_INFORMATION * pPercent =
  894. (FMIFS_PERCENT_COMPLETE_INFORMATION *) pPacketData;
  895. SendDlgItemMessage(pFormatInfo->hDlg, IDC_FMTPROGRESS,
  896. PBM_SETPOS,
  897. pPercent->PercentCompleted, 0);
  898. }
  899. break;
  900. case FmIfsFinished:
  901. {
  902. // Format is done; check for failure or success
  903. FMIFS_FINISHED_INFORMATION* pFinishedInfo = (FMIFS_FINISHED_INFORMATION*)pPacketData;
  904. pFormatInfo->fFinishedOK = pFinishedInfo->Success;
  905. if (pFinishedInfo->Success)
  906. {
  907. // fmifs will "succeed" even if we already failed, so we need to double-check
  908. // that we haven't already put up error UI
  909. if (!pFormatInfo->fErrorAlready)
  910. {
  911. // If "Enable Compression" is checked, try to enable filesystem compression
  912. if (IsDlgButtonChecked(pFormatInfo->hDlg, IDC_ECCHECK))
  913. {
  914. if (pFormatInfo->fmifs.EnableVolumeCompression(pFormatInfo->wszDriveName,
  915. COMPRESSION_FORMAT_DEFAULT) == FALSE)
  916. {
  917. ShellMessageBox(HINST_THISDLL,
  918. pFormatInfo->hDlg,
  919. MAKEINTRESOURCE(IDS_CANTENABLECOMP),
  920. NULL,
  921. MB_SETFOREGROUND | MB_ICONINFORMATION | MB_OK);
  922. }
  923. }
  924. // Even though its a quick format, the progress meter should
  925. // show 100% when the "Format Complete" requester is up
  926. SendDlgItemMessage(pFormatInfo->hDlg, IDC_FMTPROGRESS,
  927. PBM_SETPOS,
  928. 100, // set %100 Complete
  929. 0);
  930. // FUTURE Consider showing format stats, ie: ser no, bytes, etc
  931. ShellMessageBox(HINST_THISDLL,
  932. pFormatInfo->hDlg,
  933. MAKEINTRESOURCE(IDS_FORMATCOMPLETE),
  934. NULL,
  935. MB_SETFOREGROUND | MB_ICONINFORMATION | MB_OK);
  936. }
  937. // Restore the dialog title, reset progress and flags
  938. SendDlgItemMessage(pFormatInfo->hDlg,
  939. IDC_FMTPROGRESS,
  940. PBM_SETPOS,
  941. 0, // Reset Percent Complete
  942. 0);
  943. // Set the focus onto the Close button
  944. pFormatInfo->fCancelled = FALSE;
  945. }
  946. else
  947. {
  948. fFailed = TRUE;
  949. }
  950. }
  951. break;
  952. }
  953. if (fFailed && !pFormatInfo->fErrorAlready)
  954. {
  955. // If we received any kind of failure information, put up a final
  956. // "Format Failed" message. UNLESS we've already put up some nice message
  957. ShellMessageBox(HINST_THISDLL,
  958. pFormatInfo->hDlg,
  959. MAKEINTRESOURCE(iMessageID),
  960. NULL,
  961. MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK);
  962. pFormatInfo->fErrorAlready = TRUE;
  963. }
  964. }
  965. else
  966. {
  967. // user hit cancel
  968. pFormatInfo->fCancelled = TRUE;
  969. fFailed = TRUE;
  970. }
  971. }
  972. else
  973. {
  974. // no pFormatInfo? we're screwed
  975. fFailed = TRUE;
  976. }
  977. return (BOOLEAN) (fFailed == FALSE);
  978. }
  979. //
  980. // Synopsis: Spun off as its own thread, this ghosts all controls in the
  981. // dialog except "Cancel", then does the actual format
  982. //
  983. // Arguments: [pIn] -- FORMATINFO structure pointer as a void *
  984. //
  985. // Returns: HRESULT thread exit code
  986. //
  987. DWORD WINAPI BeginFormat(void * pIn)
  988. {
  989. FORMATINFO *pFormatInfo = (FORMATINFO*)pIn;
  990. HRESULT hr = S_OK;
  991. // Save the FORAMTINFO ptr for this thread, to be used in the format
  992. // callback function
  993. hr = StuffFormatInfoPtr(pFormatInfo);
  994. if (hr == S_OK)
  995. {
  996. HWND hwndFileSysCB = GetDlgItem(pFormatInfo->hDlg, IDC_FSCOMBO);
  997. int iCurSel;
  998. // Set the window title to indicate format in proress...
  999. SetDriveWindowTitle(pFormatInfo->hDlg, pFormatInfo->wszDriveName, IDS_FMT_FORMATTING);
  1000. // Determine the user's choice of filesystem
  1001. iCurSel = ComboBox_GetCurSel(hwndFileSysCB);
  1002. if (iCurSel != CB_ERR)
  1003. {
  1004. LPCWSTR pwszFileSystemName;
  1005. FMIFS_MEDIA_TYPE MediaType;
  1006. LPITEMIDLIST pidlFormat;
  1007. BOOLEAN fQuickFormat;
  1008. FILESYSENUM fseType = (FILESYSENUM)ComboBox_GetItemData(hwndFileSysCB, iCurSel);
  1009. switch (fseType)
  1010. {
  1011. case e_FAT:
  1012. pwszFileSystemName = FS_STR_FAT;
  1013. break;
  1014. case e_FAT32:
  1015. pwszFileSystemName = FS_STR_FAT32;
  1016. break;
  1017. case e_NTFS:
  1018. pwszFileSystemName = FS_STR_NTFS;
  1019. break;
  1020. }
  1021. // Determine the user's choice of media formats
  1022. iCurSel = ComboBox_GetCurSel(GetDlgItem(pFormatInfo->hDlg, IDC_CAPCOMBO));
  1023. if (iCurSel == CB_ERR)
  1024. {
  1025. iCurSel = 0;
  1026. }
  1027. MediaType = pFormatInfo->rgMedia[iCurSel];
  1028. // Get the cluster size. First selection ("Use Default") yields a zero,
  1029. // while the next 4 select 512, 1024, 2048, or 4096
  1030. iCurSel = ComboBox_GetCurSel(GetDlgItem(pFormatInfo->hDlg, IDC_ASCOMBO));
  1031. if ((iCurSel == CB_ERR) || (iCurSel == 0))
  1032. {
  1033. pFormatInfo->dwClusterSize = 0;
  1034. }
  1035. else
  1036. {
  1037. pFormatInfo->dwClusterSize = 256 << iCurSel;
  1038. }
  1039. // Quickformatting?
  1040. fQuickFormat = Button_GetCheck(GetDlgItem(pFormatInfo->hDlg, IDC_QFCHECK));
  1041. // Clear the error state.
  1042. pFormatInfo->fErrorAlready = FALSE;
  1043. // Tell the shell to get ready... Announce that the media is no
  1044. // longer valid (so people who have active views on it will navigate
  1045. // away) and tell the shell to close its FindFirstChangeNotifications.
  1046. if (SUCCEEDED(SHILCreateFromPath(pFormatInfo->wszDriveName, &pidlFormat, NULL)))
  1047. {
  1048. SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_IDLIST | SHCNF_FLUSH, pidlFormat, 0);
  1049. SHChangeNotifySuspendResume(TRUE, pidlFormat, TRUE, 0);
  1050. }
  1051. else
  1052. {
  1053. pidlFormat = NULL;
  1054. }
  1055. if (!pFormatInfo->fMakeBootDisk)
  1056. {
  1057. // Do the format.
  1058. pFormatInfo->fmifs.FormatEx(pFormatInfo->wszDriveName,
  1059. MediaType,
  1060. (PWSTR)pwszFileSystemName,
  1061. pFormatInfo->wszVolName,
  1062. fQuickFormat,
  1063. pFormatInfo->dwClusterSize,
  1064. FormatCallback);
  1065. }
  1066. else
  1067. {
  1068. pFormatInfo->diskcopy.MakeBootDisk(pFormatInfo->diskcopy.hDISKCOPY_DLL, pFormatInfo->drive, &pFormatInfo->fCancelled, FormatCallback);
  1069. }
  1070. // Wake the shell back up.
  1071. if (pidlFormat)
  1072. {
  1073. SHChangeNotifySuspendResume(FALSE, pidlFormat, TRUE, 0);
  1074. ILFree(pidlFormat);
  1075. }
  1076. // Success or failure, we should fire a notification on the disk
  1077. // since we don't really know the state after the format
  1078. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATHW, (void *)pFormatInfo->wszDriveName, NULL);
  1079. }
  1080. else
  1081. {
  1082. // couldn't get the filesys CB selection
  1083. hr = E_FAIL;
  1084. }
  1085. // Release the TLS index
  1086. UnstuffFormatInfoPtr();
  1087. }
  1088. // Post a message back to the DialogProc thread to let it know
  1089. // the format is done. We post the message since otherwise the
  1090. // DialogProc thread will be too busy waiting for this thread
  1091. // to exit to be able to process the PWM_FORMATDONE message
  1092. // immediately.
  1093. PostMessage(pFormatInfo->hDlg, (UINT) PWM_FORMATDONE, 0, 0);
  1094. ReleaseFormatInfo(pFormatInfo);
  1095. return (DWORD)hr;
  1096. }
  1097. BOOL_PTR CALLBACK FormatDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  1098. {
  1099. HRESULT hr = S_OK;
  1100. int iID = GET_WM_COMMAND_ID(wParam, lParam);
  1101. int iCMD = GET_WM_COMMAND_CMD(wParam, lParam);
  1102. // Grab our previously cached pointer to the FORMATINFO struct (see WM_INITDIALOG)
  1103. FORMATINFO *pFormatInfo = (FORMATINFO *) GetWindowLongPtr(hDlg, DWLP_USER);
  1104. switch (wMsg)
  1105. {
  1106. case PWM_FORMATDONE:
  1107. // Format is done. Reset the window title and clear the progress meter
  1108. SetDriveWindowTitle(pFormatInfo->hDlg, pFormatInfo->wszDriveName, IDS_FMT_FORMAT);
  1109. SendDlgItemMessage(pFormatInfo->hDlg, IDC_FMTPROGRESS, PBM_SETPOS, 0 /* Reset Percent Complete */, 0);
  1110. EnableControls(pFormatInfo, TRUE);
  1111. if (pFormatInfo->fCancelled)
  1112. {
  1113. // Don't put up UI if the background thread finally finished
  1114. // long after the user issued the cancel
  1115. if (!pFormatInfo->fShouldCancel)
  1116. {
  1117. ShellMessageBox(HINST_THISDLL,
  1118. pFormatInfo->hDlg,
  1119. MAKEINTRESOURCE(IDS_FORMATCANCELLED),
  1120. NULL,
  1121. MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK);
  1122. }
  1123. pFormatInfo->fCancelled = FALSE;
  1124. }
  1125. if (pFormatInfo->hThread)
  1126. {
  1127. CloseHandle(pFormatInfo->hThread);
  1128. pFormatInfo->hThread = NULL;
  1129. }
  1130. break;
  1131. case WM_INITDIALOG:
  1132. // Initialize the dialog and cache the FORMATINFO structure's pointer
  1133. // as our dialog's DWLP_USER data
  1134. pFormatInfo = (FORMATINFO *) lParam;
  1135. pFormatInfo->hDlg = hDlg;
  1136. if (FAILED(InitializeFormatDlg(pFormatInfo)))
  1137. {
  1138. EndDialog(hDlg, 0);
  1139. return -1;
  1140. }
  1141. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  1142. break;
  1143. case WM_DESTROY:
  1144. if (pFormatInfo && pFormatInfo->hDlg)
  1145. {
  1146. pFormatInfo->hDlg = NULL;
  1147. }
  1148. break;
  1149. case WM_COMMAND:
  1150. if (iCMD == CBN_SELCHANGE)
  1151. {
  1152. // User made a selection in one of the combo boxes
  1153. if (iID == IDC_FSCOMBO)
  1154. {
  1155. // User selected a filesystem... update the rest of the dialog
  1156. // based on this choice
  1157. HWND hFilesystemCombo = (HWND)lParam;
  1158. int iCurSel = ComboBox_GetCurSel(hFilesystemCombo);
  1159. FileSysChange((FILESYSENUM)ComboBox_GetItemData(hFilesystemCombo, iCurSel), pFormatInfo);
  1160. }
  1161. }
  1162. else
  1163. {
  1164. // Codepath for controls other than combo boxes...
  1165. switch (iID)
  1166. {
  1167. case IDC_BTCHECK:
  1168. pFormatInfo->fMakeBootDisk = IsDlgButtonChecked(pFormatInfo->hDlg, IDC_BTCHECK);
  1169. EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_CAPCOMBO), !pFormatInfo->fMakeBootDisk);
  1170. EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_FSCOMBO), !pFormatInfo->fMakeBootDisk);
  1171. EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_ASCOMBO), !pFormatInfo->fMakeBootDisk);
  1172. EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_VLABEL), !pFormatInfo->fMakeBootDisk);
  1173. EnableWindow(GetDlgItem(pFormatInfo->hDlg, IDC_QFCHECK), !pFormatInfo->fMakeBootDisk);
  1174. break;
  1175. case IDC_ECCHECK:
  1176. pFormatInfo->fEnableComp = IsDlgButtonChecked(hDlg, IDC_ECCHECK);
  1177. break;
  1178. case IDOK:
  1179. {
  1180. // Get user verification for format, break out on CANCEL
  1181. if (IDCANCEL == ShellMessageBox(HINST_THISDLL,
  1182. hDlg,
  1183. MAKEINTRESOURCE(IDS_OKTOFORMAT),
  1184. NULL,
  1185. MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OKCANCEL))
  1186. {
  1187. break;
  1188. }
  1189. ASSERT(pFormatInfo->hThread == NULL);
  1190. DisableControls(pFormatInfo);
  1191. pFormatInfo->fCancelled = FALSE;
  1192. pFormatInfo->fShouldCancel = FALSE;
  1193. GetWindowText(GetDlgItem(pFormatInfo->hDlg, IDC_VLABEL), pFormatInfo->wszVolName, MAX_PATH);
  1194. AddRefFormatInfo(pFormatInfo);
  1195. pFormatInfo->hThread = CreateThread(NULL,
  1196. 0,
  1197. BeginFormat,
  1198. (void *)pFormatInfo,
  1199. 0,
  1200. NULL);
  1201. if (!pFormatInfo->hThread)
  1202. {
  1203. // ISSUE: we should probably do something...
  1204. ReleaseFormatInfo(pFormatInfo);
  1205. }
  1206. }
  1207. break;
  1208. case IDCANCEL:
  1209. // If the format thread is running, wait for it. If not,
  1210. // exit the dialog
  1211. pFormatInfo->fShouldCancel = TRUE;
  1212. if (pFormatInfo->hThread)
  1213. {
  1214. DWORD dwWait;
  1215. do
  1216. {
  1217. dwWait = WaitForSingleObject(pFormatInfo->hThread, 10000);
  1218. }
  1219. while ((WAIT_TIMEOUT == dwWait) &&
  1220. (IDRETRY == ShellMessageBox(HINST_THISDLL,
  1221. hDlg,
  1222. MAKEINTRESOURCE(IDS_CANTCANCELFMT),
  1223. NULL,
  1224. MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_RETRYCANCEL)));
  1225. // If the format doesn't admit to having been killed, it didn't
  1226. // give up peacefully. Just abandon it and let it clean up
  1227. // when it finally gets around to it, at which point we will
  1228. // enable the OK button to let the user take another stab.
  1229. //
  1230. // Careful: The format may have cleaned up while the dialog box
  1231. // was up, so revalidate.
  1232. if (pFormatInfo->hThread)
  1233. {
  1234. CloseHandle(pFormatInfo->hThread);
  1235. pFormatInfo->hThread = NULL;
  1236. pFormatInfo->fCancelled = TRUE;
  1237. EnableControls(pFormatInfo, FALSE);
  1238. }
  1239. }
  1240. else
  1241. {
  1242. EndDialog(hDlg, IDCANCEL);
  1243. }
  1244. break;
  1245. }
  1246. }
  1247. break;
  1248. case WM_HELP:
  1249. WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR) (LPSTR) FmtaIds);
  1250. break;
  1251. case WM_CONTEXTMENU:
  1252. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR) (LPSTR) FmtaIds);
  1253. break;
  1254. default:
  1255. return FALSE;
  1256. }
  1257. return TRUE;
  1258. }
  1259. //
  1260. // Synopsis: The SHFormatDrive API provides access to the Shell
  1261. // format dialog. This allows apps which want to format disks
  1262. // to bring up the same dialog that the Shell does to do it.
  1263. //
  1264. // NOTE that the user can format as many diskettes in the
  1265. // specified drive, or as many times, as he/she wishes to.
  1266. //
  1267. // Arguments: [hwnd] -- Parent window (Must NOT be NULL)
  1268. // [drive] -- 0 = A:, 1 = B:, etc.
  1269. // [fmtID] -- see below
  1270. // [options] -- SHFMT_OPT_FULL overrised default quickformat
  1271. // SHFMT_OPT_SYSONLY not support for NT
  1272. //
  1273. // Returns: See Notes
  1274. //
  1275. DWORD WINAPI SHFormatDrive(HWND hwnd, UINT drive, UINT fmtID, UINT options)
  1276. {
  1277. INT_PTR ret;
  1278. FORMATINFO *pFormatInfo = (FORMATINFO *)LocalAlloc(LPTR, sizeof(*pFormatInfo));
  1279. ASSERT(drive < 26);
  1280. if (!pFormatInfo)
  1281. return SHFMT_ERROR;
  1282. HRESULT hrCoInit = SHCoInitialize();
  1283. pFormatInfo->cRef = 1;
  1284. pFormatInfo->drive = drive;
  1285. pFormatInfo->fmtID = fmtID;
  1286. pFormatInfo->options = options;
  1287. // It makes no sense for NT to "SYS" a disk
  1288. if (pFormatInfo->options & SHFMT_OPT_SYSONLY)
  1289. {
  1290. ret = 0;
  1291. goto done;
  1292. }
  1293. // Load FMIFS.DLL and DISKCOPY.DLL and open the Format dialog
  1294. if (S_OK == LoadFMIFS(&pFormatInfo->fmifs) &&
  1295. S_OK == LoadDISKCOPY(&pFormatInfo->diskcopy))
  1296. {
  1297. DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_FORMATDISK),
  1298. hwnd, FormatDlgProc, (LPARAM) pFormatInfo);
  1299. }
  1300. else
  1301. {
  1302. ASSERT(0 && "Can't load FMIFS.DLL");
  1303. ret = SHFMT_ERROR;
  1304. goto done;
  1305. }
  1306. // Since time immemorial it has been almost impossible to
  1307. // get SHFMT_CANCEL as a return code. Most of the time, you get
  1308. // SHFMT_ERROR if the user cancels.
  1309. if (pFormatInfo->fCancelled)
  1310. {
  1311. ret = SHFMT_CANCEL;
  1312. }
  1313. else if (pFormatInfo->fFinishedOK)
  1314. {
  1315. // APPCOMPAT: (stephstm) We used to say that we return the Serial
  1316. // Number but we never did. So keep on returning 0 for success.
  1317. // Furthermore, Serial number values could conflict SHFMT_*
  1318. // error codes.
  1319. ret = 0;
  1320. }
  1321. else
  1322. {
  1323. ret = SHFMT_ERROR;
  1324. }
  1325. done:
  1326. ReleaseFormatInfo(pFormatInfo);
  1327. SHCoUninitialize(hrCoInit);
  1328. return (DWORD)ret;
  1329. }
  1330. ////////////////////////////////////////////////////////////////////////////
  1331. //
  1332. // CHKDSK
  1333. //
  1334. ////////////////////////////////////////////////////////////////////////////
  1335. //
  1336. // This structure described the current chkdsk session
  1337. //
  1338. typedef struct
  1339. {
  1340. UINT lastpercent; // last percentage complete received
  1341. UINT currentphase; // current chkdsk phase
  1342. FMIFS fmifs; // ptr to FMIFS structure, above
  1343. BOOL fRecovery; // Attempt to recover bad sectors
  1344. BOOL fFixErrors; // Fix filesystem errors as found
  1345. BOOL fCancelled; // Was chkdsk terminated early?
  1346. BOOL fShouldCancel; // User has clicked cancel; pending abort
  1347. HWND hDlg; // handle to the chkdsk dialog
  1348. HANDLE hThread;
  1349. BOOL fNoFinalMsg; // Do not put up a final failure message
  1350. WCHAR wszDriveName[MAX_PATH]; // For example, "A:\", or "C:\folder\mountedvolume\"
  1351. LONG cRef; // reference count on this structure
  1352. } CHKDSKINFO;
  1353. void AddRefChkDskInfo(CHKDSKINFO *pChkDskInfo)
  1354. {
  1355. InterlockedIncrement(&pChkDskInfo->cRef);
  1356. }
  1357. void ReleaseChkDskInfo(CHKDSKINFO *pChkDskInfo)
  1358. {
  1359. if (InterlockedDecrement(&pChkDskInfo->cRef) == 0)
  1360. {
  1361. if (pChkDskInfo->fmifs.hFMIFS_DLL)
  1362. {
  1363. FreeLibrary(pChkDskInfo->fmifs.hFMIFS_DLL);
  1364. }
  1365. if (pChkDskInfo->hThread)
  1366. {
  1367. CloseHandle(pChkDskInfo->hThread);
  1368. }
  1369. LocalFree(pChkDskInfo);
  1370. }
  1371. }
  1372. static DWORD g_iTLSChkDskInfo = 0;
  1373. static LONG g_cTLSChkDskInfo = 0; // Usage count
  1374. //
  1375. // Synopsis: Allocates a thread-local index slot for this thread's
  1376. // CHKDSKINFO pointer, if the index doesn't already exist.
  1377. // In any event, stores the CHKDSKINFO pointer in the slot
  1378. // and increments the index's usage count.
  1379. //
  1380. // Arguments: [pChkDskInfo] -- The pointer to store
  1381. //
  1382. // Returns: HRESULT
  1383. //
  1384. //
  1385. // Thread-Local Storage index for our CHKDSKINFO structure pointer
  1386. //
  1387. HRESULT StuffChkDskInfoPtr(CHKDSKINFO *pChkDskInfo)
  1388. {
  1389. HRESULT hr = S_OK;
  1390. // Allocate an index slot for our thread-local CHKDSKINFO pointer, if one
  1391. // doesn't already exist, then stuff our CHKDSKINFO ptr at that index.
  1392. ENTERCRITICAL;
  1393. if (0 == g_iTLSChkDskInfo)
  1394. {
  1395. g_iTLSChkDskInfo = TlsAlloc();
  1396. if (g_iTLSChkDskInfo == (DWORD)-1)
  1397. {
  1398. hr = HRESULT_FROM_WIN32(GetLastError());
  1399. }
  1400. g_cTLSChkDskInfo = 0;
  1401. }
  1402. if (S_OK == hr)
  1403. {
  1404. if (TlsSetValue(g_iTLSChkDskInfo, (void *)pChkDskInfo))
  1405. {
  1406. g_cTLSChkDskInfo++;
  1407. }
  1408. else
  1409. {
  1410. hr = HRESULT_FROM_WIN32(GetLastError());
  1411. }
  1412. }
  1413. LEAVECRITICAL;
  1414. return hr;
  1415. }
  1416. //
  1417. // Synopsis: Decrements the usage count on our thread-local storage
  1418. // index, and if it goes to zero the index is free'd
  1419. //
  1420. // Arguments: [none]
  1421. //
  1422. // Returns: none
  1423. //
  1424. void UnstuffChkDskInfoPtr()
  1425. {
  1426. ENTERCRITICAL;
  1427. g_cTLSChkDskInfo--;
  1428. if (g_cTLSChkDskInfo == 0)
  1429. {
  1430. TlsFree(g_iTLSChkDskInfo);
  1431. g_iTLSChkDskInfo = 0;
  1432. }
  1433. LEAVECRITICAL;
  1434. }
  1435. //
  1436. // Synopsis: Retrieves this threads CHKDSKINFO ptr by grabbing the
  1437. // thread-local value previously stuff'd
  1438. //
  1439. // Arguments: [none]
  1440. //
  1441. // Returns: The pointer, of course
  1442. //
  1443. CHKDSKINFO *GetChkDskInfoPtr()
  1444. {
  1445. return (CHKDSKINFO *)TlsGetValue(g_iTLSChkDskInfo);
  1446. }
  1447. //
  1448. // Synopsis: Ghosts all controls except "Cancel", saving their
  1449. // previous state in the CHKDSKINFO structure
  1450. //
  1451. // Arguments: [pChkDskInfo] -- Describes a ChkDsk dialog session
  1452. //
  1453. // Notes: Also changes "Close" button text to read "Cancel"
  1454. //
  1455. void DisableChkDskControls(CHKDSKINFO *pChkDskInfo)
  1456. {
  1457. // We disable CANCEL because CHKDSK does not
  1458. // allow interruption at the filesystem level.
  1459. EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDC_FIXERRORS), FALSE);
  1460. EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDC_RECOVERY), FALSE);
  1461. EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDOK), FALSE);
  1462. EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDCANCEL), FALSE);
  1463. }
  1464. //
  1465. // Synopsis: Restores controls to the enabled/disabled state they were
  1466. // before a previous call to DisableControls().
  1467. //
  1468. // Arguments: [pChkDskInfo] -- Decribes a chkdsk dialog session
  1469. //
  1470. void EnableChkDskControls(CHKDSKINFO *pChkDskInfo)
  1471. {
  1472. EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDC_FIXERRORS), TRUE);
  1473. EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDC_RECOVERY), TRUE);
  1474. EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDOK), TRUE);
  1475. EnableWindow(GetDlgItem(pChkDskInfo->hDlg, IDCANCEL), TRUE);
  1476. // Erase the current phase text
  1477. SetWindowText(GetDlgItem(pChkDskInfo->hDlg, IDC_PHASE), TEXT(""));
  1478. pChkDskInfo->lastpercent = 101;
  1479. pChkDskInfo->currentphase = 0;
  1480. }
  1481. //
  1482. // Synopsis: Called from within the FMIFS DLL's ChkDsk function, this
  1483. // updates the ChkDsk dialog's status bar and responds to
  1484. // chkdsk completion/error notifications.
  1485. //
  1486. // Arguments: [PacketType] -- Type of packet (ie: % complete, error, etc)
  1487. // [PacketLength] -- Size, in bytes, of the packet
  1488. // [pPacketData] -- Pointer to the packet
  1489. //
  1490. // Returns: BOOLEAN continuation value
  1491. //
  1492. BOOLEAN ChkDskCallback(FMIFS_PACKET_TYPE PacketType, ULONG PacketLength, void *pPacketData)
  1493. {
  1494. UINT iMessageID = IDS_CHKDSKFAILED;
  1495. BOOL fFailed = FALSE;
  1496. CHKDSKINFO* pChkDskInfo = GetChkDskInfoPtr();
  1497. ASSERT(g_iTLSChkDskInfo);
  1498. // Grab the CHKDSKINFO structure for this thread
  1499. if (pChkDskInfo)
  1500. {
  1501. if (!pChkDskInfo->fShouldCancel)
  1502. {
  1503. switch(PacketType)
  1504. {
  1505. case FmIfsAccessDenied:
  1506. fFailed = TRUE;
  1507. iMessageID = IDS_CHKACCESSDENIED;
  1508. break;
  1509. case FmIfsCheckOnReboot:
  1510. {
  1511. FMIFS_CHECKONREBOOT_INFORMATION * pRebootInfo = (FMIFS_CHECKONREBOOT_INFORMATION *)pPacketData;
  1512. // Check to see whether or not the user wants to schedule this
  1513. // chkdsk for the next reboot, since the drive cannot be locked
  1514. // right now.
  1515. if (IDYES == ShellMessageBox(HINST_THISDLL,
  1516. pChkDskInfo->hDlg,
  1517. MAKEINTRESOURCE(IDS_CHKONREBOOT),
  1518. NULL,
  1519. MB_SETFOREGROUND | MB_ICONINFORMATION | MB_YESNO))
  1520. {
  1521. // Yes, have FMIFS schedule an autochk for us
  1522. pRebootInfo->QueryResult = TRUE;
  1523. pChkDskInfo->fNoFinalMsg = TRUE;
  1524. }
  1525. else
  1526. {
  1527. // Nope, just fail out with "cant lock drive"
  1528. fFailed = TRUE;
  1529. iMessageID = IDS_CHKDSKFAILED;
  1530. }
  1531. }
  1532. break;
  1533. case FmIfsMediaWriteProtected:
  1534. fFailed = TRUE;
  1535. iMessageID = IDS_WRITEPROTECTED;
  1536. break;
  1537. case FmIfsIoError:
  1538. fFailed = TRUE;
  1539. iMessageID = IDS_IOERROR;
  1540. // FUTURE Consider showing head/track etc where error was
  1541. break;
  1542. case FmIfsPercentCompleted:
  1543. {
  1544. FMIFS_PERCENT_COMPLETE_INFORMATION* pPercent = (FMIFS_PERCENT_COMPLETE_INFORMATION *)pPacketData;
  1545. SendMessage(GetDlgItem(pChkDskInfo->hDlg, IDC_CHKDSKPROGRESS),
  1546. PBM_SETPOS,
  1547. pPercent->PercentCompleted, // updatee % complete
  1548. 0);
  1549. if (pPercent->PercentCompleted < pChkDskInfo->lastpercent)
  1550. {
  1551. WCHAR wszTmp[100];
  1552. WCHAR wszFormat[100];
  1553. // If this % complete is less than the last one seen,
  1554. // we have completed a phase of the chkdsk and should
  1555. // advance to the next one.
  1556. LoadString(HINST_THISDLL, IDS_CHKPHASE, wszFormat, ARRAYSIZE(wszFormat));
  1557. wsprintf(wszTmp, wszFormat, ++(pChkDskInfo->currentphase));
  1558. SetDlgItemText(pChkDskInfo->hDlg, IDC_PHASE, wszTmp);
  1559. }
  1560. pChkDskInfo->lastpercent = pPercent->PercentCompleted;
  1561. }
  1562. break;
  1563. case FmIfsFinished:
  1564. {
  1565. // ChkDsk is done; check for failure or success
  1566. FMIFS_FINISHED_INFORMATION * pFinishedInfo = (FMIFS_FINISHED_INFORMATION *) pPacketData;
  1567. // ChkDskEx now return the proper success value
  1568. if (pFinishedInfo->Success)
  1569. {
  1570. // Since we're done, force the progress gauge to 100%, so we
  1571. // don't sit here if the chkdsk code misled us
  1572. SendMessage(GetDlgItem(pChkDskInfo->hDlg, IDC_CHKDSKPROGRESS),
  1573. PBM_SETPOS,
  1574. 100, // Percent Complete
  1575. 0);
  1576. ShellMessageBox(HINST_THISDLL,
  1577. pChkDskInfo->hDlg,
  1578. MAKEINTRESOURCE(IDS_CHKDSKCOMPLETE),
  1579. NULL,
  1580. MB_SETFOREGROUND | MB_ICONINFORMATION | MB_OK);
  1581. SetDlgItemText(pChkDskInfo->hDlg, IDC_PHASE, TEXT(""));
  1582. SendMessage(GetDlgItem(pChkDskInfo->hDlg, IDC_CHKDSKPROGRESS),
  1583. PBM_SETPOS,
  1584. 0, // reset Percent Complete
  1585. 0);
  1586. }
  1587. else
  1588. {
  1589. iMessageID = IDS_CHKDSKFAILED;
  1590. fFailed = TRUE;
  1591. }
  1592. }
  1593. break;
  1594. }
  1595. // If we received any kind of failure information, put up a final
  1596. // "ChkDsk Failed" message.
  1597. if (fFailed && (pChkDskInfo->fNoFinalMsg == FALSE))
  1598. {
  1599. pChkDskInfo->fNoFinalMsg = TRUE;
  1600. ShellMessageBox(HINST_THISDLL,
  1601. pChkDskInfo->hDlg,
  1602. MAKEINTRESOURCE(iMessageID),
  1603. NULL,
  1604. MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK);
  1605. }
  1606. }
  1607. else
  1608. {
  1609. // If the user has signalled to abort the ChkDsk, return
  1610. // FALSE out of here right now
  1611. pChkDskInfo->fCancelled = TRUE;
  1612. fFailed = TRUE;
  1613. }
  1614. }
  1615. else
  1616. {
  1617. fFailed = TRUE;
  1618. }
  1619. return (BOOLEAN) (fFailed == FALSE);
  1620. }
  1621. void DoChkDsk(CHKDSKINFO* pChkDskInfo, LPWSTR pwszFileSystem)
  1622. {
  1623. TCHAR szVolumeGUID[50]; // 50: from doc
  1624. FMIFS_CHKDSKEX_PARAM param = {0};
  1625. param.Major = 1;
  1626. param.Minor = 0;
  1627. param.Flags = pChkDskInfo->fRecovery ? FMIFS_CHKDSK_RECOVER : 0;
  1628. GetVolumeNameForVolumeMountPoint(pChkDskInfo->wszDriveName,
  1629. szVolumeGUID,
  1630. ARRAYSIZE(szVolumeGUID));
  1631. // the backslash at the end means check for fragmentation.
  1632. PathRemoveBackslash(szVolumeGUID);
  1633. pChkDskInfo->fmifs.ChkDskEx(szVolumeGUID,
  1634. pwszFileSystem,
  1635. (BOOLEAN)pChkDskInfo->fFixErrors,
  1636. &param,
  1637. ChkDskCallback);
  1638. }
  1639. //
  1640. // Synopsis: Spun off as its own thread, this ghosts all controls in the
  1641. // dialog except "Cancel", then does the actual ChkDsk
  1642. //
  1643. // Arguments: [pIn] -- CHKDSKINFO structure pointer as a void *
  1644. //
  1645. // Returns: HRESULT thread exit code
  1646. //
  1647. DWORD WINAPI BeginChkDsk(void * pIn)
  1648. {
  1649. CHKDSKINFO *pChkDskInfo = (CHKDSKINFO *)pIn;
  1650. HRESULT hr;
  1651. // Save the CHKDSKINFO ptr for this thread, to be used in the ChkDsk
  1652. // callback function
  1653. hr = StuffChkDskInfoPtr(pChkDskInfo);
  1654. if (hr == S_OK)
  1655. {
  1656. WCHAR swzFileSystem[MAX_PATH];
  1657. // Get the filesystem in use on the device
  1658. if (GetVolumeInformationW(pChkDskInfo->wszDriveName,
  1659. NULL,
  1660. 0,
  1661. NULL,
  1662. NULL,
  1663. NULL,
  1664. swzFileSystem,
  1665. MAX_PATH))
  1666. {
  1667. // Set the window title to indicate ChkDsk in proress...
  1668. SetDriveWindowTitle(pChkDskInfo->hDlg, pChkDskInfo->wszDriveName, IDS_CHKINPROGRESS);
  1669. pChkDskInfo->fNoFinalMsg = FALSE;
  1670. // Should we try data recovery?
  1671. pChkDskInfo->fRecovery = IsDlgButtonChecked(pChkDskInfo->hDlg, IDC_RECOVERY);
  1672. // Should we fix filesystem errors?
  1673. pChkDskInfo->fFixErrors = IsDlgButtonChecked(pChkDskInfo->hDlg, IDC_FIXERRORS);
  1674. // just do it!
  1675. DoChkDsk(pChkDskInfo, swzFileSystem);
  1676. }
  1677. else
  1678. {
  1679. hr = HRESULT_FROM_WIN32(GetLastError());
  1680. }
  1681. // Release the TLS index
  1682. UnstuffChkDskInfoPtr();
  1683. }
  1684. PostMessage(pChkDskInfo->hDlg, (UINT) PWM_CHKDSKDONE, 0, 0);
  1685. ReleaseChkDskInfo(pChkDskInfo);
  1686. return (DWORD)hr;
  1687. }
  1688. //
  1689. // Synopsis: DLGPROC for the chkdsk dialog
  1690. //
  1691. // Arguments: [hDlg] -- Typical
  1692. // [wMsg] -- Typical
  1693. // [wParam] -- Typical
  1694. // [lParam] -- For WM_INIT, carries the CHKDSKINFO structure
  1695. // pointer passed to DialogBoxParam() when the
  1696. // dialog was created.
  1697. //
  1698. BOOL_PTR CALLBACK ChkDskDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  1699. {
  1700. HRESULT hr = S_OK;
  1701. int iID = GET_WM_COMMAND_ID(wParam, lParam);
  1702. // Grab our previously cached pointer to the CHKDSKINFO struct (see WM_INITDIALOG)
  1703. CHKDSKINFO *pChkDskInfo = (CHKDSKINFO *) GetWindowLongPtr(hDlg, DWLP_USER);
  1704. switch (wMsg)
  1705. {
  1706. // done. Reset the window title and clear the progress meter
  1707. case PWM_CHKDSKDONE:
  1708. {
  1709. // chdsk is done. Reset the window title and clear the progress meter
  1710. SetDriveWindowTitle(pChkDskInfo->hDlg, pChkDskInfo->wszDriveName, IDS_CHKDISK);
  1711. SendMessage(GetDlgItem(pChkDskInfo->hDlg,
  1712. IDC_CHKDSKPROGRESS),
  1713. PBM_SETPOS,
  1714. 0, // Reset Percent Complete
  1715. 0);
  1716. EnableChkDskControls(pChkDskInfo);
  1717. if (pChkDskInfo->fCancelled)
  1718. {
  1719. ShellMessageBox(HINST_THISDLL,
  1720. pChkDskInfo->hDlg,
  1721. MAKEINTRESOURCE(IDS_CHKDSKCANCELLED),
  1722. NULL,
  1723. MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK);
  1724. }
  1725. if (pChkDskInfo->hThread)
  1726. {
  1727. CloseHandle(pChkDskInfo->hThread);
  1728. pChkDskInfo->hThread = NULL;
  1729. }
  1730. EndDialog(hDlg, 0);
  1731. }
  1732. break;
  1733. case WM_INITDIALOG:
  1734. // Initialize the dialog and cache the CHKDSKINFO structure's pointer
  1735. // as our dialog's DWLP_USER data
  1736. pChkDskInfo = (CHKDSKINFO *) lParam;
  1737. pChkDskInfo->hDlg = hDlg;
  1738. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  1739. // Set the dialog title to indicate which drive we are dealing with
  1740. SetDriveWindowTitle(pChkDskInfo->hDlg, pChkDskInfo->wszDriveName, IDS_CHKDISK);
  1741. break;
  1742. case WM_DESTROY:
  1743. if (pChkDskInfo && pChkDskInfo->hDlg)
  1744. {
  1745. pChkDskInfo->hDlg = NULL;
  1746. }
  1747. break;
  1748. case WM_COMMAND:
  1749. {
  1750. switch (iID)
  1751. {
  1752. case IDC_FIXERRORS:
  1753. pChkDskInfo->fFixErrors = Button_GetCheck((HWND)lParam);
  1754. break;
  1755. case IDC_RECOVERY:
  1756. pChkDskInfo->fRecovery = Button_GetCheck((HWND)lParam);
  1757. break;
  1758. case IDOK:
  1759. {
  1760. // Get user verification for chkdsk, break out on CANCEL
  1761. DisableChkDskControls(pChkDskInfo);
  1762. pChkDskInfo->fShouldCancel = FALSE;
  1763. pChkDskInfo->fCancelled = FALSE;
  1764. AddRefChkDskInfo(pChkDskInfo);
  1765. pChkDskInfo->hThread = CreateThread(NULL,
  1766. 0,
  1767. BeginChkDsk,
  1768. (void *)pChkDskInfo,
  1769. 0,
  1770. NULL);
  1771. if (!pChkDskInfo->hThread)
  1772. {
  1773. // ISSUE: we should probably do something here...
  1774. ReleaseChkDskInfo(pChkDskInfo);
  1775. }
  1776. }
  1777. break;
  1778. case IDCANCEL:
  1779. {
  1780. // If the chdsk thread is running, wait for it. If not,
  1781. // exit the dialog
  1782. pChkDskInfo->fCancelled = TRUE;
  1783. pChkDskInfo->fShouldCancel = TRUE;
  1784. if (pChkDskInfo->hThread)
  1785. {
  1786. DWORD dwWait;
  1787. do
  1788. {
  1789. dwWait = WaitForSingleObject(pChkDskInfo->hThread, 10000);
  1790. }
  1791. while ((WAIT_TIMEOUT == dwWait) &&
  1792. (IDRETRY == ShellMessageBox(HINST_THISDLL,
  1793. hDlg,
  1794. MAKEINTRESOURCE(IDS_CANTCANCELCHKDSK),
  1795. NULL,
  1796. MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_RETRYCANCEL)));
  1797. // If the chkdsk doesn't admit to having been killed, it didn't
  1798. // give up peacefully. Just abandon it and let it clean up
  1799. // when it finally gets around to it, at which point we will
  1800. // enable the controls to let the user take another stab.
  1801. //
  1802. // Careful: The chkdsk may have cleaned up while the dialog box
  1803. // was up, so revalidate.
  1804. if (pChkDskInfo->hThread)
  1805. {
  1806. CloseHandle(pChkDskInfo->hThread);
  1807. pChkDskInfo->hThread = NULL;
  1808. pChkDskInfo->fCancelled = TRUE;
  1809. EnableChkDskControls(pChkDskInfo);
  1810. }
  1811. }
  1812. else
  1813. {
  1814. EndDialog(hDlg, IDCANCEL);
  1815. }
  1816. }
  1817. break;
  1818. }
  1819. }
  1820. break;
  1821. case WM_HELP:
  1822. WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPSTR)ChkaIds);
  1823. break;
  1824. case WM_CONTEXTMENU:
  1825. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(LPSTR)ChkaIds);
  1826. break;
  1827. default:
  1828. return FALSE;
  1829. }
  1830. return TRUE;
  1831. }
  1832. #define GET_INTRESOURCE(r) (LOWORD((UINT_PTR)(r)))
  1833. static HDPA hpdaChkdskActive = NULL;
  1834. //
  1835. // Synopsis: Same as SHChkDskDrive but takes a path rather than a drive int ID
  1836. // Call this fct for both path and drive int ID to be protected
  1837. // against chkdsk'ing the same drive simultaneously
  1838. //
  1839. // Arguments: [hwnd] -- Parent window (Must NOT be NULL)
  1840. // [pszDrive] -- INTRESOURCE: string if mounted on folder, drive
  1841. // number if mounted on drive letter (0 based)
  1842. //
  1843. STDAPI_(DWORD) SHChkDskDriveEx(HWND hwnd, LPWSTR pszDrive)
  1844. {
  1845. HRESULT hr = SHFMT_ERROR;
  1846. WCHAR szUniqueID[50]; // 50: size of VolumeGUID, which can fit "A:\\" too
  1847. CHKDSKINFO *pChkDskInfo = (CHKDSKINFO *)LocalAlloc(LPTR, sizeof(*pChkDskInfo));
  1848. if (pChkDskInfo)
  1849. {
  1850. hr = S_OK;
  1851. // We use a last percentage-complete value of 101, to guarantee that the
  1852. // next one received will be less, indicating next (first) phase
  1853. pChkDskInfo->lastpercent = 101;
  1854. pChkDskInfo->cRef = 1;
  1855. lstrcpyn(pChkDskInfo->wszDriveName, pszDrive, ARRAYSIZE(pChkDskInfo->wszDriveName));
  1856. PathAddBackslash(pChkDskInfo->wszDriveName);
  1857. // Prevent multiple chkdsks of the same drive
  1858. GetVolumeNameForVolumeMountPoint(pChkDskInfo->wszDriveName, szUniqueID, ARRAYSIZE(szUniqueID));
  1859. // scoping ENTERCRITICAL's var definitions to make it cooperate with other ENTERCRITICAL
  1860. {
  1861. ENTERCRITICAL;
  1862. if (!hpdaChkdskActive)
  1863. {
  1864. hpdaChkdskActive = DPA_Create(1);
  1865. }
  1866. if (hpdaChkdskActive)
  1867. {
  1868. int i, n = DPA_GetPtrCount(hpdaChkdskActive);
  1869. // Go through the DPA of currently chkdsk'ed volumes, and check if we're already
  1870. // processing this volume
  1871. for (i = 0; i < n; ++i)
  1872. {
  1873. LPWSTR pszUniqueID = (LPWSTR)DPA_GetPtr(hpdaChkdskActive, i);
  1874. if (pszUniqueID)
  1875. {
  1876. if (!lstrcmpi(szUniqueID, pszUniqueID))
  1877. {
  1878. // we're already chkdsk'ing this drive
  1879. hr = E_FAIL;
  1880. break;
  1881. }
  1882. }
  1883. }
  1884. // Looks like we're currently not chkdsk'ing this volume, add it to the DPA of currently
  1885. // chkdsk'ed volumes
  1886. if (S_OK == hr)
  1887. {
  1888. LPWSTR pszUniqueID = StrDup(szUniqueID);
  1889. if (pszUniqueID)
  1890. {
  1891. if (-1 == DPA_AppendPtr(hpdaChkdskActive, pszUniqueID))
  1892. {
  1893. LocalFree((HLOCAL)pszUniqueID);
  1894. // if can't allocate room to store a pointer, pretty useless to go on
  1895. hr = E_FAIL;
  1896. }
  1897. }
  1898. }
  1899. }
  1900. LEAVECRITICAL;
  1901. }
  1902. // Load the FMIFS DLL and open the ChkDsk dialog
  1903. if (S_OK == hr)
  1904. {
  1905. if (S_OK == LoadFMIFS(&(pChkDskInfo->fmifs)))
  1906. {
  1907. INT_PTR ret;
  1908. INITCOMMONCONTROLSEX icc = {sizeof(icc), ICC_PROGRESS_CLASS};
  1909. InitCommonControlsEx(&icc);
  1910. ret = DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_CHKDSK),
  1911. hwnd, ChkDskDlgProc, (LPARAM) pChkDskInfo);
  1912. if (-1 == ret)
  1913. {
  1914. hr = E_UNEXPECTED;
  1915. }
  1916. else
  1917. {
  1918. if (IDCANCEL == ret)
  1919. {
  1920. hr = S_FALSE;
  1921. }
  1922. }
  1923. }
  1924. else
  1925. {
  1926. ASSERT(0 && "Can't load FMIFS.DLL");
  1927. hr = E_OUTOFMEMORY;
  1928. }
  1929. // We're finish for this volume, remove from the list of currently processed volumes
  1930. ENTERCRITICAL;
  1931. if (hpdaChkdskActive)
  1932. {
  1933. int i, n = DPA_GetPtrCount(hpdaChkdskActive);
  1934. for (i = 0; i < n; ++i)
  1935. {
  1936. LPWSTR pszUniqueID = (LPWSTR)DPA_GetPtr(hpdaChkdskActive, i);
  1937. if (pszUniqueID)
  1938. {
  1939. if (!lstrcmpi(szUniqueID, pszUniqueID))
  1940. {
  1941. LocalFree((HLOCAL)pszUniqueID);
  1942. DPA_DeletePtr(hpdaChkdskActive, i);
  1943. break;
  1944. }
  1945. }
  1946. }
  1947. }
  1948. LEAVECRITICAL;
  1949. }
  1950. // If the DPA is empty delete it
  1951. ENTERCRITICAL;
  1952. if (hpdaChkdskActive && !DPA_GetPtrCount(hpdaChkdskActive))
  1953. {
  1954. DPA_Destroy(hpdaChkdskActive);
  1955. hpdaChkdskActive = NULL;
  1956. }
  1957. LEAVECRITICAL;
  1958. ReleaseChkDskInfo(pChkDskInfo);
  1959. }
  1960. return (DWORD) hr;
  1961. }
  1962. //****************************************************************************
  1963. //
  1964. // Special hook for Win9x app compat
  1965. //
  1966. // Some Win9x apps like to WinExec("DEFRAG") or WinExec("SCANDSKW")
  1967. // even though those apps don't exist on Windows NT. When such apps
  1968. // are found, we can shim them to come here instead.
  1969. BOOL ScanDskW_OnInitDialog(HWND hdlg)
  1970. {
  1971. HICON hico;
  1972. HWND hwndList;
  1973. SHFILEINFO sfi;
  1974. HIMAGELIST himlSys;
  1975. RECT rc;
  1976. LVCOLUMN lvc;
  1977. int iDrive;
  1978. TCHAR szDrive[4];
  1979. hico = (HICON)SendDlgItemMessage(hdlg, IDC_SCANDSKICON, STM_GETICON, 0, 0);
  1980. SendMessage(hdlg, WM_SETICON, ICON_BIG, (LPARAM)hico);
  1981. SendMessage(hdlg, WM_SETICON, ICON_SMALL, (LPARAM)hico);
  1982. hwndList = GetDlgItem(hdlg, IDC_SCANDSKLV);
  1983. if (Shell_GetImageLists(NULL, &himlSys))
  1984. {
  1985. ListView_SetImageList(hwndList, himlSys, LVSIL_SMALL);
  1986. }
  1987. GetClientRect(hwndList, &rc);
  1988. lvc.mask = LVCF_WIDTH;
  1989. lvc.cx = rc.right;
  1990. lvc.iSubItem = 0;
  1991. ListView_InsertColumn(hwndList, 0, &lvc);
  1992. for (iDrive = 0; iDrive < 26; iDrive++)
  1993. {
  1994. PathBuildRoot(szDrive, iDrive);
  1995. switch (GetDriveType(szDrive))
  1996. {
  1997. case DRIVE_UNKNOWN:
  1998. case DRIVE_NO_ROOT_DIR:
  1999. case DRIVE_REMOTE:
  2000. case DRIVE_CDROM:
  2001. break; // Can't scan these drives
  2002. default:
  2003. if (SHGetFileInfo(szDrive, FILE_ATTRIBUTE_DIRECTORY, &sfi, sizeof(sfi),
  2004. SHGFI_USEFILEATTRIBUTES |
  2005. SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_DISPLAYNAME))
  2006. {
  2007. LVITEM lvi;
  2008. lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
  2009. lvi.iItem = MAXLONG;
  2010. lvi.iSubItem = 0;
  2011. lvi.pszText = sfi.szDisplayName;
  2012. lvi.iImage = sfi.iIcon;
  2013. lvi.lParam = iDrive;
  2014. ListView_InsertItem(hwndList, &lvi);
  2015. }
  2016. break;
  2017. }
  2018. }
  2019. return TRUE;
  2020. }
  2021. void ScanDskW_OnOk(HWND hdlg)
  2022. {
  2023. HWND hwndList = GetDlgItem(hdlg, IDC_SCANDSKLV);
  2024. LVITEM lvi;
  2025. lvi.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
  2026. if (lvi.iItem >= 0)
  2027. {
  2028. lvi.iSubItem = 0;
  2029. lvi.mask = LVIF_PARAM;
  2030. if (ListView_GetItem(hwndList, &lvi))
  2031. {
  2032. TCHAR szDrive[4];
  2033. PathBuildRoot(szDrive, (int)lvi.lParam);
  2034. SHChkDskDriveEx(hdlg, szDrive);
  2035. }
  2036. }
  2037. }
  2038. INT_PTR CALLBACK
  2039. ScanDskW_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
  2040. {
  2041. switch (wm)
  2042. {
  2043. case WM_INITDIALOG:
  2044. return ScanDskW_OnInitDialog(hdlg);
  2045. case WM_COMMAND:
  2046. switch (GET_WM_COMMAND_ID(wParam, lParam))
  2047. {
  2048. case IDOK:
  2049. ScanDskW_OnOk(hdlg);
  2050. break;
  2051. case IDCANCEL:
  2052. EndDialog(hdlg, 0);
  2053. break;
  2054. }
  2055. break;
  2056. case WM_NOTIFY:
  2057. {
  2058. LPNMHDR pnm = (LPNMHDR)lParam;
  2059. if (pnm->code == LVN_ITEMCHANGED)
  2060. {
  2061. EnableWindow(GetDlgItem(hdlg, IDOK), ListView_GetSelectedCount(GetDlgItem(hdlg, IDC_SCANDSKLV)));
  2062. }
  2063. }
  2064. break;
  2065. }
  2066. return FALSE;
  2067. }
  2068. // Right now, we have only one app compat shim entry point (SCANDSKW)
  2069. // In the future we can add others to the command line.
  2070. STDAPI_(void) AppCompat_RunDLLW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
  2071. {
  2072. TCHAR szCmd[MAX_PATH];
  2073. LPTSTR pszArgs;
  2074. lstrcpyn(szCmd, lpwszCmdLine, ARRAYSIZE(szCmd));
  2075. pszArgs = PathGetArgs(szCmd);
  2076. PathRemoveArgs(szCmd);
  2077. if (lstrcmpi(szCmd, L"SCANDSKW") == 0) {
  2078. DialogBoxParam(g_hinst, MAKEINTRESOURCE(IDD_SCANDSKW), NULL,
  2079. ScanDskW_DlgProc, (LPARAM)pszArgs);
  2080. }
  2081. }