Page 1 of 1

How to get the ShortcutKey of a shortcut

Posted: 02 Dec 2019, 17:11
by JoeWinograd
Hi Folks,
The FileCreateShortcut command includes an option to specify a ShortcutKey. However, the FileGetShortcut command does not retrieve that as one of the output variables. Is there some other way to determine what the ShortcutKey is? My goal is to save and restore all aspects of a shortcut, but the inability to get the ShortcutKey via FileGetShortcut makes that impossible to do via FileCreateShortcut, as far as I can tell. Thanks much, Joe

Re: How to get the ShortcutKey of a shortcut

Posted: 02 Dec 2019, 18:41
by teadrinker
Hi
Try this:

Code: Select all

lnk := A_Desktop . "\FreeMind.lnk"

S_OK := 0, STGM_READ := 0
CLSID_ShellLink  := "{00021401-0000-0000-C000-000000000046}"
IID_IShellLinkA  := "{000214EE-0000-0000-C000-000000000046}"
IID_IShellLinkW  := "{000214F9-0000-0000-C000-000000000046}"
IID_IPersistFile := "{0000010B-0000-0000-C000-000000000046}"
IID_IShellLink := A_IsUnicode ? IID_IShellLinkW : IID_IShellLinkA

HOTKEYF_SHIFT   := 0x1
HOTKEYF_CONTROL := 0x2
HOTKEYF_ALT     := 0x4
HOTKEYF_EXT     := 0x8

IShellLink := ComObjCreate(CLSID_ShellLink, IID_IShellLink)
GetHotkey := 12
IPersistFile := ComObjQuery(IShellLink, IID_IPersistFile)
Load      := 5

XFmt := Func("Format").Bind("{:#x}")

Loop 1 {
   hr := Vtable(IPersistFile, Load).Call("Ptr", &lnk, "UInt", STGM_READ, "UInt")
   if (hr != S_OK && error := "IPersistFile::Load failed, error: " . XFmt.Call(hr))
      break
   
   hr := Vtable(IShellLink, GetHotkey).Call("UShortP", buff, "UInt")
   (hr != S_OK && error := "IShellLink::GetHotkey failed, error: " . XFmt.Call(hr))
}
ObjRelease(IPersistFile)
ObjRelease(IShellLink)
if error
   throw Exception(error)

key := GetKeyName("vk" . Format("{:X}", buff & 0xFF))
mod := buff >> 8
Shift := mod & HOTKEYF_SHIFT   ? "Shift + " : ""
Ctrl :=  mod & HOTKEYF_CONTROL ? "Ctrl + "  : ""
Alt :=   mod & HOTKEYF_ALT     ? "Alt + "   : ""
Ext :=   mod & HOTKEYF_EXT     ? "Numpad"   : "" ; not sure

MsgBox, % Shift . Ctrl . Alt . Ext . key

Vtable(ptr, n) {
   return Func("DllCall").Bind(NumGet(NumGet(ptr+0), A_PtrSize*n), "Ptr", ptr)
}
I'm not sure about HOTKEYF_EXT.

Re: How to get the ShortcutKey of a shortcut

Posted: 02 Dec 2019, 20:16
by jeeswg
For reference, I wrote something similar here:
Get shortcut target without evaluating environment variables - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=66420

Re: How to get the ShortcutKey of a shortcut

Posted: 02 Dec 2019, 20:34
by JoeWinograd
Hi teadrinker,
Superb! Works perfectly! :thumbup: HOTKEYF_EXT (NumPad) works fine, too. Tested on both W7 and W10. Thanks very much! My only problem now is that FileCreateShortcut creates all shortcut keys as Ctrl+Alt shortcuts. :( Cheers, Joe

Re: How to get the ShortcutKey of a shortcut

Posted: 02 Dec 2019, 20:37
by JoeWinograd
Hi jeeswg,
Thanks for the link...I'll have a look at it. Regards, Joe

Re: How to get the ShortcutKey of a shortcut

Posted: 02 Dec 2019, 20:38
by jeeswg
My link includes an alternative version of FileCreateShortcut. :)

Re: How to get the ShortcutKey of a shortcut

Posted: 02 Dec 2019, 20:52
by JoeWinograd
jeeswg wrote:an alternative version of FileCreateShortcut
Does it support other than Ctrl+Alt shortcuts?

Re: How to get the ShortcutKey of a shortcut

Posted: 02 Dec 2019, 20:59
by jeeswg
That was the main reason I wrote the function, to handle shortcut keys generally.
Although the functions may not handle HOTKEYF_EXT (NumPad) correctly. So you might want to tweak them.

That said, if you just wanted to store shortcut information, then restore it, you could create modified versions of the functions, that get/set the raw shortcut key number, obtained via IShellLink::GetHotkey/IShellLink::SetHotkey, but that do not convert them to a friendly string.

