为了账号安全,请及时绑定邮箱和手机立即绑定

Java中的Windows快捷方式(.lnk)解析器?

/ 猿问

Java中的Windows快捷方式(.lnk)解析器?

慕桂英4014372 2019-11-18 13:54:15

我目前正在使用Win32ShellFolderManager2并ShellFolder.getLinkLocation解析Java中的Windows快捷方式。不幸的是,如果Java程序在Vista下作为服务运行getLinkLocation,则无法使用。具体来说,我得到一个异常,指出“无法获取外壳文件夹ID列表”。


搜索网络确实会提及此错误消息,但始终与关联JFileChooser。我没有使用JFileChooser,我只需要将.lnk文件解析到其目的地即可。


有人知道.lnk我可以使用用Java编写的文件的第三方解析器吗?


此后,我在这里找到了有关.lnk格式的非官方文档,但是如果有人以前做过,我宁愿不必这样做,因为该格式相当可怕。


查看完整描述

3 回答

?
ABOUTYOU

添加了注释(到目前为止有一些解释以及对每个贡献者的感谢),对文件魔术的附加检查,快速测试以查看给定的文件是否可能是有效链接(不读取所有字节),并进行了修正如果文件太小,则使用适当的消息而不是ArrayIndexOutOfBoundsException的ParseException进行一些常规清理。


在此处提供源代码(如果您有任何更改,请将其直接推到GitHub repo / project。


package org.stackoverflowusers.file;


import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.text.ParseException;


/**

 * Represents a Windows shortcut (typically visible to Java only as a '.lnk' file).

 *

 * Retrieved 2011-09-23 from http://stackoverflow.com/questions/309495/windows-shortcut-lnk-parser-in-java/672775#672775

 * Originally called LnkParser

 *

 * Written by: (the stack overflow users, obviously!)

 *   Apache Commons VFS dependency removed by crysxd (why were we using that!?) https://github.com/crysxd

 *   Headerified, refactored and commented by Code Bling http://stackoverflow.com/users/675721/code-bling

 *   Network file support added by Stefan Cordes http://stackoverflow.com/users/81330/stefan-cordes

 *   Adapted by Sam Brightman http://stackoverflow.com/users/2492/sam-brightman

 *   Based on information in 'The Windows Shortcut File Format' by Jesse Hager <jessehager@iname.com>

 *   And somewhat based on code from the book 'Swing Hacks: Tips and Tools for Killer GUIs'

 *     by Joshua Marinacci and Chris Adamson

 *     ISBN: 0-596-00907-0

 *     http://www.oreilly.com/catalog/swinghks/

 */

public class WindowsShortcut

{

    private boolean isDirectory;

    private boolean isLocal;

    private String real_file;


    /**

     * Provides a quick test to see if this could be a valid link !

     * If you try to instantiate a new WindowShortcut and the link is not valid,

     * Exceptions may be thrown and Exceptions are extremely slow to generate,

     * therefore any code needing to loop through several files should first check this.

     *

     * @param file the potential link

     * @return true if may be a link, false otherwise

     * @throws IOException if an IOException is thrown while reading from the file

     */

    public static boolean isPotentialValidLink(File file) throws IOException {

        final int minimum_length = 0x64;

        InputStream fis = new FileInputStream(file);

        boolean isPotentiallyValid = false;

        try {

            isPotentiallyValid = file.isFile()

                && file.getName().toLowerCase().endsWith(".lnk")

                && fis.available() >= minimum_length

                && isMagicPresent(getBytes(fis, 32));

        } finally {

            fis.close();

        }

        return isPotentiallyValid;

    }


    public WindowsShortcut(File file) throws IOException, ParseException {

        InputStream in = new FileInputStream(file);

        try {

            parseLink(getBytes(in));

        } finally {

            in.close();

        }

    }


    /**

     * @return the name of the filesystem object pointed to by this shortcut

     */

    public String getRealFilename() {

        return real_file;

    }


    /**

     * Tests if the shortcut points to a local resource.

     * @return true if the 'local' bit is set in this shortcut, false otherwise

     */

    public boolean isLocal() {

        return isLocal;

    }


    /**

     * Tests if the shortcut points to a directory.

     * @return true if the 'directory' bit is set in this shortcut, false otherwise

     */

    public boolean isDirectory() {

        return isDirectory;

    }


    /**

     * Gets all the bytes from an InputStream

     * @param in the InputStream from which to read bytes

     * @return array of all the bytes contained in 'in'

     * @throws IOException if an IOException is encountered while reading the data from the InputStream

     */

    private static byte[] getBytes(InputStream in) throws IOException {

        return getBytes(in, null);

    }


    /**

     * Gets up to max bytes from an InputStream

     * @param in the InputStream from which to read bytes

     * @param max maximum number of bytes to read

     * @return array of all the bytes contained in 'in'

     * @throws IOException if an IOException is encountered while reading the data from the InputStream

     */

    private static byte[] getBytes(InputStream in, Integer max) throws IOException {

        // read the entire file into a byte buffer

        ByteArrayOutputStream bout = new ByteArrayOutputStream();

        byte[] buff = new byte[256];

        while (max == null || max > 0) {

            int n = in.read(buff);

            if (n == -1) {

                break;

            }

            bout.write(buff, 0, n);

            if (max != null)

                max -= n;

        }

        in.close();

        return bout.toByteArray();

    }


    private static boolean isMagicPresent(byte[] link) {

        final int magic = 0x0000004C;

        final int magic_offset = 0x00;

        return link.length >= 32 && bytesToDword(link, magic_offset) == magic;

    }


    /**

     * Gobbles up link data by parsing it and storing info in member fields

     * @param link all the bytes from the .lnk file

     */

    private void parseLink(byte[] link) throws ParseException {

        try {

            if (!isMagicPresent(link))

                throw new ParseException("Invalid shortcut; magic is missing", 0);


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

                isDirectory = true;

            } else {

                isDirectory = 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 = bytesToWord(link, shell_offset) + 2;

            }


            // get to the file settings

            int file_start = 0x4c + shell_len;


            final int file_location_info_flag_offset_offset = 0x08;

            int file_location_info_flag = link[file_start + file_location_info_flag_offset_offset];

            isLocal = (file_location_info_flag & 2) == 0;

            // get the local volume and local system values

            //final int localVolumeTable_offset_offset = 0x0C;

            final int basename_offset_offset = 0x10;

            final int networkVolumeTable_offset_offset = 0x14;

            final int finalname_offset_offset = 0x18;

            int finalname_offset = link[file_start + finalname_offset_offset] + file_start;

            String finalname = getNullDelimitedString(link, finalname_offset);

            if (isLocal) {

                int basename_offset = link[file_start + basename_offset_offset] + file_start;

                String basename = getNullDelimitedString(link, basename_offset);

                real_file = basename + finalname;

            } else {

                int networkVolumeTable_offset = link[file_start + networkVolumeTable_offset_offset] + file_start;

                int shareName_offset_offset = 0x08;

                int shareName_offset = link[networkVolumeTable_offset + shareName_offset_offset]

                    + networkVolumeTable_offset;

                String shareName = getNullDelimitedString(link, shareName_offset);

                real_file = shareName + "\\" + finalname;

            }

        } catch (ArrayIndexOutOfBoundsException e) {

            throw new ParseException("Could not be parsed, probably not a valid WindowsShortcut", 0);

        }

    }


    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 bytesToWord(byte[] bytes, int off) {

        return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);

    }


    private static int bytesToDword(byte[] bytes, int off) {

        return (bytesToWord(bytes, off + 2) << 16) | bytesToWord(bytes, off);

    }


}


