Summary:  Paper describes a technology of Java – MDL both direction communication inside MicroStation v8.

 

Date: January 2001

Printed in : December 2001, MSM

Author: Stanislav Sumbera

 

*

download code 

 

 

 

*

pdf version

 

 

 

MSM online

 

 

 

 

  

 

 

 

 

 

*      Introduction. 2

*      Use the jmdl_import keyword to call MDL or DLL. 2

*      Using Java Native Interface to interact with MDL. 3

*      Java Native Interface to invoke the DLL function. 3

*      Calling the Java method from the native code. 4

*      Designing a native code to interact with MDL. 5

*      Designing a MDL application to interact with DLL. 5

*      Final words. 6

 

 

 

Introduction

 

  For the last several years, MicroStation developers have been using the C-style MDL language in which gigabytes of codes are written in the form of utility applications (.ma files), shared libraries (.msl –files), static libraries (.ml - files) and resource files (.r)  for graphic interfaces, command tables etc. What is more important, such MDL codes have been maintained and tested to have the best qualities for thousands of hours. There also exist a lot of supported native codes written in DLL to perform time-critical tasks in MicroStation. Such codes can be easily incorporated into MDL programs via dlm set of functions.

 

On the other side, there is a new, modern, easy to learn, platform-independent and object-oriented language called Java and its JMDL extension which enables rapid application development with a lot of advantages for MicroStation programmers such as the usage of pure Java classes from the third-party vendors, safety in memory management, scalability etc. However, Java or JMDL applications seem to have some deficiencies; for instance, they do not look and feel like the MDL application (docking, tool tips, color or level dialog box control etc.), or the standard Java class library does not support some platform-dependent features rarely required by your application, or their performance is low in case some time-consuming operation is to be optimalised.

 

Natural questions arose when Java was integrated into MicroStation/J : “How to communicate between MDL and Java applications?“, “How to access a low-level code from the Java environment?”, or “How to recall the Java code from MDL?”

 

The Java development kit delivered along with MicroStation/J contains documented and supported specifications of interface that allows the programs written in other languages to be called up from the Java code that runs within Java Virtual Machine. Vice versa, the native code is able to call up methods and access objects written in the Java language. This standard programming interface is well-known in the Java community and its name is Java Native Interface (JNI).

 

If we have read the article carefully up to now, we have already found the answers. Both MDL as well as Java have full, both-direction access to the native code. But it would not be sufficient to make the communication easy if the virtual machines for interpreting these two different codes would run in separate process space. Fortunately,  MDL runtime (ustation.dll) and Java/JMDL runtime (javai.dll)  are mapped and run within the process address space of MicroStation (ustation.exe). That is why the DLL native code attached to the Java application and then to the MDL application will receive only one DLL_PROCESS_ATTACH message from the first one. Thus, we can speak about an in-process communication between MDL and Java/JMDL.

 

 

 

Fig.1 Types of communication between Java/JMDL and MDL

 

 

 

Use the jmdl_import keyword to call MDL or DLL

 

 The easiest and most straightforward way of invoking a shared library written either in the MDL or a native code from a JMDL code is to use a mechanism that is very similar to the J/Direct technology. The jmdl_import keyword is a part of a native function declaration in which we specify the MDL or DLL library name and their exported functions which we are going to invoke. The jmdl method succeeding the jmdl_import statement must be declared as static.

An example of a JMDL method is mdl_printPromp which refers to a mdl shared library (mdlPrompt) and its function (printPrompt) :

              

jmdl_import (mdl = "mdlPrompt", name = printPrompt)

    public static native void mdl_printPrompt(byte    *showText);

 

Similarly, in order to invoke a native function from DLL, you can use the dll = <name of the native code library> instead of mdl =...:

 

jmdl_import (dll = "dllPrompt", name = printPrompt)

    public static native void dll_printPrompt(byte    *showText);

 

 

 

Using Java Native Interface to interact with MDL

 

  To use the JNI for accessing the MDL code we need an intermediate native layer for passing calls from Java to MDL and vice versa. This DLL “gateway” consists of the JNI interface to interact with Java on one side, and on the other side there must be an interface to communicate with the MDL code in a way of a dynamic link module.

 

 

       

  Java Native Interface to invoke the DLL function

 

  The JNI is quite a rich application interface that enables the interaction between  the native code and Java. If we are on Win32 platform, such a native code is represented by a regular DLL with exported functions which we are going to invoke. Through the JNI  we can:

 

