BSOne.SFC/Tocsg.Lib/VCL/Tocsg.WMI.pas

739 lines
21 KiB
Plaintext

{*******************************************************}
{ }
{ Tocsg.WMI }
{ }
{ Copyright (C) 2022 kku }
{ }
{*******************************************************}
unit Tocsg.WMI;
interface
uses
System.Classes, System.SysUtils, Winapi.Windows,
Tocsg.Thread, EM.WbemScripting_TLB;
const
WMI_ROOT_OBJECT = 'root\cimv2';
KBYTE = Sizeof(Byte) shl 10;
MBYTE = KBYTE shl 10;
GBYTE = MBYTE shl 10;
WBEM_INFINITE = $FFFFFFFF;
WMI_SERVICE_NAME = 'Winmgmt';
WMI_EVENT_CREATE = 2084146945;
WMI_EVENT_MODIFY = 1531267689;
WMI_EVENT_DELETE = 2121895681;
type
TWMIEventKind = (wkUnknown, wkCreate, wkDelete, wkModify);
TWMINotifyEvent = procedure(Sender: TObject; WMIEventKind: TWMIEventKind;
ovEvent: OleVariant) of object;
TTgWmiEventThread = class(TTgThread)
private
bInit_,
bSync_: Boolean;
sWMIClass_: String;
ovEvent_: OleVariant;
WMIEventKind_: TWMIEventKind;
WbemLocator_: TSWbemLocator;
WbemEvent_: ISWbemEventSource;
evWMINotify_: TWMINotifyEvent;
procedure DoWMIEvent;
protected
procedure Execute; override;
public
Constructor Create(const sWMIClass: String; bSync: Boolean = true);
Destructor Destroy; override;
property OnWMINotify: TWMINotifyEvent write evWMINotify_;
end;
type
T2DimStrArray = array of array of string;
function WMI_GetPropertyString(WbemProperty: ISWbemProperty): String;
function WMI_GetSingleInstance(const sArg, sProp: String; var sResult: String): Boolean;
function WMI_GetSingleInstanceProperty(const sArg, sProp: String; var sResult: String): Boolean;
function WMI_GetInformationEx(const sCom, sNameSapce, sUser, sPass, sArg: String;
var wmiResults: T2DimStrArray; var nInstances: Integer): Boolean;
function WMI_GetPropertyIndex(wmiResults: T2DimStrArray; const sProp: String): Integer;
function WMI_GetPropertyData(wmiResults: T2DimStrArray; const sProp: String; nLine: Integer = 0): String;
function WMI_ConvWMIDateToDateTime(const sWmiDate: String; var nUTCOffset: Integer; pbResult: PBoolean = nil): TDateTime;
function WMI_GetTableInfo(const sTable, sField: String; pbResult: PBoolean = nil): String;
function WMI_GetOSInfo(const sField: String): String;
function WMI_GetOSInstallDateTime: TDateTime;
function WMI_GetCpuInfo: String;
function WMI_GetBaseboardInfo: String;
function WMI_GetMotherboardInfo: String;
function WMI_GetBiosInfo(const sField: String): String;
function WMI_GetBiosReleaseDateTime: TDateTime;
function WMI_GetBiosVersion: String;
function WMI_GetVideoController: String;
function WMI_GetNetworkTotalTraffic: LONGLONG;
function WMI_GetMemory: LONGLONG;
function WMI_GetMonitor: String;
implementation
uses
Tocsg.Exception, Tocsg.Hash, Tocsg.Service, Tocsg.Safe, Tocsg.Trace,
Winapi.ActiveX, System.Win.ComObj, System.Variants, Tocsg.Strings;
var
// "지원하지 않음"의 경우 재시도 하지 않도록 체크, 처리
_Chk_GetOSInfo: Boolean = true;
_Chk_GetOSInstallDateTime: Boolean = true;
_Chk_GetCpuInfo: Boolean = true;
_Chk_GetBaseboardInfo: Boolean = true;
_Chk_GetMotherboardInfo: Boolean = true;
_Chk_GetBiosInfo: Boolean = true;
_Chk_GetBiosReleaseDateTime: Boolean = true;
_Chk_GetBiosVersion: Boolean = true;
_Chk_GetVideoController: Boolean = true;
_Chk_GetNetworkTotalTraffic: Boolean = true;
_Chk_GetMemory: Boolean = true;
_Chk_GetMonitor: Boolean = true;
_Chk_WMI_GetMonitor: Boolean = true;
{ TTgWmiEventThread }
Constructor TTgWmiEventThread.Create(const sWMIClass: String; bSync: Boolean = true);
begin
Inherited Create;
bSync_ := bSync;
sWMIClass_ := QuotedStr(sWMIClass);
WbemLocator_ := nil;
ovEvent_ := 0;
WbemEvent_ := nil;
evWMINotify_ := nil;
end;
Destructor TTgWmiEventThread.Destroy;
begin
evWMINotify_ := nil;
FreeAndNil(WbemLocator_);
Inherited;
end;
procedure TTgWmiEventThread.DoWMIEvent;
begin
if Assigned(evWMINotify_) then
evWMINotify_(Self, WMIEventKind_, ovEvent_);
end;
procedure TTgWmiEventThread.Execute;
var
nReTryCnt: Integer;
function InitWMI: Boolean;
var
WbemServices: ISWbemServices;
sQuery: String;
begin
Result := false;
nLastError_ := 0;
// 알수없는 이유로... Create()에서 생성 안될거 생각해서 이렇게 변경
if WbemLocator_ = nil then
try
WbemLocator_ := TSWbemLocator.Create(nil);
WbemServices := WbemLocator_.ConnectServer('', WMI_ROOT_OBJECT, '', '', '', '', 0, nil)
except
WbemLocator_ := nil;
exit;
end;
try
if WbemServices = nil then
begin
nLastError_ := 1;
_Trace('WMI.ConnectServer() .. Fail, Error = %d', [GetLastError]);
exit;
end;
sQuery := Format('SELECT * FROM __InstanceOperationEvent WITHIN 1 ' +
'WHERE TargetInstance ISA %s', [sWMIClass_]);
WbemEvent_ := WbemServices.ExecNotificationQuery(sQuery,
'WQL',
wbemFlagForwardOnly or wbemFlagReturnImmediately,
nil);
if WbemEvent_ = nil then
begin
nLastError_ := 2;
_Trace('WMISvc.ExecNotificationQuery() .. Fail, Error = %d', [GetLastError]);
exit;
end;
Result := true;
except
on e: Exception do
begin
nLastError_ := 3;
exit;
end;
end;
end;
begin
nReTryCnt := 0;
CoInitialize(nil);
// 운영체제가 잠금 상태일 경우 실패할 수 있다. 성공할때까지 시도..
// 위 처럼 예외처리로 서비스 재실행을 이렇게 변경해봄
while not Terminated and not bWorkStop_ and not InitWMI do
begin
Inc(nReTryCnt);
if nRetryCnt > 30 then
begin
// 10분동안 시도해서 안되면 서비스 재시작 하도록 수정
nReTryCnt := 0;
_Trace('InitWMI() .. try 10 minute .. Class="%s"', [sWMIClass_]);
ServiceStop(WMI_SERVICE_NAME);
Sleep(30000);
ServiceStart(WMI_SERVICE_NAME);
Sleep(10000);
end;
Sleep(10000);
end;
// if InitWMI then
while not Terminated and not bWorkStop_ and Assigned(WbemEvent_) do
begin
try
ovEvent_ := WbemEvent_.NextEvent(5000{WBEM_INFINITE});
case ConvStrToHash(ovEvent_.Path_.class) of
WMI_EVENT_CREATE : WMIEventKind_ := wkCreate; // __InstanceCreationEvent
WMI_EVENT_MODIFY : WMIEventKind_ := wkModify; // __InstanceModificationEvent
WMI_EVENT_DELETE : WMIEventKind_ := wkDelete; // __InstanceDeletionEvent
else continue;
end;
if bSync_ then
Synchronize(DoWMIEvent)
else
DoWMIEvent;
except
// 이거 예외 찍게 하면 엄청 찍혀서.. 일단 막자
// on e: Exception do
// ESunkException.TraceException(Self, e);
end;
end;
CoUninitialize;
end;
{ Other }
function WMI_GetPropertyString(WbemProperty: ISWbemProperty): String;
var
i: Integer;
begin
Result := '';
if VarIsNull(WbemProperty.Get_Value) then
Result := 'NULL'
else begin
case WbemProperty.CIMType of
wbemCimtypeSint8,
wbemCimtypeUint8,
wbemCimtypeSint16,
wbemCimtypeUint16,
wbemCimtypeSint32,
wbemCimtypeUint32,
wbemCimtypeSint64 :
begin
if VarIsArray(WbemProperty.Get_Value) then
begin
for i := 0 to VarArrayHighBound(WbemProperty.Get_Value, 1) do
begin
if i > 0 then
Result := Result + '|';
Result := Result + IntToStr(WbemProperty.Get_Value[i]);
end;
end else
Result := IntToStr(WbemProperty.Get_Value);
end;
wbemCimtypeReal32, wbemCimtypeReal64 : result := FloatToStr (WbemProperty.Get_Value);
wbemCimtypeBoolean : if WbemProperty.Get_Value then result := 'True' else result := 'False';
wbemCimtypeString,
wbemCimtypeUint64 :
begin
if VarIsArray(WbemProperty.Get_Value) then
begin
for i := 0 to VarArrayHighBound(WbemProperty.Get_Value, 1) do
begin
if i > 0 then
Result := Result + '|';
Result := Result + WbemProperty.Get_Value [i];
end;
end else
Result := WbemProperty.Get_Value;
end;
wbemCimtypeDatetime : Result := WbemProperty.Get_Value;
wbemCimtypeReference : Result := WbemProperty.Get_Value;
wbemCimtypeChar16 : Result := '<16-bit character>';
wbemCimtypeObject : Result := '<CIM Object>';
end ;
end;
end;
function WMI_GetSingleInstance(const sArg, sProp: String; var sResult: String): Boolean;
var
WbemLocator: TSWbemLocator;
WbemServices: ISWbemServices;
WbemObject: ISWbemObject;
WbemProperty: ISWbemProperty;
begin
Result := false;
sResult := '';
Guard(WbemLocator, TSWbemLocator.Create(nil));
try
WbemServices := WbemLocator.ConnectServer('', WMI_ROOT_OBJECT, '', '',
'', '', 0, nil);
WbemObject := WbemServices.Get(sArg, 0, nil);
WbemProperty := WbemObject.Properties_.Item(sProp, 0);
if WbemProperty.Name <> sProp then
exit;
sResult := WMI_GetPropertyString(WbemProperty);
if sResult <> 'NULL' then
Result := true;
except
on E: Exception do
ETgException.TraceException(E, 'Fail .. WMI_GetSingleInstance()');
end;
end;
function WMI_GetSingleInstanceProperty(const sArg, sProp: String; var sResult: String): Boolean;
var
WbemLocator: TSWbemLocator;
WbemServices: ISWbemServices;
WbemObjectSet: ISWbemObjectSet;
WbemObject: ISWbemObject;
WbemProperty: ISWbemProperty;
ovVar: OleVariant;
dwValue: DWORD;
enum: IEnumVariant;
sInfo: String;
begin
Result := false;
sResult := '';
VarClear(ovVar);
Guard(WbemLocator, TSWbemLocator.Create(nil));
try
WbemServices := WbemLocator.ConnectServer('', WMI_ROOT_OBJECT, '', '',
'', '', 0, nil);
WbemObjectSet := WbemServices.ExecQuery(sArg, 'WQL',
wbemFlagReturnImmediately, nil);
enum := (WbemObjectSet._NewEnum) as IEnumVariant;
while (enum.Next(1, ovVar, dwValue) = S_OK) do
begin
WbemObject := IUnknown(ovVar) as SWBemObject;
WbemProperty := WbemObject.Properties_.Item(sProp, 0);
if WbemProperty.Name = sProp then
begin
sInfo := WMI_GetPropertyString(WbemProperty);
if sInfo <> 'NULL' then
Result := true;
SumString(sResult, sInfo, ', ');
end;
VarClear(ovVar);
end;
except
on E: Exception do
begin
ETgException.TraceException(E, 'Fail .. WMI_GetSingleInstanceProperty()');
VarClear(ovVar);
end;
end;
end;
function WMI_GetInformationEx(const sCom, sNameSapce, sUser, sPass, sArg: String;
var wmiResults: T2DimStrArray; var nInstances: Integer): Boolean;
var
WbemLocator: TSWbemLocator;
WbemServices: ISWbemServices;
WbemObjectSet: ISWbemObjectSet;
WbemObject: ISWbemObject;
WbemPropertySet: ISWbemPropertySet;
WbemProperty: ISWbemProperty;
propEnum, Enum: IEnumVariant;
ovVar1, ovVar2: OleVariant;
lwValue: DWORD;
sValue: String;
nInst, nRow, nCnt: Integer;
bDimmed: Boolean;
begin
Result := true;
nInstances := 0;
SetLength(wmiResults, 0, 0);
bDimmed := false;
VarClear(ovVar1);
VarClear(ovVar2);
Guard(WbemLocator, TSWbemLocator.Create(nil));
try
WbemServices := WbemLocator.ConnectServer(sCom,
sNameSapce,
sUser,
sPass,
'', '', 0, nil);
if Pos('SELECT', sArg) = 1 then
WbemObjectSet := WbemServices.ExecQuery(sArg, 'WQL', wbemFlagReturnImmediately, nil)
else
WbemObjectSet := WbemServices.InstancesOf(sArg, wbemFlagReturnImmediately or
wbemQueryFlagShallow, nil);
nInstances := WbemObjectSet.Count;
if nInstances = 0 then
exit;
// Replicate VBScript's "for each" construct
Enum := (WbemObjectSet._NewEnum) as IEnumVariant;
nInst := 0;
while(Enum.Next (1, ovVar1, lwValue) = S_OK) do
begin
WbemObject := IUnknown(ovVar1) as SWBemObject;
WbemPropertySet := WbemObject.Properties_;
nCnt := WbemPropertySet.Count;
if not bDimmed then
begin
SetLength(wmiResults, nInstances + 1, nCnt + 1);
wmiResults[0, 0] := 'Instance';
bDimmed := true;
end ;
propEnum := (WbemPropertySet._NewEnum) as IEnumVariant;
Inc(nInst);
nRow := 1;
wmiResults[nInst, 0] := IntToStr(nInst);
// Replicate VBScript's "for each" construct
while (propEnum.Next(1, ovVar2, lwValue) = S_OK) do
begin
WbemProperty := IUnknown(ovVar2) as SWBemProperty;
sValue := WMI_GetPropertyString(WbemProperty);
if nInst = 1 then wmiResults[0, nRow] := WbemProperty.Name;
wmiResults[nInst, nRow] := sValue;
Inc(nRow);
VarClear(ovVar2); // whomp them mem leaks
end;
end;
VarClear (ovVar1); // whomp them mem leaks
except
on E: Exception do
begin
ETgException.TraceException(E, 'Fail .. WMI_GetInformationEx()');
VarClear (ovVar1);
VarClear (ovVar2);
Result := false;
end;
end;
end;
function WMI_GetPropertyIndex(wmiResults: T2DimStrArray; const sProp: String): Integer;
var
i: Integer;
begin
Result := 0;
for i := 1 to High(wmiResults[0]) do
begin
if wmiResults[0, i] = sProp then
begin
Result := i;
exit;
end;
end;
end;
function WMI_GetPropertyData(wmiResults: T2DimStrArray; const sProp: String; nLine: Integer = 0): String;
var
i: Integer;
begin
Result := '';
if (Length(wmiResults)-1) < (nLine+1) then
exit;
i := WMI_GetPropertyIndex(wmiResults, sProp);
if i > 0 then Result := wmiResults[nLine+1, i];
end;
function WMI_ConvWMIDateToDateTime(const sWmiDate: String; var nUTCOffset: Integer; pbResult: PBoolean = nil): TDateTime;
Const
// 지정된 고정길이 25
LEN_WMI_DATETIME = 25;
// yyyymmddhhnnss.zzzzzzsUUU +60 means 60 mins of UTC time
// 20030709091030.686000+060
// 1234567890123456789012345
var
yy, mm, dd,
hh, nn, ss, zz: Integer;
dt: TDateTime;
function GetNum(nOffset, nLen: integer): Integer;
var
n: Integer;
begin
Val(Copy(sWmiDate, nOffset, nLen), Result, n);
end;
begin
if pbResult <> nil then
pbResult^ := true;
Result := ERROR_SUCCESS;
nUTCOffset := 0;
if length(sWmiDate) <> LEN_WMI_DATETIME then
exit;
yy := GetNum(1, 4);
mm := GetNum(5, 2);
if (mm = 0) or (mm > 12) then
exit;
dd := GetNum(7, 2);
if (dd = 0) or (dd > 31) then
exit;
if not TryEncodeDate(yy, mm, dd, result) then // D6 and later
begin
Result := -1;
if pbResult <> nil then
pbResult^ := false;
exit;
end;
hh := GetNum(9, 2);
nn := GetNum(11, 2);
ss := GetNum(13, 2);
zz := 0 ;
if Length(sWmiDate) >= 18 then
zz := GetNum(16, 3);
if not TryEncodeTime(hh, nn, ss, zz, dt) then
exit; // D6 and later
Result := Result + dt;
nUTCOffset := GetNum(22, 4);
end;
function WMI_GetTableInfo(const sTable, sField: String; pbResult: PBoolean = nil): String;
begin
try
if not WMI_GetSingleInstanceProperty
(
Format('SELECT %s FROM %s', [sField, sTable]), sField, Result
) then
begin
if pbResult <> nil then
pbResult^ := false;
TTgTrace.T('WMI_GetTableInfo .. Fail!!, Table = %s, Field = %s', [sTable, sField], 1);
end else begin
if pbResult <> nil then
pbResult^ := true;
end;
except
on E: Exception do
begin
if pbResult <> nil then
pbResult^ := false;
ETgException.TraceException(E, Format('Fail .. WMI_GetTableInfo() .. Table=%s, Field=%s', [sTable, sField]), 1);
end;
end;
end;
function WMI_GetOSInfo(const sField: String): String;
begin
if _Chk_GetOSInfo then
Result := WMI_GetTableInfo('Win32_OperatingSystem', sField, @_Chk_GetOSInfo)
else Result := 'Unsupported OSInfo.';
end;
function WMI_GetOSInstallDateTime: TDateTime;
var
sWMIDate: String;
nUTCOffset: Integer;
begin
Result := 0;
if _Chk_GetOSInstallDateTime then
begin
if WMI_GetSingleInstanceProperty('SELECT InstallDate FROM Win32_Registry', 'InstallDate', sWMIDate) then
begin
Result := WMI_ConvWMIDateToDateTime(sWMIDate, nUTCOffset);
end else begin
_Chk_GetOSInstallDateTime := false;
TTgTrace.T('WMI_GetOSInstallDateTime .. Fail!!', 1);
end;
end;
end;
function WMI_GetCpuInfo: String;
begin
if _Chk_GetCpuInfo then
Result := WMI_GetTableInfo('Win32_Processor', 'Name', @_Chk_GetCpuInfo)
else Result := 'Unsupported CpuInfo.';
end;
function WMI_GetBaseboardInfo: String;
begin
if _Chk_GetBaseboardInfo then
Result := WMI_GetTableInfo('Win32_BaseBoard', 'Product', @_Chk_GetBaseboardInfo)
else Result := 'Unsupported BaseboardInfo.';
end;
function WMI_GetMotherboardInfo: String;
begin
if _Chk_GetMotherboardInfo then
Result := WMI_GetTableInfo('Win32_MotherboardDevice', 'Name', @_Chk_GetMotherboardInfo)
else Result := 'Unsupported MotherboardInfo.';
end;
function WMI_GetBiosInfo(const sField: String): String;
begin
if _Chk_GetBiosInfo then
Result := WMI_GetTableInfo('Win32_BIOS', sField, @_Chk_GetBiosInfo)
else Result := 'Unsupported BiosInfo.';
end;
function WMI_GetBiosReleaseDateTime: TDateTime;
var
nUTCOffset: Integer;
begin
if _Chk_GetBiosReleaseDateTime then
Result := WMI_ConvWMIDateToDateTime(WMI_GetBiosInfo('ReleaseDate'), nUTCOffset, @_Chk_GetBiosReleaseDateTime)
else Result := 0;
end;
function WMI_GetBiosVersion: String;
var
nVerCnt: Integer;
wmiResults: T2DimStrArray;
begin
if _Chk_GetBiosVersion then
begin
Result := '';
if WMI_GetInformationEx('', WMI_ROOT_OBJECT, '', '', 'Win32_BIOS', wmiResults, nVerCnt) then
begin
if nVerCnt > 0 then
Result := Format('%s v%s.%s', [WMI_GetPropertyData(wmiResults, 'SMBIOSBIOSVersion'),
WMI_GetPropertyData(wmiResults, 'SMBIOSMajorVersion'),
WMI_GetPropertyData(wmiResults, 'SMBIOSMinorVersion')]);
end else _Chk_GetBiosVersion := false;
end else Result := 'Unsupported BiosVersion.';
end;
function WMI_GetVideoController: String;
begin
if _Chk_GetVideoController then
Result := WMI_GetTableInfo('Win32_VideoController', 'Name', @_Chk_GetVideoController)
else Result := 'Unsupported VideoController.';
end;
function WMI_GetNetworkTotalTraffic: LONGLONG;
var
nVerCnt: Integer;
wmiResults: T2DimStrArray;
i: Integer;
begin
Result := -1;
if _Chk_GetNetworkTotalTraffic then
begin
if WMI_GetInformationEx('', WMI_ROOT_OBJECT, '', '', 'Win32_PerfFormattedData_Tcpip_NetworkInterface', wmiResults, nVerCnt) then
begin
Result := 0;
for i := 0 to nVerCnt - 1 do
Inc(Result, StrToInt64Def(WMI_GetPropertyData(wmiResults, 'BytesTotalPersec', i), 0));
end else _Chk_GetNetworkTotalTraffic := false;
end;
end;
function WMI_GetMemory: LONGLONG;
begin
if _Chk_GetMemory then
Result := StrToInt64Def(WMI_GetTableInfo('Win32_ComputerSystem', 'TotalPhysicalMemory', @_Chk_GetMemory), 0)
else Result := -1;
end;
function VariantByteArrayToString(V: Variant): string;
var
i, LowIdx, HighIdx: Integer;
Ch: Integer;
begin
Result := '';
if VarIsArray(V) then
begin
LowIdx := VarArrayLowBound(V, 1);
HighIdx := VarArrayHighBound(V, 1);
for i := LowIdx to HighIdx do
begin
try
// Null이나 비정상 값 방지
Ch := VarAsType(VarArrayGet(V, [i]), varByte);
if Ch > 0 then
Result := Result + Chr(Ch);
except
// 오류 발생 시 무시
end;
end;
end;
end;
function WMI_GetMonitor: String;
var
SWbemLocator, SWbemServices, SWbemObjectSet, Item: OLEVariant;
Enum: IEnumVariant;
Value: Cardinal;
V: Variant;
Manufacturer, ProductCode: string;
begin
if _Chk_GetMonitor then
begin
Result := '';
try
SWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
SWbemServices := SWbemLocator.ConnectServer('.', 'root\WMI');
SWbemObjectSet := SWbemServices.ExecQuery('SELECT * FROM WmiMonitorID', 'WQL', 0);
Enum := IEnumVariant(IUnknown(SWbemObjectSet._NewEnum));
while Enum.Next(1, Item, Value) = 0 do
begin
// 제조사와 제품 코드는 byte 배열로 리턴됨
ProductCode := VariantByteArrayToString(Item.Properties_.Item('UserFriendlyName').Value);
if ProductCode <> '' then
begin
SumString(Result, ProductCode, ', ');
end else begin
Manufacturer := VariantByteArrayToString(Item.Properties_.Item('ManufacturerName').Value);
ProductCode := VariantByteArrayToString(Item.Properties_.Item('ProductCodeID').Value);
// SumString(Result, Format('Manufacturer: %s, Product Code: %s', [Manufacturer, ProductCode]), ', ');
SumString(Result, Manufacturer + ' ' + ProductCode, ', ');
end;
Item := Unassigned;
end;
except
on E: Exception do
begin
ETgException.TraceException(E, 'Fail .. WMI_GetMonitor()');
_Chk_GetMonitor := false;
end;
end;
end else Result := 'Unsupported MonitorInfo.';
end;
end.