1

After I found out that sometimes shortcut targets cannot be queried by WScript.Shell object, but in case of advertised shortcuts need to be queried by WindowsInstaller.Installer object, I searched the web and found the promising solution below.

But either the solution is outdated or written for another version of Windows (I use PowerShell 5.1 on 64-bit Windows 10). It gives me only the following error message for advertised shortcuts like %AppData%\Microsoft\Windows\Start Menu\Programs\System Tools*.lnk:

Ausnahme beim Aufrufen von "InvokeMember" mit 5 Argument(en):  "ShortcutTarget"

which literally translates to:

Exception while calling "InvokeMember" with 5 arguments: "ShortcutTarget"

In addition, I see the following lines with no additional information for me:

    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : COMException

Unfortunately, I do not know where to lookup the correct syntax and have found nothing else than the code below for solving the original issue of getting the target of an advertised shortcut.

Any ideas where I can look next?

Code from https://www.alkanesolutions.co.uk/2020/06/03/use-powershell-to-find-an-advertised-shortcut-target:

function Get-AdvertisedShortcut {
    param([string]$pathToLnk)
    $shortcutTarget = ""
    if ($pathToLnk -ne $null -and (test-path $pathToLnk)) {    
        $windowsInstaller = New-Object -ComObject WindowsInstaller.Installer
        $lnkTarget = $WindowsInstaller.GetType().InvokeMember("ShortcutTarget","GetProperty",$null,$windowsInstaller,$pathToLnk)
        $productCode = $lnkTarget.GetType().InvokeMember("StringData","GetProperty",$null,$lnkTarget,1)
        $componentCode = $lnkTarget.GetType().InvokeMember("StringData","GetProperty",$null,$lnkTarget,3)
        $shortcutTarget = $WindowsInstaller.GetType().InvokeMember("ComponentPath","GetProperty",$null,$WindowsInstaller,@($productCode,$componentCode))        
    }
    return $shortcutTarget
}
Surak
  • 63
  • 7
  • 3
    Note that COM objects, like .NET methods, typically see a different working directory than PowerShell. Have you tried passing the shortcut's _full path_ to `Get-AdvertisedShortcut`? As an aside: A `[string]`-typed parameter in PowerShell is never `$null`, though it can be `''` (empty string), so it's better to use `if ($pathToLnk -and ...)` – mklement0 Jul 04 '22 at 15:38
  • Do you have a file that's supposed to work? Every language I try (PowerShell, C#, VBScript) fail on this ShortcutTarget API. – Simon Mourier Jul 04 '22 at 16:40
  • 2
    This code works fine for me against an advertised shortcut and only fails when targetting a non-advertised shortcut. The majority of my shortcuts were non-advertised, but managed to find **Master Packager** uses advertised ones and the code successfully retrieved the path. – NiMux Jul 04 '22 at 17:04
  • 2
    On a side note, you don't need to use `InvokeMember`, PowerShell is able to call methods and properties of COM objects directly: `$lnkTarget = $windowsInstaller.ShortcutTarget($pathToLnk)` and `$productCode = $lnkTarget.StringData(1)` and so on. – zett42 Jul 04 '22 at 17:17
  • 2
    For what mklement0 wrote, insert a line `$pathToLnk = Convert-Path -LiteralPath $pathToLnk` in function `Get-AdvertisedShortcut` after the `param()` line. This makes sure the COM object uses the full path. – zett42 Jul 04 '22 at 17:22
  • @simon-mourier: Nope, it doesn't seem to work with any of them. I found two links that should exist on all our computers: `%AppData%\Microsoft\Windows\Start Menu\Programs\System Tools\Control Panel.lnk`and `%AppData%\Microsoft\Windows\Start Menu\Programs\System Tools\File Explorer.lnk`. Can you give it a try with one of those please? – Surak Jul 05 '22 at 06:58
  • I also added the hints from the others, thanks to everybody. The line creating the error is now `$lnkTarget = $windowsInstaller.ShortcutTarget($pathToLnk)`, and also added `$pathToLnk = Convert-Path -LiteralPath $pathToLnk` in the beginning to ensure I use the full path. – Surak Jul 05 '22 at 07:01
  • 2
    I've investigated and also used C++. "ShortcutTarget" is a wrapper over MsiGetShortcutTargetW (https://learn.microsoft.com/en-us/windows/win32/api/msi/nf-msi-msigetshortcuttargetw) that you can also call directly. When you call this using the paths you gave, it gives me the ERROR_FUNCTION_FAILED error code. The function is documented as *"If the function fails, and the shortcut exists, the regular contents of the shortcut may be accessed through the IShellLink interface"*. So, when "ShortcutTarget" fails, you can just use regular link code – Simon Mourier Jul 05 '22 at 08:06
  • Hi Simon, then probably I am wrong with thinking the File Explorer shortcut is an advertised shortcut. I wrapped the function by a program that first looks for regular links via (WScript.Shell).TargetPath, and only if that does not exist I it: `if (!$thisLinkFile.TargetPath) {$thisLinkProperties.TargetPath = Get-AdvertisedShortcut $thisLinkFile.FullName}`. So you found that I assume wrong that **File Explorer.lnk** is an advertised shortcut? But it is grayed out in Explorer... – Surak Jul 06 '22 at 13:46

0 Answers0