·          call Java methods

·          call native methods

·          operate with a Java object

·          process exceptions

·          load classes and obtain their information

 

  Let’s suppose we have a class which will invoke a function in a native library called nativeLib.dll. First, Java must load the DLL library into memory and link to it via calling the System.loadLibrary. The only parameter of the method is a name of the desired dynamic library. The specified DLL must be stored in one of the system path directories or in the same directory containing a Java class file. The Java virtual machine will automatically append the appropriate extension according to the given platform so you do not need to write “.dll” extension  there. An example of the method is given below:

 

static {

    System.loadLibrary(" nativeLib");    // loading the library nativeLib.dll

   }

  public native void callDllFunc(short limit); // declaration of a native function

 

 

 

 

 Then to implement the  a Java method  in C++, we need to create a “Java to C++” mapping header file with function prototype of given method.  The task could be done simply by a standard Java Development Kit (JDK) utility javah.exe with the –jni command parameter and the name of the class that will be processed. The result is a declaration of native function placed in the header file named as the processed class. For instance, we can enter a command javah – jni JavaJni and the header file JavaJni.h” will contain a prototype for the function callDllFunc in the C++ code :

 

#include <jni.h> 

extern "C" {

JNIEXPORT jint JNICALL Java_JavaJni_callDllFunc  (JNIEnv *, jobject, jshort);

// declartion of native function

}

 

 The header file begins with an include directive for jni.h. In Jni.h there are among other things the data types and function prototypes of the JNI defined. The JNIEXPORT macro is defined in jni_md.h for Win32 platform as __declspec(dllexport). Such a function declaration means that we do not need an additional module-definition (.DEF) file of the exported functions. The JNICALL is expanded into __stdcall which expressesthe definition of the calling convention for Win32 API functions. The JNI imposes a naming style (so-called name mangling) on the native methods through which the Java Virtual Machine links the Java calls to the native methods. So the name of the native language function that implements a native method consists  of the Java keyword, followed by a package name (here we have a default package, hence, in our example, this part is omitted), then the class name  JavaJni, and finally the name of the native method  callDllFunc. Between each name part is an underscore "_" separator. A question may come up about the number of parameters in our Java_JavaJni_callDllFunc prototype where there are three arguments in total instead of an only one. These two additional arguments are passed from Java to all native functions using the JNI. The first of them is the Java Environment interface pointer containing all the necessary function pointers for the native processing of parameters and objects passed from Java. The second one is, in this case, a reference to the current instance of the JavaJni object – an equivalent to “this” in Java. These two parameters make a gateway to call back the Java method but only in the current context of calling. The third one is a corresponding argument to short type passed from the Java code.

The example code nativeLib.cpp  given below in which we implement the Java native function with an only short parameter is written in C ++ and displays the parameter value using the Win32 API:

 

#include "stdafx.h"

#include "JavaJni.h" // header file generated by the javah utility

 

BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason,LPVOID lpReserved){

   return TRUE; // dll entry point

}

JNIEXPORT jint JNICALL Java_JavaJni_callDllFunc

 (JNIEnv * env,jobject  jthis, jshort limit){

    char    message[255];

    sprintf(message,"Native Dll recieved value %d",limit);

    :: MessageBox(NULL,message,"nativeLib",MB_OK); // displaying a message

     return (jint) dll_callMdlFunc(limit); //  call to mdl will be explained soon 

}

 

 

Calling the Java method from the native code

 

 To invoke the Java non-static method from a native code, we need to do a little bit more of programming than before. First of all, we need to design a JavaJni class to implement the calling from the native code and compile it with javac utility to have a JavaJni.class. The class displays simple dialog box with a label item where will be reflected  passed  parameter  from a native function :

 

import java.awt.*;

public class JavaJni extends Frame{

   Label                        label    = new Label ("Label",Label.CENTER);

