Tras la adaptación de código que tenía escrito para este fin, os presento una función escrita sólo con la API de Windows que nos permite navegar y buscar en el registro explorando incluso las subclaves.
La función la denomino ScanRegistry y tiene los siguientes parámetros:
procedure ScanRegistry(Key, Name: PCHAR; RegAction: TRegAction; ScanType: DWORD; RegProgress: TRegProgress; var Stop: boolean);
Key: Será la clave donde comenzará el escáner, listado o búsqueda. Su notación será como en un árbol de directorios, comenzando con la clave principal. Por ejemplo:
'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\run\'
Name: Será un nombre de clave o valor o cadena que deseemos buscar en el registro, si no queremos realizar una búsqueda se ignorará.
RegAction: Se trata de una función callback que será llamada al encontrar una nueva clave o valor, en caso de que estemos realizando un listado, o el resultado de la búsqueda si es el caso.
ScanType: Se trata de un parámetro para indicar que tipo de escaner vamos a realizar y puede tener los siguientes valores:
None = $00000000; List = $00000001; ScanSubKeys = $00000002; ListSubKeys = $00000003; // List or SubKeys ScanKeys = $00000004; ScanValues = $00000008; ScanDatas = $00000010;
RegProgress: Es otra función callback que informa del nombre completo de la clave que está siendo escaneada. Su valor puede ser nulo.
Stop: Cuando vale true el escáner se detiene en ese momento.
Las funciones callback se definen de esta forma:
TRegAction = function(_hKey: HKEY; Path, Name: PCHAR; DataType: integer; Data: PBYTE; Size: DWORD): integer; stdcall; TRegProgress = function(Path: PCHAR): integer; stdcall;
Puede parecer una función compleja de usar pero no lo es tanto. Como contrapartida podremos diseñar los listados y búsquedas a nuestro gusto por la posibilidad de escribir nosotros mismos las funciones. La he diseñado fundamentalmente para listados y búsquedas secuenciales dentro de una clase o en sus subclases, pudiendo alcanzar al registro entero. La velocidad se va a ver afectada en gran medica del diseño de las funciones callback de usuario
Las búsquedas se pueden hacer por nombre de clave, por nombre de valor, por datos del valor o combinación de las anteriores. También se podrán recorrer o no las subclaves encontradas a su paso.
Un ejemplo básico de búsqueda completa en una clave dada y sus subclaves sería así:
Stop:= false; //Variable global o miembro de nuestro Form ScanRegistry(PCHAR(Edit1.Text), PCHAR(Edit2.Text), @SearchRegAction, SubKeys or SearchForKey or SearchForValue or SearchForData, @RegProgress, Stop); MessageBox(Handle, 'Terminó la búsqueda en el Registro ', 'SearchRegistry', MB_ICONINFORMATION);
Con la siguiente función callback, que respondería a cada encuentro:
function SearchRegAction(hKey0: HKEY; Path, Name: PCHAR; KeyType: integer; Data: PBYTE; Size: DWORD): integer; stdcall; begin if MessageBox(Form1.Handle, PCHAR(String(Path) + '\' + String(Name)), 'Encontrado', MB_ICONINFORMATION or MB_OKCANCEL) = IDCANCEL then Stop:= true; Result:= 0; end;
Los listados por defecto son por nombre de clave y valor, es decir siempre buscará el primer nivel de subclaves y valores de la clave dada. Pueden especificarse que solo liste subclaves, valores o ambas. También podrá especificarse que recorra o no las subclaves encontradas a su paso.
Un ejemplo de listado completo pero no en las subclaves sería:
Stop:= false; //Variable global o miembro de nuestro Form ScanRegistry(PCHAR(Edit1.Text), PCHAR(Edit2.Text), @ListRegAction, List, RegProgress, Stop);
Para lo que deberíamos implementar ListRegAction que iría recibiendo las claves y valores encontrados.
El corazón del código es la función ScanRegistry:
// Escanea o busca por un nombre especificado procedure ScanRegistry(Key, Name: PCHAR; RegAction: TRegAction; ScanType: DWORD; RegProgress: TRegProgress; var Stop: boolean); var hKey0: HKEY; hKey_: HKEY; SubKey: PCHAR; ValueName0: PCHAR; PathKey: array [0..1023] of CHAR; KeyName, ValueName: ShortString; dwValueName: DWORD; Data: PBYTE; dwData: DWORD; KeyType: DWORD; Index: integer; Error: DWORD; begin if Stop then exit; if (ScanType = None) or (ScanType = ScanSubKeys) then exit; if Key = nil then exit; if Key^ = #0 then exit; hKey0:= 0; ValueName0:= nil; dwValueName:= sizeof(ValueName); dwData:= sizeof(Data); if Key[lstrlen(Key)-1] = '\' then Key[lstrlen(Key)-1]:= #0; SubKey:= StrStr(Key, '\'); if SubKey<>nil then inc(SubKey); // Preparo los filtros de ScanType por defecto if (ScanType and ScanDatas <> 0) and (ScanType and List <> 0) then ScanType:= ScanType or ScanValues; if (ScanType = List) or (ScanType = ListSubKeys) then ScanType:= ScanType or ScanKeys or ScanValues; hKey_:= StrToHKEY(Key); if RegOpenKeyEx(hKey_, SubKey, 0, KEY_ENUMERATE_SUB_KEYS, hKey0) = ERROR_SUCCESS then begin Index:= 0; // Enumeramos las subclaves <KEY> // Error:= RegQueryInfoKey(hKey0, 0,0,0, @nsc, 0,0,0,0,0,0,0); // nsc contiene el número de subclaves // Abrir la clave con KEY_READ o KEY_QUERY_VALUE Error:= RegEnumKey(hKey0, Index, @KeyName[0], Length(PathKey)); while Error = ERROR_SUCCESS do begin inc(index); wsprintf(@PathKey[0], '%s\%s', Key, @KeyName[0]); if @RegProgress <> nil then RegProgress(@PathKey[0]); if Stop then exit; if (ScanType and List<>0) and (ScanType and ScanKeys <> 0) then begin if @RegAction <> nil then RegAction(hKey0, Key, @KeyName[0], -1, nil, 0); end else if ScanType and ScanKeys <> 0 then // Si encontrado if lstrcmpi(@KeyName[0], Name) = 0 then if @RegAction <> nil then RegAction(hKey0, @PathKey[0], @KeyName[0], -1, nil, 0); if (ScanType and ScanSubKeys) <> 0 then ScanRegistry(@PathKey[0], Name, RegAction, ScanType, RegProgress, Stop); Error:= RegEnumKey(hKey0, Index, @KeyName[0], Length(PathKey)); end; RegCloseKey(hKey0); end else begin // Si falla extraigo la ultima subclave por si es un Path de un valor ValueName0:= nil; if SubKey <> nil then begin ValueName0:= SubKey+lstrlen(SubKey)-1; while ValueName0^ <> '\' do dec(ValueName0); ValueName0^:= #0; inc(ValueName0); end; end; if ValueName0 <> nil then begin // Si el Path es un valor, lo leemos dwData:= sizeof(Data); if RegOpenKeyEx(hKey_, SubKey, 0, KEY_READ, hKey0) = ERROR_SUCCESS then begin RegQueryValueEx(hKey0, ValueName0, nil, @KeyType, nil, @dwData); if (ScanType and ScanDatas<>0) then //or (ScanType and List<>0) then begin if @RegAction <> nil then begin Data:= VirtualAlloc(nil, dwData, MEM_COMMIT, PAGE_READWRITE); RegQueryValueEx(hKey0, ValueName0, nil, @KeyType, Data, @dwData); if Data <> nil then if (StrStrNI(PCHAR(Data), Name, dwData) <> nil) or (ScanType and List<>0) then RegAction(hKey0, Key, ValueName0, KeyType, Data, dwData); VirtualFree(Data, 0, MEM_RELEASE); end; end; RegCloseKey(hKey0); ValueName0:= nil; end; end else if (ScanType and (ScanValues or ScanDatas)<>0) then begin // Si es un Path a una sublave enumeramos los valores Index:= 0; if RegOpenKeyEx(hKey_, SubKey, 0, KEY_ENUMERATE_SUB_KEYS or KEY_READ, hKey0) = ERROR_SUCCESS then begin dwValueName:= sizeof(ValueName); Error:= RegEnumValue(hKey0, Index, @ValueName[0], dwValueName, nil, @KeyType, nil, @dwData); while Error = ERROR_SUCCESS do begin if Stop then exit; // Si buscamos o listamos por nombre de valor if (ScanType and ScanValues<>0) then begin // Si encontrado o un listado por nombre de valor if (lstrcmpi(@ValueName[0], Name) = 0) or (ScanType and List <> 0) then begin if @RegAction <> nil then begin Data:= VirtualAlloc(nil, dwData, MEM_COMMIT, PAGE_READWRITE); dwValueName:= sizeof(ValueName); RegEnumValue(hKey0, Index, @ValueName[0], dwValueName, nil, @KeyType, Data, @dwData); RegAction(hKey0, Key, @ValueName[0], KeyType, Data, dwData); VirtualFree(Data, 0, MEM_RELEASE); end; end; end; //ScanType: ScanValues // Si buscamos por dato. No tiene sentido en listados if ScanType and List = 0 then if (ScanType and ScanDatas <> 0) then begin if @RegAction <> nil then begin Data:= VirtualAlloc(nil, dwData, MEM_COMMIT, PAGE_READWRITE); dwValueName:= sizeof(ValueName); RegEnumValue(hKey0, Index, @ValueName[0], dwValueName, nil, @KeyType, Data, @dwData); if (Data <> nil) then if (StrStrNI(PCHAR(Data), Name, dwData) <> nil) then begin RegAction(hKey0, Key, @ValueName[0], KeyType, Data, dwData); end; VirtualFree(Data, 0, MEM_RELEASE); end; end; dwValueName:= sizeof(ValueName); inc(Index); Error:= RegEnumValue(hKey0, Index, @ValueName[0], dwValueName, nil, @KeyType, nil, @dwData); end; if (Error <> ERROR_NO_MORE_ITEMS) and (Error <> ERROR_SUCCESS) then begin if (@RegProgress <> nil) then begin lstrcat(@PathKey[0], ' <ERROR>'); RegProgress(@PathKey[0]); end; if (@RegAction <> nil) then RegAction(hKey0, Key, '<ERROR>', REG_NONE, nil, 0); end; RegCloseKey(hKey0); end; end; end;
Para realizar búsquedas en todo el registro os propongo una función de apoyo, que podrá usarse como prototipo para toda clase de búsquedas y que, como veis usa los mismos parámetros de ScanRegistry:
// Busca en una clave o en todas si no se especifica procedure RegistrySearch(Key, Name: PCHAR; RegAction: TRegAction; ScanType: DWORD; RegProgress: TRegProgress; var Stop: boolean); var i: integer; begin // Si tengo una clave específica if (Key<>nil) and (Key^<>#0) then begin ScanRegistry(Key, Name, RegAction, ScanType, RegProgress, Stop); exit; end; // Si no tengo clave recorro todas i:= 0; while HKeys[i]<>nil do begin ScanRegistry(HKeys[i], Name, RegAction, ScanType, RegProgress, Stop); inc(i); end; end;
El código lo he encerrado en una unit: RegEx.pas
Para ilustrar un ejemplo completo he escrito una aplicación muy simple que realiza listados y búsquedas dando los resultados en un ListView. La aplicación puede seguir desarrollándose para incluir el borrado de claves y/o valores, o para crear nuevas claves y valores, editarlas, etc. En definitiva podemos crearnos un regedit a medida.
El código está probado en WinXP y Win8.
Espero que sea de utilidad para vuestros proyectos o que valga de ejemplo base para cambios y mejoras del código expuesto.
Subo todo el código con el proyecto de la aplicación de ejemplo.
Saludos.