Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Hosting .NET Controls in Java : Page 3

Using JNI and some COM interop magic, you can host .NET Windows controls directly in your Java applications.


advertisement
Implementing the Native Functions
Implementing the native functions in your C/C++ source code typically depends on your .NET control and how you exposed it as a COM component. Two of the native functions—initialize and destroy—are required to initialize the component and attach it to the HWND of the Java control.

Create a new C/C++ project. You'll need to #include the jni.h header file, so make sure you include both the include and include/win32 directories from the JDK installation directory. Import the jni.h header and the JNIBridge.h header from this sample in a C source file.

To begin implementing the native functions, copy the declarations from the header file you generated into the source file. Add parameter names and curly braces to each, setting up each function for a definition. You'll define the initialize function first (see Listing 1 or the sample source for the complete implementation).

This initialize function gets the HWND from the Canvas. This window handle is provided by the JVM. After getting the HWND, execution is passed to a thread so that the method can return and allow the Java control to continuing initializing and handling events. The new thread creates the .NET control you exposed as a COM component and attaches it to the HWND. It then initializes properties of the control when necessary and starts the message loop to process messages and to keep the control running (see Listing 2 or the sample source for implementation).

The destroy function destroys the COM component window resources, decrements the reference count, and deletes the pointer. This makes sure that the .NET control is destroyed and memory is reclaimed (see Listing 3 or sample source for implementation).

Implementing the getUsername native function requires that you get a string from the .NET control and return it to the Java control:

JNIEXPORT jstring JNICALL Java_COMControl_getUsername (JNIEnv *env, jobject obj) { HRESULT hr = S_OK; CComBSTR username; jstring jstr; if (g_spControl != NULL) { username.Empty(); hr = g_spControl->get_Username(&username); if (SUCCEEDED(hr)) { jstr = env->NewStringUTF(CW2A(username.m_str)); return jstr; } } return NULL; }

The Common Language Runtime (CLR)—the runtime for the .NET Framework—creates a COM-Callable Wrapper (CCW) that marshals System.String values as BSTR values. The Microsoft Active Template Library (ATL) provides a handy wrapper class, CComBSTR, to make this easier and to automatically free space allocated to the BSTR. You should first make sure that you have a valid reference to the .NET control. Then call get_Username—the get accessor of the Username property—passing a reference to the CComBSTR for the username. If the method returns a successful HRESULT code, use the NewStringUTF method of the JNIEnv object to convert the Unicode string to a jstring and return it.

You'll encounter an interesting problem when implementing the getBirthday native function: there is no intrinsic type for the java.util.Date class provided by JNI. You must create an object of type Date in the native implementation as shown in the following code fragment:

// Get the java.util.Date class and its ctor. cls = env->FindClass("java/util/Date"); mid = env->GetMethodID(cls, "<init>", "(J)V"); if (mid != 0) { // Allocate a new Date object. jdate = env->AllocObject(cls); // Adjust for differences in whole days. ldate = ((LONG64)date - 25568) * 86400000; // Instantiate the Date object. env->CallVoidMethod(jdate, mid, ldate); // Return the Date object. return jdate; }

Use the FindClass and GetMethodID methods from the JNIEnv object to get the jclass reference and find the method ID for the appropriate constructor. The JNI specifications state that you should use the string "" to find the constructor. The third parameter, "(J)V", dictates that the constructor should take a long value—a 64-bit integer in Java—and should return void, as all constructors do.

Another problem arises when dealing with dates. The DateTime structure in .NET stores the number of ticks—or 100 nanoseconds—from January 1, 0001 A.D. using the local time zone, as described in the NET Framework SDK documentation.

The CLR marshals the DateTime structure as a DATE, which is a typedef for a double. The DATE data type documentation describes that the CLR stores time as the number of milliseconds since December 30, 1899, also using the local time zone.

Unfortunately, Java stores the number of milliseconds since January 1, 1970 in Universal Coordinate Time (UTC). Not only do you have to worry about adjusting the base time, you must also take the time zone difference into account. The java.util.Calendar class can assist with the time zone problem.

The size and location methods in the Java class are straight-forward. Any method calls to change the position or size of the Java COMControl class will call the native functions which marshal the values to the .NET control. All three environments use 32-bit integers to represent the top and left coordinates, and the width and height of the control.

Assigning the background and foreground can be difficult, but there is a workaround to simplify your implementation. Both environments use a structure to hold color values similarly, but each uses a slightly different structure. Marshaling these two types would add complexity to your code. To make marshaling easier, the Java control instead passes the RGB value of a java.awt.Color object as a long, which is then marshaled as an OLE_COLOR to the .NET control—which converts the value to a System.Drawing.Color structure.

Finish implementing your native functions. Just remember that passing complex types can lead to marshaling problems so try to use basic types as much as possible. Understanding the marshaling services provided by the .NET Framework in the System.Runtime.InteropServices namespace and understanding the JNI specifications can help solve these problems if you must use complex types.

When you finish implementing the native functions, configure the project to link against the jawt.lib static library. I've done this for you in the sample project. You should make sure to add the lib directory in the JDK to your LIB environment variable. Compile the project.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap