Finding the target of a .lnk file

How can I find the file that a .lnk file points to?
matt_whiteAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

KDivadCommented:
Personnally, I would open the lnk and read it for the file name. I have looked at several by opening them in notepad and they all seem to have a similar format though I've never tried to decipher it.
matt_whiteAuthor Commented:
There has got to be some API to do this.  I'm just not sure which.
magoo2Commented:
matt...try this:

Private Sub Command1_Click()
'where fname is the name of the .lnk file

If Right(fname, 4) = ".lnk" Then
Dim str1 As String
Dim str2 As String

Open fname For Binary As #1
Do While Not EOF(1)
   Line Input #1, str1
  If Right(str1, 4) = ".exe" Then
    str2 = Left(str1, 3)
    str2 = Right(str2, 2)
    If str2 = ":\" Then
      If FileExist(str1) = True Then
        'do something with it
        Close #1
        Exit Sub
      Else
       MsgBox "Didnt Find The Executable"
       Close #1
       Exit Sub
      End If
  Else
    Loop
  End If
End If

Close #1
MsgBox "Didnt Find .exe In The .lnk File"

End Sub

Public Function FileExist(ByVal sIn As String, Optional flags As Long = 0) As Boolean
'Name: A Better FileExist
'
'VB Dir Constants and actual values to use as flags:
'   vbNormal = 0    (default, a normal file)
'   vbHidden = 2    (hidden attibute set)
'   vbSystem = 4    (system file)
'   vbVolume = 8    (volume lable, if used all other atrributes are ignored)
'   vbDirectory = 16    (checks for dir or folder name)
'
'   all but the vbVolume dir constants
'   can be combined in the function call such as to check for a hidden system file
'   or any file/folder no matter the attributes

On Error GoTo err_handler
'This part is a safety net, it should be
'done by the calling procedure but just in case...
sIn = Trim$(sIn)
If Len(sIn) = 0 Then
    FileExist = False
    Exit Function
End If
If flags = 0 Then
    FileExist = Dir$(sIn) <> ""
Else
    FileExist = Dir$(sIn, flags) <> ""
End If

Exit Function
err_handler:
    If Err.Number <> 0 Then
        Err.Clear
        FileExist = False
    End If
End Function


hope this helps
magoo
 
Determine the Perfect Price for Your IT Services

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden with our free interactive tool and use it to determine the right price for your IT services. Download your free eBook now!

magoo2Commented:
matt...i gave you a bad answer , my apologies. The above will not work...instead try this:

Private Sub Command1_Click()
'where fname is the name of the .lnk file
Dim fname As String
Dim str1 As String
Dim str2 As String

If Right(fname, 4) = ".lnk" Then

Open fname For Binary As #1
On Error GoTo errhandler:

Do While Not EOF(1)
   Line Input #1, str1
   If Len(str1) > 7 Then
     If Mid$(str1, 2, 2) = ":\" And _
     LCase$(Right$(Trim$(str1), 4)) = ".exe" Then
        MsgBox str1 & " Was Found"
        'do something with it here
        Close #1
        Exit Sub
      End If
   End If
   
    Loop
End If

Close #1
MsgBox "Didnt Find .exe In The .lnk File"
Exit Sub

errhandler:
Close #1
MsgBox Err & " " & Error
Exit Sub

End Sub

sorry for the inconvienence.
magoo
KDivadCommented:
Tried it, works great for an .exe file, what about others? I know, just change the extension that you look for, what if you don't want to watch for all the extensions? What about files that don't have an extension? Folders?
KDivadCommented:
Alright, I got it. Tried it with shortcuts to exe's, txt's and a folder.
Too many times have I posted a working answer as a comment because someone else had the question locked and the one with the locked question got the points, so I must ask that you release the question so I can post as an answer.
magoo2Commented:
matt...If the comment answer doesnt do what you were looking for, then please do release the question so kdivad can post his answer. Thanks.
magoo
matt_whiteAuthor Commented:
Fire away kdivad!
KDivadCommented:
First off, you need to get all the bytes out of the file. I use a function I wrote myself, but any method will work as long as it gets the entire unmodified file.
While slow for larger files, the following would work for something this small:

Dim SB As String * 1
Open MyLNKFile For Binary As 1
For I = 1 To LOF(1)
    Get #1, , SB
    FT = FT & SB
Next

'I noticed that the easiest way is to look for this specific
'string of bytes which shows up right before the drive's
'volume label.
S = Chr$(251) & Chr$(15) & Chr$(84) & Chr$(21) & Chr$(16) & Chr$(0) & Chr$(0) & Chr$(0)
'Search for the above string
x = InStr(1, FT, S)
'Jump completely over the above string and find the first
'Chr$(0), which is right before the name of the file
St = InStr(x + 8, FT, Chr$(0)) + 1
'Find the Chr$(0) that marks the end of the file name
Ln = InStr(St, FT, Chr$(0)) - St
'And, finally, remove the name from between them
FileName = Mid$(FT, St, Ln)


