{"id":11403,"date":"2011-02-24T07:00:00","date_gmt":"2011-02-24T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/02\/24\/shortcuts-are-serializable-objects-which-means-that-they-can-be-stored-in-places-other-than-just-a-file\/"},"modified":"2011-02-24T07:00:00","modified_gmt":"2011-02-24T07:00:00","slug":"shortcuts-are-serializable-objects-which-means-that-they-can-be-stored-in-places-other-than-just-a-file","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110224-00\/?p=11403","title":{"rendered":"Shortcuts are serializable objects, which means that they can be stored in places other than just a file"},"content":{"rendered":"<p>\nIt&#8217;s true that the vast majority of the time,\npeople consider the shell shortcut object as synonymous with the\n<code>.lnk<\/code> file it is normally saved into,\nshortcuts need not spend their time in a file.\nYou can put a shortcut anywhere you can save a hunk of bytes.\nHere&#8217;s a program that creates a shortcut to the file name passed\non the command line (make sure it&#8217;s a full path),\nand then serializes the shortcut to a blob of bytes\n(in the form of a <code>HGLOBAL<\/code>).\nOnce that&#8217;s done, it reconstitutes the bytes back into a\nshortcut object and sucks information out of it.\n<\/p>\n<pre>\n#define UNICODE\n#define _UNICODE\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;atlbase.h&gt;\nHGLOBAL CreateShellLinkInMemory(PCWSTR pszFile)\n{\n BOOL fSuccess = FALSE;\n HGLOBAL hglob = GlobalAlloc(GMEM_MOVEABLE, 0);\n if (hglob) {\n  CComPtr&lt;IStream&gt; spstm;\n  if (SUCCEEDED(CreateStreamOnHGlobal(hglob, FALSE, &amp;spstm))) {\n   CComPtr&lt;IShellLink&gt; spsl;\n   if (SUCCEEDED(spsl.CoCreateInstance(CLSID_ShellLink))) {\n    if (SUCCEEDED(spsl-&gt;SetPath(pszFile))) {\n     CComQIPtr&lt;IPersistStream&gt; spps(spsl);\n     fSuccess = spps &amp;&amp; SUCCEEDED(spps-&gt;Save(spstm, TRUE));\n    }\n   }\n  }\n }\n if (fSuccess) return hglob;\n if (hglob) GlobalFree(hglob);\n return NULL;\n}\n<\/pre>\n<p>\nAfter creating the shortcut object, we serialize it into\na stream backed by a chunk of memory we record in a <code>HGLOBAL<\/code>.\nThe shortcut object itself is no longer anywhere to be seen.\nIt&#8217;s been dehydrated into a pile of dust like in that old\n<i>Star Trek<\/i> episode.\n<\/p>\n<p>\nBut this time, we know how to bring it back.\n<\/p>\n<pre>\nIShellLink *CreateShellLinkFromMemory(HGLOBAL hglob)\n{\n IShellLink *pslReturn = NULL;\n CComPtr&lt;IStream&gt; spstm;\n if (SUCCEEDED(CreateStreamOnHGlobal(hglob, FALSE, &amp;spstm))) {\n  CComPtr&lt;IShellLink&gt; spsl;\n  if (SUCCEEDED(spsl.CoCreateInstance(CLSID_ShellLink))) {\n   CComQIPtr&lt;IPersistStream&gt; spps(spsl);\n   if (spps &amp;&amp; SUCCEEDED(spps-&gt;Load(spstm))) {\n    pslReturn = spsl.Detach();\n   }\n  }\n }\n return pslReturn;\n}\n<\/pre>\n<p>\nWe create a new shortcut object and tell it to restore itself\nfrom the chunk of memory we squirreled away.\nBingo, the shortcut is back, ready for action.\n<\/p>\n<pre>\nint __cdecl wmain(int argc, WCHAR **argv)\n{\n if (SUCCEEDED(CoInitialize(NULL))) {\n  HGLOBAL hglob = CreateShellLinkInMemory(argv[1]);\n  if (hglob) {\n   CComPtr&lt;IShellLink&gt; spsl;\n   spsl.Attach(CreateShellLinkFromMemory(hglob));\n   if (spsl) {\n    WCHAR szTarget[MAX_PATH];\n    if (spsl-&gt;GetPath(szTarget, MAX_PATH, NULL, 0) == S_OK) {\n     wprintf(L\"Welcome back, shortcut to %s\\n\", szTarget);\n    }\n   }\n   GlobalFree(hglob);\n  }\n  CoUninitialize();\n }\n return 0;\n}\n<\/pre>\n<p>\nSince shortcuts can be stored anywhere,\nyou can&#8217;t\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2009\/05\/28\/9645162.aspx#9647906\">\nrely on the file name to distinguish between shortcuts to files\nand shortcuts to folders<\/a>\nbecause there may not be a file name at all!\n(What&#8217;s the file name for our <code>HGLOBAL<\/code>?)\nEven if you decide that the convention applies only to shortcuts\nsaved in a file,\nyou&#8217;ve created an additional burden on people who manipulate shortcut files:\nThey have to check whether the target is a file or folder before\nchoosing the file name,\nand if the target of the shortcut changes, they may have to rename the file\nas well.\nThis is a real problem for the standard file property sheet:\nIf you change the shortcut target from the <i>Shortcut<\/i> page,\nthis might change the underlying file name.\nIf you had also made changes to the <i>Security<\/i> page,\nit will try to update the security attributes on the old file name,\neven though the <i>Shortcut<\/i> page had renamed it.\nOops, none of the other property sheet pages work, because they\nare now operating on a file that no longer exists!\n<\/p>\n<p>\n<b>Exercise<\/b>:\nUnder what conditions would it be useful to store a shortcut\nin memory rather than in a file?\n(<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/02\/24\/10133280.aspx#10133664\">Answer<\/a>.)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s true that the vast majority of the time, people consider the shell shortcut object as synonymous with the .lnk file it is normally saved into, shortcuts need not spend their time in a file. You can put a shortcut anywhere you can save a hunk of bytes. Here&#8217;s a program that creates a shortcut [&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-11403","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It&#8217;s true that the vast majority of the time, people consider the shell shortcut object as synonymous with the .lnk file it is normally saved into, shortcuts need not spend their time in a file. You can put a shortcut anywhere you can save a hunk of bytes. Here&#8217;s a program that creates a shortcut [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/11403","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=11403"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/11403\/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=11403"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=11403"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=11403"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}