 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
Lexikos
Joined: 17 Oct 2006 Posts: 7295 Location: Australia
|
Posted: Mon Nov 10, 2008 12:23 pm Post subject: EnumResources |
|
|
EnumResources( Filename, Type [, LabelOrFunctionName ] )
Enumerates names of resources of a given type in a given executable (exe, dll, icl, etc.) file.
| Code: | ; EnumResources( Filename, Type [, LabelOrFunctionName ] )
; Requires AHK v1.0.47.06.
; Type:
; See MSDN: Resource Types -
; http://msdn.microsoft.com/en-us/library/ms648009(VS.85).aspx
EnumResources(Filename, Type, Label="")
{
hmod := DllCall("GetModuleHandle", "str", Filename)
; If the DLL isn't already loaded, load it as a data file.
loaded := !hmod
&& hmod := DllCall("LoadLibraryEx", "str", Filename, "uint", 0, "uint", 0x2)
enumproc := RegisterCallback("EnumResources_callback","F")
VarSetCapacity(param,8,0), NumPut(&Label, param)
; Enumerate the resources.
DllCall("EnumResourceNames", "uint", hmod, "uint", Type, "uint", enumproc, "uint", ¶m)
DllCall("GlobalFree", "uint", enumproc)
; If we loaded the DLL, free it now.
if loaded
DllCall("FreeLibrary", "uint", hmod)
return NumGet(param,4)
}
EnumResources_callback(hModule, lpszType, lpszName, lParam)
{
NumPut(1 + NumGet(lParam+4), lParam+4)
if (lpszName >> 16 != 0)
lpszName := DllCall("MulDiv","int",lpszName,"int",1,"int",1,"str")
Label := DllCall("MulDiv","int",NumGet(lParam+0),"int",1,"int",1,"str")
if Label !=
{
if IsLabel(Label)
{ ; ErrorLevel = resource ID or name.
ErrorLevel := lpszName
gosub %Label%
}
else
%Label%(lpszName) ; Dynamic function call. Requires AHK v1.0.47.06.
}
return ErrorLevel!="#stop"
}
|
Examples:
| Code: | NumIcons := EnumResources(A_AhkPath, 14, "EnumGroupIcons")
MsgBox % NumIcons " icons:`n`n" Icons
ExitApp
EnumGroupIcons:
Icons .= ErrorLevel "`n"
return |
| Code: | NumIcons := EnumResources(A_AhkPath, 14, "EnumGroupIcons_callback")
MsgBox % NumIcons " icons:`n`n" Icons
ExitApp
EnumGroupIcons_callback(resource_name) {
global Icons .= resource_name "`n"
} |
|
|
| Back to top |
|
 |
