Hello,
I'm working on a script which replace shortcut targets with the corresponding environment variables, for a large number of files.
But once the replacement is done, FileGetShortcut returns a target path with evaluated environment variables, so the script will process the same files again and again. (However, this is oddly not the case with the startup directory).
Here is an example with a shortcut that already has an environment variable in its target path:
#NoEnv
shortcutPath := "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Immersive Control Panel.lnk"
FileGetShortcut, %shortcutPath%, shortcutTargetPath, shortcutStartupPath
MsgBox, shortcutTargetPath: %shortcutTargetPath%`nshortcutStartupPath: %shortcutStartupPath%
Returns:
shortcutTargetPath: C:\Windows\System32\Control.exe
shortcutStartupPath: %HOMEDRIVE%%HOMEPATH%
Expected:
shortcutTargetPath: %windir%\System32\Control.exe
shortcutStartupPath: %HOMEDRIVE%%HOMEPATH%
I also tried to open file properties and get "Edit" controls texts, but it's way too slow for a large number of files.
So is there another way to retrieve the target path "literaly"?
Thanks!
Get shortcut target without evaluating environment variables
Re: Get shortcut target without evaluating environment variables
I have a function based on the code here, it returns paths without expanding the environment variables:
COM QueryInterface - Page 3 - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/74471-com-queryinterface/page-3
So FileCreateShortcut appears to be working correctly.
And FileGetShortcut is using an equivalent to ExpandEnvironmentStrings: SLGP_RAWPATH to maintain environment variables, off to expand environment variables.
The source code uses SLGP_UNCPRIORITY (but not SLGP_RAWPATH):
script_autoit.cpp
psl->GetPath(buf, MAX_PATH, NULL, SLGP_UNCPRIORITY);
The function above uses SLGP_RAWPATH.
Link:
IShellLinkA::GetPath (shobjidl_core.h) | Microsoft Docs
https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishelllinka-getpath
COM QueryInterface - Page 3 - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/74471-com-queryinterface/page-3
Code: Select all
q:: ;test shortcuts
;MsgBox, % EnvGet("HOMEDRIVE") "`r`n" EnvGet("HOMEPATH")
vPathLnk := A_Desktop "\New Text Document.lnk"
;vDesktop := A_Desktop
vDesktop := "%HOMEDRIVE%%HOMEPATH%\Desktop"
vTarget := vDesktop "\New Text Document.txt"
vWorkingDir := vDesktop
FileCreateShortcut, % vTarget, % vPathLnk, % vWorkingDir
FileGetShortcut, % vPathLnk, vTarget, vWorkingDir
MsgBox, % Format("{}`r`n{}`r`n{}", vPathLnk, vTarget, vWorkingDir)
JEE_FileGetShortcut(vPathLnk, vTarget, vWorkingDir)
MsgBox, % Format("{}`r`n{}`r`n{}", vPathLnk, vTarget, vWorkingDir)
return
;==================================================
;FileGetShortcut output:
;C:\Users\me\Desktop\New Text Document.lnk
;C:\Users\me\Desktop\New Text Document.txt
;%HOMEDRIVE%%HOMEPATH%\Desktop
;JEE_FileGetShortcut output:
;C:\Users\me\Desktop\New Text Document.lnk
;%HOMEDRIVE%%HOMEPATH%\Desktop\New Text Document.txt
;%HOMEDRIVE%%HOMEPATH%\Desktop
;==================================================
;w:: ;test expand environment variables
vTarget := "C:\Users\%username%\Desktop"
vChars := 20000
VarSetCapacity(vTarget2, vChars*2)
DllCall("kernel32\ExpandEnvironmentStrings", "Str",vTarget, "Str",vTarget2, "UInt",vChars, "UInt")
MsgBox, % vTarget2
return
;==================================================
;based on IShellLinkResolve() by Guests
;COM QueryInterface - Page 3 - Ask for Help - AutoHotkey Community
;https://autohotkey.com/board/topic/74471-com-queryinterface/page-3#entry475169
;get shortcut info
;icon number here is 0-based, AutoHotkey command icon number is 1-based
;unlike AutoHotkey command, this also retrieves the shortcut key
;e.g. FileGetShortcut, % vPath, vTarget, vWorkingDir, vArgs, vDescription, vIconFile, vIconNum, vRunState
;e.g. JEE_FileGetShortcut(vPath, vTarget, vWorkingDir, vArgs, vDescription, vIconFile, vIconNum, vRunState, vShortcutKey)
;JEE_FileLnkGetInfo
;JEE_FileLnkGetDetails
JEE_FileGetShortcut(vPath, ByRef vTarget:="", ByRef vWorkingDir:="", ByRef vArgs:="", ByRef vDescription:="", ByRef vIconFile:="", ByRef vIconNum:="", ByRef vRunState:="", ByRef vShortcutKey:="")
{
local
static IID_IShellLinkW := "{000214F9-0000-0000-C000-000000000046}"
static IID_IShellLinkA := "{000214EE-0000-0000-C000-000000000046}"
static IID_IShellLink := A_IsUnicode ? IID_IShellLinkW : IID_IShellLinkA
static CLSID_ShellLink := "{00021401-0000-0000-C000-000000000046}"
static IID_IPersistFile := "{0000010b-0000-0000-C000-000000000046}"
if !(pSL := ComObjCreate(CLSID_ShellLink, IID_IShellLink))
return 0
if !(pPF := ComObjQuery(pSL, IID_IPersistFile))
return 0
;STGM_READ := 0x0
if DllCall(NumGet(NumGet(pPF+0)+5*A_PtrSize), "Ptr",pPF, "WStr",vPath, "UInt",0x0) ;IPersistFile::Load
return 0
;SLR_NO_UI := 0x1
if DllCall(NumGet(NumGet(pSL+0)+19*A_PtrSize), "Ptr",pSL, "Ptr",A_ScriptHwnd, "UInt",0x1) ;IShellLink::Resolve
return 0
;get Target
;SLGP_RAWPATH := 0x4 ;SLGP_UNCPRIORITY := 0x2 ;SLGP_SHORTPATH := 0x1 ;source: ShObjIdl.h
VarSetCapacity(WIN32_FIND_DATA, 592)
VarSetCapacity(vTarget, 260*2)
if DllCall(NumGet(NumGet(pSL+0)+3*A_PtrSize), "Ptr",pSL, "Str",vTarget, "Int",260, "Ptr",&WIN32_FIND_DATA, "UInt",0x4) ;IShellLink::GetPath
return 0
;get WorkingDir
VarSetCapacity(vWorkingDir, 260*2)
if DllCall(NumGet(NumGet(pSL+0)+8*A_PtrSize), "Ptr",pSL, "Str",vWorkingDir, "Int",260) ;IShellLink::GetWorkingDirectory
return 0
;get Args
VarSetCapacity(vArgs, 260*2)
if DllCall(NumGet(NumGet(pSL+0)+10*A_PtrSize), "Ptr",pSL, "Str",vArgs, "Int",260) ;IShellLink::GetArguments
return 0
;get Description
VarSetCapacity(vDescription, 260*2)
if DllCall(NumGet(NumGet(pSL+0)+6*A_PtrSize), "Ptr",pSL, "Str",vDescription, "Int",260) ;IShellLink::GetDescription
return 0
;get IconFile and IconNum
VarSetCapacity(vIconFile, 260*2)
VarSetCapacity(vIconNum, 4)
if DllCall(NumGet(NumGet(pSL+0)+16*A_PtrSize), "Ptr",pSL, "Str",vIconFile, "Int",260, "Ptr",&vIconNum) ;IShellLink::GetIconLocation
return 0
vIconNum := NumGet(vIconNum, 0, "Int")
;get RunState
VarSetCapacity(vRunState, 4)
if DllCall(NumGet(NumGet(pSL+0)+14*A_PtrSize), "Ptr",pSL, "Ptr",&vRunState) ;IShellLink::GetShowCmd
return 0
vRunState := NumGet(vRunState, "Int")
;get ShortcutKey
VarSetCapacity(vShortcutKey, 2)
if DllCall(NumGet(NumGet(pSL+0)+12*A_PtrSize), "Ptr",pSL, "Ptr",&vShortcutKey) ;IShellLink::GetHotkey
return 0
vLoByte := NumGet(vShortcutKey, 0, "UChar")
vHiByte := NumGet(vShortcutKey, 1, "UChar")
vShortcutKey := ""
if (vHiByte & 0x2) ;HOTKEYF_CONTROL := 0x2 ;source: CommCtrl.h
vShortcutKey .= "^"
if (vHiByte & 0x1) ;HOTKEYF_SHIFT := 0x1
vShortcutKey .= "+"
if (vHiByte & 0x4) ;HOTKEYF_ALT := 0x4
vShortcutKey .= "!"
vShortcutKey .= GetKeyName(Format("vk{:x}", vLoByte))
ObjRelease(pPF)
ObjRelease(pSL)
return 1
}
And FileGetShortcut is using an equivalent to ExpandEnvironmentStrings: SLGP_RAWPATH to maintain environment variables, off to expand environment variables.
The source code uses SLGP_UNCPRIORITY (but not SLGP_RAWPATH):
script_autoit.cpp
psl->GetPath(buf, MAX_PATH, NULL, SLGP_UNCPRIORITY);
The function above uses SLGP_RAWPATH.
Link:
IShellLinkA::GetPath (shobjidl_core.h) | Microsoft Docs
https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishelllinka-getpath
Last edited by jeeswg on 03 Dec 2019, 04:06, edited 3 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Re: Get shortcut target without evaluating environment variables
Thank you very much for your code, it works well!
To edit shortcuts, I use FileCreateShortcut, but as hotkeys are partially supported, is there any code which call the IShellLink::SetHotkey function?
(And I was wondering how do you define which address corresponds to which IShellLink function?)
Thanks!
To edit shortcuts, I use FileCreateShortcut, but as hotkeys are partially supported, is there any code which call the IShellLink::SetHotkey function?
(And I was wondering how do you define which address corresponds to which IShellLink function?)
Thanks!
Re: Get shortcut target without evaluating environment variables
AHK's FileCreateShortcut has a ShortcutKey parameter.
AHK's FileGetShortcut *does not* have a ShortcutKey parameter.
My FileGetShortcut variant above has a ShortcutKey parameter.
This is where AutoHotkey sets the shortcut key:
script_autoit.cpp
psl->SetHotkey( (WORD)vk | ((WORD)(HOTKEYF_CONTROL | HOTKEYF_ALT) << 8) );
For the methods, I determined the addresses by searching for IShellLinkWVtbl in .h files in:
C:\Program Files (x86)\Windows Kits\8.1
a folder that came with Visual Studio Express for Windows Desktop.
There may be other similar folders, for other Windows versions, depending on your version of Visual Studio.
Info for the methods is under IShellLinkWVtbl in ShObjIdl.h:
AHK's FileGetShortcut *does not* have a ShortcutKey parameter.
My FileGetShortcut variant above has a ShortcutKey parameter.
This is where AutoHotkey sets the shortcut key:
script_autoit.cpp
psl->SetHotkey( (WORD)vk | ((WORD)(HOTKEYF_CONTROL | HOTKEYF_ALT) << 8) );
For the methods, I determined the addresses by searching for IShellLinkWVtbl in .h files in:
C:\Program Files (x86)\Windows Kits\8.1
a folder that came with Visual Studio Express for Windows Desktop.
There may be other similar folders, for other Windows versions, depending on your version of Visual Studio.
Info for the methods is under IShellLinkWVtbl in ShObjIdl.h:
Code: Select all
0 QueryInterface
1 AddRef
2 Release
3 GetPath
4 GetIDList
5 SetIDList
6 GetDescription
7 SetDescription
8 GetWorkingDirectory
9 SetWorkingDirectory
10 GetArguments
11 SetArguments
12 GetHotkey
13 SetHotkey
14 GetShowCmd
15 SetShowCmd
16 GetIconLocation
17 SetIconLocation
18 SetRelativePath
19 Resolve
20 SetPath
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Re: Get shortcut target without evaluating environment variables
The problem is that FileCreateShortcut only supports "Ctrl+Alt" hotkeys combinaisons in its key parameter. I would like to have a SetHotkey function that supports all modifiers, as in the IShellLink::GetHotKey function. This is why I was wondering if there is an AutoHotkey DllCall for IShellLink::SetHotkey.
Thanks for the explanation!
Thanks for the explanation!
Re: Get shortcut target without evaluating environment variables
- Oh dear, as you say, FileCreateShortcut only supports Ctrl+Alt hotkeys.
FileCreateShortcut - Syntax & Usage | AutoHotkey
https://www.autohotkey.com/docs/commands/FileCreateShortcut.htm
- It may be a remnant of the old AutoIt code.
- I've basically recreated FileCreateShortcut below, allowing you to choose your own hotkey.
- I based it on script_autoit.cpp in the AHK source code.
- According to the .lnk file properties, RunState and ShortcutKey are being correctly set, but I'm not sure of a good piece of software to test RunState, and how exactly ShortcutKey works.
- While this link mentions Desktop/the Start Menu, there may be other nuances.
FileCreateShortcut - Syntax & Usage | AutoHotkey
https://www.autohotkey.com/docs/commands/FileCreateShortcut.htm
- Links:
IShellLinkW (shobjidl_core.h) | Microsoft Docs
https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ishelllinkw
IPersistFile (objidl.h) | Microsoft Docs
https://docs.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-ipersistfile
FileCreateShortcut - Syntax & Usage | AutoHotkey
https://www.autohotkey.com/docs/commands/FileCreateShortcut.htm
- It may be a remnant of the old AutoIt code.
- I've basically recreated FileCreateShortcut below, allowing you to choose your own hotkey.
- I based it on script_autoit.cpp in the AHK source code.
Code: Select all
q:: ;test create shortcut
vTarget := A_Desktop "\command-line parameters.ahk"
vOutput = MsgBox, `% DllCall("kernel32\GetCommandLine", "Str")
if !FileExist(vTarget)
FileAppend, % vOutput "`r`n", % "*" vTarget, UTF-8
;vTarget := A_Desktop "\New Text Document.txt"
vPathLink := A_Desktop "\z link " A_Now ".lnk"
vWorkingDir := A_ScriptDir
vArgs := "a b c"
;vArgs := ""
vDescription := "I am the description"
vIconFile := A_AhkPath
vShortcutKey := "^!+q"
vIconNum := 4
vRunState := 7
;RunState:
;FileCreateShortcut - Syntax & Usage | AutoHotkey
;https://www.autohotkey.com/docs/commands/FileCreateShortcut.htm
;1 - Normal (this is the default)
;3 - Maximized
;7 - Minimized
JEE_FileCreateShortcut(vTarget, vPathLink, vWorkingDir, vArgs, vDescription, vIconFile, vShortcutKey, vIconNum, vRunState)
MsgBox, % "done"
return
;==================================================
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
;set ShortcutKey
if !(vShortcutKey = "")
{
vHotkeyNum := 0
if RegExMatch(vShortcutKey, "^.{0,2}\+.")
{
vHotkeyNum |= (0x1 << 8) ;HOTKEYF_SHIFT := 0x1
vShortcutKey := StrReplace(vShortcutKey, "+", "",, 1)
}
if RegExMatch(vShortcutKey, "^.{0,2}\^.")
{
vHotkeyNum |= (0x2 << 8) ;HOTKEYF_CONTROL := 0x2
vShortcutKey := StrReplace(vShortcutKey, "^", "",, 1)
}
if RegExMatch(vShortcutKey, "^.{0,2}!.")
{
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
}
;==================================================
- While this link mentions Desktop/the Start Menu, there may be other nuances.
FileCreateShortcut - Syntax & Usage | AutoHotkey
https://www.autohotkey.com/docs/commands/FileCreateShortcut.htm
- Btw I corrected GetIconLocation in an earlier post from Int to Ptr.The ShortcutKey of a newly created shortcut will have no effect unless the shortcut file resides on the desktop or somewhere in the Start Menu. If the ShortcutKey you choose is already in use, your new shortcut takes precedence.
- Links:
IShellLinkW (shobjidl_core.h) | Microsoft Docs
https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ishelllinkw
IPersistFile (objidl.h) | Microsoft Docs
https://docs.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-ipersistfile
Last edited by jeeswg on 03 Dec 2019, 15:19, edited 5 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Re: Get shortcut target without evaluating environment variables
Thank you very much for your code and explanations, it seems to work as expected!
Re: Get shortcut target without evaluating environment variables
That's great!
I've updated JEE_FileGetShortcut and JEE_FileCreateShortcut above, so they should work with both Unicode and ANSI builds of AHK.
I've updated JEE_FileGetShortcut and JEE_FileCreateShortcut above, so they should work with both Unicode and ANSI builds of AHK.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Re: Get shortcut target without evaluating environment variables
I updated the 2 functions, here are links to the right posts:
JEE_FileGetShortcut:
https://autohotkey.com/boards/viewtopic.php?f=5&t=66420&p=285401#p285401
JEE_FileCreateShortcut:
https://autohotkey.com/boards/viewtopic.php?f=5&t=66420&p=287004#p287004
I fixed 3 RegEx lines, from .? to .{0,2}
JEE_FileGetShortcut:
https://autohotkey.com/boards/viewtopic.php?f=5&t=66420&p=285401#p285401
JEE_FileCreateShortcut:
https://autohotkey.com/boards/viewtopic.php?f=5&t=66420&p=287004#p287004
I fixed 3 RegEx lines, from .? to .{0,2}
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Re: Get shortcut target without evaluating environment variables
Thank you for the updates!
Who is online
Users browsing this forum: No registered users and 171 guests