Filetypes For Windows
Windows doesn't have a clean text-file way of setting filetype
associations. Instead, we will have to muck with the registry, which
isn't the easiest thing to do. There are no standard APIs to help, but
there are some options. We will start with an open source JNI library
called JNIRegistry,
created by Tim Endres and ICE Engineering. Unfortunately, it's not
terribly well documented, and again, Windows registry hacking isn't easy
for programmers used to the very open and structured Java world. Being a
Java programmer, I had no idea where to start. After a few minutes of
Googling, however, I did find some code in the jtorrent CVS tree on Source Forge that uses JNIRegistry to do exactly the same thing we want to do:
associate our program with a file extension.
The Windows registry is essentially a tree of nested keys. To view,
edit, or delete keys you have to find them and then manipulate them. The
keys we want all start in the top level key of HKCR. This
is the HKEY_CLASSES_ROOT portion of the registry database, which contains
"file extensions associations, CLSIDs file and object types." (More information can be found here.)
String topLevelKey = "HKCR";
RegistryKey topKey =
Registry.getTopLevelKey(topLevelKey);
Now we want to create a new key to hold our new values:
RegistryKey localKey =
topKey.createSubKey(key, value,
RegistryKey.ACCESS_WRITE);
The value itself has to be held in another structure, the
RegStringValue:
RegStringValue val =
new RegStringValue(localKey, valueName, value);
Now we can set the value and save the key:
localKey.setValue(val);
localKey.flushKey();
I've wrapped all of this up in a static function called setKey, which
takes a key, value name, and value. The key is the actual name of the key we want to set. The value
name specifies which value in this key we want to set. In our
case, we use "" for the default value name. The actual value is
the string we are setting. (It is also possible to put numbers
and binary data into the registry, but that is beyond the scope
of this article.) A full list of the available Windows keys can be found
here.
Now that we have the ability to set a key, what keys do we want to set? First we need to tell Windows
that the file extension .mchat is associated with our application:
setKey(".mchat","","MadChatter");
Then we need to set the description of MadChatter files:
setKey("MadChatter","","MadChatter logfile");
Now Windows recognizes .mchat files, but it doesn't know what
to do with them. Let's tell it we can open them and what to open them
with:
String command = exeDir() + "\\MadChatter.exe \"%1\"";
setKey("MadChatter\\shell","","open")
setKey("MadChatter\\shell\\open\command","",command)
The first setKey() says we know how to open files and the second
gives the command-line program that will do it. The command is three
parts: the location of our .exe, the .exe
itself, and the place where the path of the requested file will be
stored. The first part, exeDir(), is a utility function
that retrieves the jexepack.exe property. JexePack, our
Windows packaging program, stores the directory of our .exe here. If
jexepack.exe is missing, then exeDir() will
return the current working directory instead.
I've wrapped all of this code into a single static utility function,
RegTest.setAppString(), which takes the filename extension, the
command string, the name of the program, and the description of the
filetype. It will be called in the main function at program startup, which
ensures that the keys are always set to the right values.
public static final String EXTENSION = "mchat";
[...]
public void main(String[] args) {
[...]
// if on windows then run the registry code
if(!xp.isMac()) {
String command = exeDir() + "\\MadChatter.exe \"%1\"";
RegTest.setAppString("."+Startup.EXTENSION,command,
"madchatter","MadChatter logfile");
}
That's all we need to tell Windows about our app without using an
installer. This strategy has a limitation, however. If the user ever
moves our program, the filetype association will be broken until our
program is run again. This is really a limitation of the Windows
filesystem, because unlike the Mac's HFS, there is no path-independent
way of finding a file on the hard drive. Usually, however, people just
put the application in one place and never move it again, so we should
be pretty safe.
To run it, we have to load up the JNIRegistry libraries. We can add
the .jar file (registry.jar) to our build.xml
easily enough, but dealing with the .dll is trickier. It
turns out that native libraries are loaded via the system path, which
we'd rather not mess with. However, the system path already includes the
current directory, so if we just put the .dlls there we are
all set. So we copy the .dll from the lib dir to the
working dir in our build file.
<target name="run" depends="compile">
<copy file="lib/ICE_JNIRegistry.dll" todir="."/>
<java fork="true"
classpath=
"${classes};lib/joshy-common.jar;lib/registry.jar"
classname=
"org.joshy.oreilly.swingnative.Startup"/>
</target>
Now this is fine for testing from the command line, but what about
our generated .exe? Well, JexePack has already thought of
that. As long as we put the .dll and extra .jars in the build
set and add the /jni option, JexePack will take care of the
rest. So we add few more lines to our jexepack.ini file:
/jar:registry.jar
/jni
ICE_JNIRegistry.dll
Run ant win-dist and we are ready to go. File Explorer
recognizes our application type and will load it when the user double
clicks on .mchat files, as illustrated by Figure 5:

Figure 5. Windows filetype associations
Conclusion
Thank you for joining me for the second part of my series on making
Swing applications feel native. Last time, we worked on the menus and
added alerts. This time, we bundled our program into real executables and
added filetype associations. Mad Chatter is starting to feel like a
real application now. I hope you will join me for the final article,
where we will create custom icons, a splash screen, and a few more bits
of polish to make The Mad Chatter really shine.
Editor's Note: Downloadable sample code for this article and others
in the series will be provided at the conclusion of the series. You can also participate in the Mad Chatter project, part of java.net's Java Desktop community.
Joshua Marinacci first tried Java in 1995 at the request of his favorite TA and has never looked back.