/*++ Copyright (C) Microsoft Corporation Module Name: sysinfo.cpp Abstract: This module implements CSystemInfo, the class that returns various system information Author: William Hsieh (williamh) created Revision History: --*/ #include "devmgr.h" #include "sysinfo.h" // disk drive root template name. Used to retreive the disk's media // information or geometry const TCHAR* const DRIVE_ROOT = TEXT("\\\\.\\?:"); const int DRIVE_LETTER_IN_DRIVE_ROOT = 4; // disk drive root directory template name. Used to retreive the disk's // total and free space const TCHAR* const DRIVE_ROOT_DIR = TEXT("?:\\"); const int DRIVE_LETTER_IN_DRIVE_ROOT_DIR = 0; // // Registry various subkey and value names used to retreive // system information // const TCHAR* const REG_PATH_HARDWARE_SYSTEM = TEXT("HARDWARE\\DESCRIPTION\\System"); const TCHAR* const REG_VALUE_SYSTEMBIOSDATE = TEXT("SystemBiosDate"); const TCHAR* const REG_VALUE_SYSTEMBIOSVERSION = TEXT("SystemBiosVersion"); const TCHAR* const REG_VALUE_MACHINETYPE = TEXT("Identifier"); const TCHAR* const REG_PATH_WINDOWS_NT = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); const TCHAR* const REG_VALUE_REGISTERED_OWNER = TEXT("RegisteredOwner"); const TCHAR* const REG_VALUE_REGISTERED_ORGANIZATION = TEXT("RegisteredOrganization"); const TCHAR* const REG_VALUE_CURRENTBUILDNUMBER = TEXT("CurrentBuildNumber"); const TCHAR* const REG_VALUE_CURRENTVERSION = TEXT("CurrentVersion"); const TCHAR* const REG_VALUE_CSDVERSION = TEXT("CSDVersion"); const TCHAR* const REG_PATH_CPU = TEXT("HARDWARE\\DESCRIPTION\\System\\CentralProcessor"); const TCHAR* const REG_VALUE_CPU_TYPE = TEXT("Identifier"); const TCHAR* const REG_VALUE_CPU_VENDOR = TEXT("VendorIdentifier"); CSystemInfo::CSystemInfo( CMachine* pMachine ) { // assuming the machine is a local machine and initialize // the registry root key as well. m_hKeyMachine = HKEY_LOCAL_MACHINE; if (pMachine) { m_fLocalMachine = pMachine->IsLocal(); m_strComputerName += pMachine->GetMachineDisplayName(); } else { TCHAR LocalName[MAX_PATH + 1]; DWORD dwSize = ARRAYLEN(LocalName); if (!GetComputerName(LocalName, &dwSize)) { LocalName[0] = _T('\0'); } // local machine m_fLocalMachine = TRUE; m_strComputerName = LocalName; } if (!m_fLocalMachine) { // The machine is not local, connect to the registry String strFullComputerName; strFullComputerName = (LPCTSTR)TEXT("\\\\"); strFullComputerName += m_strComputerName; m_hKeyMachine = NULL; RegConnectRegistry((LPCTSTR)strFullComputerName, HKEY_LOCAL_MACHINE, &m_hKeyMachine); } } CSystemInfo::~CSystemInfo() { if (!m_fLocalMachine && NULL != m_hKeyMachine) { RegCloseKey(m_hKeyMachine); // disconnect the machine WNetCancelConnection2(TEXT("\\server\\ipc$"), 0, TRUE); } } // // This function gets the disk information about the given disk drive // INPUT: // Drive -- the drive number. 0 for A, 1 for B and etc. // DiskInfo -- the DISK_INFO to be filled with the information about // the drive. DiskInfo.cbSize must be initialized before // the call. // OUTPUT: // TRUE -- if succeeded, DiskInfo is filled with information // FALSE -- if the drive information can not be retreived. // No appropriate error code is returned; BOOL CSystemInfo::GetDiskInfo( int Drive, DISK_INFO& DiskInfo ) { // diskinfo only valid on local computer if (!m_fLocalMachine) { return FALSE; } TCHAR DriveLetter; TCHAR Root[MAX_PATH]; DriveLetter = (TCHAR)(_T('A') + Drive); StringCchCopy(Root, ARRAYLEN(Root), DRIVE_ROOT_DIR); Root[DRIVE_LETTER_IN_DRIVE_ROOT_DIR] = DriveLetter; UINT DriveType; DriveType = GetDriveType(Root); // // only valid for local drives // if (DRIVE_UNKNOWN == DriveType || DRIVE_REMOTE == DriveType || DRIVE_NO_ROOT_DIR == DriveType) { return FALSE; } if (DiskInfo.cbSize < sizeof(DISK_INFO)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // // form the disk root name from template // StringCchCopy(Root, ARRAYLEN(Root), DRIVE_ROOT); Root[DRIVE_LETTER_IN_DRIVE_ROOT] = DriveLetter; HANDLE hDisk; // FILE_READ_ATTRIBUTES is used here so that we will not get nasty // error or prompt if the disk is a removable drive and there is no // media available. hDisk = CreateFile(Root, FILE_READ_ATTRIBUTES | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE != hDisk) { // form the disk root directory name from template StringCchCopy(Root, ARRAYLEN(Root), DRIVE_ROOT_DIR); Root[DRIVE_LETTER_IN_DRIVE_ROOT_DIR] = DriveLetter; BYTE Buffer[512]; DWORD BytesRequired = 0; if (DeviceIoControl(hDisk, IOCTL_STORAGE_GET_MEDIA_TYPES_EX, NULL, 0, Buffer, sizeof(Buffer), &BytesRequired, NULL)) { GET_MEDIA_TYPES* pMediaList; DEVICE_MEDIA_INFO* pMediaInfo; pMediaList = (GET_MEDIA_TYPES*)Buffer; pMediaInfo = pMediaList->MediaInfo; DWORD MediaCount = pMediaList->MediaInfoCount; ULARGE_INTEGER MaxSpace, NewSpace; DEVICE_MEDIA_INFO* pMaxMediaInfo; MaxSpace.QuadPart = 0; pMaxMediaInfo = NULL; for (DWORD i = 0; i < MediaCount; i++, pMediaInfo++) { // // find the mediainfo which has max space // A disk drive may support multiple media types and the // one with maximum capacity is what we want to report. // if (DRIVE_REMOVABLE == DriveType || DRIVE_CDROM == DriveType) { NewSpace.QuadPart = pMediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector * pMediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack * pMediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder * pMediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart; } else { NewSpace.QuadPart = pMediaInfo->DeviceSpecific.DiskInfo.BytesPerSector * pMediaInfo->DeviceSpecific.DiskInfo.SectorsPerTrack * pMediaInfo->DeviceSpecific.DiskInfo.TracksPerCylinder * pMediaInfo->DeviceSpecific.DiskInfo.Cylinders.QuadPart; } if (NewSpace.QuadPart > MaxSpace.QuadPart) { MaxSpace.QuadPart = NewSpace.QuadPart; pMaxMediaInfo = pMediaInfo; } } if (pMaxMediaInfo) { // // a valid media information is found, compose DISK_INFO // from the media information // DiskInfo.DriveType = DriveType; if (DRIVE_REMOVABLE == DriveType || DRIVE_CDROM == DriveType) { DiskInfo.MediaType = pMaxMediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType; DiskInfo.Cylinders = pMaxMediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders; DiskInfo.Heads = pMaxMediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder; DiskInfo.BytesPerSector = pMaxMediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector; DiskInfo.SectorsPerTrack = pMaxMediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack; // // Do not call GetDiskFreeSpaceEx on removable disk // or CD-ROM // DiskInfo.TotalSpace = MaxSpace; DiskInfo.FreeSpace.QuadPart = (ULONGLONG)-1; } else { DiskInfo.MediaType = pMaxMediaInfo->DeviceSpecific.DiskInfo.MediaType; DiskInfo.Cylinders = pMaxMediaInfo->DeviceSpecific.DiskInfo.Cylinders; DiskInfo.Heads = pMaxMediaInfo->DeviceSpecific.DiskInfo.TracksPerCylinder; DiskInfo.BytesPerSector = pMaxMediaInfo->DeviceSpecific.DiskInfo.BytesPerSector; DiskInfo.SectorsPerTrack = pMaxMediaInfo->DeviceSpecific.DiskInfo.SectorsPerTrack; StringCchCopy(Root, ARRAYLEN(Root), DRIVE_ROOT_DIR); Root[DRIVE_LETTER_IN_DRIVE_ROOT_DIR] = DriveLetter; ULARGE_INTEGER FreeSpaceForCaller; if (!GetDiskFreeSpaceEx(Root, &FreeSpaceForCaller, &DiskInfo.TotalSpace, &DiskInfo.FreeSpace)) { DiskInfo.TotalSpace = MaxSpace; // unknown DiskInfo.FreeSpace.QuadPart = (ULONGLONG)-1; } } CloseHandle(hDisk); return TRUE; } } // // we wouldn't go here if the drive is not removable. // Basically, this is for floppy drives only. // if (DRIVE_REMOVABLE == DriveType && DeviceIoControl(hDisk, IOCTL_DISK_GET_MEDIA_TYPES, NULL, 0, Buffer, sizeof(Buffer), &BytesRequired, NULL)) { int TotalMediaTypes = BytesRequired / sizeof(DISK_GEOMETRY); DISK_GEOMETRY* pGeometry; pGeometry = (DISK_GEOMETRY*)Buffer; ULARGE_INTEGER MaxSpace; ULARGE_INTEGER NewSpace; MaxSpace.QuadPart = 0; DISK_GEOMETRY* pMaxGeometry = NULL; for (int i = 0; i < TotalMediaTypes; i++, pGeometry++) { // // find the geometry with maximum capacity // NewSpace.QuadPart = pGeometry->BytesPerSector * pGeometry->SectorsPerTrack * pGeometry->TracksPerCylinder * pGeometry->Cylinders.QuadPart; if (NewSpace.QuadPart > MaxSpace.QuadPart) { pMaxGeometry = pGeometry; MaxSpace = NewSpace; } } if (pMaxGeometry) { DiskInfo.DriveType = DriveType; DiskInfo.MediaType = (STORAGE_MEDIA_TYPE)pMaxGeometry->MediaType; DiskInfo.Cylinders = pMaxGeometry->Cylinders; DiskInfo.Heads = pMaxGeometry->TracksPerCylinder; DiskInfo.BytesPerSector = pMaxGeometry->BytesPerSector; DiskInfo.SectorsPerTrack = pMaxGeometry->SectorsPerTrack; DiskInfo.TotalSpace = MaxSpace; DiskInfo.FreeSpace.QuadPart = (ULONGLONG)-1; CloseHandle(hDisk); return TRUE; } } CloseHandle(hDisk); } return FALSE; } // // This functions retreive the Window version information in text string // INPUT: // Buffer -- buffer to receive the text string // BufferSize -- buffer size in char(in bytes on ANSI version) // OUTPUT: // The size of the text string, not including the terminated NULL char // If the returned value is 0, GetLastError will returns the error code. // If the returned value is larger than BufferSize, Buffer is too small // DWORD CSystemInfo::WindowsVersion( TCHAR* Buffer, DWORD BufferSize ) { if (!Buffer && BufferSize) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } String strFinalText; TCHAR Temp[LINE_LEN]; strFinalText.Empty(); strFinalText.LoadString(g_hInstance, IDS_WINDOWS_NT); CSafeRegistry regWindowsNT; if (regWindowsNT.Open(m_hKeyMachine, REG_PATH_WINDOWS_NT, KEY_READ)) { DWORD Type, Size; Size = sizeof(Temp); if (regWindowsNT.GetValue(REG_VALUE_CURRENTVERSION, &Type, (PBYTE)Temp, &Size)) { strFinalText += (LPCTSTR)Temp; } Size = sizeof(Temp); if (regWindowsNT.GetValue(REG_VALUE_CSDVERSION, &Type, (PBYTE)Temp, &Size) && Size) { strFinalText += (LPCTSTR)TEXT(" "); strFinalText += (LPCTSTR)Temp; } Size = sizeof(Temp); if (regWindowsNT.GetValue(REG_VALUE_CURRENTBUILDNUMBER, &Type, (PBYTE)Temp, &Size) && Size) { String strBuildFormat; strBuildFormat.LoadString(g_hInstance, IDS_BUILD_NUMBER); String strBuild; strBuild.Format((LPCTSTR)strBuildFormat, Temp); strFinalText += strBuild; } } if (BufferSize > (DWORD)strFinalText.GetLength()) { StringCchCopy(Buffer, BufferSize, (LPCTSTR)strFinalText); SetLastError(ERROR_SUCCESS); } else { SetLastError(ERROR_INSUFFICIENT_BUFFER); } return strFinalText.GetLength(); } // // This functions retreive a REG_SZ from the registry // INPUT: // SubkeyName -- registry subkey name. // ValueName -- registry value name; // Buffer -- buffer to receive the string // BufferSize -- buffer size in char(in bytes on ANSI version) // hKeyAncestory -- the key under which Subkeyname should be opened. // // OUTPUT: // The size of the text string, not including the terminated NULL char // If the returned value is 0, GetLastError will returns the error code. // If the returned value is larger than BufferSize, Buffer is too small // DWORD CSystemInfo::InfoFromRegistry( LPCTSTR SubkeyName, LPCTSTR ValueName, TCHAR* Buffer, DWORD BufferSize, HKEY hKeyAncestor ) { // validate parameters if (!SubkeyName || !ValueName || _T('\0') == *SubkeyName || _T('\0') == *SubkeyName || (!Buffer && BufferSize)) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } if (!hKeyAncestor) { hKeyAncestor = m_hKeyMachine; } CSafeRegistry regSubkey; if (regSubkey.Open(hKeyAncestor, SubkeyName, KEY_READ)) { TCHAR Temp[MAX_PATH]; DWORD Type; DWORD Size; Size = sizeof(Temp); if (regSubkey.GetValue(ValueName, &Type, (PBYTE)Temp, &Size) && Size) { Size /= sizeof(TCHAR); if (BufferSize > Size) { StringCchCopy(Buffer, BufferSize, Temp); } return Size; } } SetLastError(ERROR_SUCCESS); return 0; } // // This functions retreive the system BIOS date information in text string // INPUT: // Buffer -- buffer to receive the text string // BufferSize -- buffer size in char(in bytes on ANSI version) // OUTPUT: // The size of the text string, not including the terminated NULL char // If the returned value is 0, GetLastError will returns the error code. // If the returned value is larger than BufferSize, Buffer is too small // DWORD CSystemInfo::SystemBiosDate( TCHAR* Buffer, DWORD BufferSize ) { return InfoFromRegistry(REG_PATH_HARDWARE_SYSTEM, REG_VALUE_SYSTEMBIOSDATE, Buffer, BufferSize); } // // This functions retreive the system BIOS version information in text string // INPUT: // Buffer -- buffer to receive the text string // BufferSize -- buffer size in char(in bytes on ANSI version) // OUTPUT: // The size of the text string, not including the terminated NULL char // If the returned value is 0, GetLastError will returns the error code. // If the returned value is larger than BufferSize, Buffer is too small // DWORD CSystemInfo::SystemBiosVersion( TCHAR* Buffer, DWORD BufferSize ) { return InfoFromRegistry(REG_PATH_HARDWARE_SYSTEM, REG_VALUE_SYSTEMBIOSVERSION, Buffer, BufferSize); } // // This functions retreive the machine type in text string // INPUT: // Buffer -- buffer to receive the text string // BufferSize -- buffer size in char(in bytes on ANSI version) // OUTPUT: // The size of the text string, not including the terminated NULL char // If the returned value is 0, GetLastError will returns the error code. // If the returned value is larger than BufferSize, Buffer is too small // DWORD CSystemInfo::MachineType( TCHAR* Buffer, DWORD BufferSize ) { return InfoFromRegistry(REG_PATH_HARDWARE_SYSTEM, REG_VALUE_MACHINETYPE, Buffer, BufferSize); } // // This functions retreive the registered owner name // INPUT: // Buffer -- buffer to receive the text string // BufferSize -- buffer size in char(in bytes on ANSI version) // OUTPUT: // The size of the text string, not including the terminated NULL char // If the returned value is 0, GetLastError will returns the error code. // If the returned value is larger than BufferSize, Buffer is too small // DWORD CSystemInfo::RegisteredOwner( TCHAR* Buffer, DWORD BufferSize ) { return InfoFromRegistry(REG_PATH_WINDOWS_NT, REG_VALUE_REGISTERED_OWNER, Buffer, BufferSize ); } // // This functions retreive the registered organization name // INPUT: // Buffer -- buffer to receive the text string // BufferSize -- buffer size in char(in bytes on ANSI version) // OUTPUT: // The size of the text string, not including the terminated NULL char // If the returned value is 0, GetLastError will returns the error code. // If the returned value is larger than BufferSize, Buffer is too small // DWORD CSystemInfo::RegisteredOrganization( TCHAR* Buffer, DWORD BufferSize ) { return InfoFromRegistry(REG_PATH_WINDOWS_NT, REG_VALUE_REGISTERED_ORGANIZATION, Buffer, BufferSize ); } // This function resturns the number of processors on the computer // INPUT: // NONE // OUTPUT: // Number of processor. // DWORD CSystemInfo::NumberOfProcessors() { CSafeRegistry regCPU; DWORD CPUs = 0; if (regCPU.Open(m_hKeyMachine, REG_PATH_CPU, KEY_READ)) { TCHAR SubkeyName[MAX_PATH + 1]; DWORD SubkeySize = ARRAYLEN(SubkeyName); while (regCPU.EnumerateSubkey(CPUs, SubkeyName, &SubkeySize)) { SubkeySize = ARRAYLEN(SubkeyName); CPUs++; } } return CPUs; } // This function returns the processor vendor in text string // INPUT: // Buffer -- buffer to receive the string // BufferSize -- size of the buffer in char(bytes in ANSI) // OUTPUT: // The size of the text string, not including the terminated NULL char // If the returned value is 0, GetLastError will returns the error code. // If the returned value is larger than BufferSize, Buffer is too small // // The system assumes that all processor in the machine must // have the same type, therefore, this function does not take // processor number as a parameter. DWORD CSystemInfo::ProcessorVendor( TCHAR* Buffer, DWORD BufferSize ) { return ProcessorInfo(REG_VALUE_CPU_VENDOR, Buffer, BufferSize); } // This function returns the processor type in text string // INPUT: // Buffer -- buffer to receive the string // BufferSize -- size of the buffer in char(bytes in ANSI) // OUTPUT: // The size of the text string, not including the terminated NULL char // If the returned value is 0, GetLastError will returns the error code. // If the returned value is larger than BufferSize, Buffer is too small // // The system assumes that all processor in the machine must // have the same type, therefore, this function does not take // processor number as a parameter. DWORD CSystemInfo::ProcessorType( TCHAR* Buffer, DWORD BufferSize ) { return ProcessorInfo(REG_VALUE_CPU_TYPE, Buffer, BufferSize); } DWORD CSystemInfo::ProcessorInfo( LPCTSTR ValueName, TCHAR* Buffer, DWORD BufferSize ) { if (!ValueName || (!Buffer && BufferSize)) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } CSafeRegistry regCPU; DWORD CPUIndex = 0; TCHAR CPUInfo[MAX_PATH]; DWORD CPUInfoSize = 0; DWORD Type; if (regCPU.Open(m_hKeyMachine, REG_PATH_CPU, KEY_READ)) { TCHAR CPUKey[MAX_PATH + 1]; DWORD Size; Size = ARRAYLEN(CPUKey); // loop through all cpus until we find something interesting while (CPUInfoSize <= sizeof(TCHAR) && regCPU.EnumerateSubkey(CPUIndex, CPUKey, &Size)) { CSafeRegistry regTheCPU; if (regTheCPU.Open(regCPU, CPUKey, KEY_READ)) { CPUInfoSize = sizeof(CPUInfo); regTheCPU.GetValue(ValueName, &Type, (PBYTE)CPUInfo, &CPUInfoSize); } CPUIndex++; } // CPUInfoSize != 0 means we find something if (CPUInfoSize > sizeof(TCHAR)) { CPUInfoSize = CPUInfoSize / sizeof(TCHAR) - 1; if (BufferSize > CPUInfoSize) { StringCchCopy(Buffer, BufferSize, CPUInfo); } return CPUInfoSize; } } return 0; } // // This function returns the total physical memeory in KB // INPUT: // NONE // OUTPUT: // Total Memory in KB // void CSystemInfo::TotalPhysicalMemory( ULARGE_INTEGER& Size ) { if (m_fLocalMachine) { SYSTEM_BASIC_INFORMATION SysBasicInfo; NTSTATUS Status; Status = NtQuerySystemInformation(SystemBasicInformation, (PVOID)&SysBasicInfo, sizeof(SysBasicInfo), NULL); if (NT_SUCCESS(Status)) { Size.QuadPart = Int32x32To64(SysBasicInfo.PageSize, SysBasicInfo.NumberOfPhysicalPages ); } else { MEMORYSTATUS MemoryStatus; GlobalMemoryStatus(&MemoryStatus); Size.LowPart = (ULONG)MemoryStatus.dwTotalPhys; Size.HighPart = 0; } } else { Size.QuadPart = 0; SetLastError(ERROR_SUCCESS); } }