Improve Java Apps on Windows with a Native Launcher

Improve Java Apps on Windows with a Native Launcher

By Dan Lewis, OCI Senior Software Engineer

July 2004


Introduction

What do IntelliJ Idea, Eclipse, and SmartCVS all have in common? If you said Java, you would be correct. But if you said that they all include a native launcher, you'd also be correct! They all include a native launcher for Windows. Why? The reasons fall into three categories: Integration, Deployment and Performance:

Integration

Two common problems with typical Java applications are avoided by using a native launcher. The first is that annoying command prompt windows pop up temporarily. This is indicative of a batch file that is calling one of the default Java launchers (java.exe or javaw.exe). A native launcher avoids this problem by providing a Windows .exe file that the end user can run. This has an additional benefit of hiding certain details about how your program is launched, such as the CLASSPATH and the "main()" class. The second common problem is that a custom icon is not provided. Again, this is avoided by using a native launcher and including an icon in the .exe.

Deployment

Options for deployment range from fancy automated installers, to executable jar files, to "copy and run". Regardless of the level of sophistication you choose, you'll be faced with another question: Do I use the JVM (Java Virtual Machine) that is already installed on the machine? This question will be decided by size of deployment, version sensitivity of your application, and licensing considerations. The technique described in this article requires that the application include a specific JVM with your application.

Another part of deployment is troubleshooting. When you are looking for the process, you don't have to guess which java.exe or javaw.exe is the process for your app. Your .exe name will appear in the list as the process name.

Performance

This is the weakest of the three arguments, but worth mentioning for those of you that are obsessed with performance (like the author). A native launcher is faster than a batch-file launcher because interpreting batch files is slower than running native C code. The amount of time savings should be negligible, but given the slow startup time of the JVM, shaving a few milliseconds off the application start might be worth it to you.

Performance compared to an executable jar file distribution such as JEdit should also be better because startup parameters are compiled into the launcher vs. embedded in a jar file in the manifest. Again, the performance difference is minor, but it might still be worth it to you to eliminate every extra bit of overhead.

Launcher Source Code

It's a little-known fact that the source code to java.exe and javaw.exe are included in your Sun SDK distribution. Of course, this is not the complete source code to the JVM, but the source code for the launcher. In your SDK installation directory (C:\j2sdk1.4.2_04 on my machine), there is a file named src.zip. Unzip it and you will find several directories, including the launcher directory. I like to unzip it into a directory called src under my JDK home and point my Java IDE to it for debugging purposes. Let's have a look at the files in the launcher subdirectory:

The good news is that you do not have to edit any of this code to make your own custom launcher. Furthermore, it's probably a good idea not to. Sun may elect to change the launcher code at any time (and has in the past), so you would be forced to merge your changes and re-test.

The Ant Build Script