   static JavaJni         javaObj  = new JavaJni("Java dialog");

 

public JavaJni(String title){  // constructor to display a dialog box

   super(title);

   resize(150,100);

   setBackground(SystemColor.control);

   add(label); show();

}

 

public int onMdlCall(short limit){ // reciever  method of a MDL call via DLL

            label.setText("Native call, value  = " +limit);

            return true;      

            }

 

public static void main(String[] args)   {

           javaObj.show(); 

  }

}

 

 

Now we are prepared to implement calling from a native method.  

The necessary steps are listed below:

 

1.       Get a Java Virtual Machine (JVM) pointer

The JNI_GetCreatedJavaVMs function from the JNI library will successfully process your request if the JVM has already been started. The returned pointer will be used in the second task.

 

2.       Get an interface pointer

To obtain a valid interface pointer, a member function of the JVM pointer called AttachCurrentThread needs to be invoked.The function attaches the current thread to the JVM and returns the JNI interface. If the thread has already been attached, no operation is performed, just the valid interface pointer is returned.

 

 

3.       Retrieve a class reference of the called method

The JNI interface pointer received in the previous task includes all the necessary functions to operate with the JNI. One of them is FindClass which will realize the current task. The returned value is a representation of the searched class.

 

4.       Retrieve a method and object identifier

A method and object identifiers can be obtained from within a class. The identifier is requested when calling up the method. The methods of the JNI called GetMethodID and GetStaticFieldID accept the class object we received in the previous step, the method or object name of the Java code and its signature as parameters. To find out what signature is used for the given method, we need to run javap utility fromJDK as follows: javap –s JavaJni. Then, in the commentary below the method, we will find its signature. For instance, the public native int onMdlCall(short) method has the “(S)I” signature. Similarly, we may find the signature for a static javaObj object – “LJavaJni;

 

5.       Obtain a requested instance of an object

Because we designed the JavaJni object as a static one, we can use the GetStaticObjectField function to get a reference to it. The input parameters are represented by the class and object identifier. The returned value is represented by a reference to the static javaObj object.

 

6.       Invoke the non-static method

Now we know all the necessary gadgets to perform a call. There is a special function for each type of the calling method in Java. In our case we have designed a method returning the integer type, hence the CallIntMethod function is the right one. The input arguments must be represented by the object reference from the previous step, then the method identifier and the parameter expected by the Java method, in our case represented by the jshort value.

 

 

 

 

We can add now into the file nativeLib.cpp we used before an example of calling the Java method  onMdlCall via native function dll_javaMethodCall :

 

int  JNICALL dll_javaMethodCall(short limit)

{

 JavaVM            *jvm;       /* denotes a Java VM */

  JNIEnv             *env;       /* pointer to java native interface */

 jsize               nVMs;           /* number of created JVM, should be 1  */

  

/* 1.*/ jint res = JNI_GetCreatedJavaVMs(&jvm,(jsize)1,&nVMs);

/* 2.*/ res       = jvm->AttachCurrentThread(&env,NULL);

/* 3.*/ jclass    cls  = env->FindClass("JavaJni");

/* 4.*/ jmethodID mid  = env->GetMethodID(cls, "onMdlCall", "(S)I");

                jfieldID  fid  = env->GetStaticFieldID(cls, "javaObj", "LJavaJni;");

/* 5.*/     jobject   obj  = env->GetStaticObjectField(cls, fid);

/* 6.*/               return = env->CallIntMethod(obj, mid,limit);

}

 

 Now we have achieved the full interaction between Java and the native code. We are able to call up native functions from the Java environment as well as Java methods from the native code. Our task now is to create a bridge in the native code for interaction with the  MDL code.

 

 

Designing a native code to interact with MDL

 

  A technology for communicating between the MDL code and DLL is well-known and is described in the MicroStation help texts. Functions from the DLL code can be imported into the MDL code by declaring a function with the nativeCode specification. The native code is able to invoke MDL as well via the documented dlmSystem_callMdlFunction function.

Let’s create a C++ file “MDLLib.cpp” to operate with MDL. A first function dll_setMdlFuncCall accepts the mdl function offset as a parameter, gets a MDL task descriptor  and saves them into a global variable for later use. The second function  dll_callMdlFunc  will make use of the saved variables  and invoke a particular offset function of the MDL task  with one argument of the short type.

 

