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.

888 lines
24 KiB

  1. #include <nt.h>
  2. #include <ntrtl.h>
  3. #include <nturtl.h>
  4. #include <ntioapi.h>
  5. #include "diskcopy.h"
  6. #include "ids.h"
  7. #include "help.h"
  8. // SHChangeNotifySuspendResume
  9. #include <shlobjp.h>
  10. #define WM_DONE_WITH_FORMAT (WM_USER + 100)
  11. // DISKINFO Struct
  12. // Revisions: 02/04/98 dsheldon - added bDestInserted
  13. typedef struct
  14. {
  15. int nSrcDrive;
  16. int nDestDrive;
  17. UINT nCylinderSize;
  18. UINT nCylinders;
  19. UINT nHeads;
  20. UINT nSectorsPerTrack;
  21. UINT nSectorSize;
  22. BOOL bNotifiedWriting;
  23. BOOL bFormatTried;
  24. HWND hdlg;
  25. HANDLE hThread;
  26. BOOL bUserAbort;
  27. DWORD dwError;
  28. BOOL bDestInserted;
  29. } DISKINFO, *PDISKINFO;
  30. int ErrorMessageBox(DISKINFO* pdi, UINT uFlags);
  31. void SetStatusText(DISKINFO* pdi, int id);
  32. BOOL PromptInsertDisk(DISKINFO *pdi, LPCTSTR lpsz, BOOL fAutoCheck);
  33. typedef struct _fmifs {
  34. HINSTANCE hDll;
  35. PFMIFS_DISKCOPY_ROUTINE DiskCopy;
  36. } FMIFS;
  37. typedef FMIFS *PFMIFS;
  38. BOOL LoadFMIFS(PFMIFS pFMIFS)
  39. {
  40. //
  41. // Load the FMIFS DLL and query for the entry points we need
  42. //
  43. pFMIFS->hDll = LoadLibrary(TEXT("FMIFS.DLL"));
  44. if (NULL == pFMIFS->hDll)
  45. return FALSE;
  46. pFMIFS->DiskCopy = (PFMIFS_DISKCOPY_ROUTINE)GetProcAddress(pFMIFS->hDll,
  47. "DiskCopy");
  48. if (NULL == pFMIFS->DiskCopy)
  49. {
  50. FreeLibrary(pFMIFS->hDll);
  51. pFMIFS->hDll = NULL;
  52. return FALSE;
  53. }
  54. return TRUE;
  55. }
  56. void UnloadFMIFS(PFMIFS pFMIFS)
  57. {
  58. FreeLibrary(pFMIFS->hDll);
  59. pFMIFS->hDll = NULL;
  60. pFMIFS->DiskCopy = NULL;
  61. }
  62. //
  63. // Thread-Local Storage index for our DISKINFO structure pointer
  64. //
  65. static DWORD g_iTLSDiskInfo = 0;
  66. static LONG g_cTLSDiskInfo = 0; // Usage count
  67. __inline void UnstuffDiskInfoPtr()
  68. {
  69. if (InterlockedDecrement(&g_cTLSDiskInfo) == 0)
  70. {
  71. TlsFree(g_iTLSDiskInfo);
  72. g_iTLSDiskInfo = 0;
  73. }
  74. }
  75. BOOL StuffDiskInfoPtr(PDISKINFO pDiskInfo)
  76. {
  77. //
  78. // Allocate an index slot for our thread-local DISKINFO pointer, if one
  79. // doesn't already exist, then stuff our DISKINFO ptr at that index.
  80. //
  81. if (0 == g_iTLSDiskInfo)
  82. {
  83. if (0xFFFFFFFF == (g_iTLSDiskInfo = TlsAlloc()))
  84. {
  85. return FALSE;
  86. }
  87. g_cTLSDiskInfo = 0;
  88. }
  89. InterlockedIncrement(&g_cTLSDiskInfo);
  90. if (!TlsSetValue(g_iTLSDiskInfo, (LPVOID) pDiskInfo))
  91. {
  92. UnstuffDiskInfoPtr();
  93. return FALSE;
  94. }
  95. return TRUE;
  96. }
  97. __inline PDISKINFO GetDiskInfoPtr()
  98. {
  99. return (PDISKINFO)TlsGetValue(g_iTLSDiskInfo);
  100. }
  101. // DriveNumFromDriveLetterW: Return a drive number given a pointer to
  102. // a unicode drive letter.
  103. // 02/03/98: dsheldon created
  104. int DriveNumFromDriveLetterW(wchar_t* pwchDrive)
  105. {
  106. Assert(pwchDrive != NULL);
  107. return ( ((int) *pwchDrive) - ((int) L'A') );
  108. }
  109. /*
  110. Function: CopyDiskCallback
  111. Return Value:
  112. TRUE - Normally, TRUE should be returned if the Disk Copy procedure should
  113. continue after CopyDiskCallback returns. Note the HACK below, however!
  114. FALSE - Normally, this indicates that the Disk Copy procedure should be
  115. cancelled.
  116. !HACKHACK!
  117. The low-level Disk Copy procedure that invokes this callback is also used
  118. by the command-line DiskCopy utility. That utility's implementation of the
  119. callback always returns TRUE. For this reason, the low-level Disk Copy
  120. procedure will interpret TRUE as CANCEL when it is returned from callbacks
  121. that display a message box and allow the user to possibly RETRY an operation.
  122. Therefore, return TRUE after handling such messages to tell the Disk Copy
  123. procedure to abort, and return FALSE to tell Disk Copy to retry.
  124. TRUE still means 'continue' when returned from PercentComplete or Disk Insertion
  125. messages.
  126. Revision:
  127. 02/03/98: dsheldon - modified code to handle retry/cancel for bad media,
  128. write protected media, and disk being yanked out of drive during copy
  129. */
  130. BOOLEAN CopyDiskCallback( FMIFS_PACKET_TYPE PacketType, DWORD PacketLength, PVOID PacketData)
  131. {
  132. PDISKINFO pdi = GetDiskInfoPtr();
  133. int iDisk;
  134. // Quit if told to do so..
  135. if (pdi->bUserAbort)
  136. return FALSE;
  137. switch (PacketType) {
  138. case FmIfsPercentCompleted:
  139. {
  140. DWORD dwPercent = ((PFMIFS_PERCENT_COMPLETE_INFORMATION)
  141. PacketData)->PercentCompleted;
  142. //
  143. // Hokey method of determining "writing"
  144. //
  145. if (dwPercent > 50 && !pdi->bNotifiedWriting)
  146. {
  147. pdi->bNotifiedWriting = TRUE;
  148. SetStatusText(pdi, IDS_WRITING);
  149. }
  150. SendDlgItemMessage(pdi->hdlg, IDD_PROBAR, PBM_SETPOS, dwPercent,0);
  151. break;
  152. }
  153. case FmIfsInsertDisk:
  154. switch(((PFMIFS_INSERT_DISK_INFORMATION)PacketData)->DiskType) {
  155. case DISK_TYPE_SOURCE:
  156. case DISK_TYPE_GENERIC:
  157. iDisk = IDS_INSERTSRC;
  158. break;
  159. case DISK_TYPE_TARGET:
  160. iDisk = IDS_INSERTDEST;
  161. pdi->bDestInserted = TRUE;
  162. break;
  163. case DISK_TYPE_SOURCE_AND_TARGET:
  164. iDisk = IDS_INSERTSRCDEST;
  165. break;
  166. }
  167. if (!PromptInsertDisk(pdi, MAKEINTRESOURCE(iDisk), FALSE)) {
  168. pdi->bUserAbort = TRUE;
  169. return FALSE;
  170. }
  171. break;
  172. case FmIfsFormattingDestination:
  173. pdi->bNotifiedWriting = FALSE; // Reset so we get Writing later
  174. SetStatusText(pdi, IDS_FORMATTINGDEST);
  175. break;
  176. case FmIfsIncompatibleFileSystem:
  177. case FmIfsIncompatibleMedia:
  178. pdi->dwError = IDS_COPYSRCDESTINCOMPAT;
  179. if (ErrorMessageBox(pdi, MB_RETRYCANCEL | MB_ICONERROR) == IDRETRY)
  180. {
  181. pdi->dwError = 0;
  182. return FALSE; //Indicates RETRY - see HACK in function header
  183. }
  184. else
  185. {
  186. return TRUE;
  187. }
  188. break;
  189. case FmIfsMediaWriteProtected:
  190. pdi->dwError = IDS_DSTDISKBAD;
  191. if (ErrorMessageBox(pdi, MB_RETRYCANCEL | MB_ICONERROR) == IDRETRY)
  192. {
  193. pdi->dwError = 0;
  194. return FALSE; //Indicates RETRY - see HACK in function header
  195. }
  196. else
  197. {
  198. return TRUE;
  199. }
  200. break;
  201. case FmIfsCantLock:
  202. pdi->dwError = IDS_ERROR_GENERAL;
  203. ErrorMessageBox(pdi, MB_OK | MB_ICONERROR);
  204. return FALSE;
  205. case FmIfsAccessDenied:
  206. pdi->dwError = IDS_SRCDISKBAD;
  207. ErrorMessageBox(pdi, MB_OK | MB_ICONERROR);
  208. return FALSE;
  209. case FmIfsBadLabel:
  210. case FmIfsCantQuickFormat:
  211. pdi->dwError = IDS_ERROR_GENERAL;
  212. ErrorMessageBox(pdi, MB_OK | MB_ICONERROR);
  213. return FALSE;
  214. case FmIfsIoError:
  215. switch(((PFMIFS_IO_ERROR_INFORMATION)PacketData)->DiskType) {
  216. case DISK_TYPE_SOURCE:
  217. pdi->dwError = IDS_SRCDISKBAD;
  218. break;
  219. case DISK_TYPE_TARGET:
  220. pdi->dwError = IDS_DSTDISKBAD;
  221. break;
  222. default:
  223. // BobDay - We should never get this!!
  224. Assert(0);
  225. pdi->dwError = IDS_ERROR_GENERAL;
  226. break;
  227. }
  228. if (ErrorMessageBox(pdi, MB_RETRYCANCEL | MB_ICONERROR) == IDRETRY)
  229. {
  230. pdi->dwError = 0;
  231. return FALSE; //Indicates RETRY - see HACK in function header
  232. }
  233. else
  234. {
  235. return TRUE;
  236. }
  237. break;
  238. case FmIfsNoMediaInDevice:
  239. {
  240. // Note that we get a pointer to the unicode
  241. // drive letter in the PacketData argument
  242. // If the drives are the same, determine if we are
  243. // reading or writing with the "dest inserted" flag
  244. if (pdi->nSrcDrive == pdi->nDestDrive)
  245. {
  246. if (pdi->bDestInserted)
  247. pdi->dwError = IDS_ERROR_WRITE;
  248. else
  249. pdi->dwError = IDS_ERROR_READ;
  250. }
  251. else
  252. {
  253. // Otherwise, use the drive letter to determine this
  254. // ...Check if we're reading or writing
  255. int nDrive = DriveNumFromDriveLetterW(
  256. (wchar_t*) PacketData);
  257. Assert ((nDrive == pdi->nSrcDrive) ||
  258. (nDrive == pdi->nDestDrive));
  259. // Check if the source or dest disk was removed and set
  260. // error accordingly
  261. if (nDrive == pdi->nDestDrive)
  262. pdi->dwError = IDS_ERROR_WRITE;
  263. else
  264. pdi->dwError = IDS_ERROR_READ;
  265. }
  266. if (ErrorMessageBox(pdi, MB_RETRYCANCEL | MB_ICONERROR) == IDRETRY)
  267. {
  268. pdi->dwError = 0;
  269. // Note that FALSE is returned here to indicate RETRY
  270. // See HACK in the function header for explanation.
  271. return FALSE;
  272. }
  273. else
  274. {
  275. return TRUE;
  276. }
  277. }
  278. break;
  279. case FmIfsFinished:
  280. if (((PFMIFS_FINISHED_INFORMATION)PacketData)->Success)
  281. {
  282. pdi->dwError = 0;
  283. }
  284. else
  285. {
  286. pdi->dwError = IDS_ERROR_GENERAL;
  287. }
  288. break;
  289. default:
  290. break;
  291. }
  292. return TRUE;
  293. }
  294. // nDrive == 0-based drive number (a: == 0)
  295. LPITEMIDLIST GetDrivePidl(HWND hwnd, int nDrive)
  296. {
  297. TCHAR szDrive[4];
  298. PathBuildRoot(szDrive, nDrive);
  299. LPITEMIDLIST pidl = NULL;
  300. SHParseDisplayName(szDrive, NULL, &pidl, 0, NULL);
  301. return pidl;
  302. }
  303. DWORD CALLBACK CopyDiskThreadProc(LPVOID lpParam)
  304. {
  305. DISKINFO *pdi = (DISKINFO *)lpParam;
  306. FMIFS fmifs;
  307. LPITEMIDLIST pidlSrc = NULL;
  308. LPITEMIDLIST pidlDest = NULL;
  309. HWND hwndProgress = GetDlgItem(pdi->hdlg, IDD_PROBAR);
  310. // Disable change notifications for the src drive
  311. pidlSrc = GetDrivePidl(pdi->hdlg, pdi->nSrcDrive);
  312. if (NULL != pidlSrc)
  313. {
  314. SHChangeNotifySuspendResume(TRUE, pidlSrc, TRUE, 0);
  315. }
  316. if (pdi->nSrcDrive != pdi->nDestDrive)
  317. {
  318. // Do the same for the dest drive since they're different
  319. pidlDest = GetDrivePidl(pdi->hdlg, pdi->nDestDrive);
  320. if (NULL != pidlDest)
  321. {
  322. SHChangeNotifySuspendResume(TRUE, pidlDest, TRUE, 0);
  323. }
  324. }
  325. // Change notifications are disabled; do the copy
  326. EnableWindow(GetDlgItem(pdi->hdlg, IDD_FROM), FALSE);
  327. EnableWindow(GetDlgItem(pdi->hdlg, IDD_TO), FALSE);
  328. PostMessage(hwndProgress, PBM_SETRANGE, 0, MAKELONG(0, 100));
  329. pdi->bFormatTried = FALSE;
  330. pdi->bNotifiedWriting = FALSE;
  331. pdi->dwError = 0;
  332. pdi->bDestInserted = FALSE;
  333. if (StuffDiskInfoPtr(pdi) && LoadFMIFS(&fmifs))
  334. {
  335. TCHAR szSource[3];
  336. TCHAR szDestination[3];
  337. //
  338. // Now copy the disk
  339. //
  340. szSource[0] = TEXT('A') + pdi->nSrcDrive;
  341. szSource[1] = TEXT(':');
  342. szSource[2] = 0;
  343. szDestination[0] = TEXT('A') + pdi->nDestDrive;
  344. szDestination[1] = TEXT(':');
  345. szDestination[2] = 0;
  346. SetStatusText(pdi, IDS_READING);
  347. fmifs.DiskCopy(szSource, szDestination, FALSE, CopyDiskCallback);
  348. UnstuffDiskInfoPtr();
  349. UnloadFMIFS(&fmifs);
  350. }
  351. PostMessage(pdi->hdlg, WM_DONE_WITH_FORMAT, 0, 0);
  352. // Resume any shell notifications we've suspended and free
  353. // our pidls (and send updatedir notifications while we're at
  354. // it)
  355. if (NULL != pidlSrc)
  356. {
  357. SHChangeNotifySuspendResume(FALSE, pidlSrc, TRUE, 0);
  358. SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidlSrc, NULL);
  359. ILFree(pidlSrc);
  360. pidlSrc = NULL;
  361. }
  362. if (NULL != pidlDest)
  363. {
  364. Assert(pdi->nSrcDrive != pdi->nDestDrive);
  365. SHChangeNotifySuspendResume(FALSE, pidlDest, TRUE, 0);
  366. SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidlDest, NULL);
  367. ILFree(pidlDest);
  368. pidlDest = NULL;
  369. }
  370. return 0;
  371. }
  372. HANDLE _GetDeviceHandle(LPTSTR psz, DWORD dwDesiredAccess, DWORD dwFileAttributes)
  373. {
  374. HANDLE hDevice = CreateFile(psz, // drive to open
  375. dwDesiredAccess, // don't need any access to the drive
  376. FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
  377. NULL, // default security attributes
  378. OPEN_EXISTING, // disposition
  379. dwFileAttributes, // file attributes
  380. NULL); // don't copy any file's attributes
  381. return hDevice;
  382. }
  383. BOOL DriveIdIsFloppy(int iDrive)
  384. {
  385. BOOL fRetVal = FALSE;
  386. HANDLE hDevice;
  387. UINT i;
  388. TCHAR szTemp[] = TEXT("\\\\.\\a:");
  389. if (iDrive >= 0 && iDrive < 26)
  390. {
  391. szTemp[4] += (TCHAR)iDrive;
  392. hDevice = _GetDeviceHandle(szTemp, FILE_READ_ATTRIBUTES, 0); // use this flag important
  393. if (INVALID_HANDLE_VALUE != hDevice)
  394. {
  395. NTSTATUS status;
  396. FILE_FS_DEVICE_INFORMATION DeviceInfo;
  397. IO_STATUS_BLOCK ioStatus;
  398. status = NtQueryVolumeInformationFile(hDevice, &ioStatus, &DeviceInfo, sizeof(DeviceInfo), FileFsDeviceInformation);
  399. if ((NT_SUCCESS(status)) &&
  400. (FILE_DEVICE_DISK & DeviceInfo.DeviceType) &&
  401. (FILE_FLOPPY_DISKETTE & DeviceInfo.Characteristics))
  402. {
  403. fRetVal = TRUE;
  404. }
  405. CloseHandle (hDevice);
  406. }
  407. }
  408. return fRetVal;
  409. }
  410. int ErrorMessageBox(DISKINFO* pdi, UINT uFlags)
  411. {
  412. if (!pdi->bUserAbort && pdi->dwError) {
  413. TCHAR szTemp[1024];
  414. DWORD dwLastError = GetLastError();
  415. if (dwLastError) {
  416. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, szTemp, ARRAYSIZE(szTemp), NULL);
  417. } else {
  418. LoadString(g_hinst, (int)pdi->dwError, szTemp, ARRAYSIZE(szTemp));
  419. }
  420. // if the user didn't abort and it didn't complete normally, post an error box
  421. return ShellMessageBox(g_hinst, pdi->hdlg, szTemp, NULL, uFlags);
  422. } else
  423. return -1;
  424. }
  425. void SetStatusText(DISKINFO* pdi, int id)
  426. {
  427. TCHAR szMsg[128];
  428. LoadString(g_hinst, id, szMsg, ARRAYSIZE(szMsg));
  429. SendDlgItemMessage(pdi->hdlg, IDD_STATUS, WM_SETTEXT, 0, (LPARAM)szMsg);
  430. }
  431. BOOL PromptInsertDisk(DISKINFO *pdi, LPCTSTR lpsz, BOOL fAutoCheck)
  432. {
  433. if (fAutoCheck)
  434. goto AutoCheckBegin;
  435. for (;;) {
  436. DWORD dwLastErrorSrc = 0;
  437. DWORD dwLastErrorDest = 0 ;
  438. TCHAR szPath[4];
  439. if (ShellMessageBox(g_hinst, pdi->hdlg, lpsz, NULL, MB_OKCANCEL | MB_ICONINFORMATION) != IDOK) {
  440. pdi->bUserAbort = TRUE;
  441. return FALSE;
  442. }
  443. AutoCheckBegin:
  444. szPath[0] = TEXT('A') + pdi->nSrcDrive;
  445. szPath[1] = TEXT(':');
  446. szPath[2] = TEXT('\\');
  447. szPath[3] = 0;
  448. // make sure both disks are in
  449. if (GetFileAttributes(szPath) == (UINT)-1)
  450. dwLastErrorDest = GetLastError();
  451. if (pdi->nDestDrive != pdi->nSrcDrive) {
  452. szPath[0] = TEXT('A') + pdi->nDestDrive;
  453. if (GetFileAttributes(szPath) == (UINT)-1)
  454. dwLastErrorDest = GetLastError();
  455. }
  456. if (dwLastErrorDest != ERROR_NOT_READY &&
  457. dwLastErrorSrc != ERROR_NOT_READY)
  458. break;
  459. }
  460. return TRUE;
  461. }
  462. HICON GetDriveInfo(int nDrive, LPTSTR pszName)
  463. {
  464. HICON hIcon = NULL;
  465. SHFILEINFO shfi;
  466. TCHAR szRoot[4];
  467. *pszName = 0;
  468. if (PathBuildRoot(szRoot, nDrive))
  469. {
  470. if (SHGetFileInfo(szRoot, FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi),
  471. SHGFI_ICON | SHGFI_SMALLICON | SHGFI_DISPLAYNAME)) // | SHGFI_USEFILEATTRIBUTES
  472. {
  473. lstrcpy(pszName, shfi.szDisplayName);
  474. hIcon = shfi.hIcon;
  475. }
  476. else
  477. {
  478. lstrcpy(pszName, szRoot);
  479. }
  480. }
  481. return hIcon;
  482. }
  483. int AddDriveToListView(HWND hwndLV, int nDrive, int nDefaultDrive)
  484. {
  485. TCHAR szDriveName[64];
  486. LV_ITEM item;
  487. HICON hicon = GetDriveInfo(nDrive, szDriveName);
  488. HIMAGELIST himlSmall = ListView_GetImageList(hwndLV, LVSIL_SMALL);
  489. Assert(himlSmall);
  490. if (hicon)
  491. {
  492. item.iImage = ImageList_AddIcon(himlSmall, hicon);
  493. DestroyIcon(hicon);
  494. }
  495. else
  496. item.iImage = 0;
  497. item.mask = nDrive == nDefaultDrive ?
  498. LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE :
  499. LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
  500. item.stateMask = item.state = LVIS_SELECTED | LVIS_FOCUSED;
  501. item.iItem = 26; // add at end
  502. item.iSubItem = 0;
  503. item.pszText = szDriveName;
  504. item.lParam = (LPARAM)nDrive;
  505. return ListView_InsertItem(hwndLV, &item);
  506. }
  507. int GetSelectedDrive(HWND hwndLV)
  508. {
  509. LV_ITEM item;
  510. item.iItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED);
  511. if (item.iItem >= 0)
  512. {
  513. item.mask = LVIF_PARAM;
  514. item.iSubItem = 0;
  515. ListView_GetItem(hwndLV, &item);
  516. return (int)item.lParam;
  517. }
  518. // implicitly selected the 0th item
  519. ListView_SetItemState(hwndLV, 0, LVIS_SELECTED, LVIS_SELECTED);
  520. return 0;
  521. }
  522. void InitSingleColListView(HWND hwndLV)
  523. {
  524. LV_COLUMN col = {LVCF_FMT | LVCF_WIDTH, LVCFMT_LEFT};
  525. RECT rc;
  526. GetClientRect(hwndLV, &rc);
  527. col.cx = rc.right;
  528. // - GetSystemMetrics(SM_CXVSCROLL)
  529. // - GetSystemMetrics(SM_CXSMICON)
  530. // - 2 * GetSystemMetrics(SM_CXEDGE);
  531. ListView_InsertColumn(hwndLV, 0, &col);
  532. }
  533. #define g_cxSmIcon GetSystemMetrics(SM_CXSMICON)
  534. void PopulateListView(HWND hDlg, DISKINFO *pdi)
  535. {
  536. HWND hwndFrom = GetDlgItem(hDlg, IDD_FROM);
  537. HWND hwndTo = GetDlgItem(hDlg, IDD_TO);
  538. int iDrive;
  539. ListView_DeleteAllItems(hwndFrom);
  540. ListView_DeleteAllItems(hwndTo);
  541. for (iDrive = 0; iDrive < 26; iDrive++)
  542. {
  543. if (DriveIdIsFloppy(iDrive))
  544. {
  545. AddDriveToListView(hwndFrom, iDrive, pdi->nSrcDrive);
  546. AddDriveToListView(hwndTo, iDrive, pdi->nDestDrive);
  547. }
  548. }
  549. }
  550. void CopyDiskInitDlg(HWND hDlg, DISKINFO *pdi)
  551. {
  552. int iDrive;
  553. HWND hwndFrom = GetDlgItem(hDlg, IDD_FROM);
  554. HWND hwndTo = GetDlgItem(hDlg, IDD_TO);
  555. HIMAGELIST himl;
  556. SetWindowLongPtr(hDlg, DWLP_USER, (LPARAM)pdi);
  557. SendMessage(hDlg, WM_SETICON, 0, (LPARAM)LoadImage(GetWindowInstance(hDlg), MAKEINTRESOURCE(IDI_DISKCOPY), IMAGE_ICON, 16, 16, 0));
  558. SendMessage(hDlg, WM_SETICON, 1, (LPARAM)LoadIcon(GetWindowInstance(hDlg), MAKEINTRESOURCE(IDI_DISKCOPY)));
  559. pdi->hdlg = hDlg;
  560. InitSingleColListView(hwndFrom);
  561. InitSingleColListView(hwndTo);
  562. himl = ImageList_Create(g_cxSmIcon, g_cxSmIcon, ILC_MASK, 1, 4);
  563. if (himl)
  564. {
  565. // NOTE: only one of these is not marked LVS_SHAREIMAGELIST
  566. // so it will only be destroyed once
  567. ListView_SetImageList(hwndFrom, himl, LVSIL_SMALL);
  568. ListView_SetImageList(hwndTo, himl, LVSIL_SMALL);
  569. }
  570. PopulateListView(hDlg, pdi);
  571. }
  572. DWORD _inline WaitForThreadDeath(HANDLE hThread)
  573. {
  574. MSG msg;
  575. DWORD result = WAIT_FAILED;
  576. if (hThread) {
  577. while(TRUE) {
  578. result = MsgWaitForMultipleObjects(1, &hThread, FALSE, 5000, QS_SENDMESSAGE);
  579. switch (result) {
  580. default:
  581. case WAIT_OBJECT_0:
  582. case WAIT_FAILED:
  583. return result;
  584. case WAIT_TIMEOUT:
  585. TerminateThread(hThread, (DWORD)-1);
  586. return result;
  587. case WAIT_OBJECT_0 + 1:
  588. PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  589. break;
  590. }
  591. }
  592. }
  593. return(result);
  594. }
  595. void SetCancelButtonText(HWND hDlg, int id)
  596. {
  597. TCHAR szText[80];
  598. LoadString(g_hinst, id, szText, ARRAYSIZE(szText));
  599. SetDlgItemText(hDlg, IDCANCEL, szText);
  600. }
  601. void DoneWithFormat(DISKINFO* pdi)
  602. {
  603. int id;
  604. EnableWindow(GetDlgItem(pdi->hdlg, IDD_FROM), TRUE);
  605. EnableWindow(GetDlgItem(pdi->hdlg, IDD_TO), TRUE);
  606. PopulateListView(pdi->hdlg, pdi);
  607. #ifndef WINNT
  608. // unlock the drives
  609. LockDrive(pdi->nSrcDrive, FALSE, 0 );
  610. if (pdi->nSrcDrive != pdi->nDestDrive)
  611. LockDrive(pdi->nDestDrive, FALSE, 0);
  612. #endif
  613. SendDlgItemMessage(pdi->hdlg, IDD_PROBAR, PBM_SETPOS, 0, 0);
  614. EnableWindow(GetDlgItem(pdi->hdlg, IDOK), TRUE);
  615. CloseHandle(pdi->hThread);
  616. SetCancelButtonText(pdi->hdlg, IDS_CLOSE);
  617. pdi->hThread = NULL;
  618. if (pdi->bUserAbort) {
  619. id = IDS_COPYABORTED;
  620. } else {
  621. switch (pdi->dwError) {
  622. case 0:
  623. id = IDS_COPYCOMPLETED;
  624. break;
  625. default:
  626. id = IDS_COPYFAILED;
  627. break;
  628. }
  629. }
  630. SetStatusText(pdi, id);
  631. SetCancelButtonText(pdi->hdlg, IDS_CLOSE);
  632. // reset variables
  633. pdi->dwError = 0;
  634. pdi->bUserAbort = 0;
  635. }
  636. #pragma data_seg(".text")
  637. const static DWORD aCopyDiskHelpIDs[] = { // Context Help IDs
  638. IDOK, IDH_DISKCOPY_START,
  639. IDD_FROM, IDH_DISKCOPY_FROM,
  640. IDD_TO, IDH_DISKCOPY_TO,
  641. IDD_STATUS, NO_HELP,
  642. IDD_PROBAR, NO_HELP,
  643. 0, 0
  644. };
  645. #pragma data_seg()
  646. INT_PTR CALLBACK CopyDiskDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  647. {
  648. DISKINFO *pdi = (DISKINFO *)GetWindowLongPtr(hDlg, DWLP_USER);
  649. switch (uMsg) {
  650. case WM_INITDIALOG:
  651. CopyDiskInitDlg(hDlg, (DISKINFO *)lParam);
  652. break;
  653. case WM_DONE_WITH_FORMAT:
  654. DoneWithFormat(pdi);
  655. break;
  656. case WM_HELP:
  657. WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
  658. HELP_WM_HELP, (DWORD_PTR)(LPTSTR) aCopyDiskHelpIDs);
  659. return TRUE;
  660. case WM_CONTEXTMENU:
  661. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
  662. (DWORD_PTR)(LPVOID) aCopyDiskHelpIDs);
  663. return TRUE;
  664. case WM_COMMAND:
  665. switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  666. case IDCANCEL:
  667. // if there's az hThread that means we're in copy mode, abort
  668. // from that, otherwise, it means quit the dialog completely
  669. if (pdi->hThread)
  670. {
  671. pdi->bUserAbort = TRUE;
  672. // do a Msgwaitformultiple so that we don't
  673. // get blocked with them sending us a message
  674. if (WaitForThreadDeath(pdi->hThread) == WAIT_TIMEOUT)
  675. DoneWithFormat(pdi);
  676. CloseHandle(pdi->hThread);
  677. pdi->hThread = NULL;
  678. }
  679. else
  680. EndDialog(hDlg, IDCANCEL);
  681. break;
  682. case IDOK:
  683. {
  684. DWORD idThread;
  685. SetLastError(0);
  686. EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
  687. // set cancel button to "Cancel"
  688. SetCancelButtonText(hDlg, IDS_CANCEL);
  689. pdi->nSrcDrive = GetSelectedDrive(GetDlgItem(hDlg, IDD_FROM));
  690. pdi->nDestDrive = GetSelectedDrive(GetDlgItem(hDlg, IDD_TO));
  691. // remove all items except the drives we're using
  692. ListView_DeleteAllItems(GetDlgItem(hDlg, IDD_FROM));
  693. ListView_DeleteAllItems(GetDlgItem(hDlg, IDD_TO));
  694. AddDriveToListView(GetDlgItem(hDlg, IDD_FROM), pdi->nSrcDrive, pdi->nSrcDrive);
  695. AddDriveToListView(GetDlgItem(hDlg, IDD_TO), pdi->nDestDrive, pdi->nDestDrive);
  696. pdi->bUserAbort = FALSE;
  697. SendDlgItemMessage(hDlg, IDD_PROBAR, PBM_SETPOS, 0, 0);
  698. SendDlgItemMessage(pdi->hdlg, IDD_STATUS, WM_SETTEXT, 0, 0);
  699. Assert(pdi->hThread == NULL);
  700. pdi->hThread = CreateThread(NULL, 0, CopyDiskThreadProc, pdi, 0, &idThread);
  701. }
  702. break;
  703. }
  704. break;
  705. default:
  706. return FALSE;
  707. }
  708. return TRUE;
  709. }
  710. int SHCopyDisk(HWND hwnd, int nSrcDrive, int nDestDrive, DWORD dwFlags)
  711. {
  712. DISKINFO di;
  713. memset(&di, 0, sizeof(di));
  714. di.nSrcDrive = nSrcDrive;
  715. di.nDestDrive = nDestDrive;
  716. return (int)DialogBoxParam(g_hinst, MAKEINTRESOURCE(DLG_DISKCOPYPROGRESS), hwnd, CopyDiskDlgProc, (LPARAM)&di);
  717. }