<p>I'm currently using <code>Win32ShellFolderManager2</code> and <code>ShellFolder.getLinkLocation</code> to resolve windows shortcuts in Java. Unfortunately, if the Java program is running as a service under Vista, <code>getLinkLocation</code>, this does not work. Specifically, I get an exception stating "Could not get shell folder ID list".</p>

<p>Searching the web does turn up mentions of this error message, but always in connection with <code>JFileChooser</code>. I'm not using <code>JFileChooser</code>, I just need to resolve a <code>.lnk</code> file to its destination.</p>

<p>Does anyone know of a 3rd-party parser for <code>.lnk</code> files written in Java I could use?</p>

<p>I've since found unofficial documentation for the .lnk format <a href="http://mediasrv.ns.ac.yu/extra/fileformat/windows/lnk/shortcut.pdf" rel="noreferrer">here</a>, but I'd rather not have to do the work if anyone has done it before, since the format is rather scary.</p>

The code plan9assembler linked to appears to work with minor modification. I think it's just the "
```
& 0xff
```
" to prevent sign extension when
```
byte
```
s are upcast to
```
int
```
s in the
```
bytes2short
```
function that need changing. I've added the functionality described in
http://www.i2s-lab.com/Papers/The_Windows_Shortcut_File_Format.pdf
to concatenate the "final part of the pathname" even though in practice this doesn't seem to be used in my examples. I've not added any error checking to the header or dealt with network shares. Here's what I'm using now:
```
```
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.text.DecimalFormat;
import java.text.NumberFormat;

public class LnkParser {

    public LnkParser(File f) throws Exception {
        parse(f);
    }

    private boolean is_dir;

    public boolean isDirectory() {
        return is_dir;
    }

    private String real_file;

    public String getRealFilename() {
        return real_file;
    }

    private void parse(File f) throws Exception {
        // read the entire file into a byte buffer
        FileInputStream fin = new FileInputStream(f);
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        byte[] buff = new byte[256];
        while (true) {
            int n = fin.read(buff);
            if (n == -1) {
                break;
            }
            bout.write(buff, 0, n);
        }
        fin.close();
        byte[] link = bout.toByteArray();

        // get the flags byte
        byte flags = link[0x14];

        // get the file attributes byte
        final int file_atts_offset = 0x18;
        byte file_atts = link[file_atts_offset];
        byte is_dir_mask = (byte) 0x10;
        if ((file_atts & is_dir_mask) > 0) {
            is_dir = true;
        } else {
            is_dir = false;
        }

        // if the shell settings are present, skip them
        final int shell_offset = 0x4c;
        final byte has_shell_mask = (byte) 0x01;
        int shell_len = 0;
        if ((flags & has_shell_mask) > 0) {
            // the plus 2 accounts for the length marker itself
            shell_len = bytes2short(link, shell_offset) + 2;
        }

        // get to the file settings
        int file_start = 0x4c + shell_len;

        // get the local volume and local system values
        final int basename_offset_offset = 0x10;
        final int finalname_offset_offset = 0x18;
        int basename_offset = link[file_start + basename_offset_offset]
                                + file_start;
        int finalname_offset = link[file_start + finalname_offset_offset]
                                + file_start;
        String basename = getNullDelimitedString(link, basename_offset);
        String finalname = getNullDelimitedString(link, finalname_offset);
        real_file = basename + finalname;
    }

    private static String getNullDelimitedString(byte[] bytes, int off) {
        int len = 0;
        // count bytes until the null character (0)
        while (true) {
            if (bytes[off + len] == 0) {
                break;
            }
            len++;
        }
        return new String(bytes, off, len);
    }

    /*
     * convert two bytes into a short note, this is little endian because it's
     * for an Intel only OS.
     */
    private static int bytes2short(byte[] bytes, int off) {
        return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
    }
}
```
```