OK, so I went ahead and coded that more robust version. This uses Windows API commands (SendMessage, PostMessage, DLLCall) to find the Quick Launch toolbar, find the desired button, then execute it.
EDIT: This only works on XP, Win2k apparently uses a whole different taskbar structure.
(see
http://w-shadow.com/blog/2006/10/01/manipulating-taskbar-buttons/)
Code:
; Quick Launch using Win+Number Hotkey (like Windows Vista)
; v2.0
;
; AutoHotkey Version: 1.0.46.09 (tested version)
; Language: English
; Platform: Win9x/NT
; Author: Bobbo
;
; Script Function:
; Added win+num(0:9) as shortcut keys to whatever is in your quicklaunch toolbar
; (just like Windows Vista)
;
#NoEnv
SendMode Input
#1::WinNumQL(1)
#2::WinNumQL(2)
#3::WinNumQL(3)
#4::WinNumQL(4)
#5::WinNumQL(5)
#6::WinNumQL(6)
#7::WinNumQL(7)
#8::WinNumQL(8)
#9::WinNumQL(9)
WinNumQL(button_num)
{
; do nothing if toolbar not loaded
WinGetText, text, ahk_class Shell_TrayWnd
if (InStr(text, "Quick Launch") = 0)
return
; find the control name of the Quick Launch toolbar
Loop,9
{
ControlGetText,text2,ToolbarWindow32%A_Index%, ahk_class Shell_TrayWnd
IfInString,text2,Quick Launch
{
qlname = ToolbarWindow32%A_Index%
break
}
}
; do nothing if not enough buttons
SendMessage, 0x418, 0, 0, %qlname%, ahk_class Shell_TrayWnd ; TB_BUTTONCOUNT
if (ErrorLevel<button_num)
return
; get info about the requested button
WinGet, pid, PID, ahk_class Shell_TrayWnd
hProc := DllCall("OpenProcess", "Uint", 0x38, "int", 0, "Uint", pid)
pRB := DllCall("VirtualAllocEx", "Uint", hProc, "Uint", 0, "Uint", 20, "Uint", 0x1000, "Uint", 0x4)
VarSetCapacity(btn, 20)
VarSetCapacity(nfo, 24)
SendMessage, 0x417, button_num-1, pRB, %qlname%, ahk_class Shell_TrayWnd ; TB_GETBUTTON
DllCall("ReadProcessMemory", "Uint", hProc, "Uint", pRB, "Uint", &btn, "Uint", 20, "Uint", 0)
iBitmap := DecodeInteger(&btn + 0)
idn := DecodeInteger(&btn + 4)
dwData := DecodeInteger(&btn +12)
iString := DecodeInteger(&btn +16)
DllCall("ReadProcessMemory", "Uint", hProc, "Uint", dwData, "Uint", &nfo, "Uint", 24, "Uint", 0)
hWnd := DecodeInteger(&nfo + 0)
uID := DecodeInteger(&nfo + 4)
nMsg := DecodeInteger(&nfo + 8)
hIcon := DecodeInteger(&nfo +20)
; execute the button
ControlGet,qlhandle,Hwnd,,%qlname%, ahk_class Shell_TrayWnd
PostMessage, 0x111, idn, qlhandle, %qlname%, ahk_class Shell_TrayWnd ; WM_COMMAND
return
}
DecodeInteger(ptr)
{
Return *ptr | *++ptr << 8 | *++ptr << 16 | *++ptr << 24
}
EncodeInteger(ref, val, nSize = 1)
{
DllCall("ntdll\RtlFillMemoryUlong", "Uint", ref, "Uint", nSize * 4, "Uint", val)
}
Theoretically this should be more reliable than the first version, because it doesn't rely on windows shortcut keys & moving the cursor focus.
Thanks to Sean (
http://www.autohotkey.com/forum/viewtopic.php?t=17314) for his code for extracting button info.
Also check out how I execute the button using WM_COMMAND: it looks simple, but it took me a lot of googling to figure it out. You can use this method to execute any toolbar button in any application without using screen coordinates and simulated mouse clicks. This is especially important if only part of the menu is visible on the screen (e.g. some of your buttons are only accessible by a drop-down menu, because the toolbar is too short to display them all.).