Re: How to get the ShortcutKey of a shortcut

Posted: 02 Dec 2019, 21:18
by JoeWinograd
jeeswg wrote:if you just wanted to store shortcut information, then restore it
Yes, that's my entire purpose for this! I want to be able to delete a shortcut from the desktop, then restore it at a later time, fully intact.

Re: How to get the ShortcutKey of a shortcut

Posted: 02 Dec 2019, 23:57
by JoeWinograd
Hi jeeswg,
One of your comments at the thread you posted says that you updated JEE_FileGetShortcut and JEE_FileCreateShortcut. I'd like to be sure that I have the latest versions of them...please post links to both. Thanks, Joe

Re: How to get the ShortcutKey of a shortcut

Posted: 03 Dec 2019, 00:24
by jeeswg
The most up-to-date version of each function, would be the lowest-down appearance of the function definition on that page. Cheers.

Re: How to get the ShortcutKey of a shortcut

Posted: 03 Dec 2019, 03:12
by JoeWinograd
Hi jeeswg,

I think I got the latest ones...JEE_FileCreateShortcut is here:
https://www.autohotkey.com/boards/viewtopic.php?f=5&t=66420#p287004

But I cannot get it to create the shortcut key...can you?

Running on W7/64-bit with v1.1.32.00/U64, if that matters. The test script that I'm using is below. It creates the shortcut with everything set properly...except for the shortcut key...that displays in the shortcut's Properties as None. Regards, Joe

Code: Select all

vTarget:="c:\temp\test jeeswg.txt"
vPathLink:=A_Desktop . "\test jeeswg link " . A_Now . ".lnk"
vWorkingDir:=A_ScriptDir
vArgs:="a b c"
vDescription:="shortcut description"
vIconFile:=A_AhkPath
vShortcutKey:="^!+q"
vIconNum:=4
vRunState:=1
JEE_FileCreateShortcut(vTarget,vPathLink,vWorkingDir,vArgs,vDescription,vIconFile,vShortcutKey,vIconNum,vRunState)
MsgBox,4096,Create Done,%vPathLink%
ExitApp

