|
|
/*++
Copyright (c) 1995-2000 Microsoft Corporation
Module Name:
fileq5.c
Abstract:
Default queue callback function.
Author:
Ted Miller (tedm) 24-Feb-1995
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#define QUEUECONTEXT_SIGNATURE (DWORD)(0x43515053) // 'CQPS'
typedef struct _QUEUECONTEXT { DWORD Signature; // an attempt to catch re-use of deleted queuecontext
HWND OwnerWindow; DWORD MainThreadId; HWND ProgressDialog; HWND ProgressBar; BOOL Cancelled; PTSTR CurrentSourceName; BOOL ScreenReader; BOOL MessageBoxUp; WPARAM PendingUiType; PVOID PendingUiParameters; UINT CancelReturnCode; BOOL DialogKilled; //
// If the SetupInitDefaultQueueCallbackEx is used, the caller can
// specify an alternate handler for progress. This is useful to
// get the default behavior for disk prompting, error handling, etc,
// but to provide a gas gauge embedded, say, in a wizard page.
//
// The alternate window is sent ProgressMsg once when the copy queue
// is started (wParam = 0. lParam = number of files to copy).
// It is then also sent once per file copied (wParam = 1. lParam = 0).
//
// NOTE: a silent installation (i.e., no progress UI) can be accomplished
// by specifying an AlternateProgressWindow handle of INVALID_HANDLE_VALUE.
//
HWND AlternateProgressWindow; UINT ProgressMsg; UINT NoToAllMask;
HANDLE UiThreadHandle; //
// instead of posting responses to main thread, use an event with flags
//
HANDLE hEvent; BOOL bDialogExited; LPARAM lParam;
#ifdef NOCANCEL_SUPPORT
BOOL AllowCancel; #endif
} QUEUECONTEXT, *PQUEUECONTEXT;
typedef struct _VERDLGCONTEXT { PQUEUECONTEXT QueueContext; UINT Notification; UINT_PTR Param1; UINT_PTR Param2; } VERDLGCONTEXT, *PVERDLGCONTEXT;
#define WMX_PROGRESSTHREAD (WM_APP+0)
#define WMX_KILLDIALOG (WM_APP+1)
#define WMX_HELLO (WM_APP+2)
#define WMX_PERFORMUI (WM_APP+3)
#define UI_NONE 0
#define UI_COPYERROR 1
#define UI_DELETEERROR 2
#define UI_RENAMEERROR 3
#define UI_NEEDMEDIA 4
#define UI_MISMATCHERROR 5
#define UI_BACKUPERROR 6
typedef struct _COPYERRORUI { TCHAR Buffer[MAX_PATH]; PTCHAR Filename; PFILEPATHS FilePaths; DWORD Flags; PTSTR PathOut; } COPYERRORUI, *PCOPYERRORUI;
typedef struct _NEEDMEDIAUI { PSOURCE_MEDIA SourceMedia; DWORD Flags; PTSTR PathOut; } NEEDMEDIAUI, *PNEEDMEDIAUI;
PCTSTR DialogPropName = TEXT("_context");
INT_PTR pSetupProgressDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam );
LPARAM pPerformUi ( IN PQUEUECONTEXT Context, IN UINT UiType, IN PVOID UiParameters );
VOID __cdecl pSetupProgressThread( IN PVOID Context )
/*++
Routine Description:
Thread entry point for setup file progress indicator. Puts up a dialog box.
Arguments:
Context - supplies queue context.
Return Value:
0 if unsuccessful, non-0 if successful.
--*/
{ PQUEUECONTEXT context; INT_PTR i; MSG msg;
//
// Force this thread to have a message queue, just in case.
//
PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);
//
// The thread parameter is the queue context.
//
context = Context;
//
// Create the progress dialog box.
//
i = DialogBoxParam( MyDllModuleHandle, MAKEINTRESOURCE(IDD_FILEPROGRESS), context->OwnerWindow, pSetupProgressDlgProc, (LPARAM)context );
//
// flag that this is the very last time hEvent will be set
//
context->bDialogExited = TRUE; SetEvent(context->hEvent);
//
// Done.
//
_endthread(); }
BOOL pWaitForUiResponse( IN OUT PQUEUECONTEXT Context ) /*++
Routine Description:
Waits for UI event to be set
Arguments:
Context - supplies queue-context structure
Return Value:
FALSE = failure
--*/ { BOOL KeepWaiting = TRUE; DWORD WaitProcStatus;
if (Context->hEvent == NULL) { MYASSERT(Context->hEvent); return FALSE; } if (Context->bDialogExited) { //
// dialog has already exited, we wont get another event
//
return FALSE; }
while (KeepWaiting) { WaitProcStatus = MyMsgWaitForMultipleObjectsEx( 1, &Context->hEvent, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); switch (WaitProcStatus) { case WAIT_OBJECT_0 + 1: { // Process gui messages
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); }
// fall through ...
} case WAIT_IO_COMPLETION: break;
case WAIT_OBJECT_0: case WAIT_TIMEOUT: default: KeepWaiting = FALSE; break; } }
return TRUE; }
UINT PostUiMessage ( IN PQUEUECONTEXT Context, IN UINT UiType, IN UINT CancelCode, IN OUT PVOID UiParameters ) { MSG msg;
if(IsWindow(Context->ProgressDialog)) { //
// Let progress ui thread handle it.
//
Context->lParam = FILEOP_ABORT; // in case nobody gets chance to set Context->lParam
PostMessage( Context->ProgressDialog, WMX_PERFORMUI, MAKEWPARAM(UiType,CancelCode), (LPARAM)UiParameters ); pWaitForUiResponse(Context); return (UINT)Context->lParam; } else { //
// There is no progress thread so do it synchronously.
//
return (UINT)pPerformUi(Context,UiType,UiParameters); }
return 0; }
UINT pNotificationStartQueue( IN PQUEUECONTEXT Context )
/*++
Routine Description:
Handle SPFILENOTIFY_STARTQUEUE.
Creates a progress dialog in a separate thread.
Arguments:
Context - supplies queue context.
Return Value:
0 if unsuccessful, non-0 if successful.
--*/
{ ULONG_PTR Thread; MSG msg;
//
// SetupCommitFileQueue could have been called in a different
// thread. Adjust the thread id.
//
Context->MainThreadId = GetCurrentThreadId();
//
// Force this thread to have a message queue. If we don't do this,
// then PostMessage, PostThreadMessage, etc can fail, which results
// in hangs in some cases since we rely heavily on these for
// progress, synchronization, etc.
//
PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);
if(Context->AlternateProgressWindow) { //
// Either the caller is supplying their own window for progress UI,
// or this is a silent install (AlternateProgressWindow is
// INVALID_HANDLE_VALUE).
//
return(TRUE); } else { //
// Fire up the progress dialog in a separate thread.
// This allows it to be responsive without suspending
// the file operations.
//
Thread = _beginthread( pSetupProgressThread, 0, Context );
if(Thread == -1) { //
// assume OOM
//
SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(0); }
//
// Wait for notification from the thread about the state
// of the dialog. Assume out of memory if we fail
//
if(!pWaitForUiResponse(Context) || Context->bDialogExited) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } else { return TRUE; } } }
UINT pNotificationStartEndSubqueue( IN PQUEUECONTEXT Context, IN BOOL Start, IN UINT_PTR Operation, IN UINT_PTR OpCount )
/*++
Routine Description:
Handle SPFILENOTIFY_STARTSUBQUEUE, SPFILENOTIFY_ENDSUBQUEUE.
Initializes/terminates a progress control. Also sets progress dialog caption.
Arguments:
Context - supplies queue context.
Start - if TRUE, then this routine is being called to handle a subqueue start notification. Otherwise it's supposed to handle a subqueue end notification.
Operation - one of FILEOP_COPY, FILEOP_DELETE, FILEOP_RENAME.
OpCount - supplies number of copies, renames, or deletes.
Return Value:
0 if unsuccessful, non-0 if successful.
--*/
{ UINT rc; UINT CaptionStringId; TCHAR ParentText[256]; BOOL GotParentText; PCTSTR CaptionText; UINT AnimationId; HWND Animation;
rc = 1; // assume success.
if(Context->Cancelled) { SetLastError(ERROR_CANCELLED); return(0); }
if(Start) {
if(IsWindow(Context->OwnerWindow) && GetWindowText(Context->OwnerWindow,ParentText,256)) { GotParentText = TRUE; } else { GotParentText = FALSE; }
//
// Clean out the text fields first.
//
if(IsWindow(Context->ProgressDialog)) { SetDlgItemText(Context->ProgressDialog,IDT_TEXT1,TEXT("")); SetDlgItemText(Context->ProgressDialog,IDT_TEXT2,TEXT("")); }
switch(Operation) {
case FILEOP_COPY: //
// IDT_TEXT1 = target name sans path
// IDT_TEXT2 = target name with path
//
if(IsWindow(Context->ProgressDialog)) { ShowWindow(GetDlgItem(Context->ProgressDialog,IDT_TEXT2),SW_SHOW); } CaptionStringId = GotParentText ? IDS_COPY_CAPTION1 : IDS_COPY_CAPTION2; AnimationId = IDA_FILECOPY; break;
case FILEOP_RENAME: if(IsWindow(Context->ProgressDialog)) { ShowWindow(GetDlgItem(Context->ProgressDialog,IDT_TEXT2),SW_SHOW); } CaptionStringId = GotParentText ? IDS_RENAME_CAPTION1 : IDS_RENAME_CAPTION2; AnimationId = IDA_FILECOPY; break;
case FILEOP_DELETE: //
// IDT_TEXT1 = target name sans path
// IDT_TEXT2 = path
//
if(IsWindow(Context->ProgressDialog)) { ShowWindow(GetDlgItem(Context->ProgressDialog,IDT_TEXT2),SW_HIDE); } CaptionStringId = GotParentText ? IDS_DELETE_CAPTION1 : IDS_DELETE_CAPTION2; AnimationId = IDA_FILEDEL; break;
case FILEOP_BACKUP: // handle the new backup case (this is for a backup queue as opposed to on demand
if(IsWindow(Context->ProgressDialog)) { ShowWindow(GetDlgItem(Context->ProgressDialog,IDT_TEXT2),SW_SHOW); } CaptionStringId = GotParentText ? IDS_BACKUP_CAPTION1 : IDS_BACKUP_CAPTION2; AnimationId = IDA_FILECOPY; break;
default: SetLastError(ERROR_INVALID_PARAMETER); rc = 0; break; }
if(rc) { //
// Set dialog caption.
//
if(GotParentText) { CaptionText = FormatStringMessage(CaptionStringId,ParentText); } else { CaptionText = MyLoadString(CaptionStringId); } if(CaptionText) { if(IsWindow(Context->ProgressDialog)) { if(!SetWindowText(Context->ProgressDialog,CaptionText)) { SetWindowText(Context->ProgressDialog,TEXT("")); } } MyFree(CaptionText); }
if(Context->AlternateProgressWindow) { //
// If this is really an alternate progress window, notify it
// about the number of operations. Copy only.
//
if((Operation == FILEOP_COPY) && (Context->AlternateProgressWindow != INVALID_HANDLE_VALUE)) {
SendMessage(Context->AlternateProgressWindow, Context->ProgressMsg, 0, OpCount ); } } else { //
// Set up the progress control. Each file will be 1 tick.
//
if(IsWindow(Context->ProgressBar)) { SendMessage(Context->ProgressBar,PBM_SETRANGE,0,MAKELPARAM(0,OpCount)); SendMessage(Context->ProgressBar,PBM_SETSTEP,1,0); SendMessage(Context->ProgressBar,PBM_SETPOS,0,0); }
//
// And set up the animation control based on the operation type.
//
if(OpCount && IsWindow(Context->ProgressDialog)) {
Animation = GetDlgItem(Context->ProgressDialog,IDA_ANIMATION);
if(Animation) { Animate_Open(Animation,MAKEINTRESOURCE(AnimationId)); Animate_Play(Animation,0,-1,-1); } } } } } else { //
// Stop the animation control. Note that if the op count was 0
// then we never started it, so stopping/unloading will give an error,
// which we ignore. It's not harmful.
//
if(!Context->AlternateProgressWindow && IsWindow(Context->ProgressDialog)) {
Animation = GetDlgItem(Context->ProgressDialog,IDA_ANIMATION); if (Animation) { Animate_Stop(Animation); Animate_Close(Animation); } } }
return(rc); }
UINT pNotificationStartOperation( IN PQUEUECONTEXT Context, IN PFILEPATHS FilePaths, IN UINT_PTR Operation )
/*++
Routine Description:
Handle SPFILENOTIFY_STARTRENAME, SPFILENOTIFY_STARTDELETE, SPFILENOTIFY_STARTCOPY or SPFILENOTIFY_STARTBACKUP.
Updates text in the progress dialog to indicate the files involved in the operation.
Arguments:
Context - supplies queue context.
Start - if TRUE, then this routine is being called to handle a subqueue start notification. Otherwise it's supposed to handle a subqueue end notification.
Operation - one of FILEOP_COPY, FILEOP_DELETE, FILEOP_RENAME.
OpCount - supplies number of copies, renames, or deletes.
Return Value:
FILEOP_ABORT if error, otherwise FILEOP_DOIT. --*/
{ PCTSTR Text1,Text2; PTSTR p; TCHAR Target[MAX_PATH]; UINT rc; DWORD ec;
if(Context->Cancelled) { SetLastError(ERROR_CANCELLED); return(FILEOP_ABORT); }
Text1 = Text2 = NULL; rc = FILEOP_ABORT; ec = ERROR_NOT_ENOUGH_MEMORY;
switch(Operation) { case FILEOP_COPY: lstrcpyn(Target,FilePaths->Target,MAX_PATH); if(p = _tcsrchr(Target,TEXT('\\'))) { //
// Ignore the source filename completely.
//
*p++ = 0; Text1 = DuplicateString(p); Text2 = FormatStringMessage(IDS_FILEOP_TO,Target); } else { //
// Assume not full path -- strange case, but deal with it anyway.
//
Text1 = DuplicateString(FilePaths->Target); Text2 = DuplicateString(TEXT("")); } break;
case FILEOP_RENAME:
Text1 = DuplicateString(FilePaths->Source); if(p = _tcsrchr(FilePaths->Target,TEXT('\\'))) { p++; } else { p = (PTSTR)FilePaths->Target; } Text2 = FormatStringMessage(IDS_FILEOP_TO,p); break;
case FILEOP_DELETE:
lstrcpyn(Target,FilePaths->Target,MAX_PATH); if(p = _tcsrchr(Target,TEXT('\\'))) { *p++ = 0; Text1 = DuplicateString(p); Text2 = FormatStringMessage(IDS_FILEOP_FROM,Target); } else { //
// Assume not full path -- strange case, but deal with it anyway.
//
Text1 = DuplicateString(FilePaths->Target); Text2 = DuplicateString(TEXT("")); } break;
case FILEOP_BACKUP: lstrcpyn(Target,FilePaths->Source,MAX_PATH); if(p = _tcsrchr(Target,TEXT('\\'))) { //
// FilePaths->Source = what we're backing up (which is target of a restore)
// FilePaths->Target = where we're backing up to (block backup) or NULL (demand backup)
//
*p++ = 0; if (FilePaths->Target == NULL) { // Backing up <Filename>
Text1 = FormatStringMessage(IDS_FILEOP_BACKUP,p); } else { // <Filename> (title already says Backing up)
Text1 = DuplicateString(p); } // From <Directory>
Text2 = FormatStringMessage(IDS_FILEOP_FROM,Target); } else { //
// Assume not full path -- strange case, but deal with it anyway.
//
if (FilePaths->Source == NULL) { Text1 = FormatStringMessage(IDS_FILEOP_BACKUP,Target); } else { Text1 = DuplicateString(Target); } Text2 = DuplicateString(TEXT("")); } break;
default: ec = ERROR_INVALID_PARAMETER; break; }
if(Text1 && Text2) { if(IsWindow(Context->ProgressDialog)) { SetDlgItemText(Context->ProgressDialog,IDT_TEXT1,Text1); SetDlgItemText(Context->ProgressDialog,IDT_TEXT2,Text2); } rc = FILEOP_DOIT; }
if(Text1) { MyFree(Text1); } if(Text2) { MyFree(Text2); } SetLastError(ec); return(rc); }
UINT pNotificationErrorCopy( IN PQUEUECONTEXT Context, IN PFILEPATHS FilePaths, OUT PTSTR PathOut ) { UINT rc; COPYERRORUI CopyError;
CopyError.FilePaths = FilePaths; CopyError.PathOut = PathOut;
//
// Buffer gets the pathname part of the source
// and p points to the filename part of the source.
//
lstrcpyn(CopyError.Buffer,FilePaths->Source,MAX_PATH); CopyError.Filename = _tcsrchr(CopyError.Buffer,TEXT('\\')); *CopyError.Filename++ = 0;
//
// The noskip and warnifskip flags are really mutually exclusive
// but we don't try to enforce that here. Just pass through as
// appropriate.
//
CopyError.Flags = 0; if(FilePaths->Flags & SP_COPY_NOSKIP) { CopyError.Flags |= IDF_NOSKIP; } if(FilePaths->Flags & SP_COPY_WARNIFSKIP) { CopyError.Flags |= IDF_WARNIFSKIP; } //
// Also pass through the 'no browse' flag.
//
if(FilePaths->Flags & SP_COPY_NOBROWSE) { CopyError.Flags |= IDF_NOBROWSE; }
rc = PostUiMessage (Context, UI_COPYERROR, DPROMPT_CANCEL, &CopyError);
switch(rc) {
case DPROMPT_SUCCESS: //
// If a new path is indicated, verify that it actually changed.
//
if(CopyError.PathOut[0] && !lstrcmpi(CopyError.Buffer,CopyError.PathOut)) { CopyError.PathOut[0] = 0; } rc = FILEOP_RETRY; break;
case DPROMPT_SKIPFILE: rc = FILEOP_SKIP; break;
case DPROMPT_CANCEL: SetLastError(ERROR_CANCELLED); rc = FILEOP_ABORT; break;
default: SetLastError(ERROR_NOT_ENOUGH_MEMORY); rc = FILEOP_ABORT; break; }
return(rc); }
UINT pNotificationStartRegistration( IN PQUEUECONTEXT Context, IN PSP_REGISTER_CONTROL_STATUS ControlStatus, IN BOOL Register ) {
return FILEOP_DOIT; }
UINT pNotificationErrorDelete( IN PQUEUECONTEXT Context, IN PFILEPATHS FilePaths ) { UINT rc;
//
// Certain errors are not actually errors.
//
if((FilePaths->Win32Error == ERROR_FILE_NOT_FOUND) || (FilePaths->Win32Error == ERROR_PATH_NOT_FOUND)) { return(FILEOP_SKIP); }
rc = PostUiMessage (Context, UI_DELETEERROR, DPROMPT_CANCEL, FilePaths);
switch(rc) {
case DPROMPT_SUCCESS: return(FILEOP_RETRY);
case DPROMPT_SKIPFILE: return(FILEOP_SKIP);
case DPROMPT_CANCEL: SetLastError(ERROR_CANCELLED); return(FILEOP_ABORT);
default: SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FILEOP_ABORT); } }
UINT pNotificationErrorRename( IN PQUEUECONTEXT Context, IN PFILEPATHS FilePaths ) { UINT rc;
rc = PostUiMessage (Context, UI_RENAMEERROR, DPROMPT_CANCEL, FilePaths);
switch(rc) {
case DPROMPT_SUCCESS: return(FILEOP_RETRY);
case DPROMPT_SKIPFILE: return(FILEOP_SKIP);
case DPROMPT_CANCEL: SetLastError(ERROR_CANCELLED); return(FILEOP_ABORT);
default: SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FILEOP_ABORT); } }
UINT pNotificationErrorBackup( IN PQUEUECONTEXT Context, IN PFILEPATHS FilePaths ) { UINT rc;
if(!(FilePaths->Flags & SP_BACKUP_SPECIAL)) { return FILEOP_SKIP; }
rc = PostUiMessage (Context, UI_BACKUPERROR, DPROMPT_CANCEL, FilePaths);
switch(rc) {
case DPROMPT_SUCCESS: return(FILEOP_RETRY);
case DPROMPT_SKIPFILE: return(FILEOP_SKIP);
case DPROMPT_CANCEL: SetLastError(ERROR_CANCELLED); return(FILEOP_ABORT);
default: SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FILEOP_ABORT); } }
UINT pNotificationNeedMedia( IN PQUEUECONTEXT Context, IN PSOURCE_MEDIA SourceMedia, OUT PTSTR PathOut ) { UINT rc; TCHAR Buffer[MAX_PATH]; NEEDMEDIAUI NeedMedia;
if(Context->Cancelled) { SetLastError(ERROR_CANCELLED); return(FILEOP_ABORT); }
NeedMedia.SourceMedia = SourceMedia; NeedMedia.PathOut = PathOut;
//
// Remember the name of this media.
//
if(Context->CurrentSourceName) { MyFree(Context->CurrentSourceName); Context->CurrentSourceName = NULL; } if(SourceMedia->Description) { Context->CurrentSourceName = DuplicateString(SourceMedia->Description); if(!Context->CurrentSourceName) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FILEOP_ABORT); } }
//
// Set the source file in the progress dialog
// so it matches the file being sought.
//
if(!(SourceMedia->Flags & SP_FLAG_CABINETCONTINUATION)) { if(IsWindow(Context->ProgressDialog) && !Context->ScreenReader) { DWORD chars; lstrcpyn(Buffer,SourceMedia->SourcePath,MAX_PATH); pSetupConcatenatePaths(Buffer,SourceMedia->SourceFile,MAX_PATH,NULL); SetTruncatedDlgItemText(Context->ProgressDialog,IDT_TEXT1,Buffer); SetDlgItemText(Context->ProgressDialog,IDT_TEXT2,TEXT("")); } }
//
// The noskip and warnifskip flags are really mutually exclusive
// but we don't try to enforce that here. Just pass through as
// appropriate.
//
// Allow skip if this is not a cabinet continuation and
// the noskip flag is not set.
//
NeedMedia.Flags = IDF_CHECKFIRST; if(SourceMedia->Flags & (SP_FLAG_CABINETCONTINUATION | SP_COPY_NOSKIP)) { NeedMedia.Flags |= IDF_NOSKIP; } if(SourceMedia->Flags & SP_COPY_WARNIFSKIP) { NeedMedia.Flags |= IDF_WARNIFSKIP; } if(SourceMedia->Flags & SP_COPY_NOBROWSE) { NeedMedia.Flags |= IDF_NOBROWSE; }
rc = PostUiMessage (Context, UI_NEEDMEDIA, DPROMPT_CANCEL, &NeedMedia);
switch(rc) {
case DPROMPT_SUCCESS: //
// If the path really has changed, then return NEWPATH.
// Otherwise return DOIT. Account for trailing backslash
// differences.
//
lstrcpyn(Buffer,SourceMedia->SourcePath,MAX_PATH);
rc = lstrlen(Buffer); if(rc && (*CharPrev(Buffer,Buffer+rc) == TEXT('\\'))) { Buffer[rc-1] = TEXT('\0'); // valid to do if last char is '\'
}
rc = lstrlen(NeedMedia.PathOut); if(rc && (*CharPrev(NeedMedia.PathOut,NeedMedia.PathOut+rc) == TEXT('\\'))) { NeedMedia.PathOut[rc-1] = TEXT('\0'); // valid to do if last char is '\'
}
rc = (lstrcmpi(SourceMedia->SourcePath,NeedMedia.PathOut) ? FILEOP_NEWPATH : FILEOP_DOIT);
//
// Make sure <drive>: ends with a \.
//
if(NeedMedia.PathOut[0] && (NeedMedia.PathOut[1] == TEXT(':')) && !NeedMedia.PathOut[2]) { NeedMedia.PathOut[2] = TEXT('\\'); NeedMedia.PathOut[3] = TEXT('\0'); }
break;
case DPROMPT_SKIPFILE: rc = FILEOP_SKIP; break;
case DPROMPT_CANCEL: SetLastError(ERROR_CANCELLED); rc = FILEOP_ABORT; break;
default: SetLastError(ERROR_NOT_ENOUGH_MEMORY); rc = FILEOP_ABORT; break; }
return(rc); }
INT_PTR pNotificationVersionDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) { PVERDLGCONTEXT context; PFILEPATHS filePaths; HWND hwnd; TCHAR text[128]; TCHAR Buffer[MAX_PATH*2]; PCTSTR message; int i; TCHAR SourceLangName[128]; TCHAR TargetLangName[128];
switch(msg) {
case WM_INITDIALOG:
context = (PVERDLGCONTEXT)lParam; MYASSERT(context != NULL);
filePaths = (PFILEPATHS)context->Param1;
SetProp(hdlg,DialogPropName,(HANDLE)context);
//
// Set the source and target filenames. Hack: if the source filename
// looks like one of our temporary filenames from a cabinet extraction,
// hide it.
//
message = pSetupGetFileTitle(filePaths->Source); i = lstrlen(message); if((i > 8) && ((TCHAR)CharUpper((LPTSTR)message[0]) == TEXT('S')) && ((TCHAR)CharUpper((LPTSTR)message[1]) == TEXT('E')) && ((TCHAR)CharUpper((LPTSTR)message[2]) == TEXT('T')) && ((TCHAR)CharUpper((LPTSTR)message[3]) == TEXT('P')) && !lstrcmpi(message+(i-4),TEXT(".TMP"))) {
ShowWindow(GetDlgItem(hdlg,IDT_TEXT7),SW_HIDE);
} else { GetDlgItemText(hdlg,IDT_TEXT7,text,SIZECHARS(text)); message = FormatStringMessageFromString(text,filePaths->Source); if (message == NULL) goto no_memory; SetTruncatedDlgItemText(hdlg,IDT_TEXT7,message); MyFree(message); }
GetDlgItemText(hdlg,IDT_TEXT8,text,SIZECHARS(text)); message = FormatStringMessageFromString(text,filePaths->Target); if (message == NULL) goto no_memory; SetTruncatedDlgItemText(hdlg,IDT_TEXT8,message); MyFree(message);
if (context->Notification & SPFILENOTIFY_LANGMISMATCH) { //
// Language mismatch has the highest priority.
//
context->Notification = SPFILENOTIFY_LANGMISMATCH; // force other bits off, for NoToAll
//
// Format the overwrite question.
//
if(PRIMARYLANGID(LOWORD(context->Param2))==LANG_NEUTRAL) { LoadString( MyDllModuleHandle, IDS_LANG_NEUTRAL, SourceLangName, SIZECHARS(SourceLangName) ); } else { i = GetLocaleInfo( MAKELCID(LOWORD(context->Param2),SORT_DEFAULT), LOCALE_SLANGUAGE, SourceLangName, SIZECHARS(SourceLangName) ); if(!i) { LoadString( MyDllModuleHandle, IDS_LANG_UNKNOWN, SourceLangName, SIZECHARS(SourceLangName) ); } }
if(PRIMARYLANGID(HIWORD(context->Param2))==LANG_NEUTRAL) { LoadString( MyDllModuleHandle, IDS_LANG_NEUTRAL, TargetLangName, SIZECHARS(TargetLangName) ); } else { i = GetLocaleInfo( MAKELCID(HIWORD(context->Param2),SORT_DEFAULT), LOCALE_SLANGUAGE, TargetLangName, SIZECHARS(TargetLangName) ); if(!i) { LoadString( MyDllModuleHandle, IDS_LANG_UNKNOWN, TargetLangName, SIZECHARS(TargetLangName) ); } } GetDlgItemText(hdlg,IDT_TEXT4,text,SIZECHARS(text)); message = FormatStringMessageFromString(text,TargetLangName,SourceLangName); if (message == NULL) goto no_memory; SetDlgItemText(hdlg,IDT_TEXT4,message); MyFree(message);
//
// Turn off the TARGETNEWER and TARGETEXISTS messages.
//
hwnd = GetDlgItem(hdlg,IDT_TEXT2); ShowWindow(hwnd,SW_HIDE); hwnd = GetDlgItem(hdlg,IDT_TEXT3); ShowWindow(hwnd,SW_HIDE); hwnd = GetDlgItem(hdlg,IDT_TEXT5); ShowWindow(hwnd,SW_HIDE); hwnd = GetDlgItem(hdlg,IDT_TEXT6); ShowWindow(hwnd,SW_HIDE);
} else if (context->Notification & SPFILENOTIFY_TARGETNEWER) { //
// Target being newer has second highest priority.
//
context->Notification = SPFILENOTIFY_TARGETNEWER; // force other bits off, for NoToAll
//
// Turn off the LANGMISMATCH and TARGETEXISTS messages.
//
hwnd = GetDlgItem(hdlg,IDT_TEXT1); ShowWindow(hwnd,SW_HIDE); hwnd = GetDlgItem(hdlg,IDT_TEXT3); ShowWindow(hwnd,SW_HIDE); hwnd = GetDlgItem(hdlg,IDT_TEXT4); ShowWindow(hwnd,SW_HIDE); hwnd = GetDlgItem(hdlg,IDT_TEXT6); ShowWindow(hwnd,SW_HIDE);
} else { // must be exactly SPFILENOTIFY_TARGETEXISTS
//
// Target existing has the lowest priority.
//
// Turn off the LANGMISMATCH and TARGETNEWER messages.
//
hwnd = GetDlgItem(hdlg,IDT_TEXT1); ShowWindow(hwnd,SW_HIDE); hwnd = GetDlgItem(hdlg,IDT_TEXT2); ShowWindow(hwnd,SW_HIDE); hwnd = GetDlgItem(hdlg,IDT_TEXT4); ShowWindow(hwnd,SW_HIDE); hwnd = GetDlgItem(hdlg,IDT_TEXT5); ShowWindow(hwnd,SW_HIDE);
}
PostMessage(hdlg,WMX_HELLO,0,0); break;
case WMX_HELLO: //
// If this guy has no owner force him to the foreground.
// This catches cases where people are using a series of
// dialogs and then some setup apis, because when they
// close a dialog focus switches away from them.
//
hwnd = GetWindow(hdlg,GW_OWNER); if(!IsWindow(hwnd)) { SetForegroundWindow(hdlg); } break;
case WM_COMMAND: context = (PVERDLGCONTEXT)GetProp(hdlg,DialogPropName); MYASSERT(context != NULL); switch (GET_WM_COMMAND_ID(wParam,lParam)) {
case IDYES: EndDialog(hdlg,IDYES); // copy this file
break;
case IDNO: EndDialog(hdlg,IDNO); // skip this file
break;
case IDB_NOTOALL: //
// No to All was selected. Add this notification type to the
// NoToAllMask so that we don't ask about it again.
//
context->QueueContext->NoToAllMask |= context->Notification; EndDialog(hdlg,IDNO); // skip this file
break; } break;
default: return FALSE; } return TRUE;
no_memory: pSetupOutOfMemory( IsWindow(context->QueueContext->ProgressDialog) ? context->QueueContext->ProgressDialog : context->QueueContext->OwnerWindow ); EndDialog(hdlg,IDNO); // skip this file
return TRUE; }
LPARAM pPerformUi ( IN PQUEUECONTEXT Context, IN UINT UiType, IN PVOID UiParameters ) { PCOPYERRORUI CopyError; PFILEPATHS FilePaths; PNEEDMEDIAUI NeedMedia; INT_PTR rc; HWND Animation;
if(!Context->AlternateProgressWindow && IsWindow(Context->ProgressDialog)) { Animation = GetDlgItem(Context->ProgressDialog,IDA_ANIMATION); } else { Animation = NULL; }
switch (UiType) {
case UI_COPYERROR: CopyError = (PCOPYERRORUI)UiParameters; if (Animation) { Animate_Stop(Animation); } rc = SetupCopyError( IsWindow(Context->ProgressDialog) ? Context->ProgressDialog : Context->OwnerWindow, NULL, Context->CurrentSourceName, CopyError->Buffer, CopyError->Filename, CopyError->FilePaths->Target, CopyError->FilePaths->Win32Error, CopyError->Flags, CopyError->PathOut, MAX_PATH, NULL ); if (Animation) { Animate_Play(Animation,0,-1,-1); } break;
case UI_DELETEERROR: FilePaths = (PFILEPATHS)UiParameters;
if (Animation) { Animate_Stop(Animation); } rc = SetupDeleteError( IsWindow(Context->ProgressDialog) ? Context->ProgressDialog : Context->OwnerWindow, NULL, FilePaths->Target, FilePaths->Win32Error, 0 ); if (Animation) { Animate_Play(Animation,0,-1,-1); }
break;
case UI_RENAMEERROR: FilePaths = (PFILEPATHS)UiParameters;
if(Animation) { Animate_Stop(Animation); } rc = SetupRenameError( IsWindow(Context->ProgressDialog) ? Context->ProgressDialog : Context->OwnerWindow, NULL, FilePaths->Source, FilePaths->Target, FilePaths->Win32Error, 0 ); if(Animation) { Animate_Play(Animation,0,-1,-1); }
break;
case UI_BACKUPERROR: FilePaths = (PFILEPATHS)UiParameters;
if(Animation) { Animate_Stop(Animation); } rc = SetupBackupError( IsWindow(Context->ProgressDialog) ? Context->ProgressDialog : Context->OwnerWindow, NULL, FilePaths->Source, FilePaths->Target, FilePaths->Win32Error, 0 ); if(Animation) { Animate_Play(Animation,0,-1,-1); }
break;
case UI_NEEDMEDIA: NeedMedia = (PNEEDMEDIAUI)UiParameters;
if(Animation) { Animate_Stop(Animation); } rc = SetupPromptForDisk( IsWindow(Context->ProgressDialog) ? Context->ProgressDialog : Context->OwnerWindow, NULL, NeedMedia->SourceMedia->Description, NeedMedia->SourceMedia->SourcePath, NeedMedia->SourceMedia->SourceFile, NeedMedia->SourceMedia->Tagfile, NeedMedia->Flags, NeedMedia->PathOut, MAX_PATH, NULL ); if(Animation) { Animate_Play(Animation,0,-1,-1); }
break;
case UI_MISMATCHERROR: if(Animation) { Animate_Stop(Animation); } if(GlobalSetupFlags & (PSPGF_NONINTERACTIVE|PSPGF_UNATTENDED_SETUP)) { rc = DPROMPT_CANCEL; } else { rc = DialogBoxParam( MyDllModuleHandle, MAKEINTRESOURCE(IDD_REPLACE), IsWindow(Context->ProgressDialog) ? Context->ProgressDialog : Context->OwnerWindow, pNotificationVersionDlgProc, (LPARAM)UiParameters ); } if(Animation) { Animate_Play(Animation,0,-1,-1); } break;
default: MYASSERT (0); rc = 0; }
return rc; }
INT_PTR pSetupProgressDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) { BOOL b; PQUEUECONTEXT Context; HWND hwnd; PTSTR p; INT_PTR i; MSG m; BOOL Cancelled; HANDLE h; static UINT uQueryCancelAutoPlay = 0;
switch(msg) {
case WM_INITDIALOG:
#ifdef PRERELEASE
if (GuiSetupInProgress) { 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")); } #endif
Context = (PQUEUECONTEXT)lParam; MYASSERT(Context != NULL); SetProp(hdlg,DialogPropName,(HANDLE)Context);
#ifdef NOCANCEL_SUPPORT
//
// If cancel is not allowed, disable the cancel button.
//
if(!Context->AllowCancel) {
RECT rect; RECT rect2;
hwnd = GetDlgItem(hdlg,IDCANCEL);
ShowWindow(hwnd,SW_HIDE); EnableWindow(hwnd,FALSE);
GetWindowRect(hdlg,&rect); GetWindowRect(hwnd,&rect2);
SetWindowPos( hdlg, NULL, 0,0, rect.right - rect.left, (rect.bottom - rect.top) - (rect.bottom - rect2.top), SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER ); } #endif
//
// Center the progress dialog relative to the parent window.
//
pSetupCenterWindowRelativeToParent(hdlg);
SetFocus(GetDlgItem(hdlg,IDCANCEL));
//
// The main thread is processing SPFILENOTIFY_STARTQUEUE and is
// waiting for some notification about the state of the UI thread.
// Let the main thread know we succeeded, and pass back a real
// handle to this thread that can be used to wait for it to terminate.
//
b = DuplicateHandle( GetCurrentProcess(), // source process
GetCurrentThread(), // source handle
GetCurrentProcess(), // target process
&h, // new handle
0, // ignored with DUPLICATE_SAME_ACCESS
FALSE, // not inheritable
DUPLICATE_SAME_ACCESS );
if(!b) { //
// Since we cannot duplicate the handle, we must kill this dialog,
// because otherwise the inter-thread communication will be broken
// and things go horribly wrong.
//
EndDialog(hdlg, -1); return TRUE; // TRUE or FALSE, it really doesn't matter
}
//
// Store the dialog and progress bar handles in the context structure.
//
Context->ProgressDialog = hdlg; Context->ProgressBar = GetDlgItem(hdlg,IDC_PROGRESS);
Context->UiThreadHandle = h; PostMessage(hdlg,WMX_HELLO,0,0); // put WMX_HELLO on our message queue
SetEvent(Context->hEvent); // inform caller we've done initialization
b = FALSE; break;
case WMX_HELLO: //
// If this guy has no owner force him to the foreground.
// This catches cases where people are using a series of
// dialogs and then some setup apis, because when they
// close a dialog focus switches away from them.
//
hwnd = GetWindow(hdlg,GW_OWNER); if(!IsWindow(hwnd)) { SetForegroundWindow(hdlg); }
b = TRUE;
break;
case WMX_PERFORMUI: b = TRUE; Context = (PQUEUECONTEXT)GetProp(hdlg,DialogPropName); MYASSERT(Context != NULL);
//
// We'd better not already have any UI pending...
//
MYASSERT(Context->PendingUiType == UI_NONE);
if (Context->MessageBoxUp == TRUE) { Context->PendingUiType = LOWORD (wParam); Context->CancelReturnCode = HIWORD (wParam); Context->PendingUiParameters = (PVOID)lParam; } else { Context->lParam = pPerformUi (Context, LOWORD(wParam), (PVOID)lParam); SetEvent(Context->hEvent); // wakeup main thread (lParam has Ui result)
}
break;
case WM_COMMAND: Context = (PQUEUECONTEXT)GetProp(hdlg,DialogPropName); MYASSERT(Context != NULL); if((HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDCANCEL)) { p = MyLoadString(IDS_CANCELFILEOPS); Cancelled = FALSE; if(p) { //
// While the message box is up, the main thread is still copying files,
// and it might just complete. If that happens, the main thread will
// post us WMX_KILLDIALOG, which would cause this dialog to nuke itself
// out from under the message box. The main thread would then continue
// executing while the message box is sitting there. Some components
// actually unload setupapi.dll at that point, and so when the user
// dismisses the message box, an AV results.
//
// We can't freeze the main thread via SuspendThread because then
// that thread, which probably owns this dialog's parent window,
// will not be able to process messages that result from the message box's
// creation. Result is that the message box never comes up and the process
// is deadlocked.
//
// We'd better not already have a message box up!
//
MYASSERT(!Context->MessageBoxUp);
Context->MessageBoxUp = TRUE; i = MessageBox( hdlg, p, TEXT(""), MB_YESNO | MB_APPLMODAL | MB_DEFBUTTON2 | MB_SETFOREGROUND | MB_ICONQUESTION );
Context->MessageBoxUp = FALSE;
//
// We set b to TRUE if the dialog is going away.
// We set Cancelled to TRUE if the user clicked the CANCEL button.
//
if(Context->DialogKilled) { b = TRUE; Cancelled = (i == IDYES); } else { b = (i == IDYES); Cancelled = b; } MyFree(p); } else { pSetupOutOfMemory(hdlg); Cancelled = TRUE; b = TRUE; }
if(b) { if(Cancelled) { Context->Cancelled = TRUE; } PostMessage(hdlg,WMX_KILLDIALOG,0,0);
if (Context->PendingUiType != UI_NONE) {
//
// We now allow the main thread to continue. Once we do
// so, the UI parameters that we passed to us are invalid.
// Cancel the pending UI.
//
Context->PendingUiType = UI_NONE; Context->lParam = Context->CancelReturnCode; SetEvent(Context->hEvent); // wake up main thread (lParam has UI result)
}
} else { if (Context->PendingUiType != UI_NONE) { Context->lParam = pPerformUi(Context, (UINT)Context->PendingUiType, Context->PendingUiParameters);
Context->PendingUiType = UI_NONE; SetEvent(Context->hEvent); // wake up main thread (lParam has UI result)
} } b = TRUE; } else { b = FALSE; } break;
case WMX_KILLDIALOG: //
// Exit unconditionally. Clean up first.
//
b = TRUE; Context = (PQUEUECONTEXT)GetProp(hdlg, DialogPropName); MYASSERT(Context != NULL); if(Context->MessageBoxUp) { //
// The user was still interacting with the "are you sure you
// want to cancel" dialog and the copying finished. So we don't want
// to nuke the dialog out from under the message box.
//
Context->DialogKilled = TRUE; break; }
DestroyWindow(Context->ProgressBar); EndDialog(hdlg, 0);
break;
default: //
// we disable autorun because it confuses the user.
//
if (!uQueryCancelAutoPlay) { uQueryCancelAutoPlay = RegisterWindowMessage(TEXT("QueryCancelAutoPlay")); }
if (msg == uQueryCancelAutoPlay) { SetWindowLongPtr( hdlg, DWLP_MSGRESULT, 1 ); return 1; // cancel auto-play
}
b = FALSE; break; }
return(b); }
PVOID SetupInitDefaultQueueCallbackEx( IN HWND OwnerWindow, IN HWND AlternateProgressWindow, OPTIONAL IN UINT ProgressMessage, IN DWORD Reserved1, IN PVOID Reserved2 ) { PQUEUECONTEXT Context; BOOL b;
Context = MyMalloc(sizeof(QUEUECONTEXT)); if(Context) { ZeroMemory(Context,sizeof(QUEUECONTEXT));
Context->Signature = QUEUECONTEXT_SIGNATURE; Context->hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); if (Context->hEvent == NULL) { MyFree(Context); return NULL; } Context->OwnerWindow = OwnerWindow; Context->MainThreadId = GetCurrentThreadId(); Context->ProgressMsg = ProgressMessage; Context->NoToAllMask = 0;
//
// If the caller specified NULL for the alternate progress window, and
// we're running non-interactively, we want to treat this just as if
// they'd specified INVALID_HANDLE_VALUE (i.e., suppress all progress
// UI).
//
if((GlobalSetupFlags & PSPGF_NONINTERACTIVE) && !AlternateProgressWindow) { Context->AlternateProgressWindow = INVALID_HANDLE_VALUE; } else { Context->AlternateProgressWindow = AlternateProgressWindow; }
if(SystemParametersInfo(SPI_GETSCREENREADER,0,&b,0) && b) { Context->ScreenReader = TRUE; } else { Context->ScreenReader = FALSE; }
#ifdef PRERELEASE
//
// if we're running in gui-mode setup, we suppress all setupapi progress UI
// and only allow AlternateProgressWindow UI
//
if (GuiSetupInProgress && (Context->AlternateProgressWindow != (HWND)INVALID_HANDLE_VALUE) && !IsWindow(Context->AlternateProgressWindow)) { 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")); } #endif
}
return(Context); }
PVOID SetupInitDefaultQueueCallback( IN HWND OwnerWindow ) { #ifdef PRERELEASE
if (GuiSetupInProgress) { 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")); } #endif
return(SetupInitDefaultQueueCallbackEx(OwnerWindow,NULL,0,0,NULL)); }
VOID SetupTermDefaultQueueCallback( IN PVOID Context ) { PQUEUECONTEXT context;
context = Context;
try { if(context && context->Signature == QUEUECONTEXT_SIGNATURE) { if(context->CurrentSourceName) { MyFree(context->CurrentSourceName); } if (context->hEvent) { CloseHandle(context->hEvent); } context->Signature = 0; } MyFree(Context); } except(EXCEPTION_EXECUTE_HANDLER) { ; } }
#ifdef UNICODE
//
// ANSI version
//
UINT SetupDefaultQueueCallbackA( IN PVOID Context, IN UINT Notification, IN UINT_PTR Param1, IN UINT_PTR Param2 ) { UINT u;
u = pSetupCallDefaultMsgHandler( Context, Notification, Param1, Param2 );
return(u); } #else
//
// Unicode stub
//
UINT SetupDefaultQueueCallbackW( IN PVOID Context, IN UINT Notification, IN UINT_PTR Param1, IN UINT_PTR Param2 ) { UNREFERENCED_PARAMETER(Context); UNREFERENCED_PARAMETER(Notification); UNREFERENCED_PARAMETER(Param1); UNREFERENCED_PARAMETER(Param2); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(0); } #endif
UINT SetupDefaultQueueCallback( IN PVOID Context, IN UINT Notification, IN UINT_PTR Param1, IN UINT_PTR Param2 ) { UINT rc; DWORD err; PQUEUECONTEXT context = Context; MSG msg; VERDLGCONTEXT dialogContext; DWORD waitResult;
//
// a little sanity check on context
//
err = NO_ERROR; try { if (context == NULL || context->Signature != QUEUECONTEXT_SIGNATURE) { err = ERROR_INVALID_PARAMETER; } } except(EXCEPTION_EXECUTE_HANDLER) { err = ERROR_INVALID_PARAMETER; } if (err != NO_ERROR) { return pGetCallbackErrorReturn(Notification,err); }
switch(Notification) {
case SPFILENOTIFY_STARTQUEUE: rc = pNotificationStartQueue(context); break;
case SPFILENOTIFY_ENDQUEUE: //
// Make sure the progress dialog is dead.
//
if(context->AlternateProgressWindow) { //
// If we have an alternate progress window, then we'd better not
// have our own progress dialog, nor should we have a UI thread
// handle.
//
MYASSERT(!context->ProgressDialog); MYASSERT(!context->UiThreadHandle);
} else {
if(IsWindow(context->ProgressDialog)) { //
// Post a message to the dialog, instructing it to terminate,
// then wait for it to confirm.
//
PostMessage(context->ProgressDialog, WMX_KILLDIALOG, 0, 0); } //
// The dialog may have been marked for delete (thus IsWindow() fails),
// and yet the dialog has not yet been destroyed. Therefore, we always
// want to wait for the thread message that assures us that everything
// has been cleaned up in the other thread.
//
// Also, before returning we need to make sure that the UI thread is
// really gone, or else there's a hole where our caller could unload
// the library and then the UI thread would fault.
// We have seen this happen in stress.
//
while (pWaitForUiResponse(context)) /* nothing */; if(context->UiThreadHandle) { waitResult = WaitForSingleObject(context->UiThreadHandle,INFINITE); MYASSERT(waitResult != WAIT_FAILED); CloseHandle(context->UiThreadHandle); } } rc = TRUE; // return value for this notification is actually ignored.
break;
case SPFILENOTIFY_STARTSUBQUEUE: rc = pNotificationStartEndSubqueue(context,TRUE,Param1,Param2); break;
case SPFILENOTIFY_ENDSUBQUEUE: rc = pNotificationStartEndSubqueue(context,FALSE,Param1,0); break;
case SPFILENOTIFY_STARTDELETE: case SPFILENOTIFY_STARTRENAME: case SPFILENOTIFY_STARTCOPY: case SPFILENOTIFY_STARTBACKUP: //
// Update display to indicate the files involved
// in the operation, unless a screen reader is active.
//
if(context->ScreenReader) { rc = FILEOP_DOIT; } else { rc = pNotificationStartOperation(context,(PFILEPATHS)Param1,Param2); } break;
case SPFILENOTIFY_ENDDELETE: case SPFILENOTIFY_ENDRENAME: case SPFILENOTIFY_ENDCOPY: case SPFILENOTIFY_ENDBACKUP: case SPFILENOTIFY_ENDREGISTRATION:
if(context->AlternateProgressWindow) { //
// If this is really is an alternate progress window, then 'tick' it.
// Copy only.
//
if((Notification == SPFILENOTIFY_ENDCOPY) && (context->AlternateProgressWindow != INVALID_HANDLE_VALUE)) {
SendMessage(context->AlternateProgressWindow, context->ProgressMsg, 1, 0); } } else { if(IsWindow(context->ProgressBar)) { //
// Update gas gauge.
//
SendMessage(context->ProgressBar,PBM_STEPIT,0,0); } } rc = TRUE; // return value for these notifications is actually ignored.
break;
case SPFILENOTIFY_DELETEERROR: rc = pNotificationErrorDelete(context,(PFILEPATHS)Param1); break;
case SPFILENOTIFY_RENAMEERROR: rc = pNotificationErrorRename(context,(PFILEPATHS)Param1); break;
case SPFILENOTIFY_BACKUPERROR: rc = pNotificationErrorBackup(context,(PFILEPATHS)Param1); break;
case SPFILENOTIFY_COPYERROR: rc = pNotificationErrorCopy(context,(PFILEPATHS)Param1,(PTSTR)Param2); break;
case SPFILENOTIFY_NEEDMEDIA: //
// Perform prompt.
//
rc = pNotificationNeedMedia(context,(PSOURCE_MEDIA)Param1,(PTSTR)Param2); break;
case SPFILENOTIFY_STARTREGISTRATION: rc = pNotificationStartRegistration(context,(PSP_REGISTER_CONTROL_STATUS)Param1,(BOOL)Param2); break;
default: //
// The notification is either an unknown ordinal or a version mismatch.
//
if(Notification & (SPFILENOTIFY_LANGMISMATCH | SPFILENOTIFY_TARGETNEWER | SPFILENOTIFY_TARGETEXISTS)) { //
// It's one or more of our known version mismatches. First
// check to see whether No to All has already been specified
// for the mismatch(es). Turn off the bits in the notification
// that are set in NoToAllMask; if there are still bits set,
// we need to notify about this mismatch. If there are no
// longer any bits set, then don't copy this file.
//
Notification &= ~context->NoToAllMask; if (Notification != 0) { //
// Notify about this mismatch.
//
dialogContext.QueueContext = context; dialogContext.Notification = Notification; dialogContext.Param1 = Param1; dialogContext.Param2 = Param2; rc = PostUiMessage ( context, UI_MISMATCHERROR, DPROMPT_CANCEL, &dialogContext); rc = (rc == IDYES); } else { //
// No To All has already been specified for this notification type.
// Skip the file.
//
rc = 0; }
} else { //
// Unknown notification. Skip the file.
//
rc = 0; } break; }
return(rc); }
|