Microsoft .NET and Java are two powerful frameworks that provide a rich set of classes. While either of these frameworks can solve many of the same problems, each has their advantages and disadvantages. When you have existing, tested components, you may find it beneficial to use
both .NET and Java in your application, perhaps to support or extend legacy applications that have already been written in Java but adding value using the .NET Framework.
By using the Component Object Model (COM) interoperability services provided by the .NET Framework and the Java Native Interfaces (JNI) present in Java, you can host .NET controls in your Java application.
In this article, you'll build and host a control built with Microsoft C# in a Java application.
Figure 1 shows the completed control hosted in a Java app.
Designing the .NET Control
Designing a .NET control intended for hosting in a Java application isn't much different than designing any other .NET control. You start by inheriting from System.Windows.Forms.Control or a derivative class such as System.Windows.Forms.UserControl. Add your child controls and logic to the control as you normally would, but design your control so that it is not too dependent on its parent propertiesthe control should encapsulate as much as possible. When you're done, you need to expose the control as a COM control to use it from Java.
Bear in mind that because you're exposing the control as a COM control, you should observe common COM practices, by:
- Versioning your class interfaces
- Keeping your methods and properties in the original order when using interfaces inheriting from IUnknown
- Adding dispatch identifiers to interfaces inheriting from IDispatch.
You should avoid using auto-generated class interfaces even though the .NET Framework provides such support. Using auto-generated class interfaces is not recommended by Microsoft; it could lead to problems for clients using your COM components because methods may be ordered differently in the generated interface. That doesn't cause a problem for .NET clients, but it can for COM clients. Look at the topic
Qualifying .NET Types for Interoperation to see more details about the problems that could arise from using auto-generated class interfaces..
Begin by determining which properties and methods you want your .NET control to expose. The provided sample application merely prompts for a name and birthday. It uses two controls: a
TextBox control and a
DateTimePicker control, and exposes their contents via
Username and
Birthday properties (see
Figure 2).
 | |
| Figure 2. The .NET Control: This control contains a TextBox so users can enter their name, and a DateTimePicker control, for selecting their birth date. |
Because this particular control should also be resizable and should inherit the color of the Java application container, it also exposes a few properties inherited from the
Control class, such as
BackColor,
ForeColor,
Height,
Left,
Top, and
Width. The
Control class already implements these properties, so you don't need to define them in your class.
You should also add a globally-unique identifier (GUID) to this interface. After creating it, you should never change the GUID for this version of the class interface. COM uses the GUID as part of the interface contract. If you later decide to add new methods or properties to expose on this interface, create a new version (perhaps named
INETControl2) that inherits from the existing interface, change the GUID, and add only the new properties and methods. You may change the implementation of the existing methods and properties.
Here's the declaration for the sample interface,
INETControl:
[Guid("2D570F11-4BD8-40e7-BF14-38772063AAF0")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface INETControl
{
// See sample code for exposed properties.
}
This interface is declared as a dual interface, which means that it supports both
VTBL binding and late binding.
In your .NET class, the
INETControl interface should be the first interface implemented in the class declaration:
[Guid("478176F4-5105-435c-8EBC-D4CB90B7B1C7")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("DevX.NETControl")]
public class NETControl : System.Windows.Forms.UserControl, INETControl
{
// See sample code for implementation.
}
Note that the class is attributed with a GUID as well. This GUID is known as a Class ID, or CLSID. This CLSID should typically not change over the course of a control's lifecycle because both old and new code may be dependent on the class. Older clients are supported because they use the older interfaces that should still be implemented by the new or modified class. Newer clients can use the class via the old and/or new interfaces.
The class also has a program identifier, or ProgID, which is "DevX.NETControl". The ProdID lets code refer to the COM component with a short, human-readable string. The .NET Framework automatically uses this string as the version-independent ProgID. It creates a version-dependent ProgId by combining the major and minor version numbers from the
AssemblyVersionAttribute.
Finally, add an assembly-level
GuidAttribute to your
AssemblyInfo.cs file. This will serve as the type library, or typelib, identifier. Automation clients can refer to this typelib using the same typelib ID.
Compile the project and register it. If you use Microsoft Visual Studio .NET 2003 to compile the NETControl sample project, the compile process registers the component automatically. If you compile the source files from the command line, you can register the classes using the
regasm.exe that is included with the .NET Framework and Visual Studio .NET.. Here's an example:
regasm.exe /codebase NETControl.dll
If you prefer, you can also use
gacutil.exe to install the assembly in the Global Assembly Cache (GAC), or use
ngen.exe to pre-compile the assembly to native code (JIT) and add it to the GAC. You should not specify the
/codebase option when using
regasm.exe if you choose this alternative.