Sean, I'm trying to use the script to pick an item by status bar text as follows:
1. Run one script to wait for a Modified_ShellContentMenu window, then call the Modified_ShellContextMenu that includes the status bar text stuff, plus the desired status bar text as additional argument. For example:
Code:
Run, Crawl_ShellContentMenu.ahk
Sleep, 200
Modified_ShellContextMenu("C:\temp\testing.ahk","Open with Notepad2")
2. Here's the Modified_ShellContentMenu.ahk, but it's buggy... probably because I'm failing to unload some of the com stuff, (it starts off ok, but gets worse over time) - sorry to mutilate the original code; I'm just using trial and error to get from point A to B:
Code:
; Modified version of Sean's ShellContextMenu
Modified_ShellContextMenu(sPath,SbText)
{ CoInitialize()
Global pcm
If sPath Is Not Integer
DllCall("shell32\SHParseDisplayName", "Uint", Unicode4Ansi(wPath,sPath), "Uint", 0, "UintP", pidl, "Uint", 0, "Uint", 0)
Else DllCall("shell32\SHGetFolderLocation", "Uint", 0, "int", sPath, "Uint", 0, "Uint", 0, "UintP", pidl)
DllCall("shell32\SHBindToParent", "Uint", pidl, "Uint", GUID4String(IID_IShellFolder,"{000214E6-0000-0000-C000-000000000046}"), "UintP", psf, "UintP", pidlChild)
DllCall(NumGet(NumGet(1*psf)+40), "Uint", psf, "Uint", 0, "Uint", 1, "UintP", pidlChild, "Uint", GUID4String(IID_IContextMenu,"{000214E4-0000-0000-C000-000000000046}"), "Uint", 0, "UintP", pcm)
Release(psf)
CoTaskMemFree(pidl)
hMenu := DllCall("CreatePopupMenu")
DllCall(NumGet(NumGet(1*pcm)+12), "Uint", pcm, "Uint", hMenu, "Uint", 0, "Uint", 3, "Uint", 0x7FFF, "Uint", 0) ; QueryContextMenu
DetectHiddenWindows, On
Process, Exist
WinGet, hAHK, ID, ahk_pid %ErrorLevel%
WinActivate, ahk_id %hAHK%
Global pcm2 := QueryInterface(pcm,IID_IContextMenu2:="{000214F4-0000-0000-C000-000000000046}")
Global pcm3 := QueryInterface(pcm,IID_IContextMenu3:="{BCFCE0A0-EC17-11D0-8D10-00A0C90F2719}")
Global WPOld:= DllCall("SetWindowLong", "Uint", hAHK, "int",-4, "int",RegisterCallback("WindowProc"))
DllCall("GetCursorPos", "int64P", pt)
DllCall("InsertMenu", "Uint", hMenu, "Uint", 0, "Uint", 0x0400|0x800, "Uint", 2, "Uint", 0)
DllCall("InsertMenu", "Uint", hMenu, "Uint", 0, "Uint", 0x0400|0x002, "Uint", 1, "Uint", &sPath)
idn := DllCall("TrackPopupMenu", "Uint", hMenu, "Uint", 0x0100, "int", pt << 32 >> 32, "int", pt >> 32, "Uint", 0, "Uint", hAHK, "Uint", 0)
NumPut(VarSetCapacity(ici,64,0),ici), NumPut(0x4000|0x20000000,ici,4), NumPut(1,NumPut(hAHK,ici,8),12), NumPut(idn-3,NumPut(idn-3,ici,12),24), NumPut(pt,ici,56,"int64")
DllCall(NumGet(NumGet(1*pcm)+16), "Uint", pcm, "Uint", &ici) ; InvokeCommand
; VarSetCapacity(sName,259), DllCall(NumGet(NumGet(1*pcm)+20), "Uint", pcm, "Uint", idn-3, "Uint", 1, "Uint", 0, "str", sName, "Uint", 260) ; GetCommandString
DllCall("GlobalFree", "Uint", DllCall("SetWindowLong", "Uint", hAHK, "int", -4, "int", WPOld))
DllCall("DestroyMenu", "Uint", hMenu)
Release(pcm3)
Release(pcm2)
Release(pcm)
CoUninitialize()
pcm:=pcm2:=pcm3:=WPOld:=0
}
WindowProc(hWnd, nMsg, wParam, lParam)
{ Critical
Global pcm, pcm2, pcm3, WPOld
If (nMsg = 287) ; WM_MENUSELECT
{ MenuItem := wParam & 65535
VarSetCapacity(sHelp,257,79)
DllCall(NumGet(NumGet(1*pcm)+20), "Uint", pcm, "Uint", MenuItem-3, "Uint", GCS_HELPTEXTA:=1, "Uint", 0, "str", sHelp, "Uint", 256) ; GetCommandString
if sHelp = %SbText%
MsgBox, Hey
}
If pcm3
{ If !DllCall(NumGet(NumGet(1*pcm3)+28), "Uint", pcm3, "Uint", nMsg, "Uint", wParam, "Uint", lParam, "UintP", lResult)
Return lResult
}
Else If pcm2
{ If !DllCall(NumGet(NumGet(1*pcm2)+24), "Uint", pcm2, "Uint", nMsg, "Uint", wParam, "Uint", lParam)
Return 0
}
Return DllCall("user32.dll\CallWindowProcA", "Uint", WPOld, "Uint", hWnd, "Uint", nMsg, "Uint", wParam, "Uint", lParam,"Uint")
}
#Include CoHelper.ahk
It triggers a msgbox if the status bar text is found, but ultimately I'd like it to, Send {ENTER} or ControlClick to choose the menu item.
The Crawler script isn't ready yet (but that's not important yet) - it's just supposed to crawl through the context menu as quickly as ShellContextMenu allows. When it passes over the menu item we are trying to reach, Modified_ShellContextMenu is supposed to recognize the status bar text, and hit enter.
Code:
DetectHiddenWindows, on ; Might allow detection of menu sooner.
WinWait, ahk_class #32768,,2
IfWinExist, ahk_class #32768
{ id := WinExist( ahk_class #32768 )
ControlSend,,{Down 3}, ahk_id %id%
loop, 100
{ ControlSend,,{Down}, ahk_id %id%
Sleep, 50
Send, {Right}
Sleep, 50
Send, {Down}
Sleep, 50
;Send, {Left}
;Sleep, 50
IfWinNotExist, ahk_id %id%
Break
}
}
else
msgbox, boo hoo
return
What do you think? If there's a slick way to do this then all the better, but right now I'm wondering if you see any obvious flaws in the Modified_ShellContextMenu, or whether I could just use the original, and instead have the Crawler script hit the menu with DllCalls to retrieve the status bar text? (the way I can use GetStatusBarText() to get it when at an explorer window)
FWIW, I've been using the following function for FileMenu Tools - it's okay most of the time because I have the target files selected in front of me in an active explorer window, but remote access would come in handy every now and then - actually it uses your ShellDesktop and SelectItem codes to get around the problem of no status bar at Desktop view:
Code:
#NoEnv
GetMenuItemBySBar(SBar_text, TopSubText="")
{ WinGetActiveTitle, Active_title
if Active_title = Program Manager ; switches to folder view if on desktop
Goto, DesktopVer
WinGet, WindowUniqueID, ID, A
Send, {APPSKEY}
loop, 70 ; number high enough to cover all menu items
{ SendInput {DOWN}
StatusBarGetText, StatusBarText, 1, ahk_id %WindowUniqueID%
IfInString, StatusBarText, %SBar_text% ;if (StatusBarText = SBar_text)
{ SendInput {ENTER}
break
}
else if (TopSubText != "TripleZero") and (StatusBarText = "") ; skips if no 2nd arg
{ SendInput {RIGHT}
StatusBarGetText, StatusBarText, 1, ahk_id %WindowUniqueID%
if (StatusBarText != TopSubText)
SendInput {LEFT}
}
if (A_INDEX > 70)
MsgBox Didn't find "%SBar_text%" nor no window with status bar available
}
return
DesktopVer:
sSelect := ShellDesktop()
WinActivate, Desktop, FolderView
SelectItem(sSelect)
Sleep, 100
Send, {APPSKEY}
loop, 70 ; number high enough to cover all menu items
{ SendInput {DOWN}
StatusBarGetText, StatusBarText, 1, Desktop, FolderView
IfInString, StatusBarText, %SBar_text%
{ SendInput {ENTER}
break
}
else if StatusBarText =
{ SendInput {RIGHT}
StatusBarGetText, StatusBarText, 1, Desktop, FolderView
if (StatusBarText != TopSubText)
SendInput {LEFT}
}
if (A_INDEX > 70)
MsgBox Didn't find "%SBar_text%" status bar text `nor no window with status bar available
}
WinClose, Desktop, , 2
return
}