Open source Java projects: Java Native Access

An easier way to access native code

1 2 3 4 5 Page 4
Page 4 of 5

Working with TCHAR

The use of TCHAR and the business of mapping macros to functions only slightly complicate the task of transitioning the C-based declarations to a JNA-based interface -- do you work with the ASCII or wide-character version of a joystick function? Both versions are shown in the interface below.

Listing 6. WinMM.java

// WinMM.java

import com.sun.jna.*;
import com.sun.jna.win32.*;

public interface WinMM extends StdCallLibrary
{
   final static int JOYCAPSA_SIZE = 72;

   public static class JOYCAPSA extends Structure
   {
      public short wMid;
      public short wPid;
      public byte szPname [] = new byte [32];
      public int wXmin;
      public int wXmax;
      public int wYmin;
      public int wYmax;
      public int wZmin;
      public int wZmax;
      public int wNumButtons;
      public int wPeriodMin;
      public int wPeriodMax;
   }

   int joyGetDevCapsA (int id, JOYCAPSA caps, int size);

   final static int JOYCAPSW_SIZE = 104;

   public static class JOYCAPSW extends Structure
   {
      public short wMid;
      public short wPid;
      public char szPname [] = new char [32];
      public int wXmin;
      public int wXmax;
      public int wYmin;
      public int wYmax;
      public int wZmin;
      public int wZmax;
      public int wNumButtons;
      public int wPeriodMin;
      public int wPeriodMax;
   }

   int joyGetDevCapsW (int id, JOYCAPSW caps, int size);

   int joyGetNumDevs ();
}

Listing 6 does not introduce new JNA features. Instead, it reinforces the convention of naming the interface after a native library. It also shows how to map the TCHAR array to equivalent byte and char Java arrays. Finally, it reveals structure size constant declarations. Listing 7 shows the usefulness of these constants when invoking joyGetDevCapsA() and joyGetDevCapsW().

Listing 7. JoystickInfo.java

// JoystickInfo.java

import com.sun.jna.*;

public class JoystickInfo
{
   public static void main (String [] args)
   {
      WinMM lib = (WinMM) Native.loadLibrary ("winmm", WinMM.class);
      int numDev = lib.joyGetNumDevs ();

      System.out.println ("joyGetDevCapsA() Demo");
      System.out.println ("---------------------\n");

      WinMM.JOYCAPSA caps1 = new WinMM.JOYCAPSA ();
      for (int i = 0; i < numDev; i++)
           if (lib.joyGetDevCapsA (i, caps1, WinMM.JOYCAPSA_SIZE) == 0)
           {
               String pname = new String (caps1.szPname);
               pname = pname.substring (0, pname.indexOf ('\0'));
               System.out.println ("Device #"+i);
               System.out.println ("  wMid = "+caps1.wMid);
               System.out.println ("  wPid = "+caps1.wPid);
               System.out.println ("  szPname = "+pname);
               System.out.println ("  wXmin = "+caps1.wXmin);
               System.out.println ("  wXmax = "+caps1.wXmax);
               System.out.println ("  wYmin = "+caps1.wYmin);
               System.out.println ("  wYmax = "+caps1.wYmax);
               System.out.println ("  wZmin = "+caps1.wZmin);
               System.out.println ("  wZmax = "+caps1.wZmax);
               System.out.println ("  wNumButtons = "+caps1.wNumButtons);
               System.out.println ("  wPeriodMin = "+caps1.wPeriodMin);
               System.out.println ("  wPeriodMax = "+caps1.wPeriodMax);
               System.out.println ();
           }

      System.out.println ("joyGetDevCapsW() Demo");
      System.out.println ("---------------------\n");

      WinMM.JOYCAPSW caps2 = new WinMM.JOYCAPSW ();
      for (int i = 0; i < numDev; i++)
           if (lib.joyGetDevCapsW (i, caps2, WinMM.JOYCAPSW_SIZE) == 0)
           {
               String pname = new String (caps2.szPname);
               pname = pname.substring (0, pname.indexOf ('\0'));
               System.out.println ("Device #"+i);
               System.out.println ("  wMid = "+caps2.wMid);
               System.out.println ("  wPid = "+caps2.wPid);
               System.out.println ("  szPname = "+pname);
               System.out.println ("  wXmin = "+caps2.wXmin);
               System.out.println ("  wXmax = "+caps2.wXmax);
               System.out.println ("  wYmin = "+caps2.wYmin);
               System.out.println ("  wYmax = "+caps2.wYmax);
               System.out.println ("  wZmin = "+caps2.wZmin);
               System.out.println ("  wZmax = "+caps2.wZmax);
               System.out.println ("  wNumButtons = "+caps2.wNumButtons);
               System.out.println ("  wPeriodMin = "+caps2.wPeriodMin);
               System.out.println ("  wPeriodMax = "+caps2.wPeriodMax);
               System.out.println ();
           }
   }
}

Although similar to LocalTime, JoystickInfo executes WinMM lib = (WinMM) Native.loadLibrary ("winmm", WinMM.class); to allocate a WinMM instance and load winmm.dll. It also executes WinMM.JOYCAPSA caps1 = new WinMM.JOYCAPSA (); and WinMM.JOYCAPSW caps2 = new WinMM.JOYCAPSW (); to allocate needed structure instances.

Compile and run the application

Converting a C string to a Java string
The pname = pname.substring (0, pname.indexOf ('\0')); code fragment converts a C string to a Java string. Without this conversion, the \0 string terminator and any following garbage characters would be part of the Java string.

Assuming that jna.jar, WinMM.java, and JoystickInfo.java are located in the current directory, invoke javac -cp jna.jar;. JoystickInfo.java to compile the application's source code. Assuming a Windows platform, invoke java -cp jna.jar;. JoystickInfo to run the application. If no joystick devices are attached, you should see the output shown in Listing 8.

Listing 8. Output of JoystickInfo

joyGetDevCapsA() Demo
---------------------

joyGetDevCapsW() Demo
---------------------

This header-only output is due to each joyGetDevCap() invocation returning a non-zero value, which indicates either a non-attached joystick/game controller or an error. To generate more interesting output, attach one of these devices to your platform and re-run JoystickInfo. For example, I received the output shown below after attaching a Microsoft SideWinder Plug and Play Game Pad device:

Listing 9. Output after running JoystickInfo with a joystick attached

joyGetDevCapsA() Demo
---------------------

Device #0
  wMid = 1118
  wPid = 39
  szPname = Microsoft PC-joystick driver
  wXmin = 0
  wXmax = 65535
  wYmin = 0
  wYmax = 65535
  wZmin = 0
  wZmax = 65535
  wNumButtons = 6
  wPeriodMin = 10
  wPeriodMax = 1000

joyGetDevCapsW() Demo
---------------------

Device #0
  wMid = 1118
  wPid = 39
  szPname = Microsoft PC-joystick driver
  wXmin = 0
  wXmax = 65535
  wYmin = 0
  wYmax = 65535
  wZmin = 0
  wZmax = 65535
  wNumButtons = 6
  wPeriodMin = 10
  wPeriodMax = 1000
1 2 3 4 5 Page 4
Page 4 of 5