Windows-Server-2003/base/ntsetup/ocmanage/common/ocsetup.c

2342 lines
71 KiB
C

#include "precomp.h"
#pragma hdrstop
#include <stdio.h>
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; i<Params->SetupPage->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; child<Params->SetupPage->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; i<Params->SetupPage->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; child<Params->SetupPage->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; (i<SetupPage->OcManager->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; i<SetupPage->OcManager->TopLevelOcCount; i++) {
pSetupStringTableGetExtraData(
SetupPage->OcManager->ComponentStringTable,
SetupPage->OcManager->TopLevelOcStringIds[i],
&Oc,
sizeof(OPTIONAL_COMPONENT)
);
for (child=0; child<SetupPage->OcManager->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; i<SetupPage->OcManager->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; i<SetupPage->OcManager->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; i<Oc.NeededByCount; i++) {
pOcSetupDoItWorker(
SetupPage,
Oc.NeededByStringIds[i],
pOcGetTopLevelComponent(SetupPage->OcManager,Oc.NeededByStringIds[i]),
TRUE
);
}
} else {
for (i=0; i<Oc.NeedsCount; i++) {
if (Oc.NeedsStringIds[i] != StringId) {
pOcSetupDoItWorker(
SetupPage,
Oc.NeedsStringIds[i],
pOcGetTopLevelComponent(SetupPage->OcManager,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);
}
}