/*++ 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 _INST_POSTPROCESSING_INFO { DWORD Flags; } INST_POSTPROCESSING_INFO; DWORD CALLBACK DriverInstallComponents ( IN DI_FUNCTION InstallFunction, IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN OUT PCOINSTALLER_CONTEXT_DATA Context ) /*++ Routine Description: co-installer callback catch the moment of call to DIF_INSTALLDEVICE Consider installing exception packs at this point If we succeed, we may need to restart device install Arguments: InstallFunction - DIF_INSTALLDEVICE DeviceInfoSet/DeviceInfoData - describes device Return Value: status, normally NO_ERROR --*/ { DWORD Status = NO_ERROR; if((g_VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) && (g_VerInfo.dwMajorVersion >= 5)) { // // we should only be executing co-installers on Win2k+ // but this is an added sanity check // switch (InstallFunction) { case DIF_INSTALLDEVICE: VerbosePrint(TEXT("handling DIF_INSTALLDEVICE")); if(Context->PostProcessing) { Status = DoDriverInstallComponentsPostProcessing(DeviceInfoSet,DeviceInfoData,Context); } else { Status = DoDriverInstallComponents(DeviceInfoSet,DeviceInfoData,Context); } VerbosePrint(TEXT("finished DIF_INSTALLDEVICE with status=0x%08x"),Status); break; default: break; } } return Status; } DWORD DoDriverInstallComponents ( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN OUT PCOINSTALLER_CONTEXT_DATA Context ) /*++ Routine Description: co-installer callback enumerate all the components sections Arguments: DeviceInfoSet/DeviceInfoData - describes device Context - callback context Return Value: status, normally NO_ERROR --*/ { SP_DRVINFO_DATA DriverInfoData; SP_DRVINFO_DETAIL_DATA DriverInfoDetailData; HINF InfFile = INVALID_HANDLE_VALUE; DWORD AndFlags = (DWORD)(-1); DWORD OrFlags = (DWORD)0; INFCONTEXT CompLine; TCHAR InstallSectionName[LINE_LEN]; TCHAR CompSectionName[LINE_LEN]; DWORD FieldIndex; DWORD FieldCount; DWORD Status; DWORD FinalStatus = NO_ERROR; INST_POSTPROCESSING_INFO PostProcess; ZeroMemory(&PostProcess,sizeof(PostProcess)); // // determine selected driver // and INF // DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA); if (!SetupDiGetSelectedDriver( DeviceInfoSet, DeviceInfoData, &DriverInfoData)) { Status = GetLastError(); DebugPrint(TEXT("Fail: SetupDiGetSelectedDriver, Error: 0x%08x"),Status); goto clean; } DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA); if (!SetupDiGetDriverInfoDetail(DeviceInfoSet, DeviceInfoData, &DriverInfoData, &DriverInfoDetailData, sizeof(SP_DRVINFO_DETAIL_DATA), NULL)) { Status = GetLastError(); if (Status == ERROR_INSUFFICIENT_BUFFER) { // // We don't need the extended information. Ignore. // } else { DebugPrint(TEXT("Fail: SetupDiGetDriverInfoDetail, 0xError: %08x"),Status); goto clean; } } InfFile = SetupOpenInfFile(DriverInfoDetailData.InfFileName, NULL, INF_STYLE_WIN4, NULL); if (InfFile == INVALID_HANDLE_VALUE) { Status = GetLastError(); DebugPrint(TEXT("Fail: SetupOpenInfFile")); goto clean; } if(!SetupDiGetActualSectionToInstall(InfFile, DriverInfoDetailData.SectionName, InstallSectionName, LINE_LEN, NULL, NULL)) { Status = GetLastError(); DebugPrint(TEXT("Fail: SetupDiGetActualSectionToInstall, Error: 0x%08x"),Status); goto clean; } // // look for one or more Components= entries in INF section // if (SetupFindFirstLine(InfFile, InstallSectionName, KEY_COMPONENTS, &CompLine)) { VerbosePrint(TEXT("Components keyword found in %s"),DriverInfoDetailData.InfFileName); do { // // Components = section,section,... // first section @ index 1. // FieldCount = SetupGetFieldCount(&CompLine); for(FieldIndex = 1;FieldIndex<=FieldCount;FieldIndex++) { if(SetupGetStringField(&CompLine, FieldIndex, CompSectionName, LINE_LEN, NULL)) { // // we have a listed section // Status = DoDriverComponentsSection(InfFile, CompSectionName, &AndFlags, &OrFlags); if(Status != NO_ERROR) { FinalStatus = Status; goto clean; } } else { Status = GetLastError(); DebugPrint(TEXT("Fail: SetupGetStringField, Error: 0x%08x"),Status); // // non-fatal // } } } while (SetupFindNextMatchLine(&CompLine, KEY_COMPONENTS, &CompLine)); // // handle AndFlags/OrFlags here // if(OrFlags & (FLAGS_REBOOT|FLAGS_REINSTALL)) { // // reboot is required // HMACHINE hMachine = NULL; SP_DEVINFO_LIST_DETAIL_DATA DevInfoListDetail; SP_DEVINSTALL_PARAMS DeviceInstallParams; DeviceInstallParams.cbSize = sizeof(DeviceInstallParams); if(SetupDiGetDeviceInstallParams(DeviceInfoSet, DeviceInfoData, &DeviceInstallParams)) { // // set reboot flags // DeviceInstallParams.Flags |= DI_NEEDRESTART|DI_NEEDREBOOT; SetupDiSetDeviceInstallParams(DeviceInfoSet, DeviceInfoData, &DeviceInstallParams); } DevInfoListDetail.cbSize = sizeof(DevInfoListDetail); if(GetDeviceInfoListDetail(DeviceInfoSet,&DevInfoListDetail)) { hMachine = DevInfoListDetail.RemoteMachineHandle; } Set_DevNode_Problem_Ex(DeviceInfoData->DevInst, CM_PROB_NEED_RESTART, CM_SET_DEVINST_PROBLEM_OVERRIDE, hMachine); } if(OrFlags & FLAGS_REINSTALL) { // // we'll need to mark the device as needing reinstall when we go through post-processing // FinalStatus = ERROR_DI_POSTPROCESSING_REQUIRED; PostProcess.Flags |= POSTFLAGS_REINSTALL; } } clean: if (InfFile != INVALID_HANDLE_VALUE) { SetupCloseInfFile(InfFile); } if(FinalStatus == ERROR_DI_POSTPROCESSING_REQUIRED) { // // data to use during post-processing // INST_POSTPROCESSING_INFO *pPostProcess = malloc(sizeof(INST_POSTPROCESSING_INFO)); if(!pPostProcess) { return ERROR_OUTOFMEMORY; } *pPostProcess = PostProcess; Context->PrivateData = pPostProcess; } return FinalStatus; } DWORD DoDriverInstallComponentsPostProcessing ( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN OUT PCOINSTALLER_CONTEXT_DATA Context ) /*++ Routine Description: co-installer callback enumerate all the components sections Arguments: DeviceInfoSet/DeviceInfoData - describes device Context - callback context Return Value: status, normally NO_ERROR --*/ { INST_POSTPROCESSING_INFO PostProcess; SP_DEVINFO_LIST_DETAIL_DATA DevInfoListDetail; HMACHINE hMachine = NULL; if(!Context->PrivateData) { return Context->InstallResult; } PostProcess = *(INST_POSTPROCESSING_INFO*)(Context->PrivateData); free(Context->PrivateData); Context->PrivateData = NULL; DevInfoListDetail.cbSize = sizeof(DevInfoListDetail); if(GetDeviceInfoListDetail(DeviceInfoSet,&DevInfoListDetail)) { hMachine = DevInfoListDetail.RemoteMachineHandle; } if(PostProcess.Flags & POSTFLAGS_REINSTALL) { Set_DevNode_Problem_Ex(DeviceInfoData->DevInst, CM_PROB_REINSTALL, CM_SET_DEVINST_PROBLEM_OVERRIDE, hMachine); } return Context->InstallResult; } DWORD DoDriverComponentsSection( IN HINF InfFile, IN LPCTSTR CompSectionName, IN OUT DWORD *AndFlags, IN OUT DWORD *OrFlags ) /*++ Routine Description: enumerate all the component entries in component section component entry consists of filename,flags,identity,version filename is absolute directory, eg, %1%\foo.inf flags - bit 16 set indicating Exception Pack bit 0 set indicating device install needs restarting identity - component GUID version - component version Arguments: InfFile - handle to INF file CompSectionName - handle to components section AndFlags/OrFlags - accumulated flags Return Value: status, normally NO_ERROR --*/ { INFCONTEXT EntryLine; TCHAR Path[MAX_PATH]; DWORD Flags; INT FieldVal; DWORD SubFlags; DWORD Status; if (!SetupFindFirstLine(InfFile, CompSectionName, NULL, &EntryLine)) { // // section was empty // VerbosePrint(TEXT("Section [%s] is empty"),CompSectionName); return NO_ERROR; } VerbosePrint(TEXT("Processing components section [%s]"),CompSectionName); do { if(!SetupGetStringField(&EntryLine,COMPFIELD_NAME,Path,MAX_PATH,NULL)) { Status = GetLastError(); DebugPrint(TEXT("- Fail: SetupGetStringField(1), Error: 0x%08x"),Status); return NO_ERROR; } VerbosePrint(TEXT("Processing component %s"),Path); if(SetupGetIntField(&EntryLine,COMPFIELD_FLAGS,&FieldVal)) { Flags = (DWORD)FieldVal; } else { Status = GetLastError(); DebugPrint(TEXT("- Fail: SetupGetIntField(2), Error: 0x%08x"),Status); return NO_ERROR; } SubFlags = Flags & ~ FLAGS_METHOD; switch(Flags & FLAGS_METHOD) { case FLAGS_EXPACK: Status = DoDriverExPack(&EntryLine,Path,&SubFlags); break; case FLAGS_QFE: Status = DoDriverQfe(&EntryLine,Path,&SubFlags); break; default: DebugPrint(TEXT("- Fail: Invalid component type")); Status = ERROR_INVALID_DATA; break; } if(Status != NO_ERROR) { return Status; } if(SubFlags & FLAGS_INSTALLED) { *AndFlags &= SubFlags; *OrFlags |= SubFlags; } } while (SetupFindNextLine(&EntryLine,&EntryLine)); return NO_ERROR; } DWORD DoDriverExPack( IN INFCONTEXT *EntryLine, IN LPCTSTR PathName, IN OUT DWORD *Flags ) /*++ Routine Description: queries and potentially installs exception-pack component Arguments: EntryLine - context for remaining information PathName - name of exception pack INF (param 1) SubFlags - flags passed in (param 2) sans type of install component entry consists of filename,flags,identity,version filename is absolute directory, eg, %1%\foo.inf identity - component GUID version - component version Return Value: status, normally NO_ERROR if return value >= 0x80000000 then it's a HRESULT error --*/ { TCHAR CompIdentity[64]; // expecting a GUID TCHAR CompVersion[64]; // major.minor TCHAR CompDesc[DESC_SIZE]; // description GUID ComponentGuid; INT VerMajor = -1; INT VerMinor = -1; INT VerBuild = -1; INT VerQFE = -1; DWORD Status; DWORD dwLen; HRESULT hrStatus; SETUP_OS_COMPONENT_DATA OsComponentData; SETUP_OS_EXCEPTION_DATA OsExceptionData; UINT uiRes; TCHAR SrcPath[MAX_PATH]; TCHAR NewSrcPath[MAX_PATH]; TCHAR CompOsVerRange[128]; LPTSTR ToVerPart; LPTSTR SrcName; BOOL PreInst = FALSE; VerbosePrint(TEXT("- %s is an exception pack"),PathName); // // now read in identity and version // we can then check to see if an apropriate version installed // if(!SetupGetStringField(EntryLine,COMPFIELD_COMP,CompIdentity,ARRAY_SIZE(CompIdentity),NULL)) { Status = GetLastError(); DebugPrint(TEXT("- Fail: SetupGetStringField(3), Error: 0x%08x"),Status); return Status; } if(!SetupGetStringField(EntryLine,COMPFIELD_VER,CompVersion,ARRAY_SIZE(CompVersion),NULL)) { Status = GetLastError(); DebugPrint(TEXT("- Fail: SetupGetStringField(4), Error: 0x%08x"),Status); return Status; } if(!SetupGetStringField(EntryLine,COMPFIELD_DESC,CompDesc,ARRAY_SIZE(CompDesc),NULL)) { CompDesc[0] = TEXT('\0'); } if(SetupGetStringField(EntryLine,COMPFIELD_OSVER,CompOsVerRange,ARRAY_SIZE(CompOsVerRange),NULL)) { // // need to verify OS version range, do that now // int maj_f,min_f,build_f,qfe_f; int maj_t,min_t,build_t,qfe_t; ToVerPart = _tcschr(CompOsVerRange,TEXT('-')); if(ToVerPart) { *ToVerPart = TEXT('\0'); ToVerPart++; } hrStatus = VersionFromString(CompOsVerRange,&maj_f,&min_f,&build_f,&qfe_f); if(!SUCCEEDED(hrStatus)) { return (DWORD)hrStatus; } if((hrStatus == S_FALSE) || (qfe_f != 0)) { return ERROR_INVALID_PARAMETER; } if(ToVerPart) { hrStatus = VersionFromString(ToVerPart,&maj_t,&min_t,&build_t,&qfe_t); if(!SUCCEEDED(hrStatus)) { return (DWORD)hrStatus; } if((hrStatus == S_FALSE) || (qfe_t != 0)) { return ERROR_INVALID_PARAMETER; } if(CompareVersion(maj_f, min_f, build_f>0 ? build_f : -1, 0, g_VerInfo.dwMajorVersion, g_VerInfo.dwMinorVersion, g_VerInfo.dwBuildNumber, 0) > 0) { VerbosePrint(TEXT("- Skipped (OS < %u.%u.%u)"), maj_f,min_f,build_f); return NO_ERROR; } else if(CompareVersion(maj_t, min_t, build_t>0 ? build_t : -1, 0, g_VerInfo.dwMajorVersion, g_VerInfo.dwMinorVersion, g_VerInfo.dwBuildNumber, 0) < 0) { VerbosePrint(TEXT("- Skipped (OS > %u.%u.%u)"), maj_t,min_t,build_t); return NO_ERROR; } } else { if(CompareVersion(maj_f, min_f, build_f, 0, g_VerInfo.dwMajorVersion, g_VerInfo.dwMajorVersion, g_VerInfo.dwMajorVersion, 0) != 0) { VerbosePrint(TEXT("- Skipped (OS != %u.%u.%u)"), maj_f,min_f,build_f); return NO_ERROR; } } } // // fold CompIdentity into a GUID // hrStatus = GuidFromString(CompIdentity,&ComponentGuid); if(!SUCCEEDED(hrStatus)) { return (DWORD)hrStatus; } // // and version // hrStatus = VersionFromString(CompVersion,&VerMajor,&VerMinor,&VerBuild,&VerQFE); if(hrStatus == S_FALSE) { return ERROR_INVALID_PARAMETER; } if(!SUCCEEDED(hrStatus)) { return (DWORD)hrStatus; } // // now do a component check // ZeroMemory(&OsComponentData,sizeof(OsComponentData)); OsComponentData.SizeOfStruct = sizeof(OsComponentData); ZeroMemory(&OsExceptionData,sizeof(OsExceptionData)); OsExceptionData.SizeOfStruct = sizeof(OsExceptionData); if(QueryRegisteredOsComponent(&ComponentGuid,&OsComponentData,&OsExceptionData)) { // // maybe already registered? // if(CompareCompVersion(VerMajor,VerMinor,VerBuild,VerQFE,&OsComponentData)<=0) { VerbosePrint(TEXT("- Skipped, %u.%u.%u.%u <= %u.%u.%u.%u"), VerMajor,VerMinor,VerBuild,VerQFE, OsComponentData.VersionMajor, OsComponentData.VersionMinor, OsComponentData.BuildNumber, OsComponentData.QFENumber); return NO_ERROR; } } VerbosePrint(TEXT("- Install, %u.%u.%u.%u > %u.%u.%u.%u"), VerMajor,VerMinor,VerBuild,VerQFE, OsComponentData.VersionMajor, OsComponentData.VersionMinor, OsComponentData.BuildNumber, OsComponentData.QFENumber); // // we need to make sure component INF media is in // prompt for media if interactive and INF cannot be found // dwLen = GetFullPathName(PathName,MAX_PATH,SrcPath,&SrcName); if(dwLen >= MAX_PATH) { return ERROR_INSUFFICIENT_BUFFER; } if(SrcName == SrcPath) { // // shouldn't happen // return ERROR_INVALID_DATA; } *CharPrev(SrcPath,SrcName) = TEXT('\0'); uiRes = SetupPromptForDisk( NULL, // parent NULL, // title CompDesc[0] ? CompDesc : NULL, // disk name SrcPath, // path to source SrcName, // name of file NULL, // tag file IDF_CHECKFIRST|IDF_NOCOMPRESSED|IDF_NOSKIP, NewSrcPath, ARRAY_SIZE(NewSrcPath), NULL); switch(uiRes) { case DPROMPT_SUCCESS: break; case DPROMPT_CANCEL: case DPROMPT_SKIPFILE: return ERROR_FILE_NOT_FOUND; case DPROMPT_BUFFERTOOSMALL: return ERROR_INSUFFICIENT_BUFFER; case DPROMPT_OUTOFMEMORY: return ERROR_OUTOFMEMORY; default: // // shouldn't happen // return ERROR_INVALID_DATA; } hrStatus = ConcatPath(NewSrcPath,MAX_PATH,SrcName); if(!SUCCEEDED(hrStatus)) { return (DWORD)hrStatus; } hrStatus = InstallComponent(NewSrcPath, COMP_FLAGS_NOUI, &ComponentGuid, VerMajor, VerMinor, VerBuild, VerQFE, CompDesc[0] ? CompDesc : NULL); if(!SUCCEEDED(hrStatus)) { return (DWORD)hrStatus; } // // if install was not skipped, we get S_OK, else S_FALSE // if(hrStatus == S_OK) { *Flags |= FLAGS_INSTALLED; } else if(hrStatus == INST_S_REBOOT) { *Flags |= FLAGS_INSTALLED|FLAGS_REBOOT; } return NO_ERROR; } DWORD CheckQfe( IN INT SpNum, IN LPCTSTR QfeNum ) /*++ Routine Description: This is pretty dirty, it knows where the QFE #'s will go in registry for Win2k/WinXP so checks there this saves us running the QFE unless we need to (assumption is that target OS version already checked) Arguments: SpNum - service pack # that fix should be in QfeNum - QfeNum of fix Return Value: ERROR_INVALID_PARAMETER if version not supported NO_ERROR if QFE installed other status if QFE might not be installed --*/ { HKEY hKey; TCHAR KeyPath[MAX_PATH*2]; LONG res; // // what about the SP level? // if(g_VerInfo.wServicePackMajor >= SpNum) { VerbosePrint(TEXT("- Skipped (SP >= %u)"),SpNum); return NO_ERROR; } if((g_VerInfo.dwMajorVersion == 5) && (g_VerInfo.dwMinorVersion == 0)) { // // check for QFE presence on Windows 2000 // _sntprintf(KeyPath,ARRAY_SIZE(KeyPath), TEXT("Software\\Microsoft\\Updates\\Windows 2000\\SP%u\\%s"), SpNum, QfeNum); res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KeyPath, 0, KEY_READ, &hKey); if(res == NO_ERROR) { RegCloseKey(hKey); } return (DWORD)res; } else if((g_VerInfo.dwMajorVersion == 5) && (g_VerInfo.dwMinorVersion == 1)) { // // check for QFE presence on Windows XP // _sntprintf(KeyPath,ARRAY_SIZE(KeyPath), TEXT("Software\\Microsoft\\Updates\\Windows XP\\SP%u\\%s"), SpNum, QfeNum); res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KeyPath, 0, KEY_READ, &hKey); if(res == NO_ERROR) { RegCloseKey(hKey); } return (DWORD)res; } else { return ERROR_INVALID_PARAMETER; } } DWORD DoDriverQfe( IN INFCONTEXT *EntryLine, IN LPCTSTR PathName, IN OUT DWORD *Flags ) /*++ Routine Description: queries and potentially installs QFE Arguments: EntryLine - context for remaining information PathName - name of exception pack INF (param 1) SubFlags - flags passed in (param 2) sans type of install component entry consists of ,,,, filename is absolute directory, eg, %1%\foo.exe indicates what to do if qfe installed indicates os version QFE is for, eg, 5.0 indicates service pack QFE is in, eg, 1 indicates the qfe number as found in registry Return Value: status, normally NO_ERROR if return value >= 0x80000000 then it's a HRESULT error --*/ { TCHAR QfeOs[64]; // expecting major.minor TCHAR QfeNum[64]; // some descriptive name INT QfeSp; INT VerMaj,VerMin,VerBuild,VerQfe; TCHAR Buffer[MAX_PATH]; TCHAR CmdLine[MAX_PATH*3]; STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInfo; DWORD ExitCode; DWORD Status; HRESULT hrStatus; UINT uiRes; DWORD dwLen; TCHAR SrcPath[MAX_PATH]; TCHAR NewSrcPath[MAX_PATH]; LPTSTR SrcName; VerbosePrint(TEXT("- %s is a QFE"),PathName); if(!SetupGetStringField(EntryLine,COMPFIELD_QFEOS,QfeOs,ARRAY_SIZE(QfeOs),NULL)) { Status = GetLastError(); DebugPrint(TEXT("- Fail: SetupGetStringField(3), Error: 0x%08x"),Status); return Status; } if(!SetupGetIntField(EntryLine,COMPFIELD_QFESP,&QfeSp)) { Status = GetLastError(); DebugPrint(TEXT("- Fail: SetupGetIntField(4), Error: 0x%08x"),Status); return Status; } if(!SetupGetStringField(EntryLine,COMPFIELD_QFENUM,QfeNum,ARRAY_SIZE(QfeNum),NULL)) { Status = GetLastError(); DebugPrint(TEXT("- Fail: SetupGetStringField(5), Error: 0x%08x"),Status); return Status; } // // see if QFE is targeted at this version? // hrStatus = VersionFromString(QfeOs,&VerMaj,&VerMin,&VerBuild,&VerQfe); if(!SUCCEEDED(hrStatus)) { return (DWORD)hrStatus; } if((hrStatus == S_FALSE) || (VerBuild != 0) || (VerQfe != 0)) { return ERROR_INVALID_PARAMETER; } if(CompareVersion(VerMaj, VerMin, 0, 0, g_VerInfo.dwMajorVersion, g_VerInfo.dwMinorVersion, 0, 0) != 0) { VerbosePrint(TEXT("- Skipped (OS != %u.%u)"),VerMaj,VerMin); return NO_ERROR; } // // see if the Qfe needs to be installed on this OS // Status = CheckQfe(QfeSp,QfeNum); if(Status == ERROR_INVALID_PARAMETER) { // // invalid parameter because in invalid version was // specified // DebugPrint(TEXT("- Cannot install QFE's for %u.%u"), g_VerInfo.dwMajorVersion, g_VerInfo.dwMinorVersion); return Status; } // // ok, has the QFE already been installed? // if(Status == NO_ERROR) { return NO_ERROR; } // // we need to make sure component INF media is in // prompt for media if interactive and INF cannot be found // dwLen = GetFullPathName(PathName,MAX_PATH,SrcPath,&SrcName); if(dwLen >= MAX_PATH) { return ERROR_INSUFFICIENT_BUFFER; } if(SrcName == SrcPath) { // // shouldn't happen // return ERROR_INVALID_DATA; } *CharPrev(SrcPath,SrcName) = TEXT('\0'); uiRes = SetupPromptForDisk( NULL, // parent NULL, // title QfeNum, // disk name SrcPath, // path to source SrcName, // name of file NULL, // tag file IDF_CHECKFIRST|IDF_NOCOMPRESSED|IDF_NOSKIP, NewSrcPath, ARRAY_SIZE(NewSrcPath), NULL); switch(uiRes) { case DPROMPT_SUCCESS: break; case DPROMPT_CANCEL: case DPROMPT_SKIPFILE: return ERROR_FILE_NOT_FOUND; case DPROMPT_BUFFERTOOSMALL: return ERROR_INSUFFICIENT_BUFFER; case DPROMPT_OUTOFMEMORY: return ERROR_OUTOFMEMORY; default: // // shouldn't happen // return ERROR_INVALID_DATA; } hrStatus = ConcatPath(NewSrcPath,MAX_PATH,SrcName); if(!SUCCEEDED(hrStatus)) { return (DWORD)hrStatus; } // now build up command line // lstrcpy(CmdLine,NewSrcPath); lstrcat(CmdLine,TEXT(" -n -o -z -q")); ZeroMemory(&StartupInfo,sizeof(StartupInfo)); StartupInfo.cb = sizeof(StartupInfo); ZeroMemory(&ProcessInfo,sizeof(ProcessInfo)); // // kick off rundll32 process to install QFE // if(!CreateProcess(NewSrcPath, CmdLine, NULL, NULL, FALSE, // don't inherit handles CREATE_NO_WINDOW, // creation flags NULL, // environment NULL, // directory &StartupInfo, &ProcessInfo )) { return GetLastError(); } if(WaitForSingleObject(ProcessInfo.hProcess,INFINITE) == WAIT_OBJECT_0) { // // process terminated 'fine', retrieve status from shared data // if(GetExitCodeProcess(ProcessInfo.hProcess,&ExitCode)) { Status = (DWORD)ExitCode; } else { Status = GetLastError(); } } else { // // failure // Status = ERROR_INVALID_PARAMETER; } CloseHandle(ProcessInfo.hThread); CloseHandle(ProcessInfo.hProcess); if(Status != NO_ERROR) { return Status; } if(CheckQfe(QfeSp,QfeNum)!=NO_ERROR) { // // sanity check failed // return E_UNEXPECTED; } // // if install was not skipped, we get S_OK, else S_FALSE // #if 0 if(hrStatus == S_OK) { *Flags |= FLAGS_INSTALLED; } else if(hrStatus == INST_S_REBOOT) { } #endif *Flags |= FLAGS_INSTALLED|FLAGS_REBOOT; return NO_ERROR; }