That's it! Hope this works for you.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
matt_whiteAuthor Commented:
KDivad, this line returns a zero:

X = InStr(1, FT, S)

Is there something wrong with 'S'?  Is your solution Win 95/98 compatible?
KDivadCommented:
Returns a zero?!?! I wrote that on Win95. If you will send me an .lnk you are having trouble with, I'll look at it and try to get my code working for it or see why it doesn't work.

kdlsnickers@netnav.com
magoo2Commented:
kdivad...if you get the procedure to work , i would like to see the results please , because i dont don't want to watch for all the extensions and files that don't have an extension and Folders. thanks
magoo
KDivadCommented:
I am trying to get some lnk files from Win98. While the procedure works flawlessly for every lnk file I've tried that was written in Win95, Win98 apears to use a slightly different format. There are two entries in the file that could be the destination file, but I need more lnk's to compare before I can determine which one is which and write the code accordingly. Someone emailed me an lnk (I forget who) and I responded asking for another one so that I could compare but I have yet to recieve a response. If anyone will send me one or two, I should be able to figure it out about 2 or 3 minutes after I recieve them.
KDivadCommented:
Got it.
Finds (correctly, of course) the file or folder that the lnk points to. Tried it on a lnk to two dif. files and a folder and one lnk from Win98.

Give me just a moment to touch up the code and I'll post.
KDivadCommented:
Here it is. With a bonus! It retrieves the long and the short path/file name from the lnk.

Don't know if "S" needs to be so long, but I have yet to have it tricked in any of the lnk's I've tried and I didn't want to shorten it because a shorter series may show up sooner than the real one.

Still trying to get the command line parameters that are contained in some lnk's.

    S = Chr$(105) & Chr$(16) & Chr$(162) & Chr$(216) & Chr$(8) & Chr$(0) & Chr$(43) & Chr$(48) & Chr$(48) & Chr$(157) & Chr$(25) & Chr$(0) & Chr$(35)
    X = InStr(1, FT, S) + 13
    Y = InStr(X, FT, Chr$(0))
    'This first section is the drive which is seperated slightly
    'different from the others.
    ShortPath = ShortPath & Mid$(FT, X, Y - X)
    LongPath = LongPath & Mid$(FT, X, Y - X)
    X = Y + 33
NextStop:
    Y = InStr(X, FT, Chr$(0))
    X2 = Y
    Y2 = InStr(X2 + 1, FT, Chr$(0))
    LongPath = X1.TerminateWithSlash(LongPath) & Mid$(FT, X, Y - X)
    'If the long path is already 8.3 compatible, Windows doesn't
    'add a second entry for the 8.3 name, so we need to check if it's skipped
    If Y2 = X2 + 1 Then
        ShortPath = X1.TerminateWithSlash(ShortPath) & Mid$(FT, X, Y - X)
    Else
        ShortPath = X1.TerminateWithSlash(ShortPath) & Mid$(FT, X2 + 1, Y2 - (X2 + 1))
    End If
    'If the short name is followed by three Chr$(0), then there
    'are no more additions to be searched for.
    If Mid$(FT, Y2, 3) = Chr$(0) & Chr$(0) & Chr$(0) Then
        MsgBox "Short (8.3) path name: " & ShortPath & Chr$(10) & Chr$(10) & "Long path name: " & LongPath
    Else
    'Otherwise, we need to step to the next section
        X = Y2 + 15
        GoTo NextStop
    End If
KDivadCommented:
Oops. Little note here: X is an ocx I wrote, TerminateWithSlash is just a function that adds a slash to the end if there isn't one already. Just replace with something like:

If Right$(LongPath, 1) = "\" Then
    LongPath = LongPath & blah, blah, blah
Else
    LongPath = LongPath & "\" & blah, blah, blah
End If

Hope this works now.
matt_whiteAuthor Commented:
Perfect!  It works great.
KDivadCommented:
Glad it worked!

Later,
mcriderCommented:
In case any of you are interrested, this will return all of the information about a link.

Add the following to a MODULE:

'-------------------------------------------------------------------------------
    Type LnkHeader
        SOH As Long
        GUID As String * 16
        Flags As Long
        Attributes As Long
        CreatedTime As Double
        ModifiedTime As Double
        LastAccessed As Double
        Length As Long
        Icon As Long
        ShowWindow As Long
        HotKey As Long
        Resvd1 As Long
        Resvd2 As Long
        ShellIDLen As Integer
    End Type
     
    Type LnkFLocInfo
        Length As Long
        Offset As Long
        Flags As Long
        LocVolInfoOffset As Long
        BasePathOffset As Long
        NetVolInfoOffset As Long
        NetRemainingPathOffset As Long
    End Type
     
    Type LnkLocVolTable
        Length As Long
        VolType As Long
        SerialNumber As Long
        VolNameOffset As Long
    End Type
     
    Type LnkNetVolTable
        Length As Long
        Resvd1 As Long
        ShareNameOffset As Long
        Resvd2 As Long
        Resvd3 As Long
    End Type
     
    Type LnkFileEntry
        Size As Integer
        Path As String * 256
    End Type
     
    Type LinkInfo
        VolumeLabel As String
        NetShareName As String
        BasePath As String
        RemainingPath As String
        LastModified As String
        Description As String
        RelativePath As String
        WorkingDirectory As String
        CommandLine As String
        IconFile As String
    End Type
     
    Function BitValue(BitNumber As Integer, Value As Variant) As Boolean
        Dim lBits As String
        If UCase$(Left$(CStr(Value), 2)) = "&B" Then
            lBits = Right$(String(128, "0") + Mid$(Value, 3), 128)
        Else
            lBits = cvtBin(Value, 128)
        End If
        If Left$(Right$(lBits, BitNumber + 1), 1) = "1" Then BitValue = True
    End Function
     
    Function cvtBin(Value As Variant, Length As Integer) As String
        Dim BinString As String
        Dim lHex As String
        Dim iVal As Integer
        lHex = UCase$(Hex$(Val(Value)))
        BinString = String(Length, "0")
        For iVal = 1 To Len(lHex)
            Select Case Mid$(lHex, iVal, 1)
                Case "0": BinString = BinString + "0000"
                Case "1": BinString = BinString + "0001"
                Case "2": BinString = BinString + "0010"
                Case "3": BinString = BinString + "0011"
                Case "4": BinString = BinString + "0100"
                Case "5": BinString = BinString + "0101"
                Case "6": BinString = BinString + "0110"
                Case "7": BinString = BinString + "0111"
                Case "8": BinString = BinString + "1000"
                Case "9": BinString = BinString + "1001"
                Case "A": BinString = BinString + "1010"
                Case "B": BinString = BinString + "1011"
                Case "C": BinString = BinString + "1100"
                Case "D": BinString = BinString + "1101"
                Case "E": BinString = BinString + "1110"
                Case "F": BinString = BinString + "1111"
            End Select
        Next iVal
        cvtBin = Right$(BinString, Length)
    End Function
     
    Function GetLinkInfo(LinkPath As String) As LinkInfo
        Dim LinkHeader As LnkHeader
        Dim FileLocBlock As LnkFLocInfo
        Dim LocVolTable As LnkLocVolTable
        Dim NetVolTable As LnkNetVolTable
        Dim LinkInfo As LinkInfo
        Dim fInfo As LnkFileEntry
        Dim lIndex As Long
        Dim fNum As Long
        Dim fLen As Long
        Dim iVal As Long
        Dim lBuf As String
        Dim lPath As String * 256
         
        On Error Resume Next
        fNum = FreeFile
        Err = 0
        Open LinkPath For Binary Access Read As fNum
        If Not Err = 0 Then Exit Function
        LinkInfo.LastModified = FileDateTime(LinkPath)
        fLen = LOF(fNum)
        Get #fNum, 1, LinkHeader
        Get #fNum, LinkHeader.ShellIDLen + 79, FileLocBlock
        Get #fNum, FileLocBlock.LocVolInfoOffset + LinkHeader.ShellIDLen + 79, LocVolTable
        Get #fNum, LocVolTable.VolNameOffset + FileLocBlock.LocVolInfoOffset _
            + LinkHeader.ShellIDLen + 80, lPath
        LinkInfo.VolumeLabel = Left$(lPath, InStr(1, lPath, Chr$(0)) - 1)
        Get #fNum, FileLocBlock.NetVolInfoOffset + LinkHeader.ShellIDLen + 79, NetVolTable
        Get #fNum, NetVolTable.ShareNameOffset + FileLocBlock.NetVolInfoOffset _
            + LinkHeader.ShellIDLen + 79, lPath
        LinkInfo.NetShareName = Left$(lPath, InStr(1, lPath, Chr$(0)) - 1)
        Get #fNum, FileLocBlock.BasePathOffset + LinkHeader.ShellIDLen + 79, lPath
        LinkInfo.BasePath = Left$(lPath, InStr(1, lPath, Chr$(0)) - 1)
        Get #fNum, FileLocBlock.NetRemainingPathOffset + LinkHeader.ShellIDLen + 79, lPath
        LinkInfo.RemainingPath = Left$(lPath, InStr(1, lPath, Chr$(0)) - 1)
        lIndex = Len(LinkInfo.RemainingPath) + NetVolTable.Length _
            + FileLocBlock.NetVolInfoOffset + LinkHeader.ShellIDLen + 80
        If BitValue(2, LinkHeader.Flags) = True Then
            Get #fNum, lIndex, fInfo
            LinkInfo.Description = Left$(fInfo.Path, fInfo.Size)
            lIndex = lIndex + fInfo.Size + 2
        End If
        If BitValue(3, LinkHeader.Flags) = True Then
            Get #fNum, lIndex, fInfo
            LinkInfo.RelativePath = Left$(fInfo.Path, fInfo.Size)
            lIndex = lIndex + fInfo.Size + 2
        End If
        If BitValue(4, LinkHeader.Flags) = True Then
            Get #fNum, lIndex, fInfo
            LinkInfo.WorkingDirectory = Left$(fInfo.Path, fInfo.Size)
            lIndex = lIndex + fInfo.Size + 2
        End If
        If BitValue(5, LinkHeader.Flags) = True Then
            Get #fNum, lIndex, fInfo
            LinkInfo.CommandLine = Left$(fInfo.Path, fInfo.Size)
            lIndex = lIndex + fInfo.Size + 2
        End If
        If BitValue(6, LinkHeader.Flags) = True Then
            Get #fNum, lIndex, fInfo
            LinkInfo.IconFile = Left$(fInfo.Path, fInfo.Size)
            lIndex = lIndex + fInfo.Size + 2
        End If
        Close #fNum
        GetLinkInfo = LinkInfo
    End Function
'-------------------------------------------------------------------------------



Then you can do something like this to get information about a link:

'-------------------------------------------------------------------------------
    With GetLinkInfo("c:\windows\desktop\DupFinder.lnk")
        Debug.Print "VolumeLabel="; .VolumeLabel
        Debug.Print "NetShareName="; .NetShareName
        Debug.Print "BasePath="; .BasePath
        Debug.Print "RemainingPath="; .RemainingPath
        Debug.Print "LastModified="; .LastModified
        Debug.Print "Description="; .Description
        Debug.Print "RelativePath="; .RelativePath
        Debug.Print "WorkingDirectory="; .WorkingDirectory
        Debug.Print "CommandLine="; .CommandLine
        Debug.Print "IconFile="; .IconFile
    End With
'-------------------------------------------------------------------------------



Cheers!.)

KDivadCommented:
VERY interested, actually. I've been trying to figure all that info out for a while now. Thanks!
mcriderCommented:
There is also an approach using WSH, but I'm not recommending WSH to anyone after the ILU Virus...  This is completely self-contained inside your VB app...

Glad you liked it KDivad... Enough to throw some points my way?? ;-)


Cheers!®©
KDivadCommented:
I had been working with a doc on the format of a lnk file, but couldn't quite get it all figured out. Thanks to your code, I understand it now.

I have a guess about your code: A. You copy/pasted from somewhere else, or B. This is code you wrote a LONG time ago. Am I right? I make this guess because the code's not quite correct. It has a couple of "+ 80"'s that should be "+ 79"'s. It returns my HD's label incorrectly unless changed.
mcriderCommented:
KDivad, Actually, it's fairly new... I wrote it from the specs at http://wotsit.org/ 

This place has file specs for alot of file formats.  Curious... what OS are you using? I wrote it on win95...


Cheers!®©
KDivadCommented:
(maybe I can get this post to work... GRRRR...)

I think we probably used the same specs. I just have a bit of trouble following someone else's ideas. I am using Win98, but that shouldn't make a difference (Win95 vs. Win98 as pertaining to .lnk's), should it?
mcriderCommented:
Well I should hope not, but you never know with microsoft... for example from Win95 to Win98 they *completely* removed the API calls to get the available GDI memory...
MoondancerCommented:
This question was awarded, but never cleared due to the JSP-500 errors of that time.  It was "stuck" against userID -1 versus the intended expert whom you awarded.  This corrects the problem and the expert will now receive these points; points verified.

Please click on your Member Profile and select "View Question History" to navigate through any open or locked questions you may have to update and finalize them.  If you are an EE Pro user, you can also choose Power Search to find all your open questions.

This is the Community Support link, if help is needed, along with the link to All Topics which reflects many TAs recently added.

http://www.experts-exchange.com/jsp/qList.jsp?ta=commspt
http://www.experts-exchange.com/jsp/zonesAll.jsp
 
Thank you,
Moondancer
Moderator @ Experts Exchange
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic Classic

From novice to tech pro — start learning today.