Click here to Skip to main content
Licence CPOL
First Posted 28 Feb 2008
Views 17,125
Downloads 72
Bookmarked 5 times

Workaround for IShellLink::GetPath()

By | 1 Mar 2008 | Article
Workaround for IShellLink::GetPath(), which sometimes fails and crashes the application.

Introduction

IShellLink::GetPath() may fail and crash your application. This is a workaround to help you out of this.

Using the code

This is the straight forward implementation:
//*****************************************************************************
// File ..................: ReadLnkFile.cpp
// Description ...........: Decoder for .lnk files on WINDOWS
// Author ................: Peter Thoemmes (http://www.notes-about-cpp.com/)
//-----------------------------------------------------------------------------
// #                  !!! Disclaimer !!!                                      #
// # This source code is provided "AS-IS" basis, without any warranties or    #
// # representations express, implied or statutory; including, without        #
// # limitation, warranties of quality, performance, non-infringement,        #
// # merchantability or fitness for a particular purpose. Peter Thoemmes      #
// # does not warrant that this source code will meet your needs or be free   #
// # from errors.                                                             #
//-----------------------------------------------------------------------------
// Many thanks to Jesse Hager (jessehager@iname.com) for reverse engineering
// the shortcut file format and publishing the document 'The Windows Shortcut
// File Format'.
//-----------------------------------------------------------------------------
// Copyright (c) 2008 Peter Thoemmes, Weinbergstr. 3a, D-54441 Ockfen/Germany
//*****************************************************************************

#include <string>
#include <vector>
using namespace std;

//-----------------------------------------------------------------------------
// Get target file (full local path) from a link file (.lnk):
//-----------------------------------------------------------------------------

