I want to display a treeview with all the registry information in it ( i.e all the subkeys ). I have put together the following Fn to do the same. But i am getting the info of only one Key, not all. What is missing in my code ?
function TForm1.DisplayKeys(TreeNode : TTreeNode;KeyToSearch:String):String;
var
i: Integer;
RootKey : Integer;
NewTreeNode : TTreeNode;
str : TStringList;
// str2: TStringList;
begin
i:=0;
if reg.OpenKey(KeyToSearch,False) then
begin
str:=nil;
str:=TStringList.create;
reg.GetKeyNames(str);
//For all SubKeys
for i:=0 to str.Count-1 do
begin
NewTreeNode:=TreeView1.Items.AddChild(TreeNode, Str.Strings[i]);
if reg.HasSubKeys then
begin
DisplayKeys(NewTreeNode,Str.Strings[i]);
end;
end;
end;
the call to the Function is
procedure TForm1.FormCreate(Sender: TObject);
begin
reg:=nil;
reg:=TRegistry.create;
str2:=nil;
str2:=TStringList.create;
reg.RootKey:=HKEY_CURRENT_CONFIG;
TreeView1.Items.BeginUpdate; //prevents screen repaint every time node is added
DisplayKeys(nil,''); // call to fn here
TreeView1.Items.EndUpdate; // Nodes now have valid indexes
end;
Note that i am not getting any error, just that info is incomplete
Some problems:
You are using OpenKey which attempts to open the key with write access. Instead you should use OpenKeyReadOnly. If you really do mean to write to those keys then you will have to run elevated as an administrator.
You are failing to close the keys once you have finished with them.
More seriously, your use of relative registry keys is not sufficient. I believe you will need to pass around the full path to the key. I wrote a little demo console app to show what I mean:
program RegistryEnumerator;
{$APPTYPE CONSOLE}
uses
Classes, Windows, Registry;
var
Registry: TRegistry;
procedure DisplayKeys(const Key: string; const Depth: Integer);
var
i: Integer;
SubKeys: TStringList;
begin
if Registry.OpenKeyReadOnly(Key) then begin
Try
SubKeys := TStringList.Create;
Try
Registry.GetKeyNames(SubKeys);
for i := 0 to SubKeys.Count-1 do begin
Writeln(StringOfChar(' ', Depth*2) + SubKeys[i]);
DisplayKeys(Key + '\' + SubKeys[i], Depth+1);
end;
Finally
SubKeys.Free;
End;
Finally
Registry.CloseKey;
End;
end;
end;
begin
Registry := TRegistry.Create;
Try
Registry.RootKey := HKEY_CURRENT_CONFIG;
DisplayKeys('', 0);
Readln;
Finally
Registry.Free;
End;
end.
try this :-
procedure TForm1.Button1Click(Sender: TObject);
begin
TreeView1.Items.Clear;
path := Edit1.Text;
// reg.RootKey := HKEY_LOCAL_MACHINE ;
TreeView1.Items.BeginUpdate;
drawtreeview(nil, path);
TreeView1.Items.EndUpdate;
end;
procedure TForm1.drawtreeview( node: TTreeNode; name: string);
var
i: Integer;
NewTreeNode: TTreeNode;
str, str2 : TStringList;
reg : TRegistry;
begin
reg := TRegistry.Create;
reg.RootKey := HKEY_LOCAL_MACHINE;
i := 0;
if reg.OpenKeyReadOnly(name) then
begin
str := TStringList.create;
reg.GetKeyNames(str);
for i := 0 to str.Count - 1 do
begin
NewTreeNode := TreeView1.Items.AddChild(node, str.Strings[i]);
if reg.HasSubKeys then
begin
drawtreeview(NewTreeNode, name + '\' + str.Strings[i]);
end
else
ShowMessage('no sub keys');
end;
end;
reg.CloseKey;
reg.Free;
end;
Related
Here its a VCL app and I have a link with my Ini file and I wanna keep adding lines in there with time and date stamps with press of a button.
private
FLog: TStringList;
FIni: TIniFile;
aTime: TDateTime;
procedure TForm2.btnBreakClick(Sender: TObject);
begin
FLog := TStringList.Create;
try
aTime := Now;
begin
FIni.WriteString('FileName', 'Break', FormatDateTime('dd/mm/yyyy hh:nn', aTime));
end;
finally
FLog.Free;
end
end;
With this piece of code I can only replace the previous time and date stamp I have tried to do it with a for loop but without succes.
This is the outcome with the current few lines of code.
[FileName]
Break=09-10-2018 13:35
And what I want is that everytime I hit the break button it needs to add on to the file with a other time.
An INI file contains key/value pairs. To do what you are asking for, you need to create a unique key name with every button press, otherwise you are just overwriting an existing value each time, as you have already discovered.
Try something more like this:
procedure TForm2.btnBreakClick(Sender: TObject);
var
Keys: TStringList;
MaxBreak, I, Num: Integer;
begin
MaxBreak := 0;
Keys := TStringList.Create;
try
FIni.ReadSection('FileName', Keys);
for I := 0 to Keys.Count-1 do
begin
if StartsText('Break', Keys[I]) then
begin
if TryStrToInt(Copy(Keys, 6, MaxInt), Num) then
begin
if Num > MaxBreak then
MaxBreak := Num;
end;
end;
end;
finally
Keys.Free;
end;
FIni.WriteString('FileName', 'Break'+IntToStr(MaxBreak+1), FormatDateTime('dd/mm/yyyy hh:nn', Now));
end;
Or this:
procedure TForm2.btnBreakClick(Sender: TObject);
var
I: Int64;
Key: string;
begin
for I := 1 to Int64(MaxInt) do
begin
Key := 'Break' + IntToStr(I);
if not FIni.ValueExists('FileName', Key) then
begin
FIni.WriteString('FileName', Key, FormatDateTime('dd/mm/yyyy hh:nn', Now));
Exit;
end;
end;
end;
Or this:
procedure TForm2.btnBreakClick(Sender: TObject);
var
NumBreaks: Integer;
begin
NumBreaks := FIni.ReadInteger('FileName', 'NumBreaks', 0);
Inc(NumBreaks);
FIni.WriteInteger('FileName', 'NumBreaks', NumBreaks);
FIni.WriteString('FileName', 'Break' + IntToStr(NumBreaks), FormatDateTime('dd/mm/yyyy hh:nn', Now));
end;
Although you referred to TIniFile, your post and your comments tell me that that is not necessarily what you want. TIniFile is not really intended for the kind of usage you are describing, although it can be used (as the other answer shows).
For simple recording of events I suggest an ordinary text file, and for adding events to it, a TStringList as in the following example. The example is a simplified extract from code I used myself long time ago.
var
EventFile: TFileName;
procedure EventRecorder(EventTime: TDateTime; Description, Comment: string);
var
sl: TStringList;
es: string;
begin
sl: TStringList;
try
if FileExists(EventFile) then
sl.LoadFromFile(EventFile);
es := FormatDateTime('yyyy-mm-dd hh:nn:ss', EventTime)+' '+Description+' '+comment;
sl.Add(es);
sl.SaveToFile(EventFile);
finally
sl.free;
end;
end;
Typical usage
procedure TForm2.btnBreakClick(Sender: TObject);
begin
EventRecorder(now, 'Break', '');
end;
I have an ini file which contains the following:
[Colours]
1 = Red
2 = Blue
3 = Green
4 = Yellow
In my app I have a TComboBox which I would like to populate with the colours in the ini file.
Does anyone know how I'd go about this?
Thanks,
You can get a list of names in a section by using TIniFile.ReadSection() and then iterate to get the values:
procedure TForm1.LoadFile(const AFilename: String);
var
I: TIniFile;
L: TStringList;
X: Integer;
N: String;
V: String;
begin
I:= TIniFile.Create(AFilename);
try
L:= TStringList.Create;
try
ComboBox1.Items.Clear;
I.ReadSection('Colours', L);
for X := 0 to L.Count-1 do begin
N:= L[X]; //The Name
V:= I.ReadString('Colours', N, ''); //The Value
ComboBox1.Items.Add(V);
end;
finally
L.Free;
end;
finally
I.Free;
end;
end;
As an alternative, you could also dump the name/value pairs within the section into a single TStringList and read each value using the string list's built-in capabilities...
procedure TForm1.LoadFile(const AFilename: String);
var
I: TIniFile;
L: TStringList;
X: Integer;
N: String;
V: String;
begin
I:= TIniFile.Create(AFilename);
try
L:= TStringList.Create;
try
ComboBox1.Items.Clear;
I.ReadSectionValues('Colours', L);
for X := 0 to L.Count-1 do begin
N:= L.Names[X]; //The Name
V:= L.Values[N]; //The Value
ComboBox1.Items.Add(V);
end;
finally
L.Free;
end;
finally
I.Free;
end;
end;
On a side-note, Ini files do not have spaces on either side of the = sign, unless of course you want that space as part of the actual name or value.
try this, without reading the file twice:
uses IniFiles;
procedure TForm1.Button1Click(Sender: TObject);
var
lIni : TIniFile;
i: Integer;
begin
lIni := TIniFile.Create('c:\MyFile.ini');
try
lIni.ReadSectionValues('Colours', ComboBox1.Items);
for i := 0 to ComboBox1.Items.Count - 1 do
ComboBox1.Items[i] := ComboBox1.Items.ValueFromIndex[i];
finally
FreeAndNil(lIni);
end;
end;
I am trying to get the reg_binary as string from a registry key.
This is my function
function ReadBinString(key: string; AttrName: string): string;
var
ReadStr: TRegistry;
begin
// Result := '';
ReadStr := TRegistry.Create(KEY_WRITE OR KEY_WOW64_64KEY);
ReadStr.RootKey := HKEY_LOCAL_MACHINE;
if ReadStr.OpenKey(key, true) then
begin
Result := ReadStr.GetDataAsString(AttrName);
end;
ReadStr.CloseKey;
ReadStr.Free;
end;
and here is my registry key Export :
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\ZES\ACINFO]
"iamthere"=dword:00000001
"ArrayOrder"=hex:4d,79,45,78,63,6c,75,64,65
the problem is , the function returns empty string
I even tried running as administrator to make sure that it is not permissions.
Any help ?
Expanding on my comment to the question, I'd use code like so:
function ReadBinString(RootKey: HKEY; Access: LongWord; const KeyName,
ValueName: string; Encoding: TEncoding): string;
var
Registry: TRegistry;
Bytes: TBytes;
begin
Registry := TRegistry.Create(Access);
try
Registry.RootKey := RootKey;
if Registry.OpenKeyReadOnly(KeyName) then begin
SetLength(Bytes, Registry.GetDataSize(ValueName));
Registry.ReadBinaryData(ValueName, Pointer(Bytes)^, Length(Bytes));
Result := Encoding.GetString(Bytes);
end else begin
Result := '';
end;
finally
Registry.Free;
end;
end;
For your data you would call it like so:
Value := ReadBinString(HKEY_LOCAL_MACHINE, KEY_WOW64_64KEY, 'Software\ZES\ACINFO',
'ArrayOrder', TEncoding.ANSI);
Notes:
I have avoided hard-coding the root key.
I have used TEncoding to decode the byte array to text. This is far more effective than GetDataAsString.
I have allowed the caller to specify the encoding to be used.
I have allowed the caller to specify the access flags.
I have used OpenKeyReadOnly because we do not require write access.
Thanks to David Heffernan I came with this solution:
function ReadBinString(key: string; AttrName: string): string;
var
ReadStr: TRegistry;
hexStr : string;
I : Integer;
begin
// Result := '';
ReadStr := TRegistry.Create(KEY_READ OR KEY_WOW64_64KEY);
ReadStr.RootKey := HKEY_LOCAL_MACHINE;
if ReadStr.OpenKey(key, true) then
begin
hexStr := ReadStr.GetDataAsString(AttrName);
hexStr := hexStr.Replace(',','');
for I := 1 to length (hexStr) div 2 do
Result:= Result+Char(StrToInt('$'+Copy(hexStr,(I-1)*2+1,2)));
end;
ReadStr.CloseKey;
ReadStr.Free;
end;
Thanks to David Heffernan again ... this worked for me :
function ReadBinString(key: string; AttrName: string): string;
var
ReadStr: TRegistry;
hexStr: string;
I: Integer;
Bytes: TBytes;
Encoding: TEncoding;
begin
Encoding := TEncoding.ANSI;
Result := '';
ReadStr := TRegistry.Create(KEY_READ OR KEY_WOW64_64KEY);
ReadStr.RootKey := HKEY_LOCAL_MACHINE;
try
if ReadStr.OpenKeyReadOnly(key ) then
begin
SetLength(Bytes, ReadStr.GetDataSize(AttrName));
ReadStr.ReadBinaryData(AttrName, Pointer(Bytes)^, Length(Bytes));
Result := Encoding.GetString(Bytes);
// hexStr := ReadStr.GetDataAsString(AttrName);
//
// hexStr := hexStr.Replace(',','');
// for I := 1 to length (hexStr) div 2 do
// Result:= Result+Char(StrToInt('$'+Copy(hexStr,(I-1)*2+1,2)));
end;
except
end;
ReadStr.CloseKey;
ReadStr.Free;
end;
i need some help with my procedure. I want to save some strings in a stringlist which is created in another procedure. How can i do this?
I wrote a comment at the right place to understand it better.
procedure GetIniNamesWithoutExt(IniPfade: TStringList);
var
i, suchPunkt: integer;
ini: TIniFile;
Modul, fullFileName, IniName: String;
begin
try
for i := 0 to IniPfade.Count-1 do
begin
fullFileName := IniPfade.Strings[i];
Modul := ExtractFileName(fullFileName); // Dateiname aktueller Ini + .Ini Endung
suchPunkt := Pos('.', Modul);
IniName := Copy(Modul, 1, suchPunkt-1); // Aktueller Modulname ohne ini Endung
// Here should be the Code for saving the String "IniName" to a StringList which is created in procedure a. Procedure a calls the procedure GetIniNamesWithoutExt.
end;
finally
end;
end;
How about
procedure GetIniNamesWithoutExt(IniPfade, Module: TStrings);
var
i, suchPunkt: integer;
ini: TIniFile;
Modul, fullFileName, IniName: String;
begin
Module.BeginUpdate;
try
for i := 0 to IniPfade.Count-1 do
begin
fullFileName := IniPfade.Strings[i];
Modul := ExtractFileName(fullFileName); // Dateiname aktueller Ini + .Ini Endung
suchPunkt := Pos('.', Modul);
IniName := Copy(Modul, 1, suchPunkt-1); // Aktueller Modulname ohne ini Endung
Module.Add(IniName);
end;
finally
Module.EndUpdate;
end;
end;
and from procedure A:
procedure A;
var
Module: TStringList;
begin
Module := TStringList.Create;
try
GetIniNamesWithoutExt(IniPfade , Module);
// Do Whatever you want with "Module"
finally
Module.Free;
end;
end;
Want to obtain Delphi Application build number and post into title bar
Here is how I do it. I put this in almost all of my small utilities:
procedure GetBuildInfo(var V1, V2, V3, V4: word);
var
VerInfoSize, VerValueSize, Dummy: DWORD;
VerInfo: Pointer;
VerValue: PVSFixedFileInfo;
begin
VerInfoSize := GetFileVersionInfoSize(PChar(ParamStr(0)), Dummy);
if VerInfoSize > 0 then
begin
GetMem(VerInfo, VerInfoSize);
try
if GetFileVersionInfo(PChar(ParamStr(0)), 0, VerInfoSize, VerInfo) then
begin
VerQueryValue(VerInfo, '\', Pointer(VerValue), VerValueSize);
with VerValue^ do
begin
V1 := dwFileVersionMS shr 16;
V2 := dwFileVersionMS and $FFFF;
V3 := dwFileVersionLS shr 16;
V4 := dwFileVersionLS and $FFFF;
end;
end;
finally
FreeMem(VerInfo, VerInfoSize);
end;
end;
end;
function GetBuildInfoAsString: string;
var
V1, V2, V3, V4: word;
begin
GetBuildInfo(V1, V2, V3, V4);
Result := IntToStr(V1) + '.' + IntToStr(V2) + '.' +
IntToStr(V3) + '.' + IntToStr(V4);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Form1.Caption := Form1.Caption + ' - v' + GetBuildInfoAsString;
end;
I most strongly recommend not to use GetFileVersion when you want to know the version of the executable that is currently running! I have two pretty good reasons to do this:
The executable may be unaccessible (disconnected drive/share), or changed (.exe renamed to .bak and replaced by a new .exe without the running process being stopped).
The version data you're trying to read has actually already been loaded into memory, and is available to you by loading this resource, which is always better than to perform extra (relatively slow) disk operations.
To load the version resource in Delphi I use code like this:
uses Windows,Classes,SysUtils;
var
verblock:PVSFIXEDFILEINFO;
versionMS,versionLS:cardinal;
verlen:cardinal;
rs:TResourceStream;
m:TMemoryStream;
p:pointer;
s:cardinal;
begin
m:=TMemoryStream.Create;
try
rs:=TResourceStream.CreateFromID(HInstance,1,RT_VERSION);
try
m.CopyFrom(rs,rs.Size);
finally
rs.Free;
end;
m.Position:=0;
if VerQueryValue(m.Memory,'\',pointer(verblock),verlen) then
begin
VersionMS:=verblock.dwFileVersionMS;
VersionLS:=verblock.dwFileVersionLS;
AppVersionString:=Application.Title+' '+
IntToStr(versionMS shr 16)+'.'+
IntToStr(versionMS and $FFFF)+'.'+
IntToStr(VersionLS shr 16)+'.'+
IntToStr(VersionLS and $FFFF);
end;
if VerQueryValue(m.Memory,PChar('\\StringFileInfo\\'+
IntToHex(GetThreadLocale,4)+IntToHex(GetACP,4)+'\\FileDescription'),p,s) or
VerQueryValue(m.Memory,'\\StringFileInfo\\040904E4\\FileDescription',p,s) then //en-us
AppVersionString:=PChar(p)+' '+AppVersionString;
finally
m.Free;
end;
end;
Thanks to the posts above, I made my own library for this purpose.
I believe that it is a little bit more correct than all other solutions here, so I share it - feel free to reuse it...
unit KkVersion;
interface
function FileDescription: String;
function LegalCopyright: String;
function DateOfRelease: String; // Proprietary
function ProductVersion: String;
function FileVersion: String;
implementation
uses
Winapi.Windows, System.SysUtils, System.Classes, Math;
(*
function GetHeader(out AHdr: TVSFixedFileInfo): Boolean;
var
BFixedFileInfo: PVSFixedFileInfo;
RM: TMemoryStream;
RS: TResourceStream;
BL: Cardinal;
begin
Result := False;
RM := TMemoryStream.Create;
try
RS := TResourceStream.CreateFromID(HInstance, 1, RT_VERSION);
try
RM.CopyFrom(RS, RS.Size);
finally
FreeAndNil(RS);
end;
// Extract header
if not VerQueryValue(RM.Memory, '\\', Pointer(BFixedFileInfo), BL) then
Exit;
// Prepare result
CopyMemory(#AHdr, BFixedFileInfo, Math.Min(sizeof(AHdr), BL));
Result := True;
finally
FreeAndNil(RM);
end;
end;
*)
function GetVersionInfo(AIdent: String): String;
type
TLang = packed record
Lng, Page: WORD;
end;
TLangs = array [0 .. 10000] of TLang;
PLangs = ^TLangs;
var
BLngs: PLangs;
BLngsCnt: Cardinal;
BLangId: String;
RM: TMemoryStream;
RS: TResourceStream;
BP: PChar;
BL: Cardinal;
BId: String;
begin
// Assume error
Result := '';
RM := TMemoryStream.Create;
try
// Load the version resource into memory
RS := TResourceStream.CreateFromID(HInstance, 1, RT_VERSION);
try
RM.CopyFrom(RS, RS.Size);
finally
FreeAndNil(RS);
end;
// Extract the translations list
if not VerQueryValue(RM.Memory, '\\VarFileInfo\\Translation', Pointer(BLngs), BL) then
Exit; // Failed to parse the translations table
BLngsCnt := BL div sizeof(TLang);
if BLngsCnt <= 0 then
Exit; // No translations available
// Use the first translation from the table (in most cases will be OK)
with BLngs[0] do
BLangId := IntToHex(Lng, 4) + IntToHex(Page, 4);
// Extract field by parameter
BId := '\\StringFileInfo\\' + BLangId + '\\' + AIdent;
if not VerQueryValue(RM.Memory, PChar(BId), Pointer(BP), BL) then
Exit; // No such field
// Prepare result
Result := BP;
finally
FreeAndNil(RM);
end;
end;
function FileDescription: String;
begin
Result := GetVersionInfo('FileDescription');
end;
function LegalCopyright: String;
begin
Result := GetVersionInfo('LegalCopyright');
end;
function DateOfRelease: String;
begin
Result := GetVersionInfo('DateOfRelease');
end;
function ProductVersion: String;
begin
Result := GetVersionInfo('ProductVersion');
end;
function FileVersion: String;
begin
Result := GetVersionInfo('FileVersion');
end;
end.
Pass the full file name of your EXE to this function, and it will return a string like:
2.1.5.9, or whatever your version # is.
function GetFileVersion(exeName : string): string;
const
c_StringInfo = 'StringFileInfo\040904E4\FileVersion';
var
n, Len : cardinal;
Buf, Value : PChar;
begin
Result := '';
n := GetFileVersionInfoSize(PChar(exeName),n);
if n > 0 then begin
Buf := AllocMem(n);
try
GetFileVersionInfo(PChar(exeName),0,n,Buf);
if VerQueryValue(Buf,PChar(c_StringInfo),Pointer(Value),Len) then begin
Result := Trim(Value);
end;
finally
FreeMem(Buf,n);
end;
end;
end;
After defining that, you can use it to set your form's caption like so:
procedure TForm1.FormShow(Sender: TObject);
begin
//ParamStr(0) is the full path and file name of the current application
Form1.Caption := Form1.Caption + ' version ' + GetFileVersion(ParamStr(0));
end;
We do this for all our apps but we use a Raize component RzVersioninfo.
works quite well just need to use the following code
on form create
Caption := RzVersioninfo1.filedescripion + ': ' + RzVersionInfo1.FileVersion;
obviously if you don't want any of the other components from raize use one of the options above as there is a cost to the raize components.
From http://www.martinstoeckli.ch/delphi/delphi.html#AppVersion
With this function you can get the version of a file, which contains a
version resource. This way you can display the version number of your
application in an information dialog. To include a version resource to
your Delphi application, set the "Versioninfo" in the project options.
My code:
uses unit Winapi.Windows;
function GetModuleVersion(Instance: THandle; out iMajor, iMinor, iRelease, iBuild: Integer): Boolean;
var
fileInformation: PVSFIXEDFILEINFO;
verlen: Cardinal;
rs: TResourceStream;
m: TMemoryStream;
begin
result := false;
m := TMemoryStream.Create;
try
try
rs := TResourceStream.CreateFromID(Instance, 1, RT_VERSION);
try
m.CopyFrom(rs, rs.Size);
finally
rs.Free;
end;
except
exit;
end;
m.Position:=0;
if not VerQueryValue(m.Memory, '\', Pointer(fileInformation), verlen) then
begin
iMajor := 0;
iMinor := 0;
iRelease := 0;
iBuild := 0;
Exit;
end;
iMajor := fileInformation.dwFileVersionMS shr 16;
iMinor := fileInformation.dwFileVersionMS and $FFFF;
iRelease := fileInformation.dwFileVersionLS shr 16;
iBuild := fileInformation.dwFileVersionLS and $FFFF;
finally
m.Free;
end;
Result := True;
end;
Usage:
if GetModuleVersion(HInstance, iMajor, iMinor, iRelease, iBuild) then
ProgramVersion := inttostr(iMajor)+'.'+inttostr(iMinor)+'.'+inttostr(iRelease)+'.'+inttostr(iBuild);