记录第四周的C++作业


作业要求

/*
用Visual Studio 编写一个控制台程序,功能如下:
一、使用SMBIOS读取本机的MEMORY信息,并打印一下信息
[MEMORY]
NUM = 2
SIZE=32GB

[0]
LOCATOR=DIMM1
SIZE=16GB
TYPE=DDR4
SPEED=2666MHZ
MANUFACTURER=Micron

[1]
LOCATOR=DIMM1
SIZE=16GB
TYPE=DDR4
SPEED=2666MHZ
MANUFACTURER=Micron

注:可以参考RW上SMBIOS信息对照
*/

头文件

#pragma once //只编译一次,节省时间
/*包含的头文件*/
#include<stdio.h>
#include<string>
#include<iostream>
#include<windows.h>
#include <SDKDDKVer.h>
#include <tchar.h>
/*使用命名空间*/
using namespace std;
//保存之前的结构体对齐方式
#pragma pack(push) 
//使用新的结构体对齐方式
#pragma pack(1)

声明结构体

/*存储SMBIOS信息的长度和尾部地址*/
typedef struct _RawSMBIOSData
{
    DWORD Length;
    PBYTE* SMBIOSTableData;
} RawSMBIOSData, * PRawSMBIOSData;
/*存储各TYPE的三类信息*/
typedef struct _SMBIOSHEADER_
{
    BYTE Type;
    BYTE Length;
    WORD Handle;
} SMBIOSHEADER, * PSMBIOSHEADER;
/*存储TYPE16信息*/
typedef struct _TYPE_16_ {
    SMBIOSHEADER Header;
    UCHAR  Location;
    UCHAR  Use;
    UCHAR  MemoryErrorCorrection;
    ULONG32  MaximumCapacity;
    UINT16 MemoryErrorHandle;
    UINT16 NumberofMemoryDevices;
    ULONG32 ExtendedMaximumCapacity;
} PhysicalMemoryArray, * PPhysicalMemoryArray;
/*存储TYPE17信息*/
typedef struct _TYPE_17_ {
    SMBIOSHEADER Header;
    UINT16  PhysicalArrayHandle;
    UINT16  ErrorInformationHandle;
    UINT16  TotalWidth;
    UINT16  DataWidth;
    UINT16  Size;
    UCHAR   FormFactor;
    UCHAR   DeviceSet;
    UCHAR   DeviceLocator;
    UCHAR   BankLocator;
    UCHAR   MemoryType;
    UINT16  TypeDetail;
    UINT16  Speed;
    UCHAR   Manufacturer;
    UCHAR   SN;
    UCHAR   AssetTag;
    UCHAR   PN;
    UCHAR   Attributes;
} MemoryDevice, * PMemoryDevice;

枚举Memory type

enum _MemoryType_
{
    Other = 1, Un_known, DRAM, EDRAM,
    VRAM, SRAM, RAM, ROM, FLASH,
    EEPROM, FEPROM, EPROM, CDRAM,
    RAM_3D, SDRAM, SGRAM, RDRAM,
    DDR, DDR2, DDR2_FB_DIMM, Reserved_1,
    Reserved_2, Reserved_3,DDR3, FBD2, DDR4,
    LPDDR, LPDDR2, LPDDR3,LPDDR4,
    Logicalnonvolatiledevice,HBM, HBM2, DDR5, LPDDR5
};
//自定义对齐方式结束
#pragma pack(pop)

字符编码

/*根据编码选择字符解析方式*/
#ifdef UNICODE
#define LocateString LocateStringW
#else
#define LocateString    LocateStringA
#endif

几个函数

/*返回信息尾部地址*/
const char* toPointString(void* p)
{
    return (char*)p + ((PSMBIOSHEADER)p)->Length;
}

