Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Unexpected behavior from IShellLink::GetPath

10 views
Skip to first unread message

Stephane Barizien

unread,
Dec 5, 2007, 1:21:34 PM12/5/07
to
I have a .lnk that comes from another machine (actually extracted from a
Ghost image)

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?


Jim Barry

unread,
Dec 6, 2007, 8:19:33 AM12/6/07
to
Stephane Barizien wrote:
> I have a .lnk that comes from another machine (actually extracted
> from a Ghost image)
>
> This file's binary dump is as follows:
[snip]

> 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)

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)

Stephane Barizien

unread,
Dec 6, 2007, 9:37:22 AM12/6/07
to
Jim Barry wrote:
[snip]

>> 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 ;-)

Jim Barry

unread,
Dec 6, 2007, 9:46:37 AM12/6/07
to
Stephane Barizien wrote:
> 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?

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

Stephane Barizien

unread,
Dec 6, 2007, 11:01:33 AM12/6/07
to

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...


Matt Ellis

unread,
Dec 6, 2007, 7:19:07 PM12/6/07
to
On Dec 6, 4:01 pm, "Stephane Barizien"

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

Stephane Barizien

unread,
Dec 7, 2007, 4:33:15 AM12/7/07
to
Matt Ellis wrote:
> On Dec 6, 4:01 pm, "Stephane Barizien"
> <steNphOane.SbarPiz...@oAcMe.com> wrote:
>> Jim Barry wrote:
>>> Stephane Barizien wrote:
>>>> 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?
>>
>>> 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.

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


Jim Barry

unread,
Dec 7, 2007, 6:40:46 AM12/7/07
to
Matt Ellis wrote:
> First question really is - what is the path of the pidl returned from
> IShellLink::GetIDList? Is that the original path?

No - the special folder mapping is done when the shortcut is first loaded.

> Does IShellLink also implement IPersistIDList?

No.

0 new messages