I provided this script so that you can easily build your own launcher without spending hours trying to get the launcher to build. The default target is deploy, and will compile MyApp.java (an example Java program I included in the resourcesJuly2004.zip file), jar it up, compile the launcher and copy both into the deploy directory.

  1. <project name="myapp" default="deploy">
  2. <target name="set-properties">
  3. <property name="deploy"
  4. location="deploy"></property>
  5. <property name="dest"
  6. location="dest"></property>
  7. <property name="src"
  8. location="src"></property>
  9. <property name="jar.name"
  10. value="myapp.jar"></property>
  11. <property name="vctk"
  12. value="C:\Program Files\Microsoft Visual C++ Toolkit 2003"></property>
  13. <property name="ms.platform.sdk"
  14. value="C:\Program Files\Microsoft SDK"></property>
  15. <property name="java.sdk"
  16. value="C:\j2sdk1.4.2_04"></property>
  17. <property name="executable.name"
  18. value="myapp.exe"></property>
  19. <property name="link.opts"
  20. value="/Fe${executable.name} /MT /link advapi32.lib user32.lib jvm.lib"></property>
  21. <!-- The quotes must be double-escaped -->
  22. <property name="java.args"
  23. value="-DJAVA_ARGS="{ \"com.ociweb.jnb.july2004.myapp.MyMainClass\" }""></property>
  24. <property name="app.classpath"
  25. value="-DAPP_CLASSPATH="{ \"\\lib\\tools.jar\", \"\\lib\\${jar.name}\" }""></property>
  26. <property name="jdk.minor.version"
  27. value="-DJDK_MINOR_VERSION=\"4\""></property>
  28. <property name="jdk.major.version"
  29. value="-DJDK_MAJOR_VERSION=\"1\""></property>
  30. <path id="compile-classpath"></path>
  31. </target>
  32. <target name="setup" depends="set-properties">
  33. <mkdir dir="${dest}/classes"></mkdir>
  34. <mkdir dir="${dest}/lib"></mkdir>
  35. </target>
  36. <target name="compile" depends="setup">
  37. <javac srcdir="${src}" destdir="${dest}/classes">
  38. <classpath refid="compile-classpath"></classpath>
  39. </javac>
  40. </target>
  41. <target name="compile-launcher" depends="set-properties">
  42. <exec executable="${vctk}\bin\cl" dir="${src}\launcher">
  43. <env key="INCLUDE"
  44. value="${vctk}\include;${ms.platform.sdk}\include;${java.sdk}\include;${java.sdk}\include\win32"></env>
  45. <env key="LIB"
  46. value="${vctk}\lib;${ms.platform.sdk}\lib;${java.sdk}\lib"></env>
  47. <env key="CL"
  48. value="-DWIN32 -DJAVAW ${jdk.major.version} ${jdk.minor.version} ${java.args} ${app.classpath} ${link.opts}"></env>
  49. <arg value="java.c"></arg>
  50. <arg value="java_md.c"></arg>
  51. <arg value="myapp.res"></arg>
  52. </exec>
  53. </target>
  54. <target name="jar" depends="compile">
  55. <jar destfile="${dest}/lib/${jar.name}" basedir="${dest}/classes"></jar>
  56. </target>
  57. <target name="deploy" depends="jar, compile-launcher">
  58. <copy file="${src}\launcher\${executable.name}" todir="${deploy}\jre\bin"></copy>
  59. <copy file="${dest}\lib\${jar.name}" todir="${deploy}\jre\lib"></copy>
  60. </target>
  61. </project>

Configuring the Preprocessor, C Compiler, and Linker with Ant

Please note the compile-launcher target. This is responsible for configuring the preprocessor, the C compiler, and the linker. The preprocessor and C compiler are configured with the CL environment variable, in which preprocessor variables are specified. The preprocessor variables passed in (prefixed with "-D") are as follows:

Building your .exe file

  1. Unzip the resourcesJuly2004.zip file to a directory (referred to as the "project" directory later) on your system.
  2. Install Apache Ant 1.6.1.
  3. Install the Microsoft Visual C++ Toolkit on your system
  4. Install the Microsoft Platform SDK on your system
  5. Locate the file cvtres.exe on your system. On my system it is located in c:\windows\Microsoft.NET\Framework\v1.1.4322. Add this directory to your path.
  6. Copy the contents of src\launcher (found in the src.zip file located in your JDK home directory) into the project\src\launcher directory.
  7. Copy the jre directory of your JDK installation into the project\deploy directory.
  8. Edit the build.xml file, located in the project directory, and change the following properties:
    • vctk - set this to the directory where the Visual C++ Toolkit is installed
    • ms.platform.sdk - set this to the directory where the Microsoft Platform SDK is installed
    • java.sdk - set this to the directory where the Sun Java SDK is installed
  9. Use you favorite icon editor, or try the free Java applet at http://imageauthor.com to create an icon. Save the .ico file, and run the "rc" (resource compiler) utility that is included in the Platform SDK on the .rc script that I include in the project\src\launcher directory. The result will be a .res file that is compiled in with your app.
  10. Run the ant script.
  11. Test the launcher by navigating to the myapp.exe file in the project\deploy\jre\bin directory and double-clicking.

Packaging/Deployment

All that is left is to deploy the contents of the project\deploy\jre directory to the target system. Please note that I did not include either the JRE or the launcher source in the resourcesJuly2004.zip file to avoid licensing questions.

About the Visual C++ Toolkit 2003

Recent independent tests published in Dr. Dobbs Journal indicate that Visual C++ ranks highly in terms of performance and the highest in terms of ISO standards compliance. See the reference section for references to those articles. Of course, you can use any of the other free compilers available: GCC, Borland, Open Watcom, or MinGW to name a few.

Summary

Successful Java applications such as IntelliJ Idea, Eclipse and SmartCVS include native launchers. In fact, the desire to have a native launcher for Java applications has spawned a mini-industry of both commercial and open-source tools that generate native launchers, including exe4J (commercial), JEXECreator (commercial), Janel (open-source) and others. You can use one of these tools, or use the Ant script provided here and do this task yourself. However you do it, make the effort to build a native launcher for your Windows Java program. Your application will have a more polished look and leave an impression of quality with the user.

References

Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.