640 lines
14 KiB
C
640 lines
14 KiB
C
#include "svcpack.h"
|
|
|
|
//
|
|
// The module instance and name
|
|
//
|
|
HINSTANCE hDllInstance;
|
|
|
|
//
|
|
// The path to the OS Source
|
|
//
|
|
TCHAR OsSourcePath[MAX_PATH];
|
|
|
|
|
|
//
|
|
// Function declarations
|
|
//
|
|
BOOL
|
|
DoPhaseOneWork(VOID);
|
|
|
|
BOOL
|
|
DoPhaseTwoWork(VOID);
|
|
|
|
BOOL
|
|
DoPhaseThreeWork(VOID);
|
|
|
|
BOOL
|
|
DoPhaseFourWork(VOID);
|
|
|
|
BOOL
|
|
InitializeSourcePath(
|
|
PTSTR SourcePath,
|
|
HINF hInf
|
|
);
|
|
|
|
BOOL
|
|
MyInstallProductCatalog(
|
|
LPCTSTR PathToCatalog,
|
|
LPCTSTR CatalogNoPath
|
|
);
|
|
|
|
LPTSTR
|
|
CombinePaths(
|
|
IN LPTSTR ParentPath,
|
|
IN LPCTSTR ChildPath,
|
|
OUT LPTSTR TargetPath // can be same as ParentPath if want to append
|
|
);
|
|
|
|
BOOL
|
|
SpawnProcessAndWaitForItToComplete(
|
|
IN LPTSTR CommandLine,
|
|
OUT PDWORD ReturnCode OPTIONAL
|
|
);
|
|
|
|
BOOL
|
|
RunInfProcesses(
|
|
IN HINF hInf
|
|
);
|
|
|
|
BOOL
|
|
GetInfValue(
|
|
IN HINF hInf,
|
|
IN LPTSTR SectionName,
|
|
IN LPTSTR KeyName,
|
|
OUT PDWORD pdwValue
|
|
);
|
|
|
|
BOOL
|
|
DoesInfVersionInfoMatch(
|
|
IN HINF hInf
|
|
);
|
|
|
|
|
|
BOOL
|
|
CALLBACK
|
|
SvcPackCallbackRoutine(
|
|
IN DWORD dwSetupInterval,
|
|
IN DWORD dwParam1,
|
|
IN DWORD dwParam2,
|
|
IN DWORD dwParam3
|
|
)
|
|
|
|
{
|
|
|
|
switch ( dwSetupInterval ) {
|
|
case SVCPACK_PHASE_1:
|
|
//
|
|
// install catalogs, etc.
|
|
//
|
|
DoPhaseOneWork();
|
|
case SVCPACK_PHASE_2:
|
|
case SVCPACK_PHASE_3:
|
|
break;
|
|
|
|
case SVCPACK_PHASE_4:
|
|
//
|
|
// Do registry changes, etc.
|
|
//
|
|
DoPhaseFourWork();
|
|
break;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
DllMain (HINSTANCE hInstance, DWORD fdwReason, PVOID pvResreved)
|
|
{
|
|
|
|
switch (fdwReason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
//
|
|
// Save the module instance and name
|
|
//
|
|
hDllInstance = hInstance;
|
|
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
case DLL_PROCESS_DETACH:
|
|
break;
|
|
case DLL_THREAD_ATTACH:
|
|
default:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
DoPhaseOneWork(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine installs the catalogs listed in the svcpack.inf's
|
|
[ProductCatalogsToInstall] section. It is assumed that these
|
|
catalogs are present at the os source path.
|
|
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if the catalogs were successfully installed.
|
|
|
|
--*/
|
|
{
|
|
HINF hInf;
|
|
TCHAR CatalogSourcePath[MAX_PATH];
|
|
INFCONTEXT InfContext;
|
|
BOOL RetVal = TRUE;
|
|
|
|
//
|
|
// Open the svcpack.inf so we can install items from it.
|
|
//
|
|
hInf = SetupOpenInfFile(
|
|
TEXT("SVCPACK.INF"),
|
|
NULL,
|
|
INF_STYLE_WIN4,
|
|
NULL);
|
|
if (hInf == INVALID_HANDLE_VALUE) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure the INF has matching version info
|
|
// Return TRUE even if the versions don't match so setup doesn't barf.
|
|
//
|
|
if (!DoesInfVersionInfoMatch(hInf)) {
|
|
goto e0;
|
|
}
|
|
|
|
//
|
|
// Initialize the source path global variable and save it off for later.
|
|
//
|
|
if (!InitializeSourcePath(OsSourcePath,hInf)) {
|
|
RetVal = FALSE;
|
|
goto e0;
|
|
}
|
|
|
|
|
|
//
|
|
// see if we actually have any catalogs to install
|
|
//
|
|
if (SetupFindFirstLine(
|
|
hInf,
|
|
TEXT("ProductCatalogsToInstall"),
|
|
NULL,
|
|
&InfContext)) {
|
|
UINT Count,Total;
|
|
//
|
|
// we have catalogs in the section, so let's install them.
|
|
//
|
|
Total = SetupGetLineCount(hInf, TEXT("ProductCatalogsToInstall"));
|
|
|
|
for (Count = 0; Count < Total; Count++) {
|
|
PCTSTR CatalogNoPath;
|
|
|
|
//
|
|
// retrieve a catalog name
|
|
//
|
|
if(SetupGetLineByIndex(
|
|
hInf,
|
|
TEXT("ProductCatalogsToInstall"),
|
|
Count,
|
|
&InfContext)) {
|
|
CatalogNoPath = pSetupGetField(&InfContext,1);
|
|
|
|
|
|
//
|
|
// build the full path to the catalog
|
|
//
|
|
_tcscpy(CatalogSourcePath,OsSourcePath);
|
|
CombinePaths(
|
|
CatalogSourcePath,
|
|
CatalogNoPath,
|
|
CatalogSourcePath);
|
|
|
|
//
|
|
// now install the catalog
|
|
//
|
|
if (!MyInstallProductCatalog(
|
|
CatalogSourcePath,
|
|
CatalogNoPath)) {
|
|
RetVal = FALSE;
|
|
}
|
|
} else {
|
|
RetVal = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
e0:
|
|
SetupCloseInfFile( hInf );
|
|
return(RetVal);
|
|
}
|
|
|
|
BOOL
|
|
MyInstallProductCatalog(
|
|
LPCTSTR PathToCatalog,
|
|
LPCTSTR CatalogSourceNoPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine installs the specified catalog with the given source name.
|
|
|
|
The routine will copy (and if necessary, expand) the catalog file.
|
|
It then validates and installs the catalog.
|
|
|
|
|
|
Arguments:
|
|
|
|
PathToCatalog - full path to catalog
|
|
CatalogSourceNoPath - just the filename part of the catalog, which we use
|
|
as the filename of the catalog to be installed.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if the catalogs were successfully installed.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR CatalogDestPath[MAX_PATH];
|
|
TCHAR CatalogDestWithPath[MAX_PATH];
|
|
BOOL RetVal = FALSE;
|
|
SetupapiVerifyProblem Problem = SetupapiVerifyCatalogProblem;
|
|
|
|
//
|
|
// we need to copy (and potentially expand) the catalog from the source,
|
|
// and we use %windir% as a working directory.
|
|
//
|
|
if(GetWindowsDirectory(
|
|
CatalogDestPath,
|
|
sizeof(CatalogDestPath)/sizeof(CatalogDestPath[0]))
|
|
&& GetTempFileName(
|
|
CatalogDestPath,
|
|
TEXT("SETP"),
|
|
0,
|
|
CatalogDestWithPath)) {
|
|
|
|
//
|
|
// assume that media is already present -- since product catalogs
|
|
// we installed just prior to this, we know that media was present
|
|
// just a few moments ago
|
|
//
|
|
if ((SetupDecompressOrCopyFile(
|
|
PathToCatalog,
|
|
CatalogDestWithPath,
|
|
NULL) == NO_ERROR)
|
|
|
|
&& (pSetupVerifyCatalogFile(CatalogDestWithPath) == NO_ERROR)
|
|
&& (pSetupInstallCatalog(
|
|
CatalogDestWithPath,
|
|
CatalogSourceNoPath,
|
|
NULL) == NO_ERROR)) {
|
|
RetVal = TRUE;
|
|
}
|
|
|
|
//
|
|
// cleanup the temp file.
|
|
//
|
|
DeleteFile(CatalogDestWithPath);
|
|
|
|
}
|
|
|
|
return(RetVal);
|
|
|
|
}
|
|
|
|
BOOL
|
|
InitializeSourcePath(
|
|
PTSTR SourcePath,
|
|
HINF hInf
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine retrieves the os source path from the registry, then appends
|
|
the subdirectory in the specified inf.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if the catalogs were successfully installed.
|
|
|
|
--*/
|
|
|
|
{
|
|
HKEY hKey = NULL;
|
|
TCHAR TempPath[MAX_PATH];
|
|
TCHAR MyAnswerFile[MAX_PATH];
|
|
DWORD Type,Size = MAX_PATH;
|
|
INFCONTEXT InfContext;
|
|
BOOL RetVal = FALSE;
|
|
|
|
//
|
|
// if it was already initialized to something, just return TRUE.
|
|
//
|
|
if (*SourcePath != (TCHAR)TEXT('\0')) {
|
|
RetVal = TRUE;
|
|
goto e0;
|
|
}
|
|
|
|
GetSystemDirectory(MyAnswerFile, MAX_PATH);
|
|
CombinePaths( MyAnswerFile, TEXT("$winnt$.inf"), MyAnswerFile );
|
|
|
|
GetPrivateProfileString( TEXT("Data"),
|
|
TEXT("DosPath"),
|
|
TEXT(""),
|
|
TempPath,
|
|
sizeof(TempPath)/sizeof(TCHAR),
|
|
MyAnswerFile );
|
|
_tcscpy(SourcePath,TempPath);
|
|
RetVal = TRUE;
|
|
|
|
//
|
|
// now append the subdirectory specified in the inf (if any)
|
|
//
|
|
if (hInf && SetupFindFirstLine(
|
|
hInf,
|
|
TEXT("SetupData"),
|
|
TEXT("CatalogSubDir"),
|
|
&InfContext)) {
|
|
PCTSTR p = pSetupGetField(&InfContext,1);
|
|
|
|
CombinePaths(
|
|
SourcePath,
|
|
p,
|
|
SourcePath);
|
|
}
|
|
|
|
e0:
|
|
return(RetVal);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DoPhaseFourWork(VOID)
|
|
{
|
|
|
|
BOOL Success = TRUE;
|
|
HINF hInf = NULL;
|
|
|
|
//
|
|
// Attempt to open the SVCPACK.INF file.
|
|
// If found, and no problems with it, do
|
|
// the associated work.
|
|
//
|
|
hInf = SetupOpenInfFile (
|
|
TEXT("SVCPACK.INF"),
|
|
NULL,
|
|
INF_STYLE_WIN4,
|
|
NULL
|
|
);
|
|
|
|
if (( hInf == NULL ) || ( hInf == INVALID_HANDLE_VALUE )) {
|
|
Success = FALSE;
|
|
goto exit0;
|
|
}
|
|
|
|
//
|
|
// Make sure the INF has matching version info.
|
|
// Return TRUE even if the versions don't match so setup doesn't barf.
|
|
//
|
|
if (!DoesInfVersionInfoMatch(hInf)) {
|
|
goto exit1;
|
|
}
|
|
|
|
Success = RunInfProcesses( hInf );
|
|
|
|
exit1:
|
|
SetupCloseInfFile( hInf );
|
|
exit0:
|
|
return Success;
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
SpawnProcessAndWaitForItToComplete(
|
|
IN LPTSTR CommandLine,
|
|
OUT PDWORD ReturnCode OPTIONAL
|
|
)
|
|
{
|
|
LPTSTR InternalCommandLine = NULL;
|
|
PROCESS_INFORMATION ProcessInfo;
|
|
STARTUPINFO StartupInfo;
|
|
BOOL Success;
|
|
|
|
//
|
|
// CreateProcess needs a non-const command line buffer because it likes
|
|
// to party on it.
|
|
//
|
|
InternalCommandLine = malloc( MAX_PATH );
|
|
|
|
if ( InternalCommandLine == NULL ) {
|
|
return FALSE;
|
|
}
|
|
|
|
_tcscpy( InternalCommandLine, CommandLine );
|
|
|
|
ZeroMemory( &StartupInfo, sizeof( StartupInfo ));
|
|
StartupInfo.cb = sizeof( StartupInfo );
|
|
|
|
Success = CreateProcess(
|
|
NULL,
|
|
InternalCommandLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&StartupInfo,
|
|
&ProcessInfo
|
|
);
|
|
|
|
if ( ! Success ) {
|
|
free( InternalCommandLine );
|
|
return FALSE;
|
|
}
|
|
|
|
WaitForSingleObject( ProcessInfo.hProcess, INFINITE );
|
|
|
|
if ( ReturnCode != NULL ) {
|
|
GetExitCodeProcess( ProcessInfo.hProcess, ReturnCode );
|
|
}
|
|
|
|
CloseHandle( ProcessInfo.hProcess );
|
|
CloseHandle( ProcessInfo.hThread );
|
|
free( InternalCommandLine );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
LPTSTR
|
|
CombinePaths(
|
|
IN LPTSTR ParentPath,
|
|
IN LPCTSTR ChildPath,
|
|
OUT LPTSTR TargetPath // can be same as ParentPath if want to append
|
|
)
|
|
{
|
|
ULONG ParentLength = _tcslen( ParentPath );
|
|
LPTSTR p;
|
|
|
|
if ( ParentPath != TargetPath ) {
|
|
memcpy( TargetPath, ParentPath, ParentLength * sizeof(TCHAR) );
|
|
}
|
|
|
|
p = TargetPath + ParentLength;
|
|
|
|
if (( ParentLength > 0 ) &&
|
|
( *( p - 1 ) != '\\' ) &&
|
|
( *( p - 1 ) != '/' )) {
|
|
*p++ = '\\';
|
|
}
|
|
|
|
_tcscpy( p, ChildPath );
|
|
|
|
return TargetPath;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
RunInfProcesses(
|
|
IN HINF hInf
|
|
)
|
|
{
|
|
|
|
LPTSTR SectionName = TEXT("SetupHotfixesToRun");
|
|
LPTSTR szFileName;
|
|
LPTSTR szFullPath;
|
|
INFCONTEXT InfContext;
|
|
BOOL Success = TRUE;
|
|
|
|
//
|
|
// Loop through all the lines in the SetupHotfixesToRun section,
|
|
// spawning off each one.
|
|
//
|
|
szFileName = malloc( MAX_PATH );
|
|
if (szFileName == NULL) {
|
|
Success = FALSE;
|
|
goto exit0;
|
|
}
|
|
|
|
szFullPath = malloc( MAX_PATH );
|
|
if (szFullPath == NULL) {
|
|
Success = FALSE;
|
|
goto exit1;
|
|
}
|
|
|
|
Success = SetupFindFirstLine( hInf, SectionName, NULL, &InfContext ) &&
|
|
SetupGetLineText( &InfContext, NULL, NULL, NULL, szFileName, MAX_PATH, NULL );
|
|
|
|
while ( Success ) {
|
|
|
|
*szFullPath = 0;
|
|
CombinePaths( OsSourcePath, szFileName, szFullPath );
|
|
|
|
//
|
|
// OK, spawn the EXE, and ignore any errors returned
|
|
//
|
|
SpawnProcessAndWaitForItToComplete( szFullPath, NULL );
|
|
|
|
Success = SetupFindNextLine( &InfContext, &InfContext ) &&
|
|
SetupGetLineText( &InfContext, NULL, NULL, NULL, szFileName, MAX_PATH, NULL );
|
|
}
|
|
|
|
Success = TRUE;
|
|
free( (PVOID)szFullPath );
|
|
exit1:
|
|
free( (PVOID)szFileName );
|
|
exit0:
|
|
return Success;
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetInfValue(
|
|
IN HINF hInf,
|
|
IN LPTSTR SectionName,
|
|
IN LPTSTR KeyName,
|
|
OUT PDWORD pdwValue
|
|
)
|
|
{
|
|
BOOL Success;
|
|
TCHAR TextBuffer[MAX_PATH];
|
|
|
|
Success = SetupGetLineText(
|
|
NULL,
|
|
hInf,
|
|
SectionName,
|
|
KeyName,
|
|
TextBuffer,
|
|
sizeof( TextBuffer ),
|
|
NULL
|
|
);
|
|
|
|
*pdwValue = _tcstoul( TextBuffer, NULL, 0 );
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
DoesInfVersionInfoMatch(
|
|
IN HINF hInf
|
|
)
|
|
{
|
|
|
|
DWORD dwBuildNumber, dwMajorVersion, dwMinorVersion;
|
|
OSVERSIONINFOEX OsVersionInfo;
|
|
|
|
if (( ! GetInfValue( hInf, TEXT("Version"), TEXT("BuildNumber"), &dwBuildNumber )) ||
|
|
( ! GetInfValue( hInf, TEXT("Version"), TEXT("MajorVersion"), &dwMajorVersion )) ||
|
|
( ! GetInfValue( hInf, TEXT("Version"), TEXT("MinorVersion"), &dwMinorVersion ))) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
OsVersionInfo.dwOSVersionInfoSize = sizeof( OsVersionInfo );
|
|
if (!GetVersionEx( (LPOSVERSIONINFO) &OsVersionInfo )) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if ((OsVersionInfo.dwBuildNumber != dwBuildNumber) ||
|
|
(OsVersionInfo.dwMajorVersion != dwMajorVersion) ||
|
|
(OsVersionInfo.dwMinorVersion != dwMinorVersion)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|