سؤال

أنا حاليا باستخدام Win32ShellFolderManager2 و ShellFolder.getLinkLocation لحل اختصارات ويندوز في جاوة.للأسف, إذا كان جافا تشغيل البرنامج كخدمة تحت ويندوز فيستا ، getLinkLocation, هذا لا يعمل.على وجه التحديد, أحصل على استثناء قائلا "لا يمكن الحصول على قذيفة مجلد معرف قائمة".

البحث في الإنترنت لا يحضر يذكر من رسالة الخطأ هذه ، ولكن دائما في اتصال مع JFileChooser.أنا لا تستخدم JFileChooser, أنا فقط بحاجة إلى حل .lnk ملف إلى وجهتها.

لا أحد يعرف من 3-حزب محلل ل .lnk الملفات مكتوب بلغة جافا يمكنني استخدامه ؟

لقد وجدت غير رسمية من وثائق .lnk شكل هنا, ولكن أنا أفضل أن لا يكون لديك للقيام بهذا العمل إذا كان أي شخص قد فعلت ذلك من قبل ، منذ الشكل مخيفة إلى حد ما.

هل كانت مفيدة؟

المحلول

وتعليقات وأضاف (بعض التفسير فضلا عن الائتمان إلى كل مساهم حتى الآن)، الاختيار إضافية على سحر الملف، اختبار سريع لمعرفة ما إذا كان ملف معين قد يكون رابط صحيح (دون قراءة كافة وحدات البايت)، وهو إصلاح لرمي ParseException مع الرسالة المناسبة بدلا من ArrayIndexOutOfBoundsException إذا كان الملف صغير جدا، فعلت بعض العام للتنظيف.

هنا (إذا كان لديك أي تغييرات، ودفع لهم الحق في جيثب الريبو / <لأ href = "الشبكي: / /github.com/codebling/WindowsShortcuts "يختلط =" noreferrer "> مشروع .

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 &lt;jessehager@iname.com&gt;
 *   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);
    }

}

نصائح أخرى

سام برايتمان الحل هو الملفات المحلية فقط.انا واضاف لدعم ملفات الشبكة:

  • ويندوز اختصار (.lnk) محلل في جافا ؟
  • http://code.google.com/p/8bits/downloads/detail?name=The_Windows_Shortcut_File_Format.pdf
  • http://www.javafaq.nu/java-example-code-468.html

    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;
    }
    }
    

يمكنني أن أوصي هذا المستودع على جيثب:

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");

يمكنك تحرير معظم خصائص shortcutlink مثل دليل العمل, نص تلميح الأدوات, رمز, وسائط سطر الأوامر, مفاتيح الاختصار ، وخلق روابط لان الملفات المشتركة و الدلائل وأكثر من ذلك بكثير...

نأمل أن يكون هذا يساعدك :)

مع أطيب التحيات Josua فرانك

ولقد عملت أيضا (الآن لا تملك أي وقت لذلك) على '.lnk "في جاوة. قانون بلدي هو <وأ href = "http://kac-repo.xt.pl/cgi-bin/gitweb.cgi؟p=jshortcut.git؛a=summary" يختلط = "نوفولو noreferrer" عنوان = "الريبو بوابة" > هنا

وانها الفوضى قليلا (بعض التجارب القمامة) ولكن الاعراب المحلي وشبكة أعمال جيدة. خلق روابط ينفذ أيضا. يرجى اختبار وترسل لي بقع.

وفي التحليل سبيل المثال:

Shortcut scut = Shortcut.loadShortcut(new File("C:\\t.lnk"));
System.out.println(scut.toString());

وإنشاء رابط جديد:

Shortcut scut = new Shortcut(new File("C:\\temp"));
OutputStream os = new FileOutputStream("C:\\t.lnk");
os.write(scut.getBytes());
os.flush();
os.close();

ووplan9assembler كود مرتبطة يبدو للعمل مع تعديلات طفيفة. اعتقد انها مجرد "& 0xff" لمنع امتداد علامة عندما bytes هي المهوى الصاعد إلى ints في وظيفة bytes2short التي تحتاج إلى تغيير. واضاف لقد وظيفة هو موضح في http://www.i2s-lab.com/ أوراق عمل / The_Windows_Shortcut_File_Format.pdf لسلسلة "الجزء الأخير من اسم المسار" على الرغم من ذلك من الناحية العملية لا يبدو لاستخدامها في الأمثلة التي ذكرتها. أنا لم يضف أي التحقق من الخطأ إلى رأس أو التعامل مع شبكة سهم. وإليك ما أنا باستخدام الآن:

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);
    }
}

هذا الرمز القصير هو حقا مفيدة...

ولكن اثنين من الإصلاحات المطلوبة:

  • على isPotentialValidLink تحسن عدم تحميل الملف إذا كان اسم لا تنتهي ".lnk"

      public static boolean isPotentialValidLink(final File file) {
        final int minimum_length = 0x64;
        boolean isPotentiallyValid = false;
        if (file.getName().toLowerCase().endsWith(".lnk"))
            try (final InputStream fis = new FileInputStream(file)) {
                isPotentiallyValid = file.isFile() && fis.available() >= minimum_length && isMagicPresent(getBytes(fis, 32));
            } catch (Exception e) {
                // forget it
            }
        return isPotentiallyValid;
      }
    
  • الإزاحة يجب أن يكون محسوب مع 32bits ليس فقط بايت...

     final int finalname_offset = bytesToDword(link,file_start + finalname_offset_offset) + file_start;
     final int basename_offset = bytesToDword(link,file_start + basename_offset_offset) + file_start;
    

وكود معين يعمل بشكل جيد، ولكن لديه علة. بايت يافا هي قيمة موقعة من -128 إلى 127. نريد قيمة غير موقعة من 0 إلى 255 للحصول على النتائج الصحيحة. مجرد تغيير وظيفة bytes2short كما يلي:

static int bytes2short(byte[] bytes, int off) {
    int low = (bytes[off]<0 ? bytes[off]+256 : bytes[off]);
    int high = (bytes[off+1]<0 ? bytes[off+1]+256 : bytes[off+1])<<8;
    return 0 | low | high;
}

حل @رمز بلينغ لا يعمل بالنسبة لي الملفات في دليل المستخدم.
على سبيل المثال "C:/Users/Username/Filename.txt".
والسبب في ذلك هو:في The_Windows_Shortcut_File_Format.pdf

الذي تم ذكره من قبل @ستيفان كوردس في الصفحة 6 تقول أن الأولى فقط 2 بت من المهم حجم المعلومات.جميع أجزاء أخرى قد تكون مليئة عشوائية القمامة عند أول قليلا من حجم المعلومات "0".

حتى لو وصل الأمر إلى:

isLocal = (file_location_info_flag & 2) == 0;

ثم file_location_info_flag قد يكون "3".هذا الملف لا يزال المحلية ولكن هذا الخط من التعليمات البرمجية يعين false إلى isLocal.

لذا أقترح ما يلي: تعديل @رمز بلينغ كود:

isLocal = (file_location_info_flag & 1) == 1;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top