string ReadLnkFile(const string& strFullLinkFileName)
{
    //-------------------------------------------------------------------------
    // How to read the target's path from a .lnk-file:
    //-------------------------------------------------------------------------
    // Problem:
    //
    //    The COM interface to shell32.dll IShellLink::GetPath() fails!
    //
    // Solution:
    //
    //   We need to parse the file manually. The path can be found like shown
    //   here, if the shell item id list is present. In case it is not present
    //   we have to assume A = -6, but not to parse/decode byte 76 and 77.
    //
    //  +---------------------+-----------------------------------------------+
    //  | Index               | Description                                   |
    //  +---------------------+-----------------------------------------------+
    //  |                   0 | 'L' (magic value)                             |
    //  +---------------------+-----------------------------------------------+
    //  |                 ... | ...                                           |
    //  +---------------------+-----------------------------------------------+
    //  |                  76 | A_0                                           |
    //  +---------------------+-----------------------------------------------+
    //  |                  77 | A_1 (16 bit) [w/o shell item id list: A = -6] |
    //  +---------------------+-----------------------------------------------+
    //  |                 ... | ...                                           |
    //  +---------------------+-----------------------------------------------+
    //  |         78 + 16 + A | B_0                                           |
    //  +---------------------+-----------------------------------------------+
    //  |     78 + 16 + A + 1 | B_1                                           |
    //  +---------------------+-----------------------------------------------+
    //  |     78 + 16 + A + 2 | B_2                                           |
    //  +---------------------+-----------------------------------------------+
    //  |     78 + 16 + A + 3 | B_3 (32 bit)                                  |
    //  +---------------------+-----------------------------------------------+
    //  |                 ... | ...                                           |
    //  +---------------------+-----------------------------------------------+
    //  |          78 + A + B | PATH_STR_0                                    |
    //  +---------------------+-----------------------------------------------+
    //  |      78 + A + B + 1 | PATH_STR_1                                    |
    //  +---------------------+-----------------------------------------------+
    //  |      78 + A + B + 2 | PATH_STR_2                                    |
    //  +---------------------+-----------------------------------------------+
    //  |                 ... | ...                                           |
    //  +---------------------+-----------------------------------------------+
    //  |                 ... | 0x00                                          |
    //  +---------------------+-----------------------------------------------+
    //-------------------------------------------------------------------------

    //-------------------------------------------------------------------------
    // Get the .lnk-file content:
    //-------------------------------------------------------------------------

    FILE* pFile = fopen(strFullLinkFileName.c_str(),"rb");
    if(!pFile)
        return string("");
    vector<unsigned char> vectBuffer;
    unsigned char byte = '?';
    while((byte = (unsigned char) fgetc(pFile)) != (unsigned char) EOF)
        vectBuffer.push_back(byte);
    fclose(pFile);

    //-------------------------------------------------------------------------
    // Check the magic value (first byte) and the GUID (16 byte from 5th byte):
    //-------------------------------------------------------------------------
    // The GUID is telling the version of the .lnk-file format. We expect the
    // following GUID (HEX): 01 14 02 00 00 00 00 00 C0 00 00 00 00 00 00 46.
    //-------------------------------------------------------------------------

    if(vectBuffer.size() < 20)
        return string("");
    if(vectBuffer[0] != (unsigned char) 'L') //test the magic value
        return string("");
    if((vectBuffer[4] != 0x01) || //test the GUID
       (vectBuffer[5] != 0x14) ||
       (vectBuffer[6] != 0x02) ||
       (vectBuffer[7] != 0x00) ||
       (vectBuffer[8] != 0x00) ||
       (vectBuffer[9] != 0x00) ||
       (vectBuffer[10] != 0x00) ||
       (vectBuffer[11] != 0x00) ||
       (vectBuffer[12] != 0xC0) ||
       (vectBuffer[13] != 0x00) ||
       (vectBuffer[14] != 0x00) ||
       (vectBuffer[15] != 0x00) ||
       (vectBuffer[16] != 0x00) ||
       (vectBuffer[17] != 0x00) ||
       (vectBuffer[18] != 0x00) ||
       (vectBuffer[19] != 0x46))
    {
        return string("");
    }

    //-------------------------------------------------------------------------
    // Get the flags (4 byte from 21st byte):
    //-------------------------------------------------------------------------
    // Check if it points to a file or directory!
    //-------------------------------------------------------------------------
    // Flags (4 byte little endian):
    //        Bit 0 -> has shell item id list
    //        Bit 1 -> points to file or directory
    //        Bit 2 -> has description
    //        Bit 3 -> has relative path
    //        Bit 4 -> has working directory
    //        Bit 5 -> has commandline arguments
    //        Bit 6 -> has custom icon
    //-------------------------------------------------------------------------

    unsigned int i = 20;
    if(vectBuffer.size() < (i + 4))
        return string("");
    unsigned int dwFlags = (unsigned int) vectBuffer[i]; //little endian format
    dwFlags |= (((unsigned int) vectBuffer[++i]) << 8);
    dwFlags |= (((unsigned int) vectBuffer[++i]) << 16);
    dwFlags |= (((unsigned int) vectBuffer[++i]) << 24);

    bool bHasShellItemIdList = (dwFlags & 0x00000001) ? true : false;
    bool bPointsToFileOrDir = (dwFlags & 0x00000002) ? true : false;
    
    if(!bPointsToFileOrDir)
        return string("");

    //-------------------------------------------------------------------------
    // Shell item id list (starts at 76 with 2 byte length -> so we can skip):
    //-------------------------------------------------------------------------

    int32 A = -6;
    if(bHasShellItemIdList)
    {
        i = 76;
        if(vectBuffer.size() < (i + 2))
            return string("");
        A = (unsigned char) vectBuffer[i]; //little endian format
        A |= (((unsigned char) vectBuffer[++i]) << 8);
    }

    //-------------------------------------------------------------------------
    // File location info:
    //-------------------------------------------------------------------------
    // Follows the shell item id list and starts with 4 byte structure length,
    // followed by 4 byte offset for skipping.
    //-------------------------------------------------------------------------

    i = 78 + 4 + A;
    if(vectBuffer.size() < (i + 4))
        return string("");
    unsigned int B = (unsigned int) vectBuffer[i]; //little endian format
    B |= (((unsigned int) vectBuffer[++i]) << 8);
    B |= (((unsigned int) vectBuffer[++i]) << 16);
    B |= (((unsigned int) vectBuffer[++i]) << 24);

    //-------------------------------------------------------------------------
    // Local volume table:
    //-------------------------------------------------------------------------
    // Follows the file location info and starts with 4 byte table length for
    // skipping the actual table and moving to the local path string.
    //-------------------------------------------------------------------------

    i = 78 + A + B;
    if(vectBuffer.size() < (i + 4))
        return string("");
    unsigned int C = (unsigned int) vectBuffer[i]; //little endian format
    C |= (((unsigned int) vectBuffer[++i]) << 8);
    C |= (((unsigned int) vectBuffer[++i]) << 16);
    C |= (((unsigned int) vectBuffer[++i]) << 24);

    //-------------------------------------------------------------------------
    // Local path string (ending with 0x00):
    //-------------------------------------------------------------------------

    i = 78 + A + B + C;
    if(vectBuffer.size() < (i + 1))
        return string("");

    string strLinkedTarget = "";
    for(;i < vectBuffer.size();++i)
    {
        strLinkedTarget.append(1,(char) vectBuffer[i]);
        if(!vectBuffer[i])
            break;
    }

    //Return if empty:
    if(strLinkedTarget.empty())
        return string("");

    //-------------------------------------------------------------------------
    // Convert the target path into the long format (format without ~):
    //-------------------------------------------------------------------------
    // GetLongPathNameA() fails it the target file doesn't exist!
    //-------------------------------------------------------------------------

    char* szLinkedTargetLongFormat = new char[MAX_PATH + 1];
    if(!szLinkedTargetLongFormat)
        return string("");
    if(!::GetLongPathNameA(
                strLinkedTarget.c_str(),
                szLinkedTargetLongFormat,
                MAX_PATH))
    {
        return strLinkedTarget; //file doesn't exist
    }
    string strLinkedTargetLongFormat(szLinkedTargetLongFormat);
    delete[] szLinkedTargetLongFormat;

    return strLinkedTargetLongFormat;
}

Simply call the function ReadLnkFile() to get your target file from the .lnk file:

    string strFullTargetPath = ReadLnkFile("C:\\MyShortcut.lnk"); 

Points of Interest

Many thanks to Jesse Hager (jessehager@iname.com) for reverse engineering the shortcut file format and publishing the document 'The Windows Shortcut File Format'. Without this I would still stuck with IShellLink::GetPath() crashing my application.

History

2008-02-29, Peter Thoemmes: first release.

2008-03-01, Peter Thoemmes: description updated.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Peter Thoemmes

Software Developer (Senior)
SES ENGINEERING Luxembourg
Luxembourg Luxembourg

Member

I am married to Claudia and we have got two children, our daughter Anna-Lena and our son Markus.
---------
Hobbies...
Motorbiking, TaeKwonDo, Jogging, Bicycling, ...
---------
Career...
1981-1983: Apprenticeship - Nachrichtengerätemechaniker
1983-1985: Apprenticeship - Informationselekroniker
1985-1989: German Air Force - Funkelektroniker/Fachabitur
1989-1992: College of Engineering - Dipl.Ing.(FH)
1992-1995: University Saarbrücken/Germany - Dipl.Ing.(UNI)
1995-1999: Real-Time-OS developer for embedded systems
1999-2000: Senior Software Engineer - BPR tools
2001- now: Senior Software Engineer - SAT Earthstation M&C
---------
Private research & publications...
2003 ....: Book 'Notizen zu C++'
2004 ....: Article 'SmartPointers' in dotnetpro
2005 ....: Article 'Portable Strings' in dotnetpro
2006 ....: Article 'Portable Console' in dotnetpro
2007 ....: Article 'DVB-S2, HDTV, H.264 & BDA' in dotnetpro
 


Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralMy vote of 5 Pinmembertransoft10:26 15 Jun '11  
AnswerCrash in rpcrt4.dll PinmemberVasiliy Zverev23:19 6 Oct '09  
GeneralRe: Crash in rpcrt4.dll PinmemberHelgeKlein10:03 25 Aug '11  
GeneralMy vote of 2 Pinmemberilmcuts5:35 20 Dec '08  
GeneralCan't resolve "Microsoft Office Word 2007.lnk" format Pinmemberjshou15:39 3 Dec '08  
Generallol PinmemberJonathan Potter11:47 29 Feb '08  
QuestionNo explanation? WTF? PinmemberJim Crafton4:27 29 Feb '08  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120517.1 | Last Updated 1 Mar 2008
Article Copyright 2008 by Peter Thoemmes
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid