Get the Shortcut target
Get the Shortcut target
Hello all
Iv'e been searching in the forum and exploring the .lnk file by Reading the file (ReadFile()). But I still can't access the target filename????
Please help
Thank in advance
Iv'e been searching in the forum and exploring the .lnk file by Reading the file (ReadFile()). But I still can't access the target filename????
Please help
Thank in advance
Re: Get the Shortcut target
For informations about the LNK format look here:
http://www.stdlib.com/art6-Shortcut-Fil ... t-lnk.html
To find something about the UniCode-Flag:
http://evilcodecave.blogspot.com/2010/0 ... t-and.html
There is a file on the net which is called MS-SHLLINK.pdf
That's the best 'original' source.
But not directly available from MS$, they removed it
Bernd
http://www.stdlib.com/art6-Shortcut-Fil ... t-lnk.html
To find something about the UniCode-Flag:
http://evilcodecave.blogspot.com/2010/0 ... t-and.html
There is a file on the net which is called MS-SHLLINK.pdf
That's the best 'original' source.
But not directly available from MS$, they removed it
Bernd
Last edited by infratec on Sat Aug 21, 2010 8:41 pm, edited 2 times in total.
Re: Get the Shortcut target
A first result out of the above documentation: LnkInfo.pbCompile it as console program.
Bernd
Code: Select all
#Version = "1.06"
Structure ShellLinkHeaderStr
HeaderSize.l
LinkCLSID.a[16]
LinkFlags.l
FileAttributes.l
CreationTime.q
AccessTime.q
WriteTime.q
FileSize.l
IconIndex.l
ShowCommand.l
HotKey.w
Reserved1.w
Reserved2.l
Reserved3.l
EndStructure
Enumeration ; LinkFlags
#HasLinkTargetIDList
#HasLinkInfo
#HasName
#HasRelativePath
#HasWorkingDir
#HasArguments
#HasIconLocation
#IsUnicode
#ForceNoLinkInfo
#HasExpString
#RunInSeparateProcess
#Unused1
#HasDarwinID
#RunAsUser
#HasExpIcon
#NoPidAlias
#Unused2
#RunWithShimLayer
#ForceNoLinkTrack
#EnableTargetMetadata
#DisableLinkPathTracking
#DisableKnownFolderTracking
#DisableKnownFolderAlias
#AllowLinkToLink
#UnaliasOnSave
#PreferEnvironmentPath
#KeepLocalIDListForUNCTarget
EndEnumeration
; already defined
;Enumeration ; FileAttributesFlag
; #FILE_ATTRIBUTE_READONLY
; #FILE_ATTRIBUTE_HIDDEN
; #FILE_ATTRIBUTE_SYSTEM
; #Reserved1
; #FILE_ATTRIBUTE_DIRECTORY
; #FILE_ATTRIBUTE_ARCHIVE
; #Reserved2
; #FILE_ATTRIBUTE_NORMAL
; #FILE_ATTRIBUTE_TEPORARY
; #FILE_ATTRIBUTE_SPARSE_FILE
; #FILE_ATTRIBUTE_REPARSE_POINT
; #FILE_ATTRIBUTE_COMPRESSED
; #FILE_ATTRIBUTE_OFFLINE
; #FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
; #FILE_ATTRIBUTE_ENCRYPTED
;EndEnumeration
Structure LinkInfoStr
LinkInfoSize.l
LinkInfoHeaderSize.l
LinkInfoFlags.l
VolumeIDOffset.l
LocalBasePathOffset.l
CommonNetworkRelativeLinkOffset.l
CommonPathSuffixOffset.l
LocalBasePathOffsetUnicode.l
CommonPathSuffixOffsetUnicode.l
EndStructure
Structure LocalVolumeTableStr
Length.l
TypeOfVolume.l
VolumeSerialNumber.l
OffsetOfVolumeName.l
EndStructure
Enumeration
#DRIVE_UNKNOWN
#DRIVE_NO_ROOT_DIR
#DRIVE_REMOVABLE
#DRIVE_FIXED
#DRIVE_REMOTE
#DRIVE_CDROM
#DRIVE_RAMDISK
EndEnumeration
Structure NetworkVolumeStr
Length.l
Always2.l
OffsetNetworkShareName.l
Reserved0.l
AlwaysHex20000.l
EndStructure
Procedure Usage()
PrintN("")
PrintN(" LnkInfo V" + #Version)
PrintN("")
PrintN(" usage: LnkInfo filename")
PrintN("")
End 1
EndProcedure
Procedure TimeZoneOffset() ; stolen from rescator, thanks!
Protected result,mode
mode = GetTimeZoneInformation_(@TZ.TIME_ZONE_INFORMATION)
If mode = 1
result = TZ \ Bias
ElseIf mode = 2
result = TZ \ Bias + TZ \ DaylightBias
EndIf
ProcedureReturn result * 60
EndProcedure
Procedure.q MSTimeToUnixTime(Time.q)
ProcedureReturn Time / 10000000 - 11644473600 - TimeZoneOffset()
EndProcedure
If OpenConsole()
If CountProgramParameters() <> 1 : Usage() : EndIf
Filename$ = ProgramParameter(0)
If ReadFile(0, Filename$)
Define ByteLengthW.w, ByteLengthL.l, CharLength.w
ReadData(0, @ByteLengthL, 4)
FileSeek(0, 0)
*Header = AllocateMemory(ByteLengthL)
If ReadData(0, *Header, ByteLengthL) = ByteLengthL
*ShellLinkHeader.ShellLinkHeaderStr = *Header
If *ShellLinkHeader\CreationTime
PrintN("CreationTime of the target: " + FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss", MSTimeToUnixTime(*ShellLinkHeader\CreationTime)))
EndIf
If *ShellLinkHeader\AccessTime
PrintN("AccessTime of the target : " + FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss", MSTimeToUnixTime(*ShellLinkHeader\AccessTime)))
EndIf
If *ShellLinkHeader\WriteTime
PrintN("WriteTime of the target : " + FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss", MSTimeToUnixTime(*ShellLinkHeader\WriteTime)))
EndIf
If *ShellLinkHeader\LinkFlags & (1 << #IsUnicode)
UniCode = #True
PeekSFlag = #PB_Unicode
Else
UniCode = #False
PeekSFlag = #PB_Ascii
EndIf
If *ShellLinkHeader\LinkFlags & (1 << #HasLinkTargetIDList)
ReadData(0, @ByteLengthW, 2)
; skip the LinkTargetIDList for now
FileSeek(0, Loc(0) + ByteLengthW)
EndIf
Pos = Loc(0)
If *ShellLinkHeader\LinkFlags & (1 << #HasLinkInfo)
ReadData(0, @BytelengthL, 4)
If ByteLengthL > 0
FileSeek(0, Pos)
*Buffer = AllocateMemory(ByteLengthL)
If ReadData(0, *Buffer, ByteLengthL) = ByteLengthL
*LinkInfo.LinkInfoStr = *Buffer
Target$ = PeekS(*Buffer + *LinkInfo\LocalBasePathOffset)
Target$ + PeekS(*Buffer + *LinkInfo\CommonPathSuffixOffset)
PrintN("Target: " + Target$)
Else
End 2
EndIf
FreeMemory(*Buffer)
EndIf
EndIf
If *ShellLinkHeader\LinkFlags & (1 << #HasName)
ReadData(0, @ByteLengthW, 2)
*Buffer = AllocateMemory(ByteLengthW)
If ReadData(0, *Buffer, ByteLengthW) = ByteLengthW
PrintN("Description: " + PeekS(*Buffer))
Else
End 3
EndIf
FreeMemory(*Buffer)
EndIf
If *ShellLinkHeader\LinkFlags & (1 << #HasRelativePath)
ReadData(0, @CharLength, 2)
If UniCode
ByteLengthW = CharLength * 2
Else
ByteLengthW = CharLength
EndIf
*Buffer = AllocateMemory(ByteLengthW)
If ReadData(0, *Buffer, ByteLengthW) = ByteLengthW
PrintN("RelativePath: " + PeekS(*Buffer, CharLength, PeekSFlag))
Else
End 4
EndIf
FreeMemory(*Buffer)
EndIf
If *ShellLinkHeader\LinkFlags & (1 << #HasWorkingDir)
ReadData(0, @CharLength, 2)
If UniCode
ByteLengthW = CharLength * 2
Else
ByteLengthW = CharLength
EndIf
*Buffer = AllocateMemory(ByteLengthW)
If ReadData(0, *Buffer, ByteLengthW) = ByteLengthW
PrintN("WorkingDirectory: " + PeekS(*Buffer, CharLength, PeekSFlag))
Else
End 5
EndIf
FreeMemory(*Buffer)
EndIf
If *ShellLinkHeader\LinkFlags & (1 << #HasArguments)
ReadData(0, @CharLength, 2)
If UniCode
ByteLengthW = CharLength * 2
Else
ByteLengthW = CharLength
EndIf
*Buffer = AllocateMemory(ByteLengthW)
If ReadData(0, *Buffer, ByteLengthW) = ByteLengthW
PrintN("CommandlineArguments: " + PeekS(*Buffer, CharLength, PeekSFlag))
Else
End 6
EndIf
FreeMemory(*Buffer)
EndIf
If *ShellLinkHeader\LinkFlags & (1 << #HasIconLocation)
ReadData(0, @CharLength, 2)
If UniCode
ByteLengthW = CharLength * 2
Else
ByteLengthW = CharLength
EndIf
*Buffer = AllocateMemory(ByteLengthW)
If ReadData(0, *Buffer, ByteLengthW) = ByteLengthW
PrintN("CustomIcon: " + PeekS(*Buffer, CharLength, PeekSFlag))
Else
End 7
EndIf
FreeMemory(*Buffer)
EndIf
; there is more stuff, but not interresting for now.
Else
End 1
EndIf
FreeMemory(*Header)
CloseFile(0)
Else
PrintN("Was not able to open " + Filename$)
EndIf
EndIf
Bernd
Last edited by infratec on Sun Aug 22, 2010 12:53 pm, edited 12 times in total.
Re: Get the Shortcut target
I updated the file above, since I detected that there are also lnk files with
unicode strings inside.
I also updated the posting with the information links.
Bernd
unicode strings inside.
I also updated the posting with the information links.
Bernd
- Crusiatus Black
- Enthusiast
- Posts: 389
- Joined: Mon May 12, 2008 1:25 pm
- Location: The Netherlands
- Contact:
Re: Get the Shortcut target
Using the header information provided in the first link, I can successfully obtain the creationdate.
However, PB's FormatDate() always return's 0 for all masks over here. eg:
will output
Why is this?
The header is identical to yours, and qwCreationDate is also a quad with the same offset as your
structure. I have not yet tested your console application, but I'll do that and get back here.
I have tested your application and it also returns 00 for everything, here's the qwCreationDate
this shortcut contains. I've tried others, same results.
However, PB's FormatDate() always return's 0 for all masks over here. eg:
Code: Select all
FormatDate("%mm/%dd/%yyyy at %hh:%ii:%ss",LnkHeader\qwCreationDate)
Code: Select all
00/00/0000 at 00:00:00
The header is identical to yours, and qwCreationDate is also a quad with the same offset as your
structure. I have not yet tested your console application, but I'll do that and get back here.
I have tested your application and it also returns 00 for everything, here's the qwCreationDate
this shortcut contains. I've tried others, same results.
Code: Select all
Header\CreationTime was 129267286587343750
Re: Get the Shortcut target
@Crusiatus
I tested it against several of my lnk files.
Sometimes I get the right date, sometimes not.
The MS$ documentation says:
value, the value is not 0
I'll take a look on this.
Bernd
I tested it against several of my lnk files.
Sometimes I get the right date, sometimes not.
The MS$ documentation says:
But if I print out theIf the value is zero,
there is no creation time set on the link target.
value, the value is not 0
I'll take a look on this.
Bernd
Re: Get the Shortcut target
Found something:
Bernd
So we have to modify the value before we can use it, because PB uses 1.1.1970 as start date.The FILETIME structure is a 64-bit value that represents the number of 100-nanosecond intervals
that have elapsed since January 1, 1601, Coordinated Universal Time (UTC).
Bernd
Re: Get the Shortcut target
Updated to V1.03:
I added a procedure to calculate the 'correct' PB time out of MS$ time.
But to my surprise is creation time not the creation time of the link,
it is the creation time of the target.
Bernd
I added a procedure to calculate the 'correct' PB time out of MS$ time.
But to my surprise is creation time not the creation time of the link,
it is the creation time of the target.
Bernd
Re: Get the Shortcut target
Updated to 1.04:
added rescators procedure to get the timezone, because who want's to know
the time in UTC
Bernd
added rescators procedure to get the timezone, because who want's to know
the time in UTC
Bernd
Re: Get the Shortcut target
Updated to V1.05:
Fixed a bug with UniCode:
The length parameter of PeekS() is always the character length and not the
length of the string in bytes
So I learned something new
(Yes, it is written in the help of PeekS(), but who reads always everything ? )
Bernd
Fixed a bug with UniCode:
The length parameter of PeekS() is always the character length and not the
length of the string in bytes
So I learned something new
(Yes, it is written in the help of PeekS(), but who reads always everything ? )
Bernd
Re: Get the Shortcut target
Updated to V1.06:
I hope that's my last version.
I renamed some stuff to MS$ names.
I made it more robust for different sizes of the structs,
because some members are marked as 'optional', so I can not read in a fixed size.
I had to read in the length which is given inside the file itself.
I made use of pointers which overlaps now the buffer which is read in.
This saves space and so I can handle the optional structure members.
Bernd
I hope that's my last version.
I renamed some stuff to MS$ names.
I made it more robust for different sizes of the structs,
because some members are marked as 'optional', so I can not read in a fixed size.
I had to read in the length which is given inside the file itself.
I made use of pointers which overlaps now the buffer which is read in.
This saves space and so I can handle the optional structure members.
Bernd
Re: Get the Shortcut target
I really can't nothing out???? No output. But I want it as a procedure that only returns the target??? It is too complicated for me??
Re: Get the Shortcut target
MaybeIt is too complicated for me??
Or you can not read
Compile it as console program.
Code: Select all
Structure ShellLinkHeaderStr
HeaderSize.l
LinkCLSID.a[16]
LinkFlags.l
FileAttributes.l
CreationTime.q
AccessTime.q
WriteTime.q
FileSize.l
IconIndex.l
ShowCommand.l
HotKey.w
Reserved1.w
Reserved2.l
Reserved3.l
EndStructure
Structure LinkInfoStr
LinkInfoSize.l
LinkInfoHeaderSize.l
LinkInfoFlags.l
VolumeIDOffset.l
LocalBasePathOffset.l
CommonNetworkRelativeLinkOffset.l
CommonPathSuffixOffset.l
LocalBasePathOffsetUnicode.l
CommonPathSuffixOffsetUnicode.l
EndStructure
Enumeration ; neccessary LinkFlags
#HasLinkTargetIDList
#HasLinkInfo
EndEnumeration
Procedure.s GetLinkTarget(FileName$)
If ReadFile(0, Filename$)
Define ByteLengthW.w, ByteLengthL.l, CharLength.w
ReadData(0, @ByteLengthL, 4)
FileSeek(0, 0)
*Header = AllocateMemory(ByteLengthL)
If ReadData(0, *Header, ByteLengthL) = ByteLengthL
*ShellLinkHeader.ShellLinkHeaderStr = *Header
If *ShellLinkHeader\LinkFlags & (1 << #HasLinkTargetIDList)
ReadData(0, @ByteLengthW, 2)
; skip the LinkTargetIDList for now
FileSeek(0, Loc(0) + ByteLengthW)
EndIf
Pos = Loc(0)
If *ShellLinkHeader\LinkFlags & (1 << #HasLinkInfo)
ReadData(0, @BytelengthL, 4)
If ByteLengthL > 0
FileSeek(0, Pos)
*Buffer = AllocateMemory(ByteLengthL)
If ReadData(0, *Buffer, ByteLengthL) = ByteLengthL
*LinkInfo.LinkInfoStr = *Buffer
Target$ = PeekS(*Buffer + *LinkInfo\LocalBasePathOffset)
Target$ + PeekS(*Buffer + *LinkInfo\CommonPathSuffixOffset)
Else
Target$ = "Error: A fault occured"
EndIf
FreeMemory(*Buffer)
EndIf
Else
Target$ = "Error: " + Filename$ + " has no LinkInfo"
EndIf
EndIf
FreeMemory(*Header)
CloseFile(0)
Else
Target$ = "Error: Was not able to open " + Filename$
EndIf
ProcedureReturn Target$
EndProcedure
- Crusiatus Black
- Enthusiast
- Posts: 389
- Joined: Mon May 12, 2008 1:25 pm
- Location: The Netherlands
- Contact:
Re: Get the Shortcut target
Ah, thank you Bernd for the date fix and all the extra information
Re: Get the Shortcut target
Thank you soooo much. Got it to work now :DDD