 |
Cheat Engine The Official Site of Cheat Engine
|
| View previous topic :: View next topic |
| Author |
Message |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4709
|
Posted: Sat Sep 21, 2024 3:02 pm Post subject: |
|
|
registerFormAddNotification gets called for every form that gets newly created. If you don't know what it does, play around with it and see what happens:
| Code: | registerFormAddNotification(function(f)
print(f.ClassName)
end)
| Execute this then open a bunch of windows in CE. e.g. a structure dissect window
The first part of that code I stole and modified from DB's post is a helper function:
| Code: | function forEachAndFutureForm(classname, func)
local i
for i=0,getFormCount()-1 do
local f = getForm(i)
if f.ClassName==classname then
func(f)
end
end
registerFormAddNotification(function(f)
if classname==f.ClassName then
f.registerFirstShowCallback(func)
end
end)
end | The function's name, "forEachAndFutureForm", describes what it does. It takes two parameters: a class name and a function that takes a form. The provided function will be executed once for every form with the specified class name- both currently visible forms and any forms that get created in the future.
The for loop iterates over the currently visible forms and executes the function for each form that already exists.
The call to `registerFormAddNotification` will make sure CE calls the provided function whenever a new form is created. `registerFirstShowCallback` makes sure everything in the form has been initialized before the function gets called- e.g. the popup menu might not exist when the `registerFormAddNotification` callback is executed, but it will when the `registerFirstShowCallback` callback is executed.
The second part calls that helper function:
| Code: | forEachAndFutureForm('TfrmStructures2',function(f)
local mi = createMenuItem(f.pmStructureView)
mi.Name = 'miHelloWorld'
mi.Caption = 'Hello'
mi.OnClick = function() print'Hello' end
f.pmStructureView.Items.add(mi)
end) | The class name of the form you want to modify is "TfrmStructures2". The callback function takes the form and adds a new item to the popup menu.
You can probably figure out the class name by opening the window and iterating over all the current forms:
| Code: | for i = 0, getFormCount() - 1 do
print(getForm(i).ClassName)
end |
To get the name of the popup menu, iterate over all the owned components of the form:
| Code: | local structfrm
for i = 0, getFormCount() - 1 do
local f = getForm(i)
if f.ClassName == 'TfrmStructures2' then
structfrm = f
break
end
end
for i = 0, structfrm.ComponentCount - 1 do
local c = structfrm.Component[i]
print(c.ClassName, '\t', c.Name, '\t', c.Caption)
end |
If you have CE's source code readily available, it might be faster to look in the relevant lfm file. e.g. Cheat Engine/StructuresFrm2.lfm:
| Code: | object frmStructures2: TfrmStructures2
...
object pmStructureView: TPopupMenu
Images = sdImageList
OnPopup = pmStructureViewPopup
Left = 48
Top = 120
object miChangeValue: TMenuItem
Caption = 'Change value'
ImageIndex = 5
ShortCut = 13
OnClick = miChangeValueClick
end
... |
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
| Back to top |
|
 |
Csimbi I post too much
Reputation: 97
Joined: 14 Jul 2007 Posts: 3326
|
Posted: Sun Sep 22, 2024 8:50 am Post subject: |
|
|
| ParkourPenguin wrote: | | ... |
Thanks for the detailed explanation.
Let me check if I got this right:
- that timer-based registration I posted earlier was the wrong way to go.
- the right way to go is forEachAndFutureForm because that event will fire every time a new instance of a form is created.
- forEachAndFutureForm must interate through all forms and call the function passed as a parameter when the right form is found (which, in my case is fn_AddPopupMenuItems).
Questions (please forgive my ignorance again):
- Why do I have to scan all forms using forEachAndFutureForm and re-register each form? Won't that cause issues in the long run without unregistering potentially existing registrations?
- What happens when another script includes forEachAndFutureForm for the same form, albeit with a different function?
- Wouldn't it be more logical to have a PostCreate() event that fires once for every new instance of the form, after it has been instantiated and fully initialized just before it would be displayed?
|
|
| Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4709
|
Posted: Sun Sep 22, 2024 9:57 am Post subject: |
|
|
| Csimbi wrote: | | the right way to go is forEachAndFutureForm because that event will fire every time a new instance of a form is created | `forEachAndFutureForm` is just a helper function DB made up. You can call it whatever you want. The important bits are CE's API- particularly `registerFormAddNotification` and `registerFirstShowCallback`.
| Csimbi wrote: | | Why do I have to scan all forms using forEachAndFutureForm and re-register each form? Won't that cause issues in the long run without unregistering potentially existing registrations? | `registerFormAddNotification` only triggers for new forms. It doesn't trigger for forms that already exist. e.g. if you already had a structure dissect window open and execute that code without the `for` loop, any new structure dissect windows would be modified, but the one that was already opened wouldn't.
I'm not sure what you mean by "re-register each form". If you call that function more than once, then the old `registerFormAddNotification` callback(s) will still be run. You can modify the function a little to be able to unregister it:
| Code: | function forEachAndFutureForm(classname, func)
for i=0,getFormCount()-1 do
local f = getForm(i)
if f.ClassName==classname then
func(f)
end
end
return registerFormAddNotification(function(f)
if classname==f.ClassName then
f.registerFirstShowCallback(func)
end
end)
end
if structDissectPrintHelloCallback then
unregisterFormAddNotification(structDissectPrintHelloCallback)
structDissectPrintHelloCallback = nil
end
structDissectPrintHelloCallback = forEachAndFutureForm('TfrmStructures2', function(f)
if f.pmStructureView.miHelloWorld then
-- might run again if any windows are opened
f.pmStructureView.miHelloWorld.destroy()
end
local mi = createMenuItem(f.pmStructureView)
mi.Name = 'miHelloWorld'
mi.Caption = 'Hello'
mi.OnClick = function() print'Hello' end
f.pmStructureView.Items.add(mi)
end) | Try modifying the message printed and execute the script again.
NB: when this code is run multiple times, the `for` loop might execute your function on a window that a previous callback had already run on. This is why this callback now destroys the menu item created by any previous callback.
| Csimbi wrote: | | What happens when another script includes forEachAndFutureForm for the same form, albeit with a different function? | Both of the callback functions get run.
Again, it's fine to play around with Lua yourself and see what happens:
| Code: | ...
forEachAndFutureForm('TfrmStructures2', function(f)
print'Hello'
end)
forEachAndFutureForm('TfrmStructures2', function(f)
print'World'
end) |
| Csimbi wrote: | | Wouldn't it be more logical to have a PostCreate() event that fires once for every new instance of the form, after it has been instantiated and fully initialized just before it would be displayed? | That's exactly what `registerFirstShowCallback` is. It registers a callback function to be run when the form is first shown. This is better than setting a single callback function for an event (e.g. Timer.OnTimer) because several different functions can be registered without overriding each other (see previous code block).
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
|
|