Thursday, December 30, 2004

Java-JNI-COM-ActiveX Bridge.; Lots of jumps-n- hoops

I'm assuming reader will have basic idea of what and how Java-JNI works, if not refer to Sun's JNI Tutorial section . For Advanced Java-JNI Programming look at this site.

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.

Solution Detail

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).











Java VC++ VB
String[][] SAFEARRAY/VARIANT Variant array


  • 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)

{


//Define SAFEARRAY


SAFEARRAY *psa;

VARIANT var1;

SAFEARRAYBOUND rgsabound[2]; //This means 2 Dimensional
SAFEARRAY



rgsabound[0].cElements = 10; // 10 rows

rgsabound[0].lLbound = 0;



rgsabound[1].cElements = 10; //10 columns

rgsabound[1].lLbound = 0;



psa=SafeArrayCreate(VT_VARIANT, 2, rgsabound); //Finally Create
2D SAFEARRAY



//Now get First Dimension Array Length for String [][] array
passed

jint length1Dim = (env)->GetArrayLength(stringArray);



long aiIndex[2];



for (aiIndex[0] = 0; aiIndex[0] < length1Dim; aiIndex[0]++)

{



jobjectArray oneDimArray= (jobjectArray)env->GetObjectArrayElement(stringArray, aiIndex[0]);


//Get Second Dimension
of the String[][] array passed.

jint length2dim = (env)->GetArrayLength(oneDimArray);



jstring oneDim;

const char* szStr;



for(aiIndex[1]=0;aiIndex[1]<length2dim;aiIndex[1]++)

{

oneDim= (jstring)env->GetObjectArrayElement(oneDimArray,
aiIndex[1]);

szStr = env->GetStringUTFChars( oneDim, 0 );



VARIANT var;



_bstr_t str2 = szStr; //Get Sting value from array to BSTR of
C++

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
Debug

if(hrs == S_OK)

cout<< "S_OK";

else if(hrs == DISP_E_BADINDEX)

cout<< "DISP_E_BADINDEX";

else if(hrs == E_INVALIDARG)

cout<< "E_INVALIDARG";

else if(hrs == E_OUTOFMEMORY)

cout<< "E_OUTOFMEMORY";


//Have to release
Created String.

env->ReleaseStringUTFChars( oneDim, szStr );

}





}


//Finally Create Variant
from SAFEARRAY.

VariantInit(&var1);

var1.vt=VT_ARRAY VT_VARIANT;

var1.parray = psa;


//Return Variant
Representing Two-Dimensional Java 's String Array String[][]

return var1;

}



Printing 2D BSTR SAFEARRAY Contents


void printSafeArrayContents(SAFEARRAY psa*)

{

long arrayIndex[2];

cout<< "=============Printing Out..==========="<<endl;

for(arrayIndex[0] = 0; arrayIndex[0] < 6; arrayIndex[0]++)

{



for(arrayIndex[1] = 0; arrayIndex[1] < 2; arrayIndex[1]++)

{

VARIANT result;

SafeArrayGetElement(psa, arrayIndex, &result);

cout<<result.bstrVal <<endl;

}

cout<< "========================="<<endl;

}


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)

{

char s[200];

jarray aref;

jobject job;

jclass class1;

jarray row[1];

int rows = 10;

int cols = 10;

int i;

int j;

long biIndex[2];


// Got to build each row
seperately.

for(biIndex[0]=0;biIndex[0]<rows;biIndex[0]++)

{

// get an String object

job = (env)->NewStringUTF("");

// get String class

class1 = (env)->GetObjectClass(job);

// get the row of String array objects

row[biIndex[0]] = (env)->NewObjectArray(cols,class1,job);



// initialize the elements

for(biIndex[1]=0;biIndex[1]<cols;biIndex[1]++)

{



VARIANT result;

SafeArrayGetElement(psa, biIndex, &result); //Get Variant out of
SAFEARRAY



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

(env)->SetObjectArrayElement((jobjectArray)row[biIndex[0]],biIndex[1],job);

}

}


//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[0]);



// get the base array object

aref = (env)->NewObjectArray(rows,class1,0);



// fill in the array

for(i=0;i<rows;i++)

{

(env)->SetObjectArrayElement((jobjectArray)aref,i,row[i]);

}



// return the array

return aref;

}


Conclusion


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.


Resources


































http://msdn.microsoft.com
Microsoft
Developer Network


http://www.codeproject.com
Code Project

http://java.sun.com
Sun's Java
initiative


http://www.codeguru.com/
Cod Guru


http://www.relevancellc.com/halloway/JavaWin32.html
Java/COM,
Java/Win32 Integration resources


http://jguru.com/faq/view.jsp?EID=448045
JNI FAQ


http://www.geocities.com/Jeff_Louie/safearray.html
Good details
about SAFEARRAY


http://java.sun.com/docs/books/jni/html/jniTOC.html
Online JNI Book


4 comments:

cash advance san antonioc said...

Excellent blog. It was so great and I bet I will
go back to it! I get to look online for blogs like
yours is a blessing.
You must peep out my blog.

job opportunitya said...

I look for blogs as great as your work. Fine
blog. I found your site suitable for another visit!
I wish I was like you, but I'll go and peep your blog.

not-enuf-time said...

Terrific blog. I search the internet everytime I
get a moment to find blogs. Its better than cold iced
tea and I have to visit it one more time!
Check out my ace cash advance blog, you won't be sorry!

baby-dollz said...

Inspiring blog. I love finding blogs this good on
the internet, when I have the time. I'm going to go
back to it!
Please come by and see my guaranteed cash advance blog.