Obi-Wahn
Joined: 20 Apr 2006 Posts: 75 Location: Vienna
|
Posted: Mon Nov 10, 2008 5:36 pm Post subject: |
|
|
Hi Lexikos!
Thanks for sharing the Function. I'm trying to understand the code, but I have no Idea what the RegisterCallback() is.
And even without this knowledge, would it be possible to merge these two functions together to one function (eg. with a return-given list or a list with byref). I think the usage of the function would be more clear...
Thanks for the Function
O-W |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7295 Location: Australia
|
Posted: Tue Nov 11, 2008 8:16 am Post subject: |
|
|
As the callback function is not called directly from script, it would not be possible to pass a ByRef parameter. It allows only a single user-defined parameter containing a 32-bit integer. The script uses this to pass the address of a "structure" containing data relevant to the enumeration - i.e. the number of resources enumerated so far and a pointer to the LabelOrFunctionName string.
There are a few ways I can think of to build a list of resource names and return it to the caller:
- Global or Static variables.
- If a single variable is used, only one enumeration can be performed at a time. If a second enumeration is started via an interrupting thread (i.e. hotkey), it could "corrupt" the result of the first enumeration.
- A dynamically-named variable could be used, based on an integer passed in the data structure. This would not have the above problem, but one would have to take care to reuse variables - without conflicting with any current enumerations - as a long-running script could otherwise consume more and more memory.
- (Example 1) Memory could be allocated within the callback and returned via the data structure.
- (Example 2) A variable reference (ByRef) may be passed via the data structure using LowLevel functions.
- Pass a pointer to a structure or object capable of storing a list of values. For instance, A_Array or Scripting.Dictionary.
I'll work up an "example" or two.
(Edit)
Example 1 - use Global(Re)Alloc to allocate memory to store the list. A pointer to this memory is returned via the param structure. For simplicity, it reallocates the list for each item. Reallocation can be slow - a more efficient function would estimate the required space for the list and/or reallocate in larger increments to reduce the number of reallocations.
| Code: | list := GetResourceNames(A_AhkPath, 14)
MsgBox % ErrorLevel " icons:`n`n" list
GetResourceNames_callback(hModule, lpszType, lpszName, lParam)
{
; Increment the counter for this enumeration.
NumPut(1 + NumGet(lParam+0), lParam+0)
; lpszName may be an integer identifier or a pointer to a string.
; If it is a pointer to a string, retrieve the string.
if (lpszName >> 16 != 0)
lpszName := DllCall("MulDiv","int",lpszName,"int",1,"int",1,"str")
; Retrieve variables from the structure.
listptr := NumGet(lParam+4)
listlen := NumGet(lParam+8)
if listptr
{
; Reallocate the list to make room for the name of this resource.
listptr := DllCall("GlobalReAlloc", "uint", listptr, "uint", listlen+StrLen(lpszName)+2, "uint", 2)
; Append delimiter.
NumPut(Asc("`n"), listptr + listlen, "char")
listlen += 1
}
else
{
; Allocate memory for the first resource name.
listptr := DllCall("GlobalAlloc", "uint", 0, "uint", StrLen(lpszName)+1)
}
; Append the name of this resource onto the list.
DllCall("RtlMoveMemory", "uint", listptr + listlen, "str", lpszName, "uint", StrLen(lpszName)+1)
listlen += StrLen(lpszName)
; Update the structure.
NumPut(listptr, lParam+4)
NumPut(listlen, lParam+8)
; Continue enumeration.
return true
}
GetResourceNames(Filename, Type)
{
; Get a handle to a loaded DLL.
hmod := DllCall("GetModuleHandle", "str", Filename)
; If the DLL isn't already loaded, load it as a data file.
loaded := !hmod
&& hmod := DllCall("LoadLibraryEx", "str", Filename, "uint", 0, "uint", 0x2)
; Create a callback to be called by EnumResourceNames().
enumproc := RegisterCallback("GetResourceNames_callback","F")
; Create a structure to pass to the callback.
VarSetCapacity(param,12,0)
; Enumerate the resources.
DllCall("EnumResourceNames", "uint", hmod, "uint", Type, "uint", enumproc, "uint", ¶m)
; Free the memory used by the callback.
DllCall("GlobalFree", "uint", enumproc)
; If we loaded the DLL, free it now.
if loaded
DllCall("FreeLibrary", "uint", hmod)
; Retrieve the list of resource names.
if listptr := NumGet(param,4)
{
list := DllCall("MulDiv","int",listptr,"int",1,"int",1,"str")
DllCall("GlobalFree", "uint", listptr)
}
; Else: No resources or an error occurred. Leave list blank.
; Retrieve the number of resources enumerated.
ErrorLevel := NumGet(param)
return list
}
|
Example 2 - use LowLevel functions to allow the callback to refer to the main function's local variables. AutoHotkey handles memory allocation for the variables - typically more efficiently than example 1.
| Code: | list := GetResourceNames(A_AhkPath, 14)
MsgBox % ErrorLevel " icons:`n`n" list
GetResourceNames_callback(hModule, lpszType, lpszName, lParam)
{
; lpszName may be an integer identifier or a pointer to a string.
; If it is a pointer to a string, retrieve the string.
if (lpszName >> 16 != 0)
lpszName := DllCall("MulDiv","int",lpszName,"int",1,"int",1,"str")
; Retrieve variable references from the structure.
; list and count become our local aliases for list and count in GetResourceNames().
__alias(list, NumGet(lParam+0)), __alias(count, NumGet(lParam+4))
; Append the name of this resource to the list.
list .= (count ? "`n" : "") . lpszName
count += 1
; Continue enumeration.
return true
}
GetResourceNames(Filename, Type)
{
; Requires LowLevel.ahk - http://www.autohotkey.com/forum/topic26300.html
LowLevel_init()
; Get a handle to a loaded DLL.
hmod := DllCall("GetModuleHandle", "str", Filename)
; If the DLL isn't already loaded, load it as a data file.
loaded := !hmod
&& hmod := DllCall("LoadLibraryEx", "str", Filename, "uint", 0, "uint", 0x2)
; Create a callback to be called by EnumResourceNames().
enumproc := RegisterCallback("GetResourceNames_callback","F")
; Create a structure to pass to the callback.
VarSetCapacity(param,8,0)
; Store low-level variable references in the structure.
NumPut(__getVar(count), NumPut(__getVar(list), param))
; Enumerate the resources.
DllCall("EnumResourceNames", "uint", hmod, "uint", Type, "uint", enumproc, "uint", ¶m)
; Free the memory used by the callback.
DllCall("GlobalFree", "uint", enumproc)
; If we loaded the DLL, free it now.
if loaded
DllCall("FreeLibrary", "uint", hmod)
; list and count are assigned by the callback function.
ErrorLevel := count
return list
}
|
|
|
| Back to top |
|
 |
