Discussion:
Unexpected behavior from IShellLink::GetPath
(too old to reply)
Stephane Barizien
2007-12-05 18:21:34 UTC
Permalink
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
2007-12-06 13:19:33 UTC
Permalink
Post by Stephane Barizien
I have a .lnk that comes from another machine (actually extracted
from a Ghost image)
[snip]
Post by Stephane Barizien
as you can see, the shortcut's target is
C:\Program Files\YCIII\YankClip.exe
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.
Post by Stephane Barizien
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.
Post by Stephane Barizien
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
2007-12-06 14:37:22 UTC
Permalink
Jim Barry wrote:
[snip]
Post by Jim Barry
Post by Stephane Barizien
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.
Post by Stephane Barizien
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
2007-12-06 14:46:37 UTC
Permalink
Post by Stephane Barizien
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
--
Jim Barry, MVP (Windows SDK)
Stephane Barizien
2007-12-06 16:01:33 UTC
Permalink
Post by Jim Barry
Post by Stephane Barizien
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
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...
Stephane Barizien
2007-12-07 09:33:15 UTC
Permalink
On Dec 6, 4:01 pm, "Stephane Barizien"
Post by Stephane Barizien
Post by Jim Barry
Post by Stephane Barizien
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
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
2007-12-07 11:40:46 UTC
Permalink
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.
--
Jim Barry, MVP (Windows SDK)
Loading...