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.

2020 lines
55 KiB

  1. /*++
  2. Copyright (c) 1995-2000 Microsoft Corporation
  3. Module Name:
  4. fileq5.c
  5. Abstract:
  6. Default queue callback function.
  7. Author:
  8. Ted Miller (tedm) 24-Feb-1995
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. #define QUEUECONTEXT_SIGNATURE (DWORD)(0x43515053) // 'CQPS'
  14. typedef struct _QUEUECONTEXT {
  15. DWORD Signature; // an attempt to catch re-use of deleted queuecontext
  16. HWND OwnerWindow;
  17. DWORD MainThreadId;
  18. HWND ProgressDialog;
  19. HWND ProgressBar;
  20. BOOL Cancelled;
  21. PTSTR CurrentSourceName;
  22. BOOL ScreenReader;
  23. BOOL MessageBoxUp;
  24. WPARAM PendingUiType;
  25. PVOID PendingUiParameters;
  26. UINT CancelReturnCode;
  27. BOOL DialogKilled;
  28. //
  29. // If the SetupInitDefaultQueueCallbackEx is used, the caller can
  30. // specify an alternate handler for progress. This is useful to
  31. // get the default behavior for disk prompting, error handling, etc,
  32. // but to provide a gas gauge embedded, say, in a wizard page.
  33. //
  34. // The alternate window is sent ProgressMsg once when the copy queue
  35. // is started (wParam = 0. lParam = number of files to copy).
  36. // It is then also sent once per file copied (wParam = 1. lParam = 0).
  37. //
  38. // NOTE: a silent installation (i.e., no progress UI) can be accomplished
  39. // by specifying an AlternateProgressWindow handle of INVALID_HANDLE_VALUE.
  40. //
  41. HWND AlternateProgressWindow;
  42. UINT ProgressMsg;
  43. UINT NoToAllMask;
  44. HANDLE UiThreadHandle;
  45. //
  46. // instead of posting responses to main thread, use an event with flags
  47. //
  48. HANDLE hEvent;
  49. BOOL bDialogExited;
  50. LPARAM lParam;
  51. #ifdef NOCANCEL_SUPPORT
  52. BOOL AllowCancel;
  53. #endif
  54. } QUEUECONTEXT, *PQUEUECONTEXT;
  55. typedef struct _VERDLGCONTEXT {
  56. PQUEUECONTEXT QueueContext;
  57. UINT Notification;
  58. UINT_PTR Param1;
  59. UINT_PTR Param2;
  60. } VERDLGCONTEXT, *PVERDLGCONTEXT;
  61. #define WMX_PROGRESSTHREAD (WM_APP+0)
  62. #define WMX_KILLDIALOG (WM_APP+1)
  63. #define WMX_HELLO (WM_APP+2)
  64. #define WMX_PERFORMUI (WM_APP+3)
  65. #define UI_NONE 0
  66. #define UI_COPYERROR 1
  67. #define UI_DELETEERROR 2
  68. #define UI_RENAMEERROR 3
  69. #define UI_NEEDMEDIA 4
  70. #define UI_MISMATCHERROR 5
  71. #define UI_BACKUPERROR 6
  72. typedef struct _COPYERRORUI {
  73. TCHAR Buffer[MAX_PATH];
  74. PTCHAR Filename;
  75. PFILEPATHS FilePaths;
  76. DWORD Flags;
  77. PTSTR PathOut;
  78. } COPYERRORUI, *PCOPYERRORUI;
  79. typedef struct _NEEDMEDIAUI {
  80. PSOURCE_MEDIA SourceMedia;
  81. DWORD Flags;
  82. PTSTR PathOut;
  83. } NEEDMEDIAUI, *PNEEDMEDIAUI;
  84. PCTSTR DialogPropName = TEXT("_context");
  85. INT_PTR
  86. pSetupProgressDlgProc(
  87. IN HWND hdlg,
  88. IN UINT msg,
  89. IN WPARAM wParam,
  90. IN LPARAM lParam
  91. );
  92. LPARAM
  93. pPerformUi (
  94. IN PQUEUECONTEXT Context,
  95. IN UINT UiType,
  96. IN PVOID UiParameters
  97. );
  98. VOID
  99. __cdecl
  100. pSetupProgressThread(
  101. IN PVOID Context
  102. )
  103. /*++
  104. Routine Description:
  105. Thread entry point for setup file progress indicator.
  106. Puts up a dialog box.
  107. Arguments:
  108. Context - supplies queue context.
  109. Return Value:
  110. 0 if unsuccessful, non-0 if successful.
  111. --*/
  112. {
  113. PQUEUECONTEXT context;
  114. INT_PTR i;
  115. MSG msg;
  116. //
  117. // Force this thread to have a message queue, just in case.
  118. //
  119. PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);
  120. //
  121. // The thread parameter is the queue context.
  122. //
  123. context = Context;
  124. //
  125. // Create the progress dialog box.
  126. //
  127. i = DialogBoxParam(
  128. MyDllModuleHandle,
  129. MAKEINTRESOURCE(IDD_FILEPROGRESS),
  130. context->OwnerWindow,
  131. pSetupProgressDlgProc,
  132. (LPARAM)context
  133. );
  134. //
  135. // flag that this is the very last time hEvent will be set
  136. //
  137. context->bDialogExited = TRUE;
  138. SetEvent(context->hEvent);
  139. //
  140. // Done.
  141. //
  142. _endthread();
  143. }
  144. BOOL
  145. pWaitForUiResponse(
  146. IN OUT PQUEUECONTEXT Context
  147. )
  148. /*++
  149. Routine Description:
  150. Waits for UI event to be set
  151. Arguments:
  152. Context - supplies queue-context structure
  153. Return Value:
  154. FALSE = failure
  155. --*/
  156. {
  157. BOOL KeepWaiting = TRUE;
  158. DWORD WaitProcStatus;
  159. if (Context->hEvent == NULL) {
  160. MYASSERT(Context->hEvent);
  161. return FALSE;
  162. }
  163. if (Context->bDialogExited) {
  164. //
  165. // dialog has already exited, we wont get another event
  166. //
  167. return FALSE;
  168. }
  169. while (KeepWaiting) {
  170. WaitProcStatus = MyMsgWaitForMultipleObjectsEx(
  171. 1,
  172. &Context->hEvent,
  173. INFINITE,
  174. QS_ALLINPUT,
  175. MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
  176. switch (WaitProcStatus) {
  177. case WAIT_OBJECT_0 + 1: { // Process gui messages
  178. MSG msg;
  179. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  180. TranslateMessage(&msg);
  181. DispatchMessage(&msg);
  182. }
  183. // fall through ...
  184. }
  185. case WAIT_IO_COMPLETION:
  186. break;
  187. case WAIT_OBJECT_0:
  188. case WAIT_TIMEOUT:
  189. default:
  190. KeepWaiting = FALSE;
  191. break;
  192. }
  193. }
  194. return TRUE;
  195. }
  196. UINT
  197. PostUiMessage (
  198. IN PQUEUECONTEXT Context,
  199. IN UINT UiType,
  200. IN UINT CancelCode,
  201. IN OUT PVOID UiParameters
  202. )
  203. {
  204. MSG msg;
  205. if(IsWindow(Context->ProgressDialog)) {
  206. //
  207. // Let progress ui thread handle it.
  208. //
  209. Context->lParam = FILEOP_ABORT; // in case nobody gets chance to set Context->lParam
  210. PostMessage(
  211. Context->ProgressDialog,
  212. WMX_PERFORMUI,
  213. MAKEWPARAM(UiType,CancelCode),
  214. (LPARAM)UiParameters
  215. );
  216. pWaitForUiResponse(Context);
  217. return (UINT)Context->lParam;
  218. } else {
  219. //
  220. // There is no progress thread so do it synchronously.
  221. //
  222. return (UINT)pPerformUi(Context,UiType,UiParameters);
  223. }
  224. return 0;
  225. }
  226. UINT
  227. pNotificationStartQueue(
  228. IN PQUEUECONTEXT Context
  229. )
  230. /*++
  231. Routine Description:
  232. Handle SPFILENOTIFY_STARTQUEUE.
  233. Creates a progress dialog in a separate thread.
  234. Arguments:
  235. Context - supplies queue context.
  236. Return Value:
  237. 0 if unsuccessful, non-0 if successful.
  238. --*/
  239. {
  240. ULONG_PTR Thread;
  241. MSG msg;
  242. //
  243. // SetupCommitFileQueue could have been called in a different
  244. // thread. Adjust the thread id.
  245. //
  246. Context->MainThreadId = GetCurrentThreadId();
  247. //
  248. // Force this thread to have a message queue. If we don't do this,
  249. // then PostMessage, PostThreadMessage, etc can fail, which results
  250. // in hangs in some cases since we rely heavily on these for
  251. // progress, synchronization, etc.
  252. //
  253. PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);
  254. if(Context->AlternateProgressWindow) {
  255. //
  256. // Either the caller is supplying their own window for progress UI,
  257. // or this is a silent install (AlternateProgressWindow is
  258. // INVALID_HANDLE_VALUE).
  259. //
  260. return(TRUE);
  261. } else {
  262. //
  263. // Fire up the progress dialog in a separate thread.
  264. // This allows it to be responsive without suspending
  265. // the file operations.
  266. //
  267. Thread = _beginthread(
  268. pSetupProgressThread,
  269. 0,
  270. Context
  271. );
  272. if(Thread == -1) {
  273. //
  274. // assume OOM
  275. //
  276. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  277. return(0);
  278. }
  279. //
  280. // Wait for notification from the thread about the state
  281. // of the dialog. Assume out of memory if we fail
  282. //
  283. if(!pWaitForUiResponse(Context) || Context->bDialogExited) {
  284. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  285. return FALSE;
  286. } else {
  287. return TRUE;
  288. }
  289. }
  290. }
  291. UINT
  292. pNotificationStartEndSubqueue(
  293. IN PQUEUECONTEXT Context,
  294. IN BOOL Start,
  295. IN UINT_PTR Operation,
  296. IN UINT_PTR OpCount
  297. )
  298. /*++
  299. Routine Description:
  300. Handle SPFILENOTIFY_STARTSUBQUEUE, SPFILENOTIFY_ENDSUBQUEUE.
  301. Initializes/terminates a progress control.
  302. Also sets progress dialog caption.
  303. Arguments:
  304. Context - supplies queue context.
  305. Start - if TRUE, then this routine is being called to handle
  306. a subqueue start notification. Otherwise it's supposed to
  307. handle a subqueue end notification.
  308. Operation - one of FILEOP_COPY, FILEOP_DELETE, FILEOP_RENAME.
  309. OpCount - supplies number of copies, renames, or deletes.
  310. Return Value:
  311. 0 if unsuccessful, non-0 if successful.
  312. --*/
  313. {
  314. UINT rc;
  315. UINT CaptionStringId;
  316. TCHAR ParentText[256];
  317. BOOL GotParentText;
  318. PCTSTR CaptionText;
  319. UINT AnimationId;
  320. HWND Animation;
  321. rc = 1; // assume success.
  322. if(Context->Cancelled) {
  323. SetLastError(ERROR_CANCELLED);
  324. return(0);
  325. }
  326. if(Start) {
  327. if(IsWindow(Context->OwnerWindow)
  328. && GetWindowText(Context->OwnerWindow,ParentText,256)) {
  329. GotParentText = TRUE;
  330. } else {
  331. GotParentText = FALSE;
  332. }
  333. //
  334. // Clean out the text fields first.
  335. //
  336. if(IsWindow(Context->ProgressDialog)) {
  337. SetDlgItemText(Context->ProgressDialog,IDT_TEXT1,TEXT(""));
  338. SetDlgItemText(Context->ProgressDialog,IDT_TEXT2,TEXT(""));
  339. }
  340. switch(Operation) {
  341. case FILEOP_COPY:
  342. //
  343. // IDT_TEXT1 = target name sans path
  344. // IDT_TEXT2 = target name with path
  345. //
  346. if(IsWindow(Context->ProgressDialog)) {
  347. ShowWindow(GetDlgItem(Context->ProgressDialog,IDT_TEXT2),SW_SHOW);
  348. }
  349. CaptionStringId = GotParentText ? IDS_COPY_CAPTION1 : IDS_COPY_CAPTION2;
  350. AnimationId = IDA_FILECOPY;
  351. break;
  352. case FILEOP_RENAME:
  353. if(IsWindow(Context->ProgressDialog)) {
  354. ShowWindow(GetDlgItem(Context->ProgressDialog,IDT_TEXT2),SW_SHOW);
  355. }
  356. CaptionStringId = GotParentText ? IDS_RENAME_CAPTION1 : IDS_RENAME_CAPTION2;
  357. AnimationId = IDA_FILECOPY;
  358. break;
  359. case FILEOP_DELETE:
  360. //
  361. // IDT_TEXT1 = target name sans path
  362. // IDT_TEXT2 = path
  363. //
  364. if(IsWindow(Context->ProgressDialog)) {
  365. ShowWindow(GetDlgItem(Context->ProgressDialog,IDT_TEXT2),SW_HIDE);
  366. }
  367. CaptionStringId = GotParentText ? IDS_DELETE_CAPTION1 : IDS_DELETE_CAPTION2;
  368. AnimationId = IDA_FILEDEL;
  369. break;
  370. case FILEOP_BACKUP:
  371. // handle the new backup case (this is for a backup queue as opposed to on demand
  372. if(IsWindow(Context->ProgressDialog)) {
  373. ShowWindow(GetDlgItem(Context->ProgressDialog,IDT_TEXT2),SW_SHOW);
  374. }
  375. CaptionStringId = GotParentText ? IDS_BACKUP_CAPTION1 : IDS_BACKUP_CAPTION2;
  376. AnimationId = IDA_FILECOPY;
  377. break;
  378. default:
  379. SetLastError(ERROR_INVALID_PARAMETER);
  380. rc = 0;
  381. break;
  382. }
  383. if(rc) {
  384. //
  385. // Set dialog caption.
  386. //
  387. if(GotParentText) {
  388. CaptionText = FormatStringMessage(CaptionStringId,ParentText);
  389. } else {
  390. CaptionText = MyLoadString(CaptionStringId);
  391. }
  392. if(CaptionText) {
  393. if(IsWindow(Context->ProgressDialog)) {
  394. if(!SetWindowText(Context->ProgressDialog,CaptionText)) {
  395. SetWindowText(Context->ProgressDialog,TEXT(""));
  396. }
  397. }
  398. MyFree(CaptionText);
  399. }
  400. if(Context->AlternateProgressWindow) {
  401. //
  402. // If this is really an alternate progress window, notify it
  403. // about the number of operations. Copy only.
  404. //
  405. if((Operation == FILEOP_COPY) &&
  406. (Context->AlternateProgressWindow != INVALID_HANDLE_VALUE)) {
  407. SendMessage(Context->AlternateProgressWindow,
  408. Context->ProgressMsg,
  409. 0,
  410. OpCount
  411. );
  412. }
  413. } else {
  414. //
  415. // Set up the progress control. Each file will be 1 tick.
  416. //
  417. if(IsWindow(Context->ProgressBar)) {
  418. SendMessage(Context->ProgressBar,PBM_SETRANGE,0,MAKELPARAM(0,OpCount));
  419. SendMessage(Context->ProgressBar,PBM_SETSTEP,1,0);
  420. SendMessage(Context->ProgressBar,PBM_SETPOS,0,0);
  421. }
  422. //
  423. // And set up the animation control based on the operation type.
  424. //
  425. if(OpCount && IsWindow(Context->ProgressDialog)) {
  426. Animation = GetDlgItem(Context->ProgressDialog,IDA_ANIMATION);
  427. if(Animation) {
  428. Animate_Open(Animation,MAKEINTRESOURCE(AnimationId));
  429. Animate_Play(Animation,0,-1,-1);
  430. }
  431. }
  432. }
  433. }
  434. } else {
  435. //
  436. // Stop the animation control. Note that if the op count was 0
  437. // then we never started it, so stopping/unloading will give an error,
  438. // which we ignore. It's not harmful.
  439. //
  440. if(!Context->AlternateProgressWindow && IsWindow(Context->ProgressDialog)) {
  441. Animation = GetDlgItem(Context->ProgressDialog,IDA_ANIMATION);
  442. if (Animation) {
  443. Animate_Stop(Animation);
  444. Animate_Close(Animation);
  445. }
  446. }
  447. }
  448. return(rc);
  449. }
  450. UINT
  451. pNotificationStartOperation(
  452. IN PQUEUECONTEXT Context,
  453. IN PFILEPATHS FilePaths,
  454. IN UINT_PTR Operation
  455. )
  456. /*++
  457. Routine Description:
  458. Handle SPFILENOTIFY_STARTRENAME, SPFILENOTIFY_STARTDELETE,
  459. SPFILENOTIFY_STARTCOPY or SPFILENOTIFY_STARTBACKUP.
  460. Updates text in the progress dialog to indicate the files
  461. involved in the operation.
  462. Arguments:
  463. Context - supplies queue context.
  464. Start - if TRUE, then this routine is being called to handle
  465. a subqueue start notification. Otherwise it's supposed to
  466. handle a subqueue end notification.
  467. Operation - one of FILEOP_COPY, FILEOP_DELETE, FILEOP_RENAME.
  468. OpCount - supplies number of copies, renames, or deletes.
  469. Return Value:
  470. FILEOP_ABORT if error, otherwise FILEOP_DOIT.
  471. --*/
  472. {
  473. PCTSTR Text1,Text2;
  474. PTSTR p;
  475. TCHAR Target[MAX_PATH];
  476. UINT rc;
  477. DWORD ec;
  478. if(Context->Cancelled) {
  479. SetLastError(ERROR_CANCELLED);
  480. return(FILEOP_ABORT);
  481. }
  482. Text1 = Text2 = NULL;
  483. rc = FILEOP_ABORT;
  484. ec = ERROR_NOT_ENOUGH_MEMORY;
  485. switch(Operation) {
  486. case FILEOP_COPY:
  487. lstrcpyn(Target,FilePaths->Target,MAX_PATH);
  488. if(p = _tcsrchr(Target,TEXT('\\'))) {
  489. //
  490. // Ignore the source filename completely.
  491. //
  492. *p++ = 0;
  493. Text1 = DuplicateString(p);
  494. Text2 = FormatStringMessage(IDS_FILEOP_TO,Target);
  495. } else {
  496. //
  497. // Assume not full path -- strange case, but deal with it anyway.
  498. //
  499. Text1 = DuplicateString(FilePaths->Target);
  500. Text2 = DuplicateString(TEXT(""));
  501. }
  502. break;
  503. case FILEOP_RENAME:
  504. Text1 = DuplicateString(FilePaths->Source);
  505. if(p = _tcsrchr(FilePaths->Target,TEXT('\\'))) {
  506. p++;
  507. } else {
  508. p = (PTSTR)FilePaths->Target;
  509. }
  510. Text2 = FormatStringMessage(IDS_FILEOP_TO,p);
  511. break;
  512. case FILEOP_DELETE:
  513. lstrcpyn(Target,FilePaths->Target,MAX_PATH);
  514. if(p = _tcsrchr(Target,TEXT('\\'))) {
  515. *p++ = 0;
  516. Text1 = DuplicateString(p);
  517. Text2 = FormatStringMessage(IDS_FILEOP_FROM,Target);
  518. } else {
  519. //
  520. // Assume not full path -- strange case, but deal with it anyway.
  521. //
  522. Text1 = DuplicateString(FilePaths->Target);
  523. Text2 = DuplicateString(TEXT(""));
  524. }
  525. break;
  526. case FILEOP_BACKUP:
  527. lstrcpyn(Target,FilePaths->Source,MAX_PATH);
  528. if(p = _tcsrchr(Target,TEXT('\\'))) {
  529. //
  530. // FilePaths->Source = what we're backing up (which is target of a restore)
  531. // FilePaths->Target = where we're backing up to (block backup) or NULL (demand backup)
  532. //
  533. *p++ = 0;
  534. if (FilePaths->Target == NULL) {
  535. // Backing up <Filename>
  536. Text1 = FormatStringMessage(IDS_FILEOP_BACKUP,p);
  537. } else {
  538. // <Filename> (title already says Backing up)
  539. Text1 = DuplicateString(p);
  540. }
  541. // From <Directory>
  542. Text2 = FormatStringMessage(IDS_FILEOP_FROM,Target);
  543. } else {
  544. //
  545. // Assume not full path -- strange case, but deal with it anyway.
  546. //
  547. if (FilePaths->Source == NULL) {
  548. Text1 = FormatStringMessage(IDS_FILEOP_BACKUP,Target);
  549. } else {
  550. Text1 = DuplicateString(Target);
  551. }
  552. Text2 = DuplicateString(TEXT(""));
  553. }
  554. break;
  555. default:
  556. ec = ERROR_INVALID_PARAMETER;
  557. break;
  558. }
  559. if(Text1 && Text2) {
  560. if(IsWindow(Context->ProgressDialog)) {
  561. SetDlgItemText(Context->ProgressDialog,IDT_TEXT1,Text1);
  562. SetDlgItemText(Context->ProgressDialog,IDT_TEXT2,Text2);
  563. }
  564. rc = FILEOP_DOIT;
  565. }
  566. if(Text1) {
  567. MyFree(Text1);
  568. }
  569. if(Text2) {
  570. MyFree(Text2);
  571. }
  572. SetLastError(ec);
  573. return(rc);
  574. }
  575. UINT
  576. pNotificationErrorCopy(
  577. IN PQUEUECONTEXT Context,
  578. IN PFILEPATHS FilePaths,
  579. OUT PTSTR PathOut
  580. )
  581. {
  582. UINT rc;
  583. COPYERRORUI CopyError;
  584. CopyError.FilePaths = FilePaths;
  585. CopyError.PathOut = PathOut;
  586. //
  587. // Buffer gets the pathname part of the source
  588. // and p points to the filename part of the source.
  589. //
  590. lstrcpyn(CopyError.Buffer,FilePaths->Source,MAX_PATH);
  591. CopyError.Filename = _tcsrchr(CopyError.Buffer,TEXT('\\'));
  592. *CopyError.Filename++ = 0;
  593. //
  594. // The noskip and warnifskip flags are really mutually exclusive
  595. // but we don't try to enforce that here. Just pass through as
  596. // appropriate.
  597. //
  598. CopyError.Flags = 0;
  599. if(FilePaths->Flags & SP_COPY_NOSKIP) {
  600. CopyError.Flags |= IDF_NOSKIP;
  601. }
  602. if(FilePaths->Flags & SP_COPY_WARNIFSKIP) {
  603. CopyError.Flags |= IDF_WARNIFSKIP;
  604. }
  605. //
  606. // Also pass through the 'no browse' flag.
  607. //
  608. if(FilePaths->Flags & SP_COPY_NOBROWSE) {
  609. CopyError.Flags |= IDF_NOBROWSE;
  610. }
  611. rc = PostUiMessage (Context, UI_COPYERROR, DPROMPT_CANCEL, &CopyError);
  612. switch(rc) {
  613. case DPROMPT_SUCCESS:
  614. //
  615. // If a new path is indicated, verify that it actually changed.
  616. //
  617. if(CopyError.PathOut[0] &&
  618. !lstrcmpi(CopyError.Buffer,CopyError.PathOut)) {
  619. CopyError.PathOut[0] = 0;
  620. }
  621. rc = FILEOP_RETRY;
  622. break;
  623. case DPROMPT_SKIPFILE:
  624. rc = FILEOP_SKIP;
  625. break;
  626. case DPROMPT_CANCEL:
  627. SetLastError(ERROR_CANCELLED);
  628. rc = FILEOP_ABORT;
  629. break;
  630. default:
  631. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  632. rc = FILEOP_ABORT;
  633. break;
  634. }
  635. return(rc);
  636. }
  637. UINT
  638. pNotificationStartRegistration(
  639. IN PQUEUECONTEXT Context,
  640. IN PSP_REGISTER_CONTROL_STATUS ControlStatus,
  641. IN BOOL Register
  642. )
  643. {
  644. return FILEOP_DOIT;
  645. }
  646. UINT
  647. pNotificationErrorDelete(
  648. IN PQUEUECONTEXT Context,
  649. IN PFILEPATHS FilePaths
  650. )
  651. {
  652. UINT rc;
  653. //
  654. // Certain errors are not actually errors.
  655. //
  656. if((FilePaths->Win32Error == ERROR_FILE_NOT_FOUND)
  657. || (FilePaths->Win32Error == ERROR_PATH_NOT_FOUND)) {
  658. return(FILEOP_SKIP);
  659. }
  660. rc = PostUiMessage (Context, UI_DELETEERROR, DPROMPT_CANCEL, FilePaths);
  661. switch(rc) {
  662. case DPROMPT_SUCCESS:
  663. return(FILEOP_RETRY);
  664. case DPROMPT_SKIPFILE:
  665. return(FILEOP_SKIP);
  666. case DPROMPT_CANCEL:
  667. SetLastError(ERROR_CANCELLED);
  668. return(FILEOP_ABORT);
  669. default:
  670. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  671. return(FILEOP_ABORT);
  672. }
  673. }
  674. UINT
  675. pNotificationErrorRename(
  676. IN PQUEUECONTEXT Context,
  677. IN PFILEPATHS FilePaths
  678. )
  679. {
  680. UINT rc;
  681. rc = PostUiMessage (Context, UI_RENAMEERROR, DPROMPT_CANCEL, FilePaths);
  682. switch(rc) {
  683. case DPROMPT_SUCCESS:
  684. return(FILEOP_RETRY);
  685. case DPROMPT_SKIPFILE:
  686. return(FILEOP_SKIP);
  687. case DPROMPT_CANCEL:
  688. SetLastError(ERROR_CANCELLED);
  689. return(FILEOP_ABORT);
  690. default:
  691. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  692. return(FILEOP_ABORT);
  693. }
  694. }
  695. UINT
  696. pNotificationErrorBackup(
  697. IN PQUEUECONTEXT Context,
  698. IN PFILEPATHS FilePaths
  699. )
  700. {
  701. UINT rc;
  702. if(!(FilePaths->Flags & SP_BACKUP_SPECIAL)) {
  703. return FILEOP_SKIP;
  704. }
  705. rc = PostUiMessage (Context, UI_BACKUPERROR, DPROMPT_CANCEL, FilePaths);
  706. switch(rc) {
  707. case DPROMPT_SUCCESS:
  708. return(FILEOP_RETRY);
  709. case DPROMPT_SKIPFILE:
  710. return(FILEOP_SKIP);
  711. case DPROMPT_CANCEL:
  712. SetLastError(ERROR_CANCELLED);
  713. return(FILEOP_ABORT);
  714. default:
  715. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  716. return(FILEOP_ABORT);
  717. }
  718. }
  719. UINT
  720. pNotificationNeedMedia(
  721. IN PQUEUECONTEXT Context,
  722. IN PSOURCE_MEDIA SourceMedia,
  723. OUT PTSTR PathOut
  724. )
  725. {
  726. UINT rc;
  727. TCHAR Buffer[MAX_PATH];
  728. NEEDMEDIAUI NeedMedia;
  729. if(Context->Cancelled) {
  730. SetLastError(ERROR_CANCELLED);
  731. return(FILEOP_ABORT);
  732. }
  733. NeedMedia.SourceMedia = SourceMedia;
  734. NeedMedia.PathOut = PathOut;
  735. //
  736. // Remember the name of this media.
  737. //
  738. if(Context->CurrentSourceName) {
  739. MyFree(Context->CurrentSourceName);
  740. Context->CurrentSourceName = NULL;
  741. }
  742. if(SourceMedia->Description) {
  743. Context->CurrentSourceName = DuplicateString(SourceMedia->Description);
  744. if(!Context->CurrentSourceName) {
  745. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  746. return(FILEOP_ABORT);
  747. }
  748. }
  749. //
  750. // Set the source file in the progress dialog
  751. // so it matches the file being sought.
  752. //
  753. if(!(SourceMedia->Flags & SP_FLAG_CABINETCONTINUATION)) {
  754. if(IsWindow(Context->ProgressDialog) && !Context->ScreenReader) {
  755. DWORD chars;
  756. lstrcpyn(Buffer,SourceMedia->SourcePath,MAX_PATH);
  757. pSetupConcatenatePaths(Buffer,SourceMedia->SourceFile,MAX_PATH,NULL);
  758. SetTruncatedDlgItemText(Context->ProgressDialog,IDT_TEXT1,Buffer);
  759. SetDlgItemText(Context->ProgressDialog,IDT_TEXT2,TEXT(""));
  760. }
  761. }
  762. //
  763. // The noskip and warnifskip flags are really mutually exclusive
  764. // but we don't try to enforce that here. Just pass through as
  765. // appropriate.
  766. //
  767. // Allow skip if this is not a cabinet continuation and
  768. // the noskip flag is not set.
  769. //
  770. NeedMedia.Flags = IDF_CHECKFIRST;
  771. if(SourceMedia->Flags & (SP_FLAG_CABINETCONTINUATION | SP_COPY_NOSKIP)) {
  772. NeedMedia.Flags |= IDF_NOSKIP;
  773. }
  774. if(SourceMedia->Flags & SP_COPY_WARNIFSKIP) {
  775. NeedMedia.Flags |= IDF_WARNIFSKIP;
  776. }
  777. if(SourceMedia->Flags & SP_COPY_NOBROWSE) {
  778. NeedMedia.Flags |= IDF_NOBROWSE;
  779. }
  780. rc = PostUiMessage (Context, UI_NEEDMEDIA, DPROMPT_CANCEL, &NeedMedia);
  781. switch(rc) {
  782. case DPROMPT_SUCCESS:
  783. //
  784. // If the path really has changed, then return NEWPATH.
  785. // Otherwise return DOIT. Account for trailing backslash
  786. // differences.
  787. //
  788. lstrcpyn(Buffer,SourceMedia->SourcePath,MAX_PATH);
  789. rc = lstrlen(Buffer);
  790. if(rc && (*CharPrev(Buffer,Buffer+rc) == TEXT('\\'))) {
  791. Buffer[rc-1] = TEXT('\0'); // valid to do if last char is '\'
  792. }
  793. rc = lstrlen(NeedMedia.PathOut);
  794. if(rc && (*CharPrev(NeedMedia.PathOut,NeedMedia.PathOut+rc) == TEXT('\\'))) {
  795. NeedMedia.PathOut[rc-1] = TEXT('\0'); // valid to do if last char is '\'
  796. }
  797. rc = (lstrcmpi(SourceMedia->SourcePath,NeedMedia.PathOut) ?
  798. FILEOP_NEWPATH : FILEOP_DOIT);
  799. //
  800. // Make sure <drive>: ends with a \.
  801. //
  802. if(NeedMedia.PathOut[0] && (NeedMedia.PathOut[1] == TEXT(':')) &&
  803. !NeedMedia.PathOut[2]) {
  804. NeedMedia.PathOut[2] = TEXT('\\');
  805. NeedMedia.PathOut[3] = TEXT('\0');
  806. }
  807. break;
  808. case DPROMPT_SKIPFILE:
  809. rc = FILEOP_SKIP;
  810. break;
  811. case DPROMPT_CANCEL:
  812. SetLastError(ERROR_CANCELLED);
  813. rc = FILEOP_ABORT;
  814. break;
  815. default:
  816. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  817. rc = FILEOP_ABORT;
  818. break;
  819. }
  820. return(rc);
  821. }
  822. INT_PTR
  823. pNotificationVersionDlgProc(
  824. IN HWND hdlg,
  825. IN UINT msg,
  826. IN WPARAM wParam,
  827. IN LPARAM lParam
  828. )
  829. {
  830. PVERDLGCONTEXT context;
  831. PFILEPATHS filePaths;
  832. HWND hwnd;
  833. TCHAR text[128];
  834. TCHAR Buffer[MAX_PATH*2];
  835. PCTSTR message;
  836. int i;
  837. TCHAR SourceLangName[128];
  838. TCHAR TargetLangName[128];
  839. switch(msg) {
  840. case WM_INITDIALOG:
  841. context = (PVERDLGCONTEXT)lParam;
  842. MYASSERT(context != NULL);
  843. filePaths = (PFILEPATHS)context->Param1;
  844. SetProp(hdlg,DialogPropName,(HANDLE)context);
  845. //
  846. // Set the source and target filenames. Hack: if the source filename
  847. // looks like one of our temporary filenames from a cabinet extraction,
  848. // hide it.
  849. //
  850. message = pSetupGetFileTitle(filePaths->Source);
  851. i = lstrlen(message);
  852. if((i > 8)
  853. && ((TCHAR)CharUpper((LPTSTR)message[0]) == TEXT('S'))
  854. && ((TCHAR)CharUpper((LPTSTR)message[1]) == TEXT('E'))
  855. && ((TCHAR)CharUpper((LPTSTR)message[2]) == TEXT('T'))
  856. && ((TCHAR)CharUpper((LPTSTR)message[3]) == TEXT('P'))
  857. && !lstrcmpi(message+(i-4),TEXT(".TMP"))) {
  858. ShowWindow(GetDlgItem(hdlg,IDT_TEXT7),SW_HIDE);
  859. } else {
  860. GetDlgItemText(hdlg,IDT_TEXT7,text,SIZECHARS(text));
  861. message = FormatStringMessageFromString(text,filePaths->Source);
  862. if (message == NULL) goto no_memory;
  863. SetTruncatedDlgItemText(hdlg,IDT_TEXT7,message);
  864. MyFree(message);
  865. }
  866. GetDlgItemText(hdlg,IDT_TEXT8,text,SIZECHARS(text));
  867. message = FormatStringMessageFromString(text,filePaths->Target);
  868. if (message == NULL) goto no_memory;
  869. SetTruncatedDlgItemText(hdlg,IDT_TEXT8,message);
  870. MyFree(message);
  871. if (context->Notification & SPFILENOTIFY_LANGMISMATCH) {
  872. //
  873. // Language mismatch has the highest priority.
  874. //
  875. context->Notification = SPFILENOTIFY_LANGMISMATCH; // force other bits off, for NoToAll
  876. //
  877. // Format the overwrite question.
  878. //
  879. if(PRIMARYLANGID(LOWORD(context->Param2))==LANG_NEUTRAL) {
  880. LoadString(
  881. MyDllModuleHandle,
  882. IDS_LANG_NEUTRAL,
  883. SourceLangName,
  884. SIZECHARS(SourceLangName)
  885. );
  886. } else {
  887. i = GetLocaleInfo(
  888. MAKELCID(LOWORD(context->Param2),SORT_DEFAULT),
  889. LOCALE_SLANGUAGE,
  890. SourceLangName,
  891. SIZECHARS(SourceLangName)
  892. );
  893. if(!i) {
  894. LoadString(
  895. MyDllModuleHandle,
  896. IDS_LANG_UNKNOWN,
  897. SourceLangName,
  898. SIZECHARS(SourceLangName)
  899. );
  900. }
  901. }
  902. if(PRIMARYLANGID(HIWORD(context->Param2))==LANG_NEUTRAL) {
  903. LoadString(
  904. MyDllModuleHandle,
  905. IDS_LANG_NEUTRAL,
  906. TargetLangName,
  907. SIZECHARS(TargetLangName)
  908. );
  909. } else {
  910. i = GetLocaleInfo(
  911. MAKELCID(HIWORD(context->Param2),SORT_DEFAULT),
  912. LOCALE_SLANGUAGE,
  913. TargetLangName,
  914. SIZECHARS(TargetLangName)
  915. );
  916. if(!i) {
  917. LoadString(
  918. MyDllModuleHandle,
  919. IDS_LANG_UNKNOWN,
  920. TargetLangName,
  921. SIZECHARS(TargetLangName)
  922. );
  923. }
  924. }
  925. GetDlgItemText(hdlg,IDT_TEXT4,text,SIZECHARS(text));
  926. message = FormatStringMessageFromString(text,TargetLangName,SourceLangName);
  927. if (message == NULL) goto no_memory;
  928. SetDlgItemText(hdlg,IDT_TEXT4,message);
  929. MyFree(message);
  930. //
  931. // Turn off the TARGETNEWER and TARGETEXISTS messages.
  932. //
  933. hwnd = GetDlgItem(hdlg,IDT_TEXT2);
  934. ShowWindow(hwnd,SW_HIDE);
  935. hwnd = GetDlgItem(hdlg,IDT_TEXT3);
  936. ShowWindow(hwnd,SW_HIDE);
  937. hwnd = GetDlgItem(hdlg,IDT_TEXT5);
  938. ShowWindow(hwnd,SW_HIDE);
  939. hwnd = GetDlgItem(hdlg,IDT_TEXT6);
  940. ShowWindow(hwnd,SW_HIDE);
  941. } else if (context->Notification & SPFILENOTIFY_TARGETNEWER) {
  942. //
  943. // Target being newer has second highest priority.
  944. //
  945. context->Notification = SPFILENOTIFY_TARGETNEWER; // force other bits off, for NoToAll
  946. //
  947. // Turn off the LANGMISMATCH and TARGETEXISTS messages.
  948. //
  949. hwnd = GetDlgItem(hdlg,IDT_TEXT1);
  950. ShowWindow(hwnd,SW_HIDE);
  951. hwnd = GetDlgItem(hdlg,IDT_TEXT3);
  952. ShowWindow(hwnd,SW_HIDE);
  953. hwnd = GetDlgItem(hdlg,IDT_TEXT4);
  954. ShowWindow(hwnd,SW_HIDE);
  955. hwnd = GetDlgItem(hdlg,IDT_TEXT6);
  956. ShowWindow(hwnd,SW_HIDE);
  957. } else { // must be exactly SPFILENOTIFY_TARGETEXISTS
  958. //
  959. // Target existing has the lowest priority.
  960. //
  961. // Turn off the LANGMISMATCH and TARGETNEWER messages.
  962. //
  963. hwnd = GetDlgItem(hdlg,IDT_TEXT1);
  964. ShowWindow(hwnd,SW_HIDE);
  965. hwnd = GetDlgItem(hdlg,IDT_TEXT2);
  966. ShowWindow(hwnd,SW_HIDE);
  967. hwnd = GetDlgItem(hdlg,IDT_TEXT4);
  968. ShowWindow(hwnd,SW_HIDE);
  969. hwnd = GetDlgItem(hdlg,IDT_TEXT5);
  970. ShowWindow(hwnd,SW_HIDE);
  971. }
  972. PostMessage(hdlg,WMX_HELLO,0,0);
  973. break;
  974. case WMX_HELLO:
  975. //
  976. // If this guy has no owner force him to the foreground.
  977. // This catches cases where people are using a series of
  978. // dialogs and then some setup apis, because when they
  979. // close a dialog focus switches away from them.
  980. //
  981. hwnd = GetWindow(hdlg,GW_OWNER);
  982. if(!IsWindow(hwnd)) {
  983. SetForegroundWindow(hdlg);
  984. }
  985. break;
  986. case WM_COMMAND:
  987. context = (PVERDLGCONTEXT)GetProp(hdlg,DialogPropName);
  988. MYASSERT(context != NULL);
  989. switch (GET_WM_COMMAND_ID(wParam,lParam)) {
  990. case IDYES:
  991. EndDialog(hdlg,IDYES); // copy this file
  992. break;
  993. case IDNO:
  994. EndDialog(hdlg,IDNO); // skip this file
  995. break;
  996. case IDB_NOTOALL:
  997. //
  998. // No to All was selected. Add this notification type to the
  999. // NoToAllMask so that we don't ask about it again.
  1000. //
  1001. context->QueueContext->NoToAllMask |= context->Notification;
  1002. EndDialog(hdlg,IDNO); // skip this file
  1003. break;
  1004. }
  1005. break;
  1006. default:
  1007. return FALSE;
  1008. }
  1009. return TRUE;
  1010. no_memory:
  1011. pSetupOutOfMemory(
  1012. IsWindow(context->QueueContext->ProgressDialog) ?
  1013. context->QueueContext->ProgressDialog : context->QueueContext->OwnerWindow
  1014. );
  1015. EndDialog(hdlg,IDNO); // skip this file
  1016. return TRUE;
  1017. }
  1018. LPARAM
  1019. pPerformUi (
  1020. IN PQUEUECONTEXT Context,
  1021. IN UINT UiType,
  1022. IN PVOID UiParameters
  1023. )
  1024. {
  1025. PCOPYERRORUI CopyError;
  1026. PFILEPATHS FilePaths;
  1027. PNEEDMEDIAUI NeedMedia;
  1028. INT_PTR rc;
  1029. HWND Animation;
  1030. if(!Context->AlternateProgressWindow && IsWindow(Context->ProgressDialog)) {
  1031. Animation = GetDlgItem(Context->ProgressDialog,IDA_ANIMATION);
  1032. } else {
  1033. Animation = NULL;
  1034. }
  1035. switch (UiType) {
  1036. case UI_COPYERROR:
  1037. CopyError = (PCOPYERRORUI)UiParameters;
  1038. if (Animation) {
  1039. Animate_Stop(Animation);
  1040. }
  1041. rc = SetupCopyError(
  1042. IsWindow(Context->ProgressDialog) ? Context->ProgressDialog : Context->OwnerWindow,
  1043. NULL,
  1044. Context->CurrentSourceName,
  1045. CopyError->Buffer,
  1046. CopyError->Filename,
  1047. CopyError->FilePaths->Target,
  1048. CopyError->FilePaths->Win32Error,
  1049. CopyError->Flags,
  1050. CopyError->PathOut,
  1051. MAX_PATH,
  1052. NULL
  1053. );
  1054. if (Animation) {
  1055. Animate_Play(Animation,0,-1,-1);
  1056. }
  1057. break;
  1058. case UI_DELETEERROR:
  1059. FilePaths = (PFILEPATHS)UiParameters;
  1060. if (Animation) {
  1061. Animate_Stop(Animation);
  1062. }
  1063. rc = SetupDeleteError(
  1064. IsWindow(Context->ProgressDialog) ? Context->ProgressDialog : Context->OwnerWindow,
  1065. NULL,
  1066. FilePaths->Target,
  1067. FilePaths->Win32Error,
  1068. 0
  1069. );
  1070. if (Animation) {
  1071. Animate_Play(Animation,0,-1,-1);
  1072. }
  1073. break;
  1074. case UI_RENAMEERROR:
  1075. FilePaths = (PFILEPATHS)UiParameters;
  1076. if(Animation) {
  1077. Animate_Stop(Animation);
  1078. }
  1079. rc = SetupRenameError(
  1080. IsWindow(Context->ProgressDialog) ? Context->ProgressDialog : Context->OwnerWindow,
  1081. NULL,
  1082. FilePaths->Source,
  1083. FilePaths->Target,
  1084. FilePaths->Win32Error,
  1085. 0
  1086. );
  1087. if(Animation) {
  1088. Animate_Play(Animation,0,-1,-1);
  1089. }
  1090. break;
  1091. case UI_BACKUPERROR:
  1092. FilePaths = (PFILEPATHS)UiParameters;
  1093. if(Animation) {
  1094. Animate_Stop(Animation);
  1095. }
  1096. rc = SetupBackupError(
  1097. IsWindow(Context->ProgressDialog) ? Context->ProgressDialog : Context->OwnerWindow,
  1098. NULL,
  1099. FilePaths->Source,
  1100. FilePaths->Target,
  1101. FilePaths->Win32Error,
  1102. 0
  1103. );
  1104. if(Animation) {
  1105. Animate_Play(Animation,0,-1,-1);
  1106. }
  1107. break;
  1108. case UI_NEEDMEDIA:
  1109. NeedMedia = (PNEEDMEDIAUI)UiParameters;
  1110. if(Animation) {
  1111. Animate_Stop(Animation);
  1112. }
  1113. rc = SetupPromptForDisk(
  1114. IsWindow(Context->ProgressDialog) ? Context->ProgressDialog : Context->OwnerWindow,
  1115. NULL,
  1116. NeedMedia->SourceMedia->Description,
  1117. NeedMedia->SourceMedia->SourcePath,
  1118. NeedMedia->SourceMedia->SourceFile,
  1119. NeedMedia->SourceMedia->Tagfile,
  1120. NeedMedia->Flags,
  1121. NeedMedia->PathOut,
  1122. MAX_PATH,
  1123. NULL
  1124. );
  1125. if(Animation) {
  1126. Animate_Play(Animation,0,-1,-1);
  1127. }
  1128. break;
  1129. case UI_MISMATCHERROR:
  1130. if(Animation) {
  1131. Animate_Stop(Animation);
  1132. }
  1133. if(GlobalSetupFlags & (PSPGF_NONINTERACTIVE|PSPGF_UNATTENDED_SETUP)) {
  1134. rc = DPROMPT_CANCEL;
  1135. } else {
  1136. rc = DialogBoxParam(
  1137. MyDllModuleHandle,
  1138. MAKEINTRESOURCE(IDD_REPLACE),
  1139. IsWindow(Context->ProgressDialog) ?
  1140. Context->ProgressDialog : Context->OwnerWindow,
  1141. pNotificationVersionDlgProc,
  1142. (LPARAM)UiParameters
  1143. );
  1144. }
  1145. if(Animation) {
  1146. Animate_Play(Animation,0,-1,-1);
  1147. }
  1148. break;
  1149. default:
  1150. MYASSERT (0);
  1151. rc = 0;
  1152. }
  1153. return rc;
  1154. }
  1155. INT_PTR
  1156. pSetupProgressDlgProc(
  1157. IN HWND hdlg,
  1158. IN UINT msg,
  1159. IN WPARAM wParam,
  1160. IN LPARAM lParam
  1161. )
  1162. {
  1163. BOOL b;
  1164. PQUEUECONTEXT Context;
  1165. HWND hwnd;
  1166. PTSTR p;
  1167. INT_PTR i;
  1168. MSG m;
  1169. BOOL Cancelled;
  1170. HANDLE h;
  1171. static UINT uQueryCancelAutoPlay = 0;
  1172. switch(msg) {
  1173. case WM_INITDIALOG:
  1174. #ifdef PRERELEASE
  1175. if (GuiSetupInProgress) {
  1176. MYASSERT( FALSE && TEXT("bringing up file progress dialog (IDD_FILEPROGRESS) during gui-mode setup, which is a UI violation. Click yes and retrive a stack trace to determine errant caller.\n"));
  1177. }
  1178. #endif
  1179. Context = (PQUEUECONTEXT)lParam;
  1180. MYASSERT(Context != NULL);
  1181. SetProp(hdlg,DialogPropName,(HANDLE)Context);
  1182. #ifdef NOCANCEL_SUPPORT
  1183. //
  1184. // If cancel is not allowed, disable the cancel button.
  1185. //
  1186. if(!Context->AllowCancel) {
  1187. RECT rect;
  1188. RECT rect2;
  1189. hwnd = GetDlgItem(hdlg,IDCANCEL);
  1190. ShowWindow(hwnd,SW_HIDE);
  1191. EnableWindow(hwnd,FALSE);
  1192. GetWindowRect(hdlg,&rect);
  1193. GetWindowRect(hwnd,&rect2);
  1194. SetWindowPos(
  1195. hdlg,
  1196. NULL,
  1197. 0,0,
  1198. rect.right - rect.left,
  1199. (rect.bottom - rect.top) - (rect.bottom - rect2.top),
  1200. SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER
  1201. );
  1202. }
  1203. #endif
  1204. //
  1205. // Center the progress dialog relative to the parent window.
  1206. //
  1207. pSetupCenterWindowRelativeToParent(hdlg);
  1208. SetFocus(GetDlgItem(hdlg,IDCANCEL));
  1209. //
  1210. // The main thread is processing SPFILENOTIFY_STARTQUEUE and is
  1211. // waiting for some notification about the state of the UI thread.
  1212. // Let the main thread know we succeeded, and pass back a real
  1213. // handle to this thread that can be used to wait for it to terminate.
  1214. //
  1215. b = DuplicateHandle(
  1216. GetCurrentProcess(), // source process
  1217. GetCurrentThread(), // source handle
  1218. GetCurrentProcess(), // target process
  1219. &h, // new handle
  1220. 0, // ignored with DUPLICATE_SAME_ACCESS
  1221. FALSE, // not inheritable
  1222. DUPLICATE_SAME_ACCESS
  1223. );
  1224. if(!b) {
  1225. //
  1226. // Since we cannot duplicate the handle, we must kill this dialog,
  1227. // because otherwise the inter-thread communication will be broken
  1228. // and things go horribly wrong.
  1229. //
  1230. EndDialog(hdlg, -1);
  1231. return TRUE; // TRUE or FALSE, it really doesn't matter
  1232. }
  1233. //
  1234. // Store the dialog and progress bar handles in the context structure.
  1235. //
  1236. Context->ProgressDialog = hdlg;
  1237. Context->ProgressBar = GetDlgItem(hdlg,IDC_PROGRESS);
  1238. Context->UiThreadHandle = h;
  1239. PostMessage(hdlg,WMX_HELLO,0,0); // put WMX_HELLO on our message queue
  1240. SetEvent(Context->hEvent); // inform caller we've done initialization
  1241. b = FALSE;
  1242. break;
  1243. case WMX_HELLO:
  1244. //
  1245. // If this guy has no owner force him to the foreground.
  1246. // This catches cases where people are using a series of
  1247. // dialogs and then some setup apis, because when they
  1248. // close a dialog focus switches away from them.
  1249. //
  1250. hwnd = GetWindow(hdlg,GW_OWNER);
  1251. if(!IsWindow(hwnd)) {
  1252. SetForegroundWindow(hdlg);
  1253. }
  1254. b = TRUE;
  1255. break;
  1256. case WMX_PERFORMUI:
  1257. b = TRUE;
  1258. Context = (PQUEUECONTEXT)GetProp(hdlg,DialogPropName);
  1259. MYASSERT(Context != NULL);
  1260. //
  1261. // We'd better not already have any UI pending...
  1262. //
  1263. MYASSERT(Context->PendingUiType == UI_NONE);
  1264. if (Context->MessageBoxUp == TRUE) {
  1265. Context->PendingUiType = LOWORD (wParam);
  1266. Context->CancelReturnCode = HIWORD (wParam);
  1267. Context->PendingUiParameters = (PVOID)lParam;
  1268. } else {
  1269. Context->lParam = pPerformUi (Context, LOWORD(wParam), (PVOID)lParam);
  1270. SetEvent(Context->hEvent); // wakeup main thread (lParam has Ui result)
  1271. }
  1272. break;
  1273. case WM_COMMAND:
  1274. Context = (PQUEUECONTEXT)GetProp(hdlg,DialogPropName);
  1275. MYASSERT(Context != NULL);
  1276. if((HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDCANCEL)) {
  1277. p = MyLoadString(IDS_CANCELFILEOPS);
  1278. Cancelled = FALSE;
  1279. if(p) {
  1280. //
  1281. // While the message box is up, the main thread is still copying files,
  1282. // and it might just complete. If that happens, the main thread will
  1283. // post us WMX_KILLDIALOG, which would cause this dialog to nuke itself
  1284. // out from under the message box. The main thread would then continue
  1285. // executing while the message box is sitting there. Some components
  1286. // actually unload setupapi.dll at that point, and so when the user
  1287. // dismisses the message box, an AV results.
  1288. //
  1289. // We can't freeze the main thread via SuspendThread because then
  1290. // that thread, which probably owns this dialog's parent window,
  1291. // will not be able to process messages that result from the message box's
  1292. // creation. Result is that the message box never comes up and the process
  1293. // is deadlocked.
  1294. //
  1295. // We'd better not already have a message box up!
  1296. //
  1297. MYASSERT(!Context->MessageBoxUp);
  1298. Context->MessageBoxUp = TRUE;
  1299. i = MessageBox(
  1300. hdlg,
  1301. p,
  1302. TEXT(""),
  1303. MB_YESNO | MB_APPLMODAL | MB_DEFBUTTON2 | MB_SETFOREGROUND | MB_ICONQUESTION
  1304. );
  1305. Context->MessageBoxUp = FALSE;
  1306. //
  1307. // We set b to TRUE if the dialog is going away.
  1308. // We set Cancelled to TRUE if the user clicked the CANCEL button.
  1309. //
  1310. if(Context->DialogKilled) {
  1311. b = TRUE;
  1312. Cancelled = (i == IDYES);
  1313. } else {
  1314. b = (i == IDYES);
  1315. Cancelled = b;
  1316. }
  1317. MyFree(p);
  1318. } else {
  1319. pSetupOutOfMemory(hdlg);
  1320. Cancelled = TRUE;
  1321. b = TRUE;
  1322. }
  1323. if(b) {
  1324. if(Cancelled) {
  1325. Context->Cancelled = TRUE;
  1326. }
  1327. PostMessage(hdlg,WMX_KILLDIALOG,0,0);
  1328. if (Context->PendingUiType != UI_NONE) {
  1329. //
  1330. // We now allow the main thread to continue. Once we do
  1331. // so, the UI parameters that we passed to us are invalid.
  1332. // Cancel the pending UI.
  1333. //
  1334. Context->PendingUiType = UI_NONE;
  1335. Context->lParam = Context->CancelReturnCode;
  1336. SetEvent(Context->hEvent); // wake up main thread (lParam has UI result)
  1337. }
  1338. } else {
  1339. if (Context->PendingUiType != UI_NONE) {
  1340. Context->lParam = pPerformUi(Context,
  1341. (UINT)Context->PendingUiType,
  1342. Context->PendingUiParameters);
  1343. Context->PendingUiType = UI_NONE;
  1344. SetEvent(Context->hEvent); // wake up main thread (lParam has UI result)
  1345. }
  1346. }
  1347. b = TRUE;
  1348. } else {
  1349. b = FALSE;
  1350. }
  1351. break;
  1352. case WMX_KILLDIALOG:
  1353. //
  1354. // Exit unconditionally. Clean up first.
  1355. //
  1356. b = TRUE;
  1357. Context = (PQUEUECONTEXT)GetProp(hdlg, DialogPropName);
  1358. MYASSERT(Context != NULL);
  1359. if(Context->MessageBoxUp) {
  1360. //
  1361. // The user was still interacting with the "are you sure you
  1362. // want to cancel" dialog and the copying finished. So we don't want
  1363. // to nuke the dialog out from under the message box.
  1364. //
  1365. Context->DialogKilled = TRUE;
  1366. break;
  1367. }
  1368. DestroyWindow(Context->ProgressBar);
  1369. EndDialog(hdlg, 0);
  1370. break;
  1371. default:
  1372. //
  1373. // we disable autorun because it confuses the user.
  1374. //
  1375. if (!uQueryCancelAutoPlay) {
  1376. uQueryCancelAutoPlay = RegisterWindowMessage(TEXT("QueryCancelAutoPlay"));
  1377. }
  1378. if (msg == uQueryCancelAutoPlay) {
  1379. SetWindowLongPtr( hdlg, DWLP_MSGRESULT, 1 );
  1380. return 1; // cancel auto-play
  1381. }
  1382. b = FALSE;
  1383. break;
  1384. }
  1385. return(b);
  1386. }
  1387. PVOID
  1388. SetupInitDefaultQueueCallbackEx(
  1389. IN HWND OwnerWindow,
  1390. IN HWND AlternateProgressWindow, OPTIONAL
  1391. IN UINT ProgressMessage,
  1392. IN DWORD Reserved1,
  1393. IN PVOID Reserved2
  1394. )
  1395. {
  1396. PQUEUECONTEXT Context;
  1397. BOOL b;
  1398. Context = MyMalloc(sizeof(QUEUECONTEXT));
  1399. if(Context) {
  1400. ZeroMemory(Context,sizeof(QUEUECONTEXT));
  1401. Context->Signature = QUEUECONTEXT_SIGNATURE;
  1402. Context->hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
  1403. if (Context->hEvent == NULL) {
  1404. MyFree(Context);
  1405. return NULL;
  1406. }
  1407. Context->OwnerWindow = OwnerWindow;
  1408. Context->MainThreadId = GetCurrentThreadId();
  1409. Context->ProgressMsg = ProgressMessage;
  1410. Context->NoToAllMask = 0;
  1411. //
  1412. // If the caller specified NULL for the alternate progress window, and
  1413. // we're running non-interactively, we want to treat this just as if
  1414. // they'd specified INVALID_HANDLE_VALUE (i.e., suppress all progress
  1415. // UI).
  1416. //
  1417. if((GlobalSetupFlags & PSPGF_NONINTERACTIVE) && !AlternateProgressWindow) {
  1418. Context->AlternateProgressWindow = INVALID_HANDLE_VALUE;
  1419. } else {
  1420. Context->AlternateProgressWindow = AlternateProgressWindow;
  1421. }
  1422. if(SystemParametersInfo(SPI_GETSCREENREADER,0,&b,0) && b) {
  1423. Context->ScreenReader = TRUE;
  1424. } else {
  1425. Context->ScreenReader = FALSE;
  1426. }
  1427. #ifdef PRERELEASE
  1428. //
  1429. // if we're running in gui-mode setup, we suppress all setupapi progress UI
  1430. // and only allow AlternateProgressWindow UI
  1431. //
  1432. if (GuiSetupInProgress
  1433. && (Context->AlternateProgressWindow != (HWND)INVALID_HANDLE_VALUE)
  1434. && !IsWindow(Context->AlternateProgressWindow)) {
  1435. MYASSERT( FALSE && TEXT("SetupInitDefaultQueueCallbackEx() called in gui-setup without INVALID_HANDLE_VALUE, which means UI may be presented. Click yes and retrieve the stack trace to detect errant caller.\n"));
  1436. }
  1437. #endif
  1438. }
  1439. return(Context);
  1440. }
  1441. PVOID
  1442. SetupInitDefaultQueueCallback(
  1443. IN HWND OwnerWindow
  1444. )
  1445. {
  1446. #ifdef PRERELEASE
  1447. if (GuiSetupInProgress) {
  1448. MYASSERT( FALSE && TEXT("SetupInitDefaultQueueCallback() called in gui-setup, which means UI may be presented. Click yes and retrieve the stack trace to detect the errant caller.\n"));
  1449. }
  1450. #endif
  1451. return(SetupInitDefaultQueueCallbackEx(OwnerWindow,NULL,0,0,NULL));
  1452. }
  1453. VOID
  1454. SetupTermDefaultQueueCallback(
  1455. IN PVOID Context
  1456. )
  1457. {
  1458. PQUEUECONTEXT context;
  1459. context = Context;
  1460. try {
  1461. if(context && context->Signature == QUEUECONTEXT_SIGNATURE) {
  1462. if(context->CurrentSourceName) {
  1463. MyFree(context->CurrentSourceName);
  1464. }
  1465. if (context->hEvent) {
  1466. CloseHandle(context->hEvent);
  1467. }
  1468. context->Signature = 0;
  1469. }
  1470. MyFree(Context);
  1471. } except(EXCEPTION_EXECUTE_HANDLER) {
  1472. ;
  1473. }
  1474. }
  1475. #ifdef UNICODE
  1476. //
  1477. // ANSI version
  1478. //
  1479. UINT
  1480. SetupDefaultQueueCallbackA(
  1481. IN PVOID Context,
  1482. IN UINT Notification,
  1483. IN UINT_PTR Param1,
  1484. IN UINT_PTR Param2
  1485. )
  1486. {
  1487. UINT u;
  1488. u = pSetupCallDefaultMsgHandler(
  1489. Context,
  1490. Notification,
  1491. Param1,
  1492. Param2
  1493. );
  1494. return(u);
  1495. }
  1496. #else
  1497. //
  1498. // Unicode stub
  1499. //
  1500. UINT
  1501. SetupDefaultQueueCallbackW(
  1502. IN PVOID Context,
  1503. IN UINT Notification,
  1504. IN UINT_PTR Param1,
  1505. IN UINT_PTR Param2
  1506. )
  1507. {
  1508. UNREFERENCED_PARAMETER(Context);
  1509. UNREFERENCED_PARAMETER(Notification);
  1510. UNREFERENCED_PARAMETER(Param1);
  1511. UNREFERENCED_PARAMETER(Param2);
  1512. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  1513. return(0);
  1514. }
  1515. #endif
  1516. UINT
  1517. SetupDefaultQueueCallback(
  1518. IN PVOID Context,
  1519. IN UINT Notification,
  1520. IN UINT_PTR Param1,
  1521. IN UINT_PTR Param2
  1522. )
  1523. {
  1524. UINT rc;
  1525. DWORD err;
  1526. PQUEUECONTEXT context = Context;
  1527. MSG msg;
  1528. VERDLGCONTEXT dialogContext;
  1529. DWORD waitResult;
  1530. //
  1531. // a little sanity check on context
  1532. //
  1533. err = NO_ERROR;
  1534. try {
  1535. if (context == NULL || context->Signature != QUEUECONTEXT_SIGNATURE) {
  1536. err = ERROR_INVALID_PARAMETER;
  1537. }
  1538. } except(EXCEPTION_EXECUTE_HANDLER) {
  1539. err = ERROR_INVALID_PARAMETER;
  1540. }
  1541. if (err != NO_ERROR) {
  1542. return pGetCallbackErrorReturn(Notification,err);
  1543. }
  1544. switch(Notification) {
  1545. case SPFILENOTIFY_STARTQUEUE:
  1546. rc = pNotificationStartQueue(context);
  1547. break;
  1548. case SPFILENOTIFY_ENDQUEUE:
  1549. //
  1550. // Make sure the progress dialog is dead.
  1551. //
  1552. if(context->AlternateProgressWindow) {
  1553. //
  1554. // If we have an alternate progress window, then we'd better not
  1555. // have our own progress dialog, nor should we have a UI thread
  1556. // handle.
  1557. //
  1558. MYASSERT(!context->ProgressDialog);
  1559. MYASSERT(!context->UiThreadHandle);
  1560. } else {
  1561. if(IsWindow(context->ProgressDialog)) {
  1562. //
  1563. // Post a message to the dialog, instructing it to terminate,
  1564. // then wait for it to confirm.
  1565. //
  1566. PostMessage(context->ProgressDialog, WMX_KILLDIALOG, 0, 0);
  1567. }
  1568. //
  1569. // The dialog may have been marked for delete (thus IsWindow() fails),
  1570. // and yet the dialog has not yet been destroyed. Therefore, we always
  1571. // want to wait for the thread message that assures us that everything
  1572. // has been cleaned up in the other thread.
  1573. //
  1574. // Also, before returning we need to make sure that the UI thread is
  1575. // really gone, or else there's a hole where our caller could unload
  1576. // the library and then the UI thread would fault.
  1577. // We have seen this happen in stress.
  1578. //
  1579. while (pWaitForUiResponse(context)) /* nothing */;
  1580. if(context->UiThreadHandle) {
  1581. waitResult = WaitForSingleObject(context->UiThreadHandle,INFINITE);
  1582. MYASSERT(waitResult != WAIT_FAILED);
  1583. CloseHandle(context->UiThreadHandle);
  1584. }
  1585. }
  1586. rc = TRUE; // return value for this notification is actually ignored.
  1587. break;
  1588. case SPFILENOTIFY_STARTSUBQUEUE:
  1589. rc = pNotificationStartEndSubqueue(context,TRUE,Param1,Param2);
  1590. break;
  1591. case SPFILENOTIFY_ENDSUBQUEUE:
  1592. rc = pNotificationStartEndSubqueue(context,FALSE,Param1,0);
  1593. break;
  1594. case SPFILENOTIFY_STARTDELETE:
  1595. case SPFILENOTIFY_STARTRENAME:
  1596. case SPFILENOTIFY_STARTCOPY:
  1597. case SPFILENOTIFY_STARTBACKUP:
  1598. //
  1599. // Update display to indicate the files involved
  1600. // in the operation, unless a screen reader is active.
  1601. //
  1602. if(context->ScreenReader) {
  1603. rc = FILEOP_DOIT;
  1604. } else {
  1605. rc = pNotificationStartOperation(context,(PFILEPATHS)Param1,Param2);
  1606. }
  1607. break;
  1608. case SPFILENOTIFY_ENDDELETE:
  1609. case SPFILENOTIFY_ENDRENAME:
  1610. case SPFILENOTIFY_ENDCOPY:
  1611. case SPFILENOTIFY_ENDBACKUP:
  1612. case SPFILENOTIFY_ENDREGISTRATION:
  1613. if(context->AlternateProgressWindow) {
  1614. //
  1615. // If this is really is an alternate progress window, then 'tick' it.
  1616. // Copy only.
  1617. //
  1618. if((Notification == SPFILENOTIFY_ENDCOPY) &&
  1619. (context->AlternateProgressWindow != INVALID_HANDLE_VALUE)) {
  1620. SendMessage(context->AlternateProgressWindow, context->ProgressMsg, 1, 0);
  1621. }
  1622. } else {
  1623. if(IsWindow(context->ProgressBar)) {
  1624. //
  1625. // Update gas gauge.
  1626. //
  1627. SendMessage(context->ProgressBar,PBM_STEPIT,0,0);
  1628. }
  1629. }
  1630. rc = TRUE; // return value for these notifications is actually ignored.
  1631. break;
  1632. case SPFILENOTIFY_DELETEERROR:
  1633. rc = pNotificationErrorDelete(context,(PFILEPATHS)Param1);
  1634. break;
  1635. case SPFILENOTIFY_RENAMEERROR:
  1636. rc = pNotificationErrorRename(context,(PFILEPATHS)Param1);
  1637. break;
  1638. case SPFILENOTIFY_BACKUPERROR:
  1639. rc = pNotificationErrorBackup(context,(PFILEPATHS)Param1);
  1640. break;
  1641. case SPFILENOTIFY_COPYERROR:
  1642. rc = pNotificationErrorCopy(context,(PFILEPATHS)Param1,(PTSTR)Param2);
  1643. break;
  1644. case SPFILENOTIFY_NEEDMEDIA:
  1645. //
  1646. // Perform prompt.
  1647. //
  1648. rc = pNotificationNeedMedia(context,(PSOURCE_MEDIA)Param1,(PTSTR)Param2);
  1649. break;
  1650. case SPFILENOTIFY_STARTREGISTRATION:
  1651. rc = pNotificationStartRegistration(context,(PSP_REGISTER_CONTROL_STATUS)Param1,(BOOL)Param2);
  1652. break;
  1653. default:
  1654. //
  1655. // The notification is either an unknown ordinal or a version mismatch.
  1656. //
  1657. if(Notification & (SPFILENOTIFY_LANGMISMATCH | SPFILENOTIFY_TARGETNEWER | SPFILENOTIFY_TARGETEXISTS)) {
  1658. //
  1659. // It's one or more of our known version mismatches. First
  1660. // check to see whether No to All has already been specified
  1661. // for the mismatch(es). Turn off the bits in the notification
  1662. // that are set in NoToAllMask; if there are still bits set,
  1663. // we need to notify about this mismatch. If there are no
  1664. // longer any bits set, then don't copy this file.
  1665. //
  1666. Notification &= ~context->NoToAllMask;
  1667. if (Notification != 0) {
  1668. //
  1669. // Notify about this mismatch.
  1670. //
  1671. dialogContext.QueueContext = context;
  1672. dialogContext.Notification = Notification;
  1673. dialogContext.Param1 = Param1;
  1674. dialogContext.Param2 = Param2;
  1675. rc = PostUiMessage (
  1676. context, UI_MISMATCHERROR, DPROMPT_CANCEL, &dialogContext);
  1677. rc = (rc == IDYES);
  1678. } else {
  1679. //
  1680. // No To All has already been specified for this notification type.
  1681. // Skip the file.
  1682. //
  1683. rc = 0;
  1684. }
  1685. } else {
  1686. //
  1687. // Unknown notification. Skip the file.
  1688. //
  1689. rc = 0;
  1690. }
  1691. break;
  1692. }
  1693. return(rc);
  1694. }