Leaked source code of windows server 2003
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.

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