I want to get window handle by PID in autohotkey, because title of the window is always changing. If anyone wonder, I want to get handle of last.fm main window.
To get the first window Class/ID of a PID you can do the following:
Process, Exist, "notepad.exe"
NewPID = %ErrorLevel% ; Save the value immediately since ErrorLevel is often changed.
if NewPID
{ ; process exists!
WinGetClass, ClassID, ahk_pid %NewPID% ; ClassID will be read here for the process
WinGetTitle, Title, ahk_pid %NewPID% ; Title will contain the processe's first window's title
IfWinExist ahk_class %ClassID% ; this will find the first window by the ClassID
{
WinGet, WinID, ID ; this will get the ID of the window into WinID variable
WinActivate ; this will bring this window to front (not necessary for example)
ListVars ; this will display your variables
Pause
}
IfWinExist %Title% ; this will find the first window with the window title
{
WinGet, WinID, ID
WinActivate ; this will bring this window to front (not necessary for example)
ListVars
Pause
}
}
there are other methods to convert the PID other than IfWinExist I'm sure, and it is possible to have more than one process with same class ID. :)
Additionally you can use
As a reusable function:
getHwndForPid(pid) {
pidStr := "ahk_pid " . pid
WinGet, hWnd, ID, %pidStr%
return hWnd
}
You can use the WinGet command with the Cmd paramter as ID.
Cmd is the operation to perform, which if blank defaults to ID.
ID: Retrieves the unique ID number of a window. Also known as the window handle (HWND).
WinTitle can be a PID.
WinGet, UniqueID, ID, ahk_pid %VarContainingPID%
Another option is WinExist()
UniqueID := WinExist("ahk_pid" . VarContainingPID)
Related
I'm facing a somewhat hard question. Look at this script:
; Using Autohotkey 1.1.36.2
F2::
send_f2_withcond()
{
if(WinExist("ahk_class Notepad"))
{
ControlSend Edit1, % A_Now "`r", % "ahk_class Notepad"
}
else
{
; I hope F2 can do its original action. How to?
Send {$F2} ; ??? No effect!
}
}
My purpose:
If there is Notepad running, F2 will send some text to Notepad.
If Notepad is not running, I hope F2 can do it original action, for example, pressing F2 in an Explorer window will start renaming current highlighted file.
Writing Send {F2} is not correct, because it will trigger my own F2:: ... action recursively.
The doc says adding a $ prefix will suppress recursive calling, BUT, Send {$F2} takes no effect(as if I totally omit this Send), the current active application receives only F2's WM_KEYUP, no WM_KEYDOWN.
The $ prefix is used only in hotkey definitions. It forces the keyboard hook to be used, which is designed to filter out keys sent by AutoHotkey scripts.
$F2:: send_f2_withcond()
send_f2_withcond() {
if WinExist("ahk_class Notepad")
ControlSend, Edit1, % A_Now "`r", % "ahk_class Notepad"
else
Send {F2}
}
You can greatly simplify this script by using #IfWinExist.
#IfWinExist, ahk_class Notepad
F2::ControlSend Edit1, % A_Now "`r", % "ahk_class Notepad"
#IfWinExist
Using Autohotkey, I can send "ctrl+" to Windows File Explorer, to auto adjust column width.
Manually, it's ctrl+ (the + in the numpad).
This code below works, but only for first level folders, not if I open a folder within a folder.
Is there a way to send "ctrl+" again for each subfolders I might open?
Gui, +LastFound
DllCall("RegisterShellHookWindow", UInt, WinExist())
MsgNum := DllCall("RegisterWindowMessage", Str, "SHELLHOOK")
OnMessage(MsgNum, "ShellMessage")
Return
ShellMessage(wParam, lParam) {
wTitle = ahk_id %lParam%
WinGet, pname, ProcessName, %wTitle%
If (wParam != 1 || pname != "Explorer.exe")
Return
WinActivate, %wTitle%
Send ^{NumpadAdd} ;ctrl+ (numpad)
}
Seems like a pretty questionable approach.
I would rather see about somehow setting this to be the default behavior, or automating this with COM instead of sending a hotkey.
Anyway, for the hotkey approach, this seems to do the trick:
;No need to create a gui, A_ScriptHwnd is used for this
DllCall("RegisterShellHookWindow", UInt, A_ScriptHwnd)
MsgNum := DllCall("RegisterWindowMessage", Str, "SHELLHOOK")
OnMessage(MsgNum, "ShellMessage")
Return
ShellMessage(wParam, lParam)
{
static _time := 0
if (wParam = 6 && A_TickCount - _time > 100 && WinActive("A") = lParam)
{
_time := A_TickCount
WinGet, pname, ProcessName, % "ahk_id " lParam
if (pname = "explorer.exe")
{
ControlFocus, DirectUIHWND2, % "ahk_id " lParam
SendInput, ^{NumpadAdd}
}
}
}
So, first ditched the legacy way of getting a hwnd for the current script, and used A_ScriptHwnd (docs).
Also, ditched legacy syntax overall.
Then switched over to the HSHELL_REDRAW (docs) event to check for window title changes.
And the timing stuff is to filter out duplicate shell messages. When the title changes, we actually receive like 10+ of those messages at once. Only need to run the hotkey once.
So a simple 100ms cooldown does the trick.
A_TickCount (docs) is used for this.
Also made the checking order smarter.
No need to get the process name if we didn't even receive the correct shell message.
And finally before sending the hotkey, activate the correct control so the hotkey will work. This part is likely going to be wrong for you if you're on some older or future Windows version.
Also switched over to SendInput (docs) due to it being the recommended faster and more reliable send mode.
I want to write a script that will turn on caps lock when I activate a window containing particular keyword in its title(like SQL). I also want the caps lock to be turned off when I switch to a window whose title does not contain any of the keywords that I have specified.
How can I do it? I have considered #Persistent with a timer to periodically check active window. But, I think there should be a better way.
check answers at: http://www.reddit.com/r/AutoHotkey/comments/1qjf83/force_specific_program_to_use_caps/. Especially G33kDude's answer. It's a clever and efficient solution: check of current window is binded only to windows activation.
=======================
Edit: Code inserted below.
Please note that it's not a complete solution, you'll need to make some edits for your needs. Not a big ones, though.
#Persistent ; Don't close when the auto-execute ends
SetTitleMatchMode, 2 ; Partial title matching
WinGet, myHwnd, ID, Notepad ; Get the handle to the your window
; Listen for activation messages to all windows
DllCall("CoInitialize", "uint", 0)
if (!hWinEventHook := DllCall("SetWinEventHook", "uint", 0x3, "uint", 0x3, "uint", 0, "uint", RegisterCallback("HookProc"), "uint", 0, "uint", 0, "uint", 0))
{
MsgBox, Error creating shell hook
Exitapp
}
;MsgBox, Hook made
;DllCall("UnhookWinEvent", "uint", hWinEventHook) ; Remove the message listening hook
return
; Handle the messages we hooked on to
HookProc(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime)
{
global myHwnd
static lastHwnd
WinGetTitle, title, ahk_id %hwnd%
if (hwnd == myHwnd) ; If our window was just activated
{
tooltip, Gained focus
}
else if (lastHwnd == myHwnd) ; If our window was just deactivated
{
tooltip, Lost focus
}
lastHwnd := hwnd
}
If you want to do this without using SetTimer, the best way would be to use context-sensitive hotkeys. For Example:
SetTitleMatchMode, 2
#If WinActive("SQL") or WinActive("Notepad")
a::A
b::B
c::C
d::D
e::E
;; etc.
You could also use the WinActive function with Window Groups instead of the title if you wanted to avoid a very long #If line.
EDIT: Case-Insensitive Example
SetTitleMatchMode, Regex
GroupAdd, Editor, (?i).*sql ; Regular expression for window title
GroupAdd, Editor, (?i).*ahk
#IfWinActive ahk_group Editor
a::A
b::B
c::C
d::D
e::E
;; etc.
Since you have a Autoit in your tag, here is how it is done easy in autoit.
Opt("SendCapslockMode", 0)
While 1
Sleep(200)
$title = WinGetTitle("", ""); will get the title of the active window
If StringInStr($title, "sql") Then
Send("{CAPSLOCK ON}")
Else
Send("{CAPSLOCK OFF}")
EndIf
WEnd
Milos' answer is quite straight forward, but it misses a vital point. You need to set SendCapslockMode to 0. Else the effect of the Send command will be useless, because after the command the original state will be restored.
The next thing is, you don't need to use an infinite loop with a Sleep which will execute the complete loop body every few milliseconds, but you can wait for the active window to not be active any more, which is less CPU intensive. So a fully working solution in AutoIt is:
Opt("SendCapslockMode", 0)
While True
$win = WinGetHandle("[ACTIVE]")
If StringInStr(WinGetTitle($win), "sql") Then
Send("{CAPSLOCK ON}")
Else
Send("{CAPSLOCK OFF}")
EndIf
WinWaitNotActive($win)
WEnd
#Persistent
SetTitleMatchMode, 2 ; use RegEx for finer control
Loop
{
WinWaitActive, Notepad
{
WinGet, opVar, ID
SetCapsLockState, On
}
WinWaitNotActive, ahk_id %opVar%
SetCapsLockState, Off
}
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
; This script solves the problem of forgetting to turn on or off the capslock when using specific programs
; I for one ALWAYS use capital letters when typing things in CAD. My problem was when switching to another
; program, usually e-mail, I'd start typing and then have to erase it. Problem solved!
; The beauty of this script is that you don't lose the ability to manually turn the capslock on or off
; My first few interations of this script would turn it on when in the CAD window but then
; I'd never be able to turn it on wihtout it being forced off again.
Loop {
state := GetKeyState("Capslock", "T") ; check toggle state, save in variable 'state'
if state = 0
status = Off ; this block converts the 1 or 0 of variable 'state'
if state = 1 ; into the word ON or OFF and stores it in variable 'status'
status = On
Beginsub:
WinGetTitle, Title, A ; retreive current window title, store in Title
if title contains AutoCAD 2012 ; if title contains "AutoCAD" turn on capslock
; then go back to the BeginSub subroutine and see if the title
{ SetCapsLockState, on ; still matches "AutoCAD 2012" if it does not...\/\/\/
goto, BeginSub
}
SetCapsLockState, %status% ; ...Put caps lock back to the state it was when we started.
; 'SetCapsLockState' doesn't recognize a 1/0 variable
; therefore the use of 'status' and ON/OFF words
Sleep, 1000 ; only check every second
}
I want to pass the window title into a function I wrote in AutoHotKey, is window title WinTitle a string? I have 4 window titles, and I need to pass them to the same function.
Extract(my_window_title) {
; Wake and select the correct window to be in focus
WinWait, my_window_title,
IfWinNotActive, my_window_title, , WinActivate, my_window_title,
WinWaitActive, my_window_title,
; ... do a bunch of things
}
I call the function like this
title1 = "Some title"
Extract(title1)
and I also tried putting % in all the variables
Yes WinTitle is basically a string.
Check out your Autohotkey-folder, there should be a file called "AU3_Spy.exe". Use it to find the window titles.
And as Elliot DeNolf already mentioned, you made some mistakes with variables. You should also take another look at the syntax of IfWInNotActive.
This should work:
Extract(my_window_title) {
; Wake and select the correct window to be in focus
WinWait, %my_window_title%
IfWinNotActive, %my_window_title%
{
WinActivate, %my_window_title%
WinWaitActive, %my_window_title%
}
msgbox, %my_window_title%
; ... do a bunch of things
}
title1 = MyWindowTitle
Extract(title1) ;functions always expect variables, no percent-signs here
There are a few things that look like they are causing an issue in your script.
When assigning a string value and using =, quotes are not needed. If you assign the value using :=, then you need the quotes. These 2 lines are equivalent:
title1 := "Some Title"
title1 = Some Title
Once these values are called via a function ie. Extract(title1), % symbols must be used (as you mentioned at the end of your question). This can be called in 2 ways:
WinActivate, %my_window_title%
WinActivate, % my_window_title
If the title is invalid, your script will wait indefinitely on WinWait and WinWaitActive. I would recommend using a timeout value and then checking ErrorLevel to see if it was successful or not.
I wrote this short test code, but it didn't work. What am I doing wrong?
F12::
WinGetTitle, Title, A ;
MsgBox, "%Title%"
The displayed result was ""
I removed a ; and added return and this worked...
F12::
WinGetTitle, title, A
MsgBox, "%title%"
return
The best practice would probably be to use WinGetActiveTitle:
F12::
WinGetActiveTitle, Title
MsgBox, The active window is "%Title%".
return
If you do not put in a return it will run down your whole file.
Probably something not running through later in it.
Don't think the ; will affect it.
Anything after a ; is omitted from code as a comment.
Using WinGetActiveTitle or WinGetTitle will do.
Note the output of WinGetTitle contains more than the window title.
You might want to remove the ending part with the program name e.g." - Google Chrome".
WinGetActiveTitle, Title
StringGetPos,pos,Title,%A_space%-,R
if (pos != -1)
Title := SubStr(Title,1,pos)
In AutoHotKey 2 you can use this:
title := WinGetTitle("A") ; "A" matches "Active" window
In AutoHotKey 1 you can use:
WinGetActiveTitle, title