Shortcuts are serializable objects, which means that they can be stored in places other than just a file

It’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’s a program that creates a shortcut to the file name passed on the command line (make sure it’s a full path), and then serializes the shortcut to a blob of bytes (in the form of a HGLOBAL). Once that’s done, it reconstitutes the bytes back into a shortcut object and sucks information out of it.

#define UNICODE
#define _UNICODE
#include <windows.h>
#include <shlobj.h>
#include <ole2.h>
#include <stdio.h>
#include <tchar.h>
#include <atlbase.h>

HGLOBAL CreateShellLinkInMemory(PCWSTR pszFile)
 BOOL fSuccess = FALSE;
 HGLOBAL hglob = GlobalAlloc(GMEM_MOVEABLE, 0);
 if (hglob) {
  CComPtr<IStream> spstm;
  if (SUCCEEDED(CreateStreamOnHGlobal(hglob, FALSE, &spstm))) {
   CComPtr<IShellLink> spsl;
   if (SUCCEEDED(spsl.CoCreateInstance(CLSID_ShellLink))) {
    if (SUCCEEDED(spsl->SetPath(pszFile))) {
     CComQIPtr<IPersistStream> spps(spsl);
     fSuccess = spps && SUCCEEDED(spps->Save(spstm, TRUE));
 if (fSuccess) return hglob;
 if (hglob) GlobalFree(hglob);
 return NULL;

After creating the shortcut object, we serialize it into a stream backed by a chunk of memory we record in a HGLOBAL. The shortcut object itself is no longer anywhere to be seen. It’s been dehydrated into a pile of dust like in that old Star Trek episode.

But this time, we know how to bring it back.

IShellLink *CreateShellLinkFromMemory(HGLOBAL hglob)
 IShellLink *pslReturn = NULL;
 CComPtr<IStream> spstm;
 if (SUCCEEDED(CreateStreamOnHGlobal(hglob, FALSE, &spstm))) {
  CComPtr<IShellLink> spsl;
  if (SUCCEEDED(spsl.CoCreateInstance(CLSID_ShellLink))) {
   CComQIPtr<IPersistStream> spps(spsl);
   if (spps && SUCCEEDED(spps->Load(spstm))) {
    pslReturn = spsl.Detach();
 return pslReturn;

We create a new shortcut object and tell it to restore itself from the chunk of memory we squirreled away. Bingo, the shortcut is back, ready for action.

int __cdecl wmain(int argc, WCHAR **argv)
 if (SUCCEEDED(CoInitialize(NULL))) {
  HGLOBAL hglob = CreateShellLinkInMemory(argv[1]);
  if (hglob) {
   CComPtr<IShellLink> spsl;
   if (spsl) {
    WCHAR szTarget[MAX_PATH];
    if (spsl->GetPath(szTarget, MAX_PATH, NULL, 0) == S_OK) {
     wprintf(L"Welcome back, shortcut to %s\n", szTarget);
 return 0;

Since shortcuts can be stored anywhere, you can’t rely on the file name to distinguish between shortcuts to files and shortcuts to folders because there may not be a file name at all! (What’s the file name for our HGLOBAL?) Even if you decide that the convention applies only to shortcuts saved in a file, you’ve created an additional burden on people who manipulate shortcut files: They have to check whether the target is a file or folder before choosing the file name, and if the target of the shortcut changes, they may have to rename the file as well. This is a real problem for the standard file property sheet: If you change the shortcut target from the Shortcut page, this might change the underlying file name. If you had also made changes to the Security page, it will try to update the security attributes on the old file name, even though the Shortcut page had renamed it. Oops, none of the other property sheet pages work, because they are now operating on a file that no longer exists!

Exercise: Under what conditions would it be useful to store a shortcut in memory rather than in a file? (Answer.)

Comments (21)
  1. Adam Rosenfield says:

    The first thought that came to my mind was the clipboard: the only times I've ever written code dealing with HGLOBALs have been when dealing with the clipboard.  So, if you wanted your program to have the ability to copy a file shortcut to the clipboard without actually creating the shortcut (and in so doing allow the user to paste that shortcut wherever he wanted), you could create an in-memory shortcut and then call SetClipboardData(CF_SOMETHING, hglob).

    Figuring out which clipboard format to use might be tricky: when I copy a shortcut on Windows 7, I get 11 different formats on the clipboard: "DataObject" (49161), "Shell IDList Array" (49312), "DataObjectAttributes" (49502), "DataObjectAttributesRequiringElevation" (49542), "Shell Object Offsets" (49387), "Preferred DropEffect" (49359), "AsyncFlag" (49412), CF_HDROP, "FileName" (49158), "FileNameW (49159), and "Ole Private Data" (49171).

    [No need to guess. I covered this topic already. And you don't even need to do that; you can just put a HIDA on the clipboard and call it good. (The clipboard client will create the shortcut from the HIDA.) And you can even ask the shell to create the HIDA too. -Raymond]
  2. Ben says:

    "Exercise: Under what conditions would it be useful to store a shortcut in memory rather than in a file? "

    When passing a reference to a non-file shell "virtual folder" between processes (or maybe threads depending on the threading model)?

    [A shortcut is overkill for that. Just pass the pidl. -Raymond]
  3. Mike says:

    "Exercise: Under what conditions would it be useful to store a shortcut in memory rather than in a file? "

    Templatizing a shortcut, and adding/replacing certain values and/or properties based on the context in which you need a full shortcut based on that template's properties.

  4. Gabe says:

    Once, I actually found myself designing a system where shortcuts were stored in a database. Obviously serializing them to memory is the easiest way of getting the bits to store in the DB.

    So why on Earth would I need to store shortcuts in a database? The answer is link tracking. I actually just wanted to keep the paths to files, but people can move and rename files, thus invalidating their paths in the DB. If I stored a shortcut, though, I could deserialize it and tell it to resolve the link. Now I don't have to worry about files changing names and locations, servers changing names, or even moving drive arrays to new servers.

    [This was the answer I was looking for. -Raymond]
  5. Adam Rosenfield says:

    [No need to guess. I covered this topic already. And you don't even need to do that; you can just put a HIDA on the clipboard and call it good. (The clipboard client will create the shortcut from the HIDA.) -Raymond]

    With what clipboard format?  I've never heard of a HIDA, and it barely appears anywhere on MSDN (…/bb762126%28v=vs.85%29.aspx suggests CFSTR_SHELLIDLIST).

    [HIDA is the data structure that corresponds to CFSTR_SHELLIDLIST, the same way that LPSTR corresponds to CF_TEXT. -Raymond]
  6. jmthomas says:

    I do believe that IBM's OS/2 used an associative data base to track this stuff (as links) so a connection between two object never got lost.  A few things got lost in the divorce!

  7. Mike says:

    @Gabe, I'm slightly confused by your use case. The only way I can parse it that makes sense to me is if you wanted to track URIs, wanted to keep those in shortcut files, but then chose to store them in a database instead.

    Is that correct?

    [Don't get distracted by the database. The user gave you a path, which you want to save. (Say, a path to the mp3 file to play as background music for your slideshow.) But the user might rename the file or move it to another drive, so just storing a path won't work. Solution: Instead of (or in addition to) the path, also create a shortcut and save that in your project file. Then when it's time to play the slideshow, you can use the shortcut to find the background music. -Raymond]
  8. John says:

    If you're worried about the user moving or renaming files, why bother working with names at all?  Just use shortcuts for everything!

  9. WndSks says:

    @Mike: Extending on Raymonds answer; A shortcut has more metadata than just the path, it also knows the UNC name of the computer, the relative path and size, time&data and attribute information, so when you call IShellLink->Resolve() windows can search all your drives for the closest match if the original path is no longer valid.

    @Raymond: IShellItem might be enough, unless you need custom icon, launch parameters etc but that is not really the point, MSDN recommends you to use ShellLinks with ICustomDestinationList::AddUserTasks and ShellItems with ICustomDestinationList::AppendCategory. (I have never tried to give AddUserTasks a ShellItem, maybe it displays it as "myapp.exe" and not just myapp, I don't know, but MSDN recommends you to use ShellLinks with AddUserTasks and ShellItems with AppendCategory)

    @Adam Rosenfield/Raymond: HIDA is not a common term in the public SDK, but it is a pointer to a CIDA struct and a CIDA struct is really just a way to store PIDLs in a array.…/bb773212%28v=vs.85%29.aspx shows two HIDA_* macros, off the top of my head, those two and the NSWF_IGNORE_AUTOPLAY_HIDA flag are the only places where the HIDA name is part of a public "function" or flag

  10. Mike says:

    @WndSks, @Raymond, thanks; that was the missing bit I needed. I didn't realize Explorer could find shortcut targets when the shortcut's target had moved.

  11. Zan Lynx says:

    Now if only the Registry could track down files too. There are many times I'd like to move a program to a different disk for space reasons, or maybe I want to keep old copies of TurboTax on a USB stick.

    But nooooo, all the install references are to drive C:original-directory which forces a silly uninstall/reinstall cycle or the use of one of those program mover apps which track down and fix up all the registry keys for you.

    The shortcut resolver is only half the problem there. But it is pretty cool that it does work so well so thanks, shell guys!

  12. WndSks says:

    "Exercise: Under what conditions would it be useful to store a shortcut in memory rather than in a file? "

    Win7 saves IShellLink and IShellItem items for the jump lists in Recent"SecretFolders"?hashofappusermodelid?.*ms

    [I think you misunderstood the exercise. It wasn't "name somebody who stores a shortcut in memory rather than in a file>" It's "Under what conditions would it be *useful*"? -Raymond]
  13. Mike says:

    I never seem to have the answer the instructor was looking for.

    *sigh* :)

  14. WndSks says:

    @Raymond: And that does not fall under *useful*? I guess I could have phrased it differently, but my point still stands. Naming names is just a bonus!

    [All you said is "This component stores it in a file." You didn't say why it is useful. (Isn't the IShellItem enough to identify the object?) -Raymond]
  15. waleri says:

    I am a little confused. If I move/rename the file, how the shortcut will know to update itself? Yes, I know explorer searches (and sometimes find) moved/renamed programs, but I always thought it is kinda a guesswork (unless the link file contains a checksum or some sort of fileid or whatever). Point is, if I simply move my MP3 around its name remains the same and there is a chance that whoever resolves the shorcut will find the file, but it might find some other file with the same name. Also, what would happen if I rename the file to something *completly* different?

    I am not trying to nitpick, I just try to figure how this works.

    [See "link tracking service". Also this old article. Probably others, didn't bother searching. I assume you know how to search. -Raymond]
  16. Mason Wheeler says:

    Minor nitpick:  The first sentence in this post would parse a lot easier if it began with "while".

    Minor nitpick #2: I don't remember any Star Trek episode where people got dehydrated into piles of dust.  I do remember that being part of the original Batman movie, though.  (The one with Adam West.)

    [I dimly recall an episode in which crew members were reduced to little dodecahedrons. I must be mistaken… -Raymond]
  17. Zan Lynx says:

    Raymond, could you be thinking of Farscape instead of Star Trek? The final TV episode had John and Aeryn dehydrated into dust. (Later to be restored in the actual final mini-series.)

    [Unlikely, since I've never heard of Farscape much less seen an epsiode… I remember a scene in the Enterprise corridor with little dodecahedrons scattered about the floor. Maybe I just dreamed the episode… -Raymond]
  18. James Caccese says:

    @Raymond in Mason Wheeler's and Zan Lynx's comments

    No, you're right, I saw that episode when I was about 15. It creeped me out then, but now I think it was a pretty cool episode.

  19. Joshua says:

    "By any other Name"

  20. Rick C says:

    This was a second-season episode, By Any Other Name.

  21. John Crichton says:

    Clearly Raymond is not a SciFi fan.. never heard of Farscape! Best SciFi ever.

Comments are closed.