VC++获取CPU的名称、主频和核数(附源码)

       有时我们需要获取CPU的主频和核数(物理核数和逻辑核数)等信息去判断PC的性能,本文我们就介绍如何去获取这些信息。

1、获取CPU名称和主频

       CPU的主频及名称等信息均存储在注册表中,可以到下列注册表位置中读取:

HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0

该位置的系统注册表截图如下所示:

从上述注册表位置中读取CPU主频及名称信息的代码如下:

// 获取CPU信息
BOOL GetCpuInfo( TCHAR* lpszCpuName, DWORD& dwCpuMFreq )
{
    // 读出CPU信息
    HKEY hKey = NULL;
    TCHAR szCPUName[256] = { 0 };
    LONG lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0")
        , 0, KEY_READ, &hKey );
    if ( ERROR_SUCCESS != lRet )
    {
        return FALSE;
    }

    // 1、读出CPU名称
    DWORD dwSize = sizeof(szCPUName);
    DWORD dwType = REG_SZ;
    lRet = RegQueryValueEx( hKey, _T("ProcessorNameString"), NULL, &dwType, szCPUName, &dwSize );
    if ( ERROR_SUCCESS != lRet )
    {
        RegCloseKey( hKey );
        return FALSE;
    }

    // 2、读出CPU主频,以MHZ为单位
    DWORD dwCpuFreq = 0;  
    dwType = REG_DWORD;
    dwSize = sizeof( dwCpuFreq ); 
    lRet = RegQueryValueEx( hKey, _T("~MHz"), NULL, &dwType, (LPBYTE)(&dwCpuFreq), &dwSize ); 
    if ( ERROR_SUCCESS != lRet )
    {
        RegCloseKey( hKey );
        return FALSE;
    }

    _tcsncpy( lpszCpuName, szCPUName, _tcslen(szCPUName) );
    dwCpuMFreq = dwCpuFreq;

    RegCloseKey( hKey );
    return TRUE;
}

2、获取CPU的物理核数和逻辑核数

       CPU的核数分物理核数和逻辑核数。逻辑核包含物理核及通过超线程技术虚拟出来的虚拟核,所以逻辑核数是大于等于物理核数的。

       有时我们需要通过核数去判断CPU的性能,那该如何去获取CPU的核数呢?Windows从Windows2000开始就提供了一个叫做GetSystemInfo的API函数,该函数可以获取CPU的逻辑核数但该函数在较新的Windows系统中获取的逻辑核数是不准确的。

       所以从Windows XP SP3开始,Windows又提供一个获取CPU核数的新的API函数GetLogicalProcessorInformation,该函数不仅能获取到逻辑核数,还能获取到物理核数。查看MSDN对该函数说明的Requirements部分的说明,该函数的声明位于sysinfoapi.h头文件中,函数的实现位于Kernel32.dll库中,如下所示:

MSDN上给出了完整的示例代码,可以直接拿来使用,示例代码如下:

#include <windows.h>
#include <malloc.h>    
#include <stdio.h>
#include <tchar.h>

typedef BOOL (WINAPI *LPFN_GLPI)(
    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, 
    PDWORD);


// Helper function to count set bits in the processor mask.
DWORD CountSetBits(ULONG_PTR bitMask)
{
    DWORD LSHIFT = sizeof(ULONG_PTR)*8 - 1;
    DWORD bitSetCount = 0;
    ULONG_PTR bitTest = (ULONG_PTR)1 << LSHIFT;    
    DWORD i;
    
    for (i = 0; i <= LSHIFT; ++i)
    {
        bitSetCount += ((bitMask & bitTest)?1:0);
        bitTest/=2;
    }

    return bitSetCount;
}

