611 lines
17 KiB
Plaintext
611 lines
17 KiB
Plaintext
{*******************************************************}
|
|
{ }
|
|
{ Tocsg.Registry }
|
|
{ }
|
|
{ Copyright (C) 2022 kkuzil }
|
|
{ }
|
|
{*******************************************************}
|
|
|
|
unit Tocsg.Registry;
|
|
|
|
interface
|
|
|
|
uses
|
|
WinApi.Windows, System.Classes, System.Win.Registry, System.SysUtils,
|
|
System.Generics.Collections, Tocsg.Trace;
|
|
|
|
const
|
|
REG_RUN = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Run\';
|
|
REG_PROFILE = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\';
|
|
|
|
type
|
|
TRkInfo = record
|
|
sKName: string;
|
|
ftLastWriteTime: TFileTime;
|
|
end;
|
|
TRkInfoList = TList<TRkInfo>;
|
|
|
|
function SetRegValueString(K: HKEY; const sKey, sValueName, sValue: String; bCanCreate: Boolean = false): Boolean;
|
|
function SetRegValueStringEx(K: HKEY; const sKey, sValueName, sValue: String; bCanCreate: Boolean = false): Boolean;
|
|
function SetRegValueInteger(K: HKEY; const sKey, sValueName: String; nValue: Integer; bCanCreate: Boolean = false): Boolean;
|
|
|
|
function ExistsKey(K: HKEY; const sKey: String): Boolean;
|
|
function CountRegKeyValue(K: HKEY; const sKey: String): Integer;
|
|
function GetRegValueAsString(K: HKEY; const sKey, sValue: String; bCreate: Boolean = false): String;
|
|
function GetRegValueAsInteger(K: HKEY; const sKey, sValue: String; nDefVal: Integer = 0): Integer;
|
|
|
|
function GetRegRecentUserSid: String;
|
|
|
|
function DelRegKey(K: HKEY; const sKey: String): Boolean;
|
|
function DelRegValue(K: HKEY; const sKey, sValueName: String): Boolean;
|
|
|
|
function ExtRegSubKeyToStrings(K: HKEY; sKey: String; aStrings: TStrings): Boolean;
|
|
|
|
function ExistsRunAppByHLM(const AppName: String; aRootKey: HKEY = HKEY_LOCAL_MACHINE): Boolean;
|
|
function AddRunAppByHLM(const AppName, sValue: String; aRootKey: HKEY = HKEY_LOCAL_MACHINE): Boolean;
|
|
function DeleteRunAppByHLM(const AppName: String; aRootKey: HKEY = HKEY_LOCAL_MACHINE): Boolean;
|
|
|
|
function GetDomainUserNameFromReg: String;
|
|
function GetUserNameFromReg(sRegKey: String = ''): String;
|
|
|
|
procedure CopyRegKey(K: HKEY; sSrcKey, sDestKey: String);
|
|
|
|
function ExtrSubKeySortList(K: HKEY; sKey: String; aRkInfoList: TRkInfoList): Integer;
|
|
|
|
type
|
|
// 기본 TRegistry 클래스는 REG_MULTI_SZ 타입까지 분석하지 못한다.
|
|
// 그래서 헬퍼 클래스로 지원되도록 추가함 14_1006 15:08:25 kku
|
|
TRegDataTypeEx = (rdxUnknown, rdxString, rdxExpandString, rdxInteger, rdxBinary, rdxMultiString{추가});
|
|
|
|
TRegDataInfoEx = record
|
|
RegData: TRegDataTypeEx;
|
|
DataSize: Integer;
|
|
end;
|
|
|
|
TRegistryHelper = class helper for TRegistry
|
|
private
|
|
function GetDataEx(const Name: string; Buffer: Pointer; BufSize: Integer; var RegData: TRegDataTypeEx): Integer; // 지금까지 외부에서 사용할일이 없어서 숨김 14_1006 15:09:49 kku
|
|
function GetDataInfoEx(const ValueName: string; var Value: TRegDataInfoEx): Boolean; // 지금까지 외부에서 사용할일이 없어서 숨김 14_1006 15:09:49 kku
|
|
public
|
|
function GetDataAsStringEx(const ValueName: string; PrefixType: Boolean = false): string;
|
|
function GetDataTypeEx(const ValueName: string): TRegDataTypeEx; // 외부에서는 이걸로 타입 확인하고, GetDataAsStringEx 이걸로 데이터 가져오는걸로 사용한다 14_1006 15:09:20 kku
|
|
procedure WriteMultiString(const ValueName: String; aMultiStrList: TStrings);
|
|
end;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
System.RTLConsts, Tocsg.Safe, Tocsg.Exception, Tocsg.DateTime, Tocsg.Process,
|
|
System.Generics.Defaults;
|
|
|
|
function CompareByLastWriteTime(const Left, Right: TRkInfo): Integer;
|
|
begin
|
|
Result := CompareFileTime(Right.ftLastWriteTime, Left.ftLastWriteTime);
|
|
end;
|
|
|
|
function SetRegValueString(K: HKEY; const sKey, sValueName, sValue: String; bCanCreate: Boolean = false): Boolean;
|
|
var
|
|
Reg: TRegistry;
|
|
begin
|
|
Result := false;
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := K;
|
|
if Reg.OpenKey(sKey, bCanCreate) then
|
|
if bCanCreate or Reg.ValueExists(sValueName) then
|
|
begin
|
|
try
|
|
Reg.WriteString(sValueName, sValue);
|
|
Result := true;
|
|
except
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function SetRegValueStringEx(K: HKEY; const sKey, sValueName, sValue: String; bCanCreate: Boolean = false): Boolean;
|
|
var
|
|
Reg: TRegistry;
|
|
begin
|
|
Result := false;
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := K;
|
|
if Reg.OpenKey(sKey, bCanCreate) then
|
|
if bCanCreate or Reg.ValueExists(sValueName) then
|
|
begin
|
|
try
|
|
Reg.WriteExpandString(sValueName, sValue);
|
|
Result := true;
|
|
except
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function SetRegValueInteger(K: HKEY; const sKey, sValueName: String; nValue: Integer; bCanCreate: Boolean = false): Boolean;
|
|
var
|
|
Reg: TRegistry;
|
|
begin
|
|
Result := false;
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := K;
|
|
if Reg.OpenKey(sKey, bCanCreate) then
|
|
if bCanCreate or Reg.ValueExists(sValueName) then
|
|
begin
|
|
try
|
|
Reg.WriteInteger(sValueName, nValue);
|
|
Result := true;
|
|
except
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function ExistsKey(K: HKEY; const sKey: String): Boolean;
|
|
var
|
|
Reg: TRegistry;
|
|
begin
|
|
Result := false;
|
|
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := K;
|
|
try
|
|
Result := Reg.KeyExists(sKey);
|
|
except
|
|
//
|
|
end;
|
|
end;
|
|
|
|
function CountRegKeyValue(K: HKEY; const sKey: String): Integer;
|
|
var
|
|
Reg: TRegistry;
|
|
StrList: TStringList;
|
|
begin
|
|
Result := 0;
|
|
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := K;
|
|
try
|
|
if Reg.OpenKey(sKey, false) then
|
|
begin
|
|
Guard(StrList, TStringList.Create);
|
|
Reg.GetValueNames(StrList);
|
|
Result := StrList.Count;
|
|
end;
|
|
except
|
|
//
|
|
end;
|
|
end;
|
|
|
|
function GetRegValueAsString(K: HKEY; const sKey, sValue: String; bCreate: Boolean = false): String;
|
|
var
|
|
Reg: TRegistry;
|
|
begin
|
|
Result := '';
|
|
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := K;
|
|
try
|
|
if Reg.OpenKey(sKey, bCreate) and Reg.ValueExists(sValue) then
|
|
Result := Reg.ReadString(sValue);
|
|
except
|
|
//
|
|
end;
|
|
end;
|
|
|
|
function GetRegValueAsInteger(K: HKEY; const sKey, sValue: String; nDefVal: Integer = 0): Integer;
|
|
var
|
|
Reg: TRegistry;
|
|
begin
|
|
Result := nDefVal;
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := K;
|
|
try
|
|
if Reg.OpenKey(sKey, false) and Reg.ValueExists(sValue) then
|
|
begin
|
|
Result := Reg.ReadInteger(sValue);
|
|
end;
|
|
except
|
|
//
|
|
end;
|
|
end;
|
|
|
|
function GetRegRecentUserSid: String;
|
|
var
|
|
Reg, RegCheck: TRegistry;
|
|
StrList: TStringList;
|
|
i, nHigh, nLow: Integer;
|
|
sTemp: String;
|
|
dtRecent, dtCheck: TDateTime;
|
|
begin
|
|
Result := '';
|
|
try
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := HKEY_LOCAL_MACHINE;
|
|
if not Reg.OpenKeyReadOnly(REG_PROFILE) then
|
|
exit;
|
|
Guard(StrList, TStringList.Create);
|
|
|
|
dtRecent := 0;
|
|
Reg.GetKeyNames(StrList);
|
|
Reg.CloseKey;
|
|
|
|
Guard(RegCheck, TRegistry.Create);
|
|
RegCheck.RootKey := HKEY_USERS;
|
|
for i := 0 to StrList.Count - 1 do
|
|
begin
|
|
if (Length(StrList[i]) > 20) and
|
|
RegCheck.OpenKeyReadOnly(StrList[i]) then // 키가 존재하는거 대상으로 함 (로그온된거) 22_0620 16:50:47 kku
|
|
begin
|
|
RegCheck.CloseKey;
|
|
|
|
// LocalProfileLoadTimeHigh, LocalProfileLoadTimeLow가 0인 경우가 있다... 22_0620 16:45:02 kku
|
|
nHigh := GetRegValueAsInteger(HKEY_LOCAL_MACHINE, REG_PROFILE + StrList[i], 'LocalProfileLoadTimeHigh');
|
|
nLow := GetRegValueAsInteger(HKEY_LOCAL_MACHINE, REG_PROFILE + StrList[i], 'LocalProfileLoadTimeLow');
|
|
sTemp := IntToHex(nHigh) + IntToHex(nLow);
|
|
// UTC-0
|
|
dtCheck := ConvTimestampToDateTime(LONGLONG(StrToInt64Def('$' + sTemp, 0)));
|
|
|
|
if (dtRecent = 0) or (dtRecent < dtCheck) then
|
|
begin
|
|
dtRecent := dtCheck;
|
|
Result := StrList[i];
|
|
end;
|
|
end;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(E, 'Fail .. GetRegRecentUserSid()');
|
|
end;
|
|
end;
|
|
|
|
function DelRegKey(K: HKEY; const sKey: String): Boolean;
|
|
var
|
|
Reg: TRegistry;
|
|
begin
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := K;
|
|
Result := Reg.DeleteKey(sKey);
|
|
end;
|
|
|
|
function DelRegValue(K: HKEY; const sKey, sValueName: String): Boolean;
|
|
var
|
|
Reg: TRegistry;
|
|
begin
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := K;
|
|
Result := false;
|
|
if Reg.OpenKey(sKey, false) then
|
|
Result := Reg.DeleteValue(sValueName);
|
|
end;
|
|
|
|
function ExtRegSubKeyToStrings(K: HKEY; sKey: String; aStrings: TStrings): Boolean;
|
|
var
|
|
Reg: TRegistry;
|
|
SubList: TStringList;
|
|
i: Integer;
|
|
begin
|
|
Result := false;
|
|
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := K;
|
|
|
|
if Reg.KeyExists(sKey) then
|
|
if Reg.OpenKey(sKey, false) then
|
|
begin
|
|
sKey := IncludeTrailingPathDelimiter(sKey);
|
|
Guard(SubList, TStringList.Create);
|
|
Reg.GetKeyNames(SubList);
|
|
for i := 0 to SubList.Count - 1 do
|
|
begin
|
|
// 값까지 체크 해준다. notepad++ 같은 경우 설정값을 여기에 남겨 놓는경우가 있음 22_0614 16:20:37 kku
|
|
if GetRegValueAsString(K, sKey + SubList[i], 'DisplayName') <> '' then
|
|
aStrings.Add(sKey + SubList[i]);
|
|
end;
|
|
Result := true;
|
|
end;
|
|
end;
|
|
|
|
function ExistsRunAppByHLM(const AppName: String; aRootKey: HKEY = HKEY_LOCAL_MACHINE): Boolean;
|
|
var
|
|
Reg: TRegistry;
|
|
begin
|
|
Result := false;
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := aRootKey;
|
|
if Reg.OpenKey(REG_RUN, false) then
|
|
Result := Reg.ValueExists(AppName);
|
|
end;
|
|
|
|
function AddRunAppByHLM(const AppName, sValue: String; aRootKey: HKEY = HKEY_LOCAL_MACHINE): Boolean;
|
|
var
|
|
Reg: TRegistry;
|
|
begin
|
|
Result := false;
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := aRootKey;
|
|
if Reg.OpenKey(REG_RUN, false) then
|
|
begin
|
|
try
|
|
Reg.WriteString(AppName, sValue);
|
|
Result := true;
|
|
except
|
|
// reg write 오류?
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function DeleteRunAppByHLM(const AppName: String; aRootKey: HKEY = HKEY_LOCAL_MACHINE): Boolean;
|
|
begin
|
|
Result := DelRegValue(aRootKey, REG_RUN, AppName);
|
|
end;
|
|
|
|
{ TRegistryHelper }
|
|
|
|
function DataTypeToRegDataEx(Value: Integer): TRegDataTypeEx;
|
|
begin
|
|
case Value of
|
|
REG_SZ : Result := rdxString;
|
|
REG_EXPAND_SZ : Result := rdxExpandString;
|
|
REG_MULTI_SZ : Result := rdxMultiString;
|
|
REG_DWORD : Result := rdxInteger;
|
|
REG_BINARY : Result := rdxBinary;
|
|
else Result := rdxUnknown;
|
|
end;
|
|
end;
|
|
|
|
function BinaryToHexString(const BinaryData: array of Byte; const PrefixStr: string): string;
|
|
var
|
|
DataSize, I, Offset: Integer;
|
|
HexData: string;
|
|
PResult: PChar;
|
|
begin
|
|
OffSet := 0;
|
|
if PrefixStr <> '' then
|
|
begin
|
|
Result := PrefixStr;
|
|
Inc(Offset, Length(PrefixStr));
|
|
end;
|
|
DataSize := Length(BinaryData);
|
|
|
|
SetLength(Result, Offset + (DataSize*3) - 1); // less one for last ','
|
|
PResult := PChar(Result); // Use a char pointer to reduce string overhead
|
|
for I := 0 to DataSize - 1 do
|
|
begin
|
|
HexData := IntToHex(BinaryData[I], 2);
|
|
PResult[Offset] := HexData[1];
|
|
PResult[Offset+1] := HexData[2];
|
|
if I < DataSize - 1 then
|
|
PResult[Offset+2] := ',';
|
|
Inc(Offset, 3);
|
|
end;
|
|
end;
|
|
|
|
function TRegistryHelper.GetDataInfoEx(const ValueName: string; var Value: TRegDataInfoEx): Boolean;
|
|
var
|
|
DataType: Integer;
|
|
begin
|
|
FillChar(Value, SizeOf(TRegDataInfo), 0);
|
|
Result := CheckResult(RegQueryValueEx(CurrentKey, PChar(ValueName), nil, @DataType, nil,
|
|
@Value.DataSize));
|
|
Value.RegData := DataTypeToRegDataEx(DataType);
|
|
end;
|
|
|
|
function TRegistryHelper.GetDataTypeEx(const ValueName: string): TRegDataTypeEx;
|
|
var
|
|
Info: TRegDataInfoEx;
|
|
begin
|
|
if GetDataInfoEx(ValueName, Info) then
|
|
Result := Info.RegData else
|
|
Result := rdxUnknown;
|
|
end;
|
|
|
|
function TRegistryHelper.GetDataEx(const Name: string; Buffer: Pointer;
|
|
BufSize: Integer; var RegData: TRegDataTypeEx): Integer;
|
|
var
|
|
DataType: Integer;
|
|
begin
|
|
DataType := REG_NONE;
|
|
if not CheckResult(RegQueryValueEx(CurrentKey, PChar(Name), nil, @DataType, PByte(Buffer),
|
|
@BufSize)) then
|
|
raise ERegistryException.CreateResFmt(@SRegGetDataFailed, [Name]);
|
|
Result := BufSize;
|
|
RegData := DataTypeToRegDataEx(DataType);
|
|
end;
|
|
|
|
function TRegistryHelper.GetDataAsStringEx(const ValueName: string;
|
|
PrefixType: Boolean = false): string;
|
|
const
|
|
SDWORD_PREFIX = 'dword:';
|
|
SHEX_PREFIX = 'hex:';
|
|
var
|
|
Info: TRegDataInfoEx;
|
|
BinaryBuffer: array of Byte;
|
|
nPos: Integer;
|
|
pBuf: TBytes;
|
|
begin
|
|
Result := '';
|
|
if GetDataInfoEx(ValueName, Info) and (Info.DataSize > 0) then
|
|
begin
|
|
case Info.RegData of
|
|
rdxString, rdxExpandString:
|
|
begin
|
|
SetString(Result, nil, Info.DataSize);
|
|
GetDataEx(ValueName, PChar(Result), Info.DataSize, Info.RegData);
|
|
SetLength(Result, StrLen(PChar(Result)));
|
|
end;
|
|
rdxMultiString :
|
|
begin
|
|
SetLength(pBuf, Info.DataSize);
|
|
GetDataEx(ValueName, pBuf, Info.DataSize, Info.RegData);
|
|
|
|
nPos := 0;
|
|
while nPos < Info.DataSize do
|
|
begin
|
|
Result := Result + PChar(@pBuf[nPos]);
|
|
{$IFDEF UNICODE}
|
|
Inc(nPos, (Length(PChar(@pBuf[nPos])) + 1) * 2);
|
|
{$ELSE}
|
|
Inc(nPos, Length(PChar(@pBuf[nPos])) + 1);
|
|
{$ENDIF}
|
|
if nPos < Info.DataSize then
|
|
Result := Result + #13#10;
|
|
end;
|
|
end;
|
|
rdxInteger:
|
|
begin
|
|
if PrefixType then
|
|
Result := SDWORD_PREFIX+IntToHex(ReadInteger(ValueName), 8)
|
|
else
|
|
Result := IntToStr(ReadInteger(ValueName));
|
|
end;
|
|
rdxBinary, rdxUnknown:
|
|
begin
|
|
SetLength(BinaryBuffer, Info.DataSize);
|
|
ReadBinaryData(ValueName, Pointer(BinaryBuffer)^, Info.DataSize);
|
|
if PrefixType then
|
|
Result := BinaryToHexString(BinaryBuffer, SHEX_PREFIX)
|
|
else
|
|
Result := BinaryToHexString(BinaryBuffer, '');
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TRegistryHelper.WriteMultiString(const ValueName: String; aMultiStrList: TStrings);
|
|
var
|
|
i, nSize: Integer;
|
|
pBuf: TBytes;
|
|
begin
|
|
nSize := 0;
|
|
for i := 0 to aMultiStrList.Count - 1 do
|
|
begin
|
|
{$IFDEF UNICODE}
|
|
Inc(nSize, (Length(aMultiStrList[i]) + 1) * 2);
|
|
{$ELSE}
|
|
Inc(nSize, Length(aMultiStrList[i]) + 1);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
SetLength(pBuf, nSize);
|
|
ZeroMemory(pBuf, nSize);
|
|
|
|
nSize := 0;
|
|
for i := 0 to aMultiStrList.Count - 1 do
|
|
begin
|
|
StrCopy(PChar(@pBuf[nSize]), PChar(aMultiStrList[i]));
|
|
{$IFDEF UNICODE}
|
|
Inc(nSize, (Length(aMultiStrList[i]) + 1) * 2);
|
|
{$ELSE}
|
|
Inc(nSize, Length(aMultiStrList[i]) + 1);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
RegSetValueEx(CurrentKey, PChar(ValueName), 0, REG_MULTI_SZ, @pBuf[0], nSize);
|
|
end;
|
|
|
|
function GetDomainUserNameFromReg: String;
|
|
var
|
|
sRegKey,
|
|
sDomain,
|
|
sUser: String;
|
|
begin
|
|
Result := '';
|
|
try
|
|
sRegKey := GetRegRecentUserSid;
|
|
if sRegKey = '' then
|
|
exit;
|
|
|
|
sDomain := GetRegValueAsString(HKEY_USERS, sRegKey + '\Volatile Environment', 'USERDOMAIN');
|
|
sUser := GetRegValueAsString(HKEY_USERS, sRegKey + '\Volatile Environment', 'USERNAME');
|
|
if (sDomain <> '') and (sUser <> '') then
|
|
Result := sDomain + '\' + sUser;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(E, 'Fail .. GetDomainUserNameFromReg()');
|
|
end;
|
|
end;
|
|
|
|
function GetUserNameFromReg(sRegKey: String = ''): String;
|
|
begin
|
|
Result := '';
|
|
try
|
|
if sRegKey = '' then
|
|
sRegKey := GetRegRecentUserSid;
|
|
|
|
if sRegKey = '' then
|
|
exit;
|
|
|
|
Result := GetRegValueAsString(HKEY_LOCAL_MACHINE, REG_PROFILE + sRegKey, 'ProfileImagePath');
|
|
if Result <> '' then
|
|
Result := Trim(ExtractFileName(ExcludeTrailingPathDelimiter(Result)));
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(E, 'Fail .. GetUserNameFromReg()');
|
|
end;
|
|
end;
|
|
|
|
procedure CopyRegKey(K: HKEY; sSrcKey, sDestKey: String);
|
|
var
|
|
Reg: TRegistry;
|
|
begin
|
|
try
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := K;
|
|
Reg.MoveKey(sSrcKey, sDestKey, false);
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(E, 'Fail .. CopyRegKey()');
|
|
end;
|
|
end;
|
|
|
|
function ExtrSubKeySortList(K: HKEY; sKey: String; aRkInfoList: TRkInfoList): Integer;
|
|
var
|
|
Reg: TRegistry;
|
|
SubList: TStringList;
|
|
ftLocal, ftKey: TFileTime;
|
|
RkInfo: TRkInfo;
|
|
i: Integer;
|
|
begin
|
|
Result := 0;
|
|
try
|
|
Guard(Reg, TRegistry.Create);
|
|
Reg.RootKey := HKEY_USERS;
|
|
|
|
sKey := IncludeTrailingPathDelimiter(sKey);
|
|
if Reg.OpenKeyReadOnly(sKey) then
|
|
begin
|
|
Guard(SubList, TStringList.Create);
|
|
Reg.GetKeyNames(SubList);
|
|
Reg.CloseKey;
|
|
|
|
if SubList.Count > 0 then
|
|
begin
|
|
for i := 0 to SubList.Count - 1 do
|
|
begin
|
|
if Reg.OpenKeyReadOnly(sKey + SubList[i]) then
|
|
begin
|
|
if RegQueryInfoKey(Reg.CurrentKey, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, @ftKey) = ERROR_SUCCESS then
|
|
begin
|
|
FileTimeToLocalFileTime(ftKey, ftLocal);
|
|
RkInfo.sKName := SubList[i];
|
|
RkInfo.ftLastWriteTime := ftLocal;
|
|
aRkInfoList.Add(RkInfo);
|
|
end;
|
|
Reg.CloseKey;
|
|
end;
|
|
end;
|
|
|
|
Result := aRkInfoList.Count;
|
|
if Result > 0 then
|
|
aRkInfoList.Sort(TComparer<TRkInfo>.Construct(CompareByLastWriteTime));
|
|
end;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(E, 'Fail .. ExtrSubKeySortList()');
|
|
end;
|
|
end;
|
|
|
|
end.
|