/*++ Copyright (c) 1995-97 Microsoft Corporation All rights reserved. Module Name: Install.c Abstract: File queue functions Author: Muhunthan Sivapragasam (MuhuntS) 18-Nov-96 Revision History: --*/ #include "precomp.h" #include const TCHAR szWebDirPrefix[] = TEXT("\\web\\printers\\"); const TCHAR szNtPrintInf[] = TEXT("inf\\ntprint.inf"); // // File queue flags, and structure // #define CALLBACK_MEDIA_CHECKED 0x01 #define CALLBACK_SOURCE_SET 0x02 #define CALLBACK_PATH_MODIFIED 0x04 typedef struct _FILE_QUEUE_CONTEXT { HWND hwnd; PVOID QueueContext; LPCTSTR pszSource; BOOL dwCallbackFlags; DWORD dwInstallFlags; LPCTSTR pszFileSrcPath; TCHAR szInfPath[MAX_PATH]; PLATFORM platform; DWORD dwVersion; } FILE_QUEUE_CONTEXT, *PFILE_QUEUE_CONTEXT; BOOL FileExistsOnMedia( PSOURCE_MEDIA pSourceMedia ) { BOOL bRes = FALSE; TCHAR *pszFile = NULL; DWORD cchLength = 0; DWORD cchAdditionalSymbolsLen = 0; DWORD dwLen1 = 0; DWORD dwLen2 = 0; LPTSTR p = NULL; LPTSTR q = NULL; if ( !pSourceMedia->SourcePath || !*pSourceMedia->SourcePath || !pSourceMedia->SourceFile || !*pSourceMedia->SourceFile ) { goto Cleanup; } cchAdditionalSymbolsLen = lstrlen(TEXT("\\")) + lstrlen(TEXT("_")) + 1; cchLength = lstrlen(pSourceMedia->SourcePath) + lstrlen(pSourceMedia->SourceFile) + cchAdditionalSymbolsLen; if (cchLength == cchAdditionalSymbolsLen) { goto Cleanup; } // // First check if file is there on source path // pszFile = LocalAllocMem(cchLength * sizeof(TCHAR)); if (!pszFile) { goto Cleanup; } StringCchCopy(pszFile, cchLength, pSourceMedia->SourcePath); dwLen1 = lstrlen(pszFile); if ( *(pszFile + (dwLen1-1)) != TEXT('\\') ) { *(pszFile + dwLen1) = TEXT('\\'); ++dwLen1; } StringCchCopy(pszFile + dwLen1, cchLength - dwLen1, pSourceMedia->SourceFile); dwLen2 = dwLen1 + lstrlen(pSourceMedia->SourceFile); bRes = FileExists(pszFile); if (bRes) { goto Cleanup; } p = lstrchr(pszFile, TEXT('.')); q = lstrchr(pszFile, TEXT('\\')); // // A dot present in filename? // if ( q < p ) { // // For files with 0, 1, 2 characters after the dot append underscore. // if ( (lstrlen(p) < 4) && (dwLen2 < (cchLength-1)) ) { *(pszFile + dwLen2) = TEXT('_'); ++dwLen2; *(pszFile + dwLen2) = TEXT('\0'); } else { // // If 3+ characters after dot then replace last character with _ // to get the compressed file name // *(pszFile + (dwLen2-1)) = TEXT('_'); } } else { // // If no dot then replace last character with _ for compressed name // *(pszFile + (dwLen2-1)) = TEXT('_'); } // // Does the compressed file exist on source path? // bRes = FileExists(pszFile); if (bRes) { goto Cleanup; } // // Check for the file in compressed form with $ as the character // *(pszFile + (dwLen2-1)) = TEXT('$'); bRes = FileExists(pszFile); if (bRes) { goto Cleanup; } if ( !pSourceMedia->Tagfile || !*pSourceMedia->Tagfile ) { goto Cleanup; } // // Look for tag file // StringCchCopy(pszFile + dwLen1, cchLength - dwLen1, pSourceMedia->Tagfile); bRes = FileExists(pszFile); Cleanup: LocalFreeMem(pszFile); return bRes; } UINT MyQueueCallback( IN PVOID QueueContext, IN UINT Notification, IN UINT_PTR Param1, IN UINT_PTR Param2 ) { PFILE_QUEUE_CONTEXT pFileQContext=(PFILE_QUEUE_CONTEXT)QueueContext; PSOURCE_MEDIA pSourceMedia; LPTSTR pszPathOut; PFILEPATHS pFilePaths; switch (Notification) { case SPFILENOTIFY_NEEDMEDIA: pSourceMedia = (PSOURCE_MEDIA)Param1; pszPathOut = (LPTSTR)Param2; // // If pszSource is specified then we have a flat share where // all the files are available. Setup is looking for the file // in the sub-directory (ex. ..\i386) based on the layout info. // We need to tell setup to look in the root directory // if ( !(pFileQContext->dwCallbackFlags & CALLBACK_SOURCE_SET) && pFileQContext->pszSource && lstrcmpi(pFileQContext->pszSource, pSourceMedia->SourcePath) ) { StringCchCopy(pszPathOut, MAX_PATH, pFileQContext->pszSource); pFileQContext->dwCallbackFlags |= CALLBACK_SOURCE_SET; return FILEOP_NEWPATH; } // // If DRVINST_PROMPTLESS is set then we can't allow prompt // if ( pFileQContext->dwInstallFlags & DRVINST_PROMPTLESS ) { if ( !(pFileQContext->dwCallbackFlags & CALLBACK_MEDIA_CHECKED) ) { pFileQContext->dwCallbackFlags |= CALLBACK_MEDIA_CHECKED; if ( FileExistsOnMedia(pSourceMedia) ) return FILEOP_DOIT; } return FILEOP_ABORT; } // // If we do a non-native platform install and the user points // to a server CD, the inf will specify a subdir \i386 which is // correct on an installed machine but not on a CD. Remove that dir // and try again. // if ( (pFileQContext->dwInstallFlags & DRVINST_ALT_PLATFORM_INSTALL) && !(pFileQContext->dwCallbackFlags & CALLBACK_PATH_MODIFIED)) { LPSTR pCur; size_t Pos, Len, OverrideLen; // // for NT4 installations we have possibly expanded the INF // from a server CD. Point there if that's the case // if ((pFileQContext->dwVersion == 2) && (pFileQContext->pszFileSrcPath)) { Len = _tcslen(pFileQContext->szInfPath); if (_tcsnicmp(pSourceMedia->SourcePath, pFileQContext->szInfPath, Len) == 0) { StringCchCopy(pszPathOut, MAX_PATH, pFileQContext->pszFileSrcPath); pFileQContext->dwCallbackFlags |= CALLBACK_PATH_MODIFIED; return FILEOP_NEWPATH; } } // // Find the spot where the platform ID begins // Pos = Len = _tcslen(pFileQContext->szInfPath); // // sanity check // if (_tcslen(pSourceMedia->SourcePath) <= Len) goto Default; if (pSourceMedia->SourcePath[Len] == _T('\\')) { Pos++; } OverrideLen = _tcslen(PlatformOverride[pFileQContext->platform].pszName); if (_tcsnicmp(pSourceMedia->SourcePath, pFileQContext->szInfPath, Len) == 0 && _tcsnicmp(&(pSourceMedia->SourcePath[Pos]), PlatformOverride[pFileQContext->platform].pszName, OverrideLen) == 0) { StringCchCopy(pszPathOut, MAX_PATH, pFileQContext->szInfPath); pFileQContext->dwCallbackFlags |= CALLBACK_PATH_MODIFIED; return FILEOP_NEWPATH; } } goto Default; case SPFILENOTIFY_STARTCOPY: pFilePaths = (PFILEPATHS)Param1; if ( gpszSkipDir && !lstrncmpi(gpszSkipDir, pFilePaths->Target, lstrlen(gpszSkipDir)) ) return FILEOP_SKIP; goto Default; case SPFILENOTIFY_ENDCOPY: // Here we set the bMediaChecked flag to FALSE, this is because some OEM drivers // have more than one media, so we assume NEEDMEDIA,STARTCOPY,ENDCOPY,NEEDMEDIA // So if we reset the NEEDMEDIA flag after the ENDCOPY, we are OK // // Clear the per file flags // pFileQContext->dwCallbackFlags &= ~(CALLBACK_MEDIA_CHECKED | CALLBACK_SOURCE_SET | CALLBACK_PATH_MODIFIED); goto Default; case SPFILENOTIFY_COPYERROR: pFilePaths = (PFILEPATHS)Param1; // If there is a copy error happens in webpnp, we force it retry // the orginal flat directory if ( pFileQContext->dwInstallFlags & DRVINST_WEBPNP) { pszPathOut = (LPTSTR)Param2; // We need to make sure the path used in the copy operation is not as same as we're going // to replace, otherwise, it will go to indefinite loop. // if (lstrncmpi(pFileQContext->pszSource, pFilePaths->Source, lstrlen(pFileQContext->pszSource)) || lstrchr (pFilePaths->Source + lstrlen(pFileQContext->pszSource) + 1, TEXT ('\\'))) { if(SUCCEEDED(StringCchCopy(pszPathOut, MAX_PATH, pFileQContext->pszSource))) { return FILEOP_NEWPATH; } } if ( pFileQContext->dwInstallFlags & DRVINST_PROMPTLESS ) { return FILEOP_ABORT; } } goto Default; } Default: return SetupDefaultQueueCallback(pFileQContext->QueueContext, Notification, Param1, Param2); } VOID CheckAndEnqueueOneFile( IN LPCTSTR pszFileName, IN LPCTSTR pszzDependentFiles, OPTIONAL IN HSPFILEQ CopyQueue, IN LPCTSTR pszSourcePath, IN LPCTSTR pszTargetPath, IN LPCTSTR pszDiskName, OPTIONAL IN OUT LPBOOL lpFail ) /*++ Routine Description: Ensure that a file is enqueue only once for copying. To do so we check if the given file name also appears in the list of dependent files and enqueue it only if it does not. Arguments: pszFileName : File name to be checked and enqueued pszzDependentFiles : Dependent files (multi-sz) list CopyQueue : CopyQueue used to enqueue files pszSourcePath : Source directory to look for the files pszTargetPath : Target directory to copy the files to pszDiskName : Title of the disk where files are lpBool : Will be set to TRUE on error Return Value: Nothing --*/ { LPCTSTR psz; if ( *lpFail ) return; // // If the file also appears as a dependent file do not enqueue it // if ( pszzDependentFiles ) { for ( psz = pszzDependentFiles ; *psz ; psz += lstrlen(psz) + 1 ) if ( !lstrcmpi(pszFileName, psz) ) return; } *lpFail = !SetupQueueCopy( CopyQueue, pszSourcePath, NULL, // Path relative to source pszFileName, pszDiskName, // "Supplies a description of the source media to be used during disk prompts" NULL, // Source Tag file pszTargetPath, NULL, // Target file name 0); // Copy style flags } BOOL CopyPrinterDriverFiles( IN LPDRIVER_INFO_6 pDriverInfo6, IN LPCTSTR pszInfName, IN LPCTSTR pszSourcePath, IN LPCTSTR pszDiskName, IN LPCTSTR pszTargetPath, IN HWND hwnd, IN DWORD dwInstallFlags, IN BOOL bForgetSource ) /*++ Routine Description: Copy printer driver files to a specified directory using SetupQueue APIs Arguments: pDriverInfo6 : Points to a valid SELECTED_DRV_INFO szTargetPath : Target directory to copy to szSourcePath : Source directory to look for the files, if none is specified will use the one from prev. operation pszDiskName : Title of the disk where files are hwnd : Windows handle of current top-level window bForgetSource : TRUE if the path where driver files were copied from should not be remembered for future use Return Value: TRUE on succes FALSE else, use GetLastError() to get the error code --*/ { HSPFILEQ CopyQueue; BOOL bFail = FALSE; DWORD dwOldCount, dwNewCount, dwIndex; LPTSTR psz, *List = NULL; FILE_QUEUE_CONTEXT FileQContext; // // Valid DriverInfo6 // if ( !pDriverInfo6 || !pDriverInfo6->pDriverPath || !pDriverInfo6->pDataFile || !pDriverInfo6->pConfigFile ) return FALSE; // // If no additions should be made to the source list findout the count // if ( bForgetSource ) { dwOldCount = 0; if ( !SetupQuerySourceList(SRCLIST_USER | SRCLIST_SYSTEM, &List, &dwOldCount) ) { return FALSE; } SetupFreeSourceList(&List, dwOldCount); } // // Create a setup file copy queue and initialize setup queue callback // ZeroMemory(&FileQContext, sizeof(FileQContext)); FileQContext.hwnd = hwnd; FileQContext.pszSource = NULL; FileQContext.dwInstallFlags = dwInstallFlags; if ( dwInstallFlags & DRVINST_PROGRESSLESS ) { FileQContext.QueueContext = SetupInitDefaultQueueCallbackEx( hwnd, INVALID_HANDLE_VALUE, 0, 0, NULL); } else { FileQContext.QueueContext = SetupInitDefaultQueueCallback(hwnd); } CopyQueue = SetupOpenFileQueue(); if ( CopyQueue == INVALID_HANDLE_VALUE || !FileQContext.QueueContext ) goto Cleanup; CheckAndEnqueueOneFile(pDriverInfo6->pDriverPath, pDriverInfo6->pDependentFiles, CopyQueue, pszSourcePath, pszTargetPath, pszDiskName, &bFail); CheckAndEnqueueOneFile(pDriverInfo6->pDataFile, pDriverInfo6->pDependentFiles, CopyQueue, pszSourcePath, pszTargetPath, pszDiskName, &bFail); CheckAndEnqueueOneFile(pDriverInfo6->pConfigFile, pDriverInfo6->pDependentFiles, CopyQueue, pszSourcePath, pszTargetPath, pszDiskName, &bFail); if ( pDriverInfo6->pHelpFile && *pDriverInfo6->pHelpFile ) CheckAndEnqueueOneFile(pDriverInfo6->pHelpFile, pDriverInfo6->pDependentFiles, CopyQueue, pszSourcePath, pszTargetPath, pszDiskName, &bFail); // // Add each file in the dependent files field to the setup queue // if ( pDriverInfo6->pDependentFiles ) { for ( psz = pDriverInfo6->pDependentFiles ; *psz ; psz += lstrlen(psz) + 1 ) CheckAndEnqueueOneFile(psz, NULL, CopyQueue, pszSourcePath, pszTargetPath, pszDiskName, &bFail); } if ( bFail ) goto Cleanup; { // Before adding files to the File Queue set the correct Platform/Version // info for Driver Signing // Setup the structure for SETUPAPI SP_ALTPLATFORM_INFO AltPlat_Info; HINF hInf; TCHAR CatalogName[ MAX_PATH ]; LPTSTR pszCatalogFile = NULL; AltPlat_Info.cbSize = sizeof(SP_ALTPLATFORM_INFO); AltPlat_Info.Platform = VER_PLATFORM_WIN32_WINDOWS; AltPlat_Info.MajorVersion = 4; AltPlat_Info.MinorVersion = 0; AltPlat_Info.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL; AltPlat_Info.Reserved = 0; AltPlat_Info.FirstValidatedMajorVersion = AltPlat_Info.MajorVersion; AltPlat_Info.FirstValidatedMinorVersion = AltPlat_Info.MinorVersion; if ( CheckForCatalogFileInInf(pszInfName, &pszCatalogFile) && pszCatalogFile ) { if ( (lstrlen(pszSourcePath)+lstrlen(pszCatalogFile)+2) < MAX_PATH ) { StringCchCopy(CatalogName, COUNTOF(CatalogName), pszSourcePath); StringCchCat( CatalogName, COUNTOF(CatalogName), TEXT("\\")); StringCchCat( CatalogName, COUNTOF(CatalogName), pszCatalogFile ); } else { bFail = TRUE; } LocalFreeMem( pszCatalogFile ); pszCatalogFile = CatalogName; } if (bFail) goto Cleanup; // Now call the Setup API to change the parms on the FileQueue bFail = !SetupSetFileQueueAlternatePlatform( CopyQueue, &AltPlat_Info, pszCatalogFile ); } if ( bFail ) goto Cleanup; bFail = !SetupCommitFileQueue(hwnd, CopyQueue, MyQueueCallback, &FileQContext); // // If bForegetSource is set fix source list // if ( bForgetSource && SetupQuerySourceList(SRCLIST_USER | SRCLIST_SYSTEM, &List, &dwNewCount) ) { dwOldCount = dwNewCount - dwOldCount; if ( dwOldCount < dwNewCount ) for ( dwIndex = 0 ; dwIndex < dwOldCount ; ++dwIndex ) { SetupRemoveFromSourceList(SRCLIST_SYSIFADMIN, List[dwIndex]); } SetupFreeSourceList(&List, dwNewCount); } Cleanup: if ( CopyQueue != INVALID_HANDLE_VALUE ) SetupCloseFileQueue(CopyQueue); if ( FileQContext.QueueContext ) SetupTermDefaultQueueCallback(FileQContext.QueueContext); return !bFail; } BOOL AddPrinterDriverUsingCorrectLevel( IN LPCTSTR pszServerName, IN LPDRIVER_INFO_6 pDriverInfo6, IN DWORD dwAddDrvFlags ) { BOOL bReturn; DWORD dwLevel; bReturn = AddPrinterDriverEx((LPTSTR)pszServerName, 6, (LPBYTE)pDriverInfo6, dwAddDrvFlags ); for ( dwLevel = 4 ; !bReturn && GetLastError() == ERROR_INVALID_LEVEL && dwLevel > 1 ; --dwLevel ) { // // Since DRIVER_INFO_2, 3, 4 are subsets of DRIVER_INFO_6 and all fields // are at the beginning these calls can be made with same buffer // bReturn = AddPrinterDriverEx((LPTSTR)pszServerName, dwLevel, (LPBYTE)pDriverInfo6, dwAddDrvFlags); } return bReturn; } typedef struct _MONITOR_SCAN_INFO { LPTSTR pszMonitorDll; LPTSTR pszTargetDir; BOOL bFound; } MONITOR_SCAN_INFO, *PMONITOR_SCAN_INFO; UINT MonitorCheckCallback( IN PVOID pContext, IN UINT Notification, IN UINT_PTR Param1, IN UINT_PTR Param2 ) /*++ Routine Description: This callback routine is to check language monitor dll is getting copied to system32 directory. Arguments: pContext : Gives the MONITOR_SCAN_INFO structure Notification : Ignored Param1 : Gives the target file name Param2 : Ignored Return Value: Win32 error code --*/ { size_t dwLen; LPTSTR pszTarget = (LPTSTR)Param1, pszFileName; PMONITOR_SCAN_INFO pScanInfo = (PMONITOR_SCAN_INFO)pContext; if ( !pScanInfo->bFound ) { if ( !(pszFileName = FileNamePart(pszTarget)) ) return ERROR_INVALID_PARAMETER; if ( !lstrcmpi(pScanInfo->pszMonitorDll, pszFileName) ) { // // Length excludes \ (i.e. D:\winnt\system32) // dwLen = (size_t)(pszFileName - pszTarget - 1); if ( !lstrncmpi(pScanInfo->pszTargetDir, pszTarget, dwLen) && dwLen == (DWORD) lstrlen(pScanInfo->pszTargetDir) ) pScanInfo->bFound = TRUE; } } return NO_ERROR; } BOOL CheckAndEnqueueMonitorDll( IN LPCTSTR pszMonitorDll, IN LPCTSTR pszSource, IN HSPFILEQ CopyQueue, IN HINF hInf ) /*++ Routine Description: This routine is to check language monitor dll is getting copied to system32 directory. On NT 4.0 we did not list LM as a file to be copied. ntprint.dll automatically did it. Now we use a DRID for it. But for backward compatibility this routine is there to get NT 4.0 INFs to work. Arguments: pszMonitorDll : Monitor dll to enqueue pszSource : Source directory to look for files CopyQueue : File queue hInf : Printer driver INF file handle Return Value: TRUE on success, FALSE else --*/ { BOOL bRet = FALSE; DWORD dwNeeded; LPTSTR pszPathOnSource = NULL, pszDescription = NULL, pszTagFile = NULL; TCHAR szDir[MAX_PATH]; MONITOR_SCAN_INFO ScanInfo; SP_FILE_COPY_PARAMS FileCopyParams = {0}; if ( !GetSystemDirectory(szDir, SIZECHARS(szDir)) ) goto Cleanup; ScanInfo.pszMonitorDll = (LPTSTR)pszMonitorDll; ScanInfo.pszTargetDir = szDir; ScanInfo.bFound = FALSE; if ( !SetupScanFileQueue(CopyQueue, SPQ_SCAN_USE_CALLBACK, 0, MonitorCheckCallback, &ScanInfo, &dwNeeded) ) goto Cleanup; if ( !ScanInfo.bFound ) { pszPathOnSource = (LPTSTR) LocalAllocMem(MAX_PATH * sizeof(TCHAR)); if ( !pszPathOnSource ) goto Cleanup; // // This gives which subdirectory to look for. By default in same dir // if ( !FindPathOnSource(pszMonitorDll, hInf, pszPathOnSource, MAX_PATH, &pszDescription, &pszTagFile) ) { LocalFreeMem(pszPathOnSource); pszPathOnSource = NULL; } FileCopyParams.cbSize = sizeof( SP_FILE_COPY_PARAMS ); FileCopyParams.QueueHandle = CopyQueue; FileCopyParams.SourceRootPath = pszSource; FileCopyParams.SourcePath = pszPathOnSource; FileCopyParams.SourceFilename = pszMonitorDll; FileCopyParams.SourceDescription = pszDescription; FileCopyParams.SourceTagfile = pszTagFile; FileCopyParams.TargetDirectory = szDir; FileCopyParams.TargetFilename = NULL; FileCopyParams.CopyStyle = SP_COPY_NEWER; FileCopyParams.LayoutInf = hInf; FileCopyParams.SecurityDescriptor = NULL; if ( !SetupQueueCopyIndirect(&FileCopyParams) ) { goto Cleanup; } } bRet = TRUE; Cleanup: LocalFreeMem(pszPathOnSource); LocalFreeMem(pszDescription); LocalFreeMem(pszTagFile); return bRet; } BOOL GetWebPageDir( IN PPSETUP_LOCAL_DATA pLocalData, OUT TCHAR szDir[MAX_PATH] ) { BOOL bRet = FALSE; DWORD dwLen; if(!pLocalData->DrvInfo.pszManufacturer || !pLocalData->DrvInfo.pszModelName) { goto Done; } if ( !GetSystemWindowsDirectory(szDir, MAX_PATH) ) goto Done; dwLen = lstrlen(szDir) + lstrlen(szWebDirPrefix) + lstrlen(pLocalData->DrvInfo.pszManufacturer) + lstrlen(pLocalData->DrvInfo.pszModelName) + 2; if ( dwLen >= MAX_PATH ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto Done; } StringCchCat(szDir, MAX_PATH, szWebDirPrefix); StringCchCat(szDir, MAX_PATH, pLocalData->DrvInfo.pszManufacturer); StringCchCat(szDir, MAX_PATH, TEXT("\\")); StringCchCat(szDir, MAX_PATH, pLocalData->DrvInfo.pszModelName); bRet = TRUE; Done: return bRet; } BOOL SetTargetDirectories( IN PPSETUP_LOCAL_DATA pLocalData, IN PLATFORM platform, IN LPCTSTR pszServerName, IN HINF hInf, IN DWORD dwInstallFlags ) /*++ Routine Description: Set all the target directories listed in the INF file for file copy operations. Also gets the source directory where we should look for driver files Arguments: hDevInfo : Handle to printer device info list pLocalData : INF parsing information platform : Gives the platform pszSource : Source directory to look for files CopyQueue : File queue hInf : Printer driver INF file handle dwInstallFlags : Driver installation flags Return Value: TRUE on success, FALSE else --*/ { BOOL bRet=FALSE; DWORD dwNeeded, dwBytes, dwIndex, dwIndex2, dwCount, dwCount2; INT DRID; TCHAR szDir[MAX_PATH]; INFCONTEXT InfContext; if ( (dwCount = SetupGetLineCount(hInf, TEXT("DestinationDirs"))) == -1 ) goto Cleanup; // Setup the Skip Dir if ( !SetupSkipDir( platform, pszServerName ) ) goto Cleanup; // // Process every line in the DestinationDirs section // for ( dwIndex = 0 ; dwIndex < dwCount ; ++dwIndex ) { if ( !SetupGetLineByIndex(hInf, TEXT("DestinationDirs"), dwIndex, &InfContext) ) goto Cleanup; // // A file could be copied to multiple destination directories // if ( (dwCount2 = SetupGetFieldCount(&InfContext)) == 0 ) continue; for ( dwIndex2 = 1 ; dwIndex2 <= dwCount2 ; ++dwIndex2 ) { // // Not all directories are specified with a DRID // for ex. %ProgramFiles%\%OLD_ICWDIR% could be used // If DRID is smaller than DIRID_USER setup has predefined // meaning to it // if ( !SetupGetIntField(&InfContext, dwIndex2, &DRID) || ( DRID < DIRID_USER ) ) continue; dwNeeded = SIZECHARS(szDir); dwBytes = sizeof(szDir); switch (DRID) { case PRINTER_DRIVER_DIRECTORY_ID: if ( !GetPrinterDriverDirectory( (LPTSTR)pszServerName, PlatformEnv[platform].pszName, 1, (LPBYTE)szDir, sizeof(szDir), &dwNeeded) ) { goto Cleanup; } if ( dwInstallFlags & DRVINST_PRIVATE_DIRECTORY ) { // // if we have a pnp-ID, and it's an installation of a native driver // and it's not an inbox driver, make the files stick around for // pnp-reinstallations, else the user gets prompted over and over again // if ((lstrlen(pLocalData->DrvInfo.pszHardwareID) != 0) && !(dwInstallFlags & DRVINST_ALT_PLATFORM_INSTALL) && !IsSystemNTPrintInf(pLocalData->DrvInfo.pszInfName)) { // // add the pnp-ID to szDir and set flag to not clean up this directory // this is to get around users getting prompted for pnp-reinstallation // AddPnpDirTag( pLocalData->DrvInfo.pszHardwareID, szDir, sizeof(szDir)/sizeof(TCHAR)); pLocalData->Flags |= LOCALDATAFLAG_PNP_DIR_INSTALL; } else { // // Add PID\TID to szDir. // If this fails, add the dir info held in szDir to the DRIVER_INFO struct // anyway as we'll attempt the install with this. // AddDirectoryTag( szDir, sizeof(szDir)/sizeof(TCHAR) ); } ASSERT(pLocalData); // // Change DI6 to have the full szDir path included. // Can't do anything if this fails, so try finish install anyway. // AddDirToDriverInfo( szDir, &(pLocalData->InfInfo.DriverInfo6) ); } break; case PRINT_PROC_DIRECTORY_ID: if ( dwInstallFlags & DRVINST_DRIVERFILES_ONLY ) { StringCchCopy(szDir, COUNTOF(szDir), gpszSkipDir); } else if ( !GetPrintProcessorDirectory( (LPTSTR)pszServerName, PlatformEnv[platform].pszName, 1, (LPBYTE)szDir, sizeof(szDir), &dwNeeded) ) goto Cleanup; break; case SYSTEM_DIRECTORY_ID_ONLY_FOR_NATIVE_ARCHITECTURE: if ( !(dwInstallFlags & DRVINST_DRIVERFILES_ONLY) && platform == MyPlatform && MyName(pszServerName) ) { if ( !GetSystemDirectory(szDir, dwNeeded) ) goto Cleanup; } else { StringCchCopy(szDir, COUNTOF(szDir),gpszSkipDir); } break; case ICM_PROFILE_DIRECTORY_ID: if ( !GetColorDirectory(pszServerName, szDir, &dwBytes) ) goto Cleanup; break; case WEBPAGE_DIRECTORY_ID: if ( !GetWebPageDir(pLocalData, szDir) ) goto Cleanup; break; default: // // This is for any new DRIDs we may add in the future // StringCchCopy(szDir, COUNTOF(szDir), gpszSkipDir); } if ( !SetupSetDirectoryId(hInf, DRID, szDir) ) goto Cleanup; } } bRet = TRUE; Cleanup: return bRet; } BOOL PSetupInstallICMProfiles( IN LPCTSTR pszServerName, IN LPCTSTR pszzICMFiles ) /*++ Routine Description: Install ICM color profiles associated with a printer driver. Arguments: pszServerName : Server name to which we are installing pszzICMFiles : ICM profiles to install (multi-sz) Return Value: TRUE on success, FALSE else --*/ { TCHAR szDir[MAX_PATH], *p; DWORD dwSize, dwNeeded; BOOL bRet = TRUE; if ( !pszzICMFiles || !*pszzICMFiles ) return bRet; dwSize = SIZECHARS(szDir); dwNeeded = sizeof(szDir); if ( !GetColorDirectory(pszServerName, szDir, &dwNeeded) ) return FALSE; dwNeeded /= sizeof(TCHAR); szDir[dwNeeded-1] = TEXT('\\'); // // Install and assoicate each profiles from the multi-sz field // for ( p = (LPTSTR) pszzICMFiles; bRet && *p ; p += lstrlen(p) + 1 ) { if ( dwNeeded + lstrlen(p) + 1 > dwSize ) { ASSERT(dwNeeded + lstrlen(p) + 1 <= dwSize); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } StringCchCopy(szDir + dwNeeded, dwSize - dwNeeded, p); // // This function only supports NULL as the servername // bRet = InstallColorProfile( NULL, szDir); } return bRet; } BOOL MonitorRedirectDisable( IN LPCTSTR pszMonitorDll, OUT PTCHAR *ppszDir ) { BOOL bRet = FALSE; PTCHAR pszBuffer = NULL; DWORD dwDirLen = 0; if( IsInWow64() ) { pszBuffer = (PTCHAR)LocalAllocMem( MAX_PATH * sizeof( TCHAR ) ); if((pszBuffer != NULL) && GetSystemDirectory(pszBuffer, MAX_PATH)) { dwDirLen = lstrlen(pszBuffer); // // Size of the returned string + size of file name + '\' + terminating null. // if( (dwDirLen + lstrlen(pszMonitorDll) + 2) < MAX_PATH ) { if( *(pszBuffer + dwDirLen-1) != _T('\\') ) { *(pszBuffer + dwDirLen++) = _T('\\'); *(pszBuffer + dwDirLen) = 0; } StringCchCat(pszBuffer, MAX_PATH, pszMonitorDll); #if !_WIN64 Wow64DisableFilesystemRedirector(pszBuffer); #endif bRet = TRUE; } } if (ppszDir != NULL) { *ppszDir = pszBuffer; } } return bRet; } BOOL MonitorRedirectEnable( IN OUT PTCHAR *ppszDir ) { BOOL bRet = FALSE; if( IsInWow64() ) { // // This MACRO works on the current thread. Only one file can be disabled for redirection at a time. // #if !_WIN64 Wow64EnableFilesystemRedirector(); #endif } if ((ppszDir != NULL) && (*ppszDir != NULL)) { LocalFreeMem( *ppszDir ); *ppszDir = NULL; bRet = TRUE; } return bRet; } BOOL UseUniqueDirectory( IN LPCTSTR pszServerName ) /*++ Routine Description: Determines whether the unique install directory flags should be used or not. Arguments: pszServerName - the name of the remote server. NULL means local machine. Return Value: TRUE if we are going remote to a whistler or more recent server or we are installing locally but not in setup. FALSE else --*/ { BOOL bRet = FALSE; if( pszServerName && *pszServerName ) { bRet = IsWhistlerOrAbove(pszServerName); } else { if( !IsSystemSetupInProgress() ) { bRet = TRUE; } } return bRet; } DWORD PSetupShowBlockedDriverUI(HWND hwnd, DWORD BlockingStatus) /*++ Routine Description: Throws UI to ask user what to do with a blocked/warned driver Arguments: hwnd: parent window BlockingStatus: the DWORD containing the BSP_* flags that indicate whether driver is blocked Return Value: New blocking status, the user selection is OR'd. Treats errors as if the user cancelled. --*/ { DWORD NewBlockingStatus = BlockingStatus; LPTSTR pszTitle = NULL, pszPrompt = NULL; switch (BlockingStatus & BSP_BLOCKING_LEVEL_MASK) { case BSP_PRINTER_DRIVER_WARNED: if (BlockingStatus & BSP_INBOX_DRIVER_AVAILABLE) { pszTitle = GetStringFromRcFile(IDS_TITLE_BSP_WARN); pszPrompt = GetLongStringFromRcFile(IDS_BSP_WARN_WITH_INBOX); if (!pszTitle || !pszPrompt) { NewBlockingStatus |= BSP_PRINTER_DRIVER_CANCELLED; goto Cleanup; } switch (MessageBox(hwnd, pszPrompt, pszTitle, MB_YESNOCANCEL | MB_ICONWARNING)) { case IDYES: NewBlockingStatus |= BSP_PRINTER_DRIVER_PROCEEDED; break; case IDNO: NewBlockingStatus |= BSP_PRINTER_DRIVER_REPLACED; break; default: NewBlockingStatus |= BSP_PRINTER_DRIVER_CANCELLED; break; } } else // warned but not inbox available { pszTitle = GetStringFromRcFile(IDS_TITLE_BSP_WARN); pszPrompt = GetLongStringFromRcFile(IDS_BSP_WARN_NO_INBOX); if (!pszTitle || !pszPrompt) { NewBlockingStatus |= BSP_PRINTER_DRIVER_CANCELLED; goto Cleanup; } switch (MessageBox(hwnd, pszPrompt, pszTitle, MB_OKCANCEL | MB_ICONWARNING)) { case IDOK: NewBlockingStatus |= BSP_PRINTER_DRIVER_PROCEEDED; break; default: NewBlockingStatus |= BSP_PRINTER_DRIVER_CANCELLED; break; } } break; case BSP_PRINTER_DRIVER_BLOCKED: if (BlockingStatus & BSP_INBOX_DRIVER_AVAILABLE) { pszTitle = GetStringFromRcFile(IDS_TITLE_BSP_WARN); pszPrompt = GetLongStringFromRcFile(IDS_BSP_BLOCK_WITH_INBOX); if (!pszTitle || !pszPrompt) { NewBlockingStatus |= BSP_PRINTER_DRIVER_CANCELLED; goto Cleanup; } switch (MessageBox(hwnd, pszPrompt, pszTitle, MB_OKCANCEL | MB_ICONWARNING)) { case IDOK: NewBlockingStatus |= BSP_PRINTER_DRIVER_REPLACED; break; default: NewBlockingStatus |= BSP_PRINTER_DRIVER_CANCELLED; break; } } else // blocked and no inbox available - don't allow installation { pszTitle = GetStringFromRcFile(IDS_TITLE_BSP_ERROR); pszPrompt = GetLongStringFromRcFile(IDS_BSP_BLOCK_NO_INBOX); if (!pszTitle || !pszPrompt) { NewBlockingStatus |= BSP_PRINTER_DRIVER_CANCELLED; goto Cleanup; } MessageBox(hwnd, pszPrompt, pszTitle, MB_OK | MB_ICONSTOP); NewBlockingStatus |= BSP_PRINTER_DRIVER_CANCELLED; } break; } Cleanup: if (pszTitle) { LocalFreeMem(pszTitle); } if (pszPrompt) { LocalFreeMem(pszPrompt); } return NewBlockingStatus; } BOOL ValidPlatform( IN PLATFORM platform ) { BOOL bRet = FALSE; switch (platform) { case PlatformAlpha: bRet = TRUE; break; case PlatformX86: bRet = TRUE; break; case PlatformMIPS: bRet = TRUE; break; case PlatformPPC: bRet = TRUE; break; case PlatformWin95: bRet = TRUE; break; case PlatformIA64: bRet = TRUE; break; case PlatformAlpha64: bRet = TRUE; break; default: bRet = FALSE; } return bRet; } DWORD InstallDriverFromCurrentInf( IN HDEVINFO hDevInfo, IN PPSETUP_LOCAL_DATA pLocalData, IN HWND hwnd, IN PLATFORM platform, IN DWORD dwVersion, IN LPCTSTR pszServerName, IN HSPFILEQ CopyQueue, IN PVOID QueueContext, IN PSP_FILE_CALLBACK InstallMsgHandler, IN DWORD Flags, IN LPCTSTR pszSource, IN DWORD dwInstallFlags, IN DWORD dwAddDrvFlags, IN LPCTSTR pszFileSrcPath, OUT LPTSTR *ppszNewDriverName, OUT PDWORD pBlockingStatus ) { HINF hPrinterInf = INVALID_HANDLE_VALUE; BOOL bRet = FALSE; BOOL bAddMon = FALSE; BOOL bKeepMonName = FALSE; BOOL bCatInInf = FALSE; DWORD dwStatus = EXIT_FAILURE; LPTSTR pszMonitorDll, psz; PSELECTED_DRV_INFO pDrvInfo; PPARSEINF_INFO pInfInfo; PVOID pDSInfo = NULL; // Holds pointer to the driver signing class that C can't understand. FILE_QUEUE_CONTEXT FileQContext; // // The following are only used during Cleanup // BOOL bZeroInf = FALSE, bCopyInf = FALSE; DWORD dwMediaType = SPOST_NONE; DWORD dwInstallLE = ERROR_SUCCESS; // We record the LastError in case Cleanup // alters it LPTSTR pszINFName = NULL; // This will record whether the inf was // copied in LPTSTR pszNewINFName = NULL; // Hold the name of the inf to be zeroed if necessary TCHAR szFullINFName[ MAX_PATH ]; // The Original Inf Name TCHAR szFullNewINFName[ MAX_PATH ]; // The fully qualified inf name as copied onto the system. HANDLE hDriverFile = INVALID_HANDLE_VALUE; LPTSTR pszNewDriverName = NULL; DWORD fBlockingStatus = BSP_PRINTER_DRIVER_OK; PTCHAR pszDirPtr = NULL; DWORD ScanResult = 0; // // Those below are used to manage the situation when the driver is not signed // BOOL bIsPersonalOrProfessional = FALSE; HANDLE hRestorePointHandle = NULL; BOOL bDriverNotInstalled = TRUE; BOOL bIsWindows64 = FALSE; BOOL bPreviousNames = FALSE; DWORD dwOEMInfFileAttrs = 0; const DWORD dwGetFileAttrsError = INVALID_FILE_ATTRIBUTES; szFullINFName[0] = TEXT('\0'); szFullNewINFName[0] = TEXT('\0'); if(!pLocalData || !ValidPlatform(platform)) { SetLastError(ERROR_INVALID_PARAMETER); return ERROR_INVALID_PARAMETER; } pDrvInfo = &pLocalData->DrvInfo; pInfInfo = &pLocalData->InfInfo; // // Set the unique directory flags if we are in a situation that uses them. // if( UseUniqueDirectory(pszServerName) ) { dwInstallFlags |= DRVINST_PRIVATE_DIRECTORY; dwAddDrvFlags |= APD_COPY_FROM_DIRECTORY; } // // If this is a Windows update install, we need to ensure that all // cluster spooler resources get their drivers updated. // if (dwInstallFlags & DRVINST_WINDOWS_UPDATE) { dwAddDrvFlags |= APD_COPY_TO_ALL_SPOOLERS; pInfInfo->DriverInfo6.cVersion = dwVersion; } // // Open INF file and append layout.inf specified in Version section // Layout inf is optional // hPrinterInf = SetupOpenInfFile(pDrvInfo->pszInfName, NULL, INF_STYLE_WIN4, NULL); if ( hPrinterInf == INVALID_HANDLE_VALUE ) goto Cleanup; SetupOpenAppendInfFile(NULL, hPrinterInf, NULL); pInfInfo->DriverInfo6.pEnvironment = PlatformEnv[platform].pszName; // // DI_VCP tells us not to create new file-queue and use user provided one // if ( !(Flags & DI_NOVCP) ) { CopyQueue = SetupOpenFileQueue(); if ( CopyQueue == INVALID_HANDLE_VALUE ) goto Cleanup; if ( dwInstallFlags & DRVINST_PROGRESSLESS ) { QueueContext = SetupInitDefaultQueueCallbackEx( hwnd, INVALID_HANDLE_VALUE, 0, 0, NULL); } else { QueueContext = SetupInitDefaultQueueCallback(hwnd); } InstallMsgHandler = MyQueueCallback; ZeroMemory(&FileQContext, sizeof(FileQContext)); FileQContext.hwnd = hwnd; FileQContext.QueueContext = QueueContext; FileQContext.dwInstallFlags = dwInstallFlags; FileQContext.pszSource = (dwInstallFlags & DRVINST_FLATSHARE) ? pszSource : NULL; FileQContext.platform = platform; FileQContext.dwVersion = dwVersion; FileQContext.pszFileSrcPath = pszFileSrcPath; if (pDrvInfo->pszInfName) { StringCchCopy(FileQContext.szInfPath, COUNTOF(FileQContext.szInfPath), pDrvInfo->pszInfName); // // Cut off the inf file name // psz = _tcsrchr(FileQContext.szInfPath, _T('\\')); if (psz) { *psz = 0; } } } // // Setup the driver signing info. // if(NULL == (pDSInfo = SetupDriverSigning(hDevInfo, pszServerName,pDrvInfo->pszInfName, pszSource, platform, dwVersion, CopyQueue, dwInstallFlags & DRVINST_WEBPNP))) { goto Cleanup; } // // Find out if the cat was listed in a CatalogFile= entry. // This is used in the cleanup. // bCatInInf = IsCatInInf(pDSInfo); // // Check if this Driver is from CDM. // IF it is pass in the correct MediaType to Setup. // if ( (pLocalData->DrvInfo.Flags & SDFLAG_CDM_DRIVER) || (dwInstallFlags & DRVINST_WINDOWS_UPDATE) ) dwMediaType = SPOST_URL; // // For non admins, we install the catalog by calling AddDriverCatalog // // Do not fail the call when AddDriverCatalog fails // (void)AddDriverCatalogIfNotAdmin(pszServerName, pDSInfo, pDrvInfo->pszInfName, NULL, dwMediaType, 0); // // To support same INFs to install both NT and Win95 drivers actual // section to install could be different than the one corresponding // to the selected driver. // // SetupSetPlatformOverride tells setup which platform drivers we need // from the media // Also note setup does not reset PlatformPath override. So we need to // call this always // if ( !ParseInf(hDevInfo, pLocalData, platform, pszServerName, dwInstallFlags, FALSE) || !SetupSetPlatformPathOverride(PlatformOverride[platform].pszName) ) { goto Cleanup; } // Now do the actual file copies... if ( !InstallAllInfSections( pLocalData, platform, pszServerName, CopyQueue, pszSource, dwInstallFlags, hPrinterInf, pInfInfo->pszInstallSection ) ) goto Cleanup; // // If there is a language monitor make sure it is getting copied to // system32. On NT 4 we used to manually queue it to system32 // if ( pInfInfo->DriverInfo6.pMonitorName && platform == MyPlatform && !(dwInstallFlags & DRVINST_DRIVERFILES_ONLY) && !pszServerName) { // // NOTICE-2001/06/18-rorleth -- see bug NTBUG9-416129 Installing NT4 additional driver loses LM registry information. // // // If it's an alternate platform but the platform really is the same (checked above) // it's an NT4 driver on an x86 server. In this case install the LM too if not // already installed else point-and-print from an NT4 client that has the same driver // installed locally will delete the LM info from the NT4 driver. // The logic is a little twisted: install the monitor if it's not an alternate platform // or if it is (within the limits checked above) but no monitor with this name is installed yet. // if (!(dwInstallFlags & DRVINST_ALT_PLATFORM_INSTALL) || !IsLanguageMonitorInstalled(pInfInfo->DriverInfo6.pMonitorName)) { pszMonitorDll = pInfInfo->DriverInfo6.pMonitorName + lstrlen(pInfInfo->DriverInfo6.pMonitorName) + 1; // // When we parse the INF we put the monitor dll name after \0 // if ( !CheckAndEnqueueMonitorDll(pszMonitorDll, pszSource, CopyQueue, hPrinterInf) ) goto Cleanup; MonitorRedirectDisable( pszMonitorDll, &pszDirPtr ); bAddMon = TRUE; } else { // // we get here if it's an alternate platform driver and the monitor is already installed // in this case, don't clean out the monitor name from the driver info 6 below. // bKeepMonName = TRUE; } } // // DI_NOVCP is used for pre-install when the class installer is just // supposed to queue the files and return. Printing needs special // handling since APIs need to be called. But we will obey the flags // as much as possible for those who use it // if ( Flags & DI_NOVCP ) { bRet = TRUE; goto Cleanup; } // We need a Queue Context to actually install files if ( !QueueContext ) goto Cleanup; // Check if this is a WebPnP install if ( dwInstallFlags & DRVINST_WEBPNP || !(bAddMon || bKeepMonName) ) { // // Check to see if there is a Monitor. If so clear out. // There is no need to call IsLanguageMonitorInstalled here, since if we // get to this point in the code either it is a Web-PnP installation // in which case the language monitor is useless, or the printer driver // (and language monitor) is for a different platform or we are installing // on a server different than our local machine, in which case we cannot // enumerate the language monitors. Note EnumMonitors does NOT enumerate // language monitors. // if ( pInfInfo->DriverInfo6.pMonitorName ) { LocalFreeMem( pInfInfo->DriverInfo6.pMonitorName ); pInfInfo->DriverInfo6.pMonitorName = NULL; } } if (!PruneInvalidFilesIfNotAdmin( hwnd, CopyQueue )) goto Cleanup; // // prune files that are already present (correct version etc. checked by signature), ignore return value // SetupScanFileQueue( CopyQueue, (SPQ_SCAN_FILE_VALIDITY | SPQ_SCAN_PRUNE_COPY_QUEUE), hwnd, NULL, NULL, &ScanResult); if (!pszServerName || !lstrlen(pszServerName)) { bIsPersonalOrProfessional = IsProductType( VER_NT_WORKSTATION, VER_EQUAL) == S_OK; } else { bIsPersonalOrProfessional = FALSE; } if (bIsPersonalOrProfessional) { SetupSetFileQueueFlags( CopyQueue, SPQ_FLAG_ABORT_IF_UNSIGNED, SPQ_FLAG_ABORT_IF_UNSIGNED ); } if ( !SetupCommitFileQueue(hwnd, CopyQueue, (PSP_FILE_CALLBACK)InstallMsgHandler, (PVOID)&FileQContext) ) { bIsWindows64 = IsInWow64(); if ((bIsWindows64 == FALSE) && bIsPersonalOrProfessional && (GetLastError() == ERROR_SET_SYSTEM_RESTORE_POINT)) { // // Here we have to start a Restore Point because there is // something unsigned and this is either a personal or // professional. // hRestorePointHandle = StartSystemRestorePoint( NULL, (PCWSTR)(pLocalData->DrvInfo.pszModelName), ghInst, IDS_BSP_WARN_UNSIGNED_DRIVER ); // // Terminate the default setupapi callback // SetupTermDefaultQueueCallback( QueueContext ); QueueContext = NULL; // // Initialize the QueueContext structure // if ( dwInstallFlags & DRVINST_PROGRESSLESS ) { QueueContext = SetupInitDefaultQueueCallbackEx(hwnd, INVALID_HANDLE_VALUE, 0, 0, NULL); } else { QueueContext = SetupInitDefaultQueueCallback(hwnd); } if (!QueueContext) { goto Cleanup; } else { FileQContext.QueueContext = QueueContext; } // // Reset the flag and call the function again // SetupSetFileQueueFlags( CopyQueue, SPQ_FLAG_ABORT_IF_UNSIGNED, 0 ); if ( !SetupCommitFileQueue(hwnd, CopyQueue, (PSP_FILE_CALLBACK)InstallMsgHandler, (PVOID)&FileQContext) ) { goto Cleanup; } } else { goto Cleanup; } } // // Now that we did the file copy part of install we will do anything // else specified in the INF // if ( !pszServerName && platform == MyPlatform ) { SetupInstallFromInfSection(hwnd, hPrinterInf, pInfInfo->pszInstallSection, SPINST_ALL & (~SPINST_FILES), NULL, pszSource, 0, NULL, QueueContext, hDevInfo, pDrvInfo->pDevInfoData); } if ( bAddMon ) { if( !AddPrintMonitor(pInfInfo->DriverInfo6.pMonitorName, pszMonitorDll) ) { DWORD dwSavedLastError = EXIT_FAILURE; // // If we can not add monitor, check whether this // driver is in printupg. If it is, consider it blocked then and // popups a UI asking whether to install the replacement driver. // // After this point bRet is allways false, we only try to change // the error code in last error. // // // Save the last error first // dwSavedLastError = GetLastError(); if (BlockedDriverPrintUpgUI(pszServerName, &pInfInfo->DriverInfo6, dwInstallFlags & DRVINST_PRIVATE_DIRECTORY, // whether use full path !(dwInstallFlags & DRVINST_DONT_OFFER_REPLACEMENT), // whether to offer replacement !(dwInstallFlags & (DRVINST_NO_WARNING_PROMPT | DRVINST_PROMPTLESS)), // whether to popup UI &pszNewDriverName, &fBlockingStatus) && (fBlockingStatus & BSP_PRINTER_DRIVER_REPLACED)) { SetLastError(ERROR_PRINTER_DRIVER_BLOCKED); } else { SetLastError(dwSavedLastError); // restore the error code } goto Cleanup; } MonitorRedirectEnable( &pszDirPtr ); } // // If a print processor is specified in the INF need to install it. // For non-native architectur spooler fails this call (for remote case) // We have to install a Print Processor only in the case of a local installation AND // same platform. // if ( !(dwInstallFlags & DRVINST_ALT_PLATFORM_INSTALL) && !pszServerName && pInfInfo->pszPrintProc && !AddPrintProcessor((LPTSTR)pszServerName, PlatformEnv[platform].pszName, pInfInfo->pszPrintProc + lstrlen(pInfInfo->pszPrintProc) + 1, pInfInfo->pszPrintProc) && GetLastError() != ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED && GetLastError() != ERROR_INVALID_ENVIRONMENT ) { goto Cleanup; } if (IsTheSamePlatform(pszServerName, platform) && IsWhistlerOrAbove(pszServerName)) { bPreviousNames = CheckAndKeepPreviousNames( pszServerName, &pInfInfo->DriverInfo6, platform ); } bRet = AddPrinterDriverUsingCorrectLevelWithPrintUpgRetry(pszServerName, &pInfInfo->DriverInfo6, dwAddDrvFlags | APD_DONT_SET_CHECKPOINT, dwInstallFlags & DRVINST_PRIVATE_DIRECTORY, // whether use full path !(dwInstallFlags & DRVINST_DONT_OFFER_REPLACEMENT), // whether to offer replacement !(dwInstallFlags & (DRVINST_NO_WARNING_PROMPT | DRVINST_PROMPTLESS)), // whether to popup UI &pszNewDriverName, &fBlockingStatus) && PSetupInstallICMProfiles(pszServerName, pInfInfo->pszzICMFiles); if (bPreviousNames) { LocalFreeMem( pInfInfo->DriverInfo6.pszzPreviousNames ); pInfInfo->DriverInfo6.pszzPreviousNames = NULL; } bDriverNotInstalled = FALSE; Cleanup: dwInstallLE = GetLastError(); // Get the real error message if (bAddMon && pszDirPtr) { MonitorRedirectEnable( &pszDirPtr ); } if ((bIsWindows64 == FALSE) && hRestorePointHandle) { // // Here we have to end the Restore Point because one was // started // EndSystemRestorePoint(hRestorePointHandle, bDriverNotInstalled); } if (hDriverFile != INVALID_HANDLE_VALUE) { CloseHandle(hDriverFile); } // // Zero the inf if DI_NOVCP and - either we've fail, it is a Web PNP // install. or - this is installing from a non-system ntprint.inf AND // there is a cat to be protected. - not yet!! // bZeroInf = (!bRet || ( dwInstallFlags & DRVINST_WEBPNP ) || (IsNTPrintInf( pDrvInfo->pszInfName ) && bCatInInf)) && !(Flags & DI_NOVCP); // // We have to copy the INF if the following conditions are satisfied // bCopyInf = // bRet is TRUE (the call to AddPrinterDriverUsingCorrectLevelWithPrintUpgRetry succeeded) bRet && // and the Installation flags say the INF to be copied !(dwInstallFlags & DRVINST_DONOTCOPY_INF) && // and the platform is the same as ours platform == MyPlatform && // and the INF it's not the system ntprint.inf (obviously) or // it's from WU (!IsSystemNTPrintInf(pDrvInfo->pszInfName) || (pLocalData->DrvInfo.Flags & SDFLAG_CDM_DRIVER)) && // and DI_NOVCP flag is not set !(Flags & DI_NOVCP) && // and this is not Web PnP or there is a cat to protect !((dwInstallFlags & DRVINST_WEBPNP) && !bCatInInf) && // and this is not ntprint.inf or there is a cat to protect !(IsNTPrintInf( pDrvInfo->pszInfName ) && !bCatInInf); // // How we have to call SetupCopyOEMInf to take the name of the INF which // has been copied on our system when we called SetupCommitFileQueue // if (!SetupCopyOEMInf(pDrvInfo->pszInfName, NULL, dwMediaType, SP_COPY_REPLACEONLY, szFullINFName, MAX_PATH, NULL, &pszINFName) ) { // If we can't find the original name might as well not copy or // zero if (bZeroInf && !bCopyInf) { bZeroInf = FALSE; } } else { if (bZeroInf) { bCopyInf = FALSE; } } // // If we succesfully installed a native architecture driver // then is when we copy the OEM INF file and give it a unique name // if ( bCopyInf ) { // // Earlier we used to call CopyOEMInfFileAndGiveUniqueName here // Now that Setup API has this and we are going to support CDM // we call this setup API // (VOID)SetupCopyOEMInf(pDrvInfo->pszInfName, NULL, dwMediaType, SP_COPY_NOOVERWRITE, szFullNewINFName, MAX_PATH, NULL, &pszNewINFName); // // If this fails we don't give the proverbial, since the file won't // be there // } else { if (!bZeroInf && !(Flags & DI_NOVCP)) { // // We have to remove the INF in the case of unsuccessful installation and ONLY if the DI_NOVCP // flag is not set. If the flag is set then we don't have to change the state because the file // queue hasn't been commited and the INF just has been already there before the call to our // function. // // // Remove the READONLY file attribute if set // dwOEMInfFileAttrs = GetFileAttributes( szFullINFName ); if ((dwOEMInfFileAttrs != dwGetFileAttrsError) && (dwOEMInfFileAttrs & FILE_ATTRIBUTE_READONLY)) { dwOEMInfFileAttrs &= ~FILE_ATTRIBUTE_READONLY; SetFileAttributes( szFullINFName, dwOEMInfFileAttrs); } DeleteFile( szFullINFName ); } } // Ignore the error message from SetupCopyOEMInf and DeleteFile dwStatus = bRet ? ERROR_SUCCESS : dwInstallLE; // If the install failed or this was Web Point&Print // we may need to get rid of the INF if ( bZeroInf ) { // If INFName is different then possibly set to 0 length // Or this is ntprint.inf being renamed to OEMx.inf - we want it zeroed. if (( pszINFName && (psz=FileNamePart( pDrvInfo->pszInfName )) && lstrcmp( psz, pszINFName ) ) || ( IsNTPrintInf( pDrvInfo->pszInfName ) && bCatInInf && pszNewINFName && (psz=FileNamePart( pDrvInfo->pszInfName )) && lstrcmp( psz, pszINFName ) ) ) { HANDLE hFile; // // Remove the READONLY file attribute if set // dwOEMInfFileAttrs = GetFileAttributes(szFullINFName ? szFullINFName : szFullNewINFName); if ((dwOEMInfFileAttrs != dwGetFileAttrsError) && (dwOEMInfFileAttrs & FILE_ATTRIBUTE_READONLY)) { dwOEMInfFileAttrs &= ~FILE_ATTRIBUTE_READONLY; SetFileAttributes(szFullINFName ? szFullINFName : szFullNewINFName, dwOEMInfFileAttrs); } // Open the File hFile = CreateFile( szFullINFName ? szFullINFName : szFullNewINFName, (GENERIC_READ | GENERIC_WRITE), ( FILE_SHARE_READ | FILE_SHARE_WRITE ), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); // If we opened a file if ( hFile != INVALID_HANDLE_VALUE ) { SetFilePointer( hFile, 0, 0, FILE_BEGIN ); SetEndOfFile( hFile ); CloseHandle( hFile ); } } } if ( hPrinterInf != INVALID_HANDLE_VALUE ) SetupCloseInfFile(hPrinterInf); // // Free the Driver Signing class. // if(pDSInfo) { CleanupDriverSigning(pDSInfo); } if ( !(Flags & DI_NOVCP) ) { // // The driver signing code may have associated the queue to the SP_DEVINSTALL_PARAMS. // We've finished with this and need to remove the queue from the SP_DEVINSTALL_PARAMS before we delete it. // SP_DEVINSTALL_PARAMS DevInstallParams = {0}; DevInstallParams.cbSize = sizeof(DevInstallParams); if(SetupDiGetDeviceInstallParams(hDevInfo, NULL, &DevInstallParams)) { if(DevInstallParams.FileQueue == CopyQueue) { DevInstallParams.FlagsEx &= ~DI_FLAGSEX_ALTPLATFORM_DRVSEARCH; DevInstallParams.Flags &= ~DI_NOVCP; DevInstallParams.FileQueue = INVALID_HANDLE_VALUE; SetupDiSetDeviceInstallParams(hDevInfo, NULL, &DevInstallParams); } } // // Now free up the queue. // if ( CopyQueue != INVALID_HANDLE_VALUE ) { SetupCloseFileQueue(CopyQueue); } if ( QueueContext ) { SetupTermDefaultQueueCallback(QueueContext); } if( dwAddDrvFlags & APD_COPY_FROM_DIRECTORY ) { // // if this was an installation with a path derived from the Pnp-ID, // do not cleanup, else users will get prompted for media when they re-pnp the driver // if ( ! (pLocalData->Flags & LOCALDATAFLAG_PNP_DIR_INSTALL) ) { CleanupUniqueScratchDirectory( pszServerName, platform ); } } else { CleanupScratchDirectory( pszServerName, platform ); } } // // Return the new driver name and the blocking flags if they were asked for. // if (ppszNewDriverName) { *ppszNewDriverName = pszNewDriverName; } else if (pszNewDriverName) { LocalFreeMem(pszNewDriverName); } if (pBlockingStatus) { *pBlockingStatus = fBlockingStatus; } return dwStatus; } DWORD InstallDriverAfterPromptingForInf( IN PLATFORM platform, IN LPCTSTR pszServerName, IN HWND hwnd, IN LPCTSTR pszModelName, IN DWORD dwVersion, IN OUT TCHAR szInfPath[MAX_PATH], IN DWORD dwInstallFlags, IN DWORD dwAddDrvFlags, OUT LPTSTR *ppszNewDriverName ) { DWORD dwRet, dwTitleId, dwMediaId; HDEVINFO hDevInfo = INVALID_HANDLE_VALUE; PPSETUP_LOCAL_DATA pLocalData = NULL; LPTSTR pszFileSrcPath = NULL; DWORD dwBlockingStatus = BSP_PRINTER_DRIVER_OK; switch (platform) { case PlatformAlpha: dwTitleId = IDS_DRIVERS_FOR_NT4_ALPHA; break; case PlatformX86: if( dwVersion == 2 ) { dwTitleId = IDS_DRIVERS_FOR_NT4_X86; } else { dwTitleId = IDS_DRIVERS_FOR_X86; } break; case PlatformMIPS: dwTitleId = IDS_DRIVERS_FOR_NT4_MIPS; break; case PlatformPPC: dwTitleId = IDS_DRIVERS_FOR_NT4_PPC; break; case PlatformIA64: dwTitleId = IDS_DRIVERS_FOR_IA64; break; default: ASSERT(0); return ERROR_INVALID_PARAMETER; } dwMediaId = IDS_PROMPT_ALT_PLATFORM_DRIVER; dwInstallFlags |= DRVINST_ALT_PLATFORM_INSTALL | DRVINST_NO_WARNING_PROMPT; hDevInfo = GetInfAndBuildDrivers(hwnd, dwTitleId, dwMediaId, szInfPath, dwInstallFlags, platform, dwVersion, pszModelName, &pLocalData, &pszFileSrcPath); if ( hDevInfo == INVALID_HANDLE_VALUE ) { dwRet = GetLastError(); goto Cleanup; } // // we want the printupg prompt // dwInstallFlags &= ~DRVINST_NO_WARNING_PROMPT; dwRet = InstallDriverFromCurrentInf(hDevInfo, pLocalData, hwnd, platform, dwVersion, pszServerName, INVALID_HANDLE_VALUE, NULL, NULL, 0, szInfPath, dwInstallFlags, dwAddDrvFlags, pszFileSrcPath, ppszNewDriverName, &dwBlockingStatus); if (((ERROR_PRINTER_DRIVER_BLOCKED == dwRet) || (ERROR_PRINTER_DRIVER_WARNED == dwRet)) && (ppszNewDriverName && *ppszNewDriverName) && (dwBlockingStatus & BSP_PRINTER_DRIVER_REPLACED)) { // // We pass NULL instead of pLocalData here since InstallDriverAfterPromptingForInf // is not called when we want to setup a printer queue, but when the platform // and/or version of the driver matches that of our local platform or version // does not match our dwMajorVersion (note when installing a printer queue // through the APW that uses an NT4 driver dwVersion will be set to 3 so // InstallDriverFromCurrentInf will be called instead!). Thus we do not // need to pass back e.g. print processor, color profiles and vendor setup dlls. // dwRet = InstallReplacementDriver(hwnd, pszServerName, *ppszNewDriverName, platform, dwVersion, dwInstallFlags, dwAddDrvFlags, NULL); } else if (ppszNewDriverName && *ppszNewDriverName) { LocalFreeMem(*ppszNewDriverName); *ppszNewDriverName = NULL; } Cleanup: if (pszFileSrcPath) { // // we did the NT4 copy/expand thing -> delete the expanded inf! // if(SUCCEEDED(StringCchCat(szInfPath, MAX_PATH, _T("ntprint.inf")))) { DeleteFile(szInfPath); } LocalFreeMem(pszFileSrcPath); } if ( hDevInfo != INVALID_HANDLE_VALUE ) DestroyOnlyPrinterDeviceInfoList(hDevInfo); DestroyLocalData(pLocalData); return dwRet; } const TCHAR gcszNTPrint[] = _TEXT("inf\\ntprint.inf"); DWORD GetNtprintDotInfPath(LPTSTR pszNTPrintInf, DWORD len) { DWORD dwLastError = ERROR_INVALID_DATA, dwSize, dwRemainingSize; LPTSTR pData; // // Get %windir% // If the return is 0 - the call failed. // If the return is greater than MAX_PATH we want to fail as something has managed to change // the system dir to longer than MAX_PATH which is invalid. // dwSize = GetSystemWindowsDirectory( pszNTPrintInf, len ); if( !dwSize || dwSize > len ) goto Cleanup; // // If we don't end in a \ then add one. // dwSize = _tcslen(pszNTPrintInf); pData = &(pszNTPrintInf[ dwSize-1 ]); if (*pData++ != _TEXT('\\') ) { if (dwSize + 1 < len) { *(pData++) = _TEXT('\\'); dwSize++; } else { goto Cleanup; } } dwRemainingSize = len - dwSize; *(pData) = 0; dwSize += _tcslen( gcszNTPrint ) + 1; // // If what we've got sums up to a longer string than the allowable length MAX_PATH - fail // if ( dwSize > len ) goto Cleanup; // // Copy the inf\ntprint.inf string onto the end of the %windir%\ string. // StringCchCopy( pData, dwRemainingSize, gcszNTPrint); dwLastError = ERROR_SUCCESS; Cleanup: if (dwLastError != ERROR_SUCCESS) { // // Got here due to some error. Get what the called function set the last error to. // If the function set a success, set some error code. // if ( (dwLastError = GetLastError()) == ERROR_SUCCESS) { dwLastError = ERROR_INVALID_DATA; } if (len) { pszNTPrintInf[0] = 0; } } return dwLastError; } DWORD InstallReplacementDriver(HWND hwnd, LPCTSTR pszServerName, LPCTSTR pszModelName, PLATFORM platform, DWORD version, DWORD dwInstallFlags, DWORD dwAddDrvFlags, PPSETUP_LOCAL_DATA pOldLocalData) /*++ Routine Description: Install an inbox replacement driver for blocked/warned drivers. Arguments: hwnd : parent windows handle. pszServerName : Server name to which we are installing pszModelName : driver model name to install Return Value: ERROR_SUCCESS on success, error code otherwise --*/ { HDEVINFO hDevInfo = INVALID_HANDLE_VALUE; PPSETUP_LOCAL_DATA pLocalData = NULL; TCHAR szNtprintDotInf[MAX_PATH]; DWORD dwLastError; if ((dwLastError = GetNtprintDotInfPath(szNtprintDotInf, COUNTOF(szNtprintDotInf))) != ERROR_SUCCESS) { goto Cleanup; } if ((hDevInfo = PSetupCreatePrinterDeviceInfoList(NULL)) != INVALID_HANDLE_VALUE && PSetupBuildDriversFromPath(hDevInfo, szNtprintDotInf, TRUE) && PSetupPreSelectDriver(hDevInfo, NULL, pszModelName) && (pLocalData = BuildInternalData(hDevInfo, NULL)) != NULL && ParseInf(hDevInfo, pLocalData, platform, NULL, dwInstallFlags, FALSE)) { // // Don't prompt for blocked or warned drivers. // dwInstallFlags |= DRVINST_NO_WARNING_PROMPT; dwLastError = InstallDriverFromCurrentInf( hDevInfo, pLocalData, hwnd, platform, version, pszServerName, INVALID_HANDLE_VALUE, NULL, NULL, 0, szNtprintDotInf, dwInstallFlags, dwAddDrvFlags, NULL, NULL, NULL); } else { dwLastError = GetLastError(); } Cleanup: if(pOldLocalData && pLocalData) { // // The following code is almost identical to PSetupDestroySelectedDriverInfo, // only that we do not free the memory allocated for the pOldLocalData structure. // if ( pLocalData->Flags & VALID_INF_INFO ) FreeStructurePointers((LPBYTE)&pOldLocalData->InfInfo, InfInfoOffsets, FALSE); if ( pLocalData->Flags & VALID_PNP_INFO ) FreeStructurePointers((LPBYTE)&pOldLocalData->PnPInfo, PnPInfoOffsets, FALSE); FreeStructurePointers((LPBYTE)pOldLocalData, LocalDataOffsets, FALSE); // // Now copy the new data into the old pLocalData structure // *pOldLocalData = *pLocalData; } else if(pLocalData) { PSetupDestroySelectedDriverInfo(pLocalData); } // // Release the driver setup parameter handle. // if(hDevInfo != INVALID_HANDLE_VALUE) { PSetupDestroyPrinterDeviceInfoList( hDevInfo ); } return dwLastError; } // // Paths where we search for the driver files // SPLPLATFORMINFO szPlatformExtn[] = { { TEXT("\\alpha") }, { TEXT("\\i386") }, { TEXT("\\mips") }, { TEXT("\\ppc") }, { TEXT("") }, { TEXT("\\ia64") } }; VOID GetCDRomDrive( TCHAR szDrive[5] ) { DWORD dwDrives; INT iIndex; szDrive[1] = TEXT(':'); szDrive[2] = TEXT('\\'); szDrive[3] = TEXT('\0'); dwDrives = GetLogicalDrives(); for ( iIndex = 0 ; iIndex < 26 ; ++iIndex ) if ( dwDrives & (1 << iIndex) ) { szDrive[0] = TEXT('A') + iIndex; if ( GetDriveType(szDrive) == DRIVE_CDROM ) goto Done; } szDrive[0] = TEXT('A'); Done: return; } BOOL BuildPathToPrompt( IN HDEVINFO hDevInfo, IN PPSETUP_LOCAL_DATA pLocalData, IN PLATFORM platform, IN DWORD dwVersion, IN TCHAR szPathOut[MAX_PATH] ) /*++ --*/ { LPTSTR pszExtn = TEXT(""); DWORD dwLen; // // NTRAID#NTBUG9-527971-2002/03/06-mikaelho // The function BuildPathToPromp in ntprint.dll does not take into account that x86 NT4 drivers // are delivered with the OS (on IA64 server installations) and will always prompt you for driver, // even if the OS was installed from a network share and we know where to retrieve the driver. // // // The CD we installed from OS can have only the following drivers: // -- NT5 same platform drivers // -- NT4 same platform drivers (only on server CD) // -- Win9x drivers (only on server CD) // if ( (platform == MyPlatform && dwVersion >= 2) || platform == PlatformWin95 ) { GetDriverPath(hDevInfo, pLocalData, szPathOut); } else { GetCDRomDrive(szPathOut); } if ( dwVersion >= dwThisMajorVersion && platform == MyPlatform ) return TRUE; // // append a backslash if needed // dwLen = lstrlen(szPathOut); if (dwLen && (dwLen + 1 < MAX_PATH) && (szPathOut[dwLen-1] != TEXT('\\'))) { szPathOut[dwLen] = TEXT('\\'); szPathOut[++dwLen] = 0; } switch (dwVersion) { case 0: if ( platform == PlatformWin95 ) pszExtn = TEXT("printers\\Win9X\\"); // // For NT 3.51 and 3.1 we do not include drivers on CD, so // nothing to add to the base path // case 1: break; case 2: if ( platform == PlatformX86 ) // Alpha is now on the NT4.0 CD pszExtn = TEXT("printers\\NT4\\"); break; case 3: break; default: ASSERT(dwVersion <= 3); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if ( dwLen + lstrlen(pszExtn) + lstrlen(szPlatformExtn[platform].pszName) + 1 > MAX_PATH ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } StringCchCat(szPathOut, MAX_PATH, pszExtn); // // Skip the leading \ of the platform extension as we have one already. // StringCchCat(szPathOut, MAX_PATH, &(szPlatformExtn[platform].pszName[1])); return TRUE; } DWORD PSetupInstallPrinterDriver( IN HDEVINFO hDevInfo, IN PPSETUP_LOCAL_DATA pLocalData, IN LPCTSTR pszDriverName, IN PLATFORM platform, IN DWORD dwVersion, IN LPCTSTR pszServerName, IN HWND hwnd, IN LPCTSTR pszDiskName, IN LPCTSTR pszSource OPTIONAL, IN DWORD dwInstallFlags, IN DWORD dwAddDrvFlags, OUT LPTSTR *ppszNewDriverName ) /*++ Routine Description: Copies all the necessary driver files to the printer driver directory so that an AddPrinterDriver call could be made. . This function also prompt user for disk if necessary, prompt in case of unsigned driver or if the printer driver is warned or block. In addition to printer drivers it also installs language monitors, Color Profiles and Print Processors. Arguments: hDevInfo : Handle to the printer class device information list pLocalData : Gives information got by parsing the inf pszDriverName : Printer driver name, used only if pLocalData is NULL platform : Platform for which drivers need to be installed dwVersion : Version of the driver to install pszServerName : Server on which driver should be installed hwnd : Parent windows handle for UI pszDiskName : Disk name for prompting pszSource : If provided this is a flat directory having all the files dwAddDrvFlags : Flags for AddPrinterDriverEx Return Value: On succesfully copying files ERROR_SUCCESS, else the error code --*/ { BOOL bDeleteLocalData = pLocalData == NULL; DWORD dwRet; TCHAR szPath[MAX_PATH]; szPath[0] = 0; if( !( (dwVersion == 0) || (dwVersion == 2) || (dwVersion == 3) ) ) { SetLastError(dwRet = ERROR_INVALID_PARAMETER); return dwRet; } if ( pszSource && !*pszSource ) pszSource = NULL; if ( pLocalData ) { ASSERT(pLocalData->signature == PSETUP_SIGNATURE && !pszDriverName); } else { ASSERT(pszDriverName && *pszDriverName); } // // If FLATSHARE bit is set then a path should be given // ASSERT( (dwInstallFlags & DRVINST_FLATSHARE) == 0 || pszSource != NULL ); Retry: // // If a path is given use it. Otherwise if this is a driver for different // version or platform then we determine the path, otherwise let SetupAPI determine the // path. // if ( dwVersion != dwThisMajorVersion || platform != MyPlatform ) { // If this is not an NT5 driver and we are asked to get it // from the web, then just return..... if ( pLocalData && ( pLocalData->DrvInfo.Flags & SDFLAG_CDM_DRIVER ) ) return ERROR_SUCCESS; if ( pszSource ) StringCchCopy(szPath, COUNTOF(szPath), pszSource); else if ( !BuildPathToPrompt(hDevInfo, pLocalData, platform, dwVersion, szPath) ) { if ( (dwRet = GetLastError()) == ERROR_SUCCESS ) dwRet = STG_E_UNKNOWN; return dwRet; } } // // For Win95 drivers we need to parse their INFs, // For non native environemnt dirvers ask user for path // if ( platform == PlatformWin95 ) { if ( pLocalData ) { // // Parse the inf so that we can pick up any // previous names entries and use them. // if ( !ParseInf(hDevInfo, pLocalData, MyPlatform, pszServerName, dwInstallFlags, FALSE) ) return GetLastError(); dwRet = InstallWin95Driver(hwnd, pLocalData->DrvInfo.pszModelName, pLocalData->DrvInfo.pszzPreviousNames, (pLocalData->DrvInfo.Flags & SDFLAG_PREVNAME_SECTION_FOUND), pszServerName, szPath, pszDiskName, dwInstallFlags, dwAddDrvFlags); } else { dwRet = InstallWin95Driver(hwnd, pszDriverName, NULL, TRUE, // Exact model name match only pszServerName, szPath, pszDiskName, dwInstallFlags, dwAddDrvFlags); } } else if ( dwVersion != dwThisMajorVersion || platform != MyPlatform ) { dwRet = InstallDriverAfterPromptingForInf( platform, pszServerName, hwnd, pLocalData ? pLocalData->DrvInfo.pszModelName : pszDriverName, dwVersion, szPath, dwInstallFlags, dwAddDrvFlags, ppszNewDriverName); } else if ( pLocalData && (pLocalData->DrvInfo.Flags & SDFLAG_CDM_DRIVER) ) { dwRet = PSetupInstallPrinterDriverFromTheWeb(hDevInfo, pLocalData, platform, pszServerName, &OsVersionInfo, hwnd, pszSource); } else { if ( !pLocalData ) { pLocalData = PSetupDriverInfoFromName(hDevInfo, pszDriverName); } if ( pLocalData ) { DWORD dwBlockingStatus = BSP_PRINTER_DRIVER_OK; dwRet = InstallDriverFromCurrentInf(hDevInfo, pLocalData, hwnd, platform, dwVersion, pszServerName, INVALID_HANDLE_VALUE, NULL, NULL, 0, pszSource, dwInstallFlags, dwAddDrvFlags, NULL, ppszNewDriverName, &dwBlockingStatus); if ((ppszNewDriverName && *ppszNewDriverName) && (dwBlockingStatus & BSP_PRINTER_DRIVER_REPLACED)) { dwRet = InstallReplacementDriver(hwnd, pszServerName, *ppszNewDriverName, platform, dwVersion, dwInstallFlags, dwAddDrvFlags, pLocalData); } else if (ppszNewDriverName && *ppszNewDriverName) { LocalFreeMem(*ppszNewDriverName); *ppszNewDriverName = NULL; } } else { dwRet = GetLastError(); } } if ( (dwRet == ERROR_EXE_MACHINE_TYPE_MISMATCH) && !(dwInstallFlags & DRVINST_PROMPTLESS) ) { int i; TCHAR szTitle[256], szMsg[256]; LoadString(ghInst, IDS_INVALID_DRIVER, szTitle, SIZECHARS(szTitle)); LoadString(ghInst, IDS_WRONG_ARCHITECTURE, szMsg, SIZECHARS(szMsg)); i = MessageBox(hwnd, szMsg, szTitle, MB_RETRYCANCEL | MB_ICONSTOP | MB_DEFBUTTON1 | MB_APPLMODAL); if ( i == IDRETRY ) { if ( bDeleteLocalData ) { DestroyLocalData(pLocalData); pLocalData = NULL; } goto Retry; } else { SetLastError(dwRet =ERROR_CANCELLED); } } if ( bDeleteLocalData ) DestroyLocalData(pLocalData); return dwRet; } // // SCAN_INFO structure is used with SetupScanFileQueue to find dependent files // and ICM files // typedef struct _SCAN_INFO { BOOL bWin95; PPSETUP_LOCAL_DATA pLocalData; DWORD cchDependentFiles, cchICMFiles; DWORD cchDriverDir, cchColorDir; LPTSTR p1, p2; TCHAR szDriverDir[MAX_PATH], szColorDir[MAX_PATH]; } SCAN_INFO, *PSCAN_INFO; UINT DriverInfoCallback( IN PVOID pContext, IN UINT Notification, IN UINT_PTR Param1, IN UINT_PTR Param2 ) /*++ Routine Description: This callback routine is used with SetupScanFileQueue to findout the dependent files and ICM files associated in an INF. All files going to the printer driver directory are dependent files in DRIVER_INFO_6, and all files goint to the Color directory are ICM files. We use SetupScanFileQueue twice. We find the size of the buffers required for the multi-sz fields in the first pass. After allocating buffers of size found in first pass second pass is used to copy the strings and build the multi-sz fields. Arguments: pContext : Gives the SCAN_INFO structure Notification : Ignored Param1 : Gives the target file name Param2 : Ignored Return Value: Win32 error code --*/ { DWORD dwLen; LPTSTR pszTarget = (LPTSTR)Param1, pszFileName; PSCAN_INFO pScanInfo = (PSCAN_INFO)pContext; LPDRIVER_INFO_6 pDriverInfo6; pszFileName = FileNamePart(pszTarget); if ( pszFileName ) { dwLen = lstrlen(pszFileName) + 1; if ( !lstrncmpi(pszTarget, gpszSkipDir, lstrlen( gpszSkipDir ) ) ) goto Done; if ( !lstrncmpi(pszTarget, pScanInfo->szDriverDir, pScanInfo->cchDriverDir) ) { pDriverInfo6 = &pScanInfo->pLocalData->InfInfo.DriverInfo6; // // On NT dependent file list will not include files appearing as // other DRIVER_INFO_6 fields // if ( !pScanInfo->bWin95 && ( !lstrcmpi(pszFileName, pDriverInfo6->pDriverPath) || !lstrcmpi(pszFileName, pDriverInfo6->pConfigFile) || !lstrcmpi(pszFileName, pDriverInfo6->pDataFile) || ( pDriverInfo6->pHelpFile && !lstrcmpi(pszFileName, pDriverInfo6->pHelpFile))) ) goto Done; // // If pointer is not NULL this is pass 2 // if ( pScanInfo->p1 ) { StringCchCopy(pScanInfo->p1, dwLen, pszFileName); pScanInfo->p1 += dwLen; } else { pScanInfo->cchDependentFiles += dwLen; } } else if ( !lstrncmpi(pszTarget, pScanInfo->szColorDir, pScanInfo->cchColorDir) ) { // // If pointer is not NULL this is pass 2 // if ( pScanInfo->p2 ) { StringCchCopy(pScanInfo->p2, dwLen, pszFileName); pScanInfo->p2 += dwLen; } else { pScanInfo->cchICMFiles += dwLen; } } } else { return ERROR_INVALID_PARAMETER; } Done: return NO_ERROR; } BOOL InfGetDependentFilesAndICMFiles( IN HDEVINFO hDevInfo, IN HINF hInf, IN BOOL bWin95, IN OUT PPSETUP_LOCAL_DATA pLocalData, IN PLATFORM platform, IN LPCTSTR pszServerName, IN DWORD dwInstallFlags, IN LPCTSTR pszSectionNameWithExt, IN OUT LPDWORD pcchSize ) /*++ Routine Description: Findout the dependent files for the DRIVER_INFO_6 and the ICM files for the selected driver This is done by simulating the install operation by create a setup queue to do the install operations and scanning the queue to find out where files are getting copied to Dependent files are those getting copied to driver scratch directory without including other DRIVER_INFO_6 fields like pDriverPath. For Win95 case all files getting copied to the driver directory are dependent files. ICM files are those getting copied to the color directory Arguments: hInf : INF handle bWin95 : TRUE if it is a Win95 INF pLocalData : INF parsing information pszSectionNameWithExt : Section name with extension for install pcchSize : Size needed for DRIVER_INFO_6 and strings in it Return Value: TRUE on success, FALSE on error --*/ { BOOL bRet = FALSE; DWORD dwResult; PSCAN_INFO pScanInfo = NULL; HSPFILEQ ScanQueue = INVALID_HANDLE_VALUE; LPTSTR ppszDepFiles = NULL, pszzICMFiles = NULL; PSP_DEVINSTALL_PARAMS pStoreDevInstallParams = NULL; PSP_DEVINSTALL_PARAMS pDevInstallParams = NULL; SP_ALTPLATFORM_INFO AltPlat_Info = {0}; POSVERSIONINFO pOSVer = NULL; pStoreDevInstallParams = LocalAllocMem(sizeof(SP_DEVINSTALL_PARAMS)); if(!pStoreDevInstallParams) { goto Cleanup; } pDevInstallParams = LocalAllocMem(sizeof(SP_DEVINSTALL_PARAMS)); if(!pDevInstallParams) { goto Cleanup; } pOSVer = LocalAllocMem(sizeof(OSVERSIONINFO)); if(!pOSVer) { goto Cleanup; } pScanInfo = LocalAllocMem(sizeof(SCAN_INFO)); if(!pScanInfo) { goto Cleanup; } pScanInfo->p1 = pScanInfo->p2 = NULL; pScanInfo->cchDependentFiles = pScanInfo->cchICMFiles = 0; pScanInfo->cchColorDir = sizeof(pScanInfo->szColorDir); pScanInfo->cchDriverDir = sizeof(pScanInfo->szDriverDir); if ( !GetColorDirectory( pszServerName, pScanInfo->szColorDir, &pScanInfo->cchColorDir) || !GetSystemDirectory(pScanInfo->szDriverDir, (pScanInfo->cchDriverDir / sizeof(TCHAR)) ) ) { goto Cleanup; } // // Set pScanInfo->cchColorDir to char count of pScanInfo->szColorDir without \0 // pScanInfo->cchColorDir /= sizeof(TCHAR); --pScanInfo->cchColorDir; // // Win95 INFs tell setup to copy the driver files to system32 directory // NT INFs expect install programs to set the target using // SetupSetDirectoryId // if ( bWin95 ) { pScanInfo->cchDriverDir = lstrlen(pScanInfo->szDriverDir); } else { if ( !GetPrinterDriverDirectory((LPTSTR)pszServerName, PlatformEnv[platform].pszName, 1, (LPBYTE)pScanInfo->szDriverDir, pScanInfo->cchDriverDir, &pScanInfo->cchDriverDir) ) goto Cleanup; // // Set pScanInfo->cchDriverDir to char count of pScanInfo->szDriverDir // without \0 // pScanInfo->cchDriverDir /= sizeof(TCHAR); --pScanInfo->cchDriverDir; } // // Inf MAY refer to another one (like layout.inf) // SetupOpenAppendInfFile(NULL, hInf, NULL); pScanInfo->bWin95 = bWin95; pScanInfo->pLocalData = pLocalData; ScanQueue = SetupOpenFileQueue(); if (ScanQueue == INVALID_HANDLE_VALUE) { goto Cleanup; } pDevInstallParams->cbSize = sizeof(SP_DEVINSTALL_PARAMS); if(!SetupDiGetDeviceInstallParams(hDevInfo, NULL, pDevInstallParams)) { goto Cleanup; } if(!GetOSVersion(pszServerName, pOSVer)) { goto Cleanup; } // // Save the current config... // memcpy(pStoreDevInstallParams, pDevInstallParams, sizeof(SP_DEVINSTALL_PARAMS)); pDevInstallParams->FlagsEx |= DI_FLAGSEX_ALTPLATFORM_DRVSEARCH; pDevInstallParams->Flags |= DI_NOVCP; pDevInstallParams->FileQueue = ScanQueue; AltPlat_Info.cbSize = sizeof(SP_ALTPLATFORM_INFO); AltPlat_Info.MajorVersion = pOSVer->dwMajorVersion; AltPlat_Info.MinorVersion = pOSVer->dwMinorVersion; AltPlat_Info.Platform = PlatformArch[ platform ][OS_PLATFORM]; AltPlat_Info.ProcessorArchitecture = (WORD) PlatformArch[ platform ][PROCESSOR_ARCH]; AltPlat_Info.Reserved = 0; AltPlat_Info.FirstValidatedMajorVersion = AltPlat_Info.MajorVersion; AltPlat_Info.FirstValidatedMinorVersion = AltPlat_Info.MinorVersion; if(!SetupDiSetDeviceInstallParams(hDevInfo, NULL, pDevInstallParams) || !SetupSetFileQueueAlternatePlatform(ScanQueue, &AltPlat_Info, NULL)) { goto Cleanup; } // // First pass using SetupScanFileQueue will find the sizes required // if ( !InstallAllInfSections( pLocalData, platform, pszServerName, ScanQueue, NULL, dwInstallFlags, hInf, pszSectionNameWithExt ) || !SetupScanFileQueue(ScanQueue, SPQ_SCAN_USE_CALLBACK, 0, DriverInfoCallback, pScanInfo, &dwResult) ) { goto Cleanup; } if ( pScanInfo->cchDependentFiles ) { ++pScanInfo->cchDependentFiles; ppszDepFiles = (LPTSTR) LocalAllocMem(pScanInfo->cchDependentFiles * sizeof(TCHAR)); if ( !ppszDepFiles ) goto Cleanup; pScanInfo->p1 = ppszDepFiles; } if ( pScanInfo->cchICMFiles ) { ++pScanInfo->cchICMFiles; pszzICMFiles = (LPTSTR) LocalAllocMem(pScanInfo->cchICMFiles * sizeof(TCHAR)); if ( !pszzICMFiles ) goto Cleanup; pScanInfo->p2 = pszzICMFiles; } // // Second call to SetupScanFileQueue build the actual multi-sz fields // bRet = SetupScanFileQueue(ScanQueue, SPQ_SCAN_USE_CALLBACK, 0, DriverInfoCallback, pScanInfo, &dwResult); Cleanup: // // Save the last error as it may get toasted by the following calls. // dwResult = GetLastError(); if ( ScanQueue != INVALID_HANDLE_VALUE ) { if (pDevInstallParams) { pDevInstallParams->FlagsEx &= ~DI_FLAGSEX_ALTPLATFORM_DRVSEARCH; pDevInstallParams->Flags &= ~DI_NOVCP; pDevInstallParams->FileQueue = INVALID_HANDLE_VALUE; if (!SetupDiSetDeviceInstallParams(hDevInfo, NULL, pDevInstallParams)) { dwResult = (ERROR_SUCCESS == dwResult) ? GetLastError() : dwResult; } } SetupCloseFileQueue(ScanQueue); } if (pStoreDevInstallParams) { if (pStoreDevInstallParams->cbSize == sizeof(SP_DEVINSTALL_PARAMS)) { // // Reset the HDEVINFO params. // SetupDiSetDeviceInstallParams(hDevInfo, NULL, pStoreDevInstallParams); } LocalFreeMem(pStoreDevInstallParams); } if ( bRet ) { *pcchSize += pScanInfo->cchDependentFiles; pLocalData->InfInfo.DriverInfo6.pDependentFiles = ppszDepFiles; pLocalData->InfInfo.pszzICMFiles = pszzICMFiles; } else { LocalFreeMem(ppszDepFiles); LocalFreeMem(pszzICMFiles); } if (pDevInstallParams) { LocalFreeMem(pDevInstallParams); } if (pOSVer) { LocalFreeMem(pOSVer); } if (pScanInfo) { LocalFreeMem(pScanInfo); } SetLastError(dwResult); return bRet; } VOID DestroyCodedownload( PCODEDOWNLOADINFO pCodeDownLoadInfo ) { if ( pCodeDownLoadInfo ) { pCodeDownLoadInfo->pfnClose(pCodeDownLoadInfo->hConnection); if ( pCodeDownLoadInfo->hModule ) FreeLibrary(pCodeDownLoadInfo->hModule); LocalFreeMem(pCodeDownLoadInfo); } } BOOL InitCodedownload( HWND hwnd ) { BOOL bRet = FALSE; PCODEDOWNLOADINFO pCDMInfo = NULL; EnterCriticalSection(&CDMCritSect); // We already have a context & function pointers // So reuse them... if (gpCodeDownLoadInfo) { LeaveCriticalSection(&CDMCritSect); return TRUE; } pCDMInfo = (PCODEDOWNLOADINFO) LocalAllocMem(sizeof(CODEDOWNLOADINFO)); if ( !pCDMInfo ) goto Cleanup; pCDMInfo->hModule = LoadLibraryUsingFullPath(TEXT("cdm.dll")); if ( !pCDMInfo->hModule ) goto Cleanup; (FARPROC)pCDMInfo->pfnOpen = GetProcAddress(pCDMInfo->hModule, "OpenCDMContext"); (FARPROC)pCDMInfo->pfnDownload = GetProcAddress(pCDMInfo->hModule, "DownloadUpdatedFiles"); (FARPROC)pCDMInfo->pfnClose = GetProcAddress(pCDMInfo->hModule, "CloseCDMContext"); bRet = pCDMInfo->pfnOpen && pCDMInfo->pfnDownload && pCDMInfo->pfnClose; if ( bRet ) pCDMInfo->hConnection = pCDMInfo->pfnOpen(hwnd); Cleanup: if ( !bRet || ( pCDMInfo && !pCDMInfo->hConnection ) ) { DestroyCodedownload(pCDMInfo); pCDMInfo = NULL; bRet = FALSE; } if (bRet) gpCodeDownLoadInfo = pCDMInfo; LeaveCriticalSection(&CDMCritSect); return bRet; } DWORD PSetupInstallPrinterDriverFromTheWeb( IN HDEVINFO hDevInfo, IN PPSETUP_LOCAL_DATA pLocalData, IN PLATFORM platform, IN LPCTSTR pszServerName, IN LPOSVERSIONINFO pOsVersionInfo, IN HWND hwnd, IN LPCTSTR pszSource ) /*++ Routine Description: Downloads driver files from server and installs it by calling InstallDriverFromCurrentInf. Arguments: Return Value: ERROR_SUCCESS is successful. --*/ { BOOL bRet = FALSE; DWORD dwLen, dwReturn = ERROR_SUCCESS; UINT uNeeded; TCHAR szSourceDir[MAX_PATH]; DOWNLOADINFO DownLoadInfo; PPSETUP_LOCAL_DATA pNewLocalData = NULL; INT clpFileBufferLength = 0; INT cProviderNameLength = 0; INT cManufacturerNameLength = 0; INT cDriverNameLength = 0; ZeroMemory(&DownLoadInfo, sizeof(DownLoadInfo)); if (!pLocalData || !pOsVersionInfo) { SetLastError(dwReturn = ERROR_INVALID_PARAMETER); goto Cleanup; } if ( !gpCodeDownLoadInfo ) goto Cleanup; DownLoadInfo.dwDownloadInfoSize = sizeof(DownLoadInfo); DownLoadInfo.localid = lcid; // dwLen = lstrlen( cszWebNTPrintPkg ); dwLen = lstrlen(pLocalData->DrvInfo.pszHardwareID); // // lpHardwareIDs is multi-sz // if ( !(DownLoadInfo.lpHardwareIDs = LocalAllocMem((dwLen + 2 ) * sizeof(TCHAR))) ) goto Cleanup; // lstrcpy(DownLoadInfo.lpHardwareIDs, cszWebNTPrintPkg ); StringCchCopy( (LPTSTR) DownLoadInfo.lpHardwareIDs, dwLen + 2, pLocalData->DrvInfo.pszHardwareID); CopyMemory(&DownLoadInfo.OSVersionInfo, pOsVersionInfo, sizeof(OSVERSIONINFO)); // Assign the correct Processor Architecture to the download DownLoadInfo.dwArchitecture = (WORD) PlatformArch[ platform ][PROCESSOR_ARCH]; DownLoadInfo.lpFile = NULL; // // Below we have to check if we have valid Provider, Manufacturer, and // driver names and if this is the case then we have to prepare a // MULTISZ including Provider, Manufacturer, and Driver Names, // and to set a pointer to that MULTISZ into DownLoadInfo.lpFile // if (pLocalData->DrvInfo.pszProvider && pLocalData->DrvInfo.pszManufacturer && pLocalData->DrvInfo.pszModelName) { cProviderNameLength = lstrlen(pLocalData->DrvInfo.pszProvider); if (cProviderNameLength) { cManufacturerNameLength = lstrlen(pLocalData->DrvInfo.pszManufacturer); if (cManufacturerNameLength) { cDriverNameLength = lstrlen(pLocalData->DrvInfo.pszModelName); if (cDriverNameLength) { clpFileBufferLength = cProviderNameLength + 1 + cManufacturerNameLength + 1 + cDriverNameLength + 1 + 1; DownLoadInfo.lpFile = (LPTSTR)LocalAllocMem(clpFileBufferLength * sizeof(TCHAR)); if (DownLoadInfo.lpFile) { StringCchCopy( (LPTSTR)(DownLoadInfo.lpFile), cProviderNameLength + 1, (LPTSTR)(pLocalData->DrvInfo.pszProvider)); StringCchCopy( (LPTSTR)(DownLoadInfo.lpFile + cProviderNameLength + 1), cManufacturerNameLength + 1, (LPTSTR)(pLocalData->DrvInfo.pszManufacturer)); StringCchCopy( (LPTSTR)(DownLoadInfo.lpFile + cProviderNameLength + 1 + cManufacturerNameLength + 1), cDriverNameLength + 1, (LPTSTR)(pLocalData->DrvInfo.pszModelName)); } } } } } if ( !gpCodeDownLoadInfo->pfnDownload(gpCodeDownLoadInfo->hConnection, hwnd, &DownLoadInfo, szSourceDir, SIZECHARS(szSourceDir), &uNeeded) ) goto Cleanup; // Now rework install data based on the actual INF pNewLocalData = RebuildDeviceInfo( hDevInfo, pLocalData, szSourceDir ); if ( pNewLocalData == NULL ) goto Cleanup; pNewLocalData->DrvInfo.Flags |= SDFLAG_CDM_DRIVER; dwReturn = InstallDriverFromCurrentInf(hDevInfo, pNewLocalData, hwnd, platform, dwThisMajorVersion, pszServerName, INVALID_HANDLE_VALUE, NULL, NULL, 0, szSourceDir, DRVINST_FLATSHARE | DRVINST_NO_WARNING_PROMPT, APD_COPY_NEW_FILES, NULL, NULL, NULL); (VOID) DeleteAllFilesInDirectory(szSourceDir, TRUE); if ( dwReturn == ERROR_SUCCESS ) bRet = TRUE; Cleanup: if ( pNewLocalData ) DestroyLocalData( pNewLocalData ); LocalFreeMem((PVOID)DownLoadInfo.lpHardwareIDs); LocalFreeMem((PVOID)DownLoadInfo.lpFile); CleanupScratchDirectory(pszServerName, platform); if ( !bRet && ( dwReturn == ERROR_SUCCESS ) ) dwReturn = STG_E_UNKNOWN; return dwReturn; } /*++ Routine Name: PSetupInstallInboxDriverSilently Routine Description: This is used to install inbox drivers silently. The driver must exist in ntprint.inf. The inf isn't passed in to make the only code that needs to know about ntprint.inf reside in setup. No UIs are popped up and the function will fail if UI would be required. Arguments: pszDriverName - The driver name that we want to install. Return Value: BOOL, Last error --*/ DWORD PSetupInstallInboxDriverSilently( IN LPCTSTR pszDriverName ) { DWORD Status = ERROR_SUCCESS; TCHAR szInfFile[MAX_PATH]; Status = pszDriverName ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER; // // Get the system directory. // if (Status == ERROR_SUCCESS) { Status = GetSystemWindowsDirectory(szInfFile, COUNTOF(szInfFile)) ? ERROR_SUCCESS : GetLastError(); } if (Status == ERROR_SUCCESS) { Status = StrNCatBuff(szInfFile, COUNTOF(szInfFile), szInfFile, TEXT("\\"), szNtPrintInf, NULL); } if (Status == ERROR_SUCCESS) { Status = InstallDriverSilently(szInfFile, pszDriverName, NULL); } return Status; } /*++ Routine Name: InstallDriverSilently Routine Description: Install the given printer driver from the given inf from the optional source directory, do not pop up UI and fail if UI would be required. Arguments: pszInfFile - The inf file to install the driver from. pszDriverName - The driver name. pszSource - The source installation location. Return Value: BOOL, Last error --*/ DWORD InstallDriverSilently( IN LPCTSTR pszInfFile, IN LPCTSTR pszDriverName, IN LPCTSTR pszSource ) { HDEVINFO hDevInfo = INVALID_HANDLE_VALUE; DWORD dwInstallFlags = DRVINST_PROGRESSLESS | DRVINST_PROMPTLESS; PPSETUP_LOCAL_DATA pData = NULL; DWORD Status = ERROR_SUCCESS; Status = pszInfFile && pszDriverName ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER; // // Ensure that setupapi does not throw any UI // SetupSetNonInteractiveMode(TRUE); if (Status == ERROR_SUCCESS) { if ((hDevInfo = PSetupCreatePrinterDeviceInfoList(NULL)) != INVALID_HANDLE_VALUE && PSetupBuildDriversFromPath(hDevInfo, pszInfFile, TRUE) && PSetupPreSelectDriver(hDevInfo, NULL, pszDriverName) && (pData = BuildInternalData(hDevInfo, NULL)) != NULL && ParseInf(hDevInfo, pData, MyPlatform, NULL, dwInstallFlags, FALSE)) { Status = ERROR_SUCCESS; } else { // // Ensure that if we have a failure the return is shown as such. // Status = GetLastError(); Status = Status == ERROR_SUCCESS ? ERROR_INVALID_DATA : Status; } } if (Status == ERROR_SUCCESS) { // // We don't want to launch a vendor setup entry, but vendor setup // only gets launch after an AddPrinter call, which we're not doing - just adding drivers here. // NOTE: For future if this included Queue creation we WILL have to handle this. // Status = PSetupInstallPrinterDriver(hDevInfo, pData, NULL, MyPlatform, dwThisMajorVersion, NULL, NULL, NULL, pszSource, dwInstallFlags, APD_COPY_NEW_FILES, NULL); } // // Switch on setupapi UI again // SetupSetNonInteractiveMode(FALSE); if(pData != NULL) { PSetupDestroySelectedDriverInfo(pData); } // // Release the driver setup parameter handle. // if(hDevInfo != INVALID_HANDLE_VALUE) { PSetupDestroyPrinterDeviceInfoList( hDevInfo ); } return Status; }