646 lines
17 KiB
C
646 lines
17 KiB
C
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include <io.h>
|
|
|
|
typedef struct _STANDALONE_COMP {
|
|
struct _STANDALONE_COMP *Next;
|
|
LPTSTR ComponentId;
|
|
HINF Inf;
|
|
OCMANAGER_ROUTINES HelperRoutines;
|
|
|
|
} STANDALONE_COMP, *PSTANDALONE_COMP;
|
|
|
|
|
|
PSTANDALONE_COMP StandaloneComponents = NULL;
|
|
|
|
DWORD
|
|
InvokeStandAloneInstaller(
|
|
IN LPCTSTR ComponentId,
|
|
IN BOOL PreQueueCommit
|
|
);
|
|
|
|
DWORD
|
|
WaitOnApp(
|
|
IN HANDLE Process,
|
|
OUT PDWORD ExitCode
|
|
);
|
|
|
|
|
|
void
|
|
SetSuiteCurrentDir(
|
|
IN PSTANDALONE_COMP Standalone
|
|
)
|
|
{
|
|
TCHAR NewPath[MAX_PATH];
|
|
LPTSTR p;
|
|
PHELPER_CONTEXT pContext = (PHELPER_CONTEXT) Standalone->HelperRoutines.OcManagerContext;
|
|
|
|
_tcscpy(NewPath,pContext->OcManager->MasterOcInfPath);
|
|
p = _tcsrchr(NewPath,TEXT('\\'));
|
|
if (p) {
|
|
*p = 0;
|
|
}
|
|
|
|
SetCurrentDirectory(NewPath);
|
|
|
|
}
|
|
|
|
BOOL
|
|
CheckIfExistAndAskForMedia(
|
|
IN PSTANDALONE_COMP Standalone,
|
|
IN LPTSTR ExePath,
|
|
IN LPCTSTR Description
|
|
)
|
|
{
|
|
// Check to see if the Exe even exits... if not ask the
|
|
PHELPER_CONTEXT pContext = (PHELPER_CONTEXT) Standalone->HelperRoutines.OcManagerContext;
|
|
SOURCE_MEDIA Media;
|
|
TCHAR NewPath[MAX_PATH*3];
|
|
LPTSTR p;
|
|
BOOL b = FALSE;
|
|
UINT i;
|
|
|
|
|
|
// Prepare Exe file name, Strip off arguments
|
|
// Can't have spaces in Exe name.
|
|
|
|
_tcscpy(NewPath,ExePath);
|
|
|
|
p = _tcschr(NewPath,TEXT(' '));
|
|
if(!p) {
|
|
p = _tcschr(NewPath,TEXT('\t'));
|
|
}
|
|
if(p) {
|
|
*p = 0;
|
|
}
|
|
|
|
// Check if we can find the file -
|
|
// Assumes that we have right CD or full path
|
|
i = GetFileAttributes(NewPath);
|
|
|
|
if ( i == -1 ) {
|
|
|
|
// now backup to file part, strip off path leave file name.
|
|
|
|
p = _tcsrchr(NewPath,TEXT('\\'));
|
|
if (!p) {
|
|
p = NewPath;
|
|
}
|
|
|
|
Media.Reserved = NULL; // PCWSTR
|
|
Media.Description= Description; // PCWSTR
|
|
|
|
Media.SourcePath = NULL; // PCWSTR
|
|
Media.SourceFile = p; // PCWSTR
|
|
Media.Tagfile = p; // PCWSTR may be NULL
|
|
Media.Flags = 0; // DWORD subset of SP_COPY_xxx
|
|
|
|
for(b=FALSE,i=0; (i<pContext->OcManager->TopLevelOcCount) && !b; i++) {
|
|
|
|
b = OcInterfaceNeedMedia(
|
|
pContext->OcManager,
|
|
pContext->OcManager->TopLevelOcStringIds[i],
|
|
&Media,
|
|
(LPTSTR)NewPath
|
|
);
|
|
if (b) {
|
|
// Now we have a new Path to the file
|
|
// get the last segment of the path
|
|
|
|
p = _tcsrchr(ExePath,TEXT('\\'));
|
|
if (p) {
|
|
_tcscat(NewPath,p);
|
|
} else {
|
|
_tcscat(NewPath,TEXT("\\"));
|
|
_tcscat(NewPath,ExePath);
|
|
}
|
|
// Rewrite path
|
|
_tcscpy(ExePath,NewPath);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return b;
|
|
}
|
|
|
|
DWORD
|
|
StandAloneSetupAppInterfaceRoutine(
|
|
IN LPCVOID ComponentId,
|
|
IN LPCVOID SubcomponentId,
|
|
IN UINT Function,
|
|
IN UINT_PTR Param1,
|
|
IN OUT PVOID Param2
|
|
)
|
|
{
|
|
DWORD d;
|
|
PSTANDALONE_COMP Standalone,Prev;
|
|
|
|
switch(Function) {
|
|
|
|
case OC_PREINITIALIZE:
|
|
//
|
|
// Run with native character width.
|
|
//
|
|
#ifdef UNICODE
|
|
d = OCFLAG_UNICODE;
|
|
#else
|
|
d = OCFLAG_ANSI;
|
|
#endif
|
|
break;
|
|
|
|
|
|
case OC_INIT_COMPONENT:
|
|
//
|
|
// Inform OC Manager of the version we want.
|
|
//
|
|
((PSETUP_INIT_COMPONENT)Param2)->ComponentVersion = OCMANAGER_VERSION;
|
|
|
|
d = ERROR_NOT_ENOUGH_MEMORY;
|
|
if(Standalone = pSetupMalloc(sizeof(STANDALONE_COMP))) {
|
|
if(Standalone->ComponentId = pSetupMalloc((lstrlen(ComponentId)+1) * sizeof(TCHAR))) {
|
|
|
|
lstrcpy(Standalone->ComponentId,ComponentId);
|
|
|
|
Standalone->Inf = ((PSETUP_INIT_COMPONENT)Param2)->ComponentInfHandle;
|
|
Standalone->HelperRoutines = ((PSETUP_INIT_COMPONENT)Param2)->HelperRoutines;
|
|
|
|
Standalone->Next = StandaloneComponents;
|
|
StandaloneComponents = Standalone;
|
|
|
|
d = NO_ERROR;
|
|
} else {
|
|
pSetupFree(Standalone);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case OC_SET_LANGUAGE:
|
|
d = TRUE;
|
|
break;
|
|
|
|
case OC_QUERY_IMAGE:
|
|
d = 0;
|
|
break;
|
|
|
|
case OC_REQUEST_PAGES:
|
|
//
|
|
// This component has no pages.
|
|
//
|
|
d = 0;
|
|
break;
|
|
|
|
case OC_QUERY_SKIP_PAGE:
|
|
|
|
d = FALSE;
|
|
break;
|
|
|
|
case OC_QUERY_STATE:
|
|
{
|
|
DWORD dSetupMode;
|
|
|
|
//
|
|
// Allow selection state transition.
|
|
//
|
|
for(Standalone=StandaloneComponents; Standalone; Standalone=Standalone->Next) {
|
|
if(!lstrcmpi(ComponentId,Standalone->ComponentId)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
dSetupMode = Standalone->HelperRoutines.GetSetupMode(
|
|
Standalone->HelperRoutines.OcManagerContext);
|
|
//
|
|
// Use default if we have no option...
|
|
//
|
|
|
|
d = SubcompUseOcManagerDefault;
|
|
|
|
if (Param1 == OCSELSTATETYPE_CURRENT) {
|
|
|
|
switch(dSetupMode & SETUPMODE_PRIVATE_MASK) {
|
|
default:
|
|
d = SubcompUseOcManagerDefault;
|
|
break;
|
|
case SETUPMODE_REMOVEALL:
|
|
d = SubcompOff;
|
|
break;
|
|
|
|
case SETUPMODE_ADDEXTRACOMPS:
|
|
case SETUPMODE_ADDREMOVE:
|
|
case SETUPMODE_UPGRADEONLY:
|
|
case SETUPMODE_REINSTALL:
|
|
d = Standalone->HelperRoutines.QuerySelectionState(
|
|
Standalone->HelperRoutines.OcManagerContext,
|
|
SubcomponentId,
|
|
OCSELSTATETYPE_ORIGINAL) ? SubcompOn : SubcompOff;
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case OC_QUERY_CHANGE_SEL_STATE:
|
|
d = TRUE;
|
|
break;
|
|
|
|
case OC_CALC_DISK_SPACE:
|
|
|
|
for(Standalone=StandaloneComponents; Standalone; Standalone=Standalone->Next) {
|
|
if(!lstrcmpi(ComponentId,Standalone->ComponentId)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(Standalone) {
|
|
|
|
INFCONTEXT Context;
|
|
|
|
if(SetupFindFirstLine(Standalone->Inf,ComponentId,TEXT("DiskSpaceEstimate"),&Context)) {
|
|
|
|
LONGLONG Space;
|
|
int SpaceMB;
|
|
BOOL b;
|
|
TCHAR Path[MAX_PATH];
|
|
|
|
if(SetupGetIntField(&Context,1,&SpaceMB)) {
|
|
|
|
Space = (LONGLONG)SpaceMB * (1024*1024);
|
|
if(!Param1) {
|
|
Space = 0 - Space;
|
|
}
|
|
|
|
GetWindowsDirectory(Path,MAX_PATH);
|
|
Path[3] = 0;
|
|
|
|
b = SetupAdjustDiskSpaceList((HDSKSPC)Param2,Path,Space,0,0);
|
|
|
|
d = b ? NO_ERROR : GetLastError();
|
|
} else {
|
|
d = ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
} else {
|
|
d = NO_ERROR;
|
|
}
|
|
|
|
break;
|
|
|
|
case OC_QUEUE_FILE_OPS:
|
|
//
|
|
// No files to queue.
|
|
//
|
|
d = NO_ERROR;
|
|
break;
|
|
|
|
case OC_NOTIFICATION_FROM_QUEUE:
|
|
d = 0;
|
|
break;
|
|
|
|
case OC_QUERY_STEP_COUNT:
|
|
//
|
|
// Just use 1 step.
|
|
//
|
|
d = 1;
|
|
break;
|
|
|
|
case OC_ABOUT_TO_COMMIT_QUEUE:
|
|
case OC_COMPLETE_INSTALLATION:
|
|
|
|
// Figure out whether state changed, and if so, invoke
|
|
// the install/uninstall cmd line. Just to be safe, we ignore
|
|
// any requests that are not for the component as a whole,
|
|
// since these were not supposed to have been specified in the first place.
|
|
//
|
|
d = SubcomponentId
|
|
? NO_ERROR
|
|
: InvokeStandAloneInstaller(ComponentId,Function == OC_ABOUT_TO_COMMIT_QUEUE);
|
|
|
|
break;
|
|
|
|
case OC_CLEANUP:
|
|
//
|
|
// Return value is ignored.
|
|
//
|
|
Prev = NULL;
|
|
for(Standalone=StandaloneComponents; Standalone; Standalone=Standalone->Next) {
|
|
|
|
if(!lstrcmpi(ComponentId,Standalone->ComponentId)) {
|
|
if(Prev) {
|
|
Prev->Next = Standalone->Next;
|
|
} else {
|
|
StandaloneComponents = Standalone->Next;
|
|
}
|
|
|
|
pSetupFree(Standalone->ComponentId);
|
|
pSetupFree(Standalone);
|
|
break;
|
|
}
|
|
|
|
Prev = Standalone;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Return something sane.
|
|
//
|
|
d = 0;
|
|
break;
|
|
}
|
|
|
|
return(d);
|
|
}
|
|
|
|
|
|
DWORD
|
|
RunStandaloneCmd(
|
|
IN PSTANDALONE_COMP Standalone,
|
|
IN LPCTSTR Description,
|
|
IN LPCTSTR cmd
|
|
)
|
|
{
|
|
STARTUPINFO StartupInfo;
|
|
PROCESS_INFORMATION ProcessInfo;
|
|
TCHAR ExePath[3*MAX_PATH];
|
|
DWORD ExitCode;
|
|
BOOL b;
|
|
|
|
ZeroMemory(&StartupInfo,sizeof(STARTUPINFO));
|
|
ZeroMemory(&ProcessInfo,sizeof(PROCESS_INFORMATION));
|
|
|
|
StartupInfo.cb = sizeof(STARTUPINFO);
|
|
|
|
lstrcpyn(ExePath,cmd,3*MAX_PATH);
|
|
|
|
pOcExternalProgressIndicator(Standalone->HelperRoutines.OcManagerContext,TRUE);
|
|
|
|
// We will try two times to invoke the external setup. For both attempt the Current
|
|
// Directory is set the same directory of where the suite.inf file is located.
|
|
// In the first attempt we invoke the command line as we find it from the Standalone
|
|
// inf file. An for almost all cases this will work. If we fail in that invokcation we
|
|
// ask the suite dll for a "Needs Media" can allow them to tell us where the Standalone exe is.
|
|
|
|
// This accounts the two following form of commands
|
|
// InstalledCmd = "wpie15-x86.exe /Q:A /R:NG"
|
|
// UninstallCmd = "RunDll32 ADVPACK.DLL,LaunchINFSection %17%\enuwpie.inf,WebPostUninstall,5"
|
|
|
|
// Where Wpie15-x86.exe will be found in the Current Directory, and if it's not we will ask the
|
|
// suite to provide it. (Web Download) or in the second case where a system dll must be executed
|
|
// to uninstall the product. What's not covered here is if we fail to do a createProcess on the
|
|
// second form of command.
|
|
|
|
b = FALSE;
|
|
while( ! b) {
|
|
b = CreateProcess(
|
|
NULL,
|
|
ExePath,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
NULL, // Sysocmgr set CD to sourcedir
|
|
&StartupInfo,
|
|
&ProcessInfo
|
|
);
|
|
|
|
// If we failed to start the external setup, try asking the suite where
|
|
// to find this
|
|
if ( ! b) {
|
|
if ( ! CheckIfExistAndAskForMedia(Standalone, ExePath, Description)) {
|
|
// if the Suite could not locate the exe thengive up
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!b) {
|
|
pOcExternalProgressIndicator(Standalone->HelperRoutines.OcManagerContext,FALSE);
|
|
return GetLastError();
|
|
}
|
|
|
|
CloseHandle(ProcessInfo.hThread);
|
|
WaitOnApp(ProcessInfo.hProcess,&ExitCode);
|
|
CloseHandle(ProcessInfo.hProcess);
|
|
pOcExternalProgressIndicator(Standalone->HelperRoutines.OcManagerContext,FALSE);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
InvokeStandAloneInstaller(
|
|
IN LPCTSTR ComponentId,
|
|
IN BOOL PreQueueCommit
|
|
)
|
|
{
|
|
PSTANDALONE_COMP Standalone;
|
|
BOOL OldState,NewState;
|
|
LPCTSTR Key;
|
|
INFCONTEXT Context;
|
|
LPCTSTR CmdLine;
|
|
TCHAR CurDir[MAX_PATH];
|
|
BOOL b;
|
|
LPCTSTR Description;
|
|
TCHAR Text[150];
|
|
TCHAR Text2[350];
|
|
TCHAR *p;
|
|
DWORD ExitCode;
|
|
DWORD d;
|
|
DWORD dSetupMode;
|
|
|
|
//
|
|
// Find the component.
|
|
//
|
|
for(Standalone=StandaloneComponents; Standalone; Standalone=Standalone->Next) {
|
|
if(!lstrcmpi(ComponentId,Standalone->ComponentId)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!Standalone) {
|
|
d = NO_ERROR;
|
|
goto c0;
|
|
}
|
|
|
|
//
|
|
// Determine whether this component wants to be invoked pre or post-queue.
|
|
// If this doesn't match the notification we're processing then bail.
|
|
//
|
|
b = FALSE;
|
|
if(SetupFindFirstLine(Standalone->Inf,ComponentId,TEXT("InvokeBeforeQueueCommit"),&Context)
|
|
&& SetupGetIntField(&Context,1,&d)) {
|
|
b = (d != 0);
|
|
}
|
|
if((b == FALSE) != (PreQueueCommit == FALSE)) {
|
|
d = NO_ERROR;
|
|
goto c0;
|
|
}
|
|
|
|
OldState = Standalone->HelperRoutines.QuerySelectionState(
|
|
Standalone->HelperRoutines.OcManagerContext,
|
|
ComponentId,
|
|
OCSELSTATETYPE_ORIGINAL
|
|
);
|
|
|
|
NewState = Standalone->HelperRoutines.QuerySelectionState(
|
|
Standalone->HelperRoutines.OcManagerContext,
|
|
ComponentId,
|
|
OCSELSTATETYPE_CURRENT
|
|
);
|
|
|
|
dSetupMode = Standalone->HelperRoutines.GetSetupMode(
|
|
Standalone->HelperRoutines.OcManagerContext);
|
|
|
|
|
|
// Qualify this setup mode and see if we do anything
|
|
// if no change in state
|
|
|
|
// SETUPMODE_UPGRADE
|
|
// SETUPMODE_UPGRADEONLY
|
|
// SETUPMODE_ADDEXTRACOMPS
|
|
//
|
|
// SETUPMODE_MAINTANENCE
|
|
// SETUPMODE_ADDREMOVE
|
|
// SETUPMODE_REINSTALL
|
|
// SETUPMODE_REMOVEALL
|
|
// SETUPMODE_FRESH
|
|
|
|
|
|
d = NO_ERROR;
|
|
|
|
if ( NewState == OldState ) {
|
|
|
|
// no change in slected state What we do depends on the secondary setup modes
|
|
// if Setupmode is AddRemove or Removeall then Skip this
|
|
|
|
if ( NewState == 0) {
|
|
goto c0; // do nothing
|
|
}
|
|
|
|
// Mask off Public mode bits
|
|
//
|
|
dSetupMode &= SETUPMODE_PRIVATE_MASK;
|
|
if ( dSetupMode == SETUPMODE_ADDREMOVE || dSetupMode == SETUPMODE_REMOVEALL ) {
|
|
goto c0; // do nothing
|
|
}
|
|
// What remains here is NewState=1
|
|
// and Reinstall and Upgrade
|
|
}
|
|
|
|
|
|
Description = NULL;
|
|
if(SetupFindFirstLine(Standalone->Inf,ComponentId,TEXT("OptionDesc"),&Context)) {
|
|
Description = pSetupGetField(&Context,1);
|
|
}
|
|
if(Description) {
|
|
|
|
LoadString(
|
|
MyModuleHandle,
|
|
OldState ? (NewState ? IDS_EXTERNAL_UPGRADE : IDS_EXTERNAL_UNINST)
|
|
: (NewState ? IDS_EXTERNAL_INST : IDS_EXTERNAL_EXAMINE),
|
|
Text,
|
|
sizeof(Text)/sizeof(TCHAR)
|
|
);
|
|
|
|
wsprintf(Text2,Text,Description);
|
|
|
|
Standalone->HelperRoutines.SetProgressText(
|
|
Standalone->HelperRoutines.OcManagerContext,
|
|
Text2
|
|
);
|
|
} else {
|
|
Standalone->HelperRoutines.SetProgressText(
|
|
Standalone->HelperRoutines.OcManagerContext,
|
|
TEXT("")
|
|
);
|
|
}
|
|
|
|
if(OldState == NewState) {
|
|
Key = OldState ? TEXT("InstalledCmd") : TEXT("UninstalledCmd");
|
|
} else {
|
|
Key = OldState ? TEXT("UninstallCmd") : TEXT("InstallCmd");
|
|
}
|
|
|
|
d = NO_ERROR;
|
|
|
|
if(!SetupFindFirstLine(Standalone->Inf,ComponentId,Key,&Context))
|
|
goto c0;
|
|
|
|
// The current Directory to the Suite's Inf Path, with Initial installs "-N" option
|
|
// this may on the CD, with Mainatiance mode it will be the %systemroot%\system32\setup
|
|
|
|
SetSuiteCurrentDir(Standalone);
|
|
|
|
do {
|
|
if (!(CmdLine = pSetupGetField(&Context,1)))
|
|
break;
|
|
d = RunStandaloneCmd(Standalone, Description, CmdLine);
|
|
if (d != NO_ERROR)
|
|
break;
|
|
} while (SetupFindNextMatchLine(&Context,Key,&Context));
|
|
|
|
c0:
|
|
Standalone->HelperRoutines.TickGauge(
|
|
Standalone->HelperRoutines.OcManagerContext
|
|
);
|
|
|
|
return d;
|
|
}
|
|
|
|
|
|
DWORD
|
|
WaitOnApp(
|
|
IN HANDLE Process,
|
|
OUT PDWORD ExitCode
|
|
)
|
|
{
|
|
DWORD dw;
|
|
BOOL Done;
|
|
MSG msg;
|
|
|
|
//
|
|
// Process any messages that may already be in the queue.
|
|
//
|
|
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
//
|
|
// Wait for process to terminate or more messages in the queue.
|
|
//
|
|
Done = FALSE;
|
|
do {
|
|
switch(MsgWaitForMultipleObjects(1,&Process,FALSE,INFINITE,QS_ALLINPUT)) {
|
|
|
|
case WAIT_OBJECT_0:
|
|
//
|
|
// Process has terminated.
|
|
//
|
|
dw = GetExitCodeProcess(Process,ExitCode) ? NO_ERROR : GetLastError();
|
|
Done = TRUE;
|
|
break;
|
|
|
|
case WAIT_OBJECT_0+1:
|
|
//
|
|
// Messages in the queue.
|
|
//
|
|
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
|
|
DispatchMessage(&msg);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Error.
|
|
//
|
|
dw = GetLastError();
|
|
Done = TRUE;
|
|
break;
|
|
}
|
|
} while(!Done);
|
|
|
|
return(dw);
|
|
}
|
|
|