Using the Raw Native Interface |
![]() Previous |
![]() Introduction |
![]() Next |
Suppose we have a simple Java class that performs a prime-number sieve and that we want to do the calculation in C implemented in a DLL called SieveDemo.dll say. In Java the class might look like:
class Sieve { native static int CountPrimes(byte[] abFlags); static { System.loadLibrary("SieveDemo"); } }
Here, we declare CountPrimes to be native and add loadLibrary call in a static block to make sure the DLL that's going to implement the API will be loaded.
The next step is to use jvc to compile this Java source into a class file:
jvc Sieve.java Microsoft (R) Visual J++ Compiler Version 1.00.XXXX Copyright (C) Microsoft Corp 1996. All rights reserved.
Next we use msjavah to produce a header file for use by the native code:
msjavah Sieve
Which should look like:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <native.h> /* Header for class Sieve */ #ifndef _Included_Sieve #define _Included_Sieve typedef struct ClassSieve { #pragma pack(push,1) int32_t MSReserved; char PAD; /* ANSI C requires structures to have a least one member */ #pragma pack(pop) } ClassSieve; #define HSieve ClassSieve #ifdef __cplusplus extern "C" { #endif __declspec(dllexport) long __cdecl Sieve_CountPrimes(struct HSieve *,HArrayOfByte *); #ifdef __cplusplus } #endif #endif
This header file defines a structure ClassSieve which represents the data members of Sieve and a declaration for the native API that we're going to implement. Since in this example Sieve doesn't have any data members ClassSieve just contains some housekeeping fields. Native API's are of the form Package_Class_Member(...) and since Sieve is not in an explicitly named package the API we're going to implement is therefore Sieve_CountPrimes(). The first parameter to the API is the "this" pointer which for static functions will always be NULL and the second parameter is the byte array we're going to manipulate.
Note One useful feature of msjavah is that primitive types (int, boolean, long etc.) marked as being static and final become #define's in the resulting header file making it much easier to share constants.
Next we implement the C we need:
#include <varargs.h> #include <native.h> #include "Sieve.h" long cdecl Sieve_CountPrimes(struct HSieve *phThis, HArrayOfByte *phFlags) { unsigned long count = 0; unsigned long i; for (i = 0; i < obj_length(phFlags); i++) (phFlags->body)[i] = 1; for (i = 2; i < obj_length(phFlags); i++) { if ((phFlags->body)[i] != 0) { unsigned long k; for (k = i + i; k < obj_length(phFlags); k += i) (phFlags->body)[k] = 0; count++; } } return count; }
This is pretty much a standard implementation of a sieve with a couple of helpers from native.h to get access to the data of the array (phFlags->body) and to query it's length (obj_length(phFlags)).
Next we add a .def file to export this API:
LIBRARY SieveDemo DESCRIPTION 'Native DLL for SieveDemo' EXPORTS Sieve_CountPrimes
All that's left to do is build everything and copy the resulting DLL to the system directory and see if it works!