/*解析字符串*/
const char* LocateStringA(const char* str, UINT i)
{
    static const char strNull[] = "Null String";

    if (0 == i || 0 == *str) return strNull;

    while (--i)
    {
        str += strlen((char*)str) + 1;
    }
    return str;
}
/*解析字符串*/
const wchar_t* LocateStringW(const char* str, UINT i)
{
    static wchar_t buff[2048];
    const char* pStr = LocateStringA(str, i);
    SecureZeroMemory(buff, sizeof(buff));
    MultiByteToWideChar(CP_OEMCP, 0, pStr, (int)strlen(pStr), buff, sizeof(buff));
    return buff;
}
/*输出TYPE16信息*/
bool ProcPhysicalMemoryArray(void* p)
{
    PPhysicalMemoryArray pMAMA = (PPhysicalMemoryArray)p;
    const char* str = toPointString(p);

    _tprintf(TEXT("NUM = %d\n"), pMAMA->NumberofMemoryDevices);
    _tprintf(TEXT("SIZE=%dGB\n"), pMAMA->MaximumCapacity / 1024 / 1024);
    return true;
}
/*输出TYPE17信息*/
bool ProcMemoryDevice(void* p)
{
    PMemoryDevice pMD = (PMemoryDevice)p;
    const char* str = toPointString(p);

    _tprintf(TEXT("LOCATOR=%s\n"), LocateString(str, pMD->DeviceLocator));
    _tprintf(TEXT("SIZE=%dGB\n"), pMD->Size / 1024);
    switch (pMD->MemoryType)
    {
    case Other:_tprintf(TEXT("TYPE=0x%02X Other\n"),pMD->MemoryType); break;
    case Un_known:_tprintf(TEXT("TYPE=0x%02X Unknown\n"), pMD->MemoryType); break;
    case DDR3:_tprintf(TEXT("TYPE=0x%02X DDR3\n"), pMD->MemoryType); break;
    case DDR4:_tprintf(TEXT("TYPE=0x%02X DDR4\n"), pMD->MemoryType); break;
    }
    if (pMD->Header.Length > 0x15)
    {
        _tprintf(TEXT("SPEED=%dMHZ\n"), pMD->Speed);
        _tprintf(TEXT("MANUFACTURER=%s\n"), LocateString(str, pMD->Manufacturer));
    }

    return true;
}
/*根据需求调用bool函数*/
bool DispatchStructType(PSMBIOSHEADER hdr)
{
    typedef struct {
        BYTE t;
        bool(*Proc)(void* p);
    } TPFUNC;

    const TPFUNC    tpfunc[] = {
        { 16, ProcPhysicalMemoryArray},
        { 17, ProcMemoryDevice },
    };

    for (UINT i = 0; i < sizeof(tpfunc) / sizeof(TPFUNC); i++)
    {
        if (tpfunc[i].t == hdr->Type)
        {
            tpfunc[i].Proc((void*)hdr);
            return true;
        }
    }

    return false;
}
/*选择要打印的SMBIOS信息*/
void DumpSMBIOSStruct(void* Addr, UINT Len)
{
    int i = 0;
    LPBYTE p = (LPBYTE)(Addr);
    const LPBYTE lastAddress = p + Len;
    PSMBIOSHEADER pHeader;
    for (;;)
    {
        pHeader = (PSMBIOSHEADER)p;
        if (pHeader->Type == 16)
        {
            _tprintf(TEXT("[Memory]\n"));
            DispatchStructType(pHeader);
            _tprintf(TEXT("\n"));
        }
        if (pHeader->Type == 17)
        {
            _tprintf(TEXT("[%d]\n"), i);
            i++;
            DispatchStructType(pHeader);
            _tprintf(TEXT("\n"));
        }
        if ((pHeader->Type == 127) && (pHeader->Length == 4))
        {
            break;
        }
        LPBYTE nt = p + pHeader->Length; 
        while (0 != (*nt | *(nt + 1))) nt++; 
        nt += 2;
        if (nt >= lastAddress)
            break;
        p = nt;
    }
}

主函数

/*主函数*/
int _tmain(int nAgrv,char* argv[])
{
    /*_MemoryType_ zzz;
    zzz = _MemoryType_(18);
    printf("%d\n", zzz);*/
    DWORD needBufferSize = 0;
    const BYTE byteSignature[] = { 'B','M','S','R' };
    const DWORD Signature = *((DWORD*)byteSignature);
    LPBYTE pBuff = NULL;
    /*调用接口获取信息长度*/
    needBufferSize = GetSystemFirmwareTable(Signature, 0, NULL, 0);
    /*根据信息长度开辟内存空间*/
    pBuff = (LPBYTE)malloc(needBufferSize);
    if (pBuff)
    {
        //再次调用接口获取信息
        GetSystemFirmwareTable(Signature, 0, pBuff, needBufferSize);
        const PRawSMBIOSData pDMIData = (PRawSMBIOSData)pBuff;
        DumpSMBIOSStruct(&(pDMIData->SMBIOSTableData), pDMIData->Length);
    }
}

总结

是根据一篇从网上找到的现有代码,经过一定删减,并根据Type17结构体写法设计出Type16的结构体,最终筛选出所需信息,然而觉得硬件还是相当难搞。