Displaying .NET Windows Forms from Java SWING
Microsoft® .NET Framework 1.1
Microsoft Visual Studio® .NET 2003
Intrinsyc Ja.NET 1.5 (build 1.5.1287 or higher)
Summary: Learn how an application in Java SWING can invoke and display a Windows Form, pass parameters to the form and wait for a return value when the form is closed. This type of technique shows how organizations can introduce new forms and controls based on Windows Forms into an existing Java SWING environment and helping them to rapidly deploy .NET applications without having to rip out existing infrastructure or wait until new replacement applications are written.(11 printed pages)
Download the SwingInterop.msi code sample
When I work with organizations that have existing applications in Java, it is often the case that this investment is not only bound to the server side, but can also include the client. These client-side applications are typically developed in either AWT (Abstract Window Toolkit) or Java SWING.
For desktops that are running Microsoft® Windows®, many of these organizations are looking at how Microsoft .NET technology can be used on the client to either replace or extend the existing Java technology. The client-side equivalent of AWT and SWING in Microsoft .NET is referred to as Windows Forms.
This article will show a sample that uses a messaging layer between an existing Java SWING™ application and Windows Forms. Using a third-party toolkit called Ja.NET from Intrinsyc, you'll discover how an application in Java SWING can invoke and display a Windows Form, pass parameters to the form and wait for a return value when the form is closed. This type of technique shows how organizations can introduce new forms and controls based on Windows Forms into an existing Java SWING environment—helping them to rapidly deploy .NET applications without having to rip out existing infrastructure or wait until new replacement applications are written.
- Microsoft .NET Framework 1.1
- Compliant Java 2 Standard Edition (J2SE) SDK, version 1.4 or higher
- A basic working knowledge of both the .NET Framework 1.1 and J2SE SDK 1.4 or higher is required. It is assumed that you understand the principles behind writing and compiling an application for both platforms, and that you have a good understanding of the C# and Java language syntax.
- An understanding of how .NET Remoting is not required for installing and running the sample, but is useful for understanding how the sample works. Additional information can be found at An Introduction to Microsoft .NET Remoting Framework.
- Microsoft Visual Studio .NET 2003
Microsoft Visual Studio® .NET 2003 is recommended but not required for building the solution file included in the sample.
- Ja.NET 1.5 from Intrinsyc
- Ja.NET is required to provide the support for .NET Remoting in the Java environment. Download and install the Ja.NET software, accepting the default settings. You will be sent a 60-day evaluation license with the software. This evaluation license (or another valid license) is required for running the sample.
To show how this approach works, you will first install, configure and look at the sample code. This demonstrates a sample Java SWING application invoking and displaying two forms, both based on Windows Forms in .NET.
After you've looked at the sample, we'll discuss how the sample works and can be extended.
The sample code is installed and configured in stages. These are outlined as follows:
Installing and Configuring Intrinsyc's Ja.NET
First, download and run the SwingInterop.MSI to extract and install the sample code. By default, this will be installed to a C:\SwingInterop directory—but you are free to choose your own location. If you choose to change the location, be sure to replace all occurrences of C:\SwingInterop in this article with your new location.
After downloading and installing Ja.NET 1.5 from Intrinsyc (see pre-requisites), you need to configure the Ja.NET libraries so that they are available to the sample code. To do this, copy the following files from C:\Program Files\Intrinsyc Ja.NET\lib (the default library directory for Ja.NET) and place them in the C:\SwingInterop\Java\lib directory:
Also, place the Ja.NET license file (janet_license.xml) you have received via Email in the C:\SwingInterop\Java\lib directory. This is important to make sure that the application has access to the correct license when running.
With the files copied, it is now time to configure the Ja.NET proxy. Open a command prompt, and navigate to the C:\SWINGInterop\Java\SampleClient directory. From here, run the Janetor.bat batch file.
If this does not run, check to make sure that the Java.exe executable is available on your System Path.
Figure 1. Janetor, the Ja.NET Configuration Tool
Within Janetor (the Ja.NET Configuration Tool), ensure that the default remote object is configured for tcp://localhost:5656 (you can change this once the sample is working). It is also advisable to check that the licensing entry matches your license.
Close Janetor and save the settings when prompted. The Ja.NET setup and configuration is now complete.
Building the Sample Java SWING Application
To build the Java SWING application, navigate to the C:\SWINGInterop\Java\SampleClient directory and run the build.bat file. This will make the required calls to the J2SE Java compiler to build the application.
Again, if for some reason this fails, check your System Path and CLASSPATH to ensure settings include the required Java executables.
Building the .NET Windows Forms Application
If using Visual Studio .NET 2003, you can open and compile the SwingInterop.sln file, found in C:\SWINGInterop\dotNET. Within the IDE you will notice that the solution contains sub-projects:
SampleForms—a set of two sample Windows Forms
SwingInteropAdmin—the administration utility used to stop the engine and to monitor events
SwingInteropEngineLibrary—contains the components that are exposed to the Java SWING application
SwingInteropInvoker—the project responsible for starting the engine
Part of the compile process will also install the assembly containing the two sample forms into the Global Assembly Cache (GAC).
If you wish to instead compile from the command prompt, run the build.bat file from the C:\SWINGInterop\dotNET directory.
If for some reason either build procedure failures, check your System Path—the C# compiler (CSC.EXE) and GAC Registration Tool (GACUTIL.EXE) must be present in your path for this to work. By default, these can be found in the .NET Framework directory.
Starting the Engine Process
The engine (this is one of the projects that was just built) is responsible for listening for incoming messages from the Java SWING application and firing up Windows Forms on request.
To start this engine listening for messages, run StartEngine.bat from the C:\SWINGInterop\dotNET directory.
This executable runs as a background process (such that it could be run on startup in a production environment without displaying anything to the user). You can use the Task Manager in Windows Explorer to look at the processes and ensure that the SwingInteropEngine.exe process is running.
Figure 2. Windows Task Manager
Run the Java Sample Client
After the SWINGInteropEngine process is running, navigate to the C:\SWINGInterop\Java\SampleClient directory and run StartClient.bat to run the test SWING client.
Upon successfully loading the Java SWING client, you will see the main form:
Figure 3. A test message displayed in the main form
This is a form written using Java SWING. To demonstrate the purpose of this sample— how this form can invoke a Windows Form written in .NET—enter a message into the text field and click on the Open Form1 button.
When you click on the button, the Windows Form will be displayed:
Figure 4. After clicking on the button, the Windows Form is displayed.
You can see that the text message has been passed to the Windows Forms as a parameter from the calling client.
To test the reverse—returning a field from the Windows Form back to the Java SWING client—edit the text in this new Windows Form (maybe append some more text in the text field) and click on the Close button.
After the form has closed, the Java SWING application reports the modified text string:
Figure 5. Reporting the modified text string
What we have seen so far is how the SWING client has the ability to invoke a Windows Form via the running SWINGInteropEngine process, pass parameters to the form and obtain a return result from the form.
You may have noticed however that when the Windows Form was displayed, it was running in a modal (or dialog) style. This means that the underlying Java SWING client application was 'frozen' until the new Windows Form had been closed. This is due to the Java client waiting for the return result from the Windows Form.
For the purposes of this sample, if the Java SWING client is not interested in any return result from the Windows Form, it can open the Windows Forms in a non-modal view. The second button on the in the sample application demonstrates this.
Return back to the Java SWING application, and click on the Open Form2 button. The following Windows Form will open:
Figure 6. Another Sample Form window
This is another example of a Windows Form that can be launched from an existing Java SWING application, however as we mentioned, the difference here is that this form is non-modal. The Java SWING application is not waiting for a response. It is therefore possible to return to the calling application and re-open multiple instances of this form.
The sample code starts a new thread in the SWING Interop Engine process for each new instance of the form that is opened.
Stopping the Engine Process
To stop the Engine cleanly, navigate to the C:\SWINGInterop\dotNET and run the following command:
This sends a terminate signal to the forms engine that is running in the background.
This administration utility (admin.exe) can also be used to trace output from the engine with the following switch:
This will display each event that is generated by the SWINGInteropEngine process to the console. In addition, all events are logged in the Windows Event Log.
Changing the Default Ports
To change the TCP port that is used for communication between the Java SWING application and Windows Forms, ensure that the engine is stopped. Restart the engine with a /p parameter indicating the port to use instead of the default (5656):
Navigate to the C:\SWINGInterop\Java\SampleClient directory and re-run Janetor.bat. Adjust the remote object's URL to reflect the new port. For example, tcp://localhost:8888
To run the admin utility to either kill or trace the engine, again use the /p parameter.
admin /p:8888 /kill
To now, we have seen how the sample demonstrates how an existing Java SWING application can invoke and display multiple Windows Forms, pass parameters, expect return types and specify modality. How does this work under the covers?
Figure 7. Overview of how the sample demonstrates how an existing Java SWING application can invoke and display multiple Windows Forms and so on
The Java SWING application uses a proxy class (which contains an API and the necessary Ja.NET proxy files) to construct a message to the .NET SWINGInteropEngine process running on the client.
Once the message is received, the engine performs a lookup in the Global Assembly Cache (GAC) to try and load the appropriate Windows Form that matches the request. Part of this load process also includes setting of parameters to control initial visual elements on the form. If the Windows Form cannot be found in the Global Assembly Cache, the working directory of the SWINGInteropEngine process is used instead.
In our sample, the first form exposes a public property called Message. The Java SWING application can pass a name/value pair containing the name of this property and a value that this property should be set to when the form is loaded. Multiple name/value pairs can be sent from the calling application via a hashtable.
In addition to setting of properties, the Java SWING application can nominate an existing public property on the form as a return value when the form closes. When the form is closed, the engine reflects against the form to find out the values of public properties that were set during the lifetime of the form. In the sample, the Java SWING application can ask for one of these values to be returned as part of the .NET Remoting call. This allows the Windows form to set some values, and for these to be returned to the calling application.
The engine logs calls and new form requests to the event log. The admin utility can be used to monitor these entries in the event log and display them.
The Sample Windows Forms API
The Java SWING application uses a sample API to make the calls to invoke the Windows Forms (it constructs a message, which is sent to the SWINGInteropEngine process), and to abstract the underlying .NET Remoting calls.
This API is currently in a package called com.microsoft.samples.windowsforms.
The main methods exposed by this API are as follows:
This opens a channel to the running SWINGInteropEngine process.
WindowsForms.SetAssemblyDetails(String assemblyName, String namespace);
If all of the forms you are opening are stored in one assembly, this is a convenient way of pre-setting the assembly name and namespace such that simpler DisplayForm methods can be used.
WindowsForm.DisplayForm(String formName, Boolean modal);
This will display the form with the form name of formName. Modality is controlled through the Boolean, modal.
WindowsForm.DisplayForm(String formName, Hashtable props, String returnProperty, Boolean modal);
This will display the form with the given form name. Any keys in the hashtable that match public properties on the form will be set when the form is loaded. The value of any public property in the form that matches the name of returnProperty will be returned by this method when the form is closed. Modality is controlled through the Boolean, modal.
WindowsForm.DisplayForm("Form1", props, "Message", true);
This will display Form1 as a modal form, passing a hashtable of properties (props) to the form. Any keys in the hashtable that match public properties on the form will be set. The value of the public property (Message) will be returned when the form is closed. The form will be modal (Note: As mentioned, this is required for returned property values from the Windows form).
As with any sample, there are of course limitations and some room for improvement. Through writing this sample, the following could be considered:
Properties and Return Types are String Types
Currently, passed parameters and return types from Windows Forms can only be strings. Given mappings of primitive types available in Intrinsyc's Ja.NET, it is possible to extend this to include other primitives such as integers, floats and so forth.
For more complex types, we would need to think about some kind of serialization—formatting the type into a document or style that can be understood by both sides.
Bringing the Windows Forms to the Foreground
When the form loads, the user needs to currently set the form to be "on top" and "in focus" to avoid it appearing underneath the Java SWING form.
As the form is being loaded by a third party (in this case, the SWINGInteropEngine process), on occasions, the new form can appear either underneath other windows, or inactive. Changing the form properties (for example, the TopMost property) can help resolve this.
Using a TCP Channel for IPC Calls
Just because we are using .NET Remoting, do we need to use a TCP channel for inter-process communication?
When we look at performance of the sample, it is reasonable to argue that a TCP channel between two processes on the same machine could be an overhead. For single instance forms and normal user interaction, this is unlikely to be an issue (as a delay of a few milliseconds to open a new form probably won't annoy that many users!), but the argument is valid if the forms were being opened as part of a more automated approach.
To help address this, Intrinsyc are working on a shared memory channel for the next release of their Ja.NET product. This will prevent the call having to rely on the entirety of the network stack and should make for a more optimized solution.
This sample has demonstrated how .NET Remoting can be used on the same machine to allow Windows Forms to be invoked and displayed from an existing client application that uses Java SWING.
For organizations that are looking to migrate client applications from Java SWING to Windows Forms, this can be a valuable approach to immediately start deploying Windows Forms without the potential expense of waiting until the initial application is completely rewritten.
Note that since publication, the sample code presented in this article has also been made available for use with JNBridge Pro, a .NET to Java bridging solution. You can download this sample code and documentation from http://www.jnbridge.com/demos.htm.