Clarion Resource RT_String(s) - I've noticed a numbering issue with EnumResourceNamesA - Clarion or API problem?

So the Runtime stuffs the Trappable Runtime messages into the Resource section RT_STRING.

The api EnumResourceLanguagesA function (winbase.h) - Win32 apps | Microsoft Learn
is returning the RT_String but the LPCTSTR lpszName returned in callback procedure EnumResLangProc callback function (Windows) | Microsoft Learn dont match up.

EG. I get 1 from the Enum api but when I pass it to LoadStringA function (winuser.h) - Win32 apps | Microsoft Learn
its turns out its a null cstring, in other words it doesnt exist!
2 returns File Not Found
3 returns Path Not Found
works fine up to 7, its missing 8 which is Insufficient Memory, missing 15 which is Bad Drive (or Invalid Drive in the help docs),
shows 19 through to 26 which returns null cstrings and doesnt correlate to any Trappable Runtime Error.
It misses 30 which is Entry not found.

But I can manually specify the numbers myself, which is how I found that 15 is the string Bad Drive and came to the conclusion the Trappable Runtime Error strings are stuffed in the EXE/Dll Resource Strings.

I did find this Raymond Chen blog post The format of string resources - The Old New Thing (microsoft.com)
which is not admitting, but is suggesting an alternative way to get the Resource strings out of the exe/dll.

So does anyone know if this is a bug with the EnumResourceLanguagesA api?

TIA

Perhaps a resource viewer would help you in your quest for knowledge.

I’ll see if I can find that in the IDE.

Edit. Where do I find this in the C11 IDE?

I don’t think you would find something like that in the C11 IDE. Maybe Visual Studio. There are freeware viewers, but I’ve not used any.

Is this the product?
PE Explorer: PE Editor, EXE File Editor Tool, DLL Reader, Disassembler, Delphi Resource EXE DLL Editing Software. (heaventools.com)

Yes, that’s the one. I got it when Jane had posted a “deal of the day” coupon with a big discount for it many years ago. But I use it almost every day. I don’t think they’ve had any updates for many years.

They also have “just” a resource editor. Edits 64 bit resources too http://www.heaventools.com/resource-tuner.htm

Ok, well I’ve been writing one that works from a template. :innocent:
I just wasnt sure, if this is a bit of a bug in the api or the way its stored in the resource section of the PE.

TLDR the RT_Strings are unicode but when examining the memory, I cant find the strings!

1 Like

If you’d look at my screenshot, you’d see that it’s working OK.

So its looking like the windows api is not working properly, which is another added to the list of api’s not working properly.

Edit. I think I’ll process the strings as per Raymondo’s blog post The format of string resources - The Old New Thing (microsoft.com), ie process the RT_Strings seperately and just run through the ID numbers looking for strings, because the EnumResourceNames is just not working properly.

Edit2.

Well Raymondos blog doesnt work.

Whats works but is clunky is
load the library
RT_String Equate(6)
Loc:Cstring = ‘MyEXE.EXE’
Loc:LibraryHandle = LoadLibraryA(address(Loc:Cstring))
Loc:LanguageWord = 1033 !Lang_English + Sublang_English_US
Loc:ResourceBlockHandle = FindResourceExA(Loc:LibraryHandle,RT_String,X,Loc:LanguageWord)
Loc:ResourceHandle = LoadResourceA(Loc:LibraryHandle,Loc:ResourceBlockHandle)
Loc:ResourceStartByteAddress = LockResource(Loc:ResourceHandle)

Loc:Counter = 0
Loop 10000 times
Loc:Counter +=1
Loc:Address = address(Loc:ResourceDisplayCString)
Loc:Size = size(Loc:ResourceDisplayCString)
Loc:RVlong = LoadStringA(Loc:LibraryHandle,Loc:Counter,Loc:Address,Loc:Size)
End

Then I can see all the RT_Strings, using the loop. Thing is the Loc:ResourceBlockHandle points to the block of RT_Strings in memory starting at the NameID. So the X thats returned by EnumResourceNamesA gives me the NameID or block of 16 as Raymondo refers to it, and in memory I can see a number before each unicode string, but they dont seem to tie up to any pattern that I can work out, hence the loop 10000 times cludge.

I had hoped that by locking the resource, the loadstring would be forced to start at the Loc:ResourceStartByteAddress and then loop 1 to 16 for that block (or Hungarian notation 0-15), but it doesnt work like that. :face_with_raised_eyebrow:

This one is bugging me, but there’s alot more strings in the resource than whats declared in the trappable runtime errors in the help docs, they are all clarion error messages though.

Resource String 543 - Slider Controls are only available n 32bit programs, is an interesting message, because I cant see any slider control in the clarion control line up! And that message is in both the C6 and C11 runtime.

Edit.

Figured it out.

Loc:Counter = 0
Loop 16 times
Loc:Counter +=1
Loc:Address = address(Loc:ResourceDisplayCString)
Loc:Size = size(Loc:ResourceDisplayCString)
Loc:ResourceBlockStringID = ((X * 16) - 16) + Loc:Counter
Loc:RVlong = LoadStringA(Loc:LibraryHandle,Loc:ResourceBlockStringID ,Loc:Address,Loc:Size)
End

Where X is the value in the Res Name Name column in the attached images.

Jam Software’s Treesize Free is German, so I can see its calculating the German language resources correctly, but I need to blank them when there is no LanguageID. Their Resource string data is interesting (3rd app across). The Clarion debugger has its own string data messages as well.

Oh and this resource editor is called from a Clarion template inside the appgen, just to show what can be done with the templates! :stuck_out_tongue:

Simple program to load list of resources without language filtering.
test.clw (7.7 KB)

3 Likes

Highly efficient compact code! All the values match what I’m seeing in my app. Different techniques to access the data, but half the problem with the Windows API’s is trying to understand their documents. It wasnt until I’d put the loop 10000 times in and scanned for every resource string, that I could see the Res Name value * 16 gives the trappable errorcode number and string.

In my example’s text there is an accidental typo in line

?ResTree {PROP:LineHeight} = ?RedTree {PROP:FontSize} + 2

?RedTree is unknown identifier. Correct text is

?ResTree {PROP:LineHeight} = ?ResTree {PROP:FontSize} + 2

The compiler hilighted that for me. What is interesting, is your use of the attribute Private with the cstrings. In the help docs, it says Private can only be used with Class properties. Now whilst the app is an app and not a class, the compiler doesnt complain about the use of Private in an app, so are the help docs wrong or the compiler?

http://clarion.help/doku.php?id=private_set_variable_private_to_a_class_module_.htm

QUOTE
“PRIVATE is also valid when used with static (threaded or non-threaded) variables outside of a CLASS structure. If a static variable is declared with the PRIVATE attribute, the compiler generates it without a public external name. Hence, it can only be used by procedures defined in the same source module.”

That text is also on C6.2 help file, but not on C5.5

Nice example Also, neat captures Richard.

FWIW, the usage for those error messages through ERROR() can be changed to localized versions with LOCALE, .ENV files and the new technique coming…
See this example program:

  PROGRAM
  MAP
  END
  CODE
  MESSAGE('/' & EVALUATE('myFunc()') & '/' & ERRORCODE() &  '/'  & ERROR() & '/')
  SYSTEM {PROP:Codepage} = 1253        !Greek 
  SYSTEM {PROP:Locale} = 1032          !Greek
  MESSAGE('/' & EVALUATE('myFunc()') & '/' & ERRORCODE() &  '/'  & ERROR() & '/')
  LOCALE('CLACODEPAGE', 1253)
  LOCALE('CLALCID', 1032)
  MESSAGE('/' & EVALUATE('myFunc()') & '/' & ERRORCODE() &  '/'  & ERROR() & '/')
  LOCALE('CLAMSG1011','BIND PROBLEM')
  MESSAGE('/' & EVALUATE('myFunc()') & '/' & ERRORCODE() &  '/'  & ERROR() & '/')

Produces sequentially the following messages (C11.13244):
//1011/BIND has not been called for myFunc/
//1011/BIND has not been called for myFunc/
//1011/BIND has not been called for myFunc/
//1011/BIND PROBLEM myFunc/

https://clarion.help/doku.php?id=internationalization.htm

I wonder if this new technique will have the resources strings linked as shown on the screen captures (eg like English/German in Treesize showed by Richard) and which languages would include.

Among other things, the PRIVATE attribute on static variables make work of the linker slightly more easy.

Setting of codepage and LCID values for the program by the property assignment to SYSTEM or by the call to LOCALE not works in C11 - planned numbering of versions has been changed.

1 Like