Now this article is not to teach JNI, this one is about my discoveries in regards to mystery of JNI communication with Microsoft native COM/ActiveX components. It is not as straight forward it as it looks like. JNI says I CAN talk to Microsoft DLL component, but it does not say that DLL has to be written in certain way. That way is again guided and defined by JNI standards. This article will explain how to go about building a compatible bridge between any form of DLL and Java application.
Requirement: (Requirements are always simple, seems easy as grabbing a beer from the refrigerator)
My requirement is to communicate with Visual Basic ActiveX component (DLL file) using Java.
Speculation: (We always speculate with lots of options and think one thing or other will get us there easily)
- There are millions of way achieving this, Use commercial/Open source products (As mentioned on the right hand side of the article table 1)
- Write your JNI bridge to communicate.
Facts: (When you actual fact bites you, its not pleasant)
- You have to go thru many of the open source products, test them, make sure it do exactly same, as you expect.( Ah!!! lot of patience)
- Some of the product you finally pick saying "Yes" this works , then you realize there is a Licensing nightmare, you can not use it freely for production deployment. Sometimes you can use it but it
will come with good amount of per user/per machine/per processor/ (infinite loop of per thingy). So your clients or customer will decide not to buy because it needs a money to deploy ready made solution. e.g. Look at IBM's license fee for Bridge2java.
- If you decide to write your own JNI code, well "flip this". Java does not understand Visual Basic way of defining methods/functions. So it will not understand Visual Basic Active-X DLL component. Bottom line you simply can not talk to Visual Basic DLL directly using Java.
Solution: (Sooner you figure out the solution, better it is for you)
So you are left with, either use existing open source/commercial product or write your own. In my case after trying few of the products out there decide to go with my own.
As i mentioned, There is no on the earth you can make Java directly talk to Visual Basic DLL, idea is to write a COM Wrapper in C++ (VC++), which will act as bridge between Java and Visual basic. Basically Java can communicate with C++ Wrapper and C++ Wrapper knows all about Visual basic code.
Java(JNI)--> C ++ wrapper -->VB DLL
How Java Talks to VC++
Java JNI will talk to VC++ wrapper using JNI way of defining and communicating. Refer to this tutorial site about how to communicate with DLL files. Key is to follow the pattern of
Java_<Java Class Name>_<Method Name to Invoke> on your .h and .cpp files. typical
method on .h and .cpp will look like this
JNIEXPORT jobjectArray JNICALL Java_CallService_GetEarthCoOrdinates (JNIEnv *env, jobject object)
How VC++ talks to VB
I will not write in detail about this because ,there are good resources out there and one of them is
codeproject site, about how to get VC++ talking to VB.
That should give you a good idea about how to achieve, two different worlds (Java and Visual Basic) talking with each other.
What next ?; The difficult part.
Once the basic communication done, then comes the most difficult parts where you need pass parameters back and forth. If parameters are complex, then its more painful. I have listed down few points on how to get things across using different data types.
Situation : Lets look at the complex situation. Java is passing String arrays (String[ ][ ]) and Visual Basic is expecting (Variant array Dim earthParams(10, 10) As Variant).
- You have to pass String Array as jobjectArray to VC++
- Convert them to SAFEARRAY
- Convert SAFEARRAY to VARIANT
- Pass VARIANT to VB via call.
Same thing Other way round when data comes back from VB.
Convert jobjectArray (Java String[ ][ ] array) to VC++ SAFEARRAY/VARAINT (C++ Code)
VARIANT loadJavaArrayToCPPSafeArray(jobjectArray stringArray, JNIEnv * env)
SAFEARRAYBOUND rgsabound; //This means 2 Dimensional
rgsabound.cElements = 10; // 10 rows
rgsabound.lLbound = 0;
rgsabound.cElements = 10; //10 columns
rgsabound.lLbound = 0;
psa=SafeArrayCreate(VT_VARIANT, 2, rgsabound); //Finally Create
//Now get First Dimension Array Length for String  array
jint length1Dim = (env)->GetArrayLength(stringArray);
for (aiIndex = 0; aiIndex < length1Dim; aiIndex++)
jobjectArray oneDimArray= (jobjectArray)env->GetObjectArrayElement(stringArray, aiIndex);
//Get Second Dimension
of the String array passed.
jint length2dim = (env)->GetArrayLength(oneDimArray);
const char* szStr;
szStr = env->GetStringUTFChars( oneDim, 0 );
_bstr_t str2 = szStr; //Get Sting value from array to BSTR of
var.vt = VT_BSTR;
var.bstrVal = str2;
//Now Populate SAFEARRAY with Data.
HRESULT hrs = SafeArrayPutElement(psa,aiIndex,&var);
//Following is to Check
whether SafeArrayPutElement is successful or not. just for
if(hrs == S_OK)
else if(hrs == DISP_E_BADINDEX)
else if(hrs == E_INVALIDARG)
else if(hrs == E_OUTOFMEMORY)
//Have to release
env->ReleaseStringUTFChars( oneDim, szStr );
//Finally Create Variant
var1.parray = psa;
Representing Two-Dimensional Java 's String Array String
Printing 2D BSTR SAFEARRAY Contents
void printSafeArrayContents(SAFEARRAY psa*)
cout<< "=============Printing Out..==========="<<endl;
for(arrayIndex = 0; arrayIndex < 6; arrayIndex++)
for(arrayIndex = 0; arrayIndex < 2; arrayIndex++)
SafeArrayGetElement(psa, arrayIndex, &result);
Generate 2D String array (jobjectArray) from 2D SAFEARRAY, to pass back to Java (Borrowed from
JNISnippets Site and enhanced for SAFEAARRY)
jarray Get2DArrayFromSafeArray(JNIEnv* env, SAFEARRAY *psa)
int rows = 10;
int cols = 10;
// Got to build each row
// get an String object
job = (env)->NewStringUTF("");
// get String class
class1 = (env)->GetObjectClass(job);
// get the row of String array objects
row[biIndex] = (env)->NewObjectArray(cols,class1,job);
// initialize the elements
SafeArrayGetElement(psa, biIndex, &result); //Get Variant out of
sprintf(s,"%S",result.bstrVal); //Get BSTR value from
VARIANTand assign to char c*
job = (env)->NewStringUTF(s);
//Create New string from the value recieved
//Now once the rows are
constructed with columns in it , now attach it to Master index
row (This is little twisted)
class1 = (env)->GetObjectClass(row);
// get the base array object
aref = (env)->NewObjectArray(rows,class1,0);
// fill in the array
// return the array
Its lots of jumps-n-hoops to get this puppy working. I thought i was long done with my VC++/VB and it came back haunting me after 8 years. No complains; its like going back memory lane, opening Visual
Studio for VC++ and VB and sitting coding. Its fun afterall.
Java/Win32 Integration resources
|Online JNI Book|