JEE_FileCreateShortcut(vTarget, vPathLink, vWorkingDir:="", vArgs:="", vDescription:="", vIconFile:="", vShortcutKey:="", vIconNum:=0, vRunState:="")
{
  local
  static IID_IShellLinkW := "{000214F9-0000-0000-C000-000000000046}"
  static CLSID_ShellLink := "{00021401-0000-0000-C000-000000000046}"
  static IID_IPersistFile := "{0000010b-0000-0000-C000-000000000046}"
  if !(pSL := ComObjCreate(CLSID_ShellLink, IID_IShellLinkW))
    return 0

  ;set Target
  ;SLGP_RAWPATH := 0x4 ;SLGP_UNCPRIORITY := 0x2 ;SLGP_SHORTPATH := 0x1 ;source: ShObjIdl.h
  if DllCall(NumGet(NumGet(pSL+0) + 20*A_PtrSize), "Ptr",pSL, "WStr",vTarget) ;IShellLink::SetPath
    return 0

  ;set WorkingDir
  if !(vWorkingDir = "")
  && DllCall(NumGet(NumGet(pSL+0) + 9*A_PtrSize), "Ptr",pSL, "WStr",vWorkingDir) ;IShellLink::SetWorkingDirectory
    return 0

  ;set Args
  if !(vArgs = "")
  && DllCall(NumGet(NumGet(pSL+0) + 11*A_PtrSize), "Ptr",pSL, "WStr",vArgs) ;IShellLink::SetArguments
    return 0

  ;set Description
  if !(vDescription = "")
  && DllCall(NumGet(NumGet(pSL+0) + 7*A_PtrSize), "Ptr",pSL, "WStr",vDescription) ;IShellLink::SetDescription
    return 0

  ;set IconFile and IconNum
  if !(vIconFile = "")
  && DllCall(NumGet(NumGet(pSL+0) + 17*A_PtrSize), "Ptr",pSL, "WStr",vIconFile, "Int",vIconNum) ;IShellLink::SetIconLocation
    return 0

  if !(vShortcutKey = "")
  {
    vHotkeyNum := 0
    if RegExMatch(vShortcutKey, "^.?\+.")
    {
      vHotkeyNum |= (0x1 << 8) ;HOTKEYF_SHIFT := 0x1
      vShortcutKey := StrReplace(vShortcutKey, "+", "",, 1)
    }
    if RegExMatch(vShortcutKey, "^.?\^.")
    {
      vHotkeyNum |= (0x2 << 8) ;HOTKEYF_CONTROL := 0x2
      vShortcutKey := StrReplace(vShortcutKey, "^", "",, 1)
    }
    if RegExMatch(vShortcutKey, "^.?!.")
    {
      vHotkeyNum |= (0x4 << 8) ;HOTKEYF_ALT := 0x4
      vShortcutKey := StrReplace(vShortcutKey, "!", "",, 1)
    }
    if vVK := GetKeyVK(vShortcutKey)
      vHotkeyNum |= vVK
    if vVK
    && DllCall(NumGet(NumGet(pSL+0) + 13*A_PtrSize), "Ptr",pSL, "UShort",vHotkeyNum) ;IShellLink::SetHotkey
      return 0
  }

  if !(vRunState = "")
  && DllCall(NumGet(NumGet(pSL+0) + 15*A_PtrSize), "Ptr",pSL, "Int",vRunState) ;IShellLink::SetShowCmd
    return 0

  if !(pPF := ComObjQuery(pSL, IID_IPersistFile))
    return 0
  vSize := DllCall("kernel32\GetFullPathNameW", "WStr","\\?\" vPathLink, "UInt",0, "Ptr",0, "Ptr*",0, "UInt")
  VarSetCapacity(vPath2, vSize*2, 0)
  if !DllCall("kernel32\GetFullPathNameW", "WStr","\\?\" vPathLink, "UInt",vSize, "Ptr",&vPath2, "Ptr*",0, "UInt")
    return 0
  if DllCall(NumGet(NumGet(pPF+0) + 6*A_PtrSize), "Ptr",pPF, "Ptr",&vPath2, "Int",1) ;IPersistFile::Save
    return 0

  ObjRelease(pPF)
  ObjRelease(pSL)
  return 1
}

Re: How to get the ShortcutKey of a shortcut

Posted: 03 Dec 2019, 03:27
by jeeswg
Try vShortcutKey:="+^!q".
There's a slight issue with the RegEx, I'll post again when I've replaced it with something slightly more intuitive.

Re: How to get the ShortcutKey of a shortcut

Posted: 03 Dec 2019, 03:50
by JoeWinograd
vShortcutKey:="+^!q"
That works!
I'll post again when I've replaced it with something slightly more intuitive.
Looking forward to it, but it's almost 3:00am in my neck of the woods, so this is my last post for the night. Will check back into the thread first thing in my morning. Regards, Joe

Re: How to get the ShortcutKey of a shortcut

Posted: 03 Dec 2019, 04:35
by jeeswg
OK. Done.

I'm thinking we need a built-in HotkeySplit function.
So +^!a would split to +^! and a.

Code: Select all

;q:: ;test split hotkeys
vList := " ;continuation section
(
!+^q
!^+q
+!^q
+^!q
^!+q
^+!q
!+q
!^q
+!q
+^q
^!q
^+q
!q
+q
^q
q
)"

vList2 := vList
vList2 .= "`n" StrReplace(vList, "q", "Left")
vList2 .= "`n" StrReplace(vList, "q", "")
vList2 .= "`n" StrReplace(vList, "q", "+")
vList2 .= "`n" StrReplace(vList, "q", "^")
vList2 .= "`n" StrReplace(vList, "q", "!")

vIsV1 := RegExMatch("", "", o) && !IsObject(o)
vPfx := vIsV1 ? "O)" : ""
vOutput := ""
Loop Parse, vList2, % "`n", % "`r"
{
	vHotkey := A_LoopField
	RegExMatch(vHotkey, vPfx "^([+^!]{0,3})(.+)$", oMatch)
	vOutput .= vHotkey "`t" oMatch.1 "`t" oMatch.2 "`r`n"
}
Clipboard := vOutput
MsgBox, % "done"
return

That might seem like a relatively minor function to have.
However, the way AHK parses hotkeys is pretty complex, and only if it were built-in, would it be really reliable.
And extra modifiers like $ would have to be handled also.
So I think it's just about worth implementing, and hopefully should be relatively trivial since AutoHotkey already contains the logic to do this.

Similarly, it would be useful to have an IsHotkey or IsValidHotkey function.
One use would be in parsing AHK scripts.
does string look like a hotkey - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=58131

Cheers.

Re: How to get the ShortcutKey of a shortcut

Posted: 03 Dec 2019, 15:08
by JoeWinograd
Hi jeeswg,

Thanks for the updates. Just tested both on W7...worked perfectly! Btw, you should change a comment there...both sentences before the links say, "Get shortcut target..." You should change the second one to say, "Create shortcut target..."

Interesting idea on the HotkeySplit and IsValidHotkey functions...post them on the Wish List.

Thanks again to both teadrinker and you...great stuff! Regards, Joe

Re: How to get the ShortcutKey of a shortcut

Posted: 03 Dec 2019, 15:18
by jeeswg
In the post, I'd written the thread title (which starts with 'Get shortcut target') before each of the 2 urls, I've now removed the thread titles, leaving just the urls. Good suggestion. Thanks.