Error in ShellLink creation with negative IconIndex value - delphi

In Delphi XE7, I use this code to create a SHELL LINK pointing to a specific folder. This folder is displayed in Windows Explorer with a custom folder icon defined by a desktop.ini file inside this folder. The SHELL LINK should be created with the icon parameters found in the desktop.ini file, i.e. pointing to the same icon resource as the desktop.ini file. So here is the code:
function GetDesktopIniIconDataFromFolder(const APath: string; var VIconIndex: Integer): string;
var
DeskTopIniFile: string;
DesktopIni: System.IniFiles.TIniFile;
ThisIconFileStr, ThisIconIndexStr: string;
ThisIconIndexInt: Integer;
begin
Result := '';
if DirectoryExists(APath) then
begin
DeskTopIniFile := IncludeTrailingPathDelimiter(APath) + 'Desktop.ini';
if FileExists(DeskTopIniFile) then
begin
DesktopIni := System.IniFiles.TIniFile.Create(DeskTopIniFile);
try
ThisIconFileStr := DesktopIni.ReadString('.ShellClassInfo', 'IconFile', '');
if ThisIconFileStr <> '' then
begin
ThisIconIndexStr := DesktopIni.ReadString('.ShellClassInfo', 'IconIndex', '');
if ThisIconIndexStr <> '' then
begin
ThisIconIndexInt := System.SysUtils.StrToIntDef(ThisIconIndexStr, MaxInt);
if ThisIconIndexInt <> MaxInt then
begin
Result := ThisIconFileStr;
VIconIndex := ThisIconIndexInt;
end;
end;
end;
finally
DesktopIni.Free;
end;
end;
end;
end;
function MyCreateShellLink(const LinkFileName, AssocFileName, Desc, WorkDir,
Args, IconFileName: string; const IconIdx: Integer): Boolean;
var
SL: Winapi.ShlObj.IShellLink;
PF: Winapi.ActiveX.IPersistFile;
begin
Result := False;
Winapi.ActiveX.CoInitialize(nil);
try
if Winapi.ActiveX.Succeeded(
Winapi.ActiveX.CoCreateInstance(
Winapi.ShlObj.CLSID_ShellLink,
nil,
Winapi.ActiveX.CLSCTX_INPROC_SERVER,
Winapi.ShlObj.IShellLink, SL
)
) then
begin
SL.SetPath(PChar(AssocFileName));
SL.SetDescription(PChar(Desc));
SL.SetWorkingDirectory(PChar(WorkDir));
SL.SetArguments(PChar(Args));
if (IconFileName <> '') and (IconIdx >= 0) then
SL.SetIconLocation(PChar(IconFileName), IconIdx);
PF := SL as Winapi.ActiveX.IPersistFile;
Result := Winapi.ActiveX.Succeeded(
PF.Save(PWideChar(WideString(LinkFileName)), True)
);
end;
finally
Winapi.ActiveX.CoUninitialize;
end;
end;
// Usage:
var
IconFile: string;
IconIndex: Integer;
begin
IconFile := GetDesktopIniIconDataFromFolder(APath, IconIndex);
if IconFile <> '' then
MyCreateShellLink(ALinkFileName, ATargetFileName, ADescription, AWorkDir, AArgs, IconFile, IconIndex);
This works well, EXCEPT in cases where the IconIndex in the desktop.ini file is a negative value (which means the negative value indicates a resource ID rather than an ordinal value), like in this example:
[.ShellClassInfo]
InfoTip=#Shell32.dll,-12688
IconFile=%SystemRoot%\system32\mydocs.dll
IconIndex=-101
In this case the created SHELL LINK is erroneous, which means the Shell LINK does not contain the correct icon reference.
So how can I translate the negative IconIndex value -101 from the desktop.ini file to a value I can use in the MyCreateShellLink function?

If you want to use negative IconIndex then pass FULL path of icon to SetIconLocation. Use the following variant of GetDesktopIniIconDataFromFolder:
function GetDesktopIniIconDataFromFolder(const APath: string; var AIconIndex: Integer): string;
var
Setting: TSHFolderCustomSettings;
begin
ZeroMemory(#Setting, SizeOf(Setting));
Setting.dwSize := SizeOf(Setting);
Setting.dwMask := FCSM_ICONFILE;
SetLength(Result, MAX_PATH + 1);
Setting.pszIconFile := PChar(Result);
Setting.cchIconFile := MAX_PATH;
if Succeeded(SHGetSetFolderCustomSettings(#Setting, PChar(APath), FCS_READ)) then
begin
Result := PChar(Result);
AIconIndex := Setting.iIconIndex;
end
else
Result := '';
end;
It automatically expands variables of icon path. Also it supports IconResource parameter of desktop.ini.
Variant 2 (universal)
function GetObjectIconFileName(AParentWnd: HWND; const AName: UnicodeString; var AIndex: Integer): UnicodeString;
var
Desktop: IShellFolder;
Attr: DWORD;
Eaten: DWORD;
IDList: PItemIDList;
Parent: IShellFolder;
Child: PItemIDList;
ExtractIconW: IExtractIconW;
ExtractIconA: IExtractIconA;
AnsiResult: AnsiString;
Flags: DWORD;
Ext: UnicodeString;
BuffSize: DWORD;
P: Integer;
begin
OleCheck(SHGetDesktopFolder(Desktop));
try
Attr := SFGAO_STREAM;
OleCheck(Desktop.ParseDisplayName(AParentWnd, nil, PWideChar(AName), Eaten, IDList, Attr));
try
OleCheck(SHBindToParent(IDList, IShellFolder, Pointer(Parent), Child));
if Succeeded(Parent.GetUIObjectOf(AParentWnd, 1, Child, IExtractIconW, nil, ExtractIconW)) then
try
SetLength(Result, MAX_PATH + 1);
if (ExtractIconW.GetIconLocation(0, PWideChar(Result), MAX_PATH, AIndex, Flags) = S_OK) then
begin
Result := PWideChar(Result);
if // (Flags and GIL_NOTFILENAME = 0) and // Dont know why shell return GIL_NOTFILENAME flag
FileExists(Result) then
Exit
else
Result := '';
end
else
Result := '';
finally
ExtractIconW := nil;
end
else
if Succeeded(Parent.GetUIObjectOf(AParentWnd, 1, Child, IExtractIconA, nil, ExtractIconA)) then
try
SetLength(AnsiResult, MAX_PATH + 1);
if (ExtractIconA.GetIconLocation(0, PAnsiChar(AnsiResult), MAX_PATH, AIndex, Flags) = S_OK) then
begin
Result := UnicodeString(PAnsiChar(AnsiResult));
if // (Flags and GIL_NOTFILENAME = 0) and // Dont know why shell return GIL_NOTFILENAME flag
FileExists(Result) then
Exit
else
Result := '';
end
else
Result := '';
finally
ExtractIconA := nil;
end;
finally
CoTaskMemFree(IDList);
end;
finally
Desktop := nil;
end;
if Attr and SFGAO_STREAM <> 0 then
begin
Ext := ExtractFileExt(AName);
if (AssocQueryStringW(ASSOCF_NONE, ASSOCSTR_DEFAULTICON, PWideChar(Ext), nil, nil, #BuffSize) = S_FALSE) and (BuffSize > 1) then
begin
SetLength(Result, BuffSize - 1);
if Succeeded(AssocQueryStringW(ASSOCF_NONE, ASSOCSTR_DEFAULTICON, PWideChar(Ext), nil, PWideChar(Result), #BuffSize)) then
begin
AIndex := 0;
P := LastDelimiter(',', Result);
if P > 0 then
begin
AIndex := StrToIntDef(Copy(Result, P + 1, MaxInt), MaxInt);
if AIndex <> MaxInt then
Delete(Result, P, MaxInt)
else
AIndex := 0;
end;
Exit;
end;
end;
end;
Result := '';
end;

Related

How return the complete path of java.exe file ignoring java version installed?

I want associate my .jar file to open with java.exe using Windows registry and have a doubt about how return the complete path of java.exe file ignoring java version installed on computer.
Ex:
in my case i have:
C:\Program Files\Java\jdk1.7.0_45\bin\java.exe
then how access java.exe file ignoring this part 1.7.0_45?
uses
Windows, Registry;
function GetProgramFilesDir: string;
var
reg: TRegistry;
begin
reg := TRegistry.Create;
try
reg.RootKey := HKEY_LOCAL_MACHINE;
reg.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion', False);
Result := reg.ReadString('ProgramFilesDir');
finally
reg.Free;
end;
end;
procedure RegisterFileType(cMyExt, cMyFileType, ExeName: string);
var
reg: TRegistry;
begin
reg := TRegistry.Create;
try
reg.RootKey := HKEY_CURRENT_USER;
if reg.OpenKey('\Software\Classes\.jar', True) then
reg.WriteString('', 'MyAppDataFile');
if reg.OpenKey('\Software\Classes\MyAppDataFile', True) then
reg.WriteString('', 'myappname');
if reg.OpenKey('\Software\Classes\MyAppDataFile\DefaultIcon', True) then
reg.WriteString('', GetProgramFilesDir + '\Java\jdk1.7.0_45\bin\java.exe');
if reg.OpenKey('\Software\Classes\MyAppDataFile\shell\open\command', True)
then
reg.WriteString('', GetProgramFilesDir + '\Java\jdk1.7.0_45\bin\java.exe "%1"');
finally
reg.Free;
end;
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
end;
Don't use the Registry to discover the path to system folders, like Program Files. Use SHGetFolderPath() or SHGetKnownFolderPath() instead, eg:
function GetProgramFilesDir: string;
var
path: array[MAX_PATH] of Char;
begin
if SHGetFolderPath(0, CSIDL_PROGRAM_FILES, 0, SHGFP_TYPE_CURRENT, path) = S_OK then
Result := IncludeTrailingPathDelimiter(path)
else
Result := '';
end;
function GetProgramFilesDir: string;
var
path: PChar;
begin
if SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, 0, path) = S_OK then
begin
try
Result := IncludeTrailingPathDelimiter(path);
finally
CoTaskMemFree(path);
end;
end else
Result := '';
end;
That being said, to get the current installed path of java.exe, there are a few options you can try:
check if the %JAVA_HOME% environment variable is set.
check the HKLM\SOFTWARE\JavaSoft\Java Runtime Environment\<version> Registry key for a JavaHome value (there may be multiple <version> subkeys!).
search the %PATH% environment variable for any listed folder that has java.exe in it (there may be multiple folders!). You can parse the %PATH% yourself manually, or you can use SearchPath() with its lpPath parameter set to NULL (if you only care about the first copy of java.exe found).
function GetJavaPathViaEnvironment: string;
begin
Result := GetEnvironmentVariable('JAVA_HOME');
if Result <> '' then
begin
Result := IncludeTrailingPathDelimiter(Result) + 'bin' + PathDelim + 'java.exe';
// if not FileExists(Result) then Result := '';
end;
end;
function GetJavaPathViaRegistry: string;
const
JAVA_KEY: string = '\SOFTWARE\JavaSoft\Java Runtime Environment\';
Wow64Flags: array[0..2] of DWORD = (0, KEY_WOW64_32KEY, KEY_WOW64_64KEY);
var
reg: TRegistry;
s: string;
i: integer;
begin
Result := '';
reg := TRegistry.Create;
try
reg.RootKey := HKEY_LOCAL_MACHINE;
for i := Low(Wow64Flags) to High(Wow64Flags) do
begin
reg.Access := (reg.Access and not KEY_WOW64_RES) or Wow64Flags[i];
if reg.OpenKeyReadOnly(JAVA_KEY) then
begin
s := reg.ReadString('CurrentVersion');
if s <> '' then
begin
if reg.OpenKeyReadOnly(s) then
begin
s := reg.ReadString('JavaHome');
if s <> '' then
begin
Result := IncludeTrailingPathDelimiter(s) + 'bin' + PathDelim + 'java.exe';
// if not FileExists(Result) then Result := '' else
Exit;
end;
end;
end;
reg.CloseKey;
end;
end;
finally
reg.Free;
end;
end;
function GetJavaPathViaSearchPath: string;
var
path: array[0..MAX_PATH] of Char;
s: string;
len: DWORD;
begin
Result := '';
len := SearchPath(nil, 'java.exe', nil, Length(path), path, PPChar(nil)^);
if len <= Length(path) then
SetString(Result, path, len)
else
begin
repeat
SetLength(s, len);
len := SearchPath(nil, 'java.exe', nil, len, PChar(s), PPChar(nil)^);
until len <= Length(s);
SetString(Result, PChar(s), len);
end;
end;
function GetJavaPath: string;
begin
Result := GetJavaPathViaEnvironment;
if Result = '' then
Result := GetJavaPathViaRegistry;
if Result = '' then
Result := GetJavaPathViaSearchPath;
end;
Also, don't forget that paths with spaces must be wrapped in double-quotes. You can use Delphi's AnsiQuotedStr() to help you with that, eg:
reg.WriteString('', AnsiQuotedStr(GetJavaPath, '"') + ' "%1"');

Get the name and extension of selected file in active window by delphi

I want to get and show the name and extension of selected file in explorer by delphi7.
I use below code for show caption of active window but i need selected file name in active window.
function ActiveCaption: string;
var
Handle: THandle;
Len: LongInt;
Title: string;
begin
Result := '';
Handle := GetForegroundWindow;
if Handle <> 0 then
begin
Len := GetWindowTextLength(Handle) + 1;
SetLength(Title, Len);
GetWindowText(Handle, PChar(Title), Len);
ActiveCaption := TrimRight(Title);
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Label1.Caption := ActiveCaption;
end;
The only way I know of is to use the Active-X IShellWindows and IWebBrowser Interfaces to to that.
First, you have to import the "Microsoft Internet Controls" Active-X (via the Component Menu). By that you will get a unit called "SHDocVW_TLB". Put this unit and the ActiveX unit in your uses clause.
Than you can use the following two functions to retrieve the selected file from the window handle provided:
The first function does a rough test if the given handle belongs to an explorer window
function isexplorerwindow(exwnd: hwnd): boolean;
var
p: array[0..max_path] of Char;
begin
GetClassName(exwnd, p, SizeOf(p));
result := ((strcomp(p, 'CabinetWClass') = 0) or (strcomp(p, 'ExploreWClass') = 0));
end;
And the second function retrieves the name of the nth selected file:
function getexplorerselectedfile(exwnd: hwnd; nr: integer): string;
var
pvShell: IShellWindows;
pvWeb2: IWebBrowser2;
ovIE: OleVariant;
psp: IServiceProvider;
psb: IShellBrowser;
isw: IShellView;
ido: IDataObject;
FmtEtc: TFormatEtc;
Medium: TStgMedium;
dwcount: integer;
n: integer;
p: array[0..max_path] of Char;
s: string;
found: boolean;
begin
found := false;
result := '';
s :='';
try
pvShell := CoShellWindows.Create;
for dwcount := 0 to Pred(pvShell.count) do
begin
ovIE := pvShell.Item(dwcount);
if (ovIE.hwnd = exwnd) or ((exwnd = 0) and isexplorerwindow(ovIE.hwnd)) then
begin
found := true;
if (IDispatch(ovIE).QueryInterface(IWebBrowser2, pvWeb2) = S_OK) then
begin
psp := (pvWeb2 as IServiceProvider);
psp.QueryService(IID_IShellBrowser, IID_IShellBrowser, psb);
psb.QueryActiveShellView(isw);
if isw.GetItemObject(SVGIO_SELECTION, IDataObject, pointer(ido)) = S_OK then
begin
try
FmtEtc.cfFormat := CF_HDROP;
FmtEtc.ptd := nil;
FmtEtc.dwAspect := DVASPECT_CONTENT;
FmtEtc.lindex := -1;
FmtEtc.tymed := TYMED_HGLOBAL;
ido.GetData(FmtEtc, Medium);
GlobalLock(Medium.hGlobal);
try
n := DragQueryFile(Medium.hGlobal, $FFFFFFFF, nil, 0);
if nr < n then
begin
DragQueryFile(Medium.hGlobal, nr, p, max_path);
s := strpas(p);
end;
finally
DragFinish(Medium.hGlobal);
GlobalUnLock(Medium.hGlobal);
end;
finally
ReleaseStgMedium(Medium);
end;
end;
pvWeb2 := nil;
end;
end;
ovIE := Unassigned;
if found then
break;
end;
pvShell := nil;
finally
result := s;
end;
end;
To test this code create a new project and place a button and a memo on the form.
Add the following units to the uses clause:
USES SHDocVW_TLB, ShlObj, activex, shellapi;
And add this code to the button event handler:
PROCEDURE TForm2.Button1Click(Sender: TObject);
VAR
wnd, exwnd: hwnd;
n: integer;
s: STRING;
BEGIN
exwnd := 0;
wnd := getwindow(getdesktopwindow, gw_child);
REPEAT
IF isexplorerwindow(wnd) THEN
BEGIN
exwnd := wnd;
break;
END;
wnd := getwindow(wnd, gw_hwndnext);
UNTIL (wnd = 0) OR (exwnd <> 0);
IF exwnd <> 0 THEN
BEGIN
n := 0;
REPEAT
s := getexplorerselectedfile(exwnd, n);
memo1.Lines.Add(s);
inc(n);
UNTIL s = '';
END;
END;
If you press the button, the memo will contain the selected files of the first open explorer window it finds. Of course you should have an explorer window open with at least one file selected.

Delphi - Get folder icon

How i can get the icon of special folder for example Desktop
function GetFolderIcon( FName: string ): integer;
var
FInfo: TSHFileInfo;
begin
if SHGetFileInfo(pChar(FName), FILE_ATTRIBUTE_NORMAL, FInfo, SizeOf(FInfo),
SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_USEFILEATTRIBUTES or SHGFI_PIDL or SHGFI_ICON or SHGFI_OPENICON ) <> 0 then begin
Result := FInfo.iIcon
end
else
Result := -1;
end;
GetFolderIcon(GetSpecialFolder(CSIDL_DESKTOP)); retern -1
CSIDL_DESKTOP is the "virtual folder that represents the Windows desktop, the root of the namespace". As such, it does not have a filesystem path that you can pass to SHGetFileInfo(). You are probably thinking of CSIDL_DESKTOPDIRECTORY instead, which is "The file system directory used to physically store file objects on the desktop (not to be confused with the desktop folder itself)":
GetFolderIcon(GetSpecialFolder(CSIDL_DESKTOPDIRECTORY));
When calling SHGetFileInfo(), you can specify the SHGFI_PIDL flag so you can pass a PIDL instead of a filesystem path. That allows for querying virtual items. Your code is already using SHGFI_PIDL, but it is not using any PIDLs, which means you are using SHGetFileInfo() incorrectly to begin with.
Try this:
uses
..., ShlObj, SHFolder;
function GetSpecialFolderPath(FolderID: Integer): String;
var
Path: array[0..MAX_PATH] of Char;
begin
if SHGetFolderPath(0, FolderID, nil, SHGFP_TYPE_CURRENT, Path) = 0 then
Result := Path
else
Result := '';
end;
function GetSpecialFolderPidl(FolderID: Integer): PItemIDList;
begin
Result := nil;
SHGetSpecialFolderLocation(0, FolderID, Result);
end;
function GetFolderIcon( FName: String ): integer; overload;
var
FInfo: TSHFileInfo;
begin
ZeroMemory(#FInfo, SizeOf(FInfo));
if SHGetFileInfo(PChar(FName), FILE_ATTRIBUTE_NORMAL, FInfo, SizeOf(FInfo), SHGFI_USEFILEATTRIBUTES or SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_ICON or SHGFI_OPENICON ) <> 0 then
begin
Result := FInfo.iIcon;
if FInfo.hIcon <> 0 then DestroyIcon(FInfo.hIcon);
end else
Result := -1;
end;
function GetFolderIcon( Pidl: PItemIDList ): integer; overload;
var
FInfo: TSHFileInfo;
begin
if SHGetFileInfo(PChar(Pidl), FILE_ATTRIBUTE_NORMAL, FInfo, SizeOf(FInfo), SHGFI_PIDL or SHGFI_USEFILEATTRIBUTES or SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_ICON or SHGFI_OPENICON ) <> 0 then
begin
Result := FInfo.iIcon;
if FInfo.hIcon <> 0 then DestroyIcon(FInfo.hIcon);
end
else
Result := -1;
end;
var
Icon: Integer;
Pidl: PItemIDList;
begin
Icon := -1;
Pidl := GetSpecialFolderPidl(CSIDL_DESKTOP);
if Pidl <> nil then
try
Icon := GetFolderIcon(Pidl);
finally
CoTaskMemFree(Pidl);
end;
end;
var
Icon: Integer;
Path: string;
begin
Icon := -1;
Path := GetSpecialFolderPath(CSIDL_DESKTOPDIRECTORY);
if Path <> '' then
Icon := GetFolderIcon(Path);
end;

How can I get all installed components inside IDE? (Delphi)

How can I get all installed components in TStrings?
I think this code work only within packages:
uses TypInfo, ToolIntf, Exptintf;
procedure GetComponentNames(lst: TStrings);
var
i, k: Integer;
CRef: TClass;
strName: ShortString;
begin
lst.Clear;
for i := 0 to ToolServices.GetModuleCount-1 do
begin
for k := 0 to ToolServices.GetComponentCount(i)-1 do
begin
CRef := TClass(GetClass(ToolServices.GetComponentName(i, k)));
while CRef <> nil do
begin
strName := CRef.ClassName;
if lst.IndexOf(strName) = -1 then
lst.Add(strName);
if str <> 'TComponent' then
CRef := CRef.ClassParent
else
CRef := nil;
end;
end;
end;
end;
Or:
uses ToolsApi;
{....}
var
a, i: Integer;
begin
with (BorlandIDEServices as IOTAPackageServices) do
begin
for a := 0 to GetPackageCount - 1 do
begin
for i := 0 to GetComponentCount(a) - 1 do
begin
{get each component name with GetComponentName(a, i);}
// DoSomething
end;
end;
end;
end;
Thanks for help.
This example doesn't use the OpenAPI, it uses the Registry. It works but it also lists non-visual components amongst other hidden items.
procedure GetComponentNames(lst: TStrings);
var
i, j, iPos: Integer;
Reg: TRegistry;
sComponent: String;
slValues, slData: TStrings;
begin
Reg := TRegistry.Create;
slValues := TStringList.Create;
slData := TStringList.Create;
try
Reg.RootKey := HKEY_CURRENT_USER;
Reg.OpenKey('Software\Borland\Delphi\6.0\Palette', False); // Change reg key where appropriate
Reg.GetValueNames(slValues);
for i := 0 to Pred(slValues.Count) do
begin
lst.Append(slValues[i]);
lst.Append('----------');
slData.Delimiter := ';';
slData.DelimitedText := Reg.ReadString(slValues[i]);
for j := 0 to Pred(slData.Count) do
begin
sComponent := slData[j];
iPos := Pos('.', sComponent);
if (iPos > 0) then
Delete(sComponent, 1, iPos);
lst.Append(sComponent);
end;
end;
finally
slData.Free;
slValues.Free;
Reg.Free;
end; {try..finally}
end;
I'm not saying this is ideal but it does give you a list and a headstart.

Help me fix a Delphi function that counts Registry items

I have a function that counts registry items and added a new option to also retrieve the registry items names and values. Unfortunately I can't seem to understand why only the first item for each registry key is retrieved and why the values all have the same name.
Anyone see any apparent problem with the code below?
function CountRegistryItems(Root: HKEY; SubKey: string; var KeysCount: Integer;
var ValuesCount: Integer; GetValues: Boolean; const List: TStrings): Boolean;
type
TRegKeyInfo = record
NumSubKeys: Integer;
MaxSubKeyLen: Integer;
NumValues: Integer;
MaxValueLen: Integer;
MaxDataLen: Integer;
FileTime: TFileTime;
end;
var
Info: TRegKeyInfo;
i: integer;
SL: TStringList;
Status: Integer;
Key: HKEY;
Len: DWORD;
S: string;
PartialKeysCount: Integer;
PartialValuesCount: Integer;
KeyType, MaxValLen, MaxValNameLen, ValNameLen, ValLen: Cardinal;
ValName, Val: PChar;
Size: DWORD;
ValueName: string;
begin
KeysCount := 0;
ValuesCount := 0;
Result := False;
if GetValues and (List <> nil) then
List.BeginUpdate;
SL := TStringList.Create;
Try
// open current key
Status := RegOpenKeyEx(Root, PChar(SubKey), 0, KEY_READ or KEY_ENUMERATE_SUB_KEYS, Key);
if Status = ERROR_SUCCESS then
Try
// get key info
FillChar(Info, SizeOf(TRegKeyInfo), 0);
Status := RegQueryInfoKey(Key, nil, nil, nil, #Info.NumSubKeys,
#Info.MaxSubKeyLen, nil, #Info.NumValues, #Info.MaxValueLen,
#Info.MaxDataLen, nil, #Info.FileTime);
if Status = ERROR_SUCCESS then
begin
Result := True;
// NEW CODE
if GetValues and (List <> nil) then
begin
MaxValNameLen := Info.MaxValueLen*2+3;
MaxValLen := Info.MaxDataLen+1;
// Get values
GetMem(ValName, MaxValNameLen);
GetMem(Val, MaxValLen);
//if Info.NumValues <> 0 then
for i := 0 to Info.NumValues-1 do
begin
// Clear buffers
ValName^ := #0;
ValNameLen := MaxValNameLen;
if Val <> nil then
begin
Val^ := #0;
ValLen := MaxValLen;
end;
// Get value information
if RegEnumValue(Root, i, ValName, ValNameLen, nil, #KeyType,
PByte(Val), #ValLen) = ERROR_SUCCESS then
begin
//if ((KeyType = REG_SZ) or (KeyType = REG_MULTI_SZ)
//or (KeyType = REG_EXPAND_SZ)) then
begin
if ValName^ <> #0 then
List.Add(ValName + '=' + Val)
else
List.Add('Default' + '=' + Val);
end;
end;
end;
// Free buffers
//FreeMem(ValName);
//if Val <> nil then FreeMem(Val);
end;
// END NEW CODE
// enum subkeys
SetString(S, nil, Info.MaxSubKeyLen + 1);
for i := 0 to Info.NumSubKeys - 1 do
begin
Len := Info.MaxSubKeyLen + 1;
Status := RegEnumKeyEx(Key, i, PChar(S), Len, nil, nil, nil, nil);
if Status <> ERROR_SUCCESS then Continue;
SL.Add(PChar(S));
end;
end;
if Info.NumSubKeys > 0 then Inc(KeysCount, Info.NumSubKeys);
if Info.NumValues > 0 then Inc(ValuesCount, Info.NumValues);
Finally
RegCloseKey(Key);
End;
// search subkeys
if SL.Count > 0 then
begin
for i := 0 to SL.Count - 1 do
begin
Application.ProcessMessages;
PartialKeysCount := 0;
PartialValuesCount := 0;
CountRegistryItems(Root, SubKey + '\' + SL[i], PartialKeysCount,
PartialValuesCount, GetValues, List);
KeysCount := KeysCount + PartialKeysCount;
ValuesCount := ValuesCount + PartialValuesCount;
end;
end;
Finally
SL.Free;
if GetValues and (List <> nil) then
List.EndUpdate;
End;
end;
You can use TRegistry:
uses Registry;
function CountRegistryItems(Root: HKEY;
SubKey: string;
var KeysCount: Integer;
var ValuesCount: Integer;
GetValues: Boolean;
const List: TStrings): Boolean;
var
Reg : TRegistry;
KeyInfo : TRegKeyInfo;
begin
Reg := TRegistry.Create;
try
Reg.RootKey := Root;
if Reg.OpenKey(SubKey,False) then
begin
Reg.GetKeyInfo(KeyInfo);
ValuesCount := KeyInfo.NumValues;
KeysCount := KeyInfo.NumSubKeys;
if (GetValues) and (Assigned(List)) then
begin
List.Clear;
Reg.GetValueNames(List);
end;
end;
Result := True;
finally
Reg.Free;
end;
end;
you need to use the Key variable instead of Root when calling RegEnumValue function.

Resources