Let's say I have a function to get the window title of a process using its HWND id:
GetWindowTitle(hwnd) {
WinGetTitle, title, ahk_id %hwnd%
return title
}
So far, so good. It works like a charm. So, let's say I am trying to wrap some functionality within a class, in this fashion:
class RunningProcess {
hwnd := ""
windowTitle := ""
__New(hwnd) {
this.hwnd := hwnd
this.windowTitle := GetWindowTitle()
}
GetWindowTitle() {
WinGetTitle, title, ahk_id %this.hwnd%
return title
}
}
The aforementioned code fails to load with the following message:
The following variable name contains an illegal character: "this.hwnd".
I have tried several alternatives in order to use the instance variable hwnd as a WinTitle argument in WinGetTitle, to no avail. The only one that works for me involves using a local variable which grabs the contents of this.hwnd and then using the local variable as a WinTitle argument, like this:
GetWindowTitle() {
foo := this.hwnd
WinGetTitle, title, ahk_id %foo%
return title
}
However, this is less than ideal, as you might guess, at least in my opinion.
Is there a way to use this.hwnd as a WinTitle argument right off the bat?
Thanks!
I'd call it a classic case of having trouble with the legacy vs modern expression syntax.
Refreshing to see it this way though, usually people with the issue are writing 2007 level AHK, but nice to see you're writing modern AHK.
So anyway, forget about using %% to refer to variables. That's the legacy AHK way. In a modern expression statement you just type the name of the variable to refer to is, for example your assignments with the := operator are just fine that way, because it's using the modern expression syntax (= to assign would be the legacy way)
However, pretty much all commands, that haven't been replaced with more modern function, still use the legacy syntax on every parameter (unless otherwise specified in the documentation).
So, you're going to have to set the parameter to evaluate an expression, instead of expecting a legacy text parameter. To do that, you start off the parameter with a single % followed up with a space.
So your WinGetTitle command should look like this:
WinGetTitle, title, % "ahk_id " this.hwnd
Quotation marks around ahk_id , because that's how you specify a string in an expression, and then just type in the variable you want to concatenate to that string. The concatenation operator . could be used here like so % "ahk_id " . this.hwnd, but it's totally redundant and looks weird in my opinion as well.
Also, your call to the GetWindowTitle() function in the initializer __New is going to need a this. in front of it.
And initializing the instance variables to nothing is redundant as well. You can remove that if you want.
Finished product:
MyCoolObject := new RunningProcess(WinExist("A"))
MsgBox, % "The handle of currently active window is: " MyCoolObject.hwnd "`nAnd its title is: " MyCoolObject.windowTitle
class RunningProcess
{
__New(hwnd)
{
this.hwnd := hwnd
this.windowTitle := this.GetWindowTitle()
}
GetWindowTitle()
{
WinGetTitle, title, % "ahk_id " this.hwnd
return title
}
}
To learn more about legacy syntax vs expression syntax, see for example this page from the documentation:
https://www.autohotkey.com/docs/Language.htm
Related
I want to folding tha variable name
code
numberOfInstNext := Array("","","","","","","","","","","")
numberOfInstNext[1] := 777
i := 1
I tried
msgbox %numberOfInstNext[i]%
error:
The following variable name contains an illegal character 'numberOfInstNext[i]'
Expressions says
An expression can be used in a parameter that does not directly
support it by preceding the expression with a percent sign and a space or tab.
This technique is often used to access arrays.
msgbox % numberOfInstNext[i]
I find the easiest and most scalable solution is to just assign a tmp variable each time you want to access an indexed array.
tmp := numberOfInstNext[i]
msgbox the value is %tmp%!
I have the following tiny .ini file:
["General"]
"runs"="3"
When using this code to read the file, I get a message box stating the correct number of runs
path := A_ScriptDir "\setup.ini"
IniRead, temp, % path, "General", "runs"
MsgBox, % temp
When I try to pass General as a variable, the message box outputs "ERROR"
path := A_ScriptDir "\setup.ini"
iniSection := "General"
IniRead, temp, % path, % iniSection, "runs"
MsgBox, % temp
I tried enclosing the variables in %-signs, instead of just prefixing them, but it does not make a difference.
Any help is appreciated!
Variable names in an expression are not enclosed in percent
signs. Consequently, literal strings must be enclosed in double
quotes to distinguish them from variables.
To include an actual quote-character inside a literal string, specify
two consecutive quotes:
iniSection := """General"""
I'm fairly new to autohotkey and ran into a problem recently when trying to pass variables as parameters for the "run" command in autohotkey. Can anyone show me what I'm missing or is this a bug?
Gui, Setup:Add, Edit, w100 vProgram
Gui, Setup:Add, Button, Default w100 gSubmit, OK
Gui, Setup:Show,, Setup
WinWaitClose, Setup
ExitApp
Submit:
Gui, Setup:Submit
RunStuff()
RunStuff() {
Run, %Program%
}
I've even tried changing the line to this:
RunStuff() {
run, Program
}
I would really appreciate the help, thanks in advance.
First things first, while
WinWaitClose, Setup
ExitApp
Is maybe smart thinking, you're supposed to do this with the GuiClose event by just defining a function (or a label) with the name GuiClose.
GuiClose()
{
ExitApp
}
Also, giving the gui a name is redundant, although if you're planning on adding more guis, fair enough.
And to run cmd commands, you'd start off cmd with the /c(docs) switch.
Your example netsh wlan connect ssid=%networkname% name=%networkname% would be done like this:
networkname := "name"
Run, %A_ComSpec% /c netsh wlan connect ssid=%networkname% name=%networkname%
Run, % A_ComSpec " /c netsh wlan connect ssid=" networkname " name=" networkname
The first line is in legacy syntax, I wouldn't really recommend it.
The second line is in expression syntax.
The built in variable A_ComSpec(docs) contains the path to cmd.exe.
Example program based on your code to open a file in notepad:
Gui, Setup:Add, Edit, w300 vFilePath, % "C:\Users\User\Desktop\this is a text file.txt"
Gui, Setup:Add, Button, Default w100 gSubmit, OK
Gui, Setup:Show, , Setup
return
Submit:
Gui, Setup:Submit
Run, % "notepad.exe """ FilePath """"
return
GuiClose()
{
ExitApp
}
Note how the file path includes spaces, so the argument needs to wrapped in quotes.
"""" may seem weird, but a quote is escaped with another quote in AHK. So outer quotes specify that you're writing the string, and inner "" is just one quote escaped. So this produces ".
Legacy syntax vs modern expression syntax can be a bit confusing when you're learning AHK. You'll see a lot of legacy syntax when you look up stuff. This is mainly because AHK was much more popular years and years ago (when legacy syntax was the thing to use).
To get started off on the legacy vs modern expression differences, here's a pretty good documentation page:
https://www.autohotkey.com/docs/Language.htm
EDIT:
Answer to the new problem that as added in via an edit to the OP.
RunStuff() {
Run, %Program%
}
The variable Program is not defined in the function's scope.
Lets consider this example code
var1 := 1
var2 := 2
global var3 := 3
function()
return
function()
{
global var2
MsgBox, % "var1: " var1 "`nvar2: " var2 "`nvar3: " var3 "`n"
}
var1 is not defined in the function's scope, so nothing will be printed in the message box.
var2 will be used from outside the function's scope because of the line global var2.
var3 would also be used from outside of the function scope because var3 was defined as super global. Super global isn't really recommended, because anything anywhere will use that variable then. Quite easy to run into problems on larger scripts, but it's convenient for smaller scripts where you know what you'll be doing and don't have any external libraries for example.
You can read the related documentation from here:
https://www.autohotkey.com/docs/Functions.htm#Locals
It seems that you think your problem is with the gui output.
I think you might want to check things in a different way:
You could create a script to analyze the your run inputs.
For example:
Script #1, let's call it "1.ahk"
Run, 2.ahk param1 param 2 "param 3"
Script #2, let's call it "2.ahk"
txt := "Params as seen by ahk:`n"
for i, param in A_Args
txt .= i " = " param "`n"
MsgBox % txt
By running script "1.ahk" you can see the message that "2.ahk" creates:
Params as seen by ahk:
1 = param1
2 = param
3 = 2
4 = param 3
Which I think it's quite telling about how it understands spaces, and how quotes prevent splitting arguments.
Now if you run your gui outputs against "2.ahk" you might see something different to what you saw before.
RunStuff() {
Run % start "" "C:\blabla\prog.exe" -windowstyle minimized
}
I don't understand the difference between Autohotkey's If and If(...)
According to everything I have found, If(...) behaves as "expected" but there is something not working with my code.
Below does not work. It seems the statement in the If is never evaluated, %TimeString% is never set and nothing is output:
CapsLock & T::
Input Key, L1
If (Key=T)
{
FormatTime, TimeString,, HHmm
}
Send %TimeString%
Below does work, %TimeString% is set and the time is output.
CapsLock & T::
Input Key, L1
If Key=T
FormatTime, TimeString,, HHmm
Send %TimeString%
Autohotkey has two different syntaxes: legacy and expression. This also affects the if statement.
When you use parenthesis, if (expression) is used and Key is compared to the variable T which doesn't exist and is the same as an empty variable which doesn't equal T. You need to changed it to If (Key="T") and then it will compare the variable Key to the String "T" and it will work.
In the second case you're using the traditional(legacy) If which compares the variable Key to the String T and because they are equal, it works.
The curly braces { } just define a block, they do nothing and change nothing when your block contains only one line.
Are you sure this code is identical to your script? Beause
Tjs := T
if (Tjs=T)
{
MsgBox true
}
works fine for me.
I’ve written script that contains numerous hotkeys (general structure is as below). I would like to create another one that when pressed displays a list of all of the hotkeys and their corresponding descriptions that the script contains in a nice, formatted table.
The formatting and display are tenuous since AutoHotkey’s output is limited to message-boxes, but possible. More problematic is getting the hotkeys and corresponding descriptions.
The hotkeys all call the same function with different arguments. I considered adding a variable to the function so that depending on the value, the function either performs the normal function when triggered by the normal hotkeys, or builds a string or something when triggered from the special display hotkey.
I cannot figure out a way to programmatically access the script’s hotkeys at all. I checked the docs and there don’t seem to be any A_ variables that can be used for this purpose, nor does the Hotkey command lend itself well (it can be used to test if a hotkey exists, but looping through the innumerable combinations is, at best, tedious).
Failed attempts:
I tried using Elliot’s suggestion of parsing the script itself (replacing the path with %A_ScriptFullPath%, and while it does work for a raw script, it does not when the script is compiled
I tried assigning the entire hotkey section of the script to a variable as a continuation section and then parsing the variable and creating hotkeys using the Hotkey command. This worked well right up until the last part because the Hotkey command cannot take arbitrary commands as the destination and requires existing labels.
The ListHotkeys command is not applicable because it only displays the hotkeys as plain text in the control window.
Does anyone know how I can display a list of the hotkeys and either their corresponding arguments or comments?
Example script:
SomeFunc(foobar)
{
MsgBox %foobar%
}
!^#A::SomeFunc("a") ; blah
^+NumpadMult::SomeFunc("c") ; blivet
^+!#`::SomeFunc("b") ; baz
^#Space::SomeFunc("d") ; ermahgerd
…
Example desired “outputs”:
C+A+ W+ A a | C+ S+ NumpadMult b
------------------+----------------------
C+A+S+W+ ` c | C+ W+ Space d
or
Ctrl Alt Shift Win Key Action
-----------------------------------------
× × × A blah
× × NumpadMult baz
× × × × ` blivet
× × Space ermahgerd
etc.
The only thing I can think of is to read each line of your script individually and parse it. This code reads your script (script.ahk) one line at a time and parses it. This should get you started. Additionally, you could parse the line to check for the modifiers as well.
Loop
{
FileReadLine, line, C:\script.ahk, %A_Index%
if ErrorLevel
break
If Instr(line, "::")
{
StringSplit, linearray, line, ::,
key := linearray1
StringSplit, commandarray, linearray3, `;
action := commandarray2
hotkeyline := "key: " . key . "`tAction: " . action
final .= hotkeyline . "`r"
}
}
msgbox % final
return
I found a solution. It is not perfect (or ideal), and hopefully a proper, built-in method will become available in the future, but it works well (enough) and for raw and compiled scripts.
What I did was to use the FileInstall command which tells the compiler to add a file to the executable (and extract it when run).
Sadly, the FileInstall command will not allow the use of variables for the source file, so I cannot simply include the script itself (FileInstall, %A_ScriptFullPath%, %A_Temp%\%A_ScriptName%, 1).
As a work-around, I ended up extracting all of the desired hotkeys to a second file which I then parse as Elliot suggested, then delete, and #Include at the end of my script (it must be at the end since hotkeys will terminate the autoexecute section).
;;;;; Test.ahk ;;;;;
; Add hotkey file to executable and extract to Temp directory at runtime
FileInstall, Hotkeys.ahk, %A_Temp%\Hotkeys.ahk, 1
Loop
{
;Read a line from the extracted hotkey script and quit if error
FileReadLine, line, %A_Temp%\Hotkeys.ahk, %A_Index%
if ErrorLevel
break
;Trim whitespace
line=%line%
; Parse the line as in Elliot’s answer, but with tweaks as necessary
ParseHotkey(line)
…
}
FileDelete, %A_Temp%\Hotkeys.ahk ; Delete the extracted script
DisplayHotkeys() ; I ended up bulding and using a GUI instead
#Include, Hotkeys.ahk ; It is included at compile-time, so no A_Temp
;;;;; Hotkeys.ahk ;;;;;
z::MsgBox foo
y::MsgBox bar