#include "precomp.h" #pragma hdrstop #include typedef struct _SETUP_PAGE { POC_MANAGER OcManager; SETUP_PAGE_CONTROLS ControlsInfo; HSPFILEQ FileQueue; PVOID QueueContext; UINT StepCount; BOOL ForceExternalProgressIndicator; PUINT ComponentTickCounts; PUINT ComponentMaxTickCounts; LONG CurrentTopLevelComponentIndex; BOOL AllowCancel; HWND hdlg; BOOL UserClickedCancel; DWORD RefCount; // Boolean to track NeedMedia requests. NeedMedia notification is sent always by Setupapi the first time it // tries to access a new media. We need to figure out if the media is actually inaccessible by checking if setupapi // called us a second time on the same media. The Boolean is checked and set in NeedMedia and cleared on an // SPFILENOTIFY_ENDCOPY or SPFILENOTIFY_STARTSUBQUEUE. BOOL SecondNeedMedia; } SETUP_PAGE, *PSETUP_PAGE; #define WMX_SETUP (WM_APP+4537) #define WMX_TICK (WM_APP+4538) #define OCSETUPSTATE_INIT 0 #define OCSETUPSTATE_QUEUE 1 #define OCSETUPSTATE_GETSTEP 2 #define OCSETUPSTATE_DOIT 3 #define OCSETUPSTATE_COPYDONE 4 #define OCSETUPSTATE_DONE 100 #define OCSETUPSTATE_COPYABORT 101 typedef struct _GEN_THREAD_PARAMS { HWND hdlg; PSETUP_PAGE SetupPage; BOOL Async; } GEN_THREAD_PARAMS, *PGEN_THREAD_PARAMS; TCHAR g_LastFileCopied[MAX_PATH]; #ifdef UNICODE HANDLE hSfp = NULL; #endif HANDLE WorkerThreadHandle = NULL; INT_PTR SetupPageDialogProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ); BOOL pOcSetupInitialize( IN OUT PSETUP_PAGE SetupPage, IN HWND hdlg ); VOID pOcSetupStartWorkerThread( IN OUT PSETUP_PAGE SetupPage, IN HWND hdlg, IN LPTHREAD_START_ROUTINE ThreadRoutine ); DWORD pOcSetupQueue( IN PGEN_THREAD_PARAMS Params ); UINT pOcSetupQueueWorker( IN PSETUP_PAGE SetupPage, IN LONG StringId, IN LONG TopLevelStringId ); DWORD pOcSetupGetStepCount( IN PGEN_THREAD_PARAMS Params ); UINT pOcSetupGetStepCountWorker( IN PSETUP_PAGE SetupPage, IN LONG StringId, IN LONG TopLevelStringId ); DWORD pOcSetupDoIt( IN PGEN_THREAD_PARAMS Params ); VOID pOcPreOrPostCommitProcessing( IN OUT PSETUP_PAGE SetupPage, IN BOOL PreCommit ); VOID pOcTopLevelPreOrPostCommitProcessing( IN PSETUP_PAGE SetupPage, IN BOOL PreCommit ); VOID pOcSetupDoItWorker( IN PSETUP_PAGE SetupPage, IN LONG StringId, IN LONG TopLevelStringId, IN BOOL PreCommit ); BOOL pOcMarkUnprocessedStringCB( IN PVOID StringTable, IN LONG StringId, IN PCTSTR String, IN POPTIONAL_COMPONENT Oc, IN UINT OcSize, IN LPARAM Unused ); VOID _pOcExternalProgressIndicator( IN PSETUP_PAGE SetupPage, IN BOOL ExternalIndicator, IN HWND hdlg ); extern POC_MANAGER gLastOcManager; WNDPROC OldProgressProc; BOOL NewProgessProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) { switch (msg) { case PBM_DELTAPOS: case PBM_SETRANGE: case PBM_SETRANGE32: case PBM_STEPIT: case PBM_SETPOS: case PBM_SETSTEP: // If we have a callback, use it. if ((gLastOcManager) && (gLastOcManager->Callbacks.BillboardProgressCallback)) { gLastOcManager->Callbacks.BillboardProgressCallback(msg, wParam, lParam); } break; } return (BOOL)CallWindowProc(OldProgressProc,hdlg,msg,wParam,lParam); } HPROPSHEETPAGE OcCreateSetupPage( IN PVOID OcManagerContext, IN PSETUP_PAGE_CONTROLS ControlsInfo ) /*++ Routine Description: This routine creates the wizard page used for progress and installation completion. Arguments: OcManagerContext - supplies OC Manager context returned by OcInitialize. ControlsInfo - supplies information about the dialog template and control information. Return Value: Handle to property sheet page, or NULL if error (such as out of memory). --*/ { PROPSHEETPAGE Page; HPROPSHEETPAGE PageHandle; PSETUP_PAGE SetupPage; TCHAR buffer[256]; POC_MANAGER OcManager = (POC_MANAGER)OcManagerContext; SetupPage = pSetupMalloc(sizeof(SETUP_PAGE)); if (!SetupPage) { return (NULL); } ZeroMemory(SetupPage,sizeof(SETUP_PAGE)); SetupPage->OcManager = OcManagerContext; SetupPage->ControlsInfo = *ControlsInfo; SetupPage->CurrentTopLevelComponentIndex = -1; SetupPage->ForceExternalProgressIndicator = ControlsInfo->ForceExternalProgressIndicator; SetupPage->AllowCancel = ControlsInfo->AllowCancel; SetupPage->SecondNeedMedia = FALSE; InterlockedIncrement( &SetupPage->RefCount ); SetupPage->ComponentTickCounts = pSetupMalloc(SetupPage->OcManager->TopLevelOcCount * sizeof(UINT)); if (!SetupPage->ComponentTickCounts) { pSetupFree(SetupPage); return (NULL); } SetupPage->ComponentMaxTickCounts = pSetupMalloc(SetupPage->OcManager->TopLevelOcCount * sizeof(UINT)); if (!SetupPage->ComponentMaxTickCounts) { pSetupFree(SetupPage->ComponentTickCounts); pSetupFree(SetupPage); return (NULL); } Page.dwSize = sizeof(PROPSHEETPAGE); Page.dwFlags = PSP_DEFAULT; Page.hInstance = ControlsInfo->TemplateModule; Page.pszTemplate = ControlsInfo->TemplateResource; Page.pfnDlgProc = SetupPageDialogProc; Page.lParam = (LPARAM)SetupPage; Page.pszHeaderTitle = NULL; Page.pszHeaderSubTitle = NULL; if (SetupPage->OcManager->SetupPageTitle[0]) { Page.dwFlags |= PSP_USETITLE; Page.pszTitle = SetupPage->OcManager->SetupPageTitle; } if (ControlsInfo->HeaderText) { if (LoadString(Page.hInstance, ControlsInfo->HeaderText, buffer, sizeof(buffer) / sizeof(TCHAR))) { Page.dwFlags |= PSP_USEHEADERTITLE; Page.pszHeaderTitle = _tcsdup(buffer); } } if (ControlsInfo->SubheaderText) { if (LoadString(Page.hInstance, ControlsInfo->SubheaderText, buffer, sizeof(buffer) / sizeof(TCHAR))) { Page.dwFlags |= PSP_USEHEADERSUBTITLE; Page.pszHeaderSubTitle = _tcsdup(buffer); } } PageHandle = CreatePropertySheetPage(&Page); if (!PageHandle) { pSetupFree(SetupPage->ComponentTickCounts); pSetupFree(SetupPage->ComponentMaxTickCounts); pSetupFree(SetupPage); if (Page.pszHeaderTitle) { free((LPTSTR)Page.pszHeaderTitle); } if (Page.pszHeaderSubTitle) { free((LPTSTR)Page.pszHeaderSubTitle); } } else { OcManager->OcSetupPage = (PVOID) SetupPage; } return (PageHandle); } VOID pOcFreeOcSetupPage( IN PVOID pSetupPage ) /*++ Routine Description: This routine frees the setup page when it's not needed anymore. The routine uses a ref-count, and the page is only freed when the refcount drops to zero. Arguments: SetupPage - pointer to structure to be freed Return Value: None. --*/ { PSETUP_PAGE SetupPage = (PSETUP_PAGE)pSetupPage; sapiAssert( SetupPage != NULL ); // TRACE(( TEXT("pOcFreeOcSetupPage: Refcount = %d\n"), SetupPage->RefCount )); if (!InterlockedDecrement( &SetupPage->RefCount )) { // TRACE(( TEXT("pOcFreeOcSetupPage: Refcount = 0, freeing SetupPage\n") )); if (SetupPage->QueueContext) { SetupTermDefaultQueueCallback(SetupPage->QueueContext); } if (SetupPage->FileQueue) { SetupCloseFileQueue(SetupPage->FileQueue); } pSetupFree(SetupPage->ComponentTickCounts); pSetupFree(SetupPage->ComponentMaxTickCounts); pSetupFree(SetupPage); } return; } BOOL pOcDisableCancel( IN HWND hdlg ) /*++ Routine Description: This routine disables cancelling of ocm setup. Arguments: hdlg - window handle to the ocm dialog Return Value: TRUE if we succeed, else FALSE --*/ { HMENU hMenu; // // hide the cancel button // EnableWindow(GetDlgItem(GetParent(hdlg),IDCANCEL),FALSE); ShowWindow(GetDlgItem(GetParent(hdlg),IDCANCEL),SW_HIDE); if(hMenu = GetSystemMenu(GetParent(hdlg),FALSE)) { EnableMenuItem(hMenu,SC_CLOSE,MF_BYCOMMAND|MF_GRAYED); } return TRUE; } VOID PumpMessageQueue( VOID ) { MSG msg; while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { DispatchMessage(&msg); } } INT_PTR SetupPageDialogProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) { BOOL b; NMHDR *NotifyParams; PSETUP_PAGE SetupPage; DWORD Timeout; DWORD WaitProcStatus; BOOL KeepWaiting = TRUE; // // Get pointer to SetupPage data structure. If we haven't processed // WM_INITDIALOG yet, then this will be NULL, but it's still pretty // convenient to do this here once instead of all over the place below. // SetupPage = (PSETUP_PAGE)GetWindowLongPtr(hdlg,DWLP_USER); b = FALSE; switch (msg) { case WM_INITDIALOG: // // Get the pointer to the Setup Page context structure and stick it // in a window long. // SetWindowLongPtr(hdlg,DWLP_USER,((PROPSHEETPAGE *)lParam)->lParam); b = TRUE; // // eat any extra press button messages // this is necessary because netsetup is broken // it is posting an extra PSM_PRESSBUTTON message // to the wizard. // { MSG msg; HWND hwnd=GetParent(hdlg); while (PeekMessage(&msg,hwnd,PSM_PRESSBUTTON,PSM_PRESSBUTTON,PM_REMOVE)){} } break; case WM_SYSCOMMAND: if (!SetupPage->AllowCancel && wParam == SC_CLOSE) { return TRUE; } b = FALSE; break; case WM_DESTROY: PumpMessageQueue(); if (WorkerThreadHandle) { BOOL Done = FALSE; do{ switch (MsgWaitForMultipleObjects( 1, &WorkerThreadHandle, FALSE, 60*1000*20, QS_ALLINPUT)){ case WAIT_OBJECT_0+1: // // Messages in the queue. // PumpMessageQueue(); break; case WAIT_TIMEOUT: case WAIT_OBJECT_0: default: Done = TRUE; break; } }while( !Done ); CloseHandle( WorkerThreadHandle ); } if (SetupPage) { pOcFreeOcSetupPage( SetupPage ); } SetWindowLongPtr(hdlg,DWLP_USER,(LPARAM)NULL); break; case WM_NOTIFY: NotifyParams = (NMHDR *)lParam; switch (NotifyParams->code) { case PSN_SETACTIVE: #ifdef UNICODE if (SetupPage->OcManager->Callbacks.SetupPerfData) SetupPage->OcManager->Callbacks.SetupPerfData(TEXT(__FILE__),__LINE__,L"BEGIN_SECTION",L"OCSetup"); #endif // activate the cancel button accordingly if (SetupPage->AllowCancel) { ShowWindow(GetDlgItem(GetParent(hdlg),IDCANCEL),SW_SHOW); EnableWindow(GetDlgItem(GetParent(hdlg),IDCANCEL),TRUE); } else { ShowWindow(GetDlgItem(GetParent(hdlg),IDCANCEL),SW_HIDE); EnableWindow(GetDlgItem(GetParent(hdlg),IDCANCEL),FALSE); } if (SetupPage->OcManager->Callbacks.ShowHideWizardPage) { // If we have a callback, hide the wizard. SetupPage->OcManager->Callbacks.ShowHideWizardPage(FALSE); } OldProgressProc = (WNDPROC)SetWindowLongPtr(GetDlgItem(hdlg,SetupPage->ControlsInfo.ProgressBar), GWLP_WNDPROC, (LONG_PTR)NewProgessProc); // // Post a message that causes us to start the installation process. // PostMessage(hdlg,WMX_SETUP,OCSETUPSTATE_INIT,0); // // Accept activation. // SetWindowLongPtr(hdlg,DWLP_MSGRESULT,0); b = TRUE; break; case PSN_KILLACTIVE: // // Restore the wizard's cancel button if we removed it earlier // if (!SetupPage->AllowCancel) { ShowWindow(GetDlgItem(GetParent(hdlg),IDCANCEL),SW_SHOW); EnableWindow(GetDlgItem(GetParent(hdlg),IDCANCEL),TRUE); } // // Accept deactivation. // SetWindowLongPtr(hdlg,DWLP_MSGRESULT,0); b = TRUE; break; case PSN_QUERYCANCEL: if (!SetupPage->AllowCancel) { SetWindowLongPtr(hdlg,DWLP_MSGRESULT,TRUE); return(TRUE); } if ( (SetupPage->OcManager->InternalFlags & OCMFLAG_FILEABORT ) || (OcHelperConfirmCancel(hdlg) )){ b = TRUE; SetupPage->OcManager->InternalFlags |= OCMFLAG_USERCANCELED; SetupPage->UserClickedCancel = TRUE; } SetWindowLongPtr(hdlg,DWLP_MSGRESULT,!b); b = TRUE; break; } break; case WMX_SETUP: switch (wParam) { case OCSETUPSTATE_INIT: // // Initialize. // if (SetupPage->ForceExternalProgressIndicator) { _pOcExternalProgressIndicator(SetupPage,TRUE,hdlg); } PropSheet_SetWizButtons(GetParent(hdlg),0); // // If this a remove all, disable the cancel button early // if ((SetupPage->OcManager->SetupMode & SETUPMODE_PRIVATE_MASK) == SETUPMODE_REMOVEALL) { if (!SetupPage->AllowCancel) { EnableWindow(GetDlgItem(GetParent(hdlg),IDCANCEL),FALSE); ShowWindow(GetDlgItem(GetParent(hdlg),IDCANCEL),SW_HIDE); } } if (pOcSetupInitialize(SetupPage,hdlg)) { PostMessage(hdlg,WMX_SETUP,OCSETUPSTATE_QUEUE,0); } else { PostMessage(hdlg,WMX_SETUP,OCSETUPSTATE_COPYABORT,0); } break; case OCSETUPSTATE_QUEUE: // // Queue files for installation. // pOcSetupStartWorkerThread(SetupPage,hdlg,pOcSetupQueue); break; case OCSETUPSTATE_GETSTEP: // // Figure out step counts. // pOcSetupStartWorkerThread(SetupPage,hdlg,pOcSetupGetStepCount); break; case OCSETUPSTATE_DOIT: // // Quick init of the gas guage here, because the file queue could be // empty, in which case we never get WMX_TICK with wParam=0. // SendDlgItemMessage( hdlg, SetupPage->ControlsInfo.ProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0,SetupPage->StepCount) ); SendDlgItemMessage( hdlg, SetupPage->ControlsInfo.ProgressBar, PBM_SETPOS, 0, 0 ); SetCursor(LoadCursor(NULL,IDC_ARROW)); // // Commit the file queue and let the OCs install themselves. // pOcSetupStartWorkerThread(SetupPage,hdlg,pOcSetupDoIt); break; // // Unrecoverable error in copyfile phase, abort the setup // case OCSETUPSTATE_COPYABORT: SetupPage->OcManager->InternalFlags |= OCMFLAG_FILEABORT; if (SetupPage->AllowCancel && SetupPage->OcManager->SetupData.OperationFlags & SETUPOP_STANDALONE) { PropSheet_PressButton(GetParent(hdlg),PSBTN_CANCEL); } else { PropSheet_PressButton(GetParent(hdlg),PSBTN_NEXT); } break; case OCSETUPSTATE_COPYDONE: // // Get rid of the wizard's cancel button // // // AndrewR -- we've already committed the file queue // at this point, so we should not allow the user to cancel // (since: // a) in an uninstall scenario the file state and // configuration state will be out of sync // b) we don't call all of the OC components to let them know // about the cancel event, and we don't want only some of the // components to get a complete installation callback // //if(!SetupPage->AllowCancel) { SetupPage->AllowCancel = FALSE; pOcDisableCancel(hdlg); // } break; case OCSETUPSTATE_DONE: // // Done. Advance to next page in wizard. // PropSheet_SetWizButtons(GetParent(hdlg),PSWIZB_NEXT); PropSheet_PressButton(GetParent(hdlg),PSBTN_NEXT); #ifdef UNICODE if (SetupPage->OcManager->Callbacks.SetupPerfData) SetupPage->OcManager->Callbacks.SetupPerfData(TEXT(__FILE__),__LINE__,L"END_SECTION",L"OCSetup"); #endif // un-subclass the progress bar. just in case SetWindowLongPtr(GetDlgItem(hdlg,SetupPage->ControlsInfo.ProgressBar), GWLP_WNDPROC, (LONG_PTR)OldProgressProc); // // Clear user canceled flag, // SetupPage->OcManager->InternalFlags &= ~ OCMFLAG_USERCANCELED; break; } b = TRUE; break; case WMX_TICK: switch (wParam) { case 0: // // The setup API queue commit routine is telling us how many // files are to be copied. We do nothing in this case, as we // set the progress guage manually so that we also count // delete operations in our progress guage. // break; case 1: // // File copied. // SendDlgItemMessage(hdlg,SetupPage->ControlsInfo.ProgressBar,PBM_DELTAPOS,1,0); break; case 10: // // We got our private message telling us how many files are // to be processed. see comments above in the 0 case. // SendDlgItemMessage( hdlg, SetupPage->ControlsInfo.ProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0,lParam) ); break; case 500: // // Incoming tick request from component dll. Don't allow a broken component dll // to tick the gauge more than it said it wanted to. // if ((SetupPage->CurrentTopLevelComponentIndex != -1) && (SetupPage->ComponentTickCounts[SetupPage->CurrentTopLevelComponentIndex] < SetupPage->ComponentMaxTickCounts[SetupPage->CurrentTopLevelComponentIndex])) { SetupPage->ComponentTickCounts[SetupPage->CurrentTopLevelComponentIndex]++; SendDlgItemMessage(hdlg,SetupPage->ControlsInfo.ProgressBar,PBM_DELTAPOS,1,0); } break; } b = TRUE; break; } return (b); } VOID pOcTickSetupGauge( IN POC_MANAGER OcManager ) /*++ Routine Description: The tick gauge OC helper/callback routine calls this routine. Arguments: OcManager - supplies OC Manager context. Return Value: None. --*/ { // // The ProgressTextWindow is non-NULL if we are in // the installation-completion phase. // if (OcManager->ProgressTextWindow) { SendMessage(GetParent(OcManager->ProgressTextWindow),WMX_TICK,500,0); } } BOOL pOcSetupInitialize( IN OUT PSETUP_PAGE SetupPage, IN HWND hdlg ) { TCHAR Text[128]; LoadString(MyModuleHandle,IDS_INITIALIZING,Text,sizeof(Text)/sizeof(TCHAR)); SetDlgItemText(hdlg,SetupPage->ControlsInfo.ProgressText,Text); // If, update the text on the billboard for the progress bar. if (SetupPage->OcManager->Callbacks.BillBoardSetProgressText) { SetupPage->OcManager->Callbacks.BillBoardSetProgressText(Text); } // // Create a setup file queue. // SetupPage->FileQueue = SetupOpenFileQueue(); if (SetupPage->FileQueue == INVALID_HANDLE_VALUE) { _LogError(SetupPage->OcManager,OcErrLevFatal,MSG_OC_OOM); SetupPage->FileQueue = NULL; return (FALSE); } SetupPage->QueueContext = SetupInitDefaultQueueCallbackEx(hdlg,hdlg,WMX_TICK,0,0); if (!SetupPage->QueueContext) { _LogError(SetupPage->OcManager,OcErrLevFatal,MSG_OC_OOM); SetupCloseFileQueue(SetupPage->FileQueue); SetupPage->FileQueue = NULL; return (FALSE); } return (TRUE); } VOID pOcSetupStartWorkerThread( IN OUT PSETUP_PAGE SetupPage, IN HWND hdlg, IN LPTHREAD_START_ROUTINE ThreadRoutine ) { PGEN_THREAD_PARAMS pParams; GEN_THREAD_PARAMS Params; HANDLE h; DWORD id; if (WorkerThreadHandle) { CloseHandle( WorkerThreadHandle ); WorkerThreadHandle = NULL; } if (pParams = pSetupMalloc(sizeof(GEN_THREAD_PARAMS))) { pParams->SetupPage = SetupPage; pParams->SetupPage->hdlg = hdlg; pParams->hdlg = hdlg; pParams->Async = TRUE; h = CreateThread(NULL,0,ThreadRoutine,pParams,0,&id); if (!h) { pSetupFree(pParams); } else { WorkerThreadHandle = h; } } else { h = NULL; } if (!h) { // // Just try it synchronously. // Params.SetupPage = SetupPage; Params.hdlg = hdlg; Params.Async = FALSE; ThreadRoutine(&Params); } } // // a debugging routine that makes it easy to cancel at any phase of setup // /* VOID CancelRoutine( VOID ) { static int i = 0; TCHAR dbg[100]; wsprintf( dbg, TEXT("cancel routine iteration number %i \n"), i); OutputDebugString( dbg ); OutputDebugString( TEXT(" waiting 5 seconds for cancel ... \n" )); Sleep( 1000 * 5 ); OutputDebugString( TEXT(" done waiting for cancel ... \n" )); i++; } */ BOOL CheckForQueueCancel( PSETUP_PAGE SetupPage ) { BOOL bRet; //CancelRoutine(); bRet = SetupPage->UserClickedCancel; return (bRet); } DWORD pOcSetupQueue( IN PGEN_THREAD_PARAMS Params ) { UINT Err; unsigned i,child; OPTIONAL_COMPONENT Oc; TCHAR Text[128]; DWORD RetVal; InterlockedIncrement( &Params->SetupPage->RefCount ); LoadString(MyModuleHandle,IDS_BUILDINGCOPYLIST,Text,sizeof(Text)/sizeof(TCHAR)); #ifdef UNICODE if (Params->SetupPage->OcManager->Callbacks.SetupPerfData) Params->SetupPage->OcManager->Callbacks.SetupPerfData(TEXT(__FILE__),__LINE__,L"BEGIN_SECTION",Text); // If, update the text on the billboard for the progress bar. if (Params->SetupPage->OcManager->Callbacks.BillBoardSetProgressText) { Params->SetupPage->OcManager->Callbacks.BillBoardSetProgressText(Text); } #endif SetDlgItemText(Params->hdlg,Params->SetupPage->ControlsInfo.ProgressText,Text); if (CheckForQueueCancel(Params->SetupPage)) { RetVal = NO_ERROR; goto exit; } // // Handle each component. // for (i=0; iSetupPage->OcManager->TopLevelOcCount; i++) { pSetupStringTableGetExtraData( Params->SetupPage->OcManager->ComponentStringTable, Params->SetupPage->OcManager->TopLevelOcStringIds[i], &Oc, sizeof(OPTIONAL_COMPONENT) ); // // Call the component dll once for the entire component. // Err = OcInterfaceQueueFileOps( Params->SetupPage->OcManager, Params->SetupPage->OcManager->TopLevelOcStringIds[i], NULL, Params->SetupPage->FileQueue ); if (Err != NO_ERROR) { // // Notify user and continue. // _LogError( Params->SetupPage->OcManager, OcErrLevError, MSG_OC_CANT_QUEUE_FILES, Oc.Description, Err ); } if (CheckForQueueCancel(Params->SetupPage)) { RetVal = NO_ERROR; goto exit; } // // Process each top level parent item in the tree // for (child=0; childSetupPage->OcManager->TopLevelParentOcCount; child++) { Err = pOcSetupQueueWorker( Params->SetupPage, Params->SetupPage->OcManager->TopLevelParentOcStringIds[child], Params->SetupPage->OcManager->TopLevelOcStringIds[i] ); if (Err != NO_ERROR) { // // Notification is handled in the worker routine so nothing to do here. // } } if (CheckForQueueCancel(Params->SetupPage)) { RetVal = NO_ERROR; goto exit; } } if (CheckForQueueCancel(Params->SetupPage)) { RetVal = NO_ERROR; goto exit; } PostMessage(Params->hdlg,WMX_SETUP,OCSETUPSTATE_GETSTEP,0); exit: #ifdef UNICODE if (Params->SetupPage->OcManager->Callbacks.SetupPerfData) Params->SetupPage->OcManager->Callbacks.SetupPerfData(TEXT(__FILE__),__LINE__,L"END_SECTION",Text); #endif pOcFreeOcSetupPage( Params->SetupPage ); if (Params->Async) { pSetupFree(Params); } return (RetVal); } UINT pOcSetupQueueWorker( IN PSETUP_PAGE SetupPage, IN LONG StringId, IN LONG TopLevelStringId ) { OPTIONAL_COMPONENT Oc; UINT Err; LONG Id; // // Fetch extra data for this subcomponent. // pSetupStringTableGetExtraData( SetupPage->OcManager->ComponentStringTable, StringId, &Oc, sizeof(OPTIONAL_COMPONENT) ); // // If it's a child, call the component dll. // If it's a parent, then spin through its children. // if (Oc.FirstChildStringId == -1) { if (TopLevelStringId == pOcGetTopLevelComponent(SetupPage->OcManager,StringId)) { Err = OcInterfaceQueueFileOps( SetupPage->OcManager, pOcGetTopLevelComponent(SetupPage->OcManager,StringId), pSetupStringTableStringFromId(SetupPage->OcManager->ComponentStringTable,StringId), SetupPage->FileQueue ); if (Err != NO_ERROR) { // // Notify user and continue. // _LogError( SetupPage->OcManager, OcErrLevError, MSG_OC_CANT_QUEUE_FILES, Oc.Description, Err ); } } } else { for (Id = Oc.FirstChildStringId; Id != -1; Id = Oc.NextSiblingStringId) { Err = pOcSetupQueueWorker(SetupPage,Id,TopLevelStringId); if (Err != NO_ERROR) { // // Notification is handled in the worker routine so nothing to do here. // } pSetupStringTableGetExtraData( SetupPage->OcManager->ComponentStringTable, Id, &Oc, sizeof(OPTIONAL_COMPONENT) ); } } return (NO_ERROR); } DWORD pOcSetupGetStepCount( IN PGEN_THREAD_PARAMS Params ) { UINT Err; unsigned i,child; OPTIONAL_COMPONENT Oc; UINT StepCount; TCHAR Text[128]; UINT Count; InterlockedIncrement( &Params->SetupPage->RefCount ); LoadString(MyModuleHandle,IDS_PREPARING,Text,sizeof(Text)/sizeof(TCHAR)); SetDlgItemText(Params->hdlg,Params->SetupPage->ControlsInfo.ProgressText,Text); #ifdef UNICODE // If, update the text on the billboard for the progress bar. if (Params->SetupPage->OcManager->Callbacks.BillBoardSetProgressText) { Params->SetupPage->OcManager->Callbacks.BillBoardSetProgressText(Text); } if (Params->SetupPage->OcManager->Callbacks.SetupPerfData) Params->SetupPage->OcManager->Callbacks.SetupPerfData(TEXT(__FILE__),__LINE__,L"BEGIN_SECTION",Text); #endif Params->SetupPage->StepCount = 0; // // Handle each component. // for (i=0; iSetupPage->OcManager->TopLevelOcCount; i++) { // // Call the component dll once for the entire component. // Ignore any error. Later we call per-subcomponent and we'll // assume that any component that gives us an error has 1 step. // Err = OcInterfaceQueryStepCount( Params->SetupPage->OcManager, Params->SetupPage->OcManager->TopLevelOcStringIds[i], NULL, &Count ); StepCount = ((Err == NO_ERROR) ? Count : 0); // // For each top level parent item in the tree find all the children // that belong to this component // for (child=0; childSetupPage->OcManager->TopLevelParentOcCount; child++) { // // Now call the component dll for each child subcomponent. // StepCount += pOcSetupGetStepCountWorker( Params->SetupPage, Params->SetupPage->OcManager->TopLevelParentOcStringIds[child], Params->SetupPage->OcManager->TopLevelOcStringIds[i] ); } if (!StepCount) { // // Make sure each component has at least one step. // StepCount = 1; } Params->SetupPage->StepCount += StepCount; Params->SetupPage->ComponentTickCounts[i] = 0; Params->SetupPage->ComponentMaxTickCounts[i] = StepCount; } if (CheckForQueueCancel(Params->SetupPage)) { goto exit; } PostMessage(Params->hdlg,WMX_SETUP,OCSETUPSTATE_DOIT,0); exit: #ifdef UNICODE if (Params->SetupPage->OcManager->Callbacks.SetupPerfData) Params->SetupPage->OcManager->Callbacks.SetupPerfData(TEXT(__FILE__),__LINE__,L"END_SECTION",Text); #endif pOcFreeOcSetupPage( Params->SetupPage ); if (Params->Async) { pSetupFree(Params); } return (0); } UINT pOcSetupGetStepCountWorker( IN PSETUP_PAGE SetupPage, IN LONG StringId, IN LONG TopLevelStringId ) { OPTIONAL_COMPONENT Oc; UINT Err; LONG Id; UINT Count; UINT TotalCount; TotalCount = 0; Count = 0; // // Fetch extra data for this subcomponent. // pSetupStringTableGetExtraData( SetupPage->OcManager->ComponentStringTable, StringId, &Oc, sizeof(OPTIONAL_COMPONENT) ); // // If it's a child, call the component dll. // If it's a parent, then spin through its children. // if (Oc.FirstChildStringId == -1) { // // Only call the leaf node if the top level component matches // if (TopLevelStringId == pOcGetTopLevelComponent(SetupPage->OcManager,StringId)) { Err = OcInterfaceQueryStepCount( SetupPage->OcManager, pOcGetTopLevelComponent(SetupPage->OcManager,StringId), pSetupStringTableStringFromId(SetupPage->OcManager->ComponentStringTable,StringId), &Count ); if (Err == NO_ERROR) { TotalCount = Count; } } } else { for (Id = Oc.FirstChildStringId; Id != -1; Id = Oc.NextSiblingStringId) { TotalCount += pOcSetupGetStepCountWorker(SetupPage,Id,TopLevelStringId); pSetupStringTableGetExtraData( SetupPage->OcManager->ComponentStringTable, Id, &Oc, sizeof(OPTIONAL_COMPONENT) ); } } return (TotalCount); } BOOL pOcSetRenamesFlag( IN POC_MANAGER OcManager ) { HKEY hKey; long rslt = ERROR_SUCCESS; #ifdef UNICODE rslt = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Control\\Session Manager"), 0, KEY_SET_VALUE, &hKey); if (rslt == ERROR_SUCCESS) { DWORD Value = 1; rslt = RegSetValueEx( hKey, OC_ALLOWRENAME, 0, REG_DWORD, (LPBYTE)&Value, sizeof(DWORD)); RegCloseKey(hKey); if (rslt != ERROR_SUCCESS) { TRACE(( TEXT("couldn't RegSetValueEx, ec = %d\n"), rslt )); } } else { TRACE(( TEXT("couldn't RegOpenKeyEx, ec = %d\n"), rslt )); } #endif return (rslt == ERROR_SUCCESS); } BOOL pOcAttemptQueueAbort( IN UINT Notification, IN PUINT rc ) { // // user has asked to abort installation. We need to hand this request to // setupapi, but setupapi only handles this request from certain // notifications // BOOL bHandled = FALSE; switch (Notification) { case SPFILENOTIFY_STARTQUEUE: case SPFILENOTIFY_STARTSUBQUEUE: SetLastError(ERROR_CANCELLED); *rc = 0; bHandled = TRUE; break; case SPFILENOTIFY_STARTDELETE: case SPFILENOTIFY_STARTBACKUP: case SPFILENOTIFY_STARTRENAME: case SPFILENOTIFY_STARTCOPY: case SPFILENOTIFY_NEEDMEDIA: case SPFILENOTIFY_COPYERROR: case SPFILENOTIFY_DELETEERROR: case SPFILENOTIFY_RENAMEERROR: case SPFILENOTIFY_BACKUPERROR: SetLastError(ERROR_CANCELLED); *rc = FILEOP_ABORT; bHandled = TRUE; break; case SPFILENOTIFY_FILEEXTRACTED: case SPFILENOTIFY_NEEDNEWCABINET: case SPFILENOTIFY_QUEUESCAN: SetLastError(ERROR_CANCELLED); *rc = ERROR_CANCELLED; bHandled = TRUE; break; }; return (bHandled); } UINT OcManagerQueueCallback1( IN PVOID Context, IN UINT Notification, IN UINT_PTR Param1, IN UINT_PTR Param2 ) { PSETUP_PAGE SetupPage = Context; UINT i; BOOL b; TCHAR Text[MAX_PATH*2]; PFILEPATHS pFile = (PFILEPATHS) Param1; PSOURCE_MEDIA sm = (PSOURCE_MEDIA)Param1; static BOOL UserClickedCancel; UINT rc = 0; UINT retval; // // We handle the user cancelling at the beginning of the queue callback. // If the user has cancelled then we don't execute any code, we just return // until we get a callback that allows us to cancel. // // There is a window in this code where the user might cancel after we // check for cancelling but before the queue callback code executes. If we fall into // the WM_DESTROY block in our window proc when this occurs, we cannot send any more // messages to our window. Use PostMessage below to guard against that. top: if (UserClickedCancel) { pOcAttemptQueueAbort(Notification,&rc); return (rc); } if (SetupPage->UserClickedCancel) { UserClickedCancel = TRUE; } if (UserClickedCancel) { goto top; } switch (Notification) { case SPFILENOTIFY_STARTSUBQUEUE: // // Tell the user what's going on. // switch (Param1) { case FILEOP_DELETE: i = IDS_DELETING; break; case FILEOP_RENAME: i = IDS_RENAME; break; case FILEOP_COPY: i = IDS_COPYING; break; default: i = (UINT)(-1); break; } if (i != (UINT)(-1)) { LoadString(MyModuleHandle,i,Text,sizeof(Text)/sizeof(TCHAR)); SetDlgItemText(SetupPage->hdlg,SetupPage->ControlsInfo.ProgressText,Text); // If, update the text on the billboard for the progress bar. if (SetupPage->OcManager->Callbacks.BillBoardSetProgressText) { SetupPage->OcManager->Callbacks.BillBoardSetProgressText(Text); } } //Reset SecondNeedMedia as we are about to begin copying for a particular queue SetupPage->SecondNeedMedia = FALSE; break; case SPFILENOTIFY_STARTCOPY: lstrcpy( g_LastFileCopied, pFile->Target ); #ifdef UNICODE // fall through... case SPFILENOTIFY_STARTDELETE: case SPFILENOTIFY_STARTRENAME: if ((SetupPage->OcManager->SetupData.OperationFlags & SETUPOP_STANDALONE)) { if (!hSfp) { hSfp = SfcConnectToServer( NULL ); } if (hSfp) { if (SfcIsFileProtected(hSfp,pFile->Target)) { SfcFileException( hSfp, (PWSTR) pFile->Target, SFC_ACTION_REMOVED ); } } } #endif break; case SPFILENOTIFY_ENDCOPY: if (pFile->Win32Error == NO_ERROR) { _LogError(SetupPage->OcManager, OcErrLevInfo, MSG_OC_LOG_FILE_COPIED, pFile->Source, pFile->Target); } else { TRACE(( TEXT("OC:OcManagerQueueCallback Copy Error: %s --> %s (%d)\n"), pFile->Source, pFile->Target, pFile->Win32Error)); _LogError(SetupPage->OcManager, OcErrLevInfo, MSG_OC_LOG_FILE_COPY_FAILED, pFile->Source, pFile->Target, pFile->Win32Error); } //Reset SecondNeedMedia as we are at the end of copying files for this media SetupPage->SecondNeedMedia = FALSE; break; case SPFILENOTIFY_ENDDELETE: // fall through case SPFILENOTIFY_ENDRENAME: case SPFILENOTIFY_ENDBACKUP: // // tick the progress gauge manually since setupapi doesn't do it // for us. // SendMessage(SetupPage->hdlg,WMX_TICK,1,0); break; case SPFILENOTIFY_DELETEERROR: // 0x00000007 TRACE(( TEXT("OC:OcManagerQueueCallback Delete Error: %s (%d)\n"), pFile->Target, pFile->Win32Error)); break; case SPFILENOTIFY_RENAMEERROR: // 0x0000000a TRACE(( TEXT("OC:OcManagerQueueCallback Rename Error: %s (%d)\n"), pFile->Target, pFile->Win32Error)); break; case SPFILENOTIFY_COPYERROR: // 0x0000000d TRACE(( TEXT("OC:OcManagerQueueCallback Copy Error: %s (%d)\n"), pFile->Target, pFile->Win32Error)); break; case SPFILENOTIFY_NEEDMEDIA: TRACE(( TEXT("OC:OcManagerQueueCallback Need Media: %s - %s (%s)\n"), sm->SourcePath, sm->SourceFile, sm->Tagfile)); if (gLastOcManager && (gLastOcManager->InternalFlags & OCMFLAG_RUNQUIET)) { // Check if this is the second time we are getting a if (TRUE == SetupPage->SecondNeedMedia) { SetupPage->SecondNeedMedia = FALSE; return (FILEOP_ABORT); }else{ SetupPage->SecondNeedMedia = TRUE; return (FILEOP_DOIT); } } break; case SPFILENOTIFY_FILEOPDELAYED: TRACE(( TEXT("OC:OcManagerQueueCallback FileOpDelayed: %s\n"), pFile->Target )); // // We want to remember that there was at least one file // with a delayed-move, but we still want to let the // default callback get this notification also. // SetupPage->OcManager->InternalFlags |= OCMFLAG_ANYDELAYEDMOVES; SetupPage->OcManager->Callbacks.SetReboot(); pOcSetRenamesFlag(SetupPage->OcManager); for (i=0; (iOcManager->TopLevelOcCount); i++) { OcInterfaceFileBusy( SetupPage->OcManager, SetupPage->OcManager->TopLevelOcStringIds[i], (PFILEPATHS)Param1, (LPTSTR)Param2 ); } break; } return (SetupDefaultQueueCallback(SetupPage->QueueContext, Notification, Param1, Param2)); } DWORD pOcSetupDoIt( IN PGEN_THREAD_PARAMS Params ) { BOOL b; TCHAR Text[256]; TCHAR LogText[256]; OPTIONAL_COMPONENT Oc; POC_MANAGER OcManager; BOOL AllowCancel; UINT LastError = ERROR_SUCCESS; DWORD TotalFileCount,PartialCount; TRACE(( TEXT("at pOcSetupDoIt entry\n") )); InterlockedIncrement( &Params->SetupPage->RefCount ); // // Call components to let them do pre-commit processing. // LoadString(MyModuleHandle,IDS_PREQUEUECONFIG,Text,sizeof(Text)/sizeof(TCHAR)); SetDlgItemText(Params->hdlg,Params->SetupPage->ControlsInfo.ProgressText,Text); #ifdef UNICODE // If, update the text on the billboard for the progress bar. if (Params->SetupPage->OcManager->Callbacks.BillBoardSetProgressText) { Params->SetupPage->OcManager->Callbacks.BillBoardSetProgressText(Text); } // Save it, because "Text" is used below and we would not end up with a matchin END_SECTION lstrcpy(LogText, Text); if (Params->SetupPage->OcManager->Callbacks.SetupPerfData) Params->SetupPage->OcManager->Callbacks.SetupPerfData(TEXT(__FILE__),__LINE__,L"BEGIN_SECTION",LogText); #endif Params->SetupPage->OcManager->ProgressTextWindow = GetDlgItem( Params->hdlg, Params->SetupPage->ControlsInfo.ProgressText ); if (CheckForQueueCancel(Params->SetupPage)) { goto exit; } // // send OC_ABOUT_TO_COMMIT_QUEUE message // pOcPreOrPostCommitProcessing(Params->SetupPage,TRUE); OcManager = Params->SetupPage->OcManager; AllowCancel = Params->SetupPage->AllowCancel; OcManager->ProgressTextWindow = NULL; if (CheckForQueueCancel(Params->SetupPage)) { goto exit; } // // Commit the file queue. We get the total number of file operations // so we can scale the progress indicator properly. We do this manually // as setupapi only returns back the total number of copy operations, and // we want status on delete operations as well. // TotalFileCount = 0; PartialCount = 0; if (SetupGetFileQueueCount(Params->SetupPage->FileQueue, FILEOP_COPY, &PartialCount)) { TotalFileCount += PartialCount; } PartialCount = 0; if (SetupGetFileQueueCount(Params->SetupPage->FileQueue, FILEOP_RENAME, &PartialCount)) { TotalFileCount += PartialCount; } PartialCount = 0; if (SetupGetFileQueueCount(Params->SetupPage->FileQueue, FILEOP_DELETE, &PartialCount)) { TotalFileCount += PartialCount; } // // if the OC file queue is ever backup aware, add in the count // of files to be backed up here. // TRACE(( TEXT("OCM: %d file operations to complete\n"), TotalFileCount )); // // scale the progress gauge // PostMessage(Params->hdlg, WMX_TICK, 10,Params->SetupPage->StepCount + TotalFileCount); // If, update the text on the billboard for the progress bar. if (Params->SetupPage->OcManager->Callbacks.BillBoardSetProgressText) { Params->SetupPage->OcManager->Callbacks.BillBoardSetProgressText(Text); } b = FALSE; while (! b) { DWORD ScanResult; LoadString(MyModuleHandle,IDS_FILESCAN,Text,sizeof(Text)/sizeof(TCHAR)); SetDlgItemText(Params->hdlg,Params->SetupPage->ControlsInfo.ProgressText,Text); b = SetupScanFileQueue(Params->SetupPage->FileQueue, SPQ_SCAN_FILE_VALIDITY | SPQ_SCAN_PRUNE_COPY_QUEUE, Params->hdlg, NULL, NULL, &ScanResult); // // if the scan result is 1, then there isn't anything to commit, the entire // file queue has been pruned. So we skip it. // if (ScanResult != 1) { if( IsWindow( Params->hdlg ) ){ LoadString(MyModuleHandle,IDS_FILEOPS,Text,sizeof(Text)/sizeof(TCHAR)); SetDlgItemText(Params->SetupPage->hdlg,Params->SetupPage->ControlsInfo.ProgressText,Text); } if (CheckForQueueCancel(Params->SetupPage)) { goto exit; } //Set the SecondNeedMedia to FALSE as we are starting a commit operation. Params->SetupPage->SecondNeedMedia = FALSE; b = SetupCommitFileQueue( Params->hdlg, Params->SetupPage->FileQueue, OcManagerQueueCallback1, Params->SetupPage ); LastError = GetLastError(); #ifdef UNICODE if (hSfp) { SfcClose(hSfp); } #endif } if (!b) { TRACE(( TEXT("OC:SetupCommitFileQueue failed (LE=%d), last file copied was %s\n"), LastError, g_LastFileCopied )); pOcHelperReportExternalError( OcManager, 0, // defaults to Master Inf file 0, MSG_OC_CANT_COMMIT_QUEUE, ERRFLG_OCM_MESSAGE, LastError ); if ( LastError == ERROR_CANCELLED || LastError == ERROR_CONTROL_ID_NOT_FOUND || LastError == ERROR_OPERATION_ABORTED) { // // User canceled from a SetupAPI provided Dialog // when CallBack Returns FILEOP_ABORT LastError reports // ERROR_CONTROL_ID_NOT_FOUND if User aborts in SetupApi // find File Dialog you get ERROR_CANCELLED // if ( AllowCancel && (OcManager->SetupData.OperationFlags & SETUPOP_STANDALONE)) { _LogError( OcManager, OcErrLevError|MB_ICONEXCLAMATION|MB_OK, MSG_OC_USER_CANCELED, LastError ); } // // this will force the cancel of setup // LastError = IDCANCEL; } else { // // Warn the user that it might be hazzardous to continue after copy error // LastError = _LogError( OcManager, OcErrLevError|MB_ICONEXCLAMATION|MB_OKCANCEL|MB_DEFBUTTON2, MSG_OC_CANT_COMMIT_QUEUE, LastError ); } // // Abort the setup if the user pressed Cancel or // Batch mode log the error and cancel out of setup // if ( LastError == IDCANCEL || OcManager->SetupData.OperationFlags & SETUPOP_BATCH) { PostMessage(Params->hdlg,WMX_SETUP,OCSETUPSTATE_COPYABORT,0); goto exit; } else if ( LastError == IDOK ) { b = TRUE; } } } // // put a message in the log so we know we completed all file operations // _LogError(OcManager, OcErrLevInfo, MSG_OC_LOG_QUEUE_COMPLETE ); // // Tell the UI that we are done with the file operations // PostMessage(Params->hdlg,WMX_SETUP,OCSETUPSTATE_COPYDONE,0); #ifdef UNICODE if (Params->SetupPage->OcManager->Callbacks.SetupPerfData) Params->SetupPage->OcManager->Callbacks.SetupPerfData(TEXT(__FILE__),__LINE__,L"END_SECTION",LogText); #endif // // Call components to let them do post-commit processing. // LoadString(MyModuleHandle,IDS_CONFIGURING,Text,sizeof(Text)/sizeof(TCHAR)); SetDlgItemText(Params->hdlg,Params->SetupPage->ControlsInfo.ProgressText,Text); #ifdef UNICODE // If, update the text on the billboard for the progress bar. if (Params->SetupPage->OcManager->Callbacks.BillBoardSetProgressText) { Params->SetupPage->OcManager->Callbacks.BillBoardSetProgressText(Text); } if (Params->SetupPage->OcManager->Callbacks.SetupPerfData) Params->SetupPage->OcManager->Callbacks.SetupPerfData(TEXT(__FILE__),__LINE__,L"BEGIN_SECTION",Text); #endif Params->SetupPage->OcManager->ProgressTextWindow = GetDlgItem( Params->hdlg, Params->SetupPage->ControlsInfo.ProgressText ); if (CheckForQueueCancel(Params->SetupPage)) { goto exit; } pOcPreOrPostCommitProcessing(Params->SetupPage,FALSE); if (CheckForQueueCancel(Params->SetupPage)) { goto exit; } Params->SetupPage->OcManager->ProgressTextWindow = NULL; PostMessage(Params->hdlg,WMX_SETUP,OCSETUPSTATE_DONE,0); #ifdef UNICODE if (Params->SetupPage->OcManager->Callbacks.SetupPerfData) Params->SetupPage->OcManager->Callbacks.SetupPerfData(TEXT(__FILE__),__LINE__,L"END_SECTION",Text); #endif exit: TRACE(( TEXT("at pOcSetupDoIt exit\n") )); pOcFreeOcSetupPage( Params->SetupPage ); if (Params->Async) { pSetupFree(Params); } return (0); } VOID pOcPreOrPostCommitProcessing( IN OUT PSETUP_PAGE SetupPage, IN BOOL PreCommit ) /*++ Routine Description: Handle processing and notification to the component dlls before or after the file queue is committed. This involves calling interface dlls once for each top-level component, and then once for each subcomponent. The ordering for the top-level components is the order that the components were listed in the master oc inf. The ordering for leaf components is generally random within each top-level hierarchy, but 'detours' are taken when components are needed by other components. This ensures that components are called in the correct order to faciliate uninstall-type actions. Arguments: SetupPage - supplies context data structure. PreCommit - TRUE indicates OC_ABOUT_TO_COMMIT_QUEUE is to be called, otherwise OC_COMPLETE_INSTALLATION is to be called. Return Value: None. Errors are logged. --*/ { OPTIONAL_COMPONENT Oc,AuxOc; unsigned i,child; // // Call each component at the "top-level" (ie, no subcomponent). // pOcTopLevelPreOrPostCommitProcessing(SetupPage,PreCommit); if (CheckForQueueCancel(SetupPage)) { return; } if (!PreCommit) { // // Make sure the components are marked as unprocessed. // MYASSERT(SetupPage->OcManager->ComponentStringTable); // // if this doesn't exist then something is hosed. // if (!SetupPage->OcManager->ComponentStringTable) { return; } pSetupStringTableEnum( SetupPage->OcManager->ComponentStringTable, &Oc, sizeof(OPTIONAL_COMPONENT), pOcMarkUnprocessedStringCB, 0 ); } // // Call component dlls for each child subcomponent. // for (i=0; iOcManager->TopLevelOcCount; i++) { pSetupStringTableGetExtraData( SetupPage->OcManager->ComponentStringTable, SetupPage->OcManager->TopLevelOcStringIds[i], &Oc, sizeof(OPTIONAL_COMPONENT) ); for (child=0; childOcManager->TopLevelParentOcCount; child++) { pOcSetupDoItWorker( SetupPage, SetupPage->OcManager->TopLevelParentOcStringIds[child], SetupPage->OcManager->TopLevelOcStringIds[i], PreCommit ); } } } VOID pOcTopLevelPreOrPostCommitProcessing( IN PSETUP_PAGE SetupPage, IN BOOL PreCommit ) /*++ Routine Description: Call the OC_COMPLETE_INSTALLATION or OC_ABOUT_TO_COMMIT_QUEUE interface routine once for each top-level component. Arguments: SetupPage - supplies context structure. PreCommit - if 0, then call OC_COMPLETE_INSTALLATION. Otherwise call OC_ABOUT_TO_COMMIT_QUEUE. Return Value: None. Errors are logged. --*/ { unsigned i; OPTIONAL_COMPONENT Oc; UINT Err; for (i=0; iOcManager->TopLevelOcCount; i++) { pSetupStringTableGetExtraData( SetupPage->OcManager->ComponentStringTable, SetupPage->OcManager->TopLevelOcStringIds[i], &Oc, sizeof(OPTIONAL_COMPONENT) ); SetupPage->CurrentTopLevelComponentIndex = i; Err = OcInterfaceCompleteInstallation( SetupPage->OcManager, SetupPage->OcManager->TopLevelOcStringIds[i], NULL, PreCommit ); if (Err != NO_ERROR) { _LogError( SetupPage->OcManager, OcErrLevError, MSG_OC_COMP_INST_FAIL, Oc.Description, Err ); pOcHelperReportExternalError( SetupPage->OcManager, SetupPage->OcManager->TopLevelOcStringIds[i], 0, MSG_OC_COMP_INST_FAIL, ERRFLG_OCM_MESSAGE, Oc.Description, Err ); } } } VOID pOcSetupDoItWorker( IN PSETUP_PAGE SetupPage, IN LONG StringId, IN LONG TopLevelStringId, IN BOOL PreCommit ) /*++ Routine Description: Call the OC_COMPLETE_INSTALLATION or OC_ABOUT_TO_COMMIT_QUEUE interface routine for each child of a given top-level component. Arguments: SetupPage - supplies context structure. StringId - ID for the child component to be called TopLevelStringId - ID for the child's parent PreCommit - if 0, then call OC_COMPLETE_INSTALLATION. Otherwise call OC_ABOUT_TO_COMMIT_QUEUE. Return Value: None. Errors are logged. --*/ { OPTIONAL_COMPONENT Oc; UINT Err; LONG Id; unsigned i; LONG TopLevelIndex; UINT SelectionState; UINT InstalledState; // // Figure out the index of the top-level component associated with this // subcomponent. // Id = pOcGetTopLevelComponent(SetupPage->OcManager,StringId); TopLevelIndex = -1; for (i=0; iOcManager->TopLevelOcCount; i++) { if (SetupPage->OcManager->TopLevelOcStringIds[i] == Id) { TopLevelIndex = i; break; } } // // Fetch extra data for this subcomponent. // pSetupStringTableGetExtraData( SetupPage->OcManager->ComponentStringTable, StringId, &Oc, sizeof(OPTIONAL_COMPONENT) ); if (Oc.FirstChildStringId == -1) { // // Leaf subcomponent. // // In the precommit case, check the subcomponents this subcomponent // is needed by; if there are any, process them first. // // In the postcommit case, check the subcomponents this subcomponent // needs; if there are any, process them first. // if (PreCommit) { for (i=0; iOcManager,Oc.NeededByStringIds[i]), TRUE ); } } else { for (i=0; iOcManager,Oc.NeedsStringIds[i]), FALSE ); } } } // // Fetch extra data for this subcomponent again as it might have // changed in the recursive call we just made. // pSetupStringTableGetExtraData( SetupPage->OcManager->ComponentStringTable, StringId, &Oc, sizeof(OPTIONAL_COMPONENT) ); // // If not processed already, process now. // if (!(Oc.InternalFlags & OCFLAG_PROCESSED)) { Oc.InternalFlags |= OCFLAG_PROCESSED; pSetupStringTableSetExtraData( SetupPage->OcManager->ComponentStringTable, StringId, &Oc, sizeof(OPTIONAL_COMPONENT) ); SetupPage->CurrentTopLevelComponentIndex = TopLevelIndex; // // Set current install state to not installed, pending successful // outcome of the installation routine. // if (!PreCommit) { SelectionState = Oc.SelectionState; Oc.SelectionState = SELSTATE_NO; pOcSetOneInstallState(SetupPage->OcManager,StringId); } Err = OcInterfaceCompleteInstallation( SetupPage->OcManager, pOcGetTopLevelComponent(SetupPage->OcManager,StringId), pSetupStringTableStringFromId(SetupPage->OcManager->ComponentStringTable,StringId), PreCommit ); // Ignore error and ask the component // for the actual installation state. if (!PreCommit) { Oc.SelectionState = (Err) ? Oc.OriginalSelectionState : SelectionState; InstalledState = OcInterfaceQueryState( SetupPage->OcManager, pOcGetTopLevelComponent(SetupPage->OcManager,StringId), pSetupStringTableStringFromId(SetupPage->OcManager->ComponentStringTable,StringId), OCSELSTATETYPE_FINAL ); switch (InstalledState) { case SubcompOn: SelectionState = SELSTATE_YES; break; case SubcompOff: SelectionState = SELSTATE_NO; break; default: SelectionState = Oc.SelectionState; break; } Oc.SelectionState = SelectionState; pSetupStringTableSetExtraData( SetupPage->OcManager->ComponentStringTable, StringId, &Oc, sizeof(OPTIONAL_COMPONENT) ); pOcSetOneInstallState(SetupPage->OcManager,StringId); } } } else { // // Parent component. Spin through the children. // for (Id = Oc.FirstChildStringId; Id != -1; Id = Oc.NextSiblingStringId) { pOcSetupDoItWorker(SetupPage,Id,TopLevelStringId,PreCommit); pSetupStringTableGetExtraData( SetupPage->OcManager->ComponentStringTable, Id, &Oc, sizeof(OPTIONAL_COMPONENT) ); } } } BOOL pOcMarkUnprocessedStringCB( IN PVOID StringTable, IN LONG StringId, IN PCTSTR String, IN POPTIONAL_COMPONENT Oc, IN UINT OcSize, IN LPARAM Unused ) /*++ Routine Description: String table callback routine. Clears the OCFLAG_PROCESSED flag in the OPTIONAL_COMPONENT structure that is passed to it. Arguments: String string table callback arguments. Return Value: Always returns TRUE to continue enumeration. --*/ { Oc->InternalFlags &= ~OCFLAG_PROCESSED; pSetupStringTableSetExtraData(StringTable,StringId,Oc,OcSize); return (TRUE); } VOID _pOcExternalProgressIndicator( IN PSETUP_PAGE SetupPage, IN BOOL ExternalIndicator, IN HWND hdlg ) { POC_MANAGER OcManager; HWND Animation; OcManager = SetupPage->OcManager; EnableWindow( GetDlgItem(hdlg,SetupPage->ControlsInfo.ProgressBar), !ExternalIndicator ); if (SetupPage->ForceExternalProgressIndicator) { ShowWindow( GetDlgItem(hdlg,SetupPage->ControlsInfo.ProgressBar), ExternalIndicator ? SW_HIDE : SW_SHOW ); ShowWindow( GetDlgItem(hdlg,SetupPage->ControlsInfo.ProgressLabel), ExternalIndicator ? SW_HIDE : SW_SHOW ); } Animation = GetDlgItem(hdlg,SetupPage->ControlsInfo.AnimationControl); sapiAssert( Animation != NULL ); if (!ExternalIndicator) { Animate_Stop(Animation); Animate_Close(Animation); } EnableWindow(Animation,ExternalIndicator); ShowWindow(Animation,ExternalIndicator ? SW_SHOW : SW_HIDE); if (ExternalIndicator) { Animate_Open(Animation,MAKEINTRESOURCE(SetupPage->ControlsInfo.AnimationResource)); Animate_Play(Animation,0,-1,-1); } } VOID pOcExternalProgressIndicator( IN PHELPER_CONTEXT OcManagerContext, IN BOOL ExternalIndicator ) { POC_MANAGER OcManager; HWND hdlg; PSETUP_PAGE SetupPage; OcManager = OcManagerContext->OcManager; if (OcManager->ProgressTextWindow && (hdlg = GetParent(OcManager->ProgressTextWindow)) && (SetupPage = (PSETUP_PAGE)GetWindowLongPtr(hdlg,DWLP_USER)) && !SetupPage->ForceExternalProgressIndicator) { _pOcExternalProgressIndicator(SetupPage,ExternalIndicator,hdlg); } }