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:
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.
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.
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.
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:
java.c
java.h
java_md.c
java_md.h
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.
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.
<project name="myapp" default="deploy">
<target name="set-properties">
<property name="deploy"
location="deploy"/>
<property name="dest"
location="dest"/>
<property name="src"
location="src"/>
<property name="jar.name"
value="myapp.jar"/>
<property name="vctk"
value="C:\Program Files\Microsoft Visual C++ Toolkit 2003"/>
<property name="ms.platform.sdk"
value="C:\Program Files\Microsoft SDK"/>
<property name="java.sdk"
value="C:\j2sdk1.4.2_04"/>
<property name="executable.name"
value="myapp.exe"/>
<property name="link.opts"
value="/Fe${executable.name} /MT /link advapi32.lib user32.lib jvm.lib"/>
<!-- The quotes must be double-escaped -->
<property name="java.args"
value="-DJAVA_ARGS="{ \"com.ociweb.jnb.july2004.myapp.MyMainClass\" }""/>
<property name="app.classpath"
value="-DAPP_CLASSPATH="{ \"\\lib\\tools.jar\", \"\\lib\\${jar.name}\" }""/>
<property name="jdk.minor.version"
value="-DJDK_MINOR_VERSION=\"4\""/>
<property name="jdk.major.version"
value="-DJDK_MAJOR_VERSION=\"1\""/>
<path id="compile-classpath"/>
</target>
<target name="setup" depends="set-properties">
<mkdir dir="${dest}/classes"/>
<mkdir dir="${dest}/lib"/>
</target>
<target name="compile" depends="setup">
<javac srcdir="${src}" destdir="${dest}/classes">
<classpath refid="compile-classpath"/>
</javac>
</target>
<target name="compile-launcher" depends="set-properties">
<exec executable="${vctk}\bin\cl" dir="${src}\launcher">
<env key="INCLUDE"
value="${vctk}\include;${ms.platform.sdk}\include;${java.sdk}\include;${java.sdk}\include\win32"/>
<env key="LIB"
value="${vctk}\lib;${ms.platform.sdk}\lib;${java.sdk}\lib"/>
<env key="CL"
value="-DWIN32 -DJAVAW ${jdk.major.version} ${jdk.minor.version} ${java.args} ${app.classpath} ${link.opts}"/>
<arg value="java.c"/>
<arg value="java_md.c"/>
<arg value="myapp.res"/>
</exec>
</target>
<target name="jar" depends="compile">
<jar destfile="${dest}/lib/${jar.name}" basedir="${dest}/classes"/>
</target>
<target name="deploy" depends="jar, compile-launcher">
<copy file="${src}\launcher\${executable.name}" todir="${deploy}\jre\bin"/>
<copy file="${dest}\lib\${jar.name}" todir="${deploy}\jre\lib"/>
</target>
</project>
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:
WIN32
JAVAW
javaw. This means that it is a Windows program, not a console
program. In other words, it will launch with WinMain() instead of main(). One
consequence of this is that you will lose System.out and System.err output, which are artifacts of console-based
applications. It is a boolean preprocessor variable, and should not have an explicit value. Omit this variable
and you will get a java.exe-style console application.
JDK_MAJOR_VERSION
JDK_MINOR_VERSION
JAVA_ARGS
main() method is located in
com.foo.bar.myapp, then that should be the value JAVA_ARGS is set to.
APP_CLASSPATH
\lib\tools.jar (included in the JRE distribution) and any other jar files
required.
resourcesJuly2004.zip
file to a directory (referred to as the "project" directory later) on your system.
c:\windows\Microsoft.NET\Framework\v1.1.4322.
Add this directory to your path.
project\src\launcher
directory.
project\deploy directory.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
.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.
myapp.exe file in the project\deploy\jre\bin
directory and double-clicking.
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.
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.
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.