/*++ Copyright (c) 1995-97 Microsoft Corporation All rights reserved. Module Name: Util.c Abstract: Driver Setup UI Utility functions Author: Muhunthan Sivapragasam (MuhuntS) 06-Sep-1995 Revision History: --*/ #include "precomp.h" #include "splcom.h" #define MAX_DWORD_LENGTH 11 // // Keys to search for in ntprint.inf // TCHAR cszDataSection[] = TEXT("DataSection"); TCHAR cszDriverFile[] = TEXT("DriverFile"); TCHAR cszConfigFile[] = TEXT("ConfigFile"); TCHAR cszDataFile[] = TEXT("DataFile"); TCHAR cszHelpFile[] = TEXT("HelpFile"); TCHAR cszDefaultDataType[] = TEXT("DefaultDataType"); TCHAR cszLanguageMonitor[] = TEXT("LanguageMonitor"); TCHAR cszPrintProcessor[] = TEXT("PrintProcessor"); TCHAR cszCopyFiles[] = TEXT("CopyFiles"); TCHAR cszVendorSetup[] = TEXT("VendorSetup"); TCHAR cszVendorInstaller[] = TEXT("VendorInstaller"); TCHAR cszPreviousNamesSection[] = TEXT("Previous Names"); TCHAR cszOEMUrlSection[] = TEXT("OEM URLS"); TCHAR cszWebNTPrintPkg[] = TEXT("3FBF5B30-DEB4-11D1-AC97-00A0C903492B"); TCHAR cszAllInfs[] = TEXT("*.inf"); TCHAR cszInfExt[] = TEXT("\\*.inf"); TCHAR sComma = TEXT(','); TCHAR sHash = TEXT('@'); TCHAR sZero = TEXT('\0'); TCHAR cszSystemSetupKey[] = TEXT("System\\Setup"); TCHAR cszSystemUpgradeValue[] = TEXT("UpgradeInProgress"); TCHAR cszSystemSetupInProgress[] = TEXT("SystemSetupInProgress"); TCHAR cszMonitorKey[] = TEXT("SYSTEM\\CurrentControlSet\\Control\\Print\\Monitors\\"); // // Native environment name used by spooler // SPLPLATFORMINFO PlatformEnv[] = { { TEXT("Windows NT Alpha_AXP") }, { TEXT("Windows NT x86") }, { TEXT("Windows NT R4000") }, { TEXT("Windows NT PowerPC") }, { TEXT("Windows 4.0") }, { TEXT("Windows IA64") }, { TEXT("Windows Alpha_AXP64") } }; // // Platform override strings to be used to upgrade non-native architecture // printer drivers // SPLPLATFORMINFO PlatformOverride[] = { { TEXT("alpha") }, { TEXT("i386") }, { TEXT("mips") }, { TEXT("ppc") }, { NULL }, // Win95 { TEXT("ia64") }, { TEXT("axp64") } }; DWORD PlatformArch[][2] = { { VER_PLATFORM_WIN32_NT, PROCESSOR_ARCHITECTURE_ALPHA }, { VER_PLATFORM_WIN32_NT, PROCESSOR_ARCHITECTURE_INTEL }, { VER_PLATFORM_WIN32_NT, PROCESSOR_ARCHITECTURE_MIPS }, { VER_PLATFORM_WIN32_NT, PROCESSOR_ARCHITECTURE_PPC }, { VER_PLATFORM_WIN32_WINDOWS, PROCESSOR_ARCHITECTURE_INTEL }, { VER_PLATFORM_WIN32_NT, PROCESSOR_ARCHITECTURE_IA64 }, { VER_PLATFORM_WIN32_NT, PROCESSOR_ARCHITECTURE_ALPHA64 } }; PLATFORM MyPlatform = #if defined(_ALPHA_) PlatformAlpha; #elif defined(_MIPS_) PlatformMIPS; #elif defined(_PPC_) PlatformPPC; #elif defined(_X86_) PlatformX86; #elif defined(_IA64_) PlatformIA64; #elif defined(_AXP64_) PlatformAlpha64; #elif defined(_AMD64_) 0; // ****** fixfix ****** amd64 #else #error "No Target Architecture" #endif // Declare the CritSec for CDM CRITICAL_SECTION CDMCritSect; #define SKIP_DIR TEXT("\\__SKIP_") CRITICAL_SECTION SkipCritSect; LPTSTR gpszSkipDir = NULL; PVOID LocalAllocMem( IN UINT cbSize ) { return LocalAlloc( LPTR, cbSize ); } VOID LocalFreeMem( IN PVOID p ) { LocalFree(p); } // // For some reason these are needed by spllib when you use StrNCatBuf. // This doesn't make any sense, but just implement them. // LPVOID DllAllocSplMem( DWORD cbSize ) { return LocalAllocMem(cbSize); } BOOL DllFreeSplMem( LPVOID pMem ) { LocalFreeMem(pMem); return TRUE; } VOID PSetupFreeMem( IN PVOID p ) { LocalFreeMem(p); } LPTSTR AllocStr( LPCTSTR pszStr ) /*++ Routine Description: Allocate memory and make a copy of a string field Arguments: pszStr : String to copy Return Value: Pointer to the copied string. Memory is allocated. --*/ { LPTSTR pszRet = NULL; if ( pszStr && *pszStr ) { pszRet = LocalAllocMem((lstrlen(pszStr) + 1) * sizeof(*pszRet)); if ( pszRet ) lstrcpy(pszRet, pszStr); } return pszRet; } LPTSTR AllocStrWCtoTC(LPWSTR lpStr) /*++ Routine Description: Allocate memory and make a copy of a string field, convert it from a WCHAR * to a TCHAR * Arguments: pszStr : String to copy Return Value: Pointer to the copied string. Memory is allocated. --*/ { #ifdef UNICODE return AllocStr(lpStr); #else LPSTR pszRet = NULL; INT iSize; iSize = WideCharToMultiByte( CP_ACP, 0, lpStr, -1, NULL, 0, NULL, NULL); if (iSize <= 0) goto Cleanup; pszRet = LocalAllocMem( iSize ); if (!pszRet) goto Cleanup; iSize = WideCharToMultiByte( CP_ACP, 0, lpStr, -1, pszRet, iSize, NULL, NULL); if (!iSize) { LocalFreeMem( pszRet ); pszRet = NULL; } Cleanup: return pszRet; #endif } LPTSTR AllocAndCatStr( LPCTSTR pszStr1, LPCTSTR pszStr2 ) /*++ Routine Description: Allocate memory and make a copy of two string fields, cancatenate the second to the first Arguments: pszStr1 : String to copy pszStr2 : String to CAT Return Value: Pointer to the copied string. Memory is allocated. --*/ { LPTSTR pszRet = NULL; if ( pszStr1 && *pszStr1 && pszStr2 && *pszStr2 ) { pszRet = LocalAllocMem((lstrlen(pszStr1) + lstrlen(pszStr2) + 1) * sizeof(*pszRet)); if ( pszRet ) { lstrcpy( pszRet, pszStr1 ); lstrcat( pszRet, pszStr2 ); } } return pszRet; } LPTSTR AllocAndCatStr2( LPCTSTR pszStr1, LPCTSTR pszStr2, LPCTSTR pszStr3 ) /*++ Routine Description: Allocate memory and make a copy of two string fields, cancatenate the second to the first Arguments: pszStr1 : String to copy pszStr2 : String to CAT pszStr3 : Second string to CAT Return Value: Pointer to the copied string. Memory is allocated. --*/ { LPTSTR pszRet = NULL; DWORD cSize = 0; if ( pszStr1 && pszStr2 && pszStr3 ) { if(*pszStr1) { cSize += lstrlen(pszStr1); } if(*pszStr2) { cSize += lstrlen(pszStr2); } if(*pszStr3) { cSize += lstrlen(pszStr3); } pszRet = LocalAllocMem((cSize+1)*sizeof(*pszRet)); if ( pszRet ) { if(*pszStr1) { lstrcpy( pszRet, pszStr1 ); if(*pszStr2) { lstrcat( pszRet, pszStr2 ); } if(*pszStr3) { lstrcat( pszRet, pszStr3 ); } } else { if(*pszStr2) { lstrcpy( pszRet, pszStr2 ); if(*pszStr3) { lstrcat( pszRet, pszStr3 ); } } else { if(*pszStr3) { lstrcpy( pszRet, pszStr3 ); } } } } } return pszRet; } VOID FreeStructurePointers( LPBYTE pStruct, PULONG_PTR pOffsets, BOOL bFreeStruct ) /*++ Routine Description: Frees memory allocated to fields given by the pointers in a structure. Also optionally frees the memory allocated for the structure itself. Arguments: pStruct : Pointer to the structure pOffsets : Array of DWORDS (terminated by -1) givings offsets bFreeStruct : If TRUE structure is also freed Return Value: nothing --*/ { INT i; if ( pStruct ) { for( i = 0 ; pOffsets[i] != -1; ++i ) LocalFreeMem(*(LPBYTE *) (pStruct+pOffsets[i])); if ( bFreeStruct ) LocalFreeMem(pStruct); } } VOID DestroyLocalData( IN PPSETUP_LOCAL_DATA pLocalData ) { if ( pLocalData ) { if ( pLocalData->Flags & VALID_INF_INFO ) FreeStructurePointers((LPBYTE)&pLocalData->InfInfo, InfInfoOffsets, FALSE); if ( pLocalData->Flags & VALID_PNP_INFO ) FreeStructurePointers((LPBYTE)&pLocalData->PnPInfo, PnPInfoOffsets, FALSE); FreeStructurePointers((LPBYTE)pLocalData, LocalDataOffsets, TRUE); } } VOID InfGetString( IN PINFCONTEXT pInfContext, IN DWORD dwFieldIndex, OUT LPTSTR *ppszField, IN OUT LPDWORD pcchCopied, IN OUT LPBOOL pbFail ) /*++ Routine Description: Allocates memory and gets a string field from an Inf file Arguments: lpInfContext : Inf context for the line dwFieldIndex : Index of the field within the specified line ppszField : Pointer to the field to allocate memory and copy pcchCopied : On success number of charaters copied is added pbFail : Set on error, could be TRUE when called Return Value: Nothing; If *pbFail is not TRUE memory is allocated and the field is copied --*/ { TCHAR Buffer[MAX_PATH]; DWORD dwNeeded; if ( *pbFail ) return; if ( SetupGetStringField(pInfContext, dwFieldIndex, Buffer, SIZECHARS(Buffer), &dwNeeded) ) { *ppszField = AllocStr(Buffer); *pcchCopied += dwNeeded; return; } if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER || !(*ppszField = LocalAllocMem(dwNeeded*sizeof(Buffer[0]))) ) { *pbFail = TRUE; return; } if ( !SetupGetStringField(pInfContext, dwFieldIndex, *ppszField, dwNeeded, &dwNeeded) ) { *pbFail = TRUE; return; } *pcchCopied += dwNeeded; } VOID InfGetMultiSz( IN PINFCONTEXT pInfContext, IN DWORD dwFieldIndex, OUT LPTSTR *ppszField, IN OUT LPDWORD pcchCopied, IN OUT LPBOOL pbFail ) /*++ Routine Description: Allocates memory and gets a multi-sz field from an Inf file Arguments: lpInfContext : Inf context for the line dwFieldIndex : Index of the field within the specified line ppszField : Pointer to the field to allocate memory and copy pcchCopied : On success number of charaters copied is added pbFail : Set on error, could be TRUE when called Return Value: Nothing; If *pbFail is not TRUE memory is allocated and the field is copied --*/ { TCHAR Buffer[MAX_PATH]; DWORD dwNeeded; if ( *pbFail ) return; if ( SetupGetMultiSzField(pInfContext, dwFieldIndex, Buffer, SIZECHARS(Buffer), &dwNeeded) ) { if ( *ppszField = LocalAllocMem(dwNeeded*sizeof(Buffer[0])) ) { CopyMemory(*ppszField, Buffer, dwNeeded * sizeof(Buffer[0])); *pcchCopied += dwNeeded; } else { *pbFail = TRUE; } return; } if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER || !(*ppszField = LocalAllocMem(dwNeeded * sizeof(Buffer[0]))) ) { *pbFail = TRUE; return; } if ( !SetupGetMultiSzField(pInfContext, dwFieldIndex, *ppszField, dwNeeded, &dwNeeded) ) { *pbFail = TRUE; return; } *pcchCopied += dwNeeded; } VOID InfGetDriverInfoString( IN HINF hInf, IN LPCTSTR pszDriverSection, IN LPCTSTR pszDataSection, OPTIONAL IN BOOL bDataSection, IN LPCTSTR pszKey, OUT LPTSTR *ppszData, IN LPCTSTR pszDefaultData, IN OUT LPDWORD pcchCopied, IN OUT LPBOOL pbFail ) /*++ Routine Description: Allocates memory and gets a driver info field from an inf file Arguments: hInf : Handle to the Inf file pszDriverSection : Section name for the driver pszDataSection : Data section for the driver (optional) bDataSection : Specifies if there is a data section pszKey : Key value of the field to look for *ppszData : Pointer to allocate memory and copy the data field pszDefaultData : If key found this is the default value, coule be NULL pcchCopied : On success number of charaters copied is added *pbFail : Set on error, could be TRUE when called Return Value: Nothing; If *pbFail is not TRUE memory is allocated and the field is copied --*/ { INFCONTEXT InfContext; if ( *pbFail ) return; if ( SetupFindFirstLine(hInf, pszDriverSection, pszKey, &InfContext) || (bDataSection && SetupFindFirstLine(hInf, pszDataSection, pszKey, &InfContext)) ) { InfGetString(&InfContext, 1, ppszData, pcchCopied, pbFail); } else if ( pszDefaultData && *pszDefaultData ) { if ( !(*ppszData = AllocStr(pszDefaultData)) ) *pbFail = TRUE; else *pcchCopied += lstrlen(pszDefaultData) + 1; } else *ppszData = NULL; } VOID InfGet2PartString( IN HINF hInf, IN LPCTSTR pszDriverSection, IN LPCTSTR pszDataSection, OPTIONAL IN BOOL bDataSection, IN LPCTSTR pszKey, OUT LPTSTR *ppszData, IN OUT LPBOOL pbFail ) /*++ Routine Description: Allocates memory and gets a 2 part string field from an inf file Arguments: hInf : Handle to the Inf file pszDriverSection : Section name for the driver pszDataSection : Data section for the driver (optional) bDataSection : Specifies if there is a data section pszKey : Key value of the field to look for *ppszData : Pointer to allocate memory and copy the data field *pbFail : Set on error, could be TRUE when called Return Value: Nothing; If *pbFail is not TRUE memory is allocated and the field is copied --*/ { INFCONTEXT InfContext; LPTSTR psz = NULL, psz2 = NULL; DWORD dwLen = 0; if ( *pbFail ) return; if ( SetupFindFirstLine(hInf, pszDriverSection, pszKey, &InfContext) || (bDataSection && SetupFindFirstLine(hInf, pszDriverSection = pszDataSection, pszKey, &InfContext)) ) { InfGetString(&InfContext, 1, ppszData, &dwLen, pbFail); if ( *pbFail || !*ppszData ) return; //Success, field is NULL // // Usual case : field is of the form "Description, DLL-Name" // if ( psz = lstrchr(*ppszData, sComma) ) { *psz = sZero; return; // Success, field is not NULL } else { // // This is for the case "Description", DLL-Name // InfGetString(&InfContext, 2, &psz, &dwLen, pbFail); if ( *pbFail || !psz ) goto Fail; dwLen = lstrlen(*ppszData) + lstrlen(psz) + 2; if ( psz2 = LocalAllocMem(dwLen * sizeof(*psz2)) ) { lstrcpy(psz2, *ppszData); LocalFreeMem(*ppszData); *ppszData = psz2; psz2 += lstrlen(psz2) + 1; lstrcpy(psz2, psz); LocalFreeMem(psz); } else goto Fail; } } else *ppszData = NULL; return; Fail: LocalFreeMem(*ppszData); LocalFreeMem(psz); LocalFreeMem(psz2); *ppszData = NULL; *pbFail = TRUE; SetLastError(STG_E_UNKNOWN); } VOID PSetupDestroyDriverInfo3( IN LPDRIVER_INFO_3 pDriverInfo3 ) /*++ Routine Description: Frees memory allocated for a DRIVER_INFO_3 structure and all the string fields in it Arguments: pDriverInfo3 : Pointer to the DRIVER_INFO_3 structure to free memory Return Value: None --*/ { LocalFreeMem(pDriverInfo3); } LPTSTR PackString( IN LPTSTR pszEnd, IN LPTSTR pszSource, IN LPTSTR *ppszTarget ) /*++ Routine Description: After parsing the INF the DRIVER_INFO_6 is packed in a buffer where the strings are at the end. Arguments: pszEnd : Pointer to the end of the buffer pszSource : String to copy to the end of the buffer ppszTarget : After copying the source to end of buffer this will receive addess of the packed string Return Value: New end of buffer --*/ { if ( pszSource && *pszSource ) { pszEnd -= lstrlen(pszSource) + 1; lstrcpy(pszEnd, pszSource); *ppszTarget = pszEnd; } return pszEnd; } LPTSTR PackMultiSz( IN LPTSTR pszEnd, IN LPTSTR pszzSource, IN LPTSTR *ppszzTarget ) /*++ Routine Description: After parsing the INF the DRIVER_INFO_6 is packed in a buffer where the strings are at the end. Arguments: pszEnd : Pointer to the end of the buffer pszSource : Multi-sz field to copy to the end of the buffer ppszTarget : After copying the source to end of buffer this will receive addess of the packed multi-sz field Return Value: New end of buffer --*/ { size_t dwLen = 0; LPTSTR psz1, psz2; if ( (psz1 = pszzSource) != NULL && *psz1 ) { while ( *psz1 ) psz1 += lstrlen(psz1) + 1; dwLen = (size_t)(psz1 - pszzSource + 1); } if ( dwLen == 0 ) { *ppszzTarget = NULL; return pszEnd; } pszEnd -= dwLen; *ppszzTarget = pszEnd; CopyMemory((LPBYTE)pszEnd, (LPBYTE)pszzSource, dwLen * sizeof(TCHAR)); return pszEnd; } VOID PackDriverInfo6( IN LPDRIVER_INFO_6 pSourceDriverInfo6, IN LPDRIVER_INFO_6 pTargetDriverInfo6, IN DWORD cbDriverInfo6 ) /*++ Routine Description: Make a copy of a DRIVER_INFO_6 in a buffer where the strings are packed at end of the buffer. Arguments: pSourceDriverInfo6 : The DRIVER_INFO_6 to make a copy pTargetDriverInfo6 : Points to a buffer to copy the pSourceDriverInfo6 cbDriverInfo3 : Size of the buffer cbDriverInfo3, which is the size needed for DRIVER_INFO_6 and the strings Return Value: None --*/ { LPTSTR pszStr, pszStr2, pszMonitorDll; DWORD dwLen = 0; // Copy over he couple non-string fields pTargetDriverInfo6->cVersion = pSourceDriverInfo6->cVersion; pTargetDriverInfo6->ftDriverDate = pSourceDriverInfo6->ftDriverDate; pTargetDriverInfo6->dwlDriverVersion = pSourceDriverInfo6->dwlDriverVersion; pszStr = (LPTSTR)(((LPBYTE)pTargetDriverInfo6) + cbDriverInfo6); pszStr = PackString(pszStr, pSourceDriverInfo6->pName, &pTargetDriverInfo6->pName); pszStr = PackString(pszStr, pSourceDriverInfo6->pDriverPath, &pTargetDriverInfo6->pDriverPath); pszStr = PackString(pszStr, pSourceDriverInfo6->pDataFile, &pTargetDriverInfo6->pDataFile); pszStr = PackString(pszStr, pSourceDriverInfo6->pConfigFile, &pTargetDriverInfo6->pConfigFile); pszStr = PackString(pszStr, pSourceDriverInfo6->pHelpFile, &pTargetDriverInfo6->pHelpFile); // // Monitor dll is put right after the name // (ex. PJL Language monitor\0pjlmon.dd\0) // if ( pSourceDriverInfo6->pMonitorName ) { pszMonitorDll = pSourceDriverInfo6->pMonitorName + lstrlen(pSourceDriverInfo6->pMonitorName) + 1; pszStr = PackString(pszStr, pszMonitorDll, &pszStr2); // Don't care pszStr = PackString(pszStr, pSourceDriverInfo6->pMonitorName, &pTargetDriverInfo6->pMonitorName); } pszStr = PackString(pszStr, pSourceDriverInfo6->pDefaultDataType, &pTargetDriverInfo6->pDefaultDataType); pszStr = PackMultiSz(pszStr, pSourceDriverInfo6->pDependentFiles, &pTargetDriverInfo6->pDependentFiles); pszStr = PackMultiSz(pszStr, pSourceDriverInfo6->pszzPreviousNames, &pTargetDriverInfo6->pszzPreviousNames); pszStr = PackString(pszStr, pSourceDriverInfo6->pszMfgName, &pTargetDriverInfo6->pszMfgName); pszStr = PackString(pszStr, pSourceDriverInfo6->pszOEMUrl, &pTargetDriverInfo6->pszOEMUrl); pszStr = PackString(pszStr, pSourceDriverInfo6->pszHardwareID, &pTargetDriverInfo6->pszHardwareID); pszStr = PackString(pszStr, pSourceDriverInfo6->pszProvider, &pTargetDriverInfo6->pszProvider); if ( pTargetDriverInfo6->pszProvider ) { ASSERT((LPBYTE)pTargetDriverInfo6->pszProvider >= ((LPBYTE) pTargetDriverInfo6) + sizeof(DRIVER_INFO_6)); } else if ( pTargetDriverInfo6->pszHardwareID ) { ASSERT((LPBYTE)pTargetDriverInfo6->pszHardwareID >= ((LPBYTE) pTargetDriverInfo6) + sizeof(DRIVER_INFO_6)); } else if ( pTargetDriverInfo6->pszOEMUrl ) { ASSERT((LPBYTE)pTargetDriverInfo6->pszOEMUrl >= ((LPBYTE) pTargetDriverInfo6) + sizeof(DRIVER_INFO_6)); } else if ( pTargetDriverInfo6->pszMfgName ) { ASSERT((LPBYTE)pTargetDriverInfo6->pszMfgName >= ((LPBYTE) pTargetDriverInfo6) + sizeof(DRIVER_INFO_6)); } else if ( pTargetDriverInfo6->pszzPreviousNames ) { ASSERT((LPBYTE)pTargetDriverInfo6->pszzPreviousNames >= ((LPBYTE) pTargetDriverInfo6) + sizeof(DRIVER_INFO_6)); } else if ( pTargetDriverInfo6->pDependentFiles ) { ASSERT((LPBYTE)pTargetDriverInfo6->pDependentFiles >= ((LPBYTE) pTargetDriverInfo6) + sizeof(DRIVER_INFO_6)); } } LPDRIVER_INFO_6 CloneDriverInfo6( IN LPDRIVER_INFO_6 pSourceDriverInfo6, IN DWORD cbDriverInfo6 ) /*++ Routine Description: Allocate memory and build a DRIVER_INFO_6 from the information we got by parsing the INF Arguments: pszEnd : Pointer to the end of the buffer pszSource : String to copy to the end of the buffer ppszTarget : After copying the source to end of buffer this will receive addess of the packed string Return Value: New end of buffer --*/ { LPDRIVER_INFO_6 pTargetDriverInfo6; LPTSTR pszStr, pszStr2; DWORD dwLen = 0; pTargetDriverInfo6 = (LPDRIVER_INFO_6) LocalAllocMem(cbDriverInfo6); if ( pTargetDriverInfo6 ) PackDriverInfo6(pSourceDriverInfo6, pTargetDriverInfo6, cbDriverInfo6); return pTargetDriverInfo6; } VOID InfGetVendorSetup( IN OUT PPARSEINF_INFO pInfInfo, IN HINF hInf, IN LPTSTR pszDriverSection, IN LPTSTR pszDataSection, IN BOOL bDataSection, IN OUT LPBOOL pbFail ) /*++ Routine Description: Get the VendowSetup field, if specified, from the INF Arguments: pInfInfo : This is where the parsed info from the INF is stored hInf : INF handle pszDriverSection : Gives the driver installation section pszDataSection : Data section specified (optional) in driver install section bDataSection : Tells if a data section is specified pbFail : Set on error Return Value: New end of buffer --*/ { LPTSTR p; DWORD dwSize; TCHAR szBuf[MAX_PATH]; INFCONTEXT InfContext; if ( *pbFail ) return; // // If VendorSetup key is not found return; the key is optional // if ( !SetupFindFirstLine(hInf, pszDriverSection, cszVendorSetup, &InfContext) && ( !bDataSection || !SetupFindFirstLine(hInf, pszDataSection, cszVendorSetup, &InfContext)) ) { return; } if ( SetupGetLineText(&InfContext, hInf, NULL, NULL, szBuf, SIZECHARS(szBuf), &dwSize) ) { if ( dwSize == 0 || szBuf[0] == TEXT('\0') ) return; if ( !(pInfInfo->pszVendorSetup = AllocStr(szBuf)) ) *pbFail = TRUE; return; } if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) { *pbFail = TRUE; return; } pInfInfo->pszVendorSetup = (LPTSTR) LocalAllocMem(dwSize * sizeof(TCHAR)); if ( pInfInfo->pszVendorSetup && SetupGetLineText(&InfContext, hInf, NULL, NULL, pInfInfo->pszVendorSetup, dwSize, &dwSize) ) { return; } LocalFreeMem(pInfInfo->pszVendorSetup); pInfInfo->pszVendorSetup = NULL; *pbFail = TRUE; } VOID InfGetPreviousNames( IN HINF hInf, IN PSELECTED_DRV_INFO pDrvInfo, IN OUT LPDWORD pcchCopied, IN OUT LPBOOL pbFail ) /*++ Routine Description: Gets the pszzPreviousNames field for the selected driver. This field is optional, and if specified gives previous names under which the driver was known Arguments: hInf : INF handle pDrvInfo : Pointer to selected driver info pcchCopied : Number of characters copied pbFail : Set on failure Return Value: None. On failure *pbFail is set --*/ { INFCONTEXT Context; if ( *pbFail ) return; // // Previous names is optional; if not found we are done // if ( SetupFindFirstLine(hInf, cszPreviousNamesSection, pDrvInfo->pszModelName, &Context) ) { pDrvInfo->Flags |= SDFLAG_PREVNAME_SECTION_FOUND; InfGetMultiSz(&Context, 1, &pDrvInfo->pszzPreviousNames, pcchCopied, pbFail); } else if ( GetLastError() != ERROR_LINE_NOT_FOUND ) pDrvInfo->Flags |= SDFLAG_PREVNAME_SECTION_FOUND; } VOID InfGetOEMUrl( IN HINF hInf, IN PSELECTED_DRV_INFO pDrvInfo, IN OUT LPBOOL pbFail ) /*++ Routine Description: Gets the OEM URL Info for the selected driver. This field is optional Arguments: hInf : INF handle pDrvInfo : Pointer to selected driver info pbFail : Set on failure Return Value: None. On failure *pbFail is set --*/ { INFCONTEXT Context; DWORD dwDontCare = 0; if ( *pbFail ) return; // // OEM URL is optional; if not found we are done // if ( SetupFindFirstLine(hInf, cszOEMUrlSection, pDrvInfo->pszManufacturer, &Context) ) { InfGetString(&Context, 1, &pDrvInfo->pszOEMUrl, &dwDontCare, pbFail); } } VOID AddAllIncludedInf( IN HINF hInf, IN LPTSTR pszInstallSection ) { INFCONTEXT INFContext; PINFCONTEXT pINFContext = &INFContext; DWORD dwBufferNeeded; if ( SetupFindFirstLine( hInf, pszInstallSection, TEXT( "Include" ), pINFContext ) ) { // Find each INF and load it & it's LAYOUT files DWORD dwINFs = SetupGetFieldCount( pINFContext ); DWORD dwIndex; for ( dwIndex = 1; dwIndex <= dwINFs; dwIndex++ ) { if ( SetupGetStringField( pINFContext, dwIndex, NULL, 0, &dwBufferNeeded ) ) { PTSTR pszINFName = (PTSTR) LocalAllocMem( dwBufferNeeded * sizeof(TCHAR) ); if ( pszINFName ) { if ( SetupGetStringField( pINFContext, dwIndex, pszINFName, ( dwBufferNeeded * sizeof(TCHAR) ), &dwBufferNeeded ) ) { // // Open INF file and append layout.inf specified in Version section // Layout inf is optional // SetupOpenAppendInfFile( pszINFName, hInf, NULL); SetupOpenAppendInfFile( NULL, hInf, NULL); } // Got an INF Name LocalFreeMem( pszINFName ); pszINFName = NULL; } // Allocated pszINFName } // Got the Field from the INF Line } // Process all INFs in the Include Line } // Found an Include= Line } BOOL InstallAllInfSections( IN PPSETUP_LOCAL_DATA pLocalData, IN PLATFORM platform, IN LPCTSTR pszServerName, IN HSPFILEQ CopyQueue, IN LPCTSTR pszSource, IN DWORD dwInstallFlags, IN HINF hInf, IN LPCTSTR pszInstallSection ) { BOOL bRet = FALSE; HINF hIncludeInf; INFCONTEXT INFContext; PINFCONTEXT pINFContext = &INFContext; INFCONTEXT NeedsContext; PINFCONTEXT pNeedsContext = &NeedsContext; DWORD dwBufferNeeded; PTSTR pszINFName = NULL; PTSTR pszSectionName = NULL; if ( CopyQueue == INVALID_HANDLE_VALUE || !SetTargetDirectories( pLocalData, platform, pszServerName, hInf, dwInstallFlags ) || !SetupInstallFilesFromInfSection( hInf, NULL, CopyQueue, pszInstallSection, pszSource, SP_COPY_LANGUAGEAWARE) ) goto Cleanup; // To get the source directories correct, we need to load all included INFs // separately. THen use their associated layout files. if ( SetupFindFirstLine( hInf, pszInstallSection, TEXT( "Include" ), pINFContext ) ) { // Find each INF and load it & it's LAYOUT files DWORD dwINFs = SetupGetFieldCount( pINFContext ); DWORD dwIIndex; for ( dwIIndex = 1; dwIIndex <= dwINFs; dwIIndex++ ) { if ( SetupGetStringField( pINFContext, dwIIndex, NULL, 0, &dwBufferNeeded ) ) { pszINFName = (PTSTR) LocalAllocMem( dwBufferNeeded * sizeof(TCHAR) ); if ( pszINFName ) { if ( SetupGetStringField( pINFContext, dwIIndex, pszINFName, ( dwBufferNeeded * sizeof(TCHAR) ), &dwBufferNeeded ) ) { // // Open INF file and append layout.inf specified in Version section // Layout inf is optional // // SetupOpenAppendInfFile( pszINFName, hPrinterInf, NULL); hIncludeInf = SetupOpenInfFile(pszINFName, NULL, INF_STYLE_WIN4, NULL); if ( hIncludeInf == INVALID_HANDLE_VALUE ) goto Cleanup; SetupOpenAppendInfFile( NULL, hIncludeInf, NULL); // Now process all need sections for this INF // Now find the Needs Line and install all called sections if ( SetupFindFirstLine( hInf, pszInstallSection, TEXT( "needs" ), pNeedsContext ) ) { // Find each INF and load it & it's LAYOUT files DWORD dwSections = SetupGetFieldCount( pNeedsContext ); DWORD dwNIndex; for ( dwNIndex = 1; dwNIndex <= dwSections; dwNIndex++ ) { if ( SetupGetStringField( pNeedsContext, dwNIndex, NULL, 0, &dwBufferNeeded ) ) { pszSectionName = (PTSTR) LocalAllocMem( dwBufferNeeded * sizeof(TCHAR) ); if ( pszSectionName ) { if ( SetupGetStringField( pNeedsContext, dwNIndex, pszSectionName, ( dwBufferNeeded * sizeof(TCHAR) ), &dwBufferNeeded ) ) { if ( SetTargetDirectories(pLocalData, platform, pszServerName, hIncludeInf, dwInstallFlags) ) { if ( !SetupInstallFilesFromInfSection( hIncludeInf, NULL, CopyQueue, pszSectionName, NULL, SP_COPY_LANGUAGEAWARE) ) goto Cleanup; } // Able to setup Target Dirs else goto Cleanup; } // Got a Section Name LocalFreeMem( pszSectionName ); pszSectionName = NULL; } // Allocated pszSectionName } // Got the Field from the Section Line } // Process all Sections in the Needs Line } // Found a Needs= Line // Close included INF if ( hIncludeInf != INVALID_HANDLE_VALUE ) SetupCloseInfFile(hIncludeInf); } // Got an INF Name LocalFreeMem( pszINFName ); pszINFName = NULL; } // Allocated pszINFName } // Got the Field from the INF Line } // Process all INFs in the Include Line } // Found an Include= Line bRet = TRUE; Cleanup: if ( pszINFName ) LocalFreeMem( pszINFName ); if ( pszSectionName ) LocalFreeMem( pszSectionName ); return bRet; } BOOL ParseInf( IN HDEVINFO hDevInfo, IN PPSETUP_LOCAL_DATA pLocalData, IN PLATFORM platform, IN LPCTSTR pszServerName, IN DWORD dwInstallFlags ) /*++ Routine Description: Copies driver information from an Inf file to a DriverInfo3 structure. The following fields are filled on successful return pName pDriverPath pDataFile pConfigFile pHelpFile pMonitorName pDefaultDataType Arguments: pLocalData : platform : Platform for which inf should be parsed Return Value: TRUE -- Succesfully parsed the inf and built info for the selected driver FALSE -- On Error --*/ { PPARSEINF_INFO pInfInfo = &pLocalData->InfInfo; PDRIVER_INFO_6 pDriverInfo6 = &pLocalData->InfInfo.DriverInfo6; LPTSTR pszDataSection, psz, pszInstallSection; BOOL bWin95 = platform == PlatformWin95, bFail = TRUE, bDataSection = FALSE; INFCONTEXT Context; DWORD cchDriverInfo6, dwNeeded, dwDontCare; HINF hInf; // // Check if INF is already parsed, and if so for the right platform // if ( pLocalData->Flags & VALID_INF_INFO ) { if ( platform == pInfInfo->platform ) return TRUE; FreeStructurePointers((LPBYTE)pInfInfo, InfInfoOffsets, FALSE); pLocalData->Flags &= ~VALID_INF_INFO; ZeroMemory(pInfInfo, sizeof(*pInfInfo)); } pszDataSection = NULL; hInf = SetupOpenInfFile(pLocalData->DrvInfo.pszInfName, NULL, INF_STYLE_WIN4, NULL); if ( hInf == INVALID_HANDLE_VALUE ) goto Cleanup; if ( bWin95 ) { pszInstallSection = AllocStr(pLocalData->DrvInfo.pszDriverSection); if ( !pszInstallSection ) goto Cleanup; } else { if ( !SetupSetPlatformPathOverride(PlatformOverride[platform].pszName) ) goto Cleanup; if ( !SetupDiGetActualSectionToInstall( hInf, pLocalData->DrvInfo.pszDriverSection, NULL, 0, &dwNeeded, NULL) || !(pInfInfo->pszInstallSection = (LPTSTR) LocalAllocMem(dwNeeded * sizeof(TCHAR))) || !SetupDiGetActualSectionToInstall( hInf, pLocalData->DrvInfo.pszDriverSection, pInfInfo->pszInstallSection, dwNeeded, NULL, NULL) ) { SetupSetPlatformPathOverride(NULL); goto Cleanup; } SetupSetPlatformPathOverride(NULL); pszInstallSection = pInfInfo->pszInstallSection; } // // Now load all other INFs referenced in the Install Section // AddAllIncludedInf( hInf, pszInstallSection ); if ( !(pDriverInfo6->pName = AllocStr(pLocalData->DrvInfo.pszModelName)) ) goto Cleanup; bFail = FALSE; if(bFail) { goto Cleanup; } // // Does the driver section have a data section name specified? // if ( SetupFindFirstLine(hInf, pszInstallSection, cszDataSection, &Context) ) { InfGetString(&Context, 1, &pszDataSection, &dwDontCare, &bFail); bDataSection = TRUE; } cchDriverInfo6 = lstrlen(pDriverInfo6->pName) + 1; // // If DataFile key is not found data file is same as driver section name // InfGetDriverInfoString(hInf, pszInstallSection, pszDataSection, bDataSection, cszDataFile, &pDriverInfo6->pDataFile, pszInstallSection, &cchDriverInfo6, &bFail); // // If DriverFile key is not found driver file is the driver section name // InfGetDriverInfoString(hInf, pszInstallSection, pszDataSection, bDataSection, cszDriverFile, &pDriverInfo6->pDriverPath, pszInstallSection, &cchDriverInfo6, &bFail); // // If ConfigFile key is not found config file is same as driver file // InfGetDriverInfoString(hInf, pszInstallSection, pszDataSection, bDataSection, cszConfigFile, &pDriverInfo6->pConfigFile, pDriverInfo6->pDriverPath, &cchDriverInfo6, &bFail); // // Help file is optional, and by default NULL // InfGetDriverInfoString(hInf, pszInstallSection, pszDataSection, bDataSection, cszHelpFile, &pDriverInfo6->pHelpFile, NULL, &cchDriverInfo6, &bFail); // // Monitor name is optional, and by default none // InfGet2PartString(hInf, pszInstallSection, pszDataSection, bDataSection, cszLanguageMonitor, &pDriverInfo6->pMonitorName, &bFail); if ( psz = pDriverInfo6->pMonitorName ) { psz += lstrlen(psz) + 1; cchDriverInfo6 += lstrlen(pDriverInfo6->pMonitorName) + lstrlen(psz) + 2; } // // Print processor is optional, and by default none // InfGet2PartString(hInf, pszInstallSection, pszDataSection, bDataSection, cszPrintProcessor, &pLocalData->InfInfo.pszPrintProc, &bFail); // // Default data type is optional, and by default none // InfGetDriverInfoString(hInf, pszInstallSection, pszDataSection, bDataSection, cszDefaultDataType, &pDriverInfo6->pDefaultDataType, NULL, &cchDriverInfo6, &bFail); // // Vendor setup is optional, and by default none // InfGetVendorSetup(pInfInfo, hInf, pszInstallSection, pszDataSection, bDataSection, &bFail); // // Vendor installation is optional, and by default none // InfGetDriverInfoString(hInf, pszInstallSection, pszDataSection, bDataSection, cszVendorInstaller, &pInfInfo->pszVendorInstaller, NULL, &dwDontCare, &bFail); bFail = bFail || !InfGetDependentFilesAndICMFiles(hDevInfo, hInf, bWin95, pLocalData, platform, pszServerName, dwInstallFlags, pszInstallSection, &cchDriverInfo6); if ( !bWin95 ) { InfGetPreviousNames(hInf, &pLocalData->DrvInfo, &cchDriverInfo6, &bFail); InfGetOEMUrl(hInf, &pLocalData->DrvInfo, &bFail); } Cleanup: // // Save the last error is we've failed. SetupCloseInfFile can change the last error and we // don't care about it's last error in any way. // if( bFail ) { dwDontCare = GetLastError(); } LocalFreeMem(pszDataSection); if ( hInf != INVALID_HANDLE_VALUE ) SetupCloseInfFile(hInf); // // On failure free all the fields filled by this routine // if ( bFail ) { FreeStructurePointers((LPBYTE)pInfInfo, InfInfoOffsets, FALSE); ZeroMemory(pInfInfo, sizeof(*pInfInfo)); SetLastError( dwDontCare ); } else { // Point members of DriverInfo6 to strings in pDrvInfo pInfInfo->DriverInfo6.pszzPreviousNames = pLocalData->DrvInfo.pszzPreviousNames; pLocalData->InfInfo.DriverInfo6.pszMfgName = pLocalData->DrvInfo.pszManufacturer; if ( pLocalData->InfInfo.DriverInfo6.pszMfgName ) cchDriverInfo6 += ( lstrlen( pLocalData->InfInfo.DriverInfo6.pszMfgName ) + 1 ); pLocalData->InfInfo.DriverInfo6.pszOEMUrl = pLocalData->DrvInfo.pszOEMUrl; if ( pLocalData->InfInfo.DriverInfo6.pszOEMUrl ) cchDriverInfo6 += ( lstrlen( pLocalData->InfInfo.DriverInfo6.pszOEMUrl ) + 1 ); pLocalData->InfInfo.DriverInfo6.pszHardwareID = pLocalData->DrvInfo.pszHardwareID; if ( pLocalData->InfInfo.DriverInfo6.pszHardwareID ) cchDriverInfo6 += ( lstrlen( pLocalData->InfInfo.DriverInfo6.pszHardwareID ) + 1 ); pLocalData->InfInfo.DriverInfo6.pszProvider = pLocalData->DrvInfo.pszProvider; if ( pLocalData->InfInfo.DriverInfo6.pszProvider ) cchDriverInfo6 += ( lstrlen( pLocalData->InfInfo.DriverInfo6.pszProvider ) + 1 ); pLocalData->InfInfo.DriverInfo6.ftDriverDate = pLocalData->DrvInfo.ftDriverDate; pLocalData->InfInfo.DriverInfo6.dwlDriverVersion = pLocalData->DrvInfo.dwlDriverVersion; pInfInfo->cbDriverInfo6 = sizeof(DRIVER_INFO_6) + cchDriverInfo6 * sizeof(TCHAR); pLocalData->Flags |= VALID_INF_INFO; pInfInfo->platform = platform; } return !bFail; } LPDRIVER_INFO_6 GetDriverInfo6( IN PSELECTED_DRV_INFO pSelectedDrvInfo ) /*++ Routine Description: Gets the selected drivers information in a DRIVER_INFO_6 structure. Arguments: Return Value: Pointer to the DRIVER_INFO_6 structure. Memory is allocated for it. --*/ { HINF hInf; PPSETUP_LOCAL_DATA LocalData = NULL; LPDRIVER_INFO_6 pDriverInfo6 = NULL; HDEVINFO hDevInfo = INVALID_HANDLE_VALUE; if ( !pSelectedDrvInfo || !pSelectedDrvInfo->pszInfName || !*pSelectedDrvInfo->pszInfName || !pSelectedDrvInfo->pszModelName || !*pSelectedDrvInfo->pszModelName || !pSelectedDrvInfo->pszDriverSection || !*pSelectedDrvInfo->pszDriverSection ) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } if(INVALID_HANDLE_VALUE == (hDevInfo = CreatePrinterDeviceInfoList(NULL))) { return NULL; } LocalData = PSetupDriverInfoFromName(hDevInfo, pSelectedDrvInfo->pszModelName); if (!LocalData) { return NULL; } if ( ParseInf(hDevInfo, LocalData, MyPlatform, NULL, 0) ) { pDriverInfo6 = CloneDriverInfo6(&(LocalData->InfInfo.DriverInfo6), LocalData->InfInfo.cbDriverInfo6); } DestroyOnlyPrinterDeviceInfoList(hDevInfo); DestroyLocalData( LocalData ); return pDriverInfo6; } LPDRIVER_INFO_3 PSetupGetDriverInfo3( IN PSELECTED_DRV_INFO pSelectedDrvInfo ) /*++ Routine Description: Gets the selected drivers information in a DRIVER_INFO_3 structure. This is for the test teams use Arguments: Return Value: Pointer to the DRIVER_INFO_3 structure. Memory is allocated for it. --*/ { return (LPDRIVER_INFO_3) GetDriverInfo6(pSelectedDrvInfo); } LPTSTR GetStringFromRcFile( UINT uId ) /*++ Routine Description: Load a string from the .rc file and make a copy of it by doing AllocStr Arguments: uId : Identifier for the string to be loaded Return Value: String value loaded, NULL on error. Caller should free the memory --*/ { TCHAR buffer[MAX_SETUP_LEN]; int RetVal = 0; RetVal = LoadString(ghInst, uId, buffer, SIZECHARS(buffer)); if ( RetVal ) { return AllocStr(buffer); } else { return NULL; } } LPTSTR GetLongStringFromRcFile( UINT uId ) /*++ Routine Description: Load a long string from the .rc file, up to MAX_SETUP_ALLOC_STRING_LEN characters Arguments: uId : Identifier for the string to be loaded Return Value: String value loaded, NULL on error. Caller should free the memory --*/ { LPTSTR pBuf = NULL; int Retry = 0, RetVal; // // I couldn't find a way to determine the length of a string the resource file, hence // I just try until the length returned by LoadString is smaller than the buffer I passed in // for (Retry = 1; Retry <= MAX_SETUP_ALLOC_STRING_LEN/MAX_SETUP_LEN; Retry++) { int CurrentSize = Retry * MAX_SETUP_LEN; pBuf = LocalAllocMem(CurrentSize * sizeof(TCHAR)); if (!pBuf) { return NULL; } RetVal = LoadString(ghInst, uId, pBuf, CurrentSize); if (RetVal == 0) { LocalFreeMem(pBuf); return NULL; } if (RetVal < CurrentSize -1) // -1 because the LoadString ret value doesn't include the termination { return pBuf; } // // RetVal is CurrentSize - retry // LocalFreeMem(pBuf); } return NULL; } BOOL PSetupGetPathToSearch( IN HWND hwnd, IN LPCTSTR pszTitle, IN LPCTSTR pszDiskName, IN LPCTSTR pszFileName, IN BOOL bPromptForInf, IN OUT TCHAR szPath[MAX_PATH] ) /*++ Routine Description: Get path to search for some files by prompting the user Arguments: hwnd : Window handle of current top-level window pszTitle : Title for the UI pszDiskName : Diskname ot prompt the user pszFileName : Name of the file we are looking for (NULL ok) pszPath : Buffer to get the path entered by the user Return Value: TRUE on succesfully getting a path from user FALSE else, Do GetLastError() to get the error --*/ { DWORD dwReturn, dwNeeded; dwReturn = SetupPromptForDisk(hwnd, pszTitle, pszDiskName, szPath[0] ? szPath : NULL, pszFileName, NULL, bPromptForInf ? (IDF_NOSKIP | IDF_NOBEEP | IDF_NOREMOVABLEMEDIAPROMPT | IDF_USEDISKNAMEASPROMPT) : (IDF_NOSKIP | IDF_NOBEEP), szPath, MAX_PATH, &dwNeeded); if ( dwReturn == DPROMPT_SUCCESS ) { // // Remove this from source list so that next time we are looking for // native drivers we do not end up picking from wrong source // SetupRemoveFromSourceList(SRCLIST_SYSIFADMIN, szPath); // // Terminate with a \ at the end // dwNeeded = lstrlen(szPath); if ( *(szPath + dwNeeded - 1) != TEXT('\\') && dwNeeded < MAX_PATH - 2 ) { *(szPath + dwNeeded) = TEXT('\\'); *(szPath + dwNeeded + 1) = sZero; } return TRUE; } if ( dwReturn == DPROMPT_OUTOFMEMORY || dwReturn == DPROMPT_BUFFERTOOSMALL ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); } else { SetLastError(ERROR_CANCELLED); } return FALSE; } INT IsDifferent( LPTSTR p1, LPTSTR p2, DWORD (*pfn)(LPTSTR, LPTSTR) ) /*++ Routine Description: Extended version of strcmp/memcmp kind of function. Treats NULL pointer and the pointer to NULL as a match. For other cases call function passed in. Arguments: p1 : First address to compare p2 : Second address to compare pfn : Function to call if both p1 and p2 are non-NULL Return Value: + means p1 > p2 (like how memcmp or strcmp defines), - means p1 < p2. 0 if the values match --*/ { // // We want to treat NULL ptr and ptr to NULL as the same thing // if ( p1 && !*p1 ) p1 = NULL; if ( p2 && !*p2 ) p2 = NULL; // // If both are NULL then they match // if ( !p1 && !p2 ) return 0; // // Both are non NULL // if ( p1 && p2 ) return pfn(p1, p2); // // One of them is NULL // if ( p1 ) return 1; else return -1; } LPTSTR FileNamePart( IN LPCTSTR pszFullName ) /*++ Routine Description: Find the file name part of a fully qualified file name Arguments: pszFullName : Fully qualified path to the file Return Value: Pointer to the filename part in the fully qulaified string --*/ { LPTSTR pszSlash, pszTemp; if ( !pszFullName ) return NULL; // // First find the : for the drive // if ( pszTemp = lstrchr(pszFullName, TEXT(':')) ) pszFullName = pszFullName + 1; for ( pszTemp = (LPTSTR)pszFullName ; pszSlash = lstrchr(pszTemp, TEXT('\\')) ; pszTemp = pszSlash + 1 ) ; return *pszTemp ? pszTemp : NULL; } BOOL SameMultiSz( LPTSTR ppsz1, LPTSTR ppsz2, BOOL bFileName ) /*++ Routine Description: Checks if 2 multi-sz fields are the same. If bFileName is TRUE then get the file name part and compare that only (i.e. dependent files) Arguments: ppsz1 : DependentFiles field from the INF ppsz2 : DependentFiles field from the spooler bFileName : If TRUE compare file names, ignoring full path Return Value: TRUE if the string are identical, FALSE else 0 if the values match --*/ { LPTSTR p1, p2, p3; if ( !ppsz1 && !ppsz2 ) return TRUE; if ( !ppsz1 || !ppsz2 ) return FALSE; // // Check the file count is the same in the two lists // for ( p1 = ppsz1, p2 = ppsz2 ; *p1 && *p2 ; p1 += lstrlen(p1) + 1, p2 += lstrlen(p2) + 1 ) ; // Nul body // // If one of them is not NULL the number of strings is different // if ( *p1 || *p2 ) return FALSE; // // For each file in the first list see if it is present in the second list // for ( p1 = ppsz1 ; *p1 ; p1 += lstrlen(p1) + 1 ) { for ( p2 = ppsz2 ; *p2 ; p2 += lstrlen(p2) + 1 ) { p3 = bFileName ? FileNamePart(p2) : p2; if ( p3 && !lstrcmpi(p1, p3) ) break; } // // We did not find p1 in ppsz2 // if ( !*p2 ) return FALSE; } return TRUE; } BOOL IdenticalDriverInfo6( IN LPDRIVER_INFO_6 p1, IN LPDRIVER_INFO_6 p2 ) /*++ Routine Description: Checks if DRIVER_INFO_6 are the same Arguments: p1 : DRIVER_INFO_6 from the INF p2 : DRIVER_INFO_6 returned by the spooler Return Value: TRUE if the DRIVER_INFO_6s are identical, FALSE else --*/ { LPTSTR psz; return (p1->dwlDriverVersion == (DWORDLONG)0 || p2->dwlDriverVersion == (DWORDLONG)0 || p1->dwlDriverVersion == p2->dwlDriverVersion) && !lstrcmpi(p1->pName, p2->pName) && (psz = FileNamePart(p2->pDriverPath)) && !lstrcmpi(p1->pDriverPath, psz) && (psz = FileNamePart(p2->pDataFile)) && !lstrcmpi(p1->pDataFile, psz) && (psz = FileNamePart(p2->pConfigFile)) && !lstrcmpi(p1->pConfigFile, psz) && !IsDifferent(p1->pHelpFile, FileNamePart(p2->pHelpFile), lstrcmpi) && !IsDifferent(p1->pMonitorName, p2->pMonitorName, lstrcmpi); /* We changed the way we find dependent files from NT4 to NT5. So we do not want to look at them while deciding if a driver came from an INF. !IsDifferent(p1->pDefaultDataType, p2->pDefaultDataType, lstrcmpi); SameMultiSz(p1->pDependentFiles, p2->pDependentFiles, TRUE) && SameMultiSz(p1->pszzPreviousNames, p2->pszzPreviousNames, FALSE); */ } BOOL AllICMFilesInstalled( IN LPCTSTR pszServerName, IN LPTSTR pszzICMFiles ) /*++ Routine Description: Checks if all the icm files given are installed on the specified machine Arguments: pszServerName : Name of the server pszzICMFiles : Multi-sz field giving all the ICM files Return Value: TRUE if all the ICM profiles are installed on the server, FALSE else --*/ { BOOL bRet = FALSE; LPBYTE buf = NULL; LPTSTR p1, p2; DWORD dwNeeded, dwReturned; ENUMTYPE EnumType; if ( !pszzICMFiles || !*pszzICMFiles ) return TRUE; // // ICM apis are not remotablr for now // if ( pszServerName ) goto Cleanup; ZeroMemory(&EnumType, sizeof(EnumType)); EnumType.dwSize = sizeof(EnumType); EnumType.dwVersion = ENUM_TYPE_VERSION; // // Get all the color profiles installed on the machine // dwNeeded = 0; if ( EnumColorProfiles((LPTSTR)pszServerName, &EnumType, NULL, &dwNeeded, &dwReturned) || GetLastError() != ERROR_INSUFFICIENT_BUFFER || !(buf = LocalAllocMem(dwNeeded)) || !EnumColorProfiles((LPTSTR)pszServerName, &EnumType, buf, &dwNeeded, &dwReturned) ) { goto Cleanup; } for ( p1 = pszzICMFiles ; *p1 ; p1 += lstrlen(p1) + 1 ) { for ( p2 = (LPTSTR)buf, dwNeeded = 0 ; dwNeeded < dwReturned && *p2 && lstrcmpi(p1, p2) ; ++dwNeeded, p2 += lstrlen(p2) + 1 ) ; // // Did we find p1 in the enumerated color profiles? // if ( dwNeeded == dwReturned ) goto Cleanup; } bRet = TRUE; Cleanup: LocalFreeMem(buf); return bRet; } BOOL CorrectVersionDriverFound( IN LPDRIVER_INFO_2 pDriverInfo2, IN DWORD dwCount, IN LPCTSTR pszDriverName, IN DWORD dwMajorVersion ) /*++ Routine Description: Check if the correct version driver we are looking for is found in the list we got from spooler Arguments: pDriverInfo2 : Points to the buffer of DRIVER_INFO_2 structs dwCount : Number of DRIVER_INFO_2 elements in the buffer szDriverName : Driver name dwMajorVersion : Version no Return Value: TRUE if driver is found in the lise, FALSE else --*/ { DWORD dwIndex; for ( dwIndex = 0 ; dwIndex < dwCount ; ++dwIndex, ++pDriverInfo2 ) { // // Check if the driver is for the correct version // if ( dwMajorVersion != KERNEL_MODE_DRIVER_VERSION && dwMajorVersion != pDriverInfo2->cVersion ) continue; if ( dwMajorVersion == KERNEL_MODE_DRIVER_VERSION && pDriverInfo2->cVersion < 2 ) continue; if ( !lstrcmpi(pDriverInfo2->pName, pszDriverName) ) return TRUE; } return FALSE; } BOOL PSetupIsDriverInstalled( IN LPCTSTR pszServerName, IN LPCTSTR pszDriverName, IN PLATFORM platform, IN DWORD dwMajorVersion ) /*++ Routine Description: Findsout if a particular version of a printer driver is already installed in the system by querying spooler Arguments: pszServerName : Server name (NULL for local) pszDriverName : Driver name platform : platform for which we want to check the driver dwMajorVersion : Version no Return Value: TRUE if driver is installed, FALSE else (on error too) --*/ { BOOL bReturn = FALSE; DWORD dwReturned, dwNeeded = 1024, dwReturned2; LPBYTE p = NULL, p2 = NULL; LPTSTR psz; LPDRIVER_INFO_6 pDriverInfo6; LPTSTR pszServerArchitecture = NULL; if ( !(p = LocalAllocMem(dwNeeded)) ) goto Cleanup; if ( !EnumPrinterDrivers((LPTSTR)pszServerName, PlatformEnv[platform].pszName, 2, p, dwNeeded, &dwNeeded, &dwReturned) ) { LocalFreeMem(p); p = NULL; if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER || !(p = LocalAllocMem(dwNeeded)) || !EnumPrinterDrivers((LPTSTR)pszServerName, PlatformEnv[platform].pszName, 2, p, dwNeeded, &dwNeeded, &dwReturned) ) { goto Cleanup; } } bReturn = CorrectVersionDriverFound((LPDRIVER_INFO_2)p, dwReturned, pszDriverName, dwMajorVersion); // // Win95 drivers could have a different name than NT driver // if ( bReturn || platform != PlatformWin95 ) goto Cleanup; dwNeeded = 1024; if ( !(p2 = LocalAllocMem(dwNeeded)) ) goto Cleanup; pszServerArchitecture = GetArchitectureName( (LPTSTR)pszServerName ); if (!pszServerArchitecture) { goto Cleanup; } if ( !EnumPrinterDrivers((LPTSTR)pszServerName, pszServerArchitecture, 6, p2, dwNeeded, &dwNeeded, &dwReturned2) ) { LocalFreeMem(p2); p2 = NULL; if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER || !(p2 = LocalAllocMem(dwNeeded)) || !EnumPrinterDrivers((LPTSTR)pszServerName, pszServerArchitecture, 6, p2, dwNeeded, &dwNeeded, &dwReturned2) ) goto Cleanup; } for ( dwNeeded = 0, pDriverInfo6 = (LPDRIVER_INFO_6)p2 ; dwNeeded < dwReturned2 ; ++pDriverInfo6, ++dwNeeded ) { if ( pDriverInfo6->cVersion < 2 ) continue; if ( !lstrcmpi(pDriverInfo6->pName, pszDriverName) ) break; } if ( dwNeeded < dwReturned2 && (psz = pDriverInfo6->pszzPreviousNames) ) while ( *psz ) { if ( bReturn = CorrectVersionDriverFound((LPDRIVER_INFO_2)p, dwReturned, psz, dwMajorVersion) ) break; psz += lstrlen(psz) + 1; } Cleanup: LocalFreeMem(p); LocalFreeMem(p2); LocalFreeMem( pszServerArchitecture ); return bReturn; } INT PSetupIsTheDriverFoundInInfInstalled( IN LPCTSTR pszServerName, IN PPSETUP_LOCAL_DATA pLocalData, IN PLATFORM platform, IN DWORD dwMajorVersion ) /*++ Routine Description: Findsout if a particular version of a printer driver is already installed in the system by querying spooler; Additionally check if the installed driver is the same found in the INF (file name matches only) Arguments: pszServerName : Server name (NULL for local) szDriverName : Driver name platform : platform for which we want to check the driver dwMajorVersion : Version no; If KERNEL_MODE_DRIVER_VERSION check for a KM driver Return Value: DRIVER_MODEL_INSTALLED_AND_IDENTICAL : if driver is installed and all files are identical DRIVER_MODEL_NOT_INSTALLED : if a driver with the given model name is not available DRIVER_MODEL_INSTALLED_BUT_DIFFERENT : a driver with the given model name is installed but not all files are identical --*/ { INT iRet = DRIVER_MODEL_NOT_INSTALLED; DWORD dwReturned, dwNeeded, dwLastError; LPBYTE p = NULL; LPDRIVER_INFO_6 p1DriverInfo6, p2DriverInfo6; HDEVINFO hDevInfo = INVALID_HANDLE_VALUE; ASSERT(pLocalData && pLocalData->signature == PSETUP_SIGNATURE); if(INVALID_HANDLE_VALUE == (hDevInfo = CreatePrinterDeviceInfoList(NULL))) { goto Cleanup; } if ( !ParseInf(hDevInfo, pLocalData, platform, pszServerName, 0) ) goto Cleanup; p1DriverInfo6 = &pLocalData->InfInfo.DriverInfo6; if ( EnumPrinterDrivers((LPTSTR)pszServerName, PlatformEnv[platform].pszName, 6, NULL, 0, &dwNeeded, &dwReturned) ) { goto Cleanup; } if ( (dwLastError = GetLastError()) == ERROR_INVALID_LEVEL ) { iRet = PSetupIsDriverInstalled(pszServerName, p1DriverInfo6->pName, platform, dwMajorVersion) ? DRIVER_MODEL_INSTALLED_BUT_DIFFERENT : DRIVER_MODEL_NOT_INSTALLED; goto Cleanup; } if ( dwLastError != ERROR_INSUFFICIENT_BUFFER || !(p = LocalAllocMem(dwNeeded)) || !EnumPrinterDrivers((LPTSTR)pszServerName, PlatformEnv[platform].pszName, 6, p, dwNeeded, &dwNeeded, &dwReturned) ) { goto Cleanup; } for ( dwNeeded = 0, p2DriverInfo6 = (LPDRIVER_INFO_6) p ; dwNeeded < dwReturned ; ++dwNeeded, (LPBYTE) p2DriverInfo6 += sizeof(DRIVER_INFO_6) ) { // // Check if the driver is for the correct version // if ( dwMajorVersion != KERNEL_MODE_DRIVER_VERSION && dwMajorVersion != p2DriverInfo6->cVersion ) continue; if ( dwMajorVersion == KERNEL_MODE_DRIVER_VERSION && p2DriverInfo6->cVersion < 2 ) continue; if ( !lstrcmpi(p2DriverInfo6->pName, p1DriverInfo6->pName) ) { if ( IdenticalDriverInfo6(p1DriverInfo6, p2DriverInfo6) && AllICMFilesInstalled(pszServerName, pLocalData->InfInfo.pszzICMFiles) ) iRet = DRIVER_MODEL_INSTALLED_AND_IDENTICAL; else iRet = DRIVER_MODEL_INSTALLED_BUT_DIFFERENT; goto Cleanup; } } Cleanup: LocalFreeMem(p); DestroyOnlyPrinterDeviceInfoList(hDevInfo); return iRet; } PLATFORM PSetupThisPlatform( VOID ) { return MyPlatform; } BOOL DeleteAllFilesInDirectory( LPCTSTR pszDir, BOOL bDeleteDirectory ) /*++ Routine Description: Delete all the files in a directory, and optionally the directory as well. Arguments: pszDir : Directory name to cleanup bDeleteDirectory : If TRUE the directory gets deleted as well Return Value: TRUE on success, FALSE else --*/ { BOOL bRet = TRUE; HANDLE hFile; DWORD dwLen; TCHAR *pszFile = NULL; TCHAR *pszBuf = NULL; INT cbLength = 0; INT cbBufLength = 0; INT cbInitialLength = 4 * MAX_PATH; WIN32_FIND_DATA FindData; if (!pszDir) { bRet = FALSE; goto Cleanup; } cbLength = max( cbInitialLength, lstrlen( pszDir ) + lstrlen( TEXT("\\*") ) + 1); pszFile = LocalAllocMem( cbLength * sizeof( TCHAR )); if (!pszFile) { bRet = FALSE; goto Cleanup; } lstrcpy(pszFile, pszDir); dwLen = lstrlen(pszFile); lstrcpy(pszFile + dwLen, TEXT("\\*")); hFile = FindFirstFile(pszFile, &FindData); if ( hFile == INVALID_HANDLE_VALUE ) { bRet = FALSE; goto Cleanup; } *(pszFile + dwLen + 1) = TEXT('\0'); pszBuf = AllocStr( pszFile ); if (!pszBuf) { bRet = FALSE; goto Cleanup; } cbBufLength = lstrlen( pszBuf ); do { if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) continue; cbLength = cbBufLength + lstrlen( FindData.cFileName ) + 1; if (cbLength > cbInitialLength) { LocalFreeMem( pszFile ); pszFile = LocalAllocMem( cbLength * sizeof( TCHAR )); if (!pszFile) { bRet = FALSE; goto Cleanup; } cbInitialLength = cbLength; } lstrcpy(pszFile, pszBuf); lstrcat(pszFile, FindData.cFileName ); // // Remove the FILE_ATTRIBUTE_READONLY file attribute if it has been set // if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY ) { SetFileAttributes( pszFile, FindData.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY ); } if ( !DeleteFile(pszFile) ) bRet = FALSE; } while ( FindNextFile(hFile, &FindData) ); FindClose(hFile); if ( bDeleteDirectory && !RemoveDirectory(pszDir) ) bRet = FALSE; Cleanup: LocalFreeMem( pszFile ); LocalFreeMem( pszBuf ); return bRet; } // // enum to store the NT-CD type // typedef enum _eCDType { CD_Unknown, CD_NT4, CD_W2K_SRV, CD_W2K_PRO, CD_WHISTLER_SRV, CD_WHISTLER_WKS } CD_TYPE; // // structure that stores the tag file names of the NT CDs // typedef struct _CD_TAGFILE_MAP_ENTRY { CD_TYPE CdType; LPTSTR pszTagFileName; }CD_TAGFILE_MAP_ENTRY; CD_TAGFILE_MAP_ENTRY TagEntries[] = { // // the following entry for the Whistler CD is special in a couple of ways: // - it uses a wildcard because the tag filename changes from Beta1 to Beta2 and again to RTM // - it identifies the CD as W2k despite it being for Whistler. The reason is that the layout regarding // printer drivers is identical to W2k, no need to distinguish (and duplicate entries) // { CD_W2K_SRV, _T("WIN51.*") }, { CD_W2K_SRV, _T("CDROM_NT.5") }, { CD_NT4, _T("CDROM_S.40") }, { CD_NT4, _T("CDROM_W.40") }, // // no need to identify NT3.x CDs - different codepath ! // { CD_Unknown, NULL } }; // // structure to store the subpaths to printer INFs on the NT CDs // typedef struct _CD_SUBPATHS_FOR_PLATFORMS { CD_TYPE CdType; PLATFORM Platform; DWORD Version; LPCTSTR pszSubPath; } CD_SUBPATHS_FOR_PLATFORMS; // // this list is used for lookup of pathes as well - must be sorted so that paths // that contain other paths must come before them (e.g. xxx\zzz before \zzz) // CD_SUBPATHS_FOR_PLATFORMS SubPathInfo[] = { { CD_W2K_SRV, PlatformX86, 2, _T("printers\\nt4\\i386\\") }, { CD_W2K_SRV, PlatformWin95, 0, _T("printers\\win9x\\") }, { CD_W2K_SRV, PlatformX86, 3, _T("i386\\") }, { CD_W2K_SRV, PlatformIA64, 3, _T("ia64\\") }, { CD_NT4, PlatformX86, 2, _T("i386\\") }, { CD_NT4, PlatformAlpha, 2, _T("alpha\\") }, { CD_NT4, PlatformMIPS, 2, _T("mips\\") }, { CD_NT4, PlatformPPC, 2, _T("ppc\\") }, // // path = NULL terminates the array // { CD_Unknown, PlatformX86, 0 , NULL } }; CD_TYPE DetermineCDType(LPTSTR pszInfPath, LPTSTR pszRootPath) /*++ Routine Description: From a path to a printer INF, figure out what (if any) NT CD this is. It does so by figuring out the root path if it's one of the NT CDs and then checking the tag file that should be there. Arguments: pszInfPath : path to INF pszRootPath : caller-supplied buffer (MAX_PATH long) that receives the path to the CD "root". This is nontrivial in case the CD is on a network share. Ends with a backslash Return Value: The type of CD detected, CD_Unknown if not one that we know of (i.e. an OEM CD) --*/ { LPTSTR pszTemp; DWORD i; // // find the root path // DWORD_PTR MinPathLen = 0, SubPathLen, len; _tcscpy(pszRootPath, pszInfPath); len = _tcslen(pszRootPath); // // make sure it ends with a backslash // if (pszRootPath[len-1] != _T('\\')) { pszRootPath[len++] = _T('\\'); pszRootPath[len] = 0; } // // Is it a UNC path ? // if (!_tcsncmp(pszRootPath, _T("\\\\"), 2)) { pszTemp = _tcschr(pszRootPath + 2, _T('\\')); if (pszTemp) { pszTemp = _tcschr(pszTemp+1, _T('\\')); if (pszTemp) { MinPathLen = pszTemp - pszRootPath; } } // // check for illegal path, shouldn't happen // if ((MinPathLen == 0) || (MinPathLen > len)) { return CD_Unknown; } } else { MinPathLen = 2; } // // now check whether the final part of the path is one that I know of // for (i = 0; SubPathInfo[i].pszSubPath != NULL; ++i) { SubPathLen = _tcslen(SubPathInfo[i].pszSubPath); if (SubPathLen + MinPathLen <= len) { if (!_tcsnicmp(&(pszRootPath[len - SubPathLen]), SubPathInfo[i].pszSubPath, SubPathLen)) { pszRootPath[len-SubPathLen] = 0; len = _tcslen(pszRootPath); break; } } } // // if it's none of the paths I know of, it can still be the root itself. // now I know where the tag files should be if they're there // for (i = 0;TagEntries[i].pszTagFileName != NULL; ++i) { _tcscpy(&(pszRootPath[len]), TagEntries[i].pszTagFileName); if (FileExists(pszRootPath)) { pszRootPath[len] = 0; // cut off the tag file name return TagEntries[i].CdType; } } return CD_Unknown; } BOOL CheckValidInfInPath(HWND hwnd, LPTSTR pszInfPath, DWORD dwVersion, PLATFORM Platform) { TCHAR szInfFiles[MAX_PATH]; WIN32_FIND_DATA FindData; HANDLE hFind; DWORD PathLen; BOOL bRet = FALSE; // // first, find the INF in the path. There must be one else SetupPromptForPath would've complained // _tcscpy(szInfFiles, pszInfPath); PathLen = _tcslen(szInfFiles); if (szInfFiles[PathLen-1] != _T('\\')) { szInfFiles[PathLen++] = _T('\\'); szInfFiles[PathLen] = 0; } _tcscat(szInfFiles, _T("*.inf")); hFind = FindFirstFile(szInfFiles, &FindData); if (hFind != INVALID_HANDLE_VALUE) { DWORD InfStyle; HANDLE hInfFile; if ((dwVersion == 0) && (Platform != PlatformWin95)) { InfStyle = INF_STYLE_OLDNT; } else { InfStyle = INF_STYLE_WIN4; } do { _tcscpy(&(szInfFiles[PathLen]), FindData.cFileName); hInfFile = SetupOpenInfFile(szInfFiles, _T("Printer"), InfStyle, NULL); if (hInfFile != INVALID_HANDLE_VALUE) { SetupCloseInfFile(hInfFile); bRet = TRUE; break; } } while ( FindNextFile(hFind, &FindData) ); FindClose(hFind); } if (!bRet) { LPTSTR pszFormat = NULL, pszPrompt = NULL, pszTitle = NULL; pszFormat = GetStringFromRcFile(IDS_WARN_NO_ALT_PLATFORM_DRIVER); pszTitle = GetStringFromRcFile(IDS_WARN_NO_DRIVER_FOUND); if ( pszFormat && pszTitle) { pszPrompt = LocalAllocMem((lstrlen(pszFormat) + lstrlen(pszInfPath) + 2) * sizeof(TCHAR)); if ( pszPrompt ) { wsprintf(pszPrompt, pszFormat, pszInfPath); MessageBox(hwnd, pszPrompt, pszTitle, MB_OK); LocalFreeMem(pszPrompt); } } LocalFreeMem(pszFormat); LocalFreeMem(pszTitle); } return bRet; } BOOL CheckInfPath(HWND hwnd, LPTSTR pszInfPath, DWORD dwVersion, PLATFORM platform, LPTSTR *ppFileSrcPath) /*++ Routine Description: Check whether the path that a user selected as a path to install a printer from points to one of our CDs and correct the path if necessary, i.e. if the luser selected the \i386 subdir for an NT4 driver. Arguments: hwnd : windows handle of the main window pszInfPath : path to INF dwVersion : driver version that the new driver is installed for platform : the platform that the new driver is installed for ppFileSrcPath: if not NULL, receives the path to the printer files. This is used for installation from the NT4 CD that contains a compressed INF that I have to uncompress and install from without copying all the files possibly referenced in it. Needs to be freed by the caller. Return Value: TRUE: path contains a valid print inf FALSE: path doesn't contain a print inf, prompt user again --*/ { CD_TYPE CDType; TCHAR szRootPath[MAX_PATH]; DWORD i; // // determine the type of CD // CDType = DetermineCDType(pszInfPath, szRootPath); if (CDType == CD_Unknown) { return CheckValidInfInPath(hwnd, pszInfPath, dwVersion, platform); } // // NT 4 drivers are compressed -> uncompress into temp dir // if ((dwVersion == 2) && (CDType == CD_NT4)) { // // Make sure the file is actually the compressed one // DWORD rc, CompressedFileSize, UncompressedFileSize; UINT CompType; LPTSTR pszUncompFilePath = NULL, pszInfFileName = _T("ntprint.in_"); TCHAR szInf[MAX_PATH]; _tcscpy(szInf, szRootPath); // // append the correct subpath // for (i = 0; SubPathInfo[i].pszSubPath != NULL; ++i) { if ((SubPathInfo[i].CdType == CD_NT4) && (platform == SubPathInfo[i].Platform) && (dwVersion == SubPathInfo[i].Version)) { _tcscat(szInf, SubPathInfo[i].pszSubPath); break; } } _tcscat(szInf, pszInfFileName); rc = SetupGetFileCompressionInfo(szInf, &pszUncompFilePath, &CompressedFileSize, &UncompressedFileSize, &CompType); if (rc == NO_ERROR) { LocalFree(pszUncompFilePath); // don't need that if (CompType != FILE_COMPRESSION_NONE) { TCHAR UncompFilePath[MAX_PATH], *pTmp; // // decompress into temp directory // if (GetTempPath(MAX_PATH, UncompFilePath) && (_tcscat(UncompFilePath, _T("ntprint.inf")) != NULL) && (SetupDecompressOrCopyFile(szInf, UncompFilePath, NULL) == NO_ERROR)) { if (ppFileSrcPath) { // // delete the inf name from the path // pTmp = _tcsrchr(szInf, _T('\\')); if (pTmp) { *(pTmp+1) = 0; } *ppFileSrcPath = AllocStr(szInf); } _tcscpy(pszInfPath, UncompFilePath); // // delete the inf name from the path // pTmp = _tcsrchr(pszInfPath, _T('\\')); if (pTmp) { *(pTmp+1) = 0; } return TRUE; } } } } // // correct the path if it's the one for a different platform // for (i = 0; SubPathInfo[i].pszSubPath != NULL; ++i) { if ((CDType == SubPathInfo[i].CdType) && (platform == SubPathInfo[i].Platform) && (dwVersion == SubPathInfo[i].Version)) { _tcscpy(pszInfPath, szRootPath); _tcscat(pszInfPath, SubPathInfo[i].pszSubPath); break; } } return CheckValidInfInPath(hwnd, pszInfPath, dwVersion, platform); } HDEVINFO GetInfAndBuildDrivers( IN HWND hwnd, IN DWORD dwTitleId, IN DWORD dwDiskId, IN TCHAR szInfPath[MAX_PATH], IN DWORD dwInstallFlags, IN PLATFORM platform, IN DWORD dwVersion, IN LPCTSTR pszDriverName, OPTIONAL OUT PPSETUP_LOCAL_DATA *ppLocalData, OPTIONAL OUT LPTSTR *ppFileSrcPath OPTIONAL ) /*++ Routine Description: Prompt for an INF and build the list of printer drivers from INFs found in the directory. If pszDriverName is passed in then the INF should have a model with matching name (i.e. alternate driver installation case) Arguments: hwnd : Parent window handle for UI dwTitleId : Gives the identifier to be used to load the title string from the rc file dwDiskId : Gives the identifier to be used to load the disk identifier from the rc file szInfPath : Directory name where inf was found pszDriverName : Name of the driver needed in the INF ppLocalData : If a driver nam is given on return this will give the local data for that dwInstallFlags : Flags to control installation operation Return Value: TRUE on success, FALSE else --*/ { BOOL bDoRetry = TRUE; DWORD dwLen, dwLastError; LPTSTR pszTitle = NULL, pszDisk = NULL; HDEVINFO hDevInfo = INVALID_HANDLE_VALUE; dwLen = lstrlen(szInfPath); szInfPath[dwLen] = TEXT('\\'); if ( dwLen + lstrlen(cszAllInfs) + 1 > MAX_PATH ) goto Cleanup; lstrcpy(szInfPath+dwLen + 1, cszAllInfs); Retry: if ( bDoRetry && FileExists(szInfPath) ) { szInfPath[dwLen] = TEXT('\0'); } else { // // if the file doesn't exist in the first place, prompt only once ! // bDoRetry = FALSE; // // Always just prompt with the CD-ROM path // GetCDRomDrive(szInfPath); if ( dwInstallFlags & DRVINST_PROMPTLESS ) { SetLastError(ERROR_FILE_NOT_FOUND); goto Cleanup; } if ( dwTitleId && !(pszTitle = GetStringFromRcFile(dwTitleId)) ) goto Cleanup; if ( dwDiskId && !(pszDisk = GetStringFromRcFile(dwDiskId)) ) goto Cleanup; do { if ( !PSetupGetPathToSearch(hwnd, pszTitle, pszDisk, cszAllInfs, TRUE, szInfPath) ) goto Cleanup; } while (!CheckInfPath(hwnd, szInfPath, dwVersion, platform, ppFileSrcPath)); } hDevInfo = CreatePrinterDeviceInfoList(hwnd); if ( hDevInfo == INVALID_HANDLE_VALUE || !SetDevInstallParams(hDevInfo, NULL, szInfPath) || !BuildClassDriverList(hDevInfo) || (pszDriverName && !(*ppLocalData = PSetupDriverInfoFromName(hDevInfo, pszDriverName))) ) { dwLastError = GetLastError(); DestroyOnlyPrinterDeviceInfoList(hDevInfo); hDevInfo = INVALID_HANDLE_VALUE; SetLastError(dwLastError); if ( bDoRetry ) { bDoRetry = FALSE; goto Retry; } goto Cleanup; } Cleanup: LocalFreeMem(pszTitle); LocalFreeMem(pszDisk); return hDevInfo; } BOOL MyName( IN LPCTSTR pszServerName ) /*++ Routine Description: Tells if the string passed in identifies the local machine. Currently it checks for NULL and computer name only Arguments: pszServerName : Name of the server passed in Return Value: TRUE if the name is recognized as that for local machine, FALSE else --*/ { TCHAR szBuf[MAX_COMPUTERNAME_LENGTH+1]; DWORD dwNeeded; if ( !pszServerName || !*pszServerName ) return TRUE; return FALSE; /* dwNeeded = SIZECHARS(szBuf); if ( *pszServerName == TEXT('\\') && *(pszServerName+1) == TEXT('\\') && GetComputerName(szBuf, &dwNeeded) && !lstrcmpi(pszServerName+2, szBuf) ) { return TRUE; } */ } BOOL PSetupGetLocalDataField( IN PPSETUP_LOCAL_DATA pLocalData, IN PLATFORM platform, IN OUT PDRIVER_FIELD pDrvField ) /*++ Routine Description: Returns a driver installation field found from inf parsing. Printui uses this routine for all the queries. Since INF could have different sections for different platforms (notably for Win95 and NT but architecture specific install sections are possible too) Arguments: pLocalData : Pointer to local data platform : Which platform the field is for pDrvField : Points to DRIVER_FIELD where field is copied to Return Value: TRUE on success, FALSE else --*/ { BOOL bRet = FALSE; DWORD cbSize; LPTSTR psz; HDEVINFO hDevInfo = INVALID_HANDLE_VALUE; ASSERT(pLocalData && pDrvField && pLocalData->signature == PSETUP_SIGNATURE); if(INVALID_HANDLE_VALUE == (hDevInfo = CreatePrinterDeviceInfoList(NULL))) { return bRet; } switch ( pDrvField->Index ) { case DRIVER_NAME: if ( pDrvField->pszDriverName = AllocStr(pLocalData->DrvInfo.pszModelName) ) bRet = TRUE; break; case INF_NAME: if ( pDrvField->pszInfName = AllocStr(pLocalData->DrvInfo.pszInfName) ) bRet = TRUE; break; case DRV_INFO_4: if ( ParseInf(hDevInfo, pLocalData, platform, NULL, 0) && (pDrvField->pDriverInfo4 = (LPDRIVER_INFO_4) CloneDriverInfo6(&pLocalData->InfInfo.DriverInfo6, pLocalData->InfInfo.cbDriverInfo6)) ) bRet = TRUE; break; case DRV_INFO_6: if ( ParseInf(hDevInfo, pLocalData, platform, NULL, 0) && (pDrvField->pDriverInfo6 = CloneDriverInfo6(&pLocalData->InfInfo.DriverInfo6, pLocalData->InfInfo.cbDriverInfo6)) ) bRet = TRUE; break; case PRINT_PROCESSOR_NAME: pDrvField->pszPrintProc = NULL; if ( ParseInf(hDevInfo, pLocalData, platform, NULL, 0) ) { if ( !pLocalData->InfInfo.pszPrintProc || (pDrvField->pszPrintProc = AllocStr(pLocalData->InfInfo.pszPrintProc)) ) bRet = TRUE; } break; case ICM_FILES: pDrvField->pszzICMFiles = NULL; if ( ParseInf(hDevInfo, pLocalData, platform, NULL ,0) ) { for ( cbSize = 0, psz = pLocalData->InfInfo.pszzICMFiles ; psz && *psz ; cbSize += lstrlen(psz) + 1, psz += lstrlen(psz) + 1 ) ; if ( cbSize == 0 ) { bRet = TRUE; break; } // // One more char for the last \0 in the multi-sz // cbSize = (cbSize + 1 ) * sizeof(TCHAR); if ( pDrvField->pszzICMFiles = LocalAllocMem(cbSize) ) { CopyMemory((LPBYTE)pDrvField->pszzICMFiles, (LPBYTE)pLocalData->InfInfo.pszzICMFiles, cbSize); bRet = TRUE; } } break; default: SetLastError(ERROR_INVALID_PARAMETER); bRet = FALSE; } DestroyOnlyPrinterDeviceInfoList(hDevInfo); return bRet; } VOID PSetupFreeDrvField( IN PDRIVER_FIELD pDrvField ) /*++ Routine Description: Frees the memory allocated for a driver installation field in a previous call Arguments: pDrvField : Points to DRIVER_FIELD where field is copied to Return Value: None --*/ { LocalFreeMem(pDrvField->pszPrintProc); } BOOL FileExists( IN LPCTSTR pszFileName ) /*++ Routine Description: Checks if the given file exists setting correct error modes not to bring up any pop-ups. call Arguments: pszFileName : File name (fully qualified) Return Value: TRUE if file exists, FALSE else. --*/ { UINT OldMode; HANDLE hFile; WIN32_FIND_DATA FindData; OldMode = SetErrorMode(SEM_FAILCRITICALERRORS); hFile = FindFirstFile(pszFileName, &FindData); if ( hFile != INVALID_HANDLE_VALUE ) FindClose(hFile); SetErrorMode(OldMode); return hFile != INVALID_HANDLE_VALUE; } BOOL IsMonitorInstalled( IN LPTSTR pszMonitorName ) { LPBYTE pMonitors = NULL; DWORD dwNeeded, dwReturned; BOOL bMonFound = FALSE; // First Build a list of Monitors installed on machine // // First query spooler for installed monitors. If we fail let's quit // if ( !EnumMonitors((LPTSTR) NULL, 2, NULL, 0, &dwNeeded, &dwReturned) ) { if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER || !(pMonitors = LocalAllocMem(dwNeeded)) || !EnumMonitors((LPTSTR) NULL, 2, pMonitors, dwNeeded, &dwNeeded, &dwReturned) ) { goto Cleanup; } // Now see if the monitor is already installed if ( IsMonitorFound(pMonitors, dwReturned, pszMonitorName) ) bMonFound = TRUE; } Cleanup: if (pMonitors) LocalFreeMem( pMonitors ); return bMonFound; } BOOL IsLanguageMonitorInstalled(PCTSTR pszMonitorName) /*++ Routine Description: Checks for whether a language monitor is installed. The function above only checks for port monitors, because EnumMonitors doesn't enumerate language monitors. Since there is no API to do that, we sneak a peek at the registry. XP-Bug 416129. Arguments: pszMonitorName : Monitor name to check Return Value: TRUE if installed --*/ { PTSTR pKeyName = NULL; BOOL IsInstalled = FALSE; StrCatAlloc(&pKeyName, cszMonitorKey, pszMonitorName, NULL); if (pKeyName) { HKEY hKey; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, pKeyName, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { IsInstalled = TRUE; RegCloseKey(hKey); } FreeSplMem(pKeyName); } return IsInstalled; } BOOL CleanupUniqueScratchDirectory( IN LPCTSTR pszServerName, IN PLATFORM platform ) { BOOL bRet; TCHAR szDir[MAX_PATH]; DWORD dwNeeded; bRet = GetPrinterDriverDirectory((LPTSTR)pszServerName, PlatformEnv[platform].pszName, 1, (LPBYTE)szDir, sizeof(szDir), &dwNeeded); if (bRet) { bRet = AddDirectoryTag(szDir, MAX_PATH); } if (bRet) { bRet = DeleteAllFilesInDirectory(szDir, TRUE); } return bRet; } BOOL CleanupScratchDirectory( IN LPCTSTR pszServerName, IN PLATFORM platform ) { TCHAR szDir[MAX_PATH]; DWORD dwNeeded; return GetPrinterDriverDirectory((LPTSTR)pszServerName, PlatformEnv[platform].pszName, 1, (LPBYTE)szDir, sizeof(szDir), &dwNeeded) && DeleteAllFilesInDirectory(szDir, FALSE); } LPTSTR GetSystemInstallPath( VOID ) { BOOL bRet = FALSE; DWORD dwSize, dwType; HKEY hKey; TCHAR szSetupKey[] = TEXT( "Software\\Microsoft\\Windows\\CurrentVersion\\Setup"); TCHAR szSourceValue[] = TEXT("SourcePath"); LPTSTR pszSourcePath = NULL; if ( ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, szSetupKey, 0, KEY_QUERY_VALUE, &hKey) ) { if ( ERROR_SUCCESS == RegQueryValueEx(hKey, szSourceValue, NULL, &dwType, NULL, &dwSize) ) { if (pszSourcePath = (LPTSTR) LocalAllocMem(dwSize)) { if ( ERROR_SUCCESS != RegQueryValueEx(hKey, szSourceValue, NULL, &dwType, (LPBYTE)pszSourcePath, &dwSize) ) { LocalFreeMem(pszSourcePath); pszSourcePath = NULL; } } } RegCloseKey(hKey); } return pszSourcePath; } PPSETUP_LOCAL_DATA RebuildDeviceInfo( IN HDEVINFO hDevInfo, IN PPSETUP_LOCAL_DATA pLocalData, IN LPCTSTR pszSource ) { SP_DEVINSTALL_PARAMS DeviceInstallParams; SP_DRVINFO_DATA DriverInfoData, TempDriverInfoData; PPSETUP_LOCAL_DATA pNewLocalData = NULL; DWORD Err; // // Retrieve the current device install parameters, in preparation for modifying them to // target driver search at a particular INF. // ZeroMemory(&DeviceInstallParams, sizeof(DeviceInstallParams)); DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS); if(!SetupDiGetDeviceInstallParams(hDevInfo, pLocalData->DrvInfo.pDevInfoData, &DeviceInstallParams)) { return NULL; } SetupDiDestroyDriverInfoList(hDevInfo, NULL, SPDIT_CLASSDRIVER); // Set the path of the INF lstrcpy( DeviceInstallParams.DriverPath, pszSource ); // // set the flag that indicates DriverPath represents a single INF to be searched (and // not a directory path). Then store the parameters back to the device information element. // // DeviceInstallParams.Flags |= DI_ENUMSINGLEINF; DeviceInstallParams.FlagsEx |= DI_FLAGSEX_ALLOWEXCLUDEDDRVS; if(!SetupDiSetDeviceInstallParams(hDevInfo, pLocalData->DrvInfo.pDevInfoData, &DeviceInstallParams)) { Err = GetLastError(); goto clean0; } // // Now build a class driver list from this INF. // if(!SetupDiBuildDriverInfoList(hDevInfo, pLocalData->DrvInfo.pDevInfoData, SPDIT_CLASSDRIVER)) { Err = GetLastError(); goto clean0; } // // OK, now select the driver node from that INF that was used to install this device. // The three parameters that uniquely identify a driver node are INF Provider, // Device Manufacturer, and Device Description. Retrieve these three pieces of information // in preparation for selecting the proper driver node in the class list we just built. // ZeroMemory(&DriverInfoData, sizeof(DriverInfoData)); DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA); DriverInfoData.DriverType = SPDIT_CLASSDRIVER; DriverInfoData.Reserved = 0; // Search for the driver matching the specified criteria and // select it if found. // Fill in the Model & Mfg from original INF lstrcpy( DriverInfoData.Description, pLocalData->DrvInfo.pszModelName ); lstrcpy( DriverInfoData.MfgName, pLocalData->DrvInfo.pszManufacturer ); // Enum One driver entry to get the INF provider ZeroMemory(&TempDriverInfoData, sizeof(TempDriverInfoData)); TempDriverInfoData.cbSize = sizeof (SP_DRVINFO_DATA); DriverInfoData.DriverType = SPDIT_CLASSDRIVER; if (!SetupDiEnumDriverInfo (hDevInfo, NULL, SPDIT_CLASSDRIVER, 0, &TempDriverInfoData)) { Err = GetLastError(); goto clean0; } lstrcpy( DriverInfoData.ProviderName, TempDriverInfoData.ProviderName ); if(!SetupDiSetSelectedDriver(hDevInfo, pLocalData->DrvInfo.pDevInfoData, &DriverInfoData)) { Err = GetLastError(); goto clean0; } // // At this point, we've successfully selected the currently installed driver for the specified // device information element. // // Now build the new LocalData // pNewLocalData = BuildInternalData(hDevInfo, NULL); if ( pNewLocalData ) { if ( !ParseInf(hDevInfo, pNewLocalData, MyPlatform, NULL, 0) ) { Err = GetLastError(); DestroyLocalData( pNewLocalData ); pNewLocalData = NULL; } else { SELECTED_DRV_INFO TempDrvInfo; TempDrvInfo.pszInfName = AllocStr( pNewLocalData->DrvInfo.pszInfName ); TempDrvInfo.pszDriverSection = AllocStr( pNewLocalData->DrvInfo.pszDriverSection ); TempDrvInfo.pszModelName = AllocStr( pNewLocalData->DrvInfo.pszModelName ); TempDrvInfo.pszManufacturer = AllocStr( pNewLocalData->DrvInfo.pszManufacturer ); TempDrvInfo.pszHardwareID = AllocStr( pNewLocalData->DrvInfo.pszHardwareID ); TempDrvInfo.pszOEMUrl = AllocStr( pNewLocalData->DrvInfo.pszOEMUrl ); // Check that all strings were allocated if ( !TempDrvInfo.pszInfName || !TempDrvInfo.pszDriverSection || !TempDrvInfo.pszModelName || !TempDrvInfo.pszManufacturer || !TempDrvInfo.pszHardwareID || !TempDrvInfo.pszOEMUrl ) { // Free up all that worked LocalFreeMem( TempDrvInfo.pszInfName ); LocalFreeMem( TempDrvInfo.pszDriverSection ); LocalFreeMem( TempDrvInfo.pszModelName ); LocalFreeMem( TempDrvInfo.pszManufacturer ); LocalFreeMem( TempDrvInfo.pszHardwareID ); LocalFreeMem( TempDrvInfo.pszOEMUrl ); } else { // Free the DrvInfo pointers & refill from new local data LocalFreeMem( pLocalData->DrvInfo.pszInfName ); LocalFreeMem( pLocalData->DrvInfo.pszDriverSection ); LocalFreeMem( pLocalData->DrvInfo.pszModelName ); LocalFreeMem( pLocalData->DrvInfo.pszManufacturer ); LocalFreeMem( pLocalData->DrvInfo.pszHardwareID ); LocalFreeMem( pLocalData->DrvInfo.pszOEMUrl ); pLocalData->DrvInfo.pszInfName = TempDrvInfo.pszInfName; pLocalData->DrvInfo.pszDriverSection = TempDrvInfo.pszDriverSection; pLocalData->DrvInfo.pszModelName = TempDrvInfo.pszModelName; pLocalData->DrvInfo.pszManufacturer = TempDrvInfo.pszManufacturer; pLocalData->DrvInfo.pszHardwareID = TempDrvInfo.pszHardwareID; pLocalData->DrvInfo.pszOEMUrl = TempDrvInfo.pszOEMUrl; } Err = NO_ERROR; } } else Err = GetLastError(); clean0: SetLastError(Err); return pNewLocalData; } BOOL SetupSkipDir( IN PLATFORM platform, IN LPCTSTR pszServerName ) { BOOL bRet = FALSE; TCHAR szDir[MAX_PATH]; TCHAR szMSecs[10]; SYSTEMTIME SysTime; DWORD dwNeeded = ( MAX_PATH * sizeof( TCHAR ) ); EnterCriticalSection(&SkipCritSect); // We already have a skip dir created if ( !gpszSkipDir ) { // Get a location for a Temp Path if ( !GetPrinterDriverDirectory((LPTSTR)pszServerName, PlatformEnv[platform].pszName, 1, (LPBYTE) szDir, dwNeeded, &dwNeeded) ) goto Cleanup; if ( dwNeeded == 0) goto Cleanup; // Add on the Skip Prefix lstrcat( szDir, SKIP_DIR ); // Get System Time GetSystemTime( &SysTime ); wsprintf( szMSecs, TEXT("%04X"), SysTime.wMilliseconds ); lstrcat( szDir, szMSecs ); gpszSkipDir = AllocStr( szDir ); if (!gpszSkipDir ) goto Cleanup; if (!CreateDirectory( gpszSkipDir, NULL ) ) goto Cleanup; } bRet = TRUE; Cleanup: if (!bRet) { if (gpszSkipDir) { LocalFreeMem( gpszSkipDir ); gpszSkipDir = NULL; } } LeaveCriticalSection(&SkipCritSect); return bRet; } void CleanupSkipDir( VOID ) { // We already have a skip dir created if ( gpszSkipDir ) { RemoveDirectory( gpszSkipDir ); LocalFreeMem( gpszSkipDir ); } DeleteCriticalSection(&SkipCritSect); } BOOL IsLocalAdmin(BOOL *pbAdmin) /*++ Routine Description: This Routine determines if the user is a local admin. Parameters: pbAdmin - Return Value, TRUE for local admin. Return Value: TRUE - Function succeded (return value is valid). --*/ { SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY; BOOL bRet = FALSE; PSID pSIDAdmin = NULL; ASSERT( pbAdmin != NULL ); // Called locally *pbAdmin = FALSE; if (!AllocateAndInitializeSid( &SIDAuth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSIDAdmin) ) goto Cleanup; if (!CheckTokenMembership( NULL, pSIDAdmin, pbAdmin ) ) goto Cleanup; bRet = TRUE; Cleanup: if (pSIDAdmin != NULL) { FreeSid( pSIDAdmin ); } return bRet; } BOOL PruneInvalidFilesIfNotAdmin( IN HWND hWnd, IN OUT HSPFILEQ CopyQueue ) /*++ Routine Description: This routine checks whether you have administrator privileges, if you do, then it does nothing and returns. If you do not, it scans the file queue for files that are already present and signed and prunes them from the queue. The commit will not allow mixing signed and unsigned files. Note: We do this because if you are a power-user the call to MoveFileEx fails inside SetupCommitFileQueue, this happens if the existing file cannot be overwritten. We could improve this routine by checking if a file is actually in use before pruning it. Parameters: CopyQueue - The copy queue to scan. Return Value: TRUE - Either you were an administrator and no action was taken, or you were not and the FileQueue was successfully pruned. FALSE - The operation failed. --*/ { BOOL bLocalAdmin; BOOL bRet = FALSE; DWORD dwScanQueueResult; if (!IsLocalAdmin( &bLocalAdmin) ) goto Cleanup; if (bLocalAdmin) { bRet = TRUE; goto Cleanup; } if (!SetupScanFileQueue( CopyQueue, SPQ_SCAN_FILE_PRESENCE | SPQ_SCAN_PRUNE_COPY_QUEUE, hWnd , NULL , NULL , &dwScanQueueResult ) ) goto Cleanup; bRet = TRUE; Cleanup: return bRet; } BOOL AddDriverCatalogIfNotAdmin( IN PCWSTR pszServer, IN HANDLE hDriverSigningInfo, IN PCWSTR pszInfPath, IN PCWSTR pszSrcLoc, IN DWORD dwMediaType, IN DWORD dwCopyStyle ) /*++ Routine Description: This routine calls AddDriverCatalog for non-admin, aka power user. Parameters: pszServer - Name of the server hDriverSigningInfo - Handle to driver signing info Return Value: TRUE - Either you are an administrator and no action was taken, or you are not and the catalog was successfully added FALSE - The operation failed. Call GetLastError() to get detailed error information --*/ { BOOL bRet = FALSE; BOOL bLocalAdmin = TRUE; HANDLE hPrinter = NULL; PRINTER_DEFAULTS PrinterDefaults = {0}; DRIVER_INFCAT_INFO_1 DrvInfCatInfo1 = {0}; DRIVER_INFCAT_INFO_2 DrvInfCatInfo2 = {0}; PCWSTR pszCatPath = NULL; if (!hDriverSigningInfo || !DrvSigningIsLocalAdmin(hDriverSigningInfo, &bLocalAdmin) || !GetCatalogFile(hDriverSigningInfo, &pszCatPath)) { goto Cleanup; } // // If there is no Cat file or we are local admin, there is nothing to do // because for local admin, we use setup api to install the catalog file // if (!pszCatPath || bLocalAdmin) { bRet = TRUE; goto Cleanup; } PrinterDefaults.DesiredAccess = SERVER_ALL_ACCESS; if (!OpenPrinterW((PWSTR) pszServer, &hPrinter, &PrinterDefaults)) { goto Cleanup; } // // If there is a catalogfile entry in the inf file, we should call private // spooler API AddDriverCatalog with level 2 to install the catalog which // will install the inf and cat file by calling SetupCopyOEMInf. For inf // files that do not have catalogfile entry we shall call AddDriverCatalog // with level 1 which will install the catalog by using CryptoAPI // if (!IsCatInInf(hDriverSigningInfo)) { DrvInfCatInfo1.pszCatPath = pszCatPath; if (!AddDriverCatalog(hPrinter, 1, &DrvInfCatInfo1, APDC_USE_ORIGINAL_CAT_NAME)) { goto Cleanup; } } else { DrvInfCatInfo2.pszCatPath = pszCatPath; DrvInfCatInfo2.pszInfPath = pszInfPath; DrvInfCatInfo2.pszSrcLoc = pszSrcLoc; DrvInfCatInfo2.dwMediaType = dwMediaType; DrvInfCatInfo2.dwCopyStyle = dwCopyStyle; if (!AddDriverCatalog(hPrinter, 2, &DrvInfCatInfo2, APDC_NONE)) { goto Cleanup; } } bRet = TRUE; Cleanup: if (hPrinter) { ClosePrinter(hPrinter); } return bRet; } /* Function: AddDirectoryTag pszDir - TCHAR string to add the two tags to. dwSize - size in CHARACTERs of the allocated string buffer. Purpose - Takes the string pszDir and tags on "\dwPIDdwTID" on the end of it. This is used in the creation of a unique directory to copy the driver files for a specific install to. */ BOOL AddDirectoryTag( IN LPTSTR pszDir, IN DWORD dwSize ) { DWORD dwDirSize, dwPID, dwTID; PTCHAR pEnd; if( !pszDir || !dwSize || !(dwDirSize = lstrlen( pszDir )) ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } dwPID = GetCurrentProcessId(); dwTID = GetCurrentThreadId(); if( (pszDir[dwDirSize-1] != _TEXT('\\')) && (dwDirSize + 1 < dwSize) ) { pszDir[dwDirSize++] = _TEXT('\\'); pszDir[dwDirSize] = 0; } pEnd = &pszDir[dwDirSize]; _sntprintf( pEnd, (dwSize-dwDirSize), _TEXT("%d%d"), dwPID, dwTID ); return TRUE; } /* Function: AddPnpDirTag pszDir - TCHAR string to add the tag to. dwSize - size in CHARACTERs of the allocated string buffer. Purpose - Takes the string pszDir and tags on the pnp-ID on to it. This is used in the creation of a unique directory to copy the driver files for a specific install to. */ BOOL AddPnpDirTag( IN LPTSTR pszHardwareId, IN OUT LPTSTR pszDir, IN DWORD dwSize ) { DWORD dwDirSize; PTCHAR pEnd, pPnpId; if( !pszHardwareId || !pszDir || !dwSize || !(dwDirSize = lstrlen( pszDir )) || dwSize < dwDirSize + 3 ) // need at least space for backslash, one char + 0 terminator { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } if( (pszDir[dwDirSize-1] != _TEXT('\\'))) { pszDir[dwDirSize++] = _TEXT('\\'); pszDir[dwDirSize] = 0; } pEnd = &pszDir[dwDirSize]; // // Try to strip off the port enumerator, if applicable. The printer driver // should be independent of it. // if ((pPnpId = _tcsrchr(pszHardwareId, _TEXT('\\'))) == NULL) { // // it doesn't have a port enumerator, so the whole thing is the pnp ID // pPnpId = pszHardwareId; } else { // // found one: advance one beyond it if it's not the last character // to illustrate LPTENUM\abcd would become \abcd instead of abcd // if (*(pPnpId+1)) { pPnpId++; } } _tcsncpy(pEnd, pPnpId, dwSize - dwDirSize - 1); // // make sure the string is zero-terminated, so a pnp-ID that's too long doesn't result // in a runaway string. // pszDir[dwSize - 1] = 0; // // change all suspicious characters to underscores to avoid problems with / & \ etc. // all the distinguishing information should be in the alphanumerical characters // while (*pEnd) { if (!_istalnum(*pEnd)) { *pEnd = _TEXT('_'); } pEnd++; } return TRUE; } /* Function: AddDirToDriverInfo pszDir - Directory to append to driver info structure. pDriverInfo6 - Pointer to the driver info structure to update. Purpose: This function will ensure that there is no directory structure specified in the driver info structure yet (so as not to add it multiple times). If there isn't then it will update the driver file entries with the full path passed in in pszDir. */ BOOL AddDirToDriverInfo( IN LPTSTR pszDir, IN LPDRIVER_INFO_6 pDriverInfo6 ) { PTCHAR pOldString, pCurrentString, pNewString; DWORD dwLength, dwDirLength, dwNeeded = 0; if( !pszDir || !pDriverInfo6 ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } // // If the path is zero length, nothing to do. // if( !(dwDirLength = lstrlen( pszDir )) ) return TRUE; if( pDriverInfo6->pDriverPath && FileNamePart( pDriverInfo6->pDriverPath ) == pDriverInfo6->pDriverPath ) { pOldString = pDriverInfo6->pDriverPath; pDriverInfo6->pDriverPath = AllocAndCatStr2( pszDir, _TEXT("\\"), pOldString ); LocalFreeMem( pOldString ); } if( pDriverInfo6->pDataFile && FileNamePart( pDriverInfo6->pDataFile ) == pDriverInfo6->pDataFile ) { pOldString = pDriverInfo6->pDataFile; pDriverInfo6->pDataFile = AllocAndCatStr2( pszDir, _TEXT("\\"), pOldString ); LocalFreeMem( pOldString ); } if( pDriverInfo6->pConfigFile && FileNamePart( pDriverInfo6->pConfigFile ) == pDriverInfo6->pConfigFile ) { pOldString = pDriverInfo6->pConfigFile; pDriverInfo6->pConfigFile = AllocAndCatStr2( pszDir, _TEXT("\\"), pOldString ); LocalFreeMem( pOldString ); } if( pDriverInfo6->pHelpFile && FileNamePart( pDriverInfo6->pHelpFile ) == pDriverInfo6->pHelpFile ) { pOldString = pDriverInfo6->pHelpFile; pDriverInfo6->pHelpFile = AllocAndCatStr2( pszDir, _TEXT("\\"), pOldString ); LocalFreeMem( pOldString ); } if( pDriverInfo6->pDependentFiles ) { pCurrentString = pDriverInfo6->pDependentFiles; while( *pCurrentString ) { dwLength = lstrlen( pCurrentString ); if( pCurrentString == FileNamePart( pCurrentString ) ) { // // Amount needed - the two lengths + \ + 0 // dwNeeded += dwLength + dwDirLength + 1 + 1; } else { // // Amount needed - the existing + 0 // dwNeeded += dwLength + 1; } pCurrentString += dwLength + 1; } // // Increment for the final 0 // dwNeeded++; if(pNewString = LocalAllocMem( dwNeeded*sizeof(TCHAR) )) { pCurrentString = pNewString; pOldString = pDriverInfo6->pDependentFiles; while( *pOldString ) { if( pOldString == FileNamePart( pOldString ) ) { // // Add the directory info. // lstrcpy( pCurrentString, pszDir ); pCurrentString += dwDirLength; *pCurrentString++ = _TEXT('\\'); } // // Add the existing file info. // lstrcpy( pCurrentString, pOldString ); pCurrentString += lstrlen( pOldString ); *pCurrentString++ = 0; pOldString += lstrlen( pOldString ) + 1; } *pCurrentString = 0; LocalFreeMem( pDriverInfo6->pDependentFiles ); pDriverInfo6->pDependentFiles = pNewString; } } return TRUE; } BOOL IsSystemUpgradeInProgress( VOID ) /*++ Routine Description: Tells if we are in the middle of system setup Arguments: None Return Value: TRUE if system setup in progress, FALSE else --*/ { HKEY hKey = NULL; DWORD dwValue = 0, dwSize; if ( ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, cszSystemSetupKey, 0, KEY_READ, &hKey) ) { dwSize = sizeof(dwValue); if( ERROR_SUCCESS != RegQueryValueEx(hKey, cszSystemUpgradeValue, NULL, NULL, (LPBYTE)&dwValue, &dwSize) ) { dwValue = 0; } RegCloseKey(hKey); } return dwValue == 1; } BOOL IsSystemSetupInProgress( VOID ) /*++ Routine Description: Tells if we are in the middle of system setup (GUI mode) Arguments: None Return Value: TRUE if system setup in progress, FALSE else --*/ { HKEY hKey = NULL; DWORD dwValue = 0, dwSize; if ( ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, cszSystemSetupKey, 0, KEY_READ, &hKey) ) { dwSize = sizeof(dwValue); if( ERROR_SUCCESS != RegQueryValueEx(hKey, cszSystemSetupInProgress, NULL, NULL, (LPBYTE)&dwValue, &dwSize) ) { dwValue = 0; } RegCloseKey(hKey); } return dwValue == 1; } /* Function: GetMyTempDir Purpose: Creates a unique temporary directory off the TEMP directory. This gets called by UnCompressCat to create a unique directory to store the cat file that is to be expanded in. Returns: NULL if failed. The full qualified path to the new directory otherwise. Note: The returned string does contain the ending '\'. */ LPTSTR GetMyTempDir() { LPTSTR pszPath = NULL; PTCHAR pEnd; DWORD dwSize = 0; DWORD dwActualSize = 0; DWORD dwThreadID = GetCurrentThreadId(); DWORD dwProcessID = GetCurrentProcessId(); DWORD dwIDCounter = dwThreadID; BOOL bContinue = TRUE; dwSize = GetTempPath( 0, pszPath ); // // dwSize + size of the two DWORDs + \ + 0 // dwActualSize = dwSize+MAX_DWORD_LENGTH*2+2; if( dwSize && NULL != (pszPath = (LPTSTR)LocalAllocMem(dwActualSize*sizeof(TCHAR)))) { // // If this fails, then we assume that someone is playing with the temp path at the instant that // we are requesting it - unlikely so just fail (worst effect = probably leads to driver signing warning) // if( dwSize >= GetTempPath( dwSize, pszPath )) { dwSize = lstrlen(pszPath); pEnd = &pszPath[lstrlen(pszPath)]; do { _sntprintf( pEnd, dwActualSize-dwSize, _TEXT("%d%d%s"), dwProcessID, dwIDCounter, _TEXT("\\") ); if(CreateDirectory( pszPath, NULL ) || GetLastError() == ERROR_FILE_EXISTS) { // // We've got a directory, so drop out of loop. // bContinue = FALSE; } dwIDCounter++; // // Will stop loop when we have an unused directory or we loop round on the dwIDCounter // } while (bContinue && dwIDCounter != dwThreadID); if(bContinue) { LocalFreeMem( pszPath ); pszPath = NULL; } } else { LocalFreeMem( pszPath ); pszPath = NULL; } } return pszPath; } BOOL GetOSVersion( IN LPCTSTR pszServerName, OUT POSVERSIONINFO pOSVer ) { BOOL bRet = FALSE; if(pOSVer) { ZeroMemory(pOSVer,sizeof(OSVERSIONINFO)); pOSVer->dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if(!pszServerName || !*pszServerName) { bRet = GetVersionEx(pOSVer); } else { HANDLE hServer = NULL; DWORD dwNeeded = 0; DWORD dwType = REG_BINARY; PRINTER_DEFAULTS Defaults = { NULL, NULL, SERVER_READ }; // // Open the server for read access. // if( OpenPrinter( (LPTSTR) pszServerName, &hServer, &Defaults ) ) { // // Get the os version from the remote spooler. // if( ERROR_SUCCESS == ( GetPrinterData( hServer, SPLREG_OS_VERSION, &dwType, (PBYTE)pOSVer, sizeof( OSVERSIONINFO ), &dwNeeded ) ) ) { bRet = TRUE; } else { // // Assume that we're on NT4 as it doesn't support SPLREG_OS_VERSION // at it's the only OS that doesn't that could land up in this remote code path. // ZeroMemory(pOSVer, sizeof(OSVERSIONINFO)); pOSVer->dwOSVersionInfoSize = sizeof(OSVERSIONINFO); pOSVer->dwMajorVersion = 4; pOSVer->dwMinorVersion = 0; bRet = TRUE; } ClosePrinter( hServer ); } } } return bRet; } BOOL GetOSVersionEx( IN LPCTSTR pszServerName, OUT POSVERSIONINFOEX pOSVerEx ) { BOOL bRet = FALSE; if(pOSVerEx) { ZeroMemory(pOSVerEx,sizeof(OSVERSIONINFOEX)); pOSVerEx->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if(!pszServerName || !*pszServerName) { bRet = GetVersionEx((POSVERSIONINFO) pOSVerEx); } else { HANDLE hServer = NULL; DWORD dwNeeded = 0; DWORD dwType = REG_BINARY; PRINTER_DEFAULTS Defaults = { NULL, NULL, SERVER_READ }; // // Open the server for read access. // if( OpenPrinter( (LPTSTR) pszServerName, &hServer, &Defaults ) ) { // // Get the os version from the remote spooler. // if( ERROR_SUCCESS == ( GetPrinterData( hServer, SPLREG_OS_VERSIONEX, &dwType, (PBYTE)pOSVerEx, sizeof( OSVERSIONINFOEX ), &dwNeeded ) ) ) { bRet = TRUE; } else { // // Assume that we're on NT4 as it doesn't support SPLREG_OS_VERSION // at it's the only OS that doesn't that could land up in this remote code path. // ZeroMemory(pOSVerEx, sizeof(OSVERSIONINFOEX)); pOSVerEx->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); pOSVerEx->dwMajorVersion = 4; pOSVerEx->dwMinorVersion = 0; bRet = TRUE; } ClosePrinter( hServer ); } } } return bRet; } BOOL GetArchitecture( IN LPCTSTR pszServerName, OUT LPTSTR pszArch, IN OUT LPDWORD pcArchSize ) /*++ Routine Description: Obtains the local or remote server's architecture. Arguments: pszServerName - NULL = local machine. pszArch - will hold the machine's architecture. cArchSize - IN - size of pszArch in characters. OUT - character count that was filled. If failure is ERROR_MORE_DATA it will hold the needed character count. Return Value: TRUE on success. --*/ { BOOL bRet = FALSE; DWORD dwByteCount = 0; DWORD cLen = 0; if( !pszArch ) { SetLastError( ERROR_INVALID_PARAMETER ); } else { *pszArch = 0; if( !pszServerName || !*pszServerName ) { cLen = _tcslen(PlatformEnv[MyPlatform].pszName); if( cLen <= *pcArchSize ) { _tcsncpy( pszArch, PlatformEnv[MyPlatform].pszName, *pcArchSize ); bRet = TRUE; } *pcArchSize = cLen; } else { HANDLE hServer = NULL; DWORD dwNeeded = 0; DWORD dwType = REG_SZ; PRINTER_DEFAULTS Defaults = { NULL, NULL, SERVER_READ }; // // Open the server for read access. // if( OpenPrinter( (LPTSTR) pszServerName, &hServer, &Defaults ) ) { dwByteCount = *pcArchSize * sizeof( TCHAR ); // // Get the os version from the remote spooler. // if(ERROR_SUCCESS == GetPrinterData(hServer, SPLREG_ARCHITECTURE, &dwType, (PBYTE)pszArch, dwByteCount, &dwNeeded)) { bRet = TRUE; } else { *pszArch = 0; } *pcArchSize = dwNeeded / sizeof(TCHAR); ClosePrinter( hServer ); } } } return bRet; } BOOL IsInWow64() // // find out whether we're running in WOW64 // { BOOL bRet = FALSE; ULONG_PTR ul; NTSTATUS st; st = NtQueryInformationProcess(NtCurrentProcess(), ProcessWow64Information, &ul, sizeof(ul), NULL); if (NT_SUCCESS(st)) { // // If this call succeeds, we're on Win2000 or newer machines. // if (0 != ul) { // // 32-bit code running on Win64 // bRet = TRUE; } } return bRet; } BOOL IsWhistlerOrAbove( IN LPCTSTR pszServerName ) /*++ Routine Description: Determines whether the machine identified by ServerName is at least OS version 5.1 Arguments: pszServerName - the name of the remote server. NULL means local machine. Return Value: TRUE if the remote server is whistler or more recent server or local FALSE else --*/ { OSVERSIONINFO OsVer = {0}; BOOL bRet = FALSE; if (!pszServerName) { bRet = TRUE; } else if (GetOSVersion(pszServerName,&OsVer)) { if( (OsVer.dwMajorVersion > 5) || (OsVer.dwMajorVersion == 5 && OsVer.dwMinorVersion > 0) ) { bRet = TRUE; } } return bRet; } HRESULT IsProductType( IN BYTE ProductType, IN BYTE Comparison ) /*++ Routine Description: Determines whether the version of the OS is personal, professional or server depending on the given ProductType and Comparison Arguments: ProductType - VER_NT_WORKSTATION or VER_NT_SERVER Comaprison - VER_EQUAL, VER_GREATER, VER_GREATER_EQUAL, VER_LESS, VER_LESS_EQUAL Return Value: S_OK if the OS version if the OS satisfies the given conditions S_FALSE else --*/ { HRESULT hRetval = S_FALSE; OSVERSIONINFOEX OsVerEx = {0}; ULONGLONG dwlConditionMask = 0; OsVerEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); OsVerEx.wProductType = ProductType; VER_SET_CONDITION( dwlConditionMask, VER_PRODUCT_TYPE, Comparison ); if (VerifyVersionInfo(&OsVerEx, VER_PRODUCT_TYPE, dwlConditionMask)) { hRetval = S_OK; } return hRetval; } HMODULE LoadLibraryUsingFullPath( LPCTSTR lpFileName ) { TCHAR szSystemPath[MAX_PATH]; INT cLength = 0; INT cFileNameLength = 0; if (!lpFileName || ((cFileNameLength = lstrlen(lpFileName)) == 0)) { return NULL; } if (GetSystemDirectory(szSystemPath, SIZECHARS(szSystemPath) ) == 0) { return NULL; } cLength = lstrlen(szSystemPath); if (szSystemPath[cLength-1] != TEXT('\\')) { if ((cLength + 1) >= MAX_PATH) { return NULL; } szSystemPath[cLength] = TEXT('\\'); szSystemPath[cLength + 1] = TEXT('\0'); cLength++; } if ((cLength + cFileNameLength) >= MAX_PATH) { return NULL; } lstrcat(szSystemPath, lpFileName); return LoadLibrary( szSystemPath ); } BOOL IsSpoolerRunning( VOID ) { HANDLE ph; BOOL IsRunning = FALSE; if (OpenPrinter(NULL, &ph, NULL)) { IsRunning = TRUE; ClosePrinter(ph); } return IsRunning; } BOOL CheckAndKeepPreviousNames( IN LPCTSTR pszServer, IN PDRIVER_INFO_6 pDriverInfo6, IN PLATFORM platform ) { DWORD dwNeeded = 0; DWORD dwReturned = 0; DWORD dwIndex = 0; LPDRIVER_INFO_4 pCurDriverInfo = NULL; BOOL bRet = FALSE; INT cPrevNamesLength = 0; PLATFORM Platform2Enumerate = pszServer ? platform : MyPlatform; if (pDriverInfo6 && pDriverInfo6->pName && (*(pDriverInfo6->pName) == TEXT('\0')) ) { goto Cleanup; } if ( !EnumPrinterDrivers((LPTSTR)pszServer, PlatformEnv[Platform2Enumerate].pszName, 4, (LPBYTE)pCurDriverInfo, 0, &dwNeeded, &dwReturned) ) { if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER || !(pCurDriverInfo = LocalAllocMem(dwNeeded)) || !EnumPrinterDrivers((LPTSTR)pszServer, PlatformEnv[Platform2Enumerate].pszName, 4, (LPBYTE)pCurDriverInfo, dwNeeded, &dwNeeded, &dwReturned) || (dwReturned <= 0)) { goto Cleanup; } } for (dwIndex = 0; dwIndex < dwReturned; dwIndex++) { if ((pCurDriverInfo+dwIndex)->pName && (*(pCurDriverInfo+dwIndex)->pName != TEXT('\0')) && !lstrcmp(pDriverInfo6->pName,(pCurDriverInfo+dwIndex)->pName) ) { if ((pCurDriverInfo+dwIndex)->pszzPreviousNames && (*(pCurDriverInfo+dwIndex)->pszzPreviousNames != TEXT('\0'))) { cPrevNamesLength = lstrlen((pCurDriverInfo+dwIndex)->pszzPreviousNames); pDriverInfo6->pszzPreviousNames = (LPTSTR)LocalAllocMem( (cPrevNamesLength + 2) * sizeof(TCHAR) ); if (pDriverInfo6->pszzPreviousNames) { bRet = TRUE; CopyMemory( pDriverInfo6->pszzPreviousNames, (pCurDriverInfo+dwIndex)->pszzPreviousNames, cPrevNamesLength * sizeof(TCHAR) ); *(pDriverInfo6->pszzPreviousNames + cPrevNamesLength) = TEXT('\0'); *(pDriverInfo6->pszzPreviousNames + cPrevNamesLength + 1) = TEXT('\0'); } else { bRet = FALSE; } goto Cleanup; } } } Cleanup: if (pCurDriverInfo) { LocalFreeMem(pCurDriverInfo); } return bRet; } BOOL IsTheSamePlatform( IN LPCTSTR pszServer, IN PLATFORM platform ) { BOOL bRet = FALSE; DWORD dwServerArchSize = 0; DWORD dwServerArchSizeInChars = 0; TCHAR *pszServerArchitecture = NULL; if (!pszServer) { bRet = TRUE; goto Cleanup; } dwServerArchSizeInChars = lstrlen( PlatformEnv[platform].pszName ) + 1; dwServerArchSize = dwServerArchSizeInChars * sizeof(TCHAR); pszServerArchitecture = LocalAllocMem(dwServerArchSize); if (!pszServerArchitecture || !GetArchitecture(pszServer, pszServerArchitecture, &dwServerArchSizeInChars )) { bRet = FALSE; goto Cleanup; } bRet = !lstrcmp( pszServerArchitecture, PlatformEnv[platform].pszName ); Cleanup: if (pszServerArchitecture) { LocalFreeMem( pszServerArchitecture ); } return bRet; } LPTSTR GetArchitectureName( IN LPCTSTR pszServerName ) { LPTSTR pszArch = NULL; DWORD dwArchSize = 80; if (pszServerName && (*pszServerName == TEXT('\0'))) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } if (!pszServerName) { return AllocStr( PlatformEnv[MyPlatform].pszName ); } pszArch = LocalAllocMem( dwArchSize * sizeof(TCHAR)); if (!pszArch) { return NULL; } if (!GetArchitecture( pszServerName, pszArch, &dwArchSize)) { if (GetLastError() == ERROR_MORE_DATA) { LocalFreeMem( pszArch ); dwArchSize += 1; pszArch = LocalAllocMem( dwArchSize * sizeof(TCHAR) ); if (!pszArch || !GetArchitecture( pszServerName, pszArch, &dwArchSize)) { return NULL; } } } return pszArch; } /************************************************************************************ ** End of File (util.c) ************************************************************************************/