Accessing Your Own DLLs

Up to this point, our J/Direct examples have accessed only functions from the Win32 API. It is also possible to access DLLs of your own making. In this section, we'll build a DLL in C and then access this DLL from Visual J Plus Plus.

NOTE
You'll need Visual C++ in order to create the C DLL in this example. Although the steps provided are specific to Visual C++ 6, you can use earlier versions of Visual C++ to create this DLL (keeping in mind the options might differ slightly). If you don't have Visual C++, you can use the CPP_DLL.dll file from the Windows apps\CPP_DLL subdirectory on the companion CD to perform the rest of the example.

Problem

In this section, we'll solve a problem by using a user-defined DLL. Although this problem could easily be handled in Java alone, it's a good vehicle for learning about user-defined DLLs. We'll create a Visual J Plus Plus v6 app that displays a form with a button and an edit box. When the user chooses the button, the app causes the computer to beep and then displays in the output edit box the hours, minutes, and seconds in UTC.

What makes this app instructive is that both the function that sends a beep to the speaker and the function that retrieves the current system time will be contained in a user-defined DLL.

CPP_DLL Dynamic-Link Library

Before we can access a user-defined DLL from Visual J Plus Plus v6, we must build our own DLL. In this section, we'll build the DLL named CPP_DLL.

Forms Designer work

To create a DLL in Visual C++, choose New from the File menu and then select the Win32 Dynamic-Link Library option from the New dialog box. Do not select the MFC AppWizard (dll) option—this will create a DLL that incorporates MFC.

Enter CPP_DLL as the Project Name, and choose OK. Select An Empty DLL Project from the AppWizard window that appears, and choose Finish. This will create a project with the proper options set but won't create any source files. Choose New from the File menu again. This time, select the Files tab and select Text File. Make sure the Add To Project check box is checked, and insert the file name CPP_DLL.c. Choose OK. Visual C++ 6 will add the CPP_DLL.c file to the project. (Under earlier versions of Visual C++, you had to add the file manually.)

code

Open the CPP_DLL.c file and enter the following:

// CPP_DLL - user-defined DLL
// windows.h contains the prototype declarations for the Win32
// API functions including Beep() and GetSystemTime()
#include <windows.h>
// the following prototype declaration declares beepSpeaker
// to be an entry point for the DLL
__declspec(dllexport) void beepSpeaker(int freq, int duration);
// the following structure and prototype declares
// a function that returns the time of day in Universal
// Time Coordinate struct UTCTime
{
 WORD hour;
 WORD minute;
 WORD second;
};
__declspec(dllexport) void getUTCTime(struct UTCTime*);
// beepSpeaker - invoke the Beep() Win32 API function void beepSpeaker(int freq, int duration)
{
 Beep(freq, duration);
}
// getUTCTime - use the GetSystemTime Win32 API function // to fetch the current UTC time of day
// and return it in the argument provided void getUTCTime(struct UTCTime* pUTCTime)
{
 // get the SYSTEMTIME using the Win32 API call
 SYSTEMTIME systemTime;
 GetSystemTime(&systemTime);
 // now get the time of day from the SYSTEMTIME
 // structure and save it in the UTCTime structure
 // passed to the function
 pUTCTime->hour = systemTime.wHour;
 pUTCTime->minute = systemTime.wMinute;
 pUTCTime->second = systemTime.wSecond;
}


CPP_DLL.c begins by including windows.h. This gives CPP_DLL.c access to the Win32 API calls.

NOTE
The C #include directive is roughly equivalent to the Java import directive.

A prototype declaration for beepSpeaker() follows the #include directive. This declaration specifies the arguments freq and duration to be of type integer. The __declspec(dllexport) part of the declaration exports the beepSpeaker() method. Exporting the method makes it accessible from outside the DLL (similar to the Java public directive).

The second method, getUTCTime(), communicates with the calling function by means of a locally defined structure called UTCTime. This method is also exported by means of a __declspec(dllexport) modifier.

The code for the beepSpeaker() method is trivial; it calls the Win32 API Beep() method and passes the two arguments freq and duration. (The fact that beepSpeaker() calls a Win32 API is irrelevant. The point is that beepSpeaker() is a user-defined method.)

The code for getUTCTime() is only slightly more complicated. It begins by reading the system time by means of GetSystemTime(). It then populates the locally defined UTCTime structure with the hour, minute, and second fields of the returned system time.

result

Choose Set Active Configuration from the Build menu and choose the option CPP_DLL - Win32 Release. Now select Build CPP_DLL.dll from the Build menu. Building this project file generates a Release subdirectory containing a DLL with the name CPP_DLL.dll. Once you have built the DLL, close Visual C++.

CAUTION
It's important that this file be compiled as a C program and not as a CPP program. (The name of the DLL reflects the fact that it originated from the Visual C++ module of Visual Studio.) The default option is for all .C files to be compiled as C programs. If you have changed that option, you'll need to set it back for this test.

CustomDLL app

Now that we have a DLL to access, let's create an app named CustomDLL to access it.

Forms Designer work

First, create a Visual J Plus Plus Windows app named CustomDLL. Open the Form1.java file in the Forms Designer. Resize the form to hold a single button below a standard-sized Edit control. Set the form's text property to CustomDLL app to change the program's name in the form's title bar.

