4

I am trying to programmatically find the exe file that is run for a given MSI lnk file (advertised shortcut). I have used an approach similar to that shown in the answer to Is there a way to resolve a .lnk target that works for links that end up in c:\windows\installer?. This approach works fine for the majority of MSI lnk files. Unfortunately there are a minority of lnk files that run fine, but MsiGetShortcutTarget returns no component id. So the subsequent call to MsiGetComponentPath returns InvalidArg.

Here's the code I'm using (taken from here):

public const int MaxFeatureLength = 38;
public const int MaxGuidLength = 38;
public const int MaxPathLength = 1024;

public static string ParseShortcut(string shortcutFilename)
{
    StringBuilder product = new StringBuilder(MaxGuidLength + 1);
    StringBuilder feature = new StringBuilder(MaxFeatureLength + 1);
    StringBuilder component = new StringBuilder(MaxGuidLength + 1);

    var returnValue = MsiGetShortcutTarget(shortcutFilename, product, feature, component);

    if (returnValue != 0)
    {
        return null;
    }

    int pathLength = MaxPathLength;
    StringBuilder path = new StringBuilder(pathLength);

    InstallState installState = MsiGetComponentPath(product.ToString(), component.ToString(), path, ref pathLength);
    if (installState == InstallState.Local)
    {
        return path.ToString();
    }
    else
    {
        return null;
    }
}

An example on my machine is C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Office 2013\Office 2013 Tools\Office 2013 Language Preferences.lnk

Product id: {91150000-0011-0000-0000-0000000FF1CE} Feature id: SetLanguageFiles

I believe the IShellLink interface cannot be used to return the runnable exe for MSI Advertised Shortcuts and my attempts to do so have returned the path to an exe containing icon resources.

Clearly it is possible for the operating system to locate the appropriate exe (in this case it is C:\Program Files (x86)\Microsoft Office\Office15\SETLANG.EXE).

How do I get which exe file is associated with this lnk file using code?

Community
  • 1
  • 1
fractor
  • 1,534
  • 2
  • 15
  • 30
  • Just to be sure, it might help to post your code. – PhilDW Jan 06 '16 at 22:48
  • Looking at [this](http://www.symantec.com/connect/articles/working-darwin-descriptors) I can see why the lnk file is not returning the component id. It is because the Darwin Descriptor in it contains only the product code. So I guess the question boils down to: given a product code (and feature), how do I get the (single) component id? – fractor Jan 07 '16 at 09:59
  • Unfortunately I don't know. The API doco says ComponentID can be null and to use the IShellLink apis. I tried that using various examples and the best I could get was the location of the cached icon under C:\Windows\Installer. I suppose I could use that along with the ProductCode to query the cached MSI to find out what shortcut uses that icon resource and then what the component is and then the key path. I suppose it's possible for 2 shortcuts to use the same icon. Phil probably knows these internals better then me. If not him, then maybe Heath or Darryl or Rob. – Christopher Painter Jan 07 '16 at 14:56
  • A possible explanation is that the component is in fact not installed, and that's because it's an install on demand shortcut. This seems to be normal for Office shortcuts that change the language, looking at a couple of systems. – PhilDW Jan 07 '16 at 19:06
  • The component _is_ installed when the msi is installed. I have found one way of getting the component path based on [WiLstPrd.vbs](https://msdn.microsoft.com/en-us/library/aa369767(v=vs.85).aspx). This works, so is a solution. Unfortunately the algorithm enumerates _all_ components, then for each finds the products that use it. In my tests the time taken (~4s) is not prohibitive, but I was hoping for a more direct way of finding the component code given a product code. When running the shortcut directly, the application runs immediately, so presumably Windows isn't using this approach. – fractor Jan 08 '16 at 11:36
  • @PhilDW- I created an MSI as test data for this question and the product/feature/component was fully installed. It still came back as null for me. I didn't understand why though. – Christopher Painter Jan 08 '16 at 16:01
  • @ChristopherPainter If there is only one component, the component id is not stored in the Darwin Descriptor in the LNK file. See http://www.symantec.com/connect/articles/working-darwin-descriptors. – fractor Jan 08 '16 at 16:39

1 Answers1

0

When no component id is returned from MsiGetShortcutTarget, it is possible to get the component id from the MSI database. One way of doing this is to use WiX DTF as shown here: https://stackoverflow.com/a/34680682/3051702.

The returned component id can then be used in a call to MsiGetComponentPath. Alternatively, the native methods could be used directly.

Community
  • 1
  • 1
fractor
  • 1,534
  • 2
  • 15
  • 30