O'Reilly logo

Swing Hacks by Chris Adamson, Joshua Marinacci

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

158
|
Chapter 4, File Choosers
#30 Real Windows Shortcut Support
HACK
H A C K
#30
Real Windows Shortcut Support
Hack #30
Support Windows shortcuts by actually opening and parsing files with the
under-documented LNK format.
It’s one thing to properly display Windows shortcuts [Hack #29] by looking for
the .lnk extension and changing the default icon to look like a link. But
there’s a glaring flaw: when you click on the shortcut it doesn’t actually link
anywhere! This hack will make the shortcuts really work by hacking into the
undocumented shortcut files themselves.
Since links are not supported natively by the filesystem, Windows fakes it by
storing the shortcut metadata (path, icon, and other information) in a .lnk file.
When you click on the shortcut, the windows file manager reads the LNK file,
extracts the target file/directory path, and then opens a new window at the
real location. Your Java program can do the exact same thing using a custom
FileSystemView. The only tricky part is actually parsing the LNK files.
Microsoft has never documented the LNK file format, preferring native
Windows developers to use system APIs for all manipulation. Creative hack-
ers on the Web have reverse engineered most of the format, which fortu-
nately includes the parts you need to extract target filepaths.
Jesse Hager has compiled a great PDF describing the format
in detail. I used that document to write the code in this hack.
You can read the full document at http://www.i2s-lab.com/
Papers/The_Windows_Shortcut_File_Format.pdf.
The LNK files are binary data broken up into a header followed by a few
optional blocks of data. The format provides offsets that make parsing it
easy. The following code is the beginning of a LNK parser:
public class LnkParser {
public LnkParser(File f) throws Exception {
parse(f);
}
public 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);
Real Windows Shortcut Support #30
Chapter 4, File Choosers
|
159
HACK
}
fin.close( );
byte[] link = bout.toByteArray( );
The class defines one important method,
parse( )
, which accepts a
File
object representing the LNK file. The first step is to load the entire file into a
byte buffer.
I have seen some versions of this code use a more stream-
oriented approach with loops that read byte by byte. Since
LNK files are always pretty small (usually under 5k), I felt
the extra memory was worth it to allow cleaner code using
index offsets.
Next comes the header parsing:
// get the flags byte
byte flags = link[0x14];
// get the file attributes byte
final int file_atts_offset = 0x18;
byte fileatts = link[file_atts_offset];
byte is_dir_mask = (byte)0x10;
if((fileatts & is_dir_mask) > 0) {
is_dir = true;
} else {
is_dir = false;
}
The header has a lot of values in it, but we are only interested in the values
at byte
0x14 (the flags) and at 0x18 (the file attributes). Each of these values is
8 bits (a byte), where each bit represents something, such as whether the
LNK points to a file or a directory. Because they are always at the same
place, you can just jump directly to them in the array and store the values.
To access a bit you need a mask, which is a number that lets you hide all of
the bits in a value that you don’t want, leaving only the bit you do want. To
use it, you AND (a bitwise operation specified by the
& character) the value
and the mask together. Then you can just test if the final value is greater
than zero to see if that bit was set. If the 4th bit of the file attributes byte is
set (i.e., is equal to
1), then the target of this shortcut is a directory. The
fileatts and is_dir_mask are ANDed together. If the final value is greater
than zero, then the bit must have been set and the program sets the
is_dir
variable to true; otherwise, it sets is_dir to false.
// if the shell settings are present, skip them
final int shell_offset = 0x4c;
int shell_len = 0;
if((flags & 0x1) > 0) {

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required