Using the J/Direct Call Builder

J/Direct works through a set of what I'll call J/Direct directives. These directives are built into the comments that immediately precede each function definition or structure definition, in the same way that the Javadoc comments (described in Chapter 1) work. (Perhaps this is so that Microsoft's J/Direct directives won't conflict with any new Java keywords that might be introduced in the future by Oracle or by some future Java standards committee.)

It's possible, and not all that difficult, to write J/Direct directives manually; however, in most cases, the J/Direct Call Builder makes this unnecessary.

NOTE
The J/Direct Call Builder is currently limited to Win32 API functions. This limitation isn't based on any technical grounds, but is based on the fact that only Win32 API calls are documented in the text file that the J/Direct Call Builder uses to identify the functions and structures a program can access. Presumably, in the future, Microsoft will make text files available for more APIs in order to expand the number of functions accessible from the J/Direct Call Builder.

Using J/Direct: The Beep app

In this section, I'll demonstrate J/Direct and the J/Direct Call Builder with an extremely simple example. From there, we'll graduate to a slightly more complicated example involving the passing of an object reference from the Win32 API to Visual J Plus Plus.

problem

In the Beep app, our needs are simple. All we'll do is write an app with a single button that, when pressed, causes the computer to generate a beep and send the beep to the speaker.

NOTE
The standard Java library contains a function that causes the computer speaker to sound a beep, so we don't really need to resort to J/Direct for this app. However, we can use a Win32 API function in order to learn how to use J/Direct.

Finding the right function

Before you can call a function, you have to know that it exists and what arguments it expects. Microsoft Visual Studio's online help system provides this information for Win32 API functions.

To find the function you need, open the MSDN Library for Visual Studio 6, which comes with Visual J Plus Plus. You can do this by using the Visual J Plus Plus Help menu, or by using the Programs item on the Windows Start menu. Select the Index tab in the left pane of the help window, and type the word beep. Choose the keyword Beep (not beep), and you should get results similar to those shown in Figure 11-1.

Java Click to view at full size.

Screenshot-1. The Visual J Plus Plus documentation describes the Windows API function Beep().

NOTE
When I'm writing Visual J++ code, I normally have the MSDN Library Active Subset option set to Visual J++ so that my searches don't end up finding functions belonging to other languages. When using J/Direct, make sure to change the Active Subset setting to (Entire Collection) or at least to some subset that includes the Win32 API help topics.

By looking at the Beep help topic, we can see that the function Beep() appears to be exactly what we want: a Windows API function that generates a beep through the sound card (assuming a sound card is available). Under Microsoft Windows NT, Beep() can even determine the frequency and duration of the beep. However, in the Remarks section of the Beep() help topic, we learn that the frequency and duration arguments are ignored under Microsoft Windows 95 and Microsoft Windows 98. It would be nice to be able to control the frequency and duration of the beep in all versions of Windows, but we'll have to live without these extra features in all systems but Windows NT.

The arguments to Beep() are all intrinsic data types, like integers, which are easier to handle than class objects. You'll see how to pass class objects in the next example.

NOTE
You'll notice that there are several options displayed when you enter the keyword beep in the help index. The options other than Beep are actually Microsoft Visual Basic and script functions that are merely providing access to the Windows API Beep() function. One distinction between help topic options is that Win32 API functions always begin with an initial capital letter, whereas Java methods and many C++ functions start with a lowercase letter (although Visual Basic statements and methods also begin with an initial capital letter).

Accessing Beep() through the J/Direct Call Builder

Using the Visual J Plus Plus Windows app option, create an app called Beep. Now open the J/Direct Call Builder by choosing Other Windows from the View menu and selecting J/Direct Call Builder.

CAUTION
Don't confuse the J/Direct Call Builder Options command on the Tools menu with the J/Direct Call Builder command. The J/Direct Call Builder Options command enables you to specify the options for the code that the J/Direct Call Builder generates; it doesn't provide access to the J/Direct Call Builder.

Since the Beep() function takes two integers as arguments, and since it returns a simple boolean value, we aren't interested in the Win32 structures or Win32 constants. In the J/Direct Call Builder, clear the Structs and Constants check boxes to reduce the number of items you must search through. Scroll down until the function Beep() becomes visible. Figure 11-2 shows the J/Direct Call Builder window with the Beep() function selected.

Screenshot

Screenshot-2. The J/Direct Call Builder showing the Windows Beep() function.

When you examine the window at the bottom of Figure 11-2, you can see the Javadoc-style comments that are the essence of J/Direct. The dll.import() directive indicates the name of the DLL that contains the selected function. This dll.import() directive indicates that Beep() resides in Kernel32.dll. As the name implies, Kernel32 is one of the core DLLs that Windows loads at startup and keeps resident at all times.

You can also see from Figure 11-2 that the Target edit window contains the name and path of the .java file J/Direct is about to create. The default is …\Win32.java. You can change this file name by entering a new .java file name or by selecting an existing .java file name by means of the browser. Since you probably don't particularly care about the name of the file, leave the default file name.

Now choose the Copy To Target button. Since the Win32.java file doesn't yet exist, a dialog box appears asking whether you want Visual J Plus Plus to create the file. Select Yes to create the following Win32.java file, which is automatically added to the project.

public class Win32
{
 /**
 * @dll.import("KERNEL32",auto) */
 public static native boolean Beep(int dwFreq, int dwDuration);
}


You can see that the J/Direct Call Builder has created a declaration for a public static Beep() function that matches the Win32 API Beep() function with respect to arguments and return type. Declaring the function public gives all calling methods access to it. The function is static because there is no object involved. Since the Win32 API is C-based, it's not object-oriented.

The only part of the declaration that is unique to Visual J Plus Plus is the native keyword. This keyword indicates that the Beep() function is written in machine code. (It was actually written in C, but the C code is compiled into machine code rather than into Java Virtual Machine byte code. How the function ended up being compiled into machine code is irrelevant to our discussion.) The methods that are declared native contain no code, because the code resides in the DLL.

You'll also notice the dll.import() directive described earlier, which tells Visual J Plus Plus where to find the Beep() function.

Forms Designer work

The Forms Designer work for the Beep app is particularly simple. Open Form1.java in the Forms Designer. Shrink the form to make it only slightly larger than an average-sized button. Now change the form's text field to J/Direct Beep Test to match the purpose of the app.

Add a button to the middle of the form. Label the button Beep, and anchor it to the top, bottom, left, and right edges of the form. Finally, double-click the button to create the button1_click() event handler.

code

The code for this app is as follows:

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
import com.ms.wfc.html.*;
/**
 * This method demonstrates the J/Direct Call Builder.
 */
public class Form1 extends Form
{
 public Form1()
 {
 // Required for Visual J Plus Plus Form Designer support
 initForm(); }
 .
 .
 .
 private void button1_click(Object source, Event e)
 {
 // invoke the Win32 Beep() function
 Win32.Beep(400, 1000);
 }
 /**
 * NOTE: The following code is required by the Visual J Plus Plus form
 * designer. It can be modified using the form editor. Do not
 * modify it using the code editor.
 */
 Container components = new Container();
 Button button1 = new Button();
 private void initForm()
 {
 // …created by Forms Designer…
 }
 /**
 * The main entry point for the app. */
 public static void main(String args[])
 {
 app.run(new Form1());
 }
}


The button1_click() method simply calls the Beep() method that was created by the J/Direct Call Builder. The Win32.Beep() syntax is consistent with the fact that Beep() is a static method of the class Win32. The arguments indicate a 400-Hz tone (equivalent to the middle C note, I believe) of 1-second duration.

results

When the user chooses the Beep button, the computer generates a 1-second, 400-Hz tone under Windows NT, or a standard system beep under Windows 95 and Windows 98. (This is not very exciting, I admit, but it's what we asked for.)

As an aside, I'll mention that the Win32 Beep() function is already available in the WFC package com.ms.win32. This package organizes subpackages by DLL, so you would refer to Beep() as com.ms.win32.Kernel32.Beep().

Passing Objects Using J/Direct: the SetSystemTime app

The Beep app is impressive—to me, anyway—not in what it does, but because it demonstrates how easy it is to create an app that accesses the Win32 API. The Forms Designer did all the work in creating the form, and the J/Direct Call Builder, together with J/Direct, did the work of creating a bridge between Visual J Plus Plus and the C-based Win32 API.

The Beep app is simple for another reason: the arguments passed to and from the Beep() function are all intrinsic data types. The app passes two short integers to the function, and the function returns a boolean value. But what happens when the arguments being passed are class objects?

The SetSystemTime example demonstrates how to invoke object-passing Win32 functions by means of J/Direct. You'll see that accessing such functions with the J/Direct Call Builder is only slightly more difficult than accessing Win32 functions that pass intrinsic data types.

problem

In the SetSystemTime app, we'll want to be able to read and modify the computer's clock. Imagine a small form with an edit box across the top, and a Get Time button and Set Time button at the bottom. The time the edit box displays should be in an easy-to-read format similar to the format generated by the Time class we discussed in .

Finding the right function

An index search of the MSDN Library Visual Studio 6 topics for the keyword settime reveals two possible candidates in the list: setTime and SetTime. Names that begin with lowercase letters—such as setTime—are generally methods of a class and are therefore not useful for our purposes.

NOTE
Since the Win32 API is a C interface, Win32 API functions are never methods of a class.

Very often, methods whose names begin with a lowercase letter are Java functions. Further examination of the setTime() method reveals that, in fact, Date.setTime() and Time.setTime() are Java methods. The SetTime() function is more likely to be what we are looking for.

Selecting SetTime in the help topic list and choosing Display opens a dialog box that offers two topic options: CDateTimeCtrl::SetTime and COleDateTime::SetTime. Neither function looks right; however, I'm certain that the second function is incorrect. Any function with the word "Ole" embedded in it is an OLE function, and our app has nothing to do with OLE.

NOTE
The name in front of the double colon (::) is the name of the class in C++ and the name after the double colon is the name of the method. The Java equivalent to the double colon is the dot (.) after a Java class name.

The CDateTimeCtrl class in the first help topic option indicates that this SetTime() function is a method of a Microsoft Foundation Class library (MFC) class. MFC classes begin with a capital "C" followed by a capital letter. MFC classes are the C++ analog to WFC classes. The fact that this function is part of MFC doesn't sound very good, since we can't access MFC from the J/Direct Call Builder; however, it's still worth looking into, because some of the MFC functions are a thin shell over the corresponding Win32 API function.

Selecting the CDateTimeCtrl::SetTime help topic and choosing Display reveals the topic shown in Figure 11-3.

You can see in the right pane that MFC provides three different CDateTimeCtrl::SetTime() methods. We can reject the first method immediately, because it refers to OLE. The second method, SetTime(CTime*), is not a likely candidate either,

Java Click to view at full size.

Screenshot-3. The MSDN Library documentation for the available SetTime() methods.

because CTime appears to be an MFC class. The third method, SetTime(SYSTEMTIME*), is a definite possibility, because SYSTEMTIME is obviously not part of MFC—its name is all capitals letters and it doesn't start with "C". Choosing the SYSTEMTIME hot link reveals that SYSTEMTIME is a simple C structure. Things are looking up.

NOTE
The Win32 API uses C structures to pass information.

Now you can refer back to the J/Direct Call Builder window and enable the Methods and Structs options. A quick search of the J/Direct Call Builder options confirms the existence of a SetSystemTime() method and a SYSTEMTIME structure. Another quick check shows the GetSystemTime() method is also represented. Bingo!

J/Direct Call Builder work

To begin writing SetSystemTime, create the Windows app SetSystemTime in the conventional manner. To create the J/Direct declarations for the SetSystemTime() function, follow the same steps you used in the previous example for creating the J/Direct declaration for Beep(): select SetSystemTime from the list of methods, and then choose Copy To Target. Repeat this process to create the J/Direct declaration for the GetSystemTime() function.

Neither of these functions is much use without the SYSTEMTIME structure. Fortunately, creating the J/Direct declaration for a class is the same process as creating a J/Direct declaration for a method: select SYSTEMTIME from the list of Structs, and choose Copy To Target.

The final Win32.java file is as follows:

public class Win32
{
 /**
 * Set the system time.
 * * @param lpSystemTime - a reference to object containing the time
 * @dll.import("KERNEL32",auto) */
 public static native boolean SetSystemTime(
 com.ms.win32.SYSTEMTIME lpSystemTime);
 /**
 * Get the system time.
 * * @param lpSystemTIme - object to receive the system time
 * @dll.import("KERNEL32",auto) */
 public static native void GetSystemTime(
 com.ms.win32.SYSTEMTIME lpSystemTime);
}
 /**
 * The system time in Win32 API format.
 * @dll.struct() */
 public static class SYSTEMTIME
 {
 public short wYear;
 public short wMonth;
 public short wDayOfWeek;
 public short wDay;
 public short wHour;
 public short wMinute;
 public short wSecond;
 public short wMilliseconds;
 }


The SetSystemTime() and GetSystemTime() definitions resemble the definition of the earlier Beep() function, except that they reference an object rather than an intrinsic. The J/Direct Call Builder also created a definition for the class SYSTEMTIME, which contains the members described in the MSDN Library for Visual Studio 6. The J/Direct dll.struct() directive indicates that the following class is the description of a C structure. I added the comments and param statements manually.

Forms Designer work

The Forms Designer work for this app is similar to the work for other Windows apps you've seen. Change the text property of the form to J/Direct Time Structure Test.

Next add an Edit control to the upper half of the form. Enlarge the Edit control to make it long enough to hold the date and time in long format. (See for an example of long format.) Delete the contents of the text field, and change the name property setting to timeEdit. Anchor the Edit control to the top, left, and right edges of the form.

Below the Edit control, add two buttons. Label the button on the left Get Time, and set its name property to getTime. Label the other button Set Time, and set its name property to setTime. Anchor the left button to the left and bottom edges of the form and anchor the right button to the right and bottom edges of the form. Finally, double-click each button to create its click event.

code

The code for SetSystemTime is shown here:

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
import com.ms.wfc.html.*;
/**
 * Demonstrate the use of J/Direct calls that pass objects
 * by accessing the GetSystemTime() and SetSystemTime() * functions of the Win32 API.
 */
public class Form1 extends Form
{
 // retain the SYSTEMTIME in a data member
 com.ms.win32.SYSTEMTIME time = new com.ms.win32.SYSTEMTIME();
 public Form1()
 {
 // Required for Visual J Plus Plus Form Designer support
 initForm();
 // start with the current time
 getTime_click(null, null);
 }
 .
 .
 .
 private void getTime_click(Object source, Event e)
 {
 // get the UTC time using J/Direct
 Win32.GetSystemTime(time);
 // convert into a Time object
 Time t = new Time(time);
 // convert the Time object into local time
 Time lt = t.toLocalTime();
 // use the local Time object to generate a text format
 timeEdit.setText(lt.formatLongDate() +
 " " +
 lt.formatLongTime());
 }
 private void setTime_click(Object source, Event e)
 {
 try
 {
 // get the contents of the time field
 String s = timeEdit.getText();
 // now convert this into a Time object
 Time lt = new Time(s);
 // convert the Time object into a UTC time
 Time t = lt.toUniversalTime();
 // from there generate a SYSTEMTIME object
 time = t.toSystemTime();
 // now use the J/Direct call to set the system time
 Win32.SetSystemTime(time);
 }
 // in the event of an error…
 catch(Exception ex)
 {
 // restore the existing time
 getTime_click(null, null);
 }
 }
 /**
 * NOTE: The following code is required by the Visual J Plus Plus form
 * designer. It can be modified using the form editor. Do not
 * modify it using the code editor.
 */
 Container components = new Container();
 Edit timeEdit = new Edit();
 Button getTime = new Button();
 Button setTime = new Button();
 private void initForm()
 {
 // …created by the Forms Designer…
 }
 /**
 * The main entry point for the app. */
 public static void main(String args[])
 {
 app.run(new Form1());
 }
}


The getTime_click() method begins by reading the system time using the newly created Win32.GetSystemTime() native method. Fortunately, one of the constructors of the Time class accepts an object of class SYSTEMTIME. This will allow the program to use the substantial formatting capability of the Time class for output.

NOTE
Were there no way to create a Time object from the SYSTEMTIME class, the program would have to convert each field within SYSTEMTIME into a String for output. You'll get a taste of such a situation in the example of a user-defined DLL at the end of this chapter.

Since the time returned by GetSystemTime() is in Universal Time Coordinate (UTC)—previously known as Greenwich Mean Time (GMT)—it is necessary to convert the returned time to the local time zone by calling Time.toLocalTime(). The resulting time is then output to the timeEdit object using the Time class's formatLongDate() method and formatLongTime() method.

The setTime_click() method works almost in reverse of the getTime_click() method but has one additional behavior: the Time(String) constructor throws an exception if it can't parse the string that it receives into a valid time—for example, if the value in the month field is misspelled or if the value in the day field is greater than the number of days in the specified month. If the method throws an exception, the program catches the exception and restores the timeEdit value to the current time to indicate to the user that he or she entered an incorrect value.

NOTE
If the user enters in the edit box a long date that includes the day of the week, such as Tuesday, October 06, 1998, an exception will be thrown. The user must remove the day of the week for SetTime() to accept the date.
Comments