You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1988 lines
58 KiB
1988 lines
58 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All Rights Reserved.
|
|
|
|
Module Name:
|
|
|
|
msoobci.c
|
|
|
|
Abstract:
|
|
|
|
Exception Pack installer helper DLL
|
|
Can be used as a co-installer, or called via setup app, or RunDll32 stub
|
|
|
|
This DLL is for internal distribution of exception packs to update
|
|
OS components.
|
|
|
|
Author:
|
|
|
|
Jamie Hunter (jamiehun) 2001-11-27
|
|
|
|
Revision History:
|
|
|
|
Jamie Hunter (jamiehun) 2001-11-27
|
|
|
|
Initial Version
|
|
|
|
--*/
|
|
#include "msoobcip.h"
|
|
|
|
typedef struct _CALLBACKDATA {
|
|
PVOID pDefContext; // context for default queue callback
|
|
LPCTSTR Media; // where old root was
|
|
LPCTSTR Store; // where new root is
|
|
BOOL PreCopy; // if using PreCopy section
|
|
} CALLBACKDATA;
|
|
|
|
HRESULT
|
|
HandleReboot(
|
|
IN DWORD Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prompt for and execute reboot
|
|
|
|
Arguments:
|
|
|
|
Flags - how reboot should be handled
|
|
|
|
Return Value:
|
|
|
|
INST_S_REBOOT
|
|
INST_S_REBOOTING
|
|
|
|
--*/
|
|
|
|
{
|
|
if(Flags & COMP_FLAGS_NOPROMPTREBOOT) {
|
|
//
|
|
// TODO
|
|
// if set, reboot unconditionally
|
|
//
|
|
HANDLE Token;
|
|
BOOL b;
|
|
TOKEN_PRIVILEGES NewPrivileges;
|
|
LUID Luid;
|
|
|
|
//
|
|
// we need to "turn on" reboot privilege
|
|
// if any of this fails, try reboot anyway
|
|
//
|
|
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&Token)) {
|
|
goto try_reboot;
|
|
}
|
|
|
|
if(!LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&Luid)) {
|
|
CloseHandle(Token);
|
|
goto try_reboot;
|
|
}
|
|
|
|
NewPrivileges.PrivilegeCount = 1;
|
|
NewPrivileges.Privileges[0].Luid = Luid;
|
|
NewPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
|
|
AdjustTokenPrivileges(
|
|
Token,
|
|
FALSE,
|
|
&NewPrivileges,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
CloseHandle(Token);
|
|
|
|
try_reboot:
|
|
|
|
//
|
|
// attempt reboot - inform system that this is planned hardware install
|
|
//
|
|
if(ExitWindowsEx(EWX_REBOOT,
|
|
SHTDN_REASON_FLAG_PLANNED
|
|
|SHTDN_REASON_MAJOR_SOFTWARE
|
|
|SHTDN_REASON_MINOR_INSTALLATION)) {
|
|
return INST_S_REBOOTING;
|
|
}
|
|
|
|
} else if(Flags & COMP_FLAGS_PROMPTREBOOT) {
|
|
//
|
|
// TODO
|
|
// if set, prompt for reboot
|
|
//
|
|
if(IsInteractiveWindowStation()) {
|
|
if(SetupPromptReboot(NULL,NULL,FALSE) & SPFILEQ_REBOOT_IN_PROGRESS) {
|
|
return INST_S_REBOOTING;
|
|
}
|
|
}
|
|
}
|
|
return INST_S_REBOOT;
|
|
}
|
|
|
|
HRESULT
|
|
WINAPI
|
|
InstallInfSectionW(
|
|
IN LPCTSTR InfPath,
|
|
IN LPCWSTR SectionName, OPTIONAL
|
|
IN DWORD Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does an install along lines of InstallHinfSection
|
|
|
|
Arguments:
|
|
|
|
InfPath - full path to INF file
|
|
SectionName - name of section including any decoration
|
|
|
|
Return Value:
|
|
|
|
status as hresult
|
|
|
|
--*/
|
|
{
|
|
TCHAR SectionNameBuffer[LINE_LEN];
|
|
TCHAR ServiceSection[LINE_LEN+32];
|
|
HINF hInf = INVALID_HANDLE_VALUE;
|
|
HSPFILEQ hFileQueue = INVALID_HANDLE_VALUE;
|
|
PVOID QueueContext = NULL;
|
|
DWORD Status = NO_ERROR;
|
|
BOOL reboot = FALSE;
|
|
BOOL needUninstallInf = FALSE;
|
|
INT res;
|
|
INFCONTEXT InfLine;
|
|
DWORD InstFlags;
|
|
|
|
//
|
|
// Some decisions are version based
|
|
//
|
|
|
|
//
|
|
// Load the inf file
|
|
//
|
|
hInf = SetupOpenInfFile(InfPath, NULL, INF_STYLE_WIN4, NULL);
|
|
if(hInf == INVALID_HANDLE_VALUE) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
|
|
if(!SectionName) {
|
|
//
|
|
// determine section name
|
|
//
|
|
if(!SetupDiGetActualSectionToInstall(hInf,
|
|
KEY_DEFAULTINSTALL,
|
|
SectionNameBuffer,
|
|
ARRAY_SIZE(SectionNameBuffer),
|
|
NULL,
|
|
NULL)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
SectionName = SectionNameBuffer;
|
|
}
|
|
|
|
//
|
|
// Check to see if the install section has a "Reboot" line.
|
|
// or otherwise reboot forced
|
|
//
|
|
if((Flags & COMP_FLAGS_NEEDSREBOOT)
|
|
|| (SetupFindFirstLine(hInf, SectionName, KEY_REBOOT, &InfLine))) {
|
|
reboot = TRUE;
|
|
}
|
|
|
|
//
|
|
// See if UI allowed
|
|
//
|
|
if(((Flags & COMP_FLAGS_NOUI)==0) && !IsInteractiveWindowStation()) {
|
|
Flags |= COMP_FLAGS_NOUI;
|
|
}
|
|
|
|
//
|
|
// Load any layout file
|
|
//
|
|
SetupOpenAppendInfFile(NULL, hInf, NULL);
|
|
|
|
//
|
|
// Create a setup file queue and initialize the default queue callback.
|
|
//
|
|
hFileQueue = SetupOpenFileQueue();
|
|
if(hFileQueue == INVALID_HANDLE_VALUE) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
QueueContext = SetupInitDefaultQueueCallbackEx(
|
|
NULL,
|
|
((Flags & COMP_FLAGS_NOUI) ? INVALID_HANDLE_VALUE : NULL),
|
|
0,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if(!QueueContext) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
|
|
if(!SetupInstallFilesFromInfSection(hInf,
|
|
NULL,
|
|
hFileQueue,
|
|
SectionName,
|
|
NULL,
|
|
0 // SP_COPY_xxxx
|
|
)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
//
|
|
// Commit file queue.
|
|
//
|
|
if(!SetupCommitFileQueue(NULL, hFileQueue, SetupDefaultQueueCallback, QueueContext)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
|
|
//
|
|
// Note, if the INF contains a (non-NULL) ClassGUID, then it will have
|
|
// been installed into %windir%\Inf during the above queue committal.
|
|
// We make no effort to subsequently uninstall it (and its associated
|
|
// PNF and CAT) if something fails below.
|
|
//
|
|
needUninstallInf = TRUE;
|
|
|
|
InstFlags = SPINST_ALL;
|
|
if(g_VerInfo.dwMajorVersion < 5) {
|
|
InstFlags = 0x1f;
|
|
}
|
|
|
|
if(!SetupInstallFromInfSection(NULL,
|
|
hInf,
|
|
SectionName,
|
|
InstFlags &~ SPINST_FILES,
|
|
NULL, // HKEY_xxxx
|
|
NULL, // no copying...
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
lstrcpyn(ServiceSection,SectionName,LINE_LEN);
|
|
lstrcat(ServiceSection,KEY_DOTSERVICES);
|
|
//
|
|
// If services section exists, install it
|
|
//
|
|
if(SetupFindFirstLine(hInf, ServiceSection, NULL, &InfLine)) {
|
|
if(!SetupInstallServicesFromInfSection(hInf,ServiceSection,0)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
if(GetLastError() == ERROR_SUCCESS_REBOOT_REQUIRED) {
|
|
reboot = TRUE;
|
|
}
|
|
}
|
|
res = SetupPromptReboot(hFileQueue, NULL, TRUE);
|
|
if((res!=-1) && (res & SPFILEQ_REBOOT_RECOMMENDED)) {
|
|
reboot = TRUE;
|
|
}
|
|
|
|
final:
|
|
|
|
if(QueueContext) {
|
|
SetupTermDefaultQueueCallback(QueueContext);
|
|
}
|
|
if(hFileQueue != INVALID_HANDLE_VALUE) {
|
|
SetupCloseFileQueue(hFileQueue);
|
|
}
|
|
if(hInf != INVALID_HANDLE_VALUE) {
|
|
SetupCloseInfFile(hInf);
|
|
}
|
|
if(Status == NO_ERROR) {
|
|
//
|
|
// are we meant to prompt for reboot?
|
|
//
|
|
if(reboot) {
|
|
return HandleReboot(Flags);
|
|
} else {
|
|
return S_OK;
|
|
}
|
|
}
|
|
if(needUninstallInf) {
|
|
//
|
|
// call SetupUninstallOEMInf ?
|
|
//
|
|
}
|
|
return HRESULT_FROM_SETUPAPI(Status);
|
|
}
|
|
|
|
HRESULT
|
|
WINAPI
|
|
InstallInfSectionA(
|
|
IN LPCSTR InfPath,
|
|
IN LPCSTR SectionName, OPTIONAL
|
|
IN DWORD Flags
|
|
)
|
|
{
|
|
TCHAR OutPath[MAX_PATH];
|
|
TCHAR OutSection[LINE_LEN]; // as per friendly name
|
|
INT sz;
|
|
if(InfPath) {
|
|
sz = MultiByteToWideChar(CP_ACP,0,InfPath,-1,OutPath,ARRAY_SIZE(OutPath));
|
|
if(!sz) {
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
if(SectionName) {
|
|
sz = MultiByteToWideChar(CP_ACP,0,SectionName,-1,OutSection,ARRAY_SIZE(OutSection));
|
|
if(!sz) {
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
return InstallInfSection(InfPath ? OutPath : NULL,
|
|
SectionName ? OutSection : NULL,
|
|
Flags);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
AttemptStoreCopy(
|
|
IN CALLBACKDATA *pCallbackData,
|
|
IN LPCTSTR Root, OPTIONAL
|
|
IN LPCTSTR Source,
|
|
IN LPCTSTR Target OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copy from source to target, redirected to the expack store
|
|
|
|
Arguments:
|
|
|
|
pCallbackData - as passed to PreCopyQueueCallback
|
|
Root - root to source directory
|
|
Source - source, relative to Root
|
|
Target - target name
|
|
|
|
Return Value:
|
|
|
|
status as hresult
|
|
|
|
--*/
|
|
{
|
|
TCHAR FullSource[MAX_PATH];
|
|
TCHAR FullTarget[MAX_PATH];
|
|
LPTSTR SubDir;
|
|
LPTSTR BaseName;
|
|
LPTSTR DestName;
|
|
LPCTSTR p;
|
|
DWORD dwStatus;
|
|
HRESULT hrStatus;
|
|
|
|
if(Root) {
|
|
lstrcpyn(FullSource,Root,MAX_PATH);
|
|
hrStatus = ConcatPath(FullSource,MAX_PATH,Source);
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
return hrStatus;
|
|
}
|
|
} else {
|
|
lstrcpyn(FullSource,Source,MAX_PATH);
|
|
}
|
|
//
|
|
// we want to determine the source sub-directory
|
|
//
|
|
SubDir = FullSource;
|
|
p = pCallbackData->Media;
|
|
while(*p && (*p == *SubDir)) {
|
|
p = CharNext(p);
|
|
SubDir = CharNext(SubDir);
|
|
}
|
|
if(*p || ((*SubDir != TEXT('\\')) && (*SubDir != TEXT('/')))) {
|
|
//
|
|
// not a sub-directory of media
|
|
//
|
|
DebugPrint(TEXT("Not copying \"%s\" (not subdirectory of \"%s\")"),FullSource,pCallbackData->Media);
|
|
return E_FAIL;
|
|
}
|
|
lstrcpyn(FullTarget,pCallbackData->Store,MAX_PATH);
|
|
hrStatus = ConcatPath(FullTarget,MAX_PATH,SubDir);
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
return hrStatus;
|
|
}
|
|
if(Target) {
|
|
//
|
|
// change final name of this
|
|
//
|
|
BaseName = GetBaseName(Target);
|
|
DestName = GetBaseName(FullTarget);
|
|
if(((DestName-FullTarget)+lstrlen(BaseName))>=MAX_PATH) {
|
|
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
lstrcpy(DestName,BaseName);
|
|
}
|
|
if(GetFileAttributes(FullTarget)!=INVALID_FILE_ATTRIBUTES) {
|
|
//
|
|
// allow file to be replaced
|
|
//
|
|
SetFileAttributes(FullTarget,FILE_ATTRIBUTE_NORMAL);
|
|
}
|
|
MakeSureParentPathExists(FullTarget);
|
|
if(CopyFile(FullSource,FullTarget,FALSE)) {
|
|
return S_OK;
|
|
}
|
|
dwStatus = GetLastError();
|
|
return HRESULT_FROM_WIN32(dwStatus);
|
|
}
|
|
|
|
UINT
|
|
CALLBACK
|
|
PreCopyQueueCallback(
|
|
IN PVOID Context,
|
|
IN UINT Notification,
|
|
IN UINT_PTR Param1,
|
|
IN UINT_PTR Param2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Intent is to copy files from existing media to final media
|
|
Copy all files
|
|
|
|
Arguments:
|
|
|
|
FileName - name of file to scan
|
|
|
|
Return Value:
|
|
|
|
status as hresult
|
|
|
|
--*/
|
|
{
|
|
CALLBACKDATA * pCallbackData = (CALLBACKDATA *)Context;
|
|
|
|
switch(Notification) {
|
|
case SPFILENOTIFY_NEEDMEDIA:
|
|
{
|
|
UINT res;
|
|
SOURCE_MEDIA *pMedia = (SOURCE_MEDIA *)Param1;
|
|
SOURCE_MEDIA MediaCopy = *pMedia;
|
|
LPCTSTR Path = NULL;
|
|
//
|
|
// get the media in place - let default callback do this
|
|
// however we can't deal with media location being changed
|
|
// so don't allow it
|
|
//
|
|
MediaCopy.Flags |= SP_COPY_NOSKIP|SP_COPY_NOBROWSE;
|
|
res= SetupDefaultQueueCallback(pCallbackData->pDefContext,
|
|
Notification,
|
|
(UINT_PTR)&MediaCopy,
|
|
Param2);
|
|
if(res==FILEOP_DOIT) {
|
|
//
|
|
// typical case
|
|
// SourcePath unchanged
|
|
//
|
|
Path = pMedia->SourcePath;
|
|
} else if(res == FILEOP_NEWPATH) {
|
|
//
|
|
// alternative case
|
|
// we said above we don't want this
|
|
//
|
|
SetLastError(ERROR_CANCELLED);
|
|
return FILEOP_ABORT;
|
|
} else if(res == FILEOP_SKIP) {
|
|
//
|
|
// skip
|
|
// we said above we don't want this
|
|
//
|
|
SetLastError(ERROR_CANCELLED);
|
|
return FILEOP_ABORT;
|
|
} else {
|
|
//
|
|
// existing failure case
|
|
//
|
|
return res;
|
|
}
|
|
//
|
|
// if the tag exists at source media, copy it
|
|
// if the sourcefile exists at source media, copy it now
|
|
// (it might reference a cab file)
|
|
//
|
|
AttemptStoreCopy(pCallbackData,Path,pMedia->Tagfile,NULL);
|
|
AttemptStoreCopy(pCallbackData,Path,pMedia->SourceFile,NULL);
|
|
}
|
|
return FILEOP_DOIT;
|
|
|
|
case SPFILENOTIFY_STARTCOPY:
|
|
{
|
|
UINT res;
|
|
FILEPATHS *pPaths = (FILEPATHS*)Param1;
|
|
if(pCallbackData->PreCopy) {
|
|
//
|
|
// we want the target name (PRECOPY case)
|
|
//
|
|
AttemptStoreCopy(pCallbackData,NULL,pPaths->Source,pPaths->Target);
|
|
} else {
|
|
//
|
|
// we want the source name
|
|
//
|
|
AttemptStoreCopy(pCallbackData,NULL,pPaths->Source,NULL);
|
|
}
|
|
}
|
|
return FILEOP_SKIP;
|
|
|
|
case SPFILENOTIFY_STARTDELETE:
|
|
return FILEOP_SKIP;
|
|
|
|
case SPFILENOTIFY_STARTRENAME:
|
|
return FILEOP_SKIP;
|
|
|
|
|
|
default:
|
|
return SetupDefaultQueueCallback(pCallbackData->pDefContext,
|
|
Notification,
|
|
Param1,
|
|
Param2);
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
InstallExceptionPackFromInf(
|
|
IN LPCTSTR InfPath,
|
|
IN LPCTSTR Media,
|
|
IN LPCTSTR Store,
|
|
IN DWORD Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Assume INF installed into INF directory
|
|
all decisions made
|
|
media/store known
|
|
|
|
Arguments:
|
|
|
|
InfPath - name of Inf in Media location
|
|
Media - InfPath less InfName
|
|
Store - expack store
|
|
Flags - various flags
|
|
|
|
Return Value:
|
|
|
|
status as hresult
|
|
|
|
--*/
|
|
{
|
|
TCHAR SectionName[LINE_LEN];
|
|
TCHAR PrecopySectionName[LINE_LEN];
|
|
HINF hInf;
|
|
HSPFILEQ hFileQueue = INVALID_HANDLE_VALUE;
|
|
PVOID QueueContext = NULL;
|
|
CALLBACKDATA CallbackData;
|
|
DWORD Status;
|
|
|
|
//
|
|
// exception packs must be moved to a component-specific store
|
|
// run through a file-install to see what files we have to copy
|
|
// and use that list to determine source media
|
|
//
|
|
hInf = SetupOpenInfFile(InfPath, NULL, INF_STYLE_WIN4, NULL);
|
|
if(hInf == INVALID_HANDLE_VALUE) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
if(!SetupDiGetActualSectionToInstall(hInf,
|
|
KEY_DEFAULTINSTALL,
|
|
SectionName,
|
|
ARRAY_SIZE(SectionName),
|
|
NULL,
|
|
NULL)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
SetupOpenAppendInfFile(NULL,hInf,NULL);
|
|
hFileQueue = SetupOpenFileQueue();
|
|
if(hFileQueue == INVALID_HANDLE_VALUE) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
|
|
if((lstrlen(SectionName)+10)>LINE_LEN) {
|
|
Status = ERROR_INSUFFICIENT_BUFFER;
|
|
goto final;
|
|
}
|
|
lstrcpy(PrecopySectionName,SectionName);
|
|
lstrcat(PrecopySectionName,KEY_DOTPRECOPY);
|
|
|
|
|
|
QueueContext = SetupInitDefaultQueueCallbackEx(
|
|
NULL,
|
|
((Flags & COMP_FLAGS_NOUI) ? INVALID_HANDLE_VALUE : NULL),
|
|
0,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if(!QueueContext) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
ZeroMemory(&CallbackData,sizeof(CallbackData));
|
|
CallbackData.pDefContext = QueueContext;
|
|
CallbackData.Store = Store;
|
|
CallbackData.Media = Media;
|
|
|
|
|
|
if(SetupGetLineCount(hInf,PrecopySectionName)>0) {
|
|
//
|
|
// do the pre-copy install via this section instead
|
|
//
|
|
CallbackData.PreCopy = TRUE;
|
|
if(!SetupInstallFilesFromInfSection(hInf,
|
|
NULL,
|
|
hFileQueue,
|
|
PrecopySectionName,
|
|
NULL,
|
|
0 // SP_COPY_xxxx
|
|
)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
} else {
|
|
CallbackData.PreCopy = FALSE;
|
|
if(!SetupInstallFilesFromInfSection(hInf,
|
|
NULL,
|
|
hFileQueue,
|
|
SectionName,
|
|
NULL,
|
|
0 // SP_COPY_xxxx
|
|
)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Commit file queue, this will get the files to the store
|
|
//
|
|
if(!SetupCommitFileQueue(NULL, hFileQueue, PreCopyQueueCallback, &CallbackData)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
if(hFileQueue != INVALID_HANDLE_VALUE) {
|
|
SetupCloseFileQueue(hFileQueue);
|
|
hFileQueue = INVALID_HANDLE_VALUE;
|
|
}
|
|
if(hInf != INVALID_HANDLE_VALUE) {
|
|
SetupCloseInfFile(hInf);
|
|
hInf = INVALID_HANDLE_VALUE;
|
|
}
|
|
//
|
|
// now install files from here to final destination
|
|
// this should be relatively quick so don't bother with UI
|
|
//
|
|
if(!(Flags & COMP_FLAGS_NOINSTALL)) {
|
|
return InstallInfSection(InfPath,
|
|
SectionName,
|
|
COMP_FLAGS_NOUI);
|
|
}
|
|
return S_OK;
|
|
|
|
//
|
|
// TODO - move files to component directory
|
|
//
|
|
final:
|
|
if(QueueContext) {
|
|
SetupTermDefaultQueueCallback(QueueContext);
|
|
}
|
|
if(hFileQueue != INVALID_HANDLE_VALUE) {
|
|
SetupCloseFileQueue(hFileQueue);
|
|
}
|
|
if(hInf != INVALID_HANDLE_VALUE) {
|
|
SetupCloseInfFile(hInf);
|
|
}
|
|
return HRESULT_FROM_SETUPAPI(Status);
|
|
}
|
|
|
|
DWORD
|
|
DownlevelQueryInfOriginalFileInformation(
|
|
IN HINF hInf,
|
|
PSP_INF_INFORMATION InfInformation,
|
|
PSP_ORIGINAL_FILE_INFO OriginalFileInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Emulates SetupQueryInfOriginalFileInformation
|
|
we need to look in hINF to determine catalog name
|
|
only partial implementation enough to support x86
|
|
(will degrade on other architectures)
|
|
|
|
Arguments:
|
|
|
|
hInf - handle to open INF file
|
|
InfInformation - information obtained about original INF
|
|
pInfOriginalFileInformation - fill with inf/catalog names
|
|
|
|
Return Value:
|
|
|
|
status as DWORD (not HRESULT)
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// in downlevel case, filename is name of file we opened
|
|
// catalog is referenced in the INF
|
|
//
|
|
// get basename of the INF
|
|
// (actually returns full name, but we'll deal with it right)
|
|
//
|
|
INFCONTEXT InfLine;
|
|
SYSTEM_INFO SysInfo;
|
|
TCHAR KeyName[LINE_LEN];
|
|
|
|
if(!SetupQueryInfFileInformation(InfInformation,
|
|
0,
|
|
OriginalFileInfo->OriginalInfName,
|
|
ARRAY_SIZE(OriginalFileInfo->OriginalInfName),
|
|
NULL)) {
|
|
return GetLastError();
|
|
}
|
|
//
|
|
// now determine name of catalog
|
|
//
|
|
GetSystemInfo(&SysInfo);
|
|
if(SysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) {
|
|
//
|
|
// look for .NTx86
|
|
// only makes sence for x86
|
|
// which is the only architecture we'll migrate Win9x/NT4 to Win2k+
|
|
//
|
|
lstrcpy(KeyName,INFSTR_KEY_CATALOGFILE);
|
|
lstrcat(KeyName,TEXT(".NTx86"));
|
|
if(SetupFindFirstLine(hInf,INFSTR_SECT_VERSION,KeyName,&InfLine)) {
|
|
if(SetupGetStringField(&InfLine,
|
|
1,
|
|
OriginalFileInfo->OriginalCatalogName,
|
|
ARRAY_SIZE(OriginalFileInfo->OriginalCatalogName),
|
|
NULL)) {
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// look for .NT (even on 9x, as the exception pack will be re-parsed
|
|
// on NT)
|
|
//
|
|
lstrcpy(KeyName,INFSTR_KEY_CATALOGFILE);
|
|
lstrcat(KeyName,TEXT(".NT"));
|
|
if(SetupFindFirstLine(hInf,INFSTR_SECT_VERSION,KeyName,&InfLine)) {
|
|
if(SetupGetStringField(&InfLine,
|
|
1,
|
|
OriginalFileInfo->OriginalCatalogName,
|
|
ARRAY_SIZE(OriginalFileInfo->OriginalCatalogName),
|
|
NULL)) {
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
//
|
|
// finally look for undecorated
|
|
//
|
|
if(SetupFindFirstLine(hInf,INFSTR_SECT_VERSION,INFSTR_KEY_CATALOGFILE,&InfLine)) {
|
|
if(SetupGetStringField(&InfLine,
|
|
1,
|
|
OriginalFileInfo->OriginalCatalogName,
|
|
ARRAY_SIZE(OriginalFileInfo->OriginalCatalogName),
|
|
NULL)) {
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
//
|
|
// no catalog
|
|
//
|
|
OriginalFileInfo->OriginalCatalogName[0] = TEXT('\0');
|
|
return NO_ERROR;
|
|
}
|
|
|
|
HRESULT
|
|
GetInfOriginalFileInformation(
|
|
IN HINF hInf,
|
|
OUT PSP_ORIGINAL_FILE_INFO pInfOriginalFileInformation
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a handle to an INF, determine names of inf and catalog files
|
|
|
|
Arguments:
|
|
|
|
hInf - handle to open INF file
|
|
pInfOriginalFileInformation - inf/catalog names
|
|
|
|
Return Value:
|
|
|
|
status as hresult
|
|
|
|
--*/
|
|
{
|
|
PSP_INF_INFORMATION pInfInformation = NULL;
|
|
DWORD InfInformationSize;
|
|
DWORD Status;
|
|
|
|
InfInformationSize = 8192;
|
|
pInfInformation = (PSP_INF_INFORMATION)malloc(InfInformationSize);
|
|
if (pInfInformation == NULL) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
if(!SetupGetInfInformation(hInf,INFINFO_INF_SPEC_IS_HINF,pInfInformation,InfInformationSize,&InfInformationSize)) {
|
|
PVOID TempBuf;
|
|
Status = GetLastError();
|
|
if(Status != ERROR_INSUFFICIENT_BUFFER) {
|
|
free(pInfInformation);
|
|
return HRESULT_FROM_SETUPAPI(Status);
|
|
}
|
|
TempBuf = realloc(pInfInformation,InfInformationSize);
|
|
if(!TempBuf) {
|
|
free(pInfInformation);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
if(!SetupGetInfInformation(hInf,INFINFO_INF_SPEC_IS_HINF,pInfInformation,InfInformationSize,&InfInformationSize)) {
|
|
Status = GetLastError();
|
|
free(pInfInformation);
|
|
return HRESULT_FROM_SETUPAPI(Status);
|
|
}
|
|
pInfOriginalFileInformation->cbSize = sizeof(SP_ORIGINAL_FILE_INFO);
|
|
if((g_VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) && (g_VerInfo.dwMajorVersion >= 5)) {
|
|
//
|
|
// Win2k+ - have SetupAPI tell us the information (we're querying oem*.inf)
|
|
//
|
|
if (!QueryInfOriginalFileInformation(pInfInformation,0,NULL,pInfOriginalFileInformation)) {
|
|
Status = GetLastError();
|
|
free(pInfInformation);
|
|
return HRESULT_FROM_SETUPAPI(Status);
|
|
}
|
|
} else {
|
|
//
|
|
// <Win2k - querying source INF, get information from there
|
|
//
|
|
Status = DownlevelQueryInfOriginalFileInformation(hInf,pInfInformation,pInfOriginalFileInformation);
|
|
if(Status != NO_ERROR) {
|
|
free(pInfInformation);
|
|
return HRESULT_FROM_SETUPAPI(Status);
|
|
}
|
|
}
|
|
free(pInfInformation);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
DeleteDirectoryRecursive(
|
|
IN LPCTSTR Path
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
delete specified directory recursively
|
|
|
|
Arguments:
|
|
|
|
Path - path of the directory to delete
|
|
|
|
Return Value:
|
|
|
|
as HRESULT
|
|
S_FALSE if directory doesn't exist
|
|
S_OK if directory deleted
|
|
other error if, eg, files in use
|
|
|
|
--*/
|
|
{
|
|
TCHAR Wildcard[MAX_PATH];
|
|
TCHAR Target[MAX_PATH];
|
|
HRESULT hrStatus;
|
|
DWORD Status;
|
|
HRESULT hrFirstError = S_FALSE;
|
|
HANDLE hFind;
|
|
WIN32_FIND_DATA FindData;
|
|
|
|
//
|
|
// enumerate the directory
|
|
//
|
|
lstrcpyn(Wildcard,Path,MAX_PATH);
|
|
hrStatus = ConcatPath(Wildcard,MAX_PATH,TEXT("\\*.*"));
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
return hrStatus;
|
|
}
|
|
hFind = FindFirstFile(Wildcard,&FindData);
|
|
if(hFind != INVALID_HANDLE_VALUE) {
|
|
hrFirstError = S_OK;
|
|
do {
|
|
if(lstrcmp(FindData.cFileName,TEXT(".")) == 0) {
|
|
continue;
|
|
}
|
|
if(lstrcmp(FindData.cFileName,TEXT("..")) == 0) {
|
|
continue;
|
|
}
|
|
lstrcpyn(Target,Path,MAX_PATH);
|
|
hrStatus = ConcatPath(Target,MAX_PATH,FindData.cFileName);
|
|
if(SUCCEEDED(hrStatus)) {
|
|
if(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
hrStatus = DeleteDirectoryRecursive(Target);
|
|
if(SUCCEEDED(hrFirstError) && !SUCCEEDED(hrStatus)) {
|
|
hrFirstError = hrStatus;
|
|
}
|
|
} else {
|
|
SetFileAttributes(Target,FILE_ATTRIBUTE_NORMAL);
|
|
if(!DeleteFile(Target)) {
|
|
Status = GetLastError();
|
|
if(SUCCEEDED(hrFirstError)) {
|
|
hrFirstError = HRESULT_FROM_WIN32(Status);
|
|
}
|
|
}
|
|
}
|
|
} else if(SUCCEEDED(hrFirstError)) {
|
|
hrFirstError = hrStatus;
|
|
}
|
|
} while (FindNextFile(hFind,&FindData));
|
|
FindClose(hFind);
|
|
}
|
|
//
|
|
// now delete this directory
|
|
//
|
|
|
|
SetFileAttributes(Path,FILE_ATTRIBUTE_NORMAL);
|
|
if(RemoveDirectory(Path) || !SUCCEEDED(hrFirstError)) {
|
|
return hrFirstError;
|
|
}
|
|
Status = GetLastError();
|
|
if((Status == ERROR_PATH_NOT_FOUND) || (Status == ERROR_FILE_NOT_FOUND)) {
|
|
return hrFirstError;
|
|
}
|
|
return HRESULT_FROM_WIN32(Status);
|
|
}
|
|
|
|
HRESULT
|
|
RevertStore(
|
|
IN LPCTSTR BackupDir,
|
|
IN LPCTSTR TargetDir
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
moves contents from backup back to original location
|
|
overwriting files/directories if needed
|
|
|
|
Arguments:
|
|
|
|
BackupDir - directory restoring from
|
|
TargetDir - directory restoring to
|
|
|
|
Return Value:
|
|
|
|
as HRESULT
|
|
S_OK if backup created
|
|
other error if, eg, files in use
|
|
|
|
--*/
|
|
{
|
|
TCHAR Wildcard[MAX_PATH];
|
|
TCHAR Source[MAX_PATH];
|
|
TCHAR Target[MAX_PATH];
|
|
HRESULT hrStatus;
|
|
HRESULT hrFirstError = S_FALSE;
|
|
DWORD Status;
|
|
DWORD dwRes;
|
|
HANDLE hFind;
|
|
WIN32_FIND_DATA FindData;
|
|
|
|
lstrcpyn(Wildcard,BackupDir,MAX_PATH);
|
|
hrStatus = ConcatPath(Wildcard,MAX_PATH,TEXT("\\*.*"));
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
return hrStatus;
|
|
}
|
|
hFind = FindFirstFile(Wildcard,&FindData);
|
|
if(hFind != INVALID_HANDLE_VALUE) {
|
|
hrFirstError = S_OK;
|
|
do {
|
|
if(lstrcmp(FindData.cFileName,TEXT(".")) == 0) {
|
|
continue;
|
|
}
|
|
if(lstrcmp(FindData.cFileName,TEXT("..")) == 0) {
|
|
continue;
|
|
}
|
|
lstrcpyn(Source,BackupDir,MAX_PATH);
|
|
hrStatus = ConcatPath(Source,MAX_PATH,FindData.cFileName);
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
if(SUCCEEDED(hrFirstError)) {
|
|
hrFirstError = hrStatus;
|
|
}
|
|
continue;
|
|
}
|
|
lstrcpyn(Target,TargetDir,MAX_PATH);
|
|
hrStatus = ConcatPath(Target,MAX_PATH,FindData.cFileName);
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
if(SUCCEEDED(hrFirstError)) {
|
|
hrFirstError = hrStatus;
|
|
}
|
|
continue;
|
|
}
|
|
//
|
|
// does target exist?
|
|
//
|
|
dwRes = GetFileAttributes(Target);
|
|
if(dwRes != INVALID_FILE_ATTRIBUTES) {
|
|
if(dwRes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
//
|
|
// revert store recursively
|
|
//
|
|
hrStatus = RevertStore(Source,Target);
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
if(SUCCEEDED(hrFirstError)) {
|
|
hrFirstError = hrStatus;
|
|
}
|
|
continue;
|
|
}
|
|
} else {
|
|
SetFileAttributes(Target,FILE_ATTRIBUTE_NORMAL);
|
|
if(!DeleteFile(Target)) {
|
|
Status = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
if(!MoveFile(Source,Target)) {
|
|
Status = GetLastError();
|
|
hrStatus = HRESULT_FROM_WIN32(Status);
|
|
if(SUCCEEDED(hrFirstError)) {
|
|
hrFirstError = hrStatus;
|
|
}
|
|
}
|
|
} while (FindNextFile(hFind,&FindData));
|
|
FindClose(hFind);
|
|
}
|
|
//
|
|
// now attempt to remove the backup directory
|
|
//
|
|
if(RemoveDirectory(BackupDir) || !SUCCEEDED(hrFirstError)) {
|
|
return hrFirstError;
|
|
}
|
|
Status = GetLastError();
|
|
if((Status == ERROR_PATH_NOT_FOUND) || (Status == ERROR_FILE_NOT_FOUND)) {
|
|
return hrFirstError;
|
|
}
|
|
return HRESULT_FROM_WIN32(Status);
|
|
}
|
|
|
|
HRESULT
|
|
BackupStore(
|
|
IN LPCTSTR Path,
|
|
OUT LPTSTR BackupDir,
|
|
OUT DWORD BackupDirLen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
moves contents to new backup, ideally to \\$BACKUP$
|
|
returns name of backup
|
|
|
|
Arguments:
|
|
|
|
Path - path of the store
|
|
BackupDir - filled with directory containing backup
|
|
BackupDirLen - containing length of BackupDir
|
|
|
|
Return Value:
|
|
|
|
as HRESULT
|
|
S_OK if backup created
|
|
other error if, eg, files in use
|
|
|
|
--*/
|
|
{
|
|
TCHAR Wildcard[MAX_PATH];
|
|
TCHAR Source[MAX_PATH];
|
|
TCHAR Target[MAX_PATH];
|
|
HRESULT hrStatus;
|
|
DWORD Status;
|
|
HANDLE hFind;
|
|
WIN32_FIND_DATA FindData;
|
|
int i;
|
|
int len;
|
|
|
|
lstrcpyn(BackupDir,Path,BackupDirLen);
|
|
hrStatus = ConcatPath(BackupDir,BackupDirLen,TEXT("\\$BACKUP$"));
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
//
|
|
// obviously path is too big, no point ignoring
|
|
// as we'd fail elsewhere
|
|
//
|
|
return hrStatus;
|
|
}
|
|
len = lstrlen(BackupDir);
|
|
if((BackupDirLen-len)<5) {
|
|
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
//
|
|
// first, if there's a backup, try and delete it
|
|
//
|
|
hrStatus = DeleteDirectoryRecursive(BackupDir);
|
|
if(SUCCEEDED(hrStatus)) {
|
|
hrStatus = MakeSurePathExists(BackupDir);
|
|
}
|
|
if((hrStatus == HRESULT_FROM_WIN32(ERROR_WRITE_PROTECT)) ||
|
|
(hrStatus == HRESULT_FROM_WIN32(ERROR_INVALID_ACCESS))) {
|
|
//
|
|
// no point even trying again
|
|
//
|
|
return hrStatus;
|
|
}
|
|
for(i = 0;!SUCCEEDED(hrStatus) && i<1000;i++) {
|
|
_sntprintf(BackupDir+len,5,TEXT(".%03u"),i);
|
|
hrStatus = DeleteDirectoryRecursive(BackupDir);
|
|
if(SUCCEEDED(hrStatus)) {
|
|
hrStatus = MakeSurePathExists(BackupDir);
|
|
}
|
|
}
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
return hrStatus;
|
|
}
|
|
//
|
|
// now we have a backup directory, move all the files there
|
|
//
|
|
lstrcpyn(Wildcard,Path,MAX_PATH);
|
|
hrStatus = ConcatPath(Wildcard,MAX_PATH,TEXT("\\*.*"));
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
return hrStatus;
|
|
}
|
|
hrStatus = S_FALSE;
|
|
hFind = FindFirstFile(Wildcard,&FindData);
|
|
if(hFind != INVALID_HANDLE_VALUE) {
|
|
do {
|
|
if(lstrcmp(FindData.cFileName,TEXT(".")) == 0) {
|
|
continue;
|
|
}
|
|
if(lstrcmp(FindData.cFileName,TEXT("..")) == 0) {
|
|
continue;
|
|
}
|
|
if(_tcsnicmp(FindData.cFileName,TEXT("$BACKUP$"),8) == 0) {
|
|
//
|
|
// a/the backup directory
|
|
//
|
|
continue;
|
|
}
|
|
lstrcpyn(Source,Path,MAX_PATH);
|
|
hrStatus = ConcatPath(Source,MAX_PATH,FindData.cFileName);
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
break;
|
|
}
|
|
lstrcpyn(Target,BackupDir,MAX_PATH);
|
|
hrStatus = ConcatPath(Target,MAX_PATH,FindData.cFileName);
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
break;
|
|
}
|
|
if(!MoveFile(Source,Target)) {
|
|
Status = GetLastError();
|
|
hrStatus = HRESULT_FROM_WIN32(Status);
|
|
}
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
break;
|
|
}
|
|
hrStatus = S_OK;
|
|
} while (FindNextFile(hFind,&FindData));
|
|
FindClose(hFind);
|
|
}
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
RevertStore(BackupDir,Path);
|
|
}
|
|
return hrStatus;
|
|
}
|
|
|
|
HRESULT
|
|
WINAPI
|
|
InstallComponentW(
|
|
IN LPCTSTR InfPath,
|
|
IN DWORD Flags,
|
|
IN const GUID * CompGuid, OPTIONAL
|
|
IN INT VerMajor, OPTIONAL
|
|
IN INT VerMinor, OPTIONAL
|
|
IN INT VerBuild, OPTIONAL
|
|
IN INT VerQFE, OPTIONAL
|
|
IN LPCTSTR Name OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
exported for call by setup routine
|
|
install a component with a given version assumed
|
|
show progress while pulling files from original location
|
|
|
|
Arguments:
|
|
|
|
InfPath - path to INF file
|
|
Flags - flags
|
|
COMP_FLAGS_NOINSTALL - place in store, don't install
|
|
COMP_FLAGS_NOUI - don't show any UI
|
|
COMP_FLAGS_NOPROMPTREBOOT - reboot if needed (no prompt)
|
|
COMP_FLAGS_PROMPTREBOOT - prompt for reboot if needed
|
|
COMP_FLAGS_NEEDSREBOOT - assume reboot needed
|
|
|
|
CompGuid - if NULL, use GUID specified in INF (ComponentId)
|
|
else verify against GUID specified in INF
|
|
VerMajor/VerMinor/VerBuild/VerQFE
|
|
- if -1, use version specified in INF (ComponentVersion)
|
|
else use this version and verify against version if specified in INF
|
|
Name
|
|
- if NULL, use name specified in INF (ComponentName)
|
|
else use this component name.
|
|
|
|
Return Value:
|
|
|
|
status as hresult
|
|
|
|
--*/
|
|
{
|
|
HINF hInf = INVALID_HANDLE_VALUE;
|
|
INFCONTEXT InfLine;
|
|
TCHAR Buffer[MAX_PATH*3];
|
|
TCHAR FriendlyName[DESC_SIZE];
|
|
TCHAR NewStore[MAX_PATH];
|
|
TCHAR OldStore[MAX_PATH];
|
|
TCHAR MediaRoot[MAX_PATH];
|
|
TCHAR GuidString[64];
|
|
LPTSTR BaseName;
|
|
LPTSTR SubDir;
|
|
DWORD Status = NO_ERROR; // set Status or hrStatus
|
|
DWORD DwRes;
|
|
UINT UiRes;
|
|
HRESULT hrStatus = S_OK;
|
|
GUID InfGuid;
|
|
INT InfVerMajor,InfVerMinor,InfVerBuild,InfVerQFE;
|
|
BOOL PrevReg = FALSE;
|
|
BOOL NeedProxy = FALSE;
|
|
BOOL CanRevert = FALSE;
|
|
BOOL BackedUp = FALSE;
|
|
SETUP_OS_COMPONENT_DATA OsComponentData;
|
|
SETUP_OS_EXCEPTION_DATA OsExceptionData;
|
|
SETUP_OS_COMPONENT_DATA NewOsComponentData;
|
|
SETUP_OS_EXCEPTION_DATA NewOsExceptionData;
|
|
SP_ORIGINAL_FILE_INFO InfOriginalFileInformation;
|
|
|
|
//
|
|
// validate args
|
|
//
|
|
if((InfPath == NULL)
|
|
|| (VerMajor<-1)
|
|
|| (VerMajor>65535)
|
|
|| (VerMinor<-1)
|
|
|| (VerMinor>65535)
|
|
|| (VerBuild<-1)
|
|
|| (VerBuild>65535)
|
|
|| (VerQFE<-1)
|
|
|| (VerQFE>65535)
|
|
|| (lstrlen(InfPath)>=MAX_PATH)
|
|
|| (Name && (lstrlen(Name)>=ARRAY_SIZE(FriendlyName)))) {
|
|
return E_INVALIDARG;
|
|
}
|
|
//
|
|
// open the INF, we're going to do some information finding
|
|
//
|
|
hInf = SetupOpenInfFile(InfPath,NULL,INF_STYLE_WIN4,NULL);
|
|
if(hInf == INVALID_HANDLE_VALUE) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
//
|
|
// get various information about this exception pack
|
|
// We want to know about the exception pack
|
|
// check classguid is correct
|
|
// get componentid
|
|
// get version if exists, and validate against any passed in
|
|
// get description if exists (overwritten by that passed in)
|
|
//
|
|
|
|
//
|
|
// CLASSGUID={F5776D81-AE53-4935-8E84-B0B283D8BCEF}
|
|
//
|
|
if(!SetupFindFirstLine(hInf,INFSTR_SECT_VERSION,INFSTR_KEY_CLASSGUID,&InfLine)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
if(!SetupGetStringField(&InfLine,1,Buffer,MAX_PATH,NULL)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
if(_tcsicmp(Buffer,TEXT("{F5776D81-AE53-4935-8E84-B0B283D8BCEF}"))!=0) {
|
|
hrStatus = SPAPI_E_CLASS_MISMATCH;
|
|
goto final;
|
|
}
|
|
//
|
|
// determine what component the INF says
|
|
// ComponentId must exist for exception packs
|
|
//
|
|
if(!SetupFindFirstLine(hInf,INFSTR_SECT_VERSION,KEY_COMPONENTID,&InfLine)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
if(!SetupGetStringField(&InfLine,1,Buffer,MAX_PATH,NULL)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
hrStatus = GuidFromString(Buffer,&InfGuid);
|
|
if(SUCCEEDED(hrStatus)) {
|
|
hrStatus = S_OK;
|
|
} else {
|
|
goto final;
|
|
}
|
|
if(CompGuid && !IsEqualGUID(CompGuid,&InfGuid)) {
|
|
//
|
|
// mismatched
|
|
//
|
|
hrStatus = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
//
|
|
// determine version - optional, just for msoobci
|
|
// but if not specified in INF in DriverVer = <date>,<version>
|
|
// must be passed in
|
|
//
|
|
if(SetupFindFirstLine(hInf,INFSTR_SECT_VERSION,INFSTR_DRIVERVERSION_SECTION,&InfLine)) {
|
|
if(!SetupGetStringField(&InfLine,2,Buffer,MAX_PATH,NULL)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
hrStatus = VersionFromString(Buffer,&InfVerMajor,&InfVerMinor,&InfVerBuild,&InfVerQFE);
|
|
if(hrStatus == S_FALSE) {
|
|
hrStatus = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
if(SUCCEEDED(hrStatus)) {
|
|
hrStatus = S_OK;
|
|
} else {
|
|
goto final;
|
|
}
|
|
if(VerMajor>=0) {
|
|
if(VerMajor != InfVerMajor) {
|
|
hrStatus = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
if(VerMinor>=0) {
|
|
if(VerMinor != InfVerMinor) {
|
|
hrStatus = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
if(VerBuild>=0) {
|
|
if(VerBuild != InfVerBuild) {
|
|
hrStatus = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
if(VerQFE>=0) {
|
|
if(VerQFE != InfVerQFE) {
|
|
hrStatus = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
}
|
|
} else if(VerQFE != -1) {
|
|
//
|
|
// VerQFE must be -1
|
|
//
|
|
hrStatus = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
} else if((VerBuild != -1) || (VerQFE != -1)) {
|
|
//
|
|
// VerBuild & VerQFE must be -1
|
|
//
|
|
hrStatus = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
} else if((VerMinor != -1) || (VerBuild != -1) || (VerQFE != -1)) {
|
|
//
|
|
// VerMinor, VerBuild & VerQFE must be -1
|
|
//
|
|
hrStatus = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
} else {
|
|
//
|
|
// must be specified
|
|
//
|
|
if((VerMajor<0) || (VerMinor<0) || (VerBuild<0) || (VerQFE<0)) {
|
|
hrStatus = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
InfVerMajor = VerMajor;
|
|
InfVerMinor = VerMinor;
|
|
InfVerBuild = VerBuild;
|
|
InfVerQFE = VerQFE;
|
|
}
|
|
//
|
|
// determine friendly name
|
|
// use Class= entry in INF (must always be specified)
|
|
// if Name not defined, use class name instead
|
|
//
|
|
if(!SetupFindFirstLine(hInf,INFSTR_SECT_VERSION,INFSTR_KEY_CLASS,&InfLine)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
if(!Name) {
|
|
if(!SetupGetStringField(&InfLine,1,FriendlyName,ARRAY_SIZE(FriendlyName),NULL)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
Name = FriendlyName;
|
|
}
|
|
|
|
//
|
|
// we might not need to update this package after all
|
|
//
|
|
ZeroMemory(&OsComponentData,sizeof(OsComponentData));
|
|
OsComponentData.SizeOfStruct = sizeof(OsComponentData);
|
|
ZeroMemory(&OsExceptionData,sizeof(OsExceptionData));
|
|
OsExceptionData.SizeOfStruct = sizeof(OsExceptionData);
|
|
if(QueryRegisteredOsComponent(&InfGuid,&OsComponentData,&OsExceptionData)) {
|
|
//
|
|
// already registered? see if we supercede
|
|
//
|
|
if(((Flags & COMP_FLAGS_FORCE)==0) && (CompareCompVersion(InfVerMajor,InfVerMinor,InfVerBuild,InfVerQFE,&OsComponentData)<=0)) {
|
|
VerbosePrint(TEXT("Not installing %s, %u.%u.%u.%u <= %u.%u.%u.%u"),
|
|
InfPath,
|
|
InfVerMajor,InfVerMinor,InfVerBuild,InfVerQFE,
|
|
OsComponentData.VersionMajor,
|
|
OsComponentData.VersionMinor,
|
|
OsComponentData.BuildNumber,
|
|
OsComponentData.QFENumber);
|
|
hrStatus = S_FALSE;
|
|
goto final;
|
|
}
|
|
PrevReg = TRUE;
|
|
}
|
|
|
|
//
|
|
// determine MediaRoot and INF basename
|
|
//
|
|
DwRes= GetFullPathName(InfPath,MAX_PATH,MediaRoot,&BaseName);
|
|
if(DwRes == 0) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
} else if(DwRes >= MAX_PATH) {
|
|
Status = ERROR_INSUFFICIENT_BUFFER;
|
|
goto final;
|
|
}
|
|
if((BaseName == NULL) || (BaseName == InfPath) || !BaseName[0]) {
|
|
hrStatus = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
if(BaseName[-1] != TEXT('\\')) {
|
|
hrStatus = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
//
|
|
// split off MediaRoot and BaseName
|
|
//
|
|
BaseName[-1] = TEXT('\0');
|
|
//
|
|
// get Windows directory
|
|
//
|
|
UiRes = GetRealWindowsDirectory(Buffer,MAX_PATH);
|
|
if(UiRes == 0) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
} else if(UiRes >= MAX_PATH) {
|
|
Status = ERROR_INSUFFICIENT_BUFFER;
|
|
goto final;
|
|
}
|
|
if(!SUCCEEDED(ConcatPath(Buffer,MAX_PATH,TEXT("\\")))) {
|
|
Status = ERROR_INSUFFICIENT_BUFFER;
|
|
goto final;
|
|
}
|
|
SubDir = Buffer+lstrlen(Buffer);
|
|
//
|
|
// c:\windows\
|
|
// ^-buffer ^-subdir
|
|
// we'll do a number of operations using the subdir part of this buffer
|
|
//
|
|
// if PrevReg is TRUE, we most likely have access to previous package
|
|
// so that we can install prev package to revert back to it
|
|
// we expect this package to be in the windows directory as per spec
|
|
// if it's not, then the old package might not still be around
|
|
//
|
|
if(PrevReg && _tcsncmp(OsExceptionData.ExceptionInfName,Buffer,SubDir-Buffer)==0) {
|
|
//
|
|
// it's a sub-directory of %windir%
|
|
// now check for presence of INF and CAT files
|
|
//
|
|
DwRes = GetFileAttributes(OsExceptionData.ExceptionInfName);
|
|
if(DwRes != INVALID_FILE_ATTRIBUTES) {
|
|
DwRes = GetFileAttributes(OsExceptionData.CatalogFileName);
|
|
if(DwRes != INVALID_FILE_ATTRIBUTES) {
|
|
//
|
|
// both present, looks good
|
|
//
|
|
CanRevert = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// determine final path/name of INF and catalog
|
|
// We must place it directly in %windir%\<comp>
|
|
// (WFP relies on this!!!!)
|
|
// we'll backup what's there so that we can restore it later if needed
|
|
//
|
|
hrStatus = StringFromGuid(&InfGuid,GuidString,ARRAY_SIZE(GuidString));
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
goto final;
|
|
}
|
|
hrStatus = S_OK;
|
|
_sntprintf(SubDir,MAX_PATH,TEXT("%s\\%s"),
|
|
TEXT("RegisteredPackages"),
|
|
GuidString
|
|
);
|
|
if((lstrlen(Buffer)+16)>MAX_PATH) {
|
|
Status = ERROR_INSUFFICIENT_BUFFER;
|
|
goto final;
|
|
}
|
|
lstrcpy(NewStore,Buffer);
|
|
|
|
if(CanRevert) {
|
|
hrStatus = BackupStore(NewStore,OldStore,ARRAY_SIZE(OldStore));
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
//
|
|
// if we failed backup, that means there's something bad
|
|
// such as files in the store in use
|
|
// probability is that we'll fail later
|
|
// so fail gracefully now instead of badly later
|
|
//
|
|
goto final;
|
|
}
|
|
hrStatus = S_OK;
|
|
}
|
|
|
|
//
|
|
// see if %windir%\INF\<BaseName> is there?
|
|
//
|
|
lstrcpy(SubDir,TEXT("INF\\"));
|
|
if(!SUCCEEDED(ConcatPath(Buffer,MAX_PATH,BaseName))) {
|
|
Status = ERROR_INSUFFICIENT_BUFFER;
|
|
goto final;
|
|
}
|
|
DwRes = GetFileAttributes(Buffer);
|
|
if(DwRes != INVALID_FILE_ATTRIBUTES) {
|
|
//
|
|
// replacing an existing INF
|
|
// to work around a cache bug, we'll kick the actuall install
|
|
// off in another process
|
|
//
|
|
NeedProxy = TRUE;
|
|
}
|
|
|
|
hrStatus = MakeSurePathExists(NewStore);
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
goto final;
|
|
}
|
|
|
|
//
|
|
// install INF into %windir%\INF directory noting location the files
|
|
// should be
|
|
//
|
|
if((g_VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) && (g_VerInfo.dwMajorVersion >= 5)) {
|
|
//
|
|
// only do this on Win2k+
|
|
// this will have SetupAPI tell us the original name and the catalog
|
|
// name
|
|
//
|
|
if(CopyOEMInf(InfPath,NewStore,SPOST_PATH,0,NULL,0,NULL,NULL)) {
|
|
//
|
|
// Switch to the INF that's in %windir%\INF directory
|
|
//
|
|
SetupCloseInfFile(hInf);
|
|
hInf = SetupOpenInfFile(Buffer,NULL,INF_STYLE_WIN4,NULL);
|
|
if(hInf == INVALID_HANDLE_VALUE) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
} else {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
}
|
|
//
|
|
// now find out what the catalog name would be
|
|
//
|
|
hrStatus = GetInfOriginalFileInformation(hInf,&InfOriginalFileInformation);
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
goto final;
|
|
}
|
|
if((InfOriginalFileInformation.OriginalInfName[0]==TEXT('\0'))
|
|
||(InfOriginalFileInformation.OriginalCatalogName[0]==TEXT('\0'))) {
|
|
//
|
|
// shouldn't happen
|
|
//
|
|
hrStatus = E_FAIL;
|
|
goto final;
|
|
}
|
|
|
|
ZeroMemory(&NewOsExceptionData,sizeof(NewOsExceptionData));
|
|
NewOsExceptionData.SizeOfStruct = sizeof(NewOsExceptionData);
|
|
//
|
|
// INF name
|
|
//
|
|
BaseName = GetBaseName(InfOriginalFileInformation.OriginalInfName);
|
|
lstrcpyn(NewOsExceptionData.ExceptionInfName,NewStore,ARRAY_SIZE(NewOsExceptionData.ExceptionInfName));
|
|
if(!SUCCEEDED(ConcatPath(NewOsExceptionData.ExceptionInfName,ARRAY_SIZE(NewOsExceptionData.ExceptionInfName),BaseName))) {
|
|
Status = ERROR_INSUFFICIENT_BUFFER;
|
|
goto final;
|
|
}
|
|
lstrcpy(Buffer,MediaRoot);
|
|
if(!SUCCEEDED(ConcatPath(Buffer,MAX_PATH,BaseName))) {
|
|
Status = ERROR_INSUFFICIENT_BUFFER;
|
|
goto final;
|
|
}
|
|
if(!CopyFile(Buffer,NewOsExceptionData.ExceptionInfName,FALSE)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
//
|
|
// CAT name
|
|
//
|
|
BaseName = GetBaseName(InfOriginalFileInformation.OriginalCatalogName);
|
|
lstrcpyn(NewOsExceptionData.CatalogFileName,NewStore,ARRAY_SIZE(NewOsExceptionData.CatalogFileName));
|
|
if(!SUCCEEDED(ConcatPath(NewOsExceptionData.CatalogFileName,ARRAY_SIZE(NewOsExceptionData.CatalogFileName),BaseName))) {
|
|
Status = ERROR_INSUFFICIENT_BUFFER;
|
|
goto final;
|
|
}
|
|
lstrcpy(Buffer,MediaRoot);
|
|
if(!SUCCEEDED(ConcatPath(Buffer,MAX_PATH,BaseName))) {
|
|
Status = ERROR_INSUFFICIENT_BUFFER;
|
|
goto final;
|
|
}
|
|
if(!CopyFile(Buffer,NewOsExceptionData.CatalogFileName,FALSE)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
|
|
//
|
|
// WFP may query exception pack as a source to restore files that are replaced
|
|
// change registration so if WFP does get in loop, it goes to the right place
|
|
//
|
|
if(PrevReg) {
|
|
UnRegisterOsComponent(&InfGuid);
|
|
}
|
|
ZeroMemory(&NewOsComponentData,sizeof(NewOsComponentData));
|
|
NewOsComponentData.SizeOfStruct = sizeof(NewOsComponentData);
|
|
NewOsComponentData.ComponentGuid = InfGuid;
|
|
lstrcpyn(NewOsComponentData.FriendlyName,Name,ARRAY_SIZE(NewOsComponentData.FriendlyName));
|
|
NewOsComponentData.VersionMajor = (WORD)InfVerMajor;
|
|
NewOsComponentData.VersionMinor = (WORD)InfVerMinor;
|
|
NewOsComponentData.BuildNumber = (WORD)InfVerBuild;
|
|
NewOsComponentData.QFENumber = (WORD)InfVerQFE;
|
|
if(!RegisterOsComponent(&NewOsComponentData,&NewOsExceptionData)) {
|
|
Status = GetLastError();
|
|
goto final;
|
|
}
|
|
if(((Flags & COMP_FLAGS_NOUI)==0) && !IsInteractiveWindowStation()) {
|
|
Flags |= COMP_FLAGS_NOUI;
|
|
}
|
|
|
|
if(NeedProxy) {
|
|
//
|
|
// A bug in Win2k/XP means that we have problems if replacing an existing
|
|
// exception-pack component
|
|
//
|
|
hrStatus = ProxyInstallExceptionPackFromInf(InfPath,MediaRoot,NewStore,Flags);
|
|
} else {
|
|
hrStatus = InstallExceptionPackFromInf(InfPath,MediaRoot,NewStore,Flags);
|
|
}
|
|
if(!SUCCEEDED(hrStatus)) {
|
|
//
|
|
// not sure best thing to do here, but
|
|
// the component that we had above is definately invalid
|
|
//
|
|
UnRegisterOsComponent(&InfGuid);
|
|
if(PrevReg) {
|
|
RegisterOsComponent(&OsComponentData,&OsExceptionData);
|
|
}
|
|
if(BackedUp) {
|
|
//
|
|
// we got part through and failed. Re-install the old component
|
|
// to revert whatever we did
|
|
//
|
|
RevertStore(OldStore,NewStore);
|
|
BackedUp = FALSE;
|
|
InstallInfSection(OsExceptionData.ExceptionInfName,NULL,COMP_FLAGS_NOUI);
|
|
}
|
|
goto final;
|
|
} else {
|
|
//
|
|
// don't need backup any more
|
|
//
|
|
if(BackedUp) {
|
|
DeleteDirectoryRecursive(OldStore);
|
|
BackedUp = FALSE;
|
|
}
|
|
}
|
|
//
|
|
// succeeded
|
|
//
|
|
Status = NO_ERROR;
|
|
if(hrStatus == INST_S_REBOOT) {
|
|
hrStatus = HandleReboot(Flags);
|
|
} else {
|
|
hrStatus = S_OK;
|
|
}
|
|
|
|
|
|
final:
|
|
if(hInf != INVALID_HANDLE_VALUE) {
|
|
SetupCloseInfFile(hInf);
|
|
}
|
|
if((hrStatus == S_OK) && Status != NO_ERROR) {
|
|
hrStatus = HRESULT_FROM_SETUPAPI(Status);
|
|
}
|
|
if(BackedUp) {
|
|
//
|
|
// we need to revert the backup
|
|
//
|
|
RevertStore(OldStore,NewStore);
|
|
}
|
|
return hrStatus;
|
|
}
|
|
|
|
HRESULT
|
|
WINAPI
|
|
InstallComponentA(
|
|
IN LPCSTR InfPath,
|
|
IN DWORD Flags,
|
|
IN const GUID * CompGuid, OPTIONAL
|
|
IN INT VerMajor, OPTIONAL
|
|
IN INT VerMinor, OPTIONAL
|
|
IN INT VerBuild, OPTIONAL
|
|
IN INT VerQFE, OPTIONAL
|
|
IN LPCSTR Name OPTIONAL
|
|
)
|
|
{
|
|
TCHAR OutPath[MAX_PATH];
|
|
TCHAR OutDesc[DESC_SIZE]; // as per friendly name
|
|
INT sz;
|
|
if(InfPath) {
|
|
sz = MultiByteToWideChar(CP_ACP,0,InfPath,-1,OutPath,ARRAY_SIZE(OutPath));
|
|
if(!sz) {
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
if(Name) {
|
|
sz = MultiByteToWideChar(CP_ACP,0,Name,-1,OutDesc,ARRAY_SIZE(OutDesc));
|
|
if(!sz) {
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
return InstallComponent(InfPath ? OutPath : NULL,
|
|
Flags,
|
|
CompGuid,
|
|
VerMajor,
|
|
VerMinor,
|
|
VerBuild,
|
|
VerQFE,
|
|
Name ? OutDesc : NULL);
|
|
}
|
|
|
|
VOID
|
|
WINAPI
|
|
DoInstallW(
|
|
IN HWND Window,
|
|
IN HINSTANCE ModuleHandle,
|
|
IN PCTSTR CommandLine,
|
|
IN INT ShowCommand
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
exported for call by rundll32
|
|
|
|
Arguments:
|
|
|
|
Window - parent window (not used)
|
|
ModuleHandle - not used
|
|
CommandLine - see below
|
|
ShowCommand - not used
|
|
|
|
CommandLine -
|
|
"InfPath;Flags;GUID;High.Low.Build.QFE;Name" (; - CMD_SEP)
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
TCHAR InfPath[MAX_PATH];
|
|
TCHAR Desc[DESC_SIZE];
|
|
TCHAR Hold[64];
|
|
INT VerMajor = -1;
|
|
INT VerMinor = -1;
|
|
INT VerBuild = -1;
|
|
INT VerQFE = -1;
|
|
GUID Guid;
|
|
DWORD Flags = 0;
|
|
LPGUID pGuid = NULL;
|
|
LPTSTR pDesc = NULL;
|
|
LPCTSTR pCmd = CommandLine;
|
|
LPCTSTR pEnd;
|
|
HRESULT hResult = S_OK;
|
|
|
|
//
|
|
// break CommandLine up into relevent parts
|
|
// First InfPath
|
|
//
|
|
pEnd = _tcschr(pCmd,CMD_SEP);
|
|
if(!pEnd) {
|
|
pEnd = pCmd+lstrlen(pCmd);
|
|
}
|
|
if((pEnd == pCmd) || ((pEnd-pCmd)>=MAX_PATH)) {
|
|
hResult = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
CopyMemory(InfPath,pCmd,(pEnd-pCmd)*sizeof(TCHAR));
|
|
InfPath[pEnd-pCmd] = TEXT('\0');
|
|
if(*pEnd == CMD_SEP) {
|
|
pCmd = pEnd+1;
|
|
if((*pCmd == CMD_SEP) || (*pCmd == TEXT('\0'))) {
|
|
//
|
|
// skip
|
|
//
|
|
pEnd = pCmd;
|
|
} else {
|
|
//
|
|
// Flags
|
|
//
|
|
Flags = (DWORD)_tcstoul(pCmd,&(LPTSTR)pEnd,0);
|
|
if((*pEnd != CMD_SEP) && (*pEnd != TEXT('\0'))) {
|
|
hResult = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
}
|
|
}
|
|
if(*pEnd == CMD_SEP) {
|
|
pCmd = pEnd+1;
|
|
if((*pCmd == CMD_SEP) || (*pCmd == TEXT('\0'))) {
|
|
//
|
|
// skip
|
|
//
|
|
pEnd = pCmd;
|
|
} else {
|
|
//
|
|
// Guid
|
|
//
|
|
pEnd = _tcschr(pCmd,CMD_SEP);
|
|
if(!pEnd) {
|
|
pEnd = pCmd+lstrlen(pCmd);
|
|
}
|
|
if((pEnd-pCmd)>=ARRAY_SIZE(Hold)) {
|
|
hResult = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
CopyMemory(Hold,pCmd,(pEnd-pCmd)*sizeof(TCHAR));
|
|
Hold[pEnd-pCmd] = TEXT('\0');
|
|
hResult = GuidFromString(Hold,&Guid);
|
|
if(!SUCCEEDED(hResult)) {
|
|
goto final;
|
|
}
|
|
pGuid = &Guid;
|
|
}
|
|
}
|
|
if(*pEnd == CMD_SEP) {
|
|
pCmd = pEnd+1;
|
|
if((*pCmd == CMD_SEP) || (*pCmd == TEXT('\0'))) {
|
|
//
|
|
// skip
|
|
//
|
|
pEnd = pCmd;
|
|
} else {
|
|
//
|
|
// Version
|
|
//
|
|
pEnd = _tcschr(pCmd,CMD_SEP);
|
|
if(!pEnd) {
|
|
pEnd = pCmd+lstrlen(pCmd);
|
|
}
|
|
if((pEnd-pCmd)>=ARRAY_SIZE(Hold)) {
|
|
hResult = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
CopyMemory(Hold,pCmd,(pEnd-pCmd)*sizeof(TCHAR));
|
|
Hold[pEnd-pCmd] = TEXT('\0');
|
|
hResult = VersionFromString(Hold,&VerMajor,&VerMinor,&VerBuild,&VerQFE);
|
|
if(!SUCCEEDED(hResult)) {
|
|
goto final;
|
|
}
|
|
if(hResult == S_FALSE) {
|
|
VerMajor = VerMinor = VerBuild = VerQFE = -1;
|
|
}
|
|
}
|
|
}
|
|
if(*pEnd == CMD_SEP) {
|
|
pCmd = pEnd+1;
|
|
pEnd = pCmd+lstrlen(pCmd);
|
|
if(pEnd != pCmd) {
|
|
if((pEnd-pCmd) >= ARRAY_SIZE(Desc)) {
|
|
hResult = E_INVALIDARG;
|
|
goto final;
|
|
}
|
|
CopyMemory(Desc,pCmd,(pEnd-pCmd)*sizeof(TCHAR));
|
|
Desc[pEnd-pCmd] = TEXT('\0');
|
|
pDesc = Desc;
|
|
}
|
|
}
|
|
hResult = InstallComponent(InfPath,Flags,pGuid,VerMajor,VerMinor,VerBuild,VerQFE,pDesc);
|
|
|
|
final:
|
|
if(SUCCEEDED(hResult)) {
|
|
//
|
|
// deal with specific success scenarios
|
|
//
|
|
} else {
|
|
//
|
|
// an error occurred
|
|
//
|
|
DebugPrint(TEXT("DoInstall failed with error: 0x%08x"),hResult);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
WINAPI
|
|
DoInstallA(
|
|
IN HWND Window,
|
|
IN HINSTANCE ModuleHandle,
|
|
IN PCSTR CommandLine,
|
|
IN INT ShowCommand
|
|
)
|
|
{
|
|
TCHAR OutLine[MAX_PATH*2];
|
|
INT sz;
|
|
sz = MultiByteToWideChar(CP_ACP,0,CommandLine,-1,OutLine,ARRAY_SIZE(OutLine));
|
|
if(!sz) {
|
|
DebugPrint(TEXT("DoInstallA was passed too big a command line"));
|
|
return;
|
|
}
|
|
DoInstall(Window,ModuleHandle,OutLine,ShowCommand);
|
|
}
|
|
|
|
|