查看完整回答
反对 回复 2019-11-18
?
慕后森

public class LnkParser {


public LnkParser(File f) throws IOException {

    parse(f);

}


private boolean isDirectory;

private boolean isLocal;


public boolean isDirectory() {

    return isDirectory;

}


private String real_file;


public String getRealFilename() {

    return real_file;

}


private void parse(File f) throws IOException {

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


    parseLink(link);

}


private void parseLink(byte[] link) {

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

        isDirectory = true;

    } else {

        isDirectory = 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;


    final int file_location_info_flag_offset_offset = 0x08;

    int file_location_info_flag = link[file_start + file_location_info_flag_offset_offset];

    isLocal = (file_location_info_flag & 2) == 0;

    // get the local volume and local system values

    //final int localVolumeTable_offset_offset = 0x0C;

    final int basename_offset_offset = 0x10;

    final int networkVolumeTable_offset_offset = 0x14;

    final int finalname_offset_offset = 0x18;

    int finalname_offset = link[file_start + finalname_offset_offset] + file_start;

    String finalname = getNullDelimitedString(link, finalname_offset);

    if (isLocal) {

        int basename_offset = link[file_start + basename_offset_offset] + file_start;

        String basename = getNullDelimitedString(link, basename_offset);

        real_file = basename + finalname;

    } else {

        int networkVolumeTable_offset = link[file_start + networkVolumeTable_offset_offset] + file_start;

        int shareName_offset_offset = 0x08;

        int shareName_offset = link[networkVolumeTable_offset + shareName_offset_offset]

                + networkVolumeTable_offset;

        String shareName = getNullDelimitedString(link, shareName_offset);

        real_file = shareName + "\\" + 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);

}


/**

 * Returns the value of the instance variable 'isLocal'.

 *

 * @return Returns the isLocal.

 */

public boolean isLocal() {

    return isLocal;

}

}


查看完整回答
反对 回复 2019-11-18
?
达令说

我可以在GitHub上推荐此存储库:


https://github.com/BlackOverlord666/mslinks


在那里,我找到了创建快捷方式的简单解决方案:


ShellLink.createLink("path/to/existing/file.txt", "path/to/the/future/shortcut.lnk");


如果您想阅读快捷方式:


File shortcut = ...;

String pathToExistingFile = new ShellLink(shortcut).resolveTarget();

如果要更改快捷方式的图标,请使用:


ShellLink sl = ...;

sl.setIconLocation("/path/to/icon/file");

您可以编辑快捷链接的大多数属性,例如工作目录,工具提示文本,图标,命令行参数,热键,创建指向LAN共享文件和目录的链接等等。


希望这对您有所帮助:)


查看完整回答
反对 回复 2019-11-18

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信