This file's binary dump is as follows:
0: 4C 00 00 00 01 14 02 00 00 00 00 00 C0 00 00 00 | L...............
10: 00 00 00 46 8B 00 00 00 20 00 00 00 C4 2D AF 4C | ...F.... ....-.L
20: 3B 20 C7 01 34 4D C2 4C 3B 20 C7 01 00 33 DA 17 | ; ..4M.L; ...3..
30: A3 25 C5 01 00 E0 14 00 00 00 00 00 00 00 00 00 | .%..............
40: 00 00 00 00 00 00 00 00 00 00 00 00 FB 00 14 00 | ................
50: 1F 50 E0 4F D0 20 EA 3A 69 10 A2 D8 08 00 2B 30 | .P.O. .:i.....+0
60: 30 9D 19 00 2F 43 3A 5C 00 00 00 00 00 00 00 00 | 0.../C:\........
70: 00 00 00 00 00 00 00 00 00 00 00 4A 00 31 00 00 | ...........J.1..
80: 00 00 00 8F 35 9D 59 11 00 50 52 4F 47 52 41 7E | ....5.Y..PROGRA~
90: 31 00 00 32 00 03 00 04 00 EF BE F4 30 9C 49 8F | 1..2........0.I.
A0: 35 9D 59 14 00 00 00 50 00 72 00 6F 00 67 00 72 | 5.Y....P.r.o.g.r
B0: 00 61 00 6D 00 20 00 46 00 69 00 6C 00 65 00 73 | .a.m. .F.i.l.e.s
C0: 00 00 00 18 00 36 00 31 00 00 00 00 00 8F 35 CE | .....6.1......5.
D0: 5A 10 00 59 43 49 49 49 00 22 00 03 00 04 00 EF | Z..YCIII."......
E0: BE 8F 35 9D 59 8F 35 CE 5A 14 00 00 00 59 00 43 | ..5.Y.5.Z....Y.C
F0: 00 49 00 49 00 49 00 00 00 14 00 4C 00 32 00 00 | .I.I.I.....L.2..
100: E0 14 00 6A 32 43 97 20 00 59 61 6E 6B 43 6C 69 | ...j2C. .YankCli
110: 70 2E 65 78 65 00 00 30 00 03 00 04 00 EF BE 8F | p.exe..0........
120: 35 CE 5A 8F 35 CE 5A 14 00 00 00 59 00 61 00 6E | 5.Z.5.Z....Y.a.n
130: 00 6B 00 43 00 6C 00 69 00 70 00 2E 00 65 00 78 | .k.C.l.i.p...e.x
140: 00 65 00 00 00 1C 00 00 00 58 00 00 00 1C 00 00 | .e.......X......
150: 00 01 00 00 00 1C 00 00 00 33 00 00 00 00 00 00 | .........3......
160: 00 57 00 00 00 17 00 00 00 03 00 00 00 5C 00 F6 | .W...........\..
170: 84 10 00 00 00 53 59 53 54 45 4D 00 43 3A 5C 50 | .....SYSTEM.C:\P
180: 72 6F 67 72 61 6D 20 46 69 6C 65 73 5C 59 43 49 | rogram Files\YCI
190: 49 49 5C 59 61 6E 6B 43 6C 69 70 2E 65 78 65 00 | II\YankClip.exe.
1A0: 00 2C 00 2E 00 2E 00 5C 00 2E 00 2E 00 5C 00 2E | .,.....\.....\..
1B0: 00 2E 00 5C 00 2E 00 2E 00 5C 00 50 00 72 00 6F | ...\.....\.P.r.o
1C0: 00 67 00 72 00 61 00 6D 00 20 00 46 00 69 00 6C | .g.r.a.m. .F.i.l
1D0: 00 65 00 73 00 5C 00 59 00 43 00 49 00 49 00 49 | .e.s.\.Y.C.I.I.I
1E0: 00 5C 00 59 00 61 00 6E 00 6B 00 43 00 6C 00 69 | .\.Y.a.n.k.C.l.i
1F0: 00 70 00 2E 00 65 00 78 00 65 00 10 00 00 00 05 | .p...e.x.e......
200: 00 00 A0 26 00 00 00 77 00 00 00 60 00 00 00 03 | ...&...w...`....
210: 00 00 A0 58 00 00 00 00 00 00 00 6D 61 73 74 65 | ...X.......maste
220: 72 33 38 30 00 00 00 00 00 00 00 DC C4 FB 5D 00 | r380..........].
230: 48 10 4B 87 52 25 C8 D8 96 D5 23 36 32 7D 8A 2E | H.K.R%....#62}..
240: 8C DB 11 81 97 00 13 72 27 20 D0 DC C4 FB 5D 00 | .......r' ....].
250: 48 10 4B 87 52 25 C8 D8 96 D5 23 36 32 7D 8A 2E | H.K.R%....#62}..
260: 8C DB 11 81 97 00 13 72 27 20 D0 00 00 00 00 | .......r' .....
as you can see, the shortcut's target is
C:\Program Files\YCIII\YankClip.exe
But if I ask IShellLink::GetPath to tell me the shortcut's target, I get:
I:\Program Files\YCIII\YankClip.exe
(even with SLGP_RAWPATH)
The "I:\Program Files" presumably comes from my machine's
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramsFilesDir
How comes I don't get the value that's inside the .lnk?
How do get I the real value?
SLGP_RAWPATH tells GetPath to extract the path from the EXP_SZ_LINK data block. This is used when the shortcut target contains expandable environment variables, which is not the case here.
> The "I:\Program Files" presumably comes from my machine's
>
> HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramsFilesDir
>
> How comes I don't get the value that's inside the .lnk?
The shell link object tracks special folders. When the shortcut is created, the link object notices that the shortcut target is under CSIDL_PROGRAM_FILES and attaches a EXP_SPECIAL_FOLDER data block. When the shortcut is subsequently loaded, the link object munges the shortcut target so that it matches the current location of the special folder.
> How do get I the real value?
I don't see any way to get this other than by extracting it directly from the lnk file. This is actually not too hard. The file begins with the shell link data structure, the first 4 bytes of which contain its own length (0x4C bytes). There then follows a length-prefixed ID-list, which can be read in using ILLoadFromStream[Ex]. You can then call SHGetPathFromIDList to obtain the path.
--
Jim Barry, MVP (Windows SDK)
>> The "I:\Program Files" presumably comes from my machine's
>>
>> HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramsFilesDir
>>
>> How comes I don't get the value that's inside the .lnk?
>
> The shell link object tracks special folders. When the shortcut is
> created, the link object notices that the shortcut target is under
> CSIDL_PROGRAM_FILES and attaches a EXP_SPECIAL_FOLDER data block.
> When the shortcut is subsequently loaded, the link object munges the
> shortcut target so that it matches the current location of the
> special folder.
>
>> How do get I the real value?
>
> I don't see any way to get this other than by extracting it directly
> from the lnk file. This is actually not too hard. The file begins
> with the shell link data structure, the first 4 bytes of which
> contain its own length (0x4C bytes). There then follows a
> length-prefixed ID-list, which can be read in using
> ILLoadFromStream[Ex]. You can then call SHGetPathFromIDList to obtain
> the path.
Thx a zillion Jim for the detailed explanation.
Where do I find information about the actual internal structure of the .LNK
file format, where EXP_SPECIAL_FOLDER and EXP_SZ_LINK and friends are
presumably documented?
(RTFM is OK, just gimme the value of TFM ;-)
The format of .lnk files is undocumented. For information on the extra data blocks, see IShellLinkDataList:
http://msdn2.microsoft.com/en-us/library/bb774916.aspx
Let me rephrase the question then: given the absence of a documentation for
the binary-level format of .LNK files, what lower-level-than-IShellLink APIs
do I have to use to open a .LNK file, determine whether it contains a
EXP_SPECIAL_FOLDER (if needed) , and at the end of the day get the
"unmunged" version of the shortcut's target?
Call this "re-implementing IShellLink::GetPath() without the special folder
handling" if you wish...
First question really is - what is the path of the pidl returned from
IShellLink::GetIDList? Is that the original path? It might be that
this doesn't resolve to the new special folder value.
But if it doesn't, then I think you're a bit stuffed.
I reckon what happens when you call GetPath, is that
IShellLinkDataList::CopyDataBlock is called for EXP_SPECIAL_FOLDER.
This gets a copy of the EXP_SPECIAL_FOLDER structure (http://
msdn2.microsoft.com/en-us/library/bb773279.aspx), which will contain
the id of the special folder the item referred to in the link lives
under. It also contains an offset which isn't very well documented. I
think this is an offset into the IDList returned by GetIDList, which
is of course just a list of ItemIDs from the desktop through to the
actual item. The offset would simply lop off the first part of the
pidl that corresponds to the special folder, so to create a new
IDList, you'd simply get the current IDList of the special folder and
append the offset of the original IDList. Which IDList is exposed by
GetIDList is another question...
Does IShellLink also implement IPersistIDList? Might this expose the
original IDList?
Cheers
Matt
Using
LPITEMIDLIST pidl;
hRes = ipShellLink->GetIDList(&pidl);
if (FAILED(hRes))
return hRes;
SHGetPathFromIDList(pidl, szPath);
I *still* get the *modified* (i.e., local, not original) path...
>
> But if it doesn't, then I think you're a bit stuffed.
>
> I reckon what happens when you call GetPath, is that
> IShellLinkDataList::CopyDataBlock is called for EXP_SPECIAL_FOLDER.
> This gets a copy of the EXP_SPECIAL_FOLDER structure (http://
> msdn2.microsoft.com/en-us/library/bb773279.aspx), which will contain
> the id of the special folder the item referred to in the link lives
> under. It also contains an offset which isn't very well documented. I
> think this is an offset into the IDList returned by GetIDList, which
> is of course just a list of ItemIDs from the desktop through to the
> actual item. The offset would simply lop off the first part of the
> pidl that corresponds to the special folder, so to create a new
> IDList, you'd simply get the current IDList of the special folder and
> append the offset of the original IDList. Which IDList is exposed by
> GetIDList is another question...
How does one "manually walk" the IDList returned by GetIDList()?
>
> Does IShellLink also implement IPersistIDList? Might this expose the
> original IDList?
How would one know / invoke it?
>
> Cheers
> Matt
No - the special folder mapping is done when the shortcut is first loaded.
> Does IShellLink also implement IPersistIDList?
No.