Your question is really good. As of the date of your question the WindowsShortcut class you refer to only implements code to get the path to the file pointed to by a shortcut, but doesn't provide any further data inside a shortcut file. But it's open source, so let's extend it!
Let's do some research first
In the inofficial documentation by Jesse Hager we find this:
______________________________________________________________________________
| |
| **The flags** |
|______________________________________________________________________________|
| | | |
| Bit | Meaning when 1 | Meaning when 0 |
|_____|____________________________________|___________________________________|
| | | |
| 0 | The shell item id list is present. | The shell item id list is absent. |
| 1 | Points to a file or directory. | Points to something else. |
| 2 | Has a description string. | No description string. |
| 3 | Has a relative path string. | No relative path. |
| 4 | Has a working directory. | No working directory. |
| 5 | Has command line arguments. | No command line arguments. |
| 6 | Has a custom icon. | Has the default icon. |
|_____|____________________________________|___________________________________|
So we know that we can check the flags byte
for the existence of these additional strings. And we already have access to the flags byte
prepared in our WindowsShortcut
class.
Now we only need to know where those strings are stored in the shortcut file. In the inofficial documentation we also find this structure:
File header
Shell item ID list
Item 1
Item 2
etc..
File locator info
Local path
Network path
Description string
Relative path string
Working directory string
Command line string
Icon filename string
Extra stuff
So the strings we are interested in come directly after the File locator info
block. Which is neat, because the existing WindowsShortcut
class already parses the File locator info
to get the file path.
The docs also say that each string consists of a length given as unsigned short
and then ASCII characters. However, at least under Windows10, I encountered UTF-16 strings and implemented my code accordingly.
Let's implement!
We can simply add a few more lines at the end of the parseLink
method.
First we get the offset directly after the File locator info
block and call it next_string_start
, as it now points to the first additional string:
final int file_location_size = bytesToDword(link, file_start);
int next_string_start = file_start + file_location_size;
We then check the flags for each of the strings in order, and if it exists, we parse it:
final byte has_description = (byte)0b00000100;
final byte has_relative_path = (byte)0b00001000;
final byte has_working_directory = (byte)0b00010000;
final byte has_command_line_arguments = (byte)0b00100000;
// if description is present, parse it
if ((flags & has_description) > 0) {
final int string_len = bytesToWord(link, next_string_start) * 2; // times 2 because UTF-16
description = getUTF16String(link, next_string_start + 2, string_len);
next_string_start = next_string_start + string_len + 2;
}
// if relative path is present, parse it
if ((flags & has_relative_path) > 0) {
final int string_len = bytesToWord(link, next_string_start) * 2; // times 2 because UTF-16
relative_path = getUTF16String(link, next_string_start + 2, string_len);
next_string_start = next_string_start + string_len + 2;
}
// if working directory is present, parse it
if ((flags & has_working_directory) > 0) {
final int string_len = bytesToWord(link, next_string_start) * 2; // times 2 because UTF-16
working_directory = getUTF16String(link, next_string_start + 2, string_len);
next_string_start = next_string_start + string_len + 2;
}
// if command line arguments are present, parse them
if ((flags & has_command_line_arguments) > 0) {
final int string_len = bytesToWord(link, next_string_start) * 2; // times 2 because UTF-16
command_line_arguments = getUTF16String(link, next_string_start + 2, string_len);
next_string_start = next_string_start + string_len + 2;
}
The getUTF16String
method is simply:
private static String getUTF16String(final byte[] bytes, final int off, final int len) {
return new String(bytes, off, len, StandardCharsets.UTF_16LE);
}
And finally we need members and getters for those new Strings:
private String description;
private String relative_path;
private String working_directory;
private String command_line_arguments;
public String getDescription() {
return description;
}
public String getRelativePath() {
return relative_path;
}
public String getWorkingDirectory() {
return working_directory;
}
public String getCommandLineArguments() {
return command_line_arguments;
}
I tested this under Windows 10 and it worked like a charm.
I made a pull request with my changes on the original repo, until then you can also find the complete code here.