Te MDLLib.cpp file :

 

#include "stdafx.h"

extern "C"{                   // mdl communication include

#define   winNT

#include "mssystem.fdf"

#include "dlmsys.fdf"

}

 

mdlDesc             *mdlDescP = NULL; // Pointer to Call back the MDL module

MdlFunctionP       funcOffset = 0L;       // offset   to MDL function

 

 

int __stdcall  dll_setMdlFuncCall(MdlFunctionP mdlFunc){

  if ((funcOffset = mdlFunc) && (mdlDescP = mdlSystem_getCurrMdlDesc()))

     return true;

  return false;

}

 

int __stdcall dll_callMdlFunc(short limit){ // Invoker of  a MDL function

  if ((funcOffset) && (mdlDescP))

        return  dlmSystem_callMdlFunction(mdlDescP, funcOffset,limit);

      return false;

}

 

 Finally, we need to create a module-definition file (.DEF extension) to provide the linker with information about exported functions about the nativeLib.dll to be linked.

The Export.def file exports two functions:

The first one has just been described above, the second one is the Java method invoker function we designed in the section 2.2. Note that the previous JNI function of Java_JNIgo_callDllFunc is exported automatically.

 

 

LIBRARY      "nativeLib"

DESCRIPTION  'JAVA - MDL gateway library'

 

EXPORTS

dll_setMdlFuncCall             @1                 ; MDL function call back saver

            dll_javaMethodCall            @2                 ; Java method invoker

 

 

Designing a MDL application to interact with DLL

 

 Finally we must  design a MDL application to interact with the finished nativeLib.dll. First of all we need to map exported DLL function to the MDL import functions  conventions. The linker mlink.exe  needs for  native function references in MDL code  the Dynamic Link Specification object file (.DLS extension for source and .DLO for the precompiled object).  This object is treated as a static library and is linked with an MDL program. The MDL virtual machine automatically loads the specified DLL defined in DLO file when MDL application is loaded. Compare it  to the Java where the DLL must be explicitly loaded. The import.dls file reflecting just created  file Export.def:

 

%Version 0x700

%ModuleName nativeLib

%Functions

dll_setMdlFuncCall

dll_javaMethodCall

%EndFunctions

%End

 

 

Steps to design MDL code to be able to call DLL native functions and also to be called from the DLL  are listed below:

 

1)      Add the native declarations to the global part of the mdl module corresponding to the  function declarations in the Import.dls file.

 

nativeCode int dll_setMdlFuncCall(ULong);

nativeCode int dll_javaMethodCall(short);

 

 

2)      Create a function mdlOnJavaCall to be called from the native DLL library nativeLib.dll by the function  Java_JavaJni_callDllFunc which has been called by the Java class JavaJni from the method callDllFunc.

 

int mdlOnJavaCall ( short   limit)

{

    char    message[255];

    sprintf(message,"We got request on %d iterations",limit);

    mdlDialog_openMessageBox(DIALOGID_MsgBoxOK,message,

                             DIALOGID_MediumInfoBox);  // display info box

    // do some interations...

    return TRUE;

}

 

3)      Set the function hook offset of mdlOnJavaCall for the native code in the main entry point of a MDL

 

int main(int  argc, char   *argv[]){

 // some init of MDL code here...

 dll_setMdlFuncCall((ULong)mdlOnJavaCall ); // set a hook for DLL 

  return TRUE;

}

 

4)      Finally vice versa make a  call to the nativeLib.dll on some user input event  or whenever you like to call up the native function dll_javaMethodCall which will invoke Java method onMdlCall  from the class JavaJni

 

case DITEM_MESSAGE_BUTTON:

dll_javaMethodCall(i);// call a Java method via dll

  break;

 

 

Final words

 

 The technology of interaction between Java/JMDL virtual machine and MDL gives us possibility to utilize existing MDL code for new language platform without needs of rewriting an existing code into Java.  We can concentrate on new features of Java language when designing new applications without loosing low level accessing capability of our software. The examples given in the article are for learning purpose so I removed all errors and exeptions handling for shorter code. The code  was tested on MicroStation v. 07.01.01.36,  and native library was compiled using Microsoft Visual C++ 6.0.