Obi-Wahn
Joined: 20 Apr 2006 Posts: 75 Location: Vienna
|
Posted: Tue Nov 11, 2008 1:59 pm Post subject: |
|
|
Hi!
Thanks Lexikos, thats quite exactly the way I like it.
I've 2 (finally) Questions:
1.) In the first post, you link to the MSDN-Site, which contains a list of resource types that can be queried. But at the MSDN Site, the Types are Names (like RT_BITMAP), but the script doesn't accept this style. It seems like, you have to pass a Number as equivalent of the Style-Name. But there are no numbers on the MSDN Site. Does I have to guess the numbers, or exists a List of Numbers? Even with google, I found only lists with the Names, not numbers.
2.) Thats a What-If question. What if the RegisterCallback points on the same function as launched from? The callpack function has 4 parameters (which all needed, i guess). Woud it be possible to make a if-question (like if (lParam != "") ) and execute the code from there?
Should be the same, or am I wrong?
Thanks
O-W
EDIT: I tried to pack it into one function, an the RegisterCallback calls the function 7 times (using resource A_AhkPath) with Icon-Type. BUT: There are no Names of the resource.
| Code: | MsgBox % "Icon Names:`n" GetResNames(A_AhkPath, 14)
;MsgBox % "Icon Names:`n" GetResourceNames(A_AhkPath, 14, C) "`n`n" C " Icons found"
ExitApp
GetResNames(File, Type, Count="", lParam="") {
If (File = "0") {
NumPut(1 + NumGet(lParam+0), lParam+0)
If (Count >> 16 != 0)
Count := DllCall("MulDiv", Int, Count, Int, 1, Int, 1, Str)
listptr := NumGet(lParam+4), listlen := NumGet(lParam+8)
If listptr {
listptr := DllCall("GlobalReAlloc", UInt, listptr, UInt, listlen+StrLen(Count)+2, UInt, 2)
NumPut(Asc("`n"), listptr + listlen, "char"), listlen += 1
} Else
listptr := DllCall("GlobalAlloc", UInt, 0, UInt, StrLen(Count)+1)
DllCall("RtlMoveMemory", UInt, listptr + listlen, Str, Count, UInt, StrLen(Count)+1)
listlen += StrLen(Count)
NumPut(listptr, lParam+4), NumPut(listlen, lParam+8)
;Msgbox, , , here, 1 ; Simple debug, msgbox appears 7 times
Return, true
}
If (!hmod := DllCall("GetModuleHandle", Str, File))
hmod := DllCall("LoadLibraryEx", Str, File, UInt, 0, UInt, 0x2), loaded := 1
enumproc := RegisterCallback("GetResNames", "F")
VarSetCapacity(param, 12, 0)
DllCall("EnumResourceNames", UInt, hmod, UInt, Type, UInt, enumproc, UInt, ¶m)
DllCall("GlobalFree", UInt, enumproc)
IfEqual, loaded, 1, DllCall("FreeLibrary", UInt, hmod)
If (listptr := NumGet(param,4)) {
list := DllCall("MulDiv", Int, listptr, Int, 1, Int, 1, Str)
DllCall("GlobalFree", UInt, listptr)
}
Count := NumGet(param)
Return, list
}
GetResourceNames(Filename, Type, ByRef Count="") {
; Get a handle to a loaded DLL.
If (!hmod := DllCall("GetModuleHandle", Str, File))
hmod := DllCall("LoadLibraryEx", Str, Filename, UInt, 0, UInt, 0x2)
; Create a callback to be called by EnumResourceNames().
enumproc := RegisterCallback("GetResourceNames_callback", "F")
; Create a structure to pass to the callback.
VarSetCapacity(param, 12, 0)
; Enumerate the resources.
DllCall("EnumResourceNames", UInt, hmod, UInt, Type, UInt, enumproc, UInt, ¶m)
; Free the memory used by the callback.
DllCall("GlobalFree", UInt, enumproc)
; If we loaded the DLL, free it now.
If loaded
DllCall("FreeLibrary", UInt, hmod)
; Retrieve the list of resource names.
If (listptr := NumGet(param,4)) {
list := DllCall("MulDiv", Int, listptr, Int, 1, Int, 1, Str)
DllCall("GlobalFree", UInt, listptr)
} ; Else: No resources or an error occurred. Leave list blank.
; Retrieve the number of resources enumerated.
Count := NumGet(param)
Return, list
}
GetResourceNames_callback(hModule, lpszType, lpszName, lParam) {
; Increment the counter for this enumeration.
NumPut(1 + NumGet(lParam+0), lParam+0)
; lpszName may be an integer identifier or a pointer to a string.
; If it is a pointer to a string, retrieve the string.
If (lpszName >> 16 != 0)
lpszName := DllCall("MulDiv", Int, lpszName, Int, 1, Int, 1,Str)
; Retrieve variables from the structure.
listptr := NumGet(lParam+4), listlen := NumGet(lParam+8)
If listptr {
; Reallocate the list to make room for the name of this resource.
listptr := DllCall("GlobalReAlloc", UInt, listptr, UInt, listlen+StrLen(lpszName)+2, UInt, 2)
; Append delimiter.
NumPut(Asc("`n"), listptr + listlen, "char"), listlen += 1
} Else {
; Allocate memory for the first resource name.
listptr := DllCall("GlobalAlloc", UInt, 0, UInt, StrLen(lpszName)+1)
}
; Append the name of this resource onto the list.
DllCall("RtlMoveMemory", UInt, listptr + listlen, Str, lpszName, UInt, StrLen(lpszName)+1)
listlen += StrLen(lpszName)
; Update the structure.
NumPut(listptr, lParam+4), NumPut(listlen, lParam+8)
; Continue enumeration.
Return, true
}
|
@lexikos: I modified the functions a little bit. I hope, its ok. |
|
| Back to top |
|
 |