int _cdecl _tmain ()
{
    LPFN_GLPI glpi;
    BOOL done = FALSE;
    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
    DWORD returnLength = 0;
    DWORD logicalProcessorCount = 0;
    DWORD numaNodeCount = 0;
    DWORD processorCoreCount = 0;
    DWORD processorL1CacheCount = 0;
    DWORD processorL2CacheCount = 0;
    DWORD processorL3CacheCount = 0;
    DWORD processorPackageCount = 0;
    DWORD byteOffset = 0;
    PCACHE_DESCRIPTOR Cache;

    glpi = (LPFN_GLPI) GetProcAddress(
                            GetModuleHandle(TEXT("kernel32")),
                            "GetLogicalProcessorInformation");
    if (NULL == glpi) 
    {
        _tprintf(TEXT("\nGetLogicalProcessorInformation is not supported.\n"));
        return (1);
    }

    while (!done)
    {
        DWORD rc = glpi(buffer, &returnLength);

        if (FALSE == rc) 
        {
            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) 
            {
                if (buffer) 
                    free(buffer);

                buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(
                        returnLength);

                if (NULL == buffer) 
                {
                    _tprintf(TEXT("\nError: Allocation failure\n"));
                    return (2);
                }
            } 
            else 
            {
                _tprintf(TEXT("\nError %d\n"), GetLastError());
                return (3);
            }
        } 
        else
        {
            done = TRUE;
        }
    }

    ptr = buffer;

    while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) 
    {
        switch (ptr->Relationship) 
        {
        case RelationNumaNode:
            // Non-NUMA systems report a single record of this type.
            numaNodeCount++;
            break;

        case RelationProcessorCore:
            processorCoreCount++;

            // A hyperthreaded core supplies more than one logical processor.
            logicalProcessorCount += CountSetBits(ptr->ProcessorMask);
            break;

        case RelationCache:
            // Cache data is in ptr->Cache, one CACHE_DESCRIPTOR structure for each cache. 
            Cache = &ptr->Cache;
            if (Cache->Level == 1)
            {
                processorL1CacheCount++;
            }
            else if (Cache->Level == 2)
            {
                processorL2CacheCount++;
            }
            else if (Cache->Level == 3)
            {
                processorL3CacheCount++;
            }
            break;

        case RelationProcessorPackage:
            // Logical processors share a physical package.
            processorPackageCount++;
            break;

        default:
            _tprintf(TEXT("\nError: Unsupported LOGICAL_PROCESSOR_RELATIONSHIP value.\n"));
            break;
        }
        byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
        ptr++;
    }

    _tprintf(TEXT("\nGetLogicalProcessorInformation results:\n"));
    _tprintf(TEXT("Number of NUMA nodes: %d\n"), 
             numaNodeCount);
    _tprintf(TEXT("Number of physical processor packages: %d\n"), 
             processorPackageCount);
    _tprintf(TEXT("Number of processor cores: %d\n"), 
             processorCoreCount);
    _tprintf(TEXT("Number of logical processors: %d\n"), 
             logicalProcessorCount);
    _tprintf(TEXT("Number of processor L1/L2/L3 caches: %d/%d/%d\n"), 
             processorL1CacheCount,
             processorL2CacheCount,
             processorL3CacheCount);
    
    free(buffer);

    return 0;
}

        示例代码中不仅可以获取CPU的物理核数,还可以获取到逻辑核数。

3、为了保持对老的Windows系统的兼容,采用动态获取函数地址的方式去调用新接口

       上述示例代码中没有直接去调用GetLogicalProcessorInformation接口,而是到kernel32.dll库中去动态地获取GetLogicalProcessorInformation接口的地址,因为该接口只在Windows XP SP3及以后的系统才添加这个接口,而WIndows XP SP2及以前的系统是没有的,为了保证代码能兼容WIndows XP SP2及以前的系统,采用动态获取接口地址的方式。对于在WIndows XP SP2及以前的系统中获取到的接口为NULL时,则使用GetSystemInfo函数去获取。       

       所以,对于Windows新推出的接口,为了让程序兼容老的系统,一般都采用动态获取接口地址的方式。如果直接在代码中调用API函数,则老的不支持该接口的系统中运行,启动时会弹出找不到接口的报错提示,导致程序没法运行。

       比如之前有底层的模块在代码中直接调用了inet_pton接口将IPV6字符串地址转化为整型数字地址,而这个函数在win8.1系统中才支持,如下:

 所以在win7上运行程序时就报错了,启动时报找不到inet_pton接口了,如下所示:

所以后面在调用一些新接口要注意了。 

猜你喜欢

转载自blog.csdn.net/chenlycly/article/details/124837451