iInfo 資訊交流: 用Windows API取得系統SMBIOS table的資訊

最新消息

[公告2014/05/30] 如有需要將部落格中,任何一篇文章的程式碼使用在商業用途,請與我聯繫。

[公告2015/04/26] Line版的 iInfo程式與投資應用 群組已上線想加入的朋友們,請先查看 "入群須知" 再與我聯繫 Line : aminwhite5168,加入請告知身分與回答 "入群須知" 的問題。

[公告2017/02/20] 近來有網友詢問 MultiCharts 與 Excel 畫冰火能量圖 (8)MultiCharts 與Excel 畫冰火能量圖(11) --- 更名「台股儀表板」 文章中教學檔案取得方式,有興趣的朋友可透過 Line了解詳情 (請勿以為是免費分享),Line : aminwhite5168。

[公告2018/04/22] 台北 Python + Excel VBA 金融資訊爬蟲課程,於 7/21、7/22 兩天開課,課程如網頁內容 金融資訊爬蟲班:台北班 Python 金融資訊爬蟲、EXCEL VBA 金融資訊爬蟲,5/30 前早鳥優惠票,請盡快把握機會,歡迎券商、大專院校邀約講座。。

2015年12月31日 星期四

用Windows API取得系統SMBIOS table的資訊

在UEFI BIOS機器上已不像Legacy BIOS機器這麼容易取得SMBIOS Table的資訊,這時可以使用微軟提供的Windows API GetSystemFirmwareTable函數取得UEFI BIOS下的SMBIOS Table的資訊,想了解更多有關SMBIOS Table請至官網查詢,以下就用程式碼來說明。

將以下程式碼Key到SMBIOS.cpp。
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
 CSMBIOSClass cSMBIOS;
 if(!cSMBIOS.InitSMBIOS())
 {
  printf("ERROR : initialize SMBIOS fail.\n");
  return FALSE;
 }
 cSMBIOS.ScanSMBIOS();
 cSMBIOS.PrintSMBIOSALLType(atoi(argv[1]));
 return TRUE;
}

將以下程式碼Key到SMBIOSClass.cpp。
// CSMBIOSClass
CSMBIOSClass::CSMBIOSClass()
{
 m_nTable_Length = 0;
 m_nTypeCount = 0;
 m_ucpbuffer = new UCHAR [SMBIOS_BUFFER_SIZE];
 ZeroMemory(&m_SMBIOSInfo, sizeof(m_SMBIOSInfo));
}

CSMBIOSClass::~CSMBIOSClass()
{
 if (m_ucpbuffer != NULL)
  delete [] m_ucpbuffer;
}

//取出每個Type的真正長度,請參考SMBOS官方網頁內容的spec https://www.dmtf.org/standards/smbios
UCHAR SMBIOS::GetRealLength()
{
 UCHAR ucRealLength = 0;
 UCHAR* ucpBegin = NULL;
 UCHAR* ucpPos = NULL;

 ucpBegin = (UCHAR*)this;
 ucpPos = ucpBegin + ucLength;

 for (;;)
 {  
  //每個Type結尾為兩個0
  if ((*ucpPos == 0) && (*(ucpPos+1) == 0)) 
  {
   ucpPos+=2;
   break;
  }
  ucpPos++;
 }

 ucRealLength = ucpPos - ucpBegin;

 return ucRealLength;
}

//使用GetSystemFirmwareTable取得系統SMBIOS table
BOOL CSMBIOSClass::InitSMBIOS()
{
 UCHAR ucTempData[SMBIOS_BUFFER_SIZE];
 PRawSMBIOSData pRawSMBiosData = NULL;

 ZeroMemory(ucTempData, sizeof(ucTempData));
 if(!GetSystemFirmwareTable('RSMB', NULL, ucTempData, SMBIOS_BUFFER_SIZE) )
  return FALSE;

 pRawSMBiosData = (PRawSMBIOSData)&ucTempData[0];
 m_nTable_Length = pRawSMBiosData->usLength;
 memcpy(m_ucpbuffer, (UCHAR * )pRawSMBiosData->ucSMBIOSTableData, SMBIOS_BUFFER_SIZE);

 return TRUE;
}

//掃出SMBIOS table全部的Type
void CSMBIOSClass::ScanSMBIOS()
{
 SMBIOS *pSMB;
 USHORT usOffset;
 int iCount;
 int i, j;

 j = 0;
 for(i = 0 ; i < SMBIOS_TYPE_COUNT ; i++)
 {
  usOffset = 0;
  iCount = 0;  
  m_SMBIOSInfo[j].nTypeCount = 0 ;
  while(usOffset < m_nTable_Length) {
   pSMB = (SMBIOS*) (m_ucpbuffer + usOffset);
   if (pSMB->ucType == i){
    m_SMBIOSInfo[j].dwAddress[iCount] = (DWORD)(m_ucpbuffer + usOffset);
    m_SMBIOSInfo[j].nLength[iCount] = pSMB->GetRealLength();
    iCount++;
   }
   usOffset += pSMB->GetRealLength();   
  }
  if(iCount){
   m_SMBIOSInfo[j].bIsExist = TRUE;
   m_SMBIOSInfo[j].ucType = i;
   m_SMBIOSInfo[j].nTypeCount = iCount;
   j++;
  }
 }
 m_nTypeCount = j;
}

//印出指定Type的內容
void CSMBIOSClass::PrintSMBIOSALLType(int nType)
{
 int i, j, k;
 UCHAR *cpPtr = NULL;

 for(i = 0 ; i < m_nTypeCount ; i++)
 {
  if( m_SMBIOSInfo[i].ucType == nType)
  {   
   for(j = 0 ; j < m_SMBIOSInfo[i].nTypeCount ; j++)
   {
    printf("SMBIS Type %d, 0x%02X\n", m_SMBIOSInfo[i].ucType, m_SMBIOSInfo[i].ucType);
    cpPtr = (UCHAR *)m_SMBIOSInfo[i].dwAddress[j];
    PrintData(cpPtr, m_SMBIOSInfo[i].nLength[j]);
   }
  }
 }
}

//映出SMBIOS每個Type的內容
void CSMBIOSClass::PrintData(UCHAR * ucptr, int nLength)
{
 int  j, k, nRemainLength = 0, nCurrentLength = 0; 

 printf("   | ");
 for(j = 0 ; j < 16; j++)
  printf("%02X ", j);
 printf("  ");
 for(j = 0 ; j < 16; j++)
  printf("%X", j);
 printf("\n");
 printf("-----------------------------------------------------------------------\n");

 nRemainLength = nLength;
 for(j = 0 ; j < nLength ; j+=16)
 {  
  if(nRemainLength > 16)
   nCurrentLength = 16;
  else
   nCurrentLength = nRemainLength;

  printf("%02X | ", j);
  for(k = 1 ; k <= nCurrentLength ; k++)
   printf("%02X ", ucptr[j+k-1]);

  printf("  ");

  if(nRemainLength < 16) 
   SetXY(55);

  for(k = 1 ; k <= nCurrentLength ; k++)
  {
   if((ucptr[j+k-1] <= 0x20) || (ucptr[j+k-1] > 0x7F))
    printf(".");
   else
    printf("%c", ucptr[j+k-1]);
  }

  printf("\n");
  nRemainLength -= 16; 
 }
 printf("-----------------------------------------------------------------------\n\n");
}

//設定游標座標位置
void CSMBIOSClass::SetXY(int nCoordX)   
{   
 CONSOLE_SCREEN_BUFFER_INFO scrInfo;
 COORD coord;
 int x, y;

 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&scrInfo);   
 x = scrInfo.dwCursorPosition.X ;   
 y = scrInfo.dwCursorPosition.Y ;
 coord.X = nCoordX;
 coord.Y = y;
 SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), coord );
}

將以下程式碼Key到SMBIOSClass.h
// CSMBIOSClass
#define SMBIOS_TYPE_COUNT 256
#define SMBIOS_BUFFER_SIZE  0xFFFF

typedef struct _tagSYSTYPEINFO
{
 BOOL bIsExist;
 UCHAR ucType;
 int nTypeCount;
 int nLength[32];
 DWORD dwAddress[32];
}SYSTYPEINFO, *PSYSTYPEINFO;

//請參考微軟官網https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms724379(v=vs.85).aspx 定義的結構
typedef struct tagRawSMBIOSData
{
 UCHAR ucUsed20CallingMethod;
 UCHAR ucSMBIOSMajorVersion;
 UCHAR ucSMBIOSMinorVersion;
 UCHAR ucmiRevision;
 ULONG usLength;
 UCHAR ucSMBIOSTableData[];
}RawSMBIOSData, *PRawSMBIOSData;

class SMBIOS
{
public:
 UCHAR ucType;
 UCHAR ucLength;
 USHORT usHandle;
 UCHAR GetRealLength();
};

class CSMBIOSClass : SMBIOS
{
public:
 CSMBIOSClass();
 virtual ~CSMBIOSClass();
 BOOL InitSMBIOS();
 void ScanSMBIOS();
 void PrintSMBIOSALLType(int nType);

private:
 void PrintData(UCHAR * ucptr, int nLength);
 void SetXY(int nCoordX);
 SYSTYPEINFO m_SMBIOSInfo[SMBIOS_TYPE_COUNT];
 UCHAR * m_ucpbuffer;
 int m_nTable_Length, m_nTypeCount;
};

執行畫面

參考資料: