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:
#include <string>
#include <vector>
using namespace std;
string ReadLnkFile(const string& strFullLinkFileName)
{
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);
if(vectBuffer.size() < 20)
return string("");
if(vectBuffer[0] != (unsigned char) 'L')
return string("");
if((vectBuffer[4] != 0x01) ||
(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("");
}
unsigned int i = 20;
if(vectBuffer.size() < (i + 4))
return string("");
unsigned int dwFlags = (unsigned int) vectBuffer[i];
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("");
int32 A = -6;
if(bHasShellItemIdList)
{
i = 76;
if(vectBuffer.size() < (i + 2))
return string("");
A = (unsigned char) vectBuffer[i];
A |= (((unsigned char) vectBuffer[++i]) << 8);
}
i = 78 + 4 + A;
if(vectBuffer.size() < (i + 4))
return string("");
unsigned int B = (unsigned int) vectBuffer[i];
B |= (((unsigned int) vectBuffer[++i]) << 8);
B |= (((unsigned int) vectBuffer[++i]) << 16);
B |= (((unsigned int) vectBuffer[++i]) << 24);
i = 78 + A + B;
if(vectBuffer.size() < (i + 4))
return string("");
unsigned int C = (unsigned int) vectBuffer[i];
C |= (((unsigned int) vectBuffer[++i]) << 8);
C |= (((unsigned int) vectBuffer[++i]) << 16);
C |= (((unsigned int) vectBuffer[++i]) << 24);
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;
}
if(strLinkedTarget.empty())
return string("");
char* szLinkedTargetLongFormat = new char[MAX_PATH + 1];
if(!szLinkedTargetLongFormat)
return string("");
if(!::GetLongPathNameA(
strLinkedTarget.c_str(),
szLinkedTargetLongFormat,
MAX_PATH))
{
return strLinkedTarget;
}
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.