|
Il programma che vado ad esporre in questo articolo nasce tempo addietro, il
giorno in cui mi venne in mente di determinare quali fossero le voci di registro
che definiscono in Windows XP l' utilizzo del menł di avvio proprio di Windows
XP oppure l' utilizzo di quello classico (stile windows 2000 tanto per
intenderci). Utilizzando il Registry Monitor di marca Sysinternals,
venivo sommerso da un mare di modifiche ... in pratica non era quella la strada.
Allora ho pensato ad un programmino per enumerare tutte le modifiche effettuate
sul registro in un determinato intervallo. Fortunatamente le api win32 relative
alla gestione del registro consentono la determinazione della data e ora di
ultima modifica di ogni chiave di registro. Passiamo ai paragrafi successivi per
esaminare i dettagli dell'implementazione.
1. RegQueryInfoKey
L'api win32 RegQueryInfoKey implementata in advapi32.dll ,
consente di ottenere informazioni relative ad un determinata chiave di registro.
Di seguito la definizione
PFileTime = ^TFileTime;
TFileTime = record
dwLowDateTime: Cardinal;
dwHighDateTime: Cardinal;
end;
function RegQueryInfoKey(hKey: Cardinal;
lpClass: PChar;
lpcbClass: PCardinal;
lpReserved: Pointer;
lpcSubKeys: PCardinal;
lpcbMaxSubKeyLen: PCardinal;
lpcbMaxClassLen: PCardinal;
lpcValues: PCardinal;
lpcbMaxValueNameLen: PCardinal;
lpcbMaxValueLen: PCardinal;
lpcbSecurityDescriptor: PCardinal;
lpftLastWriteTime: PFileTime
): Integer; stdcall;
Analizziamo i singoli parametri: - hKey:[input] handle alla chiave di registro che si vuole esaminare; tale handle puņ essere un valore restituito dalle api win32 RegCreateKeyEx e RegOpenKeyEx (in tal caso bisogna aver specificato il diritto di accesso KEY_QUERY_VALUE) oppure puņ essere uno dei valori predefiniti che fanno riferimento alle "root" ossia: HKEY_CLASSES_ROOT ($80000000), HKEY_CURRENT_USER ($80000001), HKEY_LOCAL_MACHINE ($80000002), HKEY_USERS ($80000003), HKEY_PERFORMANCE_DATA ($80000004), HKEY_CURRENT_CONFIG ($80000005),
- lpClass: [output] si puņ ignorare
- lpcbClass: [input, output] fa riferimento a lpClass e quindi ignorabile
- lpReserved: deve essere nil obbligatoriamente
- lpcSubKeys: [output] riceve il numero di sotto-chiavi della chiave in esame
- lpcbMaxSubKeyLen: [output] riceve la lunghezza massima (senza contare il carattere di terminazione) dei nomi delle sotto-chiavi della chiave in esame
- lpcbMaxClassLen: [output] si puņ ignorare
- lpcValues: [output] riceve il numero dei valori associati con la chiave in esame
- lpcbMaxValueNameLen: [output] riceve la lunghezza massima (senza contare il carattere di terminazione) dei nomi dei valori associati alla chiave in esame
- lpcbMaxValueLen: [output] riceve la dimensione massima dei dati dei valori associati alla chiave in esame
- lpcbSecurityDescriptor: [output] riceve la dimensione del security descriptor della chiave: si puņ ignorare
- lpftLastWriteTime: [output] riceve la data/ora di ultima scrittura (quello che ci interessa principalmente in questo articolo)
In caso di successo la funzione restituisce 0; in caso differente si puņ usare GetLastError per maggiori informazioni sull'errore che si č verificato. 2. GetKeyInfo GetKeyInfo č un metodo della classe TRegistry implementata da Delphi nella unit Registry.pas. In pratica č un wrapper all'api RegQueryInfoKey discussa precedentemente. Di seguito la dichiarazione
type
TRegKeyInfo = record
NumSubKeys: Integer; //numero di sottochiavi
MaxSubKeyLen: Integer; //lunghezza massima dei nomi delle sottochiavi
NumValues: Integer; //numero di valori
MaxValueLen: Integer; //lunghezza massima dei nomi dei valori
MaxDataLen: Integer; //dimensione massima dei dati dei valori
FileTime: TFileTime; //data/ora di ultima scrittura
end;
function TRegistry.GetKeyInfo(var Value: TRegKeyInfo
): Boolean;
Useremo questo metodo per estrarre la data/ora di ultima modifica di una chiave di registro. 3. Conversione da TFileTime a TDateTime Per ottenere un valore di tipo TDateTime dal valore TregKeyInfo.FileTime (di tipo TFileTime) ci serviremo delle seguenti 2 funzioni di supporto:
function DateTimeToLocalDateTime(dt: TDateTime): TDateTime;
var
TimeZoneInfo: TTimeZoneInformation;
begin
case GetTimeZoneInformation(TimeZoneInfo) of
TIME_ZONE_ID_STANDARD:
Result := dt - (TimeZoneInfo.Bias / 60 / 24);
TIME_ZONE_ID_DAYLIGHT:
Result := dt - ((TimeZoneInfo.Bias + TimeZoneInfo.DaylightBias) / 60 / 24);
else
Result := 0;
end;
end;
function FileTimeToDateTime(ft: TFileTime): TDateTime;
var
SystemTime: TSystemTime;
FileTime: TFileTime;
begin
if FileTimeToLocalFileTime(ft, FileTime) then
begin
FileTimeToSystemTime(ft, SystemTime);
Result := SystemTimeToDateTime(SystemTime);
end;
end;
Di seguito il codice per la conversione
//conversione da TFileTime a TDateTime
var
FileTime: TFileTime;
RegDate: TDateTime;
...
begin
...
RegDate := FileTimeToDateTime(FileTime);
RegDate := DateTimeToLocalDateTime(RegDate);
...
end;
Chiaramente per convertire un TDateTime nella stringa corrispondente possiamo usare la nota TDateTimeToStr 4. Enumerazione delle chiavi di registro a partire da una chiave Siamo finalmente arrivati alla procedura ricorsiva che enumera le chiavi di registro a partire da una chiave data e per ognuna di esse chiama la GetKeyInfo per verificare se la data/ora di ultima modifica rientra nell'intervallo prefissato.
var
Inizio, Fine: TDateTime; //estremi dell'intervallo di riferimento
procedure EnumAllKeys(hkey: THandle; //handle alla chiave
KeyName: string //nome della chiave
);
var
l: TStringList;
n: Integer;
KeyName_: string;
RegDate: TDateTime;
RegKeyInfo: TRegKeyInfo;
begin
KeyName_ := KeyName;
with TRegistry.Create do
try
RootKey := hkey;
OpenKey(EmptyStr, False);
l := TStringList.Create;
try
GetKeynames(l);
CloseKey;
for n := 0 to l.Count - 1 do
begin
if OpenKey(l[n], False) then
begin
if (l[n] = '') then
begin
CloseKey;
continue;
end;
GetKeyInfo(RegKeyInfo);
with RegKeyInfo do
begin
RegDate := FileTimeToDateTime(FileTime);
RegDate := DateTimeToLocalDateTime(RegDate);
end;
if (RegDate <= Fine) and (RegDate >= Inizio) then
begin
//ho trovato una chiave la cui data/ora di ultima modifica
//rientra nel'intervallo specificato
FrmMain.Memo1.Lines.Add(DateTimeToStr(RegDate) + ' --- ' + KeyName_ + '\' + l[n]);
end;
EnumAllKeys(CurrentKey, KeyName_ + '\' + l[n]);
CloseKey;
end;
end;
finally
l.Free
end;
finally
Free;
end;
end;
5. Esempi Analisi nel contesto dell'utente corrente:
EnumAllKeys(HKEY_CURRENT_USER, 'HKey_Current_User');
Analisi a livello di macchina:
EnumAllKeys(HKEY_LOCAL_MACHINE, 'HKey_Local_Machine');
6. Qualcosina per finire Behh, abbiamo visto come trovare le chiavi di registro modificate in un determinato intervallo. A questo punto quindi puņ diventare divertente salvarsi il ramo di registro che ci interessa, fare determinate operazioni, trovare poi le chiavi che sono state modificate e fare dei bei confronti "prima"-"dopo". Ahh ... dimenticavo, ero partito dicendo che il motivo di tutti sti ragionamenti era quello di determinare le parti di registro che regolano il tipo di menu in Windows Xp (classico o nuovo): ebbene ora abbiamo la soluzione
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer]
"ShellState"=hex:24,00,00,00,3a,08,01,00,00,00,00,00,00,00,00,00,00,00,00,00,\
01,00,00,00,0d,00,00,00,00,00,00,00,01,00,00,00
Occorre considerare il quart'ultimo elemento: 03 = avvio di default 01 = avvio classico Dare un'occhiata anche all'articolo Personalizzazione della Shell di Windows da registro per ulteriori approfondimenti al riguardo. RegEnum
|