It’s time for a more technical article. Recently I went on a trip overseas and while away I took a lot of photos. Being such a smart guy, I came up with what I thought was a smart way to pick out the best photos. I created subdirectories, and created shortcuts to the various photos (the directories were named "Best of <place name>"). The advantage was zero file duplication.
When I got back it became apparent that there were disadvantages too. When using the Windows image previewer, it would iterate all the photos in the source directory – not the shortcuts! I’m sure there might have been other options at this point, but I decided to spend about two hours walking down the windy path that is "understanding windows shortcuts"… (insert dramatic orchestral piece here).. dant dant daaaahh
It took very little time to realise that the .Net Framework seemed to offer little support for links. It might somewhere deep down, but I lost interest in finding out after a while. It was time for a trip back into C/C++ land again.
You may be wondering why. Shortcuts, .lnk files, are tied to the Windows shell. If you open one up in, say, Notepad you’ll get a lot of odd content. They’re binary files. When you double click a .lnk file the system executes the link to locate the target. In parsing this file in a hand written application, it isn’t immediately obvious what to do to locate the link’s target.
You need to make a Win32 API call to the Shell API to ask the system what the link’s target is. Doing it from .Net is probably possible, but the faster way is to do what I did. I wanted to be able to do my file I/O operations in C#, but make my Win32 calls from unmanaged code.
Henceforth, I wrote a native DLL (in C++). I then used PInvoke/Interop to make calls to this DLL from a .Net assembly. It was simply a case of passing the source path and file name, and receiving the target path and file name as a result.
First, ensure you have these includes defined for the shell bits we’ll use:
Next, you need a function signature defined, remember to include those glorious DllExport directives. We’ll take the path and size of the path (thought we could just as easily have worked the length out ourselves. Yay to lazy programming!
extern "C" __declspec(dllexport) LPCTSTR GetLinkTarget(LPWSTR szShortcutFile, int lengthofPath)
Next, define some local variables to use:
IShellLink* psh = NULL;
IPersistFile* psf = NULL;
TCHAR FileType; //note fixed size here
FileType = ‘\0’;
Next, this is the bulk of our function. We’ll initialise the various Shell objects we need to eventually call GetPath which is the IShellLink function we need to utilize the API to return the shortcut’s target.
if (FAILED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **) &psh) ))
if (FAILED(psh->QueryInterface(IID_IPersistFile, (void **) &psf)))
if (FAILED(psf->Load(szShortcutFile, STGM_READ) ))
psh->GetPath(FileType, lengthofPath, NULL, 0);
Finally, we can clean up and exit, with the result:
if (psf) psf->Release();
if (psh) psh->Release();
Now, keep in mind that the last part (the call to GetPath) might fail. It would be just as easy to add some code to return a more meaningful message if any error occurs during this function – it’s your job to define the error handling.
Next post, I’ll go over how to use this library. In part 3 I’ll post my source code.