![]() |
Languages »
C / C++ Language »
Howto
Intermediate
License: The Code Project Open License (CPOL)
Example of using Object-Oriented Java Native Interface in C++By Vitaly ShelestDemonstrates an easy way of creating and embedding Java objects into C++ code, with Object-Oriented JNI. |
VC6, Java, Win2K, WinXP, Dev
|
|
Advanced Search |
|
|
|
||||||||||||||||

As I am a lazy boy in writing code, I try to develop tools for reducing my efforts in programming. This article is a result of using the last tool developed by me.
Java Native Interface (JNI) is a set of low level C functions that are very complex to use. A great number of things should be written to support JNI code integrity. Using pure JNI looks like developing OLE modules in MS Visual C++ without using ATL tools and libraries. I have found dozens of products that partially solve this problem, but they all have some serious defects, like:
By developing the Object-Oriented JNI (OOJNI) SDK, I tried to solve the problems mentioned above. OOJNI completely hides the low level JNI specifics, making C++ programming with JNI simple and comfortable. This SDK preserves the Java style programming in C++.
Before you can compile and run the sample project, please make sure that you have properly installed the Java Development Kit (JDK) version 1.3.x or higher on your computer.
The code of this application was generated with MS Visual Studio and OOJNI Advanced Add-in for VC 6.0. Java class wrappers were created using the runtime library (rt.jar) of IBM JDK 1.3. To run this code, put the EXE and the OOJNI runtime (OOJNI.DLL) modules in the same directory.
The application code was generated with the MFC Application Wizard. EmbeddedAWTFrame Demo starts a dialog as the main window.
To use the Java objects from C++ code, I have generated C++ JNI wrappers with OOJNI Advanced Add-in for VC 6.0. Each C++ JNI class wraps specific Java class members that are used in my code (this option is supported by the tool):
| Java class used | C++ wrapper generated | Members wrapped |
java.awt.Button |
CPP_Java_Bridge::java:: awt::ButtonJNIProxy |
constructor:java.awt.Button(); |
method: | ||
java.awt.Color |
CPP_Java_Bridge::java:: awt::ColorJNIProxy |
field:java.awt.Color blue; |
java.awt.Panel |
CPP_Java_Bridge::java:: awt::PanelJNIProxy |
constuctor:java.awt.Panel();<CODE> |
method:java.awt.Component add(java.awt.Component c); | ||
method:void setBackground(java.awt.Color c); | ||
javax.swing. SwingUtilities |
CPP_Java_Bridge::javax:: swing::SwingUtilitiesJNIProxy |
method:static void invokeLater(java.lang.Runnable r); |
sun.awt.windows. WEmbeddedFrame |
CPP_Java_Bridge::sun::awt:: windows::WEmbeddedFrameJNIProxy |
constructor:sun.awt.windows. WEmbeddedFrame(int n); |
method:java.awt.Component add(java.awt.Component c); | ||
method:void setBounds(int x, int y, int w, int h); | ||
method:void show(); |
Each C++ wrapper does all the routine job hidden from the programmer. Look at the wrapper for the method setLabel from CPP_Java_Bridge::java::awt::ButtonJNIProxy:
void CPP_Java_Bridge::java::awt::ButtonJNIProxy:: setLabel(::jni_helpers::JStringHelper p0) { try{ if(setLabel_ID == 0) setLabel_ID = ::jni_helpers::JNIEnvHelper:: GetMethodID(clazz, "setLabel", "(Ljava/lang/String;)V"); ::jni_helpers::JNIEnvHelper::CallVoidMethod(*this, setLabel_ID, (jobject)p0); }catch(::jni_helpers::JVMException e){ throw ::jni_helpers::JVMException(jni_helpers:: CSmartString("CPP_Java_Bridge::java::awt:: ButtonJNIProxy::setLabel:\n") + e.getMessage()); }catch(...){ throw ::jni_helpers::JVMException("CPP_Java_Bridge" "::java::awt::ButtonJNIProxy::" "setLabel: System Error"); } }
This code gets the method ID and calls setLabel. It also makes all the C++ - to - Java conversions, and catches Java and C++ exceptions.
In the demo code, I also use a callback interface java.lang.Runnable (it has only one method void run()). For this interface, I generated its C++ implementation (with the tool above):
namespace CPP_Java_Bridge{ namespace java{ namespace lang{ #ifdef __JNIHDLL class __declspec(dllexport) RunnableJNIImpl: public jni_helpers::JNIInterface{ #elif __USEDLL class __declspec(dllimport) RunnableJNIImpl: public jni_helpers::JNIInterface{ #else class RunnableJNIImpl: public jni_helpers::JNIInterface{ #endif static const JNINativeMethod methods[]; static const jbyte bytes[]; static const char* _clazzName; public: RunnableJNIImpl(); virtual void run(){} }; namespace RunnableJNIImpl_Native{ extern "C" { JNIEXPORT void JNICALL run(JNIEnv *, jobject, jlong); }} }}}
The method void run() is virtual, empty, and should be overloaded for use:
class Runnable: public CPP_Java_Bridge::java::lang::RunnableJNIImpl { CPP_Java_Bridge::sun::awt:: windows::WEmbeddedFrameJNIProxy& frm; CPP_Java_Bridge::java::awt::ButtonJNIProxy& button1; CPP_Java_Bridge::java::awt::ButtonJNIProxy& button2; CPP_Java_Bridge::java::awt::PanelJNIProxy& panel; CRect rc; public: Runnable(CPP_Java_Bridge::sun::awt:: windows::WEmbeddedFrameJNIProxy& embFrm, CPP_Java_Bridge::java::awt::ButtonJNIProxy& b1, CPP_Java_Bridge::java::awt::ButtonJNIProxy& b2, CPP_Java_Bridge::java::awt::PanelJNIProxy& p): CPP_Java_Bridge::java::lang::RunnableJNIImpl() ,frm(embFrm) ,button1(b1) ,button2(b2) ,panel(p) { } void init(HWND hwnd, CRect& rc) { CPP_Java_Bridge::sun::awt::windows:: WEmbeddedFrameJNIProxy tmp((long)hwnd); frm = JObject_t(tmp); this->rc = rc; } void run(); };
The application starts a dialog as the main window, with Java components embedded in it. To compile the code, I added this line to the EmbeddedAWTFrameDlg.h file:
#include "defproxies.h"
It includes all the referenced OOJNI headers. For activating the Java engine in the CEmbeddedAWTFrameApp::InitInstance() function, I call jni_helpers::JVM::load(). This method loads the default JVM installed on your computer:
BOOL CEmbeddedAWTFrameApp::InitInstance()
{
AfxEnableControlContainer();
// Standard initialization
// If you are not using these features
// and wish to reduce the size
// of your final executable,
// you should remove from the following
// the specific initialization
// routines you do not need.
#ifdef _AFXDLL Enable3dControls();
// Call this when using MFC in a shared DLL
#else Enable3dControlsStatic();
// Call this when linking to MFC statically
#endif
jni_helpers::JVM::load();
CEmbeddedAWTFrameDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here
// to handle when the dialog is
// dismissed with OK
} else if (nResponse == IDCANCEL)
{
// TODO: Place code here
// to handle when the dialog is
// dismissed with Cancel
}
// Since the dialog has been closed,
// return FALSE so that we exit the
// application, rather than start
// the application's message pump.
return FALSE;
}
The dialog's constructor creates the Java GUI objects, and constructs the Runnable object with the GUI objects created:
CEmbeddedAWTFrameDlg::CEmbeddedAWTFrameDlg(CWnd* pParent /*=NULL*/) : CDialog(CEmbeddedAWTFrameDlg::IDD, pParent) ,embFrm((jobject)0) ,run(embFrm, button1, button2, panel) { //{{AFX_DATA_INIT(CEmbeddedAWTFrameDlg) // NOTE: the ClassWizard will // add member initialization here //}}AFX_DATA_INIT // Note that LoadIcon does not require // a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); }
On the dialog's WM_SHOWWINDOW event, the Java GUI objects are embedded to the dialog. For demonstrating the usage of the Java callback interface in C++, I do this embedding asynchronously (by calling javax.swing.SwingUtilities.invokeLater with the run object):
void CEmbeddedAWTFrameDlg::OnShowWindow(BOOL bShow, UINT nStatus) { CDialog::OnShowWindow(bShow, nStatus); if(IsWindow(m_hWnd)) { CRect rc; CRect rcDlgRc; CRect rcDlgCl; GetWindowRect(&rcDlgRc); GetClientRect(&rcDlgCl); m_oStatic.GetWindowRect(&rc); ScreenToClient(&rc); run.init(m_hWnd, rc); CPP_Java_Bridge::javax::swing:: SwingUtilitiesJNIProxy::invokeLater(run); } }
The JVM calls the Runnable::run() function from EventQueue, and here I do the main things:
void Runnable::run() { HWND h = (HWND)jni_helpers::JNIEnvHelper:: getNativeFromComponent(frm); ::MoveWindow(h, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); button1.setLabel("Java Button1"); button2.setLabel("Java Button2"); panel.add(button1); panel.add(button2); panel.setBackground(CPP_Java_Bridge::java:: awt::ColorJNIProxy::get_blue()); frm.add(panel); frm.show(); }
This function gets the WEmbeddedFrame object's window handle to resize it, fills the Panel object with two push buttons, sets its background color to blue, and inserts the Panel to the WEmbeddedFrame object, which is shown.
You can modify the example to embed SWING components into JNI code. But because of a bug in WEmbeddedFrame class SWING components will not respond to key/mouse events. Having been created WEmbeddedFrame object has no LightweightDispatcher associated with it. This dispatcher should redirect events to lightweight components inside the heavyweight container. The dispatcher should be created explicitly in JNI code just after constructing WEmbeddedFrame object. Do this in the function Runnable::init():
void init(HWND hwnd, CRect& rc) { CPP_Java_Bridge::sun::awt::windows:: WEmbeddedFrameJNIProxy tmp((long)hwnd); // Create LightweightDispatcher object to enable // lightweight component in WEmbeddedFrame. We use // java::awt::ContainerJNIProxy to access its private // member field 'dispatcher' java::awt::ContainerJNIProxy c(tmp); c.dispatcher = java::awt::LightweightDispatcherJNIProxy((java::awt::Container)tmp); frm = JObject_t(tmp); this->rc = rc; }
The wrappers java::awt::ContainerJNIProxy and java::awt::LightweightDispatcherJNIProxy are generated with OOJNI Tool for classes java.awt.Container and java.awt.LightweightDispatcher.
I would like to express my deep gratitude to my friend Igor Ladnik for giving me remarks of material significance.
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 3 Feb 2006 Editor: Rinish Biju |
Copyright 2006 by Vitaly Shelest Everything else Copyright © CodeProject, 1999-2009 Web15 | Advertise on the Code Project |