[Update 2012-02-04: Jonathan pointed out a few additional things in the comments.]
[Update 2010-01-20: It looks like the calls to MsiGetShortcutTarget() and MsiGetComponentPath() only work correctly on Windows 7 if you make them from a thread that has STA apartment state. So if you do a lengthy operation like iterating through the entire Start menu on a background thread, make sure to use the Thread class for that (remember to call SetApartmentState() before kicking it off) and not BackgroundWorker. BackgroundWorker threads are taken from a thread pool and are always MTA (they can’t be forced to STA in any way). Thanks to the folks on stackoverflow for discussing and figuring this out, especially Arnout.]
This is a problem that’s been bugging me for about a year and I finally found time to dig in and work on how to solve it. A big thank you goes to Aaron Stebner for pointing me in the right direction. Thanks Aaron!
Anyway, for various reasons I’ve wanted to display a list of applications the user has installed on the computer. Getting at the All Programs folder in the Start Menu is not too big a problem. You can get parts of it via Environment.GetFolderPath(SpecialFolder.Programs) and other parts of it (for all users) via a bunch of P/Invoke incantations that I’m not going into here. Iterating through all subfolders and picking out the .lnk files is not a problem either.
What is a problem (at least it was for me) is what to do with those .lnk files that you can’t get parsed correctly via WshShell.CreateShortcutFile() after adding a reference to the Windows Script Host Object Model to your project. The .TargetPath will usually return something down in a C:\Windows\Installer\{GUID} directory.
I had noticed that the Windows Installer XML 3.0 project creates shortcuts like that, and since Aaron is involved in that project, I asked him about it. He graciously told me that those shortcuts are “advertised” shortcuts, a kind of Windows Installer shortcut that enables putting a link in the Start Menu (or elsewhere) while not necessarily installing the whole product that the link points to. So I started digging in with Google. A CodeProject article contained a comment about two functions that can be used in combination to find the real target of and advertised shortcut: MsiGetShortcutTarget() and MsiGetComponentPath(). After some more digging, I had P/Invoke declarations for both of these from this SourceForge project.
I ended up with this bit of code, which I use first on any shortcut file I need to interpret; then if it returns null, I use the WshShell way instead.
public static string ParseShortcut(string file) { StringBuilder product = new StringBuilder(MaxGuidLength +1); StringBuilder feature = new StringBuilder(MaxFeatureLength +1); StringBuilder component = new StringBuilder(MaxGuidLength +1); MsiGetShortcutTarget(file, product, feature, component); 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; } }Â
If you’re interested, you can download the class with the rest of the needed declarations.
Pingback: Parsing those *.lnk type files - VB.NET version | Hodson Report
Greg Zelesnik
GeekTieGuy
Greg Zelesnik
GeekTieGuy
Greg Zelesnik
GeekTieGuy
Ben
Dirk
Gaurav
GeekTieGuy
JackZhong
GeekTieGuy
Matthew Clark
GeekTieGuy
Man
TcoUpLoad
GeekTieGuy
Goverdhan A
GeekTieGuy
cosmin
Jonathan
Jonathan
GeekTieGuy
Rus
Rus
Pingback: How to view advertised shortcut