Retrieving short 8.3 filenames in Windows using Java

In some rare occasions you might want to find out the short filename of a file in Windows (for example to run legacy code). After looking around on the net, I couldn't find a java function to do it. And no, messing around with the output of dir /x is not a pretty solution.

Microsoft provides a Windows API function called GetShortPathName, which you can call from java using JNI. Too lazy actually messing around with JNI I found a package called NativeCall which allows calling a Windows API function from java without using JNI.

Here's the code for retrieving the short (8.3) filename given a long filename. Have fun with it. Don't forget to download the nativecall package and to add it to your classpath. For the lazy people I've made a zip containing this class, the two jars and how to execute it.

import com.eaio.nativecall.*;

public class EightPointThree {

    public static void main(String[] args) {
        String usage = "java " + EightPointThree.class.getName()
                + " longfilename";
        if (args.length != 1) {
            System.err.println(usage);
            System.exit(1);
        } else {
            System.out.println(getEightPointThree(args[0]));
        }
    }

    /**
     * returns the short filename (8.3) for a file in Windows
     * 
     * @param longFileName 
     * @return a string with the short filename, or null if an error occurred or the
     *         file does not exist.
     */
    public static String getEightPointThree(String longFileName) {
        try {
            // the result
            String shortName = null;
            NativeCall.init();
            IntCall ic = new IntCall("GetShortPathNameW");
            try {
                // size of result is at most the long file name (times 2 for the
                // number
                // of bytes)
                Struct resultStruct = new Struct(longFileName.length() * 2);
                int iResultCode = ic.executeCall(new Object[] { longFileName,
                        resultStruct, new Integer(longFileName.length()) });
                if (iResultCode > 0) {
                    // iResultCode is length
                    byte[] data = resultStruct.getData();
                    int length = data.length > (iResultCode * 2) ? (iResultCode * 2)
                            : data.length;
                    byte[] data2 = new byte[length];
                    System.arraycopy(data, 0, data2, 0, length);

                    // structResult contains letter space letter space letter
                    String structResult = new String(data2);
                    // remove spaces in between
                    StringBuffer result = new StringBuffer();
                    for (int i = 0; i < structResult.length(); i += 2)
                        result.append(structResult.charAt(i));
                    shortName = result.toString().trim();
                    if (shortName.equals(""))
                        shortName = longFileName;
                }
            } finally {
                ic.destroy();
            }
            return shortName;
        } catch (Exception e) {
            System.err.println("Exception of type " + e.getClass().getName()
                    + ":\n" + e.getMessage());
            e.printStackTrace();
        }
        return null;
    }
}

Note that your file system must be configured to be backwards compatible with good old DOS. Windows (by default) generates a 8.3 filename for a newly created files. By setting the registry key HKEY_LOCAL_MACHINE/SYSTEM\CurrentControlSet\Control\FileSystem\NtfsDisable8dot3NameCreation however (0 to enable 8.3 name creation, or to 1 to disable), no 8.3 filenames are created anymore!

Related (just to help you find this page):