freakkk
Joined: 29 Jul 2005 Posts: 179
|
Posted: Tue Nov 11, 2008 7:24 pm Post subject: |
|
|
| Obi-Wahn wrote: | | 1.) In the first post, you link to the MSDN-Site, which contains a list of resource types that can be queried. But at the MSDN Site, the Types are Names (like RT_BITMAP), but the script doesn't accept this style. It seems like, you have to pass a Number as equivalent of the Style-Name. But there are no numbers on the MSDN Site. Does I have to guess the numbers, or exists a List of Numbers? Even with google, I found only lists with the Names, not numbers. | You can look them up using ApiVeiwer --or-- SKAN's Crazy Scripting : A handy tool to lookup Win32 Constants. Also-- here is a thread that can probably help answer any general questions you have on getting started w/ dllcalls.
| Obi-Wahn wrote: | | 2.) Thats a What-If question. What if the RegisterCallback points on the same function as launched from? The callpack function has 4 parameters (which all needed, i guess). Woud it be possible to make a if-question (like if (lParam != "") ) and execute the code from there? Should be the same, or am I wrong? |
You can call a callback adr (recursion like), but the problem your running into is that your func cannot have byref params (like Lexikos mentioned), nor can it have default values. Here is an example that should illustrate that for you:
| Code: | example1( 1000 )
example2( 1000 )
exitapp
example1( p ) {
If ( A_EventInfo != 0 )
MsgBox,, % A_ThisFunc, Input from calling your callback adr: %p%
If ( A_EventInfo = 0 ) {
MsgBox,, % A_ThisFunc, Input from you: %p%
adr := RegisterCallback( A_ThisFunc, "F")
r := DllCall(adr, "UInt", p )
DllCall("GlobalFree", "UInt",adr)
}
}
example2( p="default string" ) {
If ( A_EventInfo != 0 )
MsgBox,, % A_ThisFunc, Input from calling your callback adr: %p%
If ( A_EventInfo = 0 ) {
MsgBox,, % A_ThisFunc, Input from you: %p%
adr := RegisterCallback( A_ThisFunc, "F")
r := DllCall(adr, "UInt", p )
DllCall("GlobalFree", "UInt",adr)
}
} |
Notice how the dllcall invoking your callback address ('<==') is identical, but the param doesn't work the same when callback is called in example2.
It just so happens that for EnumResource, you could get away with combining those into a single function, but thats only because we could get away w/ not passing param & instead store the label in a static var.
| Code: |
NumIcons := EnumResources(A_AhkPath, 14, "EnumGroupIcons")
MsgBox % NumIcons " icons:`n`n" Icons
exitapp
EnumGroupIcons:
Icons .= ErrorLevel "`n"
return
EnumResources(Filename, Type, Label)
{
static uLabel
If ( A_EventInfo = 0 ) { ; when called by user
hmod := DllCall("GetModuleHandle", "str", Filename)
; If the DLL isn't already loaded, load it as a data file.
loaded := !hmod
&& hmod := DllCall("LoadLibraryEx", "str", Filename, "uint", 0, "uint", 0x2)
enumproc := RegisterCallback(A_ThisFunc,"F")
;--- not needed since static uLabel var will store the label user specified ---
; VarSetCapacity(param,8,0) ;, NumPut(&Label, param)
; DllCall("EnumResourceNames", "uint", hmod, "uint", Type, "uint", enumproc, "uint", ¶m)
uLabel := Label
; Enumerate the resources.
DllCall("EnumResourceNames", "uint", hmod, "uint", Type, "uint", enumproc)
DllCall("GlobalFree", "uint", enumproc)
; If we loaded the DLL, free it now.
if loaded
DllCall("FreeLibrary", "uint", hmod)
uLabel := ""
return NumGet(param,4)
}
If ( A_EventInfo != 0 ) { ; when called from invoking callback
; Alias for Lex's EnumResources_callback param
lpszName:=Label
;--- not needed anymore, since static uLabel var is storing the label user specified ---
; NumPut(1 + NumGet(lParam+4), lParam+4)
; if (lpszName >> 16 != 0)
; lpszName := DllCall("MulDiv","int",lpszName,"int",1,"int",1,"str")
; Label := DllCall("MulDiv","int",NumGet(lParam+0),"int",1,"int",1,"str")
if uLabel !=
{
if IsLabel(uLabel)
{ ; ErrorLevel = resource ID or name.
ErrorLevel := lpszName
gosub %uLabel%
}
else
%uLabel%(lpszName) ; Dynamic function call. Requires AHK v1.0.47.06.
}
return ErrorLevel!="#stop"
}
}
|
I have to ask WHY? This doesn't offer any advantage, & its not really easier to follow. You also obviously loose the ability to offer the user optional parameters in the function call too. _________________ .o0[ corey ]0o. |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7295 Location: Australia
|
Posted: Tue Nov 11, 2008 9:46 pm Post subject: |
|
|
| Quote: | Source: AutoHotkey Documentation: RegisterCallback
ParamCount
The number of parameters that Address's caller will pass to it. If entirely omitted, it defaults to the number of mandatory parameters in the definition of FunctionName. In either case, ensure that the caller passes exactly this number of parameters. |
|
|
| Back to top |
|
 |
freakkk
Joined: 29 Jul 2005 Posts: 179
|
Posted: Tue Nov 11, 2008 10:14 pm Post subject: |
|
|
| Lexikos wrote: | | Quote: | Source: AutoHotkey Documentation: RegisterCallback
ParamCount
The number of parameters that Address's caller will pass to it. If entirely omitted, it defaults to the number of mandatory parameters in the definition of FunctionName. In either case, ensure that the caller passes exactly this number of parameters. |
| oh-- haha. I actually meant, I have to ask WHY obi-wahn is fixated on combining your two functions into one; I actually prefer your original demo.  _________________ .o0[ corey ]0o. |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7295 Location: Australia
|
Posted: Wed Nov 12, 2008 5:14 am Post subject: |
|
|
| I know what you meant, and I agree. I was merely pointing out that optional parameters do not matter as long as you specify the actual number of parameters required by the callback. |
|
| Back to top |
|
 |
Obi-Wahn
Joined: 20 Apr 2006 Posts: 75 Location: Vienna
|
Posted: Wed Nov 12, 2008 8:08 am Post subject: |
|
|
| freakkk wrote: | ...oh-- haha. I actually meant, I have to ask WHY obi-wahn is fixated on combining your two functions into one; I actually prefer your original demo.  |
Hi Lexikos & freakkk!
I don't know why I'm fixated on functions, especially functions that are ONE function that does the job. Maybe it's like the "Lord of the Ring" - Thing. One Function to rule 'em all...
But seriously: I use functions that referer to other Functions but only if
a.) The Called function has more than one line code, and b.) It's called repeatedly.
BTW: I know, that ByRef Parameter are prohibited, but according to the helpfile, optional parameters are permitted.
Anyway. Thanks for the Help. I think I can live with two Functions.... |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7295 Location: Australia
|
Posted: Wed Nov 12, 2008 8:36 am Post subject: |
|
|
| Obi-Wahn wrote: | But seriously: I use functions that referer to other Functions but only if
a.) The Called function has more than one line code, and b.) It's called repeatedly. | Both are true of GetResourceNames...
| Quote: | | BTW: I know, that ByRef Parameter are prohibited, but according to the helpfile, optional parameters are permitted. | If you don't specify the number of parameters, optional parameters are excluded from the callback. Most callbacks use the stdcall calling convention, which requires the callee to "clean up" the stack. If the callback expects no parameters, any data pushed onto the stack by the caller will remain there after the callback returns. This can and will lead to stack corruption. |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|