{"id":13523,"date":"2010-07-02T07:00:00","date_gmt":"2010-07-02T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2010\/07\/02\/instead-of-trying-to-figure-out-what-shortcut-class-to-use-just-ask-the-shell-to-do-it-for-you\/"},"modified":"2010-07-02T07:00:00","modified_gmt":"2010-07-02T07:00:00","slug":"instead-of-trying-to-figure-out-what-shortcut-class-to-use-just-ask-the-shell-to-do-it-for-you","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20100702-00\/?p=13523","title":{"rendered":"Instead of trying to figure out what shortcut class to use, just ask the shell to do it for you"},"content":{"rendered":"<p>\nIf a shell namespace item has the\n<code>SFGAO_LINK<\/code> attribute,\nthen it is a shortcut to another location.\nThe most common type of shortcut is the <code>.lnk<\/code> file,\nwhich you can load by creating the\n<code>CLSID_Shell&shy;Link<\/code> object and using\n<code>IPersist&shy;File::Load<\/code>,\nbut what if you have some other type of shortcut?\nHow do you know what CLSID to use?\n<\/p>\n<p>\nSince anybody can create their own shortcut file types,\na hard-coded list mapping file extensions to CLSIDs is\nnot going to work for long.\nBut fortunately, you don&#8217;t have to know how to look up the CLSID\nfor a particular shortcut;\nyou can just ask the namespace to do it for you\nby asking for the <code>IShell&shy;Link<\/code> UI object.\n<\/p>\n<pre>\n#include &lt;windows.h&gt;\n#include &lt;shlobj.h&gt;\n#include &lt;ole2.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;tchar.h&gt;\n#include &lt;shellapi.h&gt;\n\/\/ <a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/09\/20\/231739.aspx\">GetUIObjectOfFile<\/a> function incorporated by reference\nint __cdecl _tmain()\n{\n  int argc;\n  LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &amp;argc);\n  if (argv == NULL || argc != 2) return 0;\n  if (SUCCEEDED(CoInitialize(NULL))) {\n    IShellLink *psl;\n    if (SUCCEEDED(GetUIObjectOfFile(NULL, argv[1], IID_PPV_ARGS(&amp;psl)))) {\n      TCHAR sz[MAX_PATH];\n      if (SUCCEEDED(psl-&gt;GetPath(sz, MAX_PATH, NULL, 0))) {\n        _tprintf(TEXT(\"-&gt; %ls\\n\"), sz);\n      }\n      else _tprintf(TEXT(\"GetPath failed\\n\"));\n      psl-&gt;Release();\n     }\n     else _tprintf(TEXT(\"GetUIObjectOf failed\\n\"));\n    CoUninitialize();\n  }\n  LocalFree(argv);\n  return 0;\n}\n<\/pre>\n<p>\nI&#8217;ve limited myself to files here for simplicity of exposition,\nand I assume that you&#8217;ve passed a fully-qualified path on the\ncommand line.\nOf course, you can have shortcuts to non-file objects as well,\nand for those shortcuts,\n<code>IShell&shy;Link::Get&shy;Path<\/code>\nis unlikely to return an actual\nfile path.\n(In fact, for things like shortcuts to the Control Panel,\nthey&#8217;re unlikely to return anything at all.)\nI&#8217;ve also used the <code>Command&shy;Line&shy;To&shy;ArgvW<\/code> function\ninstead of the built-in <code>argc<\/code> and <code>argv<\/code>\nbecause the <code>Get&shy;UI&shy;Object&shy;Of&shy;File<\/code> function wants\na Unicode file name, but the C runtime&#8217;s <code>argv<\/code>\nis a <code>TCHAR *<\/code> string, which might not be Unicode.\n<\/p>\n<p>\nLet&#8217;s take this program for a spin.\n<\/p>\n<p>\n<b>Warning<\/b>: I am using hard-coded paths.\nIn real life, you would use appropriate functions to obtain\nthe paths to the files you care about.\n(Actually, in real life, you probably will have a pidl to the item\nrather than a path, so the issue of paths disappears.)\n<\/p>\n<pre>\n&gt;set STARTMENU=%APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\n&gt;scratch \"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Accessories\\Calculator.lnk\"\n-&gt; C:\\Windows\\System32\\calc.exe\n&gt;scratch \"%STARTMENU%\\Internet Explorer.lnk\"\n-&gt; C:\\Program Files\\Internet Explorer\\iexplore.exe\n<\/pre>\n<p>\nOkay, these are your regular <code>.lnk<\/code> files,\nso there&#8217;s nothing special going on here.\nLet&#8217;s try something fancier, like a symbolic link.\n<\/p>\n<pre>\n&gt;echo &gt; blah.txt\n&gt;mklink other blah.txt\nsymbolic link created for other &lt;&lt;===&gt;&gt; blah.txt\n&gt;scratch \"%CD%\\other\"\n-&gt; C:\\test\\blah.txt\n<\/pre>\n<p>\nVia the Add Network Location wizard,\nI created a network location (which is internally represented\nas a Folder Shortcut).\nLet&#8217;s see what happens with that:\n<\/p>\n<pre>\n&gt; scratch \"%APPDATA%\\Microsoft\\Windows\\Network Shortcuts\\Tools\"\n-&gt; \\\\live.sysinternals.com\\tools\n<\/pre>\n<p>\nHow about Internet shortcuts?\n<\/p>\n<pre>\n&gt; scratch \"%USERPROFILE%\\Favorites\\MSN Websites\\MSN.url\"\n-&gt; http:\/\/go.microsoft.com\/fwlink\/?LinkId=54729\n<\/pre>\n<p>\nOneClick shortcuts?\n(MS Space is an internal application which lets you view floor\nplans of every Microsoft building,\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2009\/05\/15\/9617531.aspx\">\nbook conference rooms<\/a>,\nreserve\n<a HREF=\"http:\/\/microsoftjobsblog.com\/blog\/employees-score-with-touchdown-space\/\">\ntouchdown space<\/a>,\nthat sort of thing.)\n<\/p>\n<pre>\n&gt; scratch \"%STARTMENU%\\MS Space.appref-ms\"\nGetUIObjectOf failed\n<\/pre>\n<p>\nHuh? What happened?\n<\/p>\n<p>\nIt so happens that the people who wrote the shortcut handler for\nOneClick applications only bothered to implement the Unicode\nversion of the <code>IShell&shy;Link<\/code> interface.\nWe built our application as ANSI, so our attempt to get the\n<code>IShell&shy;LinkA<\/code> interface failed.\nBut that&#8217;s easily worked around:\n<\/p>\n<pre>\n<font COLOR=\"blue\">#define <a HREf=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/02\/12\/71851.aspx\">_UNICODE<\/a>\n#define UNICODE<\/font>\n#include &lt;windows.h&gt;\n#include &lt;shlobj.h&gt;\n#include &lt;ole2.h&gt;\n...\n<\/pre>\n<p>\n(In real life, your program would probably first ask for the Unicode\ninterface, and if the call fails, then ask for the ANSI interface.)\n<\/p>\n<p>\nWith the Unicode version of the program, the shortcut resolves:\n<\/p>\n<pre>\n&gt; scratch \"%STARTMENU%\\MS Space.appref-ms\"\n-&gt; C:\\Users\\<span STYLE=\"border: solid .75pt black\">...<\/span>\\MSSpaceDeploy.exe\n<\/pre>\n<p>\n(I elided some of the ugly path because, well, it&#8217;s ugly.\nThe full unabbreviated path is 139 characters,\nmost of which is just hex digits.)\n<\/p>\n<p>\nAnyway,\nthe point for today wasn&#8217;t the minutiae of obtaining\nshortcut targets from shell namespace items.\nIt was the principle that if you want something from the shell\nnamespace,\nthe\n<code>IShell&shy;Folder::Get&shy;UI&shy;Object&shy;Of<\/code>\nmethod will often get\nit for you.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If a shell namespace item has the SFGAO_LINK attribute, then it is a shortcut to another location. The most common type of shortcut is the .lnk file, which you can load by creating the CLSID_Shell&shy;Link object and using IPersist&shy;File::Load, but what if you have some other type of shortcut? How do you know what CLSID [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-13523","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>If a shell namespace item has the SFGAO_LINK attribute, then it is a shortcut to another location. The most common type of shortcut is the .lnk file, which you can load by creating the CLSID_Shell&shy;Link object and using IPersist&shy;File::Load, but what if you have some other type of shortcut? How do you know what CLSID [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/13523","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=13523"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/13523\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=13523"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=13523"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=13523"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}