UPDATE : If you want access the SMBIOS using Delphi or Free Pascal try the TSMBIOS project.
The system management BIOS (SMBIOS) is a specification of how the system vendors present management information about their products in a standard format, so you can use the SMBIOS to discover information about the hardware platform, such as the system manufacturer, the system BIOS version, processor installed characteristics and so on. From windows you can acceess the SMBIOS tables using the WMI or the WinApi. In this article I will show how you can gain access to the SMBios tables using the WMI and Delphi.
The SMBIOS Tables
The tables are located directly adjacent to each other in memory. Each table is composed of a 4-byte header, a specific structure , and an optional string table.
Header
This is the header description of each table

The type field present in the header define how each table must be interpreted, this is a list of a few table types
- BIOS Information (Type 0)
- System Information (Type 1)
- System Enclosure (Type 3)
- Processor Information (Type 4)
- Cache Information (Type 7)
- System Slots (Type 9)
- Physical Memory Array (Type 16)
- Memory Device (Type 17)
- Memory Array Mapped Address (Type 19)
- System Boot Information (Type 32)
Text Strings
The text strings associated with a given SMBIOS table are appended directly after the formatted portion of the structure. Each string is terminated with a null (00h) BYTE and the set of strings is terminated with an additional null (00h) BYTE. When the formatted portion of a SMBIOS structure references a string, it does so by specifying a non-zero string number within the structure’s string-set. For example, if a string field contains 02h, it references the second string following the formatted portion of the SMBIOS structure. If a string field references no string, a null (0) is placed in that string field.
Check the next representation of a BIOS Table (Type
db 0 ; Indicates BIOS Structure Type |
db 13h ; Length of information in bytes | Header of the table
dw ? ; Reserved for handle |
db 01h ; String 1 is the Vendor Name |
db 02h ; String 2 is the BIOS version |
dw 0E800h ; BIOS Starting Address |
db 03h ; String 3 is the BIOS Build Date | formatted portion
db 1 ; Size of BIOS ROM is 128K (64K * (1 + 1)) |
dq BIOS_Char ; BIOS Characteristics |
db 0 ; BIOS Characteristics Extension Byte 1 |
db ‘System BIOS Vendor Name’,0 ; |
db ‘4.04’,0 ; | Text Strings (unformatted portion)
db ‘00/00/0000’,0 ; |
db 0 ; End of structure
WMI
Now which you know the basics about the SMBios table structures, you need a way to retrieve such tables. the WMI provides a way to get the raw SMBIOS data in a single buffer using the class MSSmBios_RawSMBiosTables this class is in the root\WMI namespace and is available since Windows XP.
Check this small snippet to retrieve the SMBios tables buffer contained in the SMBiosData property
const
wbemFlagForwardOnly = $00000020;
var
FSWbemLocator : OLEVariant;
FWMIService : OLEVariant;
FWbemObjectSet: OLEVariant;
FWbemObject : OLEVariant;
oEnum : IEnumvariant;
iValue : LongWord;
vArray : variant;
Value : integer;
i : integer;
begin;
FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
//connect to the Wmi service
FWMIService := FSWbemLocator.ConnectServer('localhost', 'root\WMI', '', '');
//execute the WQL sentence
FWbemObjectSet := FWMIService.ExecQuery('SELECT * FROM MSSmBios_RawSMBiosTables', 'WQL', wbemFlagForwardOnly);
oEnum := IUnknown(FWbemObjectSet._NewEnum) as IEnumvariant;
while oEnum.Next(1, FWbemObject, iValue) = 0 do
begin
//store the size of the buffer
FSize := FWbemObject.Size;
GetMem(FBuffer, FSize);
//get the addtional properties like the SMBIos version
FDmiRevision:= FWbemObject.DmiRevision;
FSmbiosMajorVersion :=FWbemObject.SmbiosMajorVersion;
FSmbiosMinorVersion :=FWbemObject.SmbiosMinorVersion;
//get the buffer of the SMBios tables
vArray := FWbemObject.SMBiosData;
FWbemObject := Unassigned;
end;
end;
Delphi
From the Delphi side we need to create a few structures to hold the data of the tables and write some helper functions to parse the information.
type
// http://www.dmtf.org/standards/smbios
SMBiosTables = (
BIOSInformation = 0,
SystemInformation = 1,
BaseBoardInformation = 2,
EnclosureInformation = 3,
ProcessorInformation = 4,
MemoryControllerInformation = 5,
MemoryModuleInformation = 6,
CacheInformation = 7,
PortConnectorInformation = 8,
SystemSlotsInformation = 9,
OnBoardDevicesInformation = 10,
OEMStrings = 11,
SystemConfigurationOptions = 12,
BIOSLanguageInformation = 13,
GroupAssociations = 14,
SystemEventLog = 15,
PhysicalMemoryArray = 16,
MemoryDevice = 17,
MemoryErrorInformation = 18,
MemoryArrayMappedAddress = 19,
MemoryDeviceMappedAddress = 20,
EndofTable = 127); //for more tables check the official specifications http://www.dmtf.org/standards/smbios
//the header of each SMBios table
TSmBiosTableHeader = packed record
TableType: Byte;
Length: Byte;
Handle: Word;
end;
//this is a helper record to store the header and index of each table stored in the buffer
TSMBiosTableEntry = record
Header: TSmBiosTableHeader;
Index : Integer;
end;
//this is the record to store Bios information about the table Type 0
TBiosInfo = packed record
Header: TSmBiosTableHeader;
Vendor: Byte;
Version: Byte;
StartingSegment: Word;
ReleaseDate: Byte;
BiosRomSize: Byte;
Characteristics: Int64;
ExtensionBytes : array [0..1] of Byte;
end;
The next code show how you can parse the buffer with the raw data and fill a Generic TList of TSMBiosTableEntry records.
function GetSMBiosTablesList(Buffer : PByteArray): TList;
Var
Index : integer;
Header: TSmBiosTableHeader;
Entry : TSMBiosTableEntry;
begin
Result := TList.Create;
Index := 0;
repeat
//read the header
Move(Buffer[Index], Header, SizeOf(Header));
Entry.Header:=Header;
Entry.Index:=Index;
//add to the list
Result.Add(Entry);
//check if the tale type is 127 (end of tables)
if Header.TableType=Ord(SMBiosTables.EndofTable) then break;
//increase the Index in the length of the formatted data
inc(Index, Header.Length);
//Text strings zone
//check for the boundaries of the buffer
if Index+1>FSize then
Break;
//check for $00 $00 -> table termination
while not((Buffer[Index] = 0) and (Buffer[Index + 1] = 0)) do
if Index+1>FSize then
Break
else
inc(Index);
inc(Index, 2);
until (Index>FSize);
end;
Now to retrieve a text string from a SMBios table
//the Entry Parameter is the buffer to the unformatted data
//The index is the number of the text string to retrieve
function GetSMBiosString(Entry, Index: integer): AnsiString;
var
i: integer;
p: PAnsiChar;
begin
Result := '';
for i := 1 to Index do
begin
p := PAnsiChar(@Buffer[Entry]);
if i = Index then
begin
Result := p;
break;
end
else
inc(Entry, StrLen(p) + 1);
end;
end;
A unit to retrieve the Smbios tables
Here I leave a unit to retrieve and parse the SMBios tables, the unit does not cover all the tables types (you can read the official specification)
//Author Rodrigo Ruz V.
//2011-08-01
unit uSMBIOS;
interface
uses
SysUtils,
Windows,
Generics.Collections,
Classes;
type
// http://www.dmtf.org/standards/smbios
SMBiosTables = (
BIOSInformation = 0,
SystemInformation = 1,
BaseBoardInformation = 2,
EnclosureInformation = 3,
ProcessorInformation = 4,
MemoryControllerInformation = 5,
MemoryModuleInformation = 6,
CacheInformation = 7,
PortConnectorInformation = 8,
SystemSlotsInformation = 9,
OnBoardDevicesInformation = 10,
OEMStrings = 11,
SystemConfigurationOptions = 12,
BIOSLanguageInformation = 13,
GroupAssociations = 14,
SystemEventLog = 15,
PhysicalMemoryArray = 16,
MemoryDevice = 17,
MemoryErrorInformation = 18,
MemoryArrayMappedAddress = 19,
MemoryDeviceMappedAddress = 20,
EndofTable = 127);
TSmBiosTableHeader = packed record
TableType: Byte;
Length: Byte;
Handle: Word;
end;
TBiosInfo = packed record
Header: TSmBiosTableHeader;
Vendor: Byte;
Version: Byte;
StartingSegment: Word;
ReleaseDate: Byte;
BiosRomSize: Byte;
Characteristics: Int64;
ExtensionBytes : array [0..1] of Byte;
end;
TSysInfo = packed record
Header: TSmBiosTableHeader;
Manufacturer: Byte;
ProductName: Byte;
Version: Byte;
SerialNumber: Byte;
UUID: array [0 .. 15] of Byte;
WakeUpType: Byte;
end;
TBaseBoardInfo = packed record
Header: TSmBiosTableHeader;
Manufacturer: Byte;
Product: Byte;
Version: Byte;
SerialNumber: Byte;
end;
TEnclosureInfo = packed record
Header: TSmBiosTableHeader;
Manufacturer: Byte;
&Type: Byte;
Version: Byte;
SerialNumber: Byte;
AssetTagNumber: Byte;
BootUpState: Byte;
PowerSupplyState: Byte;
ThermalState: Byte;
SecurityStatus: Byte;
OEM_Defined: DWORD;
end;
TProcessorInfo = packed record
Header: TSmBiosTableHeader;
SocketDesignation: Byte;
ProcessorType: Byte;
ProcessorFamily: Byte;
ProcessorManufacturer: Byte;
ProcessorID: Int64; // QWORD;
ProcessorVersion: Byte;
Voltaje: Byte;
ExternalClock: Word;
MaxSpeed: Word;
CurrentSpeed: Word;
Status: Byte;
ProcessorUpgrade: Byte;
L1CacheHandler: Word;
L2CacheHandler: Word;
L3CacheHandler: Word;
SerialNumber: Byte;
AssetTag: Byte;
PartNumber: Byte;
end;
TCacheInfo = packed record
Header: TSmBiosTableHeader;
SocketDesignation: Byte;
CacheConfiguration: DWORD;
MaximumCacheSize: Word;
InstalledSize: Word;
SupportedSRAMType: Word;
CurrentSRAMType: Word;
CacheSpeed: Byte;
ErrorCorrectionType: Byte;
SystemCacheType: Byte;
Associativity: Byte;
end;
TSMBiosTableEntry = record
Header: TSmBiosTableHeader;
Index : Integer;
end;
TSMBios = class
private
FSize: integer;
FBuffer: PByteArray;
FDataString: AnsiString;
FBiosInfo: TBiosInfo;
FSysInfo: TSysInfo;
FBaseBoardInfo: TBaseBoardInfo;
FEnclosureInfo: TEnclosureInfo;
FProcessorInfo: TProcessorInfo;
FBiosInfoIndex: Integer;
FSysInfoIndex: Integer;
FBaseBoardInfoIndex: Integer;
FEnclosureInfoIndex: Integer;
FProcessorInfoIndex: Integer;
FDmiRevision: Integer;
FSmbiosMajorVersion: Integer;
FSmbiosMinorVersion: Integer;
FSMBiosTablesList: TList;
procedure LoadSMBIOS;
procedure ReadSMBiosTables;
function GetHasBiosInfo: Boolean;
function GetHasSysInfo: Boolean;
function GetHasBaseBoardInfo: Boolean;
function GetHasEnclosureInfo: Boolean;
function GetHasProcessorInfo: Boolean;
function GetSMBiosTablesList:TList;
public
constructor Create;
destructor Destroy; override;
function SearchSMBiosTable(TableType: SMBiosTables): integer;
function GetSMBiosTableIndex(TableType: SMBiosTables): integer;
function GetSMBiosString(Entry, Index: integer): AnsiString;
property Size: integer read FSize;
property Buffer: PByteArray read FBuffer;
property DataString: AnsiString read FDataString;
property DmiRevision: Integer read FDmiRevision;
property SmbiosMajorVersion : Integer read FSmbiosMajorVersion;
property SmbiosMinorVersion : Integer read FSmbiosMinorVersion;
property SMBiosTablesList : TList read FSMBiosTablesList;
property BiosInfo: TBiosInfo read FBiosInfo Write FBiosInfo;
property BiosInfoIndex: Integer read FBiosInfoIndex Write FBiosInfoIndex;
property HasBiosInfo : Boolean read GetHasBiosInfo;
property SysInfo: TSysInfo read FSysInfo Write FSysInfo;
property SysInfoIndex: Integer read FSysInfoIndex Write FSysInfoIndex;
property HasSysInfo : Boolean read GetHasSysInfo;
property BaseBoardInfo: TBaseBoardInfo read FBaseBoardInfo write FBaseBoardInfo;
property BaseBoardInfoIndex: Integer read FBaseBoardInfoIndex Write FBaseBoardInfoIndex;
property HasBaseBoardInfo : Boolean read GetHasBaseBoardInfo;
property EnclosureInfo: TEnclosureInfo read FEnclosureInfo write FEnclosureInfo;
property EnclosureInfoIndex: Integer read FEnclosureInfoIndex Write FEnclosureInfoIndex;
property HasEnclosureInfo : Boolean read GetHasEnclosureInfo;
property ProcessorInfo: TProcessorInfo read FProcessorInfo write FProcessorInfo;
property ProcessorInfoIndex: Integer read FProcessorInfoIndex Write FProcessorInfoIndex;
property HasProcessorInfo : Boolean read GetHasProcessorInfo;
end;
implementation
uses
ComObj,
ActiveX,
Variants;
{ TSMBios }
constructor TSMBios.Create;
begin
Inherited;
FBuffer := nil;
FSMBiosTablesList:=nil;
LoadSMBIOS;
ReadSMBiosTables;
end;
destructor TSMBios.Destroy;
begin
if Assigned(FBuffer) and (FSize > 0) then
FreeMem(FBuffer);
if Assigned(FSMBiosTablesList) then
FSMBiosTablesList.Free;
Inherited;
end;
function TSMBios.GetHasBaseBoardInfo: Boolean;
begin
Result:=FBaseBoardInfoIndex>=0;
end;
function TSMBios.GetHasBiosInfo: Boolean;
begin
Result:=FBiosInfoIndex>=0;
end;
function TSMBios.GetHasEnclosureInfo: Boolean;
begin
Result:=FEnclosureInfoIndex>=0;
end;
function TSMBios.GetHasProcessorInfo: Boolean;
begin
Result:=FProcessorInfoIndex>=0;
end;
function TSMBios.GetHasSysInfo: Boolean;
begin
Result:=FSysInfoIndex>=0;
end;
function TSMBios.SearchSMBiosTable(TableType: SMBiosTables): integer;
Var
Index : integer;
Header : TSmBiosTableHeader;
begin
Index := 0;
repeat
Move(Buffer[Index], Header, SizeOf(Header));
if Header.TableType = Ord(TableType) then
break
else
begin
inc(Index, Header.Length);
if Index+1>FSize then
begin
Index:=-1;
Break;
end;
while not((Buffer[Index] = 0) and (Buffer[Index + 1] = 0)) do
if Index+1>FSize then
begin
Index:=-1;
Break;
end
else
inc(Index);
inc(Index, 2);
end;
until (Index>FSize);
Result := Index;
end;
function TSMBios.GetSMBiosString(Entry, Index: integer): AnsiString;
var
i: integer;
p: PAnsiChar;
begin
Result := '';
for i := 1 to Index do
begin
p := PAnsiChar(@Buffer[Entry]);
if i = Index then
begin
Result := p;
break;
end
else
inc(Entry, StrLen(p) + 1);
end;
end;
function TSMBios.GetSMBiosTableIndex(TableType: SMBiosTables): integer;
Var
Entry : TSMBiosTableEntry;
begin
Result:=-1;
for Entry in FSMBiosTablesList do
if Entry.Header.TableType=Ord(TableType) then
begin
Result:=Entry.Index;
Break;
end;
end;
function TSMBios.GetSMBiosTablesList: TList;
Var
Index : integer;
Header: TSmBiosTableHeader;
Entry : TSMBiosTableEntry;
begin
Result := TList.Create;
Index := 0;
repeat
Move(Buffer[Index], Header, SizeOf(Header));
Entry.Header:=Header;
Entry.Index:=Index;
Result.Add(Entry);
if Header.TableType=Ord(SMBiosTables.EndofTable) then break;
inc(Index, Header.Length);// + 1);
if Index+1>FSize then
Break;
while not((Buffer[Index] = 0) and (Buffer[Index + 1] = 0)) do
if Index+1>FSize then
Break
else
inc(Index);
inc(Index, 2);
until (Index>FSize);
end;
procedure TSMBios.LoadSMBIOS;
const
wbemFlagForwardOnly = $00000020;
var
FSWbemLocator: OLEVariant;
FWMIService: OLEVariant;
FWbemObjectSet: OLEVariant;
FWbemObject: OLEVariant;
oEnum: IEnumvariant;
iValue: LongWord;
vArray: variant;
Value: integer;
i: integer;
begin;
FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
FWMIService := FSWbemLocator.ConnectServer('localhost', 'root\WMI', '', '');
FWbemObjectSet := FWMIService.ExecQuery('SELECT * FROM MSSmBios_RawSMBiosTables', 'WQL', wbemFlagForwardOnly);
oEnum := IUnknown(FWbemObjectSet._NewEnum) as IEnumvariant;
if oEnum.Next(1, FWbemObject, iValue) = 0 then
begin
FSize := FWbemObject.Size;
GetMem(FBuffer, FSize);
FDmiRevision:= FWbemObject.DmiRevision;
FSmbiosMajorVersion :=FWbemObject.SmbiosMajorVersion;
FSmbiosMinorVersion :=FWbemObject.SmbiosMinorVersion;
vArray := FWbemObject.SMBiosData;
if (VarType(vArray) and VarArray) <> 0 then
for i := VarArrayLowBound(vArray, 1) to VarArrayHighBound(vArray, 1) do
begin
Value := vArray[i];
Buffer[i] := Value;
if Value in [$20..$7E] then
FDataString := FDataString + AnsiString(Chr(Value))
else
FDataString := FDataString + '.';
end;
FSMBiosTablesList:=GetSMBiosTablesList;
FWbemObject := Unassigned;
end;
end;
procedure TSMBios.ReadSMBiosTables;
begin
FBiosInfoIndex := GetSMBiosTableIndex(BIOSInformation);
if FBiosInfoIndex >= 0 then
Move(Buffer[FBiosInfoIndex], FBiosInfo, SizeOf(FBiosInfo));
FSysInfoIndex := GetSMBiosTableIndex(SystemInformation);
if FSysInfoIndex >= 0 then
Move(Buffer[FSysInfoIndex], FSysInfo, SizeOf(FSysInfo));
FBaseBoardInfoIndex := GetSMBiosTableIndex(BaseBoardInformation);
if FBaseBoardInfoIndex >= 0 then
Move(Buffer[FBaseBoardInfoIndex], FBaseBoardInfo, SizeOf(FBaseBoardInfo));
FEnclosureInfoIndex := GetSMBiosTableIndex(EnclosureInformation);
if FEnclosureInfoIndex >= 0 then
Move(Buffer[FEnclosureInfoIndex], FEnclosureInfo, SizeOf(FEnclosureInfo));
FProcessorInfoIndex := GetSMBiosTableIndex(ProcessorInformation);
if FProcessorInfoIndex >= 0 then
Move(Buffer[FProcessorInfoIndex], FProcessorInfo, SizeOf(FProcessorInfo));
end;
end.
Sample project
This console application show how use this unit
{$APPTYPE CONSOLE}
uses
Classes,
SysUtils,
ActiveX,
ComObj,
uSMBIOS in 'uSMBIOS.pas';
procedure GetMSSmBios_RawSMBiosTablesInfo;
Var
SMBios : TSMBios;
UUID : Array[0..31] of Char;
Entry : TSMBiosTableEntry;
begin
SMBios:=TSMBios.Create;
try
With SMBios do
begin
Writeln(Format('%d SMBios tables found',[SMBiosTablesList.Count]));
Writeln('');
Writeln('Type Handle Length Index');
for Entry in SMBiosTablesList do
Writeln(Format('%3d %4x %3d %4d',[Entry.Header.TableType, Entry.Header.Handle, Entry.Header.Length, Entry.Index]));
Readln;
if HasBiosInfo then
begin
WriteLn('Bios Information');
WriteLn('Vendor '+GetSMBiosString(BiosInfoIndex + BiosInfo.Header.Length, BiosInfo.Vendor));
WriteLn('Version '+GetSMBiosString(BiosInfoIndex + BiosInfo.Header.Length, BiosInfo.Version));
WriteLn('Start Segment '+IntToHex(BiosInfo.StartingSegment,4));
WriteLn('ReleaseDate '+GetSMBiosString(BiosInfoIndex + BiosInfo.Header.Length, BiosInfo.ReleaseDate));
WriteLn(Format('Bios Rom Size %d k',[64*(BiosInfo.BiosRomSize+1)]));
WriteLn('');
end;
if HasSysInfo then
begin
WriteLn('System Information');
WriteLn('Manufacter '+GetSMBiosString(SysInfoIndex + SysInfo.Header.Length, SysInfo.Manufacturer));
WriteLn('Product Name '+GetSMBiosString(SysInfoIndex + SysInfo.Header.Length, SysInfo.ProductName));
WriteLn('Version '+GetSMBiosString(SysInfoIndex + SysInfo.Header.Length, SysInfo.Version));
WriteLn('Serial Number '+GetSMBiosString(SysInfoIndex + SysInfo.Header.Length, SysInfo.SerialNumber));
BinToHex(@SysInfo.UUID,UUID,SizeOf(SysInfo.UUID));
WriteLn('UUID '+UUID);
WriteLn('');
end;
if HasBaseBoardInfo then
begin
WriteLn('BaseBoard Information');
WriteLn('Manufacter '+GetSMBiosString(BaseBoardInfoIndex + BaseBoardInfo.Header.Length, BaseBoardInfo.Manufacturer));
WriteLn('Product '+GetSMBiosString(BaseBoardInfoIndex + BaseBoardInfo.Header.Length, BaseBoardInfo.Product));
WriteLn('Version '+GetSMBiosString(BaseBoardInfoIndex + BaseBoardInfo.Header.Length, BaseBoardInfo.Version));
WriteLn('Serial Number '+GetSMBiosString(BaseBoardInfoIndex + BaseBoardInfo.Header.Length, BaseBoardInfo.SerialNumber));
WriteLn('');
end;
if HasEnclosureInfo then
begin
WriteLn('Enclosure Information');
WriteLn('Manufacter '+GetSMBiosString(EnclosureInfoIndex + EnclosureInfo.Header.Length, EnclosureInfo.Manufacturer));
WriteLn('Version '+GetSMBiosString(EnclosureInfoIndex + EnclosureInfo.Header.Length, EnclosureInfo.Version));
WriteLn('Serial Number '+GetSMBiosString(EnclosureInfoIndex + EnclosureInfo.Header.Length, EnclosureInfo.SerialNumber));
WriteLn('Asset Tag Number '+GetSMBiosString(EnclosureInfoIndex + EnclosureInfo.Header.Length, EnclosureInfo.AssetTagNumber));
WriteLn('');
end;
if HasProcessorInfo then
begin
WriteLn('Processor Information');
WriteLn('Socket Designation '+GetSMBiosString(ProcessorInfoIndex + ProcessorInfo.Header.Length, ProcessorInfo.SocketDesignation));
WriteLn('Processor Manufacturer '+GetSMBiosString(ProcessorInfoIndex + ProcessorInfo.Header.Length, ProcessorInfo.ProcessorManufacturer));
WriteLn('Serial Number '+GetSMBiosString(ProcessorInfoIndex + ProcessorInfo.Header.Length, ProcessorInfo.SerialNumber));
WriteLn('Asset Tag '+GetSMBiosString(ProcessorInfoIndex + ProcessorInfo.Header.Length, ProcessorInfo.AssetTag));
WriteLn('Part Number '+GetSMBiosString(ProcessorInfoIndex + ProcessorInfo.Header.Length, ProcessorInfo.PartNumber));
WriteLn('');
end;
end;
finally
SMBios.Free;
end;
end;
begin
try
CoInitialize(nil);
try
GetMSSmBios_RawSMBiosTablesInfo;
finally
CoUninitialize;
end;
except
on E:EOleException do
Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
on E:Exception do
Writeln(E.Classname, ':', E.Message);
end;
Writeln('Press Enter to exit');
Readln;
end.
Check these samples images of the console App.


Additional Information
Check the next resources to get more info about the SMBios
-33.636934
-70.679350