I'm trying to list all the resources of my program with the name of the resource and resource type as "RT_BITMAP" or any other.
The code :
var
Form1: TForm1;
list_resources: string;
function EnumResNameProc(lpszName: PChar; lParam: integer; lpszType: PChar;
hModule: Cardinal): BOOL;
begin
list_resources := list_resources + sLineBreak + lpszName + ' - ' + lpszType;
Result := True;
end;
procedure TForm1.btnListResourcesClick(Sender: TObject);
begin
EnumResourceNames(0, RT_RCDATA, @EnumResNameProc, 0);
Memo1.Lines.Add(list_resources);
end;
The code work good but never show the type of resource , what is the problem ?
The first problem with your code is that the callback function has the wrong calling convention and indeed the wrong signature. It should be declared like this:
function EnumResNameProc(hModule: HMODULE; lpszType, lpszName: PChar;
lParam: LONG_PTR): BOOL; stdcall;
The output that your code was producing was completely accidental. It's really important that you get the signatures of these function correct. I don't know where your signature came from. It looks like you just made it up! The documentation on MSDN has the correct signature. Embarcadero make things hard by declaring the API functions that accept callbacks in a way that means type checking for the signatures is neglected. So the onus falls on your. Read the documentation very carefully.
Once you have fixed that problem, there is still more to be done. Resource types, and indeed resource names, can either be integers or strings. The convention is that values < 65536 are interpreted as integers, otherwise the value is a pointer to a null-terminated character array. Rather than hard-code that magic constant, you can instead call Is_IntResource, the Delphi translation of the Windows macro IS_INTRESOURCE.
In your case you will be receiving named resources, but resource types that are actually integer values. From the Windows unit:
const
RT_CURSOR = MakeIntResource(1);
RT_BITMAP = MakeIntResource(2);
RT_ICON = MakeIntResource(3);
RT_MENU = MakeIntResource(4);
RT_DIALOG = MakeIntResource(5);
RT_STRING = MakeIntResource(6);
RT_FONTDIR = MakeIntResource(7);
RT_FONT = MakeIntResource(8);
RT_ACCELERATOR = MakeIntResource(9);
RT_RCDATA = System.Types.RT_RCDATA; //MakeIntResource(10);
DIFFERENCE = 11;
RT_GROUP_CURSOR = MakeIntResource(DWORD(RT_CURSOR) + DIFFERENCE);
RT_GROUP_ICON = MakeIntResource(DWORD(RT_ICON) + DIFFERENCE);
RT_VERSION = MakeIntResource(16);
RT_DLGINCLUDE = MakeIntResource(17);
RT_PLUGPLAY = MakeIntResource(19);
RT_VXD = MakeIntResource(20);
RT_ANICURSOR = MakeIntResource(21);
RT_ANIICON = MakeIntResource(22);
RT_HTML = MakeIntResource(23);
RT_MANIFEST = MakeIntResource(24);
The other convention at play is that you use a # symbol to indicate a numeric identifier. So you could adopt the following policy:
Is_IntResource returns True, then convert the numeric value to string and prefix with #.The code is quite simple:
function ResourceNameToString(lpszName: PChar): string;
begin
if Is_IntResource(lpszName) then
Result := '#' + IntToStr(NativeUInt(lpszName))
else
Result := lpszName;
end;
It's imperative that you do this, for names as well as types. Otherwise your code will fail with runtime access violation errors when it tries to de-reference a pointer that is actually representing an integer. The code in the question will fail that way if you fixed the callback signature but make no further changes.
If you want your code to be more helpful you will detect the pre-defined resource types and give them special treatment.
function ResourceTypeToString(lpszType: PChar): string;
begin
case NativeUInt(lpszType) of
NativeUInt(RT_CURSOR):
Result := 'RT_CURSOR';
NativeUInt(RT_BITMAP):
Result := 'RT_BITMAP';
NativeUInt(RT_RCDATA):
Result := 'RT_RCDATA';
// etc.
else
Result := ResourceNameToString(lpszType);
end;
end;
I'll let you fill in the missing values.
Put it all together like so:
{$APPTYPE CONSOLE}
uses
SysUtils, Windows;
function ResourceNameToString(lpszName: PChar): string;
begin
if Is_IntResource(lpszName) then
Result := '#' + IntToStr(NativeUInt(lpszName))
else
Result := lpszName;
end;
function ResourceTypeToString(lpszType: PChar): string;
begin
case NativeUInt(lpszType) of
NativeUInt(RT_CURSOR):
Result := 'RT_CURSOR';
NativeUInt(RT_BITMAP):
Result := 'RT_BITMAP';
NativeUInt(RT_RCDATA):
Result := 'RT_RCDATA';
// etc.
else
Result := ResourceNameToString(lpszType);
end;
end;
function EnumResNameProc(hModule: HMODULE; lpszType, lpszName: PChar;
lParam: LONG_PTR): BOOL; stdcall;
begin
Writeln(ResourceTypeToString(lpszType) + ' - ' + ResourceNameToString(lpszName));
Result := True;
end;
begin
EnumResourceNames(0, RT_RCDATA, @EnumResNameProc, 0);
Readln;
end.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With