Add an Edit control to the form. Set its name to timeEdit. Use the property window to clear the control's text property so that timeEdit initially appears blank. Now add a Button control immediately below the Edit control, and label it Get UTC Time. Anchor the Edit control to the top, left, and right edges of the form, and anchor the Button control to the bottom, left, and right edges of the form.

Double-click the Button control to create its click event handler.

NOTE
Now is a good time to copy the CPP_DLL.dll file into your CustomDLL directory. Alternatively, you can copy the DLL into the Windows\System directory. You must do this before you can execute the app; otherwise, CustomDLL won't be able to find CPP_DLL.dll.

MyDLL code

First we'll need to add a MyDll class to provide the interface to the C functions included in the CPP_DLL dynamic-link library.

Choose Add Item from the Project menu. Now select Class and give it the name MyDLL. This will create the file MyDLL.java and add it to the project. Update the blank Java file as follows:

/**
 * Define access methods for CPP_DLL.dll.
 */
public class MyDLL
{
 /**
 * The beepSpeaker() method beeps the speaker.
 * @dll.import("CPP_DLL")
 */
 public static native void beepSpeaker(int freq, int duration);
 /**
 * The UTC time structure.
 * @dll.struct() */
 public static class UTCTime
 {
 public short hour;
 public short minute;
 public short second;
 }
 /**
 * The getUTCTime() method retrieves the UTC time of day.
 * @dll.import("CPP_DLL")
 */
 public static native void getUTCTime(UTCTime utcTime);
}


The class MyDLL defines two static native functions. Both follow the pattern for J/Direct declarations we have seen earlier.

The first dll.import() directive declares beepSpeaker() to be a member of the CPP_DLL dynamic-link library. Following this is the prototype declaration for the beepSpeaker() function. It's important that the declaration match the C declaration in CPP_DLL.c exactly.

The dll.struct() directive defines a relatively straightforward C structure called UTCTime. The declaration for getUTCTime() declares the function's argument to be a reference to UTCTime.

NOTE
Compare the Visual J Plus Plus v6 prototype declaration static void getUTCTime(UTCTime utcTime) to the C declaration void getUTCTime(struct UTCTime* pUTCTime). J/Direct can't pass a structure to a C function by value.

Form1.java code

The Form1.java code is straightforward.

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
import com.ms.wfc.html.*;
/**
 * Demonstrate the use of the user-defined DLL.
 */
public class Form1 extends Form
{
 public Form1()
 {
 // Required for Visual J Plus Plus Form Designer support
 initForm(); }
 /**
 * Make sure that the time string is 2 digits.
 */
 String padTime(String time)
 {
 // if time is already 2 digits…
 if (time.length() == 2)
 {
 // just return it; otherwise…
 return time;
 }
 // tack a 0 onto the front
 return "0" + time;
 }
 private void button1_click(Object source, Event e)
 {
 // get the time of day
 MyDLL.UTCTime time = new MyDLL.UTCTime();
 MyDLL.getUTCTime(time);
 // now display it, making sure each
 // field is 2 digits wide
 String hour = Integer.toString(time.hour);
 hour = padTime(hour);
 String minute = Integer.toString(time.minute);
 minute = padTime(minute);
 String second = Integer.toString(time.second);
 second = padTime(second);
 // display the result in the Edit control
 timeEdit.setText("UTC time:" +
 hour + ":" +
 minute + ":" +
 second);
 // now send a beep to the speaker
 MyDLL.beepSpeaker(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();
 Edit timeEdit = new Edit();
 Button button1 = 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 button1_click() method begins by creating an object time of class MyDLL.UTCTime. The method then invokes getUTCTime() to populate the time object with the current UTC time.

Each of the data members of time is converted into a string and then passed to padTime(), to make sure that the value in each field is two characters long. (In other words, "1" should be displayed as "01".) Once all the data members have been converted into two-character strings, the strings are concatenated with a colon in between. The result is displayed in the timeEdit object using Edit.setText(). Before returning, button1_click() calls MyDLL.beepSpeaker() to send a beep to the speaker.

Results

The results of executing CustomDLL are shown in Figure 11-4.

Screenshot

Screenshot-4. The UTC time of day displayed by CustomDLL.

It's interesting to see which system DLLs are loaded when CustomDLL executes. Choose About from the Visual J Plus Plus Help menu. Now choose System Info to bring up the Microsoft System Information window. Under the Software Environment folder, select 32-bit Modules Loaded. This window enables you to look at a list of the DLLs that are currently in memory. After scrolling through a long list of names, you'll come upon our own CPP_DLL as shown in Figure 11-5.

Java Click to view at full size.

Screenshot-5. The System Information display showing our CPP_DLL among the list of 32-bit DLLs currently loaded.

Now exit CustomDLL. Scroll through the 32-bit Modules Loaded list again and you'll notice that the CPP_DLL dynamic-link library is gone.

An aside

You might be tempted to think that the CustomDLL program is no different than the earlier apps that invoked the Win32 API functions directly. From the calling program's standpoint, it isn't. After all, the user-defined DLL doesn't do anything more than call the necessary Win32 API. However, the CPP_DLL.dll functions could have done a lot more than simply call a single Win32 API. By following the pattern demonstrated in CustomDLL, you can create your own dynamic link libraries to do anything you like. Comments