/*++ Copyright (c) 2000 Microsoft Corporation Module Name: utils.c Abstract: Utilitaries for winnt32. Author: Revision History: Ovidiu Temereanca (ovidiut) 24-Jul-2000 --*/ #include "precomp.h" #include #pragma hdrstop VOID MyWinHelp( IN HWND Window, IN UINT Command, IN ULONG_PTR Data ) { TCHAR Buffer[2*MAX_PATH]; LPTSTR p; HANDLE FindHandle; BOOL b; WIN32_FIND_DATA FindData; LPCTSTR HelpFileName = TEXT("winnt32.hlp"); // // The likely scenario is that a user invokes winnt32 from // a network share. We'll expect the help file to be there too. // b = FALSE; if(GetModuleFileName(NULL,Buffer,sizeof(Buffer)/sizeof(TCHAR)) && (p = _tcsrchr(Buffer,TEXT('\\')))) { lstrcpy(p+1,HelpFileName); // // See whether the help file is there. If so, use it. // FindHandle = FindFirstFile(Buffer,&FindData); if(FindHandle != INVALID_HANDLE_VALUE) { FindClose(FindHandle); b = WinHelp(Window,Buffer,Command,Data); } } if(!b) { // // Try just the base help file name. // b = WinHelp(Window,HelpFileName,Command,Data); } if(!b) { // // Tell user. // MessageBoxFromMessage( Window, MSG_CANT_OPEN_HELP_FILE, FALSE, AppTitleStringId, MB_OK | MB_ICONINFORMATION, HelpFileName ); } } VOID ConcatenatePaths( IN OUT PTSTR Path1, IN LPCTSTR Path2, IN DWORD BufferSizeChars ) /*++ Routine Description: Concatenate two path strings together, supplying a path separator character (\) if necessary between the 2 parts. Arguments: Path1 - supplies prefix part of path. Path2 is concatenated to Path1. Path2 - supplies the suffix part of path. If Path1 does not end with a path separator and Path2 does not start with one, then a path sep is appended to Path1 before appending Path2. BufferSizeChars - supplies the size in chars (Unicode version) or bytes (Ansi version) of the buffer pointed to by Path1. The string will be truncated as necessary to not overflow that size. Return Value: None. --*/ { BOOL NeedBackslash = TRUE; DWORD l; if(!Path1) return; l = lstrlen(Path1); if(BufferSizeChars >= sizeof(TCHAR)) { // // Leave room for terminating nul. // BufferSizeChars -= sizeof(TCHAR); } // // Determine whether we need to stick a backslash // between the components. // if(l && (Path1[l-1] == TEXT('\\'))) { NeedBackslash = FALSE; } if(Path2 && *Path2 == TEXT('\\')) { if(NeedBackslash) { NeedBackslash = FALSE; } else { // // Not only do we not need a backslash, but we // need to eliminate one before concatenating. // Path2++; } } // // Append backslash if necessary and if it fits. // if(NeedBackslash && (l < BufferSizeChars)) { lstrcat(Path1,TEXT("\\")); } // // Append second part of string to first part if it fits. // if(Path2 && ((l+lstrlen(Path2)) < BufferSizeChars)) { lstrcat(Path1,Path2); } } LPTSTR DupString( IN LPCTSTR String ) /*++ Routine Description: Make a duplicate of a nul-terminated string. Arguments: String - supplies pointer to nul-terminated string to copy. Return Value: Copy of string or NULL if OOM. Caller can free with FREE(). --*/ { LPTSTR p; if(p = MALLOC((lstrlen(String)+1)*sizeof(TCHAR))) { lstrcpy(p,String); } return(p); } PTSTR DupMultiSz ( IN PCTSTR MultiSz ) /*++ Routine Description: Make a duplicate of a MultiSz. Arguments: MultiSz - supplies pointer to the multi-string to duplicate. Return Value: Copy of string or NULL if OOM. Caller can free with FREE(). --*/ { PCTSTR p; PTSTR q; DWORD size = sizeof (TCHAR); for (p = MultiSz; *p; p = _tcschr (p, 0) + 1) { size += (lstrlen (p) + 1) * sizeof(TCHAR); } if (q = MALLOC (size)) { CopyMemory (q, MultiSz, size); } return q; } PTSTR CreatePrintableString ( IN PCTSTR MultiSz ) /*++ Routine Description: Creates a string of the form (str1, str2, ..., strN) from a MultiSz Arguments: MultiSz - supplies pointer to the MultiSz string to represent. Return Value: Pointer to the new string string or NULL if OOM. Caller can free with FREE(). --*/ { PCTSTR p; PTSTR q, r; DWORD size = 3 * sizeof (TCHAR); for (p = MultiSz; *p; p = _tcschr (p, 0) + 1) { size += (lstrlen (p) + 1) * sizeof(TCHAR); } if (r = MALLOC (size)) { q = r; *q++ = TEXT('('); for (p = MultiSz; *p; p = _tcschr (p, 0) + 1) { if (q - r > 1) { *q++ = TEXT(','); } q += wsprintf (q, TEXT("%s"), p); } *q++ = TEXT(')'); *q = 0; } return r; } PSTR UnicodeToAnsi ( IN PCWSTR Unicode ) /*++ Routine Description: Makes an ANSI duplicate of a UNICODE string. Arguments: Unicode - supplies pointer to the UNICODE string to duplicate. Return Value: Copy of string or NULL if OOM. Caller can free with FREE(). --*/ { PSTR p; DWORD size; if (!Unicode) { return NULL; } size = (lstrlenW (Unicode) + 1) * sizeof(WCHAR); if (p = MALLOC (size)) { if (!WideCharToMultiByte ( CP_ACP, 0, Unicode, -1, p, size, NULL, NULL )) { FREE (p); p = NULL; } } return p; } PWSTR MultiSzAnsiToUnicode ( IN PCSTR MultiSzAnsi ) /*++ Routine Description: Makes a UNICODE duplicate of a multi-sz ANSI string. Arguments: MultiSzAnsi - supplies pointer to the multisz ANSI string to duplicate. Return Value: Copy of string or NULL if OOM. Caller can free with FREE(). --*/ { PCSTR p; PWSTR q; DWORD size = 1; if (!MultiSzAnsi) { return NULL; } for (p = MultiSzAnsi; *p; p = _mbschr (p, 0) + 1) { size += lstrlenA (p) + 1; } if (q = MALLOC (size * sizeof(WCHAR))) { if (!MultiByteToWideChar ( CP_ACP, 0, MultiSzAnsi, size, q, size )) { FREE (q); q = NULL; } } return q; } UINT MyGetDriveType( IN TCHAR Drive ) /*++ Routine Description: Same as GetDriveType() Win32 API except on NT returns DRIVE_FIXED for removeable hard drives. Arguments: Drive - supplies drive letter whose type is desired. Return Value: Same as GetDriveType(). --*/ { TCHAR DriveNameNt[] = TEXT("\\\\.\\?:"); TCHAR DriveName[] = TEXT("?:\\"); HANDLE hDisk; BOOL b; UINT rc; DWORD DataSize; DISK_GEOMETRY MediaInfo; // // First, get the win32 drive type. If it tells us DRIVE_REMOVABLE, // then we need to see whether it's a floppy or hard disk. Otherwise // just believe the api. // // MYASSERT (Drive); DriveName[0] = Drive; rc = GetDriveType(DriveName); #ifdef _X86_ //NEC98 // // NT5 for NEC98 can not access AT formated HD during setup. // We need except these type. if (IsNEC98() && ISNT() && (rc == DRIVE_FIXED) && BuildNumber <= NT40) { TCHAR aho[100]; // // Check ATA Card? // { HANDLE hDisk; TCHAR HardDiskName[] = TEXT("\\\\.\\?:"); HardDiskName[4] = Drive; hDisk = CreateFile( HardDiskName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hDisk == INVALID_HANDLE_VALUE) { return(DRIVE_UNKNOWN); } if (CheckATACardonNT4(hDisk)){ CloseHandle(hDisk); return(DRIVE_REMOVABLE); } CloseHandle(hDisk); } if (!IsValidDrive(Drive)){ // HD format is not NEC98 format. return(DRIVE_UNKNOWN); } } if((rc != DRIVE_REMOVABLE) || !ISNT() || (!IsNEC98() && (Drive < L'C'))) { return(rc); } #else //NEC98 if((rc != DRIVE_REMOVABLE) || !ISNT() || (Drive < L'C')) { return(rc); } #endif // // DRIVE_REMOVABLE on NT. // // // Disallow use of removable media (e.g. Jazz, Zip, ...). // DriveNameNt[4] = Drive; hDisk = CreateFile( DriveNameNt, FILE_READ_ATTRIBUTES | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if(hDisk != INVALID_HANDLE_VALUE) { b = DeviceIoControl( hDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &MediaInfo, sizeof(MediaInfo), &DataSize, NULL ); // // It's really a hard disk if the media type is removable. // if(b && (MediaInfo.MediaType == RemovableMedia)) { rc = DRIVE_FIXED; } CloseHandle(hDisk); } return(rc); } #ifdef UNICODE UINT MyGetDriveType2 ( IN PCWSTR NtVolumeName ) /*++ Routine Description: Same as GetDriveType() Win32 API except on NT returns DRIVE_FIXED for removeable hard drives. Arguments: NtVolumeName - supplies device name whose type is desired. Return Value: Same as GetDriveType(). --*/ { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; UNICODE_STRING DeviceName; HANDLE hDisk; IO_STATUS_BLOCK IoStatusBlock; BOOL b; UINT rc; DWORD DataSize; DISK_GEOMETRY MediaInfo; FILE_FS_DEVICE_INFORMATION DeviceInfo; // // First, get the win32 drive type. If it tells us DRIVE_REMOVABLE, // then we need to see whether it's a floppy or hard disk. Otherwise // just believe the api. // INIT_OBJA (&Obja, &DeviceName, NtVolumeName); Status = NtOpenFile ( &hDisk, (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE ); if (!NT_SUCCESS( Status )) { return DRIVE_NO_ROOT_DIR; } // // Determine if this is a network or disk file system. If it // is a disk file system determine if this is removable or not // Status = NtQueryVolumeInformationFile( hDisk, &IoStatusBlock, &DeviceInfo, sizeof(DeviceInfo), FileFsDeviceInformation ); if (!NT_SUCCESS (Status)) { rc = DRIVE_UNKNOWN; } else if (DeviceInfo.Characteristics & FILE_REMOTE_DEVICE) { rc = DRIVE_REMOTE; } else { switch (DeviceInfo.DeviceType) { case FILE_DEVICE_NETWORK: case FILE_DEVICE_NETWORK_FILE_SYSTEM: rc = DRIVE_REMOTE; break; case FILE_DEVICE_CD_ROM: case FILE_DEVICE_CD_ROM_FILE_SYSTEM: rc = DRIVE_CDROM; break; case FILE_DEVICE_VIRTUAL_DISK: rc = DRIVE_RAMDISK; break; case FILE_DEVICE_DISK: case FILE_DEVICE_DISK_FILE_SYSTEM: if ( DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA ) { rc = DRIVE_REMOVABLE; } else { rc = DRIVE_FIXED; } break; default: rc = DRIVE_UNKNOWN; break; } } if(rc == DRIVE_REMOVABLE) { // // DRIVE_REMOVABLE on NT. // Disallow use of removable media (e.g. Jazz, Zip, ...). // Status = NtDeviceIoControlFile( hDisk, 0, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &MediaInfo, sizeof(DISK_GEOMETRY) ); // // It's really a hard disk if the media type is removable. // if(NT_SUCCESS (Status) && (MediaInfo.MediaType == RemovableMedia)) { rc = DRIVE_FIXED; } } NtClose (hDisk); return(rc); } #endif BOOL GetPartitionInfo( IN TCHAR Drive, OUT PPARTITION_INFORMATION PartitionInfo ) /*++ Routine Description: Fill in a PARTITION_INFORMATION structure with information about a particular drive. This routine is meaningful only when run on NT -- it always fails on Win95. Arguments: Drive - supplies drive letter whose partition info is desired. PartitionInfo - upon success, receives partition info for Drive. Return Value: Boolean value indicating whether PartitionInfo has been filled in. --*/ { TCHAR DriveName[] = TEXT("\\\\.\\?:"); HANDLE hDisk; BOOL b; DWORD DataSize; if(!ISNT()) { return(FALSE); } DriveName[4] = Drive; hDisk = CreateFile( DriveName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if(hDisk == INVALID_HANDLE_VALUE) { return(FALSE); } b = DeviceIoControl( hDisk, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, PartitionInfo, sizeof(PARTITION_INFORMATION), &DataSize, NULL ); CloseHandle(hDisk); return(b); } #ifdef UNICODE BOOL GetPartitionInfo2 ( IN PCWSTR NtVolumeName, OUT PPARTITION_INFORMATION PartitionInfo ) /*++ Routine Description: Fill in a PARTITION_INFORMATION structure with information about a particular drive. This routine is meaningful only when run on NT -- it always fails on Win95. Arguments: NtVolumeName - supplies NT volume name whose partition info is desired. PartitionInfo - upon success, receives partition info for Drive. Return Value: Boolean value indicating whether PartitionInfo has been filled in. --*/ { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; UNICODE_STRING DeviceName; HANDLE hDisk; IO_STATUS_BLOCK IoStatusBlock; BOOL b = FALSE; DWORD DataSize; // // Open the file // INIT_OBJA (&Obja, &DeviceName, NtVolumeName); Status = NtOpenFile ( &hDisk, (ACCESS_MASK)FILE_READ_DATA | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT ); if (NT_SUCCESS (Status)) { Status = NtDeviceIoControlFile ( hDisk, 0, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, PartitionInfo, sizeof(PARTITION_INFORMATION) ); NtClose (hDisk); b = NT_SUCCESS (Status); } return(b); } #endif BOOL IsDriveNTFT( IN TCHAR Drive, IN PCTSTR NtVolumeName ) /*++ Routine Description: Determine whether a drive is any kind of NTFT set. This routine is meaningful only when run on NT -- it always fails on Win95. Arguments: Drive - supplies drive letter to check; optional NtVolumeName - supplies volume name to check; required if Drive not specified Return Value: Boolean value indicating whether the drive is NTFT. --*/ { PARTITION_INFORMATION PartitionInfo; if(!ISNT()) { return(FALSE); } // // If we can't open the drive, assume not NTFT. // if (Drive) { if(!GetPartitionInfo(Drive,&PartitionInfo)) { return(FALSE); } } else { #ifdef UNICODE if(!GetPartitionInfo2 (NtVolumeName, &PartitionInfo)) { return(FALSE); } #else MYASSERT (FALSE); return(FALSE); #endif } // // It's FT if the partition type is marked NTFT (ie, high bit set). // if((IsRecognizedPartition(PartitionInfo.PartitionType)) && ((PartitionInfo.PartitionType & PARTITION_NTFT) != 0)) { #if defined(_IA64_) // // This check is dependant on the EFI system partition type not being // a recognized type. It's unlikely that we'd start recognizing it // before we start requiring GPT partitions on the system disk, but // just in case we'll assert before returning true for an ESP. // ASSERT(PartitionInfo.PartitionType != 0xef); #endif return TRUE; } else { return FALSE; } } BOOL IsDriveVeritas( IN TCHAR Drive, IN PCTSTR NtVolumeName ) { TCHAR name[3]; TCHAR Target[MAX_PATH]; if(ISNT()) { // // Check for Veritas volume, which links to \Device\HarddiskDmVolumes... // if (Drive) { name[0] = Drive; name[1] = TEXT(':'); name[2] = 0; if(!QueryDosDevice(name,Target,MAX_PATH)) { return FALSE; } } else { lstrcpy (Target, NtVolumeName); } if(!_tcsnicmp(Target,TEXT("\\Device\\HarddiskDm"),18)) { return(TRUE); } } return(FALSE); } // // Get Harddisk BPS // I970721 // ULONG GetHDBps( HANDLE hDisk ) { BOOL b; UINT rc; DWORD DataSize; DISK_GEOMETRY MediaInfo; b = DeviceIoControl( hDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &MediaInfo, sizeof(MediaInfo), &DataSize, NULL ); if(!b) { return(0); } else { return(MediaInfo.BytesPerSector); } } #ifdef UNICODE #ifdef _WIN64 // // define IOCTL_VOLUME_IS_PARTITION since we don't include ntddvol.h // #define IOCTL_VOLUME_IS_PARTITION CTL_CODE(IOCTL_VOLUME_BASE, 10, METHOD_BUFFERED, FILE_ANY_ACCESS) BOOL IsSoftPartition( IN TCHAR Drive, IN PCTSTR NtVolumeName ) /*++ Routine Description: Finds out whether the given volume is soft partition or not (i.e. does it have an underlying partition). NOTE : We just use the IOCTL_VOLUME_IS_PARTITION. Arguments: Drive - supplies drive letter for the volume NtVolumeName - supplies NT volume name Return Value: TRUE if the volume is soft partition otherwise FALSE. --*/ { BOOL SoftPartition; HANDLE VolumeHandle = INVALID_HANDLE_VALUE; ULONG DataSize; // // Assume that the partition is a soft one. // If we cannot determine whether or not the partition is a soft partition, then assume it is a soft // partition. This will prevent us from placing $win_nt$.~ls in such a drive. // SoftPartition = TRUE; if (Drive) { TCHAR Name[MAX_PATH]; BOOL Result; wsprintf(Name, TEXT("\\\\.\\%c:"), Drive); VolumeHandle = CreateFile(Name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (VolumeHandle != INVALID_HANDLE_VALUE) { Result = DeviceIoControl(VolumeHandle, IOCTL_VOLUME_IS_PARTITION, NULL, 0, NULL, 0, &DataSize, NULL); SoftPartition = !Result; CloseHandle(VolumeHandle); } } else { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; UNICODE_STRING DeviceName; IO_STATUS_BLOCK IoStatusBlock; // // Open the file // INIT_OBJA (&Obja, &DeviceName, NtVolumeName); Status = NtOpenFile (&VolumeHandle, (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE); if (NT_SUCCESS (Status)) { Status = NtDeviceIoControlFile(VolumeHandle, 0, NULL, NULL, &IoStatusBlock, IOCTL_VOLUME_IS_PARTITION, NULL, 0, NULL, 0); if (NT_SUCCESS(Status)) { SoftPartition = FALSE; } NtClose(VolumeHandle); } } return SoftPartition; } #else BOOL IsSoftPartition( IN TCHAR Drive, IN PCTSTR NtVolumeName ) { TCHAR name[80]; PARTITION_INFORMATION partInfo; DWORD bytes; BOOL SoftPartition = TRUE; BOOL b; HANDLE h = INVALID_HANDLE_VALUE; IO_STATUS_BLOCK IoStatusBlock; LARGE_INTEGER SoftPartitionStartingOffset; ULONG bps; NTSTATUS Status; OBJECT_ATTRIBUTES Obja; UNICODE_STRING DeviceName; DWORD DataSize; DISK_GEOMETRY MediaInfo; if( !IsDriveVeritas( Drive, NtVolumeName ) ) { return( FALSE ); } // // Assume that the partition is a soft one. // If we cannot determine whether or not the partition is a soft partition, then assume it is a soft // partition. This will prevent us from placing $win_nt$.~ls in such a drive. // SoftPartition = TRUE; if (Drive) { wsprintf(name, TEXT("\\\\.\\%c:"), Drive); h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (h == INVALID_HANDLE_VALUE) { #if DBG GetLastError(); #endif goto Exit; } b = DeviceIoControl( h, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &MediaInfo, sizeof(MediaInfo), &DataSize, NULL ); if(!b) { #if DBG GetLastError(); #endif goto CleanUp; } b = DeviceIoControl(h, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &partInfo, sizeof(partInfo), &bytes, NULL); if (!b) { #if DBG GetLastError(); #endif goto CleanUp; } } else { // // Open the file // INIT_OBJA (&Obja, &DeviceName, NtVolumeName); Status = NtOpenFile ( &h, (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE ); if (!NT_SUCCESS (Status)) { goto Exit; } Status = NtDeviceIoControlFile( h, 0, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &MediaInfo, sizeof(DISK_GEOMETRY) ); if (!NT_SUCCESS (Status)) { goto CleanUp; } Status = NtDeviceIoControlFile( h, 0, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &partInfo, sizeof(PARTITION_INFORMATION) ); if (!NT_SUCCESS (Status)) { goto CleanUp; } } bps = MediaInfo.BytesPerSector; // // Find out the number of bytes per sector of the drive // // // A soft partition always starts at sector 29 (0x1d) // SoftPartitionStartingOffset.QuadPart = 29*bps; SoftPartition = ( partInfo.StartingOffset.QuadPart == SoftPartitionStartingOffset.QuadPart ); CleanUp: if (Drive) { CloseHandle(h); } else { NtClose (h); } Exit: return( SoftPartition ); } #endif // WIN64 BOOL MyGetDiskFreeSpace ( IN PCWSTR NtVolumeName, IN PDWORD SectorsPerCluster, IN PDWORD BytesPerSector, IN PDWORD NumberOfFreeClusters, IN PDWORD TotalNumberOfClusters ) { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; HANDLE Handle; UNICODE_STRING VolumeName; IO_STATUS_BLOCK IoStatusBlock; BOOLEAN TranslationStatus; PVOID FreeBuffer; FILE_FS_SIZE_INFORMATION SizeInfo; WCHAR DefaultPath[2]; DWORD dwTemp; BOOL bAppHack; INIT_OBJA (&Obja, &VolumeName, NtVolumeName); // // Open the file // Status = NtOpenFile( &Handle, (ACCESS_MASK)FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_FREE_SPACE_QUERY ); if (!NT_SUCCESS (Status)) { return FALSE; } // // Determine the size parameters of the volume. // Status = NtQueryVolumeInformationFile( Handle, &IoStatusBlock, &SizeInfo, sizeof(SizeInfo), FileFsSizeInformation ); NtClose(Handle); if (!NT_SUCCESS(Status)) { return FALSE; } if (SizeInfo.TotalAllocationUnits.HighPart) { SizeInfo.TotalAllocationUnits.LowPart = (ULONG)-1; } if (SizeInfo.AvailableAllocationUnits.HighPart) { SizeInfo.AvailableAllocationUnits.LowPart = (ULONG)-1; } *SectorsPerCluster = SizeInfo.SectorsPerAllocationUnit; *BytesPerSector = SizeInfo.BytesPerSector; *NumberOfFreeClusters = SizeInfo.AvailableAllocationUnits.LowPart; *TotalNumberOfClusters = SizeInfo.TotalAllocationUnits.LowPart; return TRUE; } #endif BOOL IsDriveNTFS( IN TCHAR Drive ) /*++ Routine Description: Determine whether a drive is any kind of NTFT set. This routine is meaningful only when run on NT -- it always fails on Win95. Arguments: Drive - supplies drive letter to check. Return Value: Boolean value indicating whether the drive is NTFT. --*/ { TCHAR DriveName[4]; TCHAR Filesystem[256]; TCHAR VolumeName[MAX_PATH]; DWORD SerialNumber; DWORD MaxComponent; DWORD Flags; BOOL b; if(!ISNT()) { return(FALSE); } MYASSERT (Drive); DriveName[0] = Drive; DriveName[1] = TEXT(':'); DriveName[2] = TEXT('\\'); DriveName[3] = 0; b = GetVolumeInformation( DriveName, VolumeName,MAX_PATH, &SerialNumber, &MaxComponent, &Flags, Filesystem, sizeof(Filesystem)/sizeof(TCHAR) ); if(!b || !lstrcmpi(Filesystem,TEXT("NTFS"))) { return( TRUE ); } return( FALSE ); } DWORD MapFileForRead( IN LPCTSTR FileName, OUT PDWORD FileSize, OUT PHANDLE FileHandle, OUT PHANDLE MappingHandle, OUT PVOID *BaseAddress ) /*++ Routine Description: Open and map an entire file for read access. The file must not be 0-length or the routine fails. Arguments: FileName - supplies pathname to file to be mapped. FileSize - receives the size in bytes of the file. FileHandle - receives the win32 file handle for the open file. The file will be opened for generic read access. MappingHandle - receives the win32 handle for the file mapping object. This object will be for read access. This value is undefined if the file being opened is 0 length. BaseAddress - receives the address where the file is mapped. This value is undefined if the file being opened is 0 length. Return Value: NO_ERROR if the file was opened and mapped successfully. The caller must unmap the file with UnmapFile when access to the file is no longer desired. Win32 error code if the file was not successfully mapped. --*/ { DWORD rc; // // Open the file -- fail if it does not exist. // *FileHandle = CreateFile( FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if(*FileHandle == INVALID_HANDLE_VALUE) { rc = GetLastError(); } else { // // Get the size of the file. // *FileSize = GetFileSize(*FileHandle,NULL); if(*FileSize == (DWORD)(-1)) { rc = GetLastError(); } else { // // Create file mapping for the whole file. // *MappingHandle = CreateFileMapping( *FileHandle, NULL, PAGE_READONLY, 0, *FileSize, NULL ); if(*MappingHandle) { // // Map the whole file. // *BaseAddress = MapViewOfFile( *MappingHandle, FILE_MAP_READ, 0, 0, *FileSize ); if(*BaseAddress) { return(NO_ERROR); } rc = GetLastError(); CloseHandle(*MappingHandle); } else { rc = GetLastError(); } } CloseHandle(*FileHandle); } return(rc); } DWORD UnmapFile( IN HANDLE MappingHandle, IN PVOID BaseAddress ) /*++ Routine Description: Unmap and close a file. Arguments: MappingHandle - supplies the win32 handle for the open file mapping object. BaseAddress - supplies the address where the file is mapped. Return Value: NO_ERROR if the file was unmapped successfully. Win32 error code if the file was not successfully unmapped. --*/ { DWORD rc; rc = UnmapViewOfFile(BaseAddress) ? NO_ERROR : GetLastError(); if(!CloseHandle(MappingHandle)) { if(rc == NO_ERROR) { rc = GetLastError(); } } return(rc); } VOID GenerateCompressedName( IN LPCTSTR Filename, OUT LPTSTR CompressedName ) /*++ Routine Description: Given a filename, generate the compressed form of the name. The compressed form is generated as follows: Look backwards for a dot. If there is no dot, append "._" to the name. If there is a dot followed by 0, 1, or 2 charcaters, append "_". Otherwise assume there is a 3-character extension and replace the third character after the dot with "_". Arguments: Filename - supplies filename whose compressed form is desired. CompressedName - receives compressed form. This routine assumes that this buffer is MAX_PATH TCHARs in size. Return Value: None. --*/ { LPTSTR p,q; // // Leave room for the worst case, namely where there's no extension // (and we thus have to append ._). // lstrcpyn(CompressedName,Filename,MAX_PATH-2); p = _tcsrchr(CompressedName,TEXT('.')); q = _tcsrchr(CompressedName,TEXT('\\')); if(q < p) { // // If there are 0, 1, or 2 characters after the dot, just append // the underscore. p points to the dot so include that in the length. // if(lstrlen(p) < 4) { lstrcat(CompressedName,TEXT("_")); } else { // // Assume there are 3 characters in the extension and replace // the final one with an underscore. // p[3] = TEXT('_'); } } else { // // No dot, just add ._. // lstrcat(CompressedName,TEXT("._")); } } DWORD CreateMultiLevelDirectory( IN LPCTSTR Directory ) /*++ Routine Description: This routine ensures that a multi-level path exists by creating individual levels one at a time. It can handle either paths of form x:... or \\?\Volume{... Arguments: Directory - supplies fully-qualified Win32 pathspec of directory to create Return Value: Win32 error code indicating outcome. --*/ { TCHAR Buffer[MAX_PATH]; PTCHAR p,q; TCHAR c; BOOL Done; DWORD d = ERROR_SUCCESS; INT Skip=0; lstrcpyn(Buffer,Directory,MAX_PATH); // // If it already exists do nothing. (We do this before syntax checking // to allow for remote paths that already exist. This is needed for // remote boot machines.) // d = GetFileAttributes(Buffer); if(d != (DWORD)(-1)) { return((d & FILE_ATTRIBUTE_DIRECTORY) ? NO_ERROR : ERROR_DIRECTORY); } // // Check path format // c = (TCHAR)CharUpper((LPTSTR)Buffer[0]); if(((c < TEXT('A')) || (c > TEXT('Z')) || (Buffer[1] != TEXT(':'))) && c != TEXT('\\')) { return(ERROR_INVALID_PARAMETER); } if (c != TEXT('\\')) { // // Ignore drive roots, which we allow to be either x:\ or x:. // if(Buffer[2] != TEXT('\\')) { return(Buffer[2] ? ERROR_INVALID_PARAMETER : ERROR_SUCCESS); } q = Buffer + 3; if(*q == 0) { return(ERROR_SUCCESS); } } else { // // support \\server\share[\xxx] format // q = NULL; if (Buffer[1] != TEXT('\\') || Buffer[1] != 0 && Buffer[2] == TEXT('\\')) { return(ERROR_INVALID_PARAMETER); } q = _tcschr (&Buffer[2], TEXT('\\')); if (!q) { return(ERROR_INVALID_PARAMETER); } if (q[1] == TEXT('\\')) { return(ERROR_INVALID_PARAMETER); } q = _tcschr (&q[1], TEXT('\\')); if (!q) { return(ERROR_SUCCESS); } q++; #ifdef UNICODE // // Hack to make sure the system partition case works on IA64 (arc) // We beieve this should be the only case where we use a // GlobalRoot style name as the other cases deal with OEM partitions etc. // which we should never touch. WE skip over by the length of // SystemPartitionVolumeGuid. We take care of the \ present at the end. // if (SystemPartitionVolumeGuid != NULL && _wcsnicmp (Buffer, SystemPartitionVolumeGuid, (wcslen(SystemPartitionVolumeGuid)-1)) == 0 ){ Skip = wcslen(SystemPartitionVolumeGuid)-1; } else if (_wcsnicmp (Buffer, L"\\\\?\\Volume{", 11) == 0 && Buffer[47] == L'}') { // // skip over the VolumeGUID part // Skip = 48; } if (Skip > 0) { if (Buffer[Skip] == 0) { return ERROR_SUCCESS; } q = Buffer + Skip + 1; } #endif } Done = FALSE; do { // // Locate the next path sep char. If there is none then // this is the deepest level of the path. // if(p = _tcschr(q,TEXT('\\'))) { *p = 0; } else { Done = TRUE; } // // Create this portion of the path. // if(CreateDirectory(Buffer,NULL)) { d = ERROR_SUCCESS; } else { d = GetLastError(); if(d == ERROR_ALREADY_EXISTS) { d = ERROR_SUCCESS; } } if(d == ERROR_SUCCESS) { // // Put back the path sep and move to the next component. // if(!Done) { *p = TEXT('\\'); q = p+1; } } else { Done = TRUE; } } while(!Done); return(d); } BOOL ForceFileNoCompress( IN LPCTSTR Filename ) /*++ Routine Description: This routine makes sure that a file on a volume that supports per-file compression is not compressed. The caller need not ensure that the volume actually supports this, since this routine will query the attributes of the file before deciding whether any operation is actually necessary, and the compressed attribute will not be set on volumes that don't support per-file compression. It assumed that the file exists. If the file does not exist, this routine will fail. Arguments: Filename - supplies the filename of the file to mke uncompressed. Return Value: Boolean value indicating outcome. If FALSE, last error is set. --*/ { ULONG d; HANDLE h; BOOL b; USHORT u; DWORD Attributes; Attributes = GetFileAttributes(Filename); if(Attributes == (DWORD)(-1)) { return(FALSE); } if(!(Attributes & FILE_ATTRIBUTE_COMPRESSED)) { return(TRUE); } // // Temporarily nullify attributes that might prevent opening // the file for read-write access. // // We preserve the 'standard' attributes that the file might have, // to be restored later. // Attributes &= (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE); SetFileAttributes(Filename,FILE_ATTRIBUTE_NORMAL); h = CreateFile( Filename, FILE_READ_DATA | FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN, NULL ); if(h == INVALID_HANDLE_VALUE) { SetFileAttributes(Filename,Attributes); return(FALSE); } u = 0; b = DeviceIoControl( h, FSCTL_SET_COMPRESSION, &u, sizeof(USHORT), NULL, 0, &d, FALSE); d = GetLastError(); CloseHandle(h); SetFileAttributes(Filename,Attributes); SetLastError(d); return(b); } BOOL IsCurrentOsServer( void ) { LONG l; HKEY hKey; DWORD d; DWORD Size; TCHAR Value[100]; DWORD Type; l = RegCreateKeyEx( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"), 0, NULL, 0, KEY_QUERY_VALUE, NULL, &hKey, &d ); if (l != NO_ERROR) { return FALSE; } Size = sizeof(Value); l = RegQueryValueEx(hKey,TEXT("ProductType"),NULL,&Type,(LPBYTE)Value,&Size); RegCloseKey(hKey); if (l != NO_ERROR) { return FALSE; } if (lstrcmpi(Value,TEXT("winnt")) == 0) { return FALSE; } return TRUE; } BOOL IsCurrentAdvancedServer( void ) { LONG l; HKEY hKey; DWORD d; DWORD Size; TCHAR Value[100]; DWORD Type; l = RegCreateKeyEx( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"), 0, NULL, 0, KEY_QUERY_VALUE, NULL, &hKey, &d ); if (l != NO_ERROR) { return FALSE; } Size = sizeof(Value); l = RegQueryValueEx(hKey,TEXT("ProductType"),NULL,&Type,(LPBYTE)Value,&Size); RegCloseKey(hKey); if (l != NO_ERROR) { return FALSE; } if (lstrcmpi(Value,TEXT("lanmannt")) == 0) { return TRUE; } return FALSE; } BOOL ConcatenateFile( IN HANDLE hOpenFile, IN LPTSTR FileName ) /*++ Routine Description: This routine will go load the named file, and concatenate its contents into the open file. Arguments: FileName The name of the file we're going to concatenate. Return Value: TRUE Everything went okay. FALSE We failed. --*/ { HANDLE hFile, hFileMapping; DWORD FileSize, BytesWritten; BYTE *pbFile; BOOL ReturnValue = TRUE; // // Open the file... // hFile = CreateFile( FileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if( hFile != INVALID_HANDLE_VALUE ) { // // Map the file... // hFileMapping = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL ); if( hFileMapping ) { pbFile = MapViewOfFile( hFileMapping, FILE_MAP_READ, 0, 0, 0 ); if( pbFile ) { // // Write the file... // FileSize = GetFileSize( hFile, NULL ); if( FileSize != 0xFFFFFFFF ) { if( hOpenFile ) { WriteFile( hOpenFile, pbFile, FileSize, &BytesWritten, NULL ); } else { ReturnValue = FALSE; } } else { ReturnValue = FALSE; } UnmapViewOfFile( pbFile ); } else { ReturnValue = FALSE; } CloseHandle( hFileMapping ); } else { ReturnValue = FALSE; } CloseHandle( hFile ); } else { ReturnValue = FALSE; } return( ReturnValue ); } BOOL FileExists( IN PCTSTR FileName, OUT PWIN32_FIND_DATA FindData OPTIONAL ) /*++ Routine Description: Determine if a file exists and is accessible. Errormode is set (and then restored) so the user will not see any pop-ups. Arguments: FileName - supplies full path of file to check for existance. FindData - if specified, receives find data for the file. Return Value: TRUE if the file exists and is accessible. FALSE if not. GetLastError() returns extended error info. --*/ { WIN32_FIND_DATA findData; HANDLE FindHandle; UINT OldMode; DWORD Error; OldMode = SetErrorMode(SEM_FAILCRITICALERRORS); FindHandle = FindFirstFile(FileName,&findData); if(FindHandle == INVALID_HANDLE_VALUE) { Error = GetLastError(); } else { FindClose(FindHandle); if(FindData) { *FindData = findData; } Error = NO_ERROR; } SetErrorMode(OldMode); SetLastError(Error); return (Error == NO_ERROR); } BOOL DoesDirectoryExist ( IN PCTSTR DirSpec ) /*++ Routine Description: Determine if a directory exists and is accessible. This routine works even with the root of drives or with the root of network shares (like \\server\share). Arguments: DirSpec - supplies full path of dir to check for existance; Return Value: TRUE if the dir exists and is accessible. --*/ { TCHAR pattern[MAX_PATH]; BOOL b = FALSE; if (DirSpec) { if (BuildPath2 (pattern, MAX_PATH, DirSpec, TEXT("*"))) { WIN32_FIND_DATA fd; HANDLE h = FindFirstFile (pattern, &fd); if (h != INVALID_HANDLE_VALUE) { FindClose (h); b = TRUE; } } } return b; } #ifdef _X86_ BOOLEAN IsValidDrive( TCHAR Drive ) { /*++ Routine Description: This routine check formatted disk type NEC98 of NT4 has supported NEC98 format and PC-AT format. But BIOS is handling only NEC98 format. So We need setup Boot stuff to ONLY NEC98 formated HD. Arguments: Drive Drive letter. Return Value: TRUE Dive is NEC98 format. FALSE Drive is not NEC98 format. --*/ HANDLE hDisk; TCHAR HardDiskName[] = TEXT("\\\\.\\?:"); PUCHAR pBuffer,pUBuffer; WCHAR Buffer[128]; WCHAR DevicePath[128]; WCHAR DriveName[3]; WCHAR DiskNo; STORAGE_DEVICE_NUMBER number; PWCHAR p; ULONG bps; NTSTATUS Sts; DWORD DataSize,ExtentSize; BOOL b; PVOLUME_DISK_EXTENTS Extent; if (!ISNT()) return TRUE; HardDiskName[4] = Drive; DriveName[0] = Drive; DriveName[1] = ':'; DriveName[2] = 0; if(QueryDosDeviceW(DriveName, Buffer, sizeof(Buffer)/sizeof(TCHAR))) { if (BuildNumber <= NT40){ //check NT Version // // QueryDosDevice in NT3.51 is buggy. // This API return "\\Harddisk\...." or // "\\harddisk\...." // We need work around. // p = wcsstr(Buffer, L"arddisk"); if (!p) { return FALSE; } DiskNo = (*(p + 7) - 0x30); } else { hDisk = CreateFile( HardDiskName, 0, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if(hDisk == INVALID_HANDLE_VALUE) { return FALSE; } b = DeviceIoControl(hDisk, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &number, sizeof(number), &DataSize, NULL); if (b) { DiskNo = (TCHAR) number.DeviceNumber; } else { Extent = malloc(1024); ExtentSize = 1024; if(!Extent) { CloseHandle( hDisk ); return FALSE; } b = DeviceIoControl(hDisk, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, (PVOID)Extent, ExtentSize, &DataSize, NULL); if (!b) { free(Extent); CloseHandle( hDisk ); return FALSE; } if (Extent->NumberOfDiskExtents != 1){ free(Extent); CloseHandle( hDisk ); return FALSE; } DiskNo = (TCHAR)Extent->Extents->DiskNumber; free(Extent); } CloseHandle(hDisk); } swprintf(DevicePath, L"\\\\.\\PHYSICALDRIVE%u", DiskNo); hDisk = CreateFileW( DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if(hDisk == INVALID_HANDLE_VALUE) { return FALSE; } if ((bps = GetHDBps(hDisk)) == 0){ CloseHandle(hDisk); return FALSE; } pUBuffer = MALLOC(bps * 2); pBuffer = ALIGN(pUBuffer, bps); RtlZeroMemory(pBuffer, bps); Sts = SpReadWriteDiskSectors(hDisk,0,1,bps,pBuffer, NEC_READSEC); if(!NT_SUCCESS(Sts)) { FREE(pUBuffer); CloseHandle(hDisk); return FALSE; } if (!(pBuffer[4] == 'I' && pBuffer[5] == 'P' && pBuffer[6] == 'L' && pBuffer[7] == '1')){ FREE(pUBuffer); CloseHandle(hDisk); return FALSE; } FREE(pUBuffer); CloseHandle(hDisk); return TRUE; } return FALSE; } BOOLEAN CheckATACardonNT4( HANDLE hDisk ) { // // NT4, NT3.51 for NEC98. // NEC98 does not handle to boot from PCMCIA ATA card disk. // So we need to check ATA Disk. // // Return // TRUE is ATA Card // FALSE is Other // #define IOCTL_DISK_GET_FORMAT_MEDIA CTL_CODE(IOCTL_DISK_BASE, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FORMAT_MEDIA_98 0 // TYPE NEC98 #define FORMAT_MEDIA_AT 1 // TYPE PC-AT #define FORMAT_MEDIA_OTHER 2 // Unknown struct _OutBuffer { ULONG CurrentFormatMedia; ULONG InitializeFormatMedia; } OutBuffer; DWORD ReturnedByteCount; if (!(DeviceIoControl(hDisk, IOCTL_DISK_GET_FORMAT_MEDIA, NULL, 0, &OutBuffer, sizeof(struct _OutBuffer), &ReturnedByteCount, NULL ) )){ return FALSE; } if (OutBuffer.InitializeFormatMedia == FORMAT_MEDIA_AT){ return TRUE; } return FALSE; } #endif BOOL IsMachineSupported( OUT PCOMPATIBILITY_ENTRY CompEntry ) /*++ Routine Description: This function determines whether or not the machine is supported by the version of NT to be installed. Arguments: CompEntry - If the machine is not supported, the Compatability Entry is updated to describe why the machine is not supported. Return Value: Boolean value indicating whether the machine is supported. --*/ { TCHAR SetupLogPath[MAX_PATH]; TCHAR KeyName[MAX_PATH]; TCHAR HalName[MAX_PATH]; LPTSTR p; LPTSTR szHalDll = TEXT("hal.dll"); LPTSTR SectionName; LPTSTR UnsupportedName; BOOL b; // // Assume that the machine is supported // b = TRUE; #ifdef _X86_ try { ULONG Name0, Name1, Name2, Family, Flags; _asm { push ebx ;; save ebx mov eax, 0 ;; get processor vendor _emit 00fh ;; CPUID(0) _emit 0a2h ;; mov Name0, ebx ;; Name[0-3] mov Name1, edx ;; Name[4-7] mov Name2, ecx ;; Name[8-11] mov eax, 1 ;; get family/model/stepping and features _emit 00fh ;; CPUID(1) _emit 0a2h mov Family, eax ;; save family/model/stepping mov Flags, edx ;; save flags returned by CPUID pop ebx ;; restore ebx } // // Check the cmpxchg8b flag in the flags returned by CPUID. // if ((Flags & 0x100) == 0) { // // This processor doesn't support the CMPXCHG instruction // which is required for Whistler. // // Some processors actually do support it but claim they // don't because of a bug in NT 4. See if this processor // is one of these. // if (!(((Name0 == 'uneG') && (Name1 == 'Teni') && (Name2 == '68xM') && (Family >= 0x542)) || (Name0 == 'tneC') && (Name1 == 'Hrua') && (Name2 == 'slua') && (Family >= 0x500))) { b = FALSE; } } } except(EXCEPTION_EXECUTE_HANDLER) { // // If this processor doesn't support CPUID, we don't // run on it. // b = FALSE; } if (!b) { CompEntry->HtmlName = TEXT("cpufeat.htm"); CompEntry->TextName = TEXT("cpufeat.txt"); SectionName = TEXT("UnsupportedArchitectures"); UnsupportedName = TEXT("missprocfeat"); } #endif // _X86_ if( b && ISNT() ) { // // Build the path to setup.log // MyGetWindowsDirectory( SetupLogPath, MAX_PATH ); ConcatenatePaths( SetupLogPath, TEXT("repair\\setup.log"), MAX_PATH ); // // Find out the actual name of the hal installed // if (!IsArc()) { #ifdef _X86_ // // On BIOS, look for %windir%\system32\hal.dll in the section // [Files.WinNt] // GetSystemDirectory( KeyName, MAX_PATH ); ConcatenatePaths(KeyName, szHalDll, MAX_PATH ); SectionName = TEXT("Files.WinNt"); // // While we are at it, see if this is Windows 2000 or higher // to see if the hal should be preserved or not // #ifdef UNICODE if (BUILDNUM() >= 2195) { PCHAR halName; halName = FindRealHalName( KeyName ); if (halName) { WriteAcpiHalValue = TRUE; if (!strcmp(halName,"halacpi") || !strcmp(halName,"halmacpi") || !strcmp(halName,"halaacpi")) { AcpiHalValue = TRUE; } } } #endif // UNICODE #endif // _X86_ } else { #ifdef UNICODE // Always true for ARC, never true for Win9x upgrade // // On ARC, look for hal.dll in the section [Files.SystemPartition] // lstrcpy( KeyName, szHalDll ); SectionName = TEXT("Files.SystemPartition"); #endif // UNICODE } // if (!IsArc()) GetPrivateProfileString( SectionName, KeyName, TEXT(""), HalName, sizeof(HalName)/sizeof(TCHAR), SetupLogPath ); // // GetPrivateProfileString() will strip the first and last '"' from the logged value, // so find the next '"' character and replace it with NUL, and we will end up with // the actual hal name. // if( lstrlen(HalName) && ( p = _tcschr( HalName, TEXT('"') ) ) ) { *p = TEXT('\0'); // // Find out if the hal is listed in [UnsupportedArchitectures] (dosnet.inf) // SectionName = TEXT("UnsupportedArchitectures"); b = !InfDoesLineExistInSection( MainInf, SectionName, HalName ); UnsupportedName = HalName; } } // // If architecture is not supported, look up the description. // if( !b ) { CompEntry->Description = (LPTSTR)InfGetFieldByKey( MainInf, SectionName, UnsupportedName, 0 ); } return( b ); } BOOL DoesCurrentSystemHasThirdPartyKernel( VOID ); BOOL SystemKernelCheck( PCOMPAIBILITYCALLBACK CompatibilityCallback, LPVOID Context ) { BOOL bResult = TRUE; PWSTR Buffer; #if defined(UNICODE) && defined(_X86_) COMPATIBILITY_ENTRY CompEntry; if(!DoesCurrentSystemHasThirdPartyKernel()){ return FALSE; } FormatMessageW( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER, hInst, MSG_SYSTEM_HAS_THIRD_PARTY_KERNEL, 0, (PWSTR)&Buffer, 0, NULL ); CompEntry.Description = Buffer; CompEntry.HtmlName = TEXT("compdata\\krnlchk.htm"); CompEntry.TextName = TEXT("compdata\\krnlchk.txt"); CompEntry.RegKeyName = NULL; CompEntry.RegValName = NULL; CompEntry.RegValDataSize = 0; CompEntry.RegValData = NULL; CompEntry.SaveValue = NULL; CompEntry.Flags = 0; CompEntry.InfName = NULL; CompEntry.InfSection = NULL; bResult = CompatibilityCallback(&CompEntry, Context); LocalFree(Buffer); #endif return bResult; } BOOL UnsupportedArchitectureCheck( PCOMPAIBILITYCALLBACK CompatibilityCallback, LPVOID Context ) /*++ Routine Description: Check if the machine is no longer supported by Windows NT. This routine is meaningful only when run on NT -- it always succeeds on Win95. Arguments: CompatibilityCallback - pointer to call back function Context - context pointer Return Value: Returns always TRUE. --*/ { COMPATIBILITY_ENTRY CompEntry; CompEntry.Description = TEXT("mca");//BUGBUG: must be changed #ifdef _X86_ CompEntry.HtmlName = TEXT("mca.htm"); CompEntry.TextName = TEXT("mca.txt"); #else CompEntry.HtmlName = TEXT(""); CompEntry.TextName = TEXT(""); #endif CompEntry.RegKeyName = NULL; CompEntry.RegValName = NULL; CompEntry.RegValDataSize = 0; CompEntry.RegValData = NULL; CompEntry.SaveValue = NULL; CompEntry.Flags = 0; CompEntry.InfName = NULL; CompEntry.InfSection = NULL; if( !IsMachineSupported( &CompEntry ) ) { if(!CompatibilityCallback(&CompEntry, Context)){ DWORD Error; Error = GetLastError(); } } return( TRUE ); } BOOL GetUserPrintableFileSizeString( IN DWORDLONG Size, OUT LPTSTR Buffer, IN DWORD BufferSize ) /*++ Routine Description: Takes a size and comes up with a printable version of this size, using the appropriate size format (ie., KB, MB, GB, Bytes, etc.) Arguments: Size - size to be converted (in bytes) Buffer - string buffer to receive the data BufferSize - indicates the buffer size, *in characters* Return Value: TRUE indicates success, FALSE indicates failure. If we fail, call GetLastError() to get extended failure status. --*/ { LPTSTR NumberString; UINT uResource; TCHAR ResourceString[100]; DWORD cb; DWORD d; BOOL RetVal = FALSE; DWORDLONG TopPart; DWORDLONG BottomPart; // // Determine which resource string to use // if (Size < 1024) { uResource = IDS_SIZE_BYTES; TopPart = 0; BottomPart = 1; wsprintf(ResourceString, TEXT("%u"), Size); } else if (Size < (1024 * 1024)) { uResource = IDS_SIZE_KBYTES; TopPart = (Size%1024)*100; BottomPart = 1024; wsprintf(ResourceString, TEXT("%u.%02u"), (DWORD) (Size / 1024), (DWORD)(TopPart/BottomPart)); } else if (Size < (1024 * 1024 * 1024)) { uResource = IDS_SIZE_MBYTES; TopPart = (Size%(1024*1024))*100; BottomPart = 1024*1024; wsprintf(ResourceString, TEXT("%u.%02u"), (DWORD)(Size / (1024 * 1024)), (DWORD)(TopPart/BottomPart) ); } else { uResource = IDS_SIZE_GBYTES; TopPart = (Size%(1024*1024*1024))*100; BottomPart = 1024*1024*1024; wsprintf(ResourceString, TEXT("%u.%02u"), (DWORD)(Size / (1024 * 1024 * 1024)), (DWORD)(TopPart/BottomPart) ); } // Format the number string cb = GetNumberFormat(LOCALE_USER_DEFAULT, 0, ResourceString, NULL, NULL, 0); NumberString = (LPTSTR) MALLOC((cb + 1) * sizeof(TCHAR)); if (!NumberString) { d = ERROR_NOT_ENOUGH_MEMORY; RetVal = FALSE; goto e0; } GetNumberFormat(LOCALE_USER_DEFAULT, 0, ResourceString, NULL, NumberString, cb); LoadString(hInst, uResource, ResourceString, sizeof(ResourceString)/sizeof(TCHAR)); // // it's tricky to know if we really have enough space in the buffer since // we're dealing with substitution strings. The below is an // approximate check since we assume that the number string is likely // larger than our substitution string (%s) that we're filling in. // if (BufferSize > (DWORD)(lstrlen(ResourceString) + lstrlen(NumberString) + 1)) { wsprintf(Buffer, ResourceString, NumberString); d = ERROR_SUCCESS; RetVal = TRUE; } else { d = ERROR_INSUFFICIENT_BUFFER; RetVal = FALSE; } FREE(NumberString); e0: SetLastError(d); return(RetVal); } BOOL BuildSystemPartitionPathToFile ( IN PCTSTR FileName, OUT PTSTR Path, IN DWORD BufferSizeChars ) { // // must have a root // if(SystemPartitionDriveLetter) { Path[0] = SystemPartitionDriveLetter; Path[1] = TEXT(':'); Path[2] = 0; } else { #ifdef UNICODE if (SystemPartitionVolumeGuid) { lstrcpyn (Path, SystemPartitionVolumeGuid, BufferSizeChars); } else #endif { MYASSERT (FALSE); return FALSE; } } ConcatenatePaths (Path, FileName, BufferSizeChars); return TRUE; } PTSTR BuildPath ( IN PTSTR DestPath, IN PCTSTR Path1, IN PCTSTR Path2 ) { PTSTR p; p = _tcsrchr (Path1, TEXT('\\')); if (p && !p[1]) { *p = 0; } if (*Path2 == TEXT('\\')) { Path2++; } return DestPath + wsprintf (DestPath, TEXT("%s\\%s"), Path1, Path2); } PTSTR BuildPath2 ( IN PTSTR DestPath, IN DWORD Chars, IN PCTSTR Path1, IN PCTSTR Path2 ) { INT i = _sntprintf (DestPath, Chars, TEXT("%s\\%s"), Path1, Path2); if (i < 0) { return NULL; } return DestPath + i; } BOOL EnumFirstFilePattern ( OUT PFILEPATTERN_ENUM Enum, IN PCTSTR Dir, IN PCTSTR FilePattern ) { TCHAR pattern[MAX_PATH]; BuildPath (pattern, Dir, FilePattern); Enum->Handle = FindFirstFile (pattern, &Enum->FindData); if (Enum->Handle == INVALID_HANDLE_VALUE) { return FALSE; } lstrcpy (Enum->FullPath, Dir); Enum->FileName = _tcschr (Enum->FullPath, 0); *Enum->FileName++ = TEXT('\\'); lstrcpy (Enum->FileName, Enum->FindData.cFileName); if (Enum->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (!lstrcmp (Enum->FindData.cFileName, TEXT (".")) || !lstrcmp (Enum->FindData.cFileName, TEXT (".."))) { return EnumNextFilePattern (Enum); } } return TRUE; } BOOL EnumNextFilePattern ( IN OUT PFILEPATTERN_ENUM Enum ) { again: if (!FindNextFile (Enum->Handle, &Enum->FindData)) { AbortEnumFilePattern (Enum); return FALSE; } lstrcpy (Enum->FileName, Enum->FindData.cFileName); if (Enum->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (!lstrcmp (Enum->FindData.cFileName, TEXT (".")) || !lstrcmp (Enum->FindData.cFileName, TEXT (".."))) { goto again; } } return TRUE; } VOID AbortEnumFilePattern ( IN OUT PFILEPATTERN_ENUM Enum ) { if (Enum->Handle != INVALID_HANDLE_VALUE) { FindClose (Enum->Handle); Enum->Handle = INVALID_HANDLE_VALUE; } } BOOL EnumFirstFilePatternRecursive ( OUT PFILEPATTERNREC_ENUM Enum, IN PCTSTR Dir, IN PCTSTR FilePattern, IN DWORD ControlFlags ) { PFILEENUMLIST dir; dir = CreateFileEnumCell (Dir, FilePattern, 0, ENUM_FIRSTFILE); if (!dir) { return FALSE; } Enum->FilePattern = DupString (FilePattern); if (!Enum->FilePattern) { DeleteFileEnumCell (dir); return FALSE; } Enum->DirCurrent = dir; Enum->FindData = &dir->Enum.FindData; Enum->RootLen = lstrlen (Dir) + 1; Enum->ControlFlags = ControlFlags; Enum->Handle = INVALID_HANDLE_VALUE; return EnumNextFilePatternRecursive (Enum); } BOOL EnumNextFilePatternRecursive ( IN OUT PFILEPATTERNREC_ENUM Enum ) { TCHAR pattern[MAX_PATH]; WIN32_FIND_DATA fd; PFILEENUMLIST dir; while (Enum->DirCurrent) { if (Enum->ControlFlags & ECF_ABORT_ENUM_DIR) { // // caller wants to abort enum of this subdir // remove the current node from list // Enum->ControlFlags &= ~ECF_ABORT_ENUM_DIR; dir = Enum->DirCurrent->Next; DeleteFileEnumCell (Enum->DirCurrent); Enum->DirCurrent = dir; if (dir) { Enum->FindData = &dir->Enum.FindData; } continue; } switch (Enum->DirCurrent->EnumState) { case ENUM_FIRSTFILE: if (EnumFirstFilePattern (&Enum->DirCurrent->Enum, Enum->DirCurrent->Dir, Enum->FilePattern)) { Enum->DirCurrent->EnumState = ENUM_NEXTFILE; Enum->FullPath = Enum->DirCurrent->Enum.FullPath; Enum->SubPath = Enum->FullPath + Enum->RootLen; Enum->FileName = Enum->DirCurrent->Enum.FileName; return TRUE; } Enum->DirCurrent->EnumState = ENUM_SUBDIRS; break; case ENUM_NEXTFILE: if (EnumNextFilePattern (&Enum->DirCurrent->Enum)) { Enum->FullPath = Enum->DirCurrent->Enum.FullPath; Enum->SubPath = Enum->FullPath + Enum->RootLen; Enum->FileName = Enum->DirCurrent->Enum.FileName; return TRUE; } Enum->DirCurrent->EnumState = ENUM_SUBDIRS; // // fall through // case ENUM_SUBDIRS: BuildPath (pattern, Enum->DirCurrent->Dir, TEXT("*.*")); Enum->Handle = FindFirstFile (pattern, &fd); if (Enum->Handle != INVALID_HANDLE_VALUE) { do { if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (!lstrcmp (fd.cFileName, TEXT (".")) || !lstrcmp (fd.cFileName, TEXT (".."))) { continue; } wsprintf (pattern, TEXT("%s\\%s"), Enum->DirCurrent->Dir, fd.cFileName); if (!InsertList ( (PGENERIC_LIST*)&Enum->DirCurrent, (PGENERIC_LIST) CreateFileEnumCell ( pattern, Enum->FilePattern, fd.dwFileAttributes, Enum->ControlFlags & ECF_ENUM_SUBDIRS ? ENUM_SUBDIR : ENUM_FIRSTFILE ) )) { AbortEnumFilePatternRecursive (Enum); return FALSE; } } } while (FindNextFile (Enum->Handle, &fd)); FindClose (Enum->Handle); Enum->Handle = INVALID_HANDLE_VALUE; } // // remove the current node from list // dir = Enum->DirCurrent->Next; DeleteFileEnumCell (Enum->DirCurrent); Enum->DirCurrent = dir; if (dir) { Enum->FindData = &dir->Enum.FindData; } break; case ENUM_SUBDIR: Enum->FullPath = Enum->DirCurrent->Dir; Enum->SubPath = Enum->FullPath + Enum->RootLen; Enum->FileName = _tcsrchr (Enum->FullPath, TEXT('\\')) + 1; Enum->DirCurrent->EnumState = ENUM_FIRSTFILE; return TRUE; } } return FALSE; } VOID AbortEnumFilePatternRecursive ( IN OUT PFILEPATTERNREC_ENUM Enum ) { if (Enum->DirCurrent) { DeleteFileEnumList (Enum->DirCurrent); Enum->DirCurrent = NULL; } if (Enum->Handle != INVALID_HANDLE_VALUE) { FindClose (Enum->Handle); Enum->Handle = INVALID_HANDLE_VALUE; } } BOOL CopyTree ( IN PCTSTR SourceRoot, IN PCTSTR DestRoot ) { DWORD rc = ERROR_SUCCESS; FILEPATTERNREC_ENUM e; TCHAR destFile[MAX_PATH]; PTSTR p; if (EnumFirstFilePatternRecursive (&e, SourceRoot, TEXT("*"), 0)) { do { BuildPath (destFile, DestRoot, e.SubPath); p = _tcsrchr (destFile, TEXT('\\')); if (!p) { continue; } *p = 0; rc = CreateMultiLevelDirectory (destFile); if (rc != ERROR_SUCCESS) { AbortEnumFilePatternRecursive (&e); break; } *p = TEXT('\\'); SetFileAttributes (destFile, FILE_ATTRIBUTE_NORMAL); if (!CopyFile (e.FullPath, destFile, FALSE)) { rc = GetLastError (); AbortEnumFilePatternRecursive (&e); break; } } while (EnumNextFilePatternRecursive (&e)); } else { rc = GetLastError (); } SetLastError (rc); return rc == ERROR_SUCCESS; } PSTRINGLIST CreateStringCell ( IN PCTSTR String ) { PSTRINGLIST p = MALLOC (sizeof (STRINGLIST)); if (p) { ZeroMemory (p, sizeof (STRINGLIST)); if (String) { p->String = DupString (String); if (!p->String) { FREE (p); p = NULL; } } else { p->String = NULL; } } return p; } VOID DeleteStringCell ( IN PSTRINGLIST Cell ) { if (Cell) { FREE (Cell->String); FREE (Cell); } } VOID DeleteStringList ( IN PSTRINGLIST List ) { PSTRINGLIST p, q; for (p = List; p; p = q) { q = p->Next; DeleteStringCell (p); } } BOOL FindStringCell ( IN PSTRINGLIST StringList, IN PCTSTR String, IN BOOL CaseSensitive ) { PSTRINGLIST p; INT i; if (!StringList || !String) { return FALSE; } for (p = StringList; p; p = p->Next) { i = CaseSensitive ? _tcscmp (String, p->String) : _tcsicmp (String, p->String); if (i == 0) { return TRUE; } } return FALSE; } PFILEENUMLIST CreateFileEnumCell ( IN PCTSTR Dir, IN PCTSTR FilePattern, IN DWORD Attributes, IN DWORD EnumState ) { PFILEENUMLIST p = MALLOC (sizeof (FILEENUMLIST)); if (p) { ZeroMemory (p, sizeof (FILEENUMLIST)); p->Enum.FindData.dwFileAttributes = Attributes; p->EnumState = EnumState; p->Dir = DupString (Dir); if (!p->Dir) { FREE (p); p = NULL; } } return p; } VOID DeleteFileEnumCell ( IN PFILEENUMLIST Cell ) { if (Cell) { FREE (Cell->Dir); FREE (Cell); } } BOOL InsertList ( IN OUT PGENERIC_LIST* List, IN PGENERIC_LIST NewList ) { PGENERIC_LIST p; if (!NewList) { return FALSE; } if (*List) { for (p = *List; p->Next; p = p->Next) ; p->Next = NewList; } else { *List = NewList; } return TRUE; } VOID DeleteFileEnumList ( IN PFILEENUMLIST NewList ) { PFILEENUMLIST p, q; for (p = NewList; p; p = q) { q = p->Next; DeleteFileEnumCell (p); } } PCTSTR FindSubString ( IN PCTSTR String, IN TCHAR Separator, IN PCTSTR SubStr, IN BOOL CaseSensitive ) { SIZE_T len1, len2; PCTSTR end; MYASSERT (Separator); MYASSERT (!_istleadbyte (Separator)); MYASSERT (SubStr); MYASSERT (!_tcschr (SubStr, Separator)); len1 = lstrlen (SubStr); MYASSERT (SubStr[len1] == 0); while (String) { end = _tcschr (String, Separator); if (end) { len2 = end - String; } else { len2 = lstrlen (String); } if ((len1 == len2) && (CaseSensitive ? !_tcsncmp (String, SubStr, len1) : !_tcsnicmp (String, SubStr, len1) )) { break; } if (end) { String = end + 1; } else { String = NULL; } } return String; } VOID GetCurrentWinnt32RegKey ( OUT PTSTR Key, IN DWORD Chars ) { _sntprintf ( Key, Chars, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Setup\\Winnt32\\%u.%u"), VER_PRODUCTMAJORVERSION, VER_PRODUCTMINORVERSION ); } BOOL GetFileVersion ( IN PCTSTR FilePath, OUT PTSTR FileVersion ) { DWORD dwLength, dwTemp; LPVOID lpData; VS_FIXEDFILEINFO *VsInfo; UINT DataLength; BOOL b = FALSE; if (FileExists (FilePath, NULL)) { if (dwLength = GetFileVersionInfoSize ((PTSTR)FilePath, &dwTemp)) { if (lpData = LocalAlloc (LPTR, dwLength)) { if (GetFileVersionInfo ((PTSTR)FilePath, 0, dwLength, lpData)) { if (VerQueryValue (lpData, TEXT("\\"), &VsInfo, &DataLength)) { wsprintf ( FileVersion, TEXT("%u.%u.%u.%u"), (UINT)HIWORD(VsInfo->dwFileVersionMS), (UINT)LOWORD(VsInfo->dwFileVersionMS), (UINT)HIWORD(VsInfo->dwFileVersionLS), (UINT)LOWORD(VsInfo->dwFileVersionLS) ); b = TRUE; } } LocalFree (lpData); } } } return b; } BOOL IsFileVersionLesser ( IN PCTSTR FileToCompare, IN PCTSTR FileToCompareWith ) { TCHAR version[20]; if (GetFileVersion (FileToCompareWith, version) && CheckForFileVersion (FileToCompare, version)) { DebugLog ( Winnt32LogInformation, TEXT("File %1 has a smaller version (%2) than %3"), 0, FileToCompare, version, FileToCompareWith ); return TRUE; } return FALSE; } BOOL FindPathToInstallationFileEx ( IN PCTSTR FileName, OUT PTSTR PathToFile, IN DWORD PathToFileBufferSize, OUT PBOOL Compressed OPTIONAL ) { DWORD i; DWORD attr; BOOL b; HANDLE h; WIN32_FIND_DATA fd; PTSTR p, q; if (!FileName || !*FileName) { return FALSE; } // // Search for installation files in this order: // 1. AlternateSourcePath (specified on the cmd line with /M:Path // 2. Setup Update files (downloaded from the web) // 3. NativeSourcePath(s) // 4. SourcePath(s) // if (AlternateSourcePath[0]) { lstrcpyn (PathToFile, AlternateSourcePath, PathToFileBufferSize); ConcatenatePaths (PathToFile, FileName, PathToFileBufferSize); attr = GetFileAttributes (PathToFile); if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) { return TRUE; } } if (g_DynUpdtStatus->UpdatesPath[0]) { BuildPath (PathToFile, g_DynUpdtStatus->UpdatesPath, FileName); attr = GetFileAttributes (PathToFile); if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) { return TRUE; } } for (i = 0; i < SourceCount; i++) { lstrcpyn (PathToFile, NativeSourcePaths[i], PathToFileBufferSize); ConcatenatePaths (PathToFile, FileName, PathToFileBufferSize); p = CharPrev (PathToFile, _tcschr (PathToFile, 0)); *p = TEXT('?'); b = FALSE; h = FindFirstFile (PathToFile, &fd); if (h != INVALID_HANDLE_VALUE) { if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { q = CharPrev (fd.cFileName, _tcschr (fd.cFileName, 0)); *p = *q; if (Compressed) { *Compressed = (*q == TEXT('_')); } b = TRUE; } FindClose (h); } if (b) { return TRUE; } } for (i = 0; i < SourceCount; i++) { lstrcpyn (PathToFile, SourcePaths[i], PathToFileBufferSize); ConcatenatePaths (PathToFile, FileName, PathToFileBufferSize); p = CharPrev (PathToFile, _tcschr (PathToFile, 0)); *p = TEXT('?'); b = FALSE; h = FindFirstFile (PathToFile, &fd); if (h != INVALID_HANDLE_VALUE) { if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { q = CharPrev (fd.cFileName, _tcschr (fd.cFileName, 0)); *p = *q; if (Compressed) { *Compressed = (*q == TEXT('_')); } b = TRUE; } FindClose (h); } if (b) { return TRUE; } } return FALSE; } BOOL FindPathToWinnt32File ( IN PCTSTR FileRelativePath, OUT PTSTR PathToFile, IN DWORD PathToFileBufferSize ) { DWORD i; DWORD attr; TCHAR cdFilePath[MAX_PATH]; PTSTR p; if (!FileRelativePath || !*FileRelativePath) { return FALSE; } if (!GetModuleFileName (NULL, cdFilePath, MAX_PATH) || !(p = _tcsrchr (cdFilePath, TEXT('\\')))) { return FALSE; } lstrcpy (p + 1, FileRelativePath); // // Search for winnt32 files in this order: // 1. AlternateSourcePath (specified on the cmd line with /M:Path // 2. Setup Update files (downloaded from the web) // 3. NativeSourcePath(s) // 4. SourcePath(s) // if (AlternateSourcePath[0]) { if (BuildPath2 (PathToFile, PathToFileBufferSize, AlternateSourcePath, FileRelativePath)) { attr = GetFileAttributes (PathToFile); if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) { return TRUE; } } p = _tcsrchr (PathToFile, TEXT('\\')); if (p) { // // try the root of /M too, for backward compatibility with W2K // if (BuildPath2 (PathToFile, PathToFileBufferSize, AlternateSourcePath, p + 1)) { attr = GetFileAttributes (PathToFile); if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) { return TRUE; } } } } if (g_DynUpdtStatus && g_DynUpdtStatus->Winnt32Path[0]) { BuildPath (PathToFile, g_DynUpdtStatus->Winnt32Path, FileRelativePath); attr = GetFileAttributes (PathToFile); if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) { // // check file version relative to the CD version // if (!IsFileVersionLesser (PathToFile, cdFilePath)) { return TRUE; } } } #ifndef UNICODE // // on Win9x systems, first check if the file was downloaded in %windir%\winnt32 // load it from there if it's present // if (g_LocalSourcePath) { lstrcpynA (PathToFile, g_LocalSourcePath, PathToFileBufferSize); ConcatenatePaths (PathToFile, FileRelativePath, PathToFileBufferSize); attr = GetFileAttributes (PathToFile); if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) { return TRUE; } } #endif for (i = 0; i < SourceCount; i++) { lstrcpyn (PathToFile, NativeSourcePaths[i], PathToFileBufferSize); ConcatenatePaths (PathToFile, FileRelativePath, PathToFileBufferSize); attr = GetFileAttributes (PathToFile); if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) { return TRUE; } } for (i = 0; i < SourceCount; i++) { lstrcpyn (PathToFile, SourcePaths[i], PathToFileBufferSize); ConcatenatePaths (PathToFile, FileRelativePath, PathToFileBufferSize); attr = GetFileAttributes (PathToFile); if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) { return TRUE; } } attr = GetFileAttributes (cdFilePath); if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) { lstrcpyn (PathToFile, cdFilePath, MAX_PATH); return TRUE; } return FALSE; } BOOL CreateDir ( IN PCTSTR DirName ) { return CreateDirectory (DirName, NULL) || GetLastError () == ERROR_ALREADY_EXISTS; } BOOL GetLinkDate ( IN PCTSTR FilePath, OUT PDWORD LinkDate ) { HANDLE hFile; HANDLE hFileMapping; PVOID pFileBase; DWORD fileSize; PIMAGE_DOS_HEADER dosHeader; PIMAGE_NT_HEADERS pNtHeaders; DWORD rc; rc = MapFileForRead (FilePath, &fileSize, &hFile, &hFileMapping, &pFileBase); if (rc != ERROR_SUCCESS) { SetLastError (rc); return FALSE; } __try { if (fileSize < sizeof (IMAGE_DOS_HEADER)) { rc = ERROR_BAD_FORMAT; __leave; } dosHeader = (PIMAGE_DOS_HEADER)pFileBase; if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) { rc = ERROR_BAD_FORMAT; __leave; } if ((DWORD)dosHeader->e_lfanew + sizeof (IMAGE_NT_HEADERS) > fileSize) { rc = ERROR_BAD_FORMAT; __leave; } pNtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)pFileBase + dosHeader->e_lfanew); if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) { rc = ERROR_BAD_FORMAT; __leave; } *LinkDate = pNtHeaders->FileHeader.TimeDateStamp; rc = ERROR_SUCCESS; } __finally { UnmapFile (hFileMapping, pFileBase); CloseHandle (hFile); SetLastError (rc); } return rc == ERROR_SUCCESS; } BOOL CheckForFileVersionEx ( LPCTSTR FileName, LPCTSTR FileVer, OPTIONAL LPCTSTR BinProductVer, OPTIONAL LPCTSTR LinkDate OPTIONAL ) /* Arguments - FileName - Full path to the file to check Filever - Version value to check against of the for x.x.x.x BinProductVer - Version value to check against of the for x.x.x.x LinkDate - Link date of executable Function will check the actual file against the fields specified. The depth of the check is as deep as specified in "x.x.x.x" i..e if FileVer = 3.5.1 and actual version on the file is 3.5.1.4 we only compare upto 3.5.1. Return values - TRUE - If the version of the file is <= FileVer which means that the file is an incompatible one else we return FALSE */ { TCHAR Buffer[MAX_PATH]; TCHAR temp[MAX_PATH]; DWORD dwLength, dwTemp; UINT DataLength; LPVOID lpData; VS_FIXEDFILEINFO *VsInfo; LPTSTR s,e; DWORD Vers[5],File_Vers[5];//MajVer, MinVer; INT i, Depth; BOOL bEqual, bError = FALSE; DWORD linkDate, fileLinkDate; BOOL bIncompatible; BOOL bIncompFileVer, bIncompBinProdVer; if (!ExpandEnvironmentStrings( FileName, Buffer, sizeof(Buffer)/sizeof(TCHAR) )) { return FALSE; } if(!FileExists(Buffer, NULL)) return FALSE; bIncompatible = FALSE; if(FileVer && *FileVer || BinProductVer && *BinProductVer) { // // we need to read the version info // if(dwLength = GetFileVersionInfoSize( Buffer, &dwTemp )) { if(lpData = LocalAlloc( LPTR, dwLength )) { if(GetFileVersionInfo( Buffer, 0, dwLength, lpData )) { if (VerQueryValue( lpData, TEXT("\\"), &VsInfo, &DataLength )) { if (FileVer && *FileVer) { File_Vers[0] = (HIWORD(VsInfo->dwFileVersionMS)); File_Vers[1] = (LOWORD(VsInfo->dwFileVersionMS)); File_Vers[2] = (HIWORD(VsInfo->dwFileVersionLS)); File_Vers[3] = (LOWORD(VsInfo->dwFileVersionLS)); lstrcpy (temp, FileVer); // //Parse and get the depth of versioning we look for // s = e = temp; bEqual = FALSE; i = 0; if (*e == TEXT('=')) { bEqual = TRUE; e++; s++; } while (e) { if (((*e < TEXT('0')) || (*e > TEXT('9'))) && (*e != TEXT('.')) && (*e != TEXT('\0')) ) { MYASSERT (FALSE); bError = TRUE; break; } if (*e == TEXT('\0')) { *e = 0; Vers[i] = _ttoi(s); break; } if (*e == TEXT('.')) { *e = 0; Vers[i++] = _ttoi(s); s = e+1; } e++; }// while if (!bError) { Depth = i + 1; if (Depth > 4) { Depth = 4; } for (i = 0; i < Depth; i++) { if (File_Vers[i] == Vers[i]) { continue; } if (bEqual) { break; } if (File_Vers[i] > Vers[i]) { break; } bIncompatible = TRUE; break; } if (i == Depth) { // // everything matched - the file is incompatible // bIncompatible = TRUE; } } } else { bIncompatible = TRUE; } if (!bError && bIncompatible && BinProductVer && *BinProductVer) { // // reset status // bIncompatible = FALSE; File_Vers[0] = (HIWORD(VsInfo->dwProductVersionMS)); File_Vers[1] = (LOWORD(VsInfo->dwProductVersionMS)); File_Vers[2] = (HIWORD(VsInfo->dwProductVersionLS)); File_Vers[3] = (LOWORD(VsInfo->dwProductVersionLS)); lstrcpy (temp, BinProductVer); // //Parse and get the depth of versioning we look for // s = e = temp; bEqual = FALSE; i = 0; if (*e == TEXT('=')) { bEqual = TRUE; e++; s++; } while (e) { if (((*e < TEXT('0')) || (*e > TEXT('9'))) && (*e != TEXT('.')) && (*e != TEXT('\0')) ) { MYASSERT (FALSE); bError = TRUE; break; } if (*e == TEXT('\0')) { *e = 0; Vers[i] = _ttoi(s); break; } if (*e == TEXT('.')) { *e = 0; Vers[i++] = _ttoi(s); s = e+1; } e++; }// while if (!bError) { Depth = i + 1; if (Depth > 4) { Depth = 4; } for (i = 0; i < Depth; i++) { if (File_Vers[i] == Vers[i]) { continue; } if (bEqual) { break; } if (File_Vers[i] > Vers[i]) { break; } bIncompatible = TRUE; break; } if (i == Depth) { // // everything matched - the file is incompatible // bIncompatible = TRUE; } } } } } LocalFree( lpData ); } } } else { bIncompatible = TRUE; } if (!bError && bIncompatible && LinkDate && *LinkDate) { bEqual = FALSE; if (*LinkDate == TEXT('=')) { LinkDate++; bEqual = TRUE; } bIncompatible = FALSE; if (StringToInt (LinkDate, &linkDate)) { if (GetLinkDate (Buffer, &fileLinkDate)) { if (fileLinkDate == linkDate || !bEqual && fileLinkDate < linkDate ) { bIncompatible = TRUE; } } } } if (bError) { bIncompatible = FALSE; } return bIncompatible; } BOOL StringToInt ( IN PCTSTR Field, OUT PINT IntegerValue ) /*++ Routine Description: Arguments: Return Value: Remarks: Hexadecimal numbers are also supported. They must be prefixed by '0x' or '0X', with no space allowed between the prefix and the number. --*/ { INT Value; UINT c; BOOL Neg; UINT Base; UINT NextDigitValue; INT OverflowCheck; BOOL b; if(!Field) { SetLastError(ERROR_INVALID_PARAMETER); return(FALSE); } if(*Field == TEXT('-')) { Neg = TRUE; Field++; } else { Neg = FALSE; if(*Field == TEXT('+')) { Field++; } } if((*Field == TEXT('0')) && ((*(Field+1) == TEXT('x')) || (*(Field+1) == TEXT('X')))) { // // The number is in hexadecimal. // Base = 16; Field += 2; } else { // // The number is in decimal. // Base = 10; } for(OverflowCheck = Value = 0; *Field; Field++) { c = (UINT)*Field; if((c >= (UINT)'0') && (c <= (UINT)'9')) { NextDigitValue = c - (UINT)'0'; } else if(Base == 16) { if((c >= (UINT)'a') && (c <= (UINT)'f')) { NextDigitValue = (c - (UINT)'a') + 10; } else if ((c >= (UINT)'A') && (c <= (UINT)'F')) { NextDigitValue = (c - (UINT)'A') + 10; } else { break; } } else { break; } Value *= Base; Value += NextDigitValue; // // Check for overflow. For decimal numbers, we check to see whether the // new value has overflowed into the sign bit (i.e., is less than the // previous value. For hexadecimal numbers, we check to make sure we // haven't gotten more digits than will fit in a DWORD. // if(Base == 16) { if(++OverflowCheck > (sizeof(INT) * 2)) { break; } } else { if(Value < OverflowCheck) { break; } else { OverflowCheck = Value; } } } if(*Field) { SetLastError(ERROR_INVALID_DATA); return(FALSE); } if(Neg) { Value = 0-Value; } b = TRUE; try { *IntegerValue = Value; } except(EXCEPTION_EXECUTE_HANDLER) { b = FALSE; } if(!b) { SetLastError(ERROR_INVALID_PARAMETER); } return(b); } BOOLEAN CheckForFileVersion ( LPCTSTR FileName, LPCTSTR FileVer ) { return (BOOLEAN)CheckForFileVersionEx (FileName, FileVer, NULL, NULL); } VOID FixMissingKnownDlls ( OUT PSTRINGLIST* MissingKnownDlls, IN PCTSTR RestrictedCheckList OPTIONAL ) { PCTSTR regStr; HKEY key; DWORD rc; DWORD index; TCHAR dllValue[MAX_PATH]; TCHAR dllName[MAX_PATH]; DWORD type; DWORD size1 = MAX_PATH, size2 = MAX_PATH; TCHAR systemDir[MAX_PATH]; TCHAR dllPath[MAX_PATH]; BOOL bCheck; if (!GetSystemDirectory (systemDir, MAX_PATH)) { return; } #ifdef UNICODE regStr = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\KnownDLLs"; #else regStr = "SYSTEM\\CurrentControlSet\\Control\\SessionManager\\KnownDLLs"; #endif rc = RegOpenKey (HKEY_LOCAL_MACHINE, regStr, &key); if (rc == ERROR_SUCCESS) { index = 0; while (RegEnumValue ( key, index++, dllValue, &size1, NULL, &type, (LPBYTE)dllName, &size2 ) == ERROR_SUCCESS) { if (type == REG_SZ) { bCheck = TRUE; if (RestrictedCheckList) { PCTSTR fileName = RestrictedCheckList; while (*fileName) { if (!lstrcmpi (fileName, dllName)) { break; } fileName = _tcschr (fileName, 0) + 1; } if (*fileName == 0) { // // we are not interested in this dll // bCheck = FALSE; } } if (bCheck) { BuildPath (dllPath, systemDir, dllName); if (!FileExists (dllPath, NULL)) { DebugLog ( Winnt32LogWarning, TEXT("The file %1 doesn't exist, although it's registered as a Known Dll"), 0, dllPath ); // // OK, we found a bogus reg entry; remove the value and remember the data // if (RegDeleteValue (key, dllValue) == ERROR_SUCCESS) { InsertList ( (PGENERIC_LIST*)MissingKnownDlls, (PGENERIC_LIST)CreateStringCell (dllValue) ); InsertList ( (PGENERIC_LIST*)MissingKnownDlls, (PGENERIC_LIST)CreateStringCell (dllName) ); } } } } size1 = size2 = MAX_PATH; } RegCloseKey (key); } } VOID UndoFixMissingKnownDlls ( IN PSTRINGLIST MissingKnownDlls ) { PCTSTR regStr; HKEY key; DWORD rc; PSTRINGLIST p, q; #ifdef UNICODE regStr = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\KnownDLLs"; #else regStr = "SYSTEM\\CurrentControlSet\\Control\\SessionManager\\KnownDLLs"; #endif rc = RegOpenKey (HKEY_LOCAL_MACHINE, regStr, &key); if (rc == ERROR_SUCCESS) { p = MissingKnownDlls; while (p) { q = p->Next; if (q) { RegSetValueEx ( key, p->String, 0, REG_SZ, (const PBYTE)q->String, (lstrlen (q->String) + 1) * sizeof (TCHAR) ); p = q->Next; } else { p = NULL; } } RegCloseKey (key); } DeleteStringList (MissingKnownDlls); } #ifndef UNICODE /*++ Routine Description: IsPatternMatch compares a string against a pattern that may contain standard * or ? wildcards. Arguments: wstrPattern - A pattern possibly containing wildcards wstrStr - The string to compare against the pattern Return Value: TRUE when wstrStr and wstrPattern match when wildcards are expanded. FALSE if wstrStr does not match wstrPattern. --*/ #define MBCHAR INT BOOL IsPatternMatchA ( IN PCSTR strPattern, IN PCSTR strStr ) { MBCHAR chSrc, chPat; while (*strStr) { chSrc = _mbctolower ((MBCHAR) _mbsnextc (strStr)); chPat = _mbctolower ((MBCHAR) _mbsnextc (strPattern)); if (chPat == '*') { // Skip all asterisks that are grouped together while (_mbsnextc (_mbsinc (strStr)) == '*') { strStr = _mbsinc (strStr); } // Check if asterisk is at the end. If so, we have a match already. if (!_mbsnextc (_mbsinc (strPattern))) { return TRUE; } // do recursive check for rest of pattern if (IsPatternMatchA (_mbsinc (strPattern), strStr)) { return TRUE; } // Allow any character and continue strStr = _mbsinc (strStr); continue; } if (chPat != '?') { if (chSrc != chPat) { return FALSE; } } strStr = _mbsinc (strStr); strPattern = _mbsinc (strPattern); } // // Fail when there is more pattern and pattern does not end in an asterisk // while (_mbsnextc (strPattern) == '*') { strPattern = _mbsinc (strPattern); } if (_mbsnextc (strPattern)) { return FALSE; } return TRUE; } #endif // Wierd logic here required to make builds work, as this is defined // in another file that gets linked in on x86 #ifdef _WIN64 BOOL IsPatternMatchW ( IN PCWSTR wstrPattern, IN PCWSTR wstrStr ) { WCHAR chSrc, chPat; while (*wstrStr) { chSrc = towlower (*wstrStr); chPat = towlower (*wstrPattern); if (chPat == L'*') { // Skip all asterisks that are grouped together while (wstrPattern[1] == L'*') wstrPattern++; // Check if asterisk is at the end. If so, we have a match already. chPat = towlower (wstrPattern[1]); if (!chPat) return TRUE; // Otherwise check if next pattern char matches current char if (chPat == chSrc || chPat == L'?') { // do recursive check for rest of pattern wstrPattern++; if (IsPatternMatchW (wstrPattern, wstrStr)) return TRUE; // no, that didn't work, stick with star wstrPattern--; } // // Allow any character and continue // wstrStr++; continue; } if (chPat != L'?') { // // if next pattern character is not a question mark, src and pat // must be identical. // if (chSrc != chPat) return FALSE; } // // Advance when pattern character matches string character // wstrPattern++; wstrStr++; } // // Fail when there is more pattern and pattern does not end in an asterisk // chPat = *wstrPattern; if (chPat && (chPat != L'*' || wstrPattern[1])) return FALSE; return TRUE; } #endif typedef BOOL (WINAPI * GETDISKFREESPACEEXA)( PCSTR lpDirectoryName, // directory name PULARGE_INTEGER lpFreeBytesAvailable, // bytes available to caller PULARGE_INTEGER lpTotalNumberOfBytes, // bytes on disk PULARGE_INTEGER lpTotalNumberOfFreeBytes // free bytes on disk ); typedef BOOL (WINAPI * GETDISKFREESPACEEXW)( PCWSTR lpDirectoryName, // directory name PULARGE_INTEGER lpFreeBytesAvailable, // bytes available to caller PULARGE_INTEGER lpTotalNumberOfBytes, // bytes on disk PULARGE_INTEGER lpTotalNumberOfFreeBytes // free bytes on disk ); BOOL GetDiskFreeSpaceNewA( IN PCSTR DriveName, OUT DWORD * OutSectorsPerCluster, OUT DWORD * OutBytesPerSector, OUT ULARGE_INTEGER * OutNumberOfFreeClusters, OUT ULARGE_INTEGER * OutTotalNumberOfClusters ) /*++ Routine Description: On Win9x GetDiskFreeSpace never return free/total space more than 2048MB. GetDiskFreeSpaceNew use GetDiskFreeSpaceEx to calculate real number of free/total clusters. Has same declaration as GetDiskFreeSpaceA. Arguments: DriveName - supplies directory name OutSectorsPerCluster - receive number of sectors per cluster OutBytesPerSector - receive number of bytes per sector OutNumberOfFreeClusters - receive number of free clusters OutTotalNumberOfClusters - receive number of total clusters Return Value: TRUE if the function succeeds. If the function fails, the return value is FALSE. To get extended error information, call GetLastError --*/ { ULARGE_INTEGER TotalNumberOfFreeBytes = {0, 0}; ULARGE_INTEGER TotalNumberOfBytes = {0, 0}; ULARGE_INTEGER DonotCare; HMODULE hKernel32; GETDISKFREESPACEEXA pGetDiskFreeSpaceExA; ULARGE_INTEGER NumberOfFreeClusters = {0, 0}; ULARGE_INTEGER TotalNumberOfClusters = {0, 0}; DWORD SectorsPerCluster; DWORD BytesPerSector; if(!GetDiskFreeSpaceA(DriveName, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters.LowPart, &TotalNumberOfClusters.LowPart)){ DebugLog ( Winnt32LogError, TEXT("GetDiskFreeSpaceNewA: GetDiskFreeSpaceA failed on drive %1"), 0, DriveName); return FALSE; } hKernel32 = LoadLibraryA("kernel32.dll"); pGetDiskFreeSpaceExA = (GETDISKFREESPACEEXA)GetProcAddress(hKernel32, "GetDiskFreeSpaceExA"); if(pGetDiskFreeSpaceExA && pGetDiskFreeSpaceExA(DriveName, &DonotCare, &TotalNumberOfBytes, &TotalNumberOfFreeBytes)){ NumberOfFreeClusters.QuadPart = TotalNumberOfFreeBytes.QuadPart / (SectorsPerCluster * BytesPerSector); TotalNumberOfClusters.QuadPart = TotalNumberOfBytes.QuadPart / (SectorsPerCluster * BytesPerSector); } else{ DebugLog ( Winnt32LogWarning, pGetDiskFreeSpaceExA? TEXT("GetDiskFreeSpaceNewA: GetDiskFreeSpaceExA is failed"): TEXT("GetDiskFreeSpaceNewA: GetDiskFreeSpaceExA function is not in kernel32.dll"), 0); } FreeLibrary(hKernel32); if(OutSectorsPerCluster){ *OutSectorsPerCluster = SectorsPerCluster; } if(OutBytesPerSector){ *OutBytesPerSector = BytesPerSector; } if(OutNumberOfFreeClusters){ OutNumberOfFreeClusters->QuadPart = NumberOfFreeClusters.QuadPart; } if(OutTotalNumberOfClusters){ OutTotalNumberOfClusters->QuadPart = TotalNumberOfClusters.QuadPart; } return TRUE; } BOOL GetDiskFreeSpaceNewW( IN PCWSTR DriveName, OUT DWORD * OutSectorsPerCluster, OUT DWORD * OutBytesPerSector, OUT ULARGE_INTEGER * OutNumberOfFreeClusters, OUT ULARGE_INTEGER * OutTotalNumberOfClusters ) /*++ Routine Description: Correct NumberOfFreeClusters and TotalNumberOfClusters out parameters with using GetDiskFreeSpace and GetDiskFreeSpaceEx Arguments: DriveName - supplies directory name OutSectorsPerCluster - receive number of sectors per cluster OutBytesPerSector - receive number of bytes per sector OutNumberOfFreeClusters - receive number of free clusters OutTotalNumberOfClusters - receive number of total clusters Return Value: TRUE if the function succeeds. If the function fails, the return value is FALSE. To get extended error information, call GetLastError --*/ { ULARGE_INTEGER TotalNumberOfFreeBytes = {0, 0}; ULARGE_INTEGER TotalNumberOfBytes = {0, 0}; ULARGE_INTEGER DonotCare; HMODULE hKernel32; GETDISKFREESPACEEXW pGetDiskFreeSpaceExW; ULARGE_INTEGER NumberOfFreeClusters = {0, 0}; ULARGE_INTEGER TotalNumberOfClusters = {0, 0}; DWORD SectorsPerCluster; DWORD BytesPerSector; if(!GetDiskFreeSpaceW(DriveName, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters.LowPart, &TotalNumberOfClusters.LowPart)){ DebugLog ( Winnt32LogError, TEXT("GetDiskFreeSpaceNewW: GetDiskFreeSpaceW failed on drive %1"), 0, DriveName); return FALSE; } hKernel32 = LoadLibraryA("kernel32.dll"); pGetDiskFreeSpaceExW = (GETDISKFREESPACEEXW)GetProcAddress(hKernel32, "GetDiskFreeSpaceExW"); if(pGetDiskFreeSpaceExW && pGetDiskFreeSpaceExW(DriveName, &DonotCare, &TotalNumberOfBytes, &TotalNumberOfFreeBytes)){ NumberOfFreeClusters.QuadPart = TotalNumberOfFreeBytes.QuadPart / (SectorsPerCluster * BytesPerSector); TotalNumberOfClusters.QuadPart = TotalNumberOfBytes.QuadPart / (SectorsPerCluster * BytesPerSector); } else{ DebugLog ( Winnt32LogWarning, pGetDiskFreeSpaceExW? TEXT("GetDiskFreeSpaceNewW: GetDiskFreeSpaceExW is failed"): TEXT("GetDiskFreeSpaceNewW: GetDiskFreeSpaceExW function is not in kernel32.dll"), 0); } FreeLibrary(hKernel32); if(OutSectorsPerCluster){ *OutSectorsPerCluster = SectorsPerCluster; } if(OutBytesPerSector){ *OutBytesPerSector = BytesPerSector; } if(OutNumberOfFreeClusters){ OutNumberOfFreeClusters->QuadPart = NumberOfFreeClusters.QuadPart; } if(OutTotalNumberOfClusters){ OutTotalNumberOfClusters->QuadPart = TotalNumberOfClusters.QuadPart; } return TRUE; } BOOL ReplaceSubStr( IN OUT LPTSTR SrcStr, IN LPTSTR SrcSubStr, IN LPTSTR DestSubStr ) /*++ Routine Description: Replaces the source substr with the destination substr in the source string. NOTE : SrcSubStr needs to be longer than or equal in length to DestSubStr. Arguments: SrcStr : The source to operate upon. Also receives the new string. SrcSubStr : The source substring to search for and replace. DestSubStr : The substring to replace with for the occurences of SrcSubStr in SrcStr. Return Value: TRUE if successful, otherwise FALSE. --*/ { BOOL Result = FALSE; // // Validate the arguments // if (SrcStr && SrcSubStr && *SrcSubStr && (!DestSubStr || (_tcslen(SrcSubStr) >= _tcslen(DestSubStr)))) { if (!DestSubStr || _tcsicmp(SrcSubStr, DestSubStr)) { ULONG SrcStrLen = _tcslen(SrcStr); ULONG SrcSubStrLen = _tcslen(SrcSubStr); ULONG DestSubStrLen = DestSubStr ? _tcslen(DestSubStr) : 0; LPTSTR DestStr = malloc((SrcStrLen + 1) * sizeof(TCHAR)); if (DestStr) { LPTSTR CurrDestStr = DestStr; LPTSTR PrevSrcStr = SrcStr; LPTSTR CurrSrcStr = _tcsstr(SrcStr, SrcSubStr); while (CurrSrcStr) { // // Skip starting substr & copy previous unmatched pattern // if (PrevSrcStr != CurrSrcStr) { _tcsncpy(CurrDestStr, PrevSrcStr, (CurrSrcStr - PrevSrcStr)); CurrDestStr += (CurrSrcStr - PrevSrcStr); *CurrDestStr = TEXT('\0'); } // // Copy destination substr // if (DestSubStr) { _tcscpy(CurrDestStr, DestSubStr); CurrDestStr += DestSubStrLen; *CurrDestStr = TEXT('\0'); } // // Look for next substr // CurrSrcStr += SrcSubStrLen; PrevSrcStr = CurrSrcStr; CurrSrcStr = _tcsstr(CurrSrcStr, SrcSubStr); } // // Copy remaining src string if any // if (!_tcsstr(PrevSrcStr, SrcSubStr)) { _tcscpy(CurrDestStr, PrevSrcStr); } // // Copy the new string back to the src string // _tcscpy(SrcStr, DestStr); free(DestStr); Result = TRUE; } } else { Result = TRUE; } } return Result; } VOID RemoveTrailingWack ( PTSTR String ) { if (String) { PTSTR p = _tcsrchr (String, TEXT('\\')); if (p && p[1] == 0) { *p = 0; } } } ULONGLONG SystemTimeToFileTime64 ( IN PSYSTEMTIME SystemTime ) { FILETIME ft; ULARGE_INTEGER result; SystemTimeToFileTime (SystemTime, &ft); result.LowPart = ft.dwLowDateTime; result.HighPart = ft.dwHighDateTime; return result.QuadPart; }