Some weeks ago, I was wondering how Sun's ActiveX-Bridge was embedding JavaBeans in MFC-Dialogs. After reading some newsgroup articles on that topic, I came to the conclusion that there was no real working example of embedding an own JavaBean in a Dialog by means of the MFC. So I decided to write my own JavaBridge and here is the outcome of my efforts.
But first of all, please excuse me for any mistakes in my written English or the structure of this article, because this is the first article on CodeProject I have ever written.
Using the code
Before you can run the sample project, please make sure that you have properly installed the Java Software Development Kit (SDK) version 1.4.2 or higher on your computer. You can get the SDK from here.
Please also take care, that the
JAVA_HOME environment variable is set to the appropriate directory on your system.
I am using the Java Native Interface (respectively the Invocation Interface) for loading the Java Virtual Machine and instantiating the JavaBean. For a better understanding, I divided the classes into two groups. The first group consists of classes covering the JNI issue. You will find them in the subfolder JBridge. Don't bother too much with those classes, because this article is not intended to be an introduction on JNI. You will find proper papers on that topic in the web (see Links below).
The other group of classes cover the MFC issue. Especially
CEmbeddedJFrame will be the main point of our interest.
When you start the application, an instance of
CEmbeddedJFrame will be created and the
init() member function will be invoked from within
My idea was to create the JavaBean, to embed it in a
JFrame instance and to make this frame object a child window of my dialog. The code fragment below shows where this is implemented.
The Java VM and the bean are loaded in line 9. As you can see, the bean is delivered by a Java archive file called bean.jar. You will find this file in the bean subdirectory from your project's working directory. After instantiating the bean, the
JFrame object is created. Because I did not want to bother you with JNI details, I wrapped the appropriate JNI invocations in the
JFrame class from the JBridge subfolder. Before the frame is shown for the first time, the bean instance is added to the content pane of that frame (see line 23).
For making the frame to a child window of the dialog, it is essential to get the
HWND of it. There are two ways (let me better say: two ways I know of) to get the
HWND of a Java object from a native application. The first approach is by using JNI to get a pointer to the JAWT interface. This interface provides some functions for getting the drawing surface of a graphics object like
JFrame. You can identify the
HWND from this drawing surface. But when I implemented this, it was a very unstable and error-prone solution. So I decided to do it the dirty way by searching the appropriate title of the frame. This is done in the operation
1 void CEmbeddedJFrame::init()
3 TCHAR szExePath[MAX_PATH];
4 GetModuleFileName(AfxGetInstanceHandle(), szExePath, MAX_PATH);
5 CString beanjar = szExePath;
6 beanjar = beanjar.Left(beanjar.ReverseFind('\\') + 1) + "bean\\bean.jar";
8 9 theAdapter.initf(beanjar, "codeproject", "Editor");
10 JNIEnv *env = theAdapter.getJVMLoader()->getENV();
14 CString title;
15 title.Format("bean_Editor_%d%d", rand(), rand());
17 18 frame = new JFrame((const char*)title);
25 26 Attach_JavaFrame((const char*)title);
30 31 frame->setVisible(JNI_TRUE);
Finally, this Java frame is made a child window of the dialog by setting the
WS_CHILDWINDOW style and the right parent. Please take a closer look into the
dockundock() member function.
Known Bugs (your help is welcome
In this example, the JavaBean is directly embedded in the dialog and everything is OK. But if you put that
CEmbeddedJFrame in a
CTabCtrl, then AWT sends hundreds of
WM_GETDLGCODE messages (per second!) to that container. Due to that message flooding, the CPU usage increases up to 100% and the whole system hangs.
My first assumption was that this effect has something to do with the container, in this case the
CTabCtrl. I decided to embed the
CEmbeddedJFrame in a
CDialog anytime, using the
CDialog as an intermediate layer. But when I forced the
CDialog to be a child window of
CTabCtrl, it ended up in the same system deadlock.
If you want to see this effect, please try to embed the
CEmbeddedJFrame in a
CTabCtrl. You can do this easily in the MSVC resource editor. But be careful. When you are using Spy++ and start the application in a debug session, then your system may hang at the beginning of
Any contribution of any kind is welcome on this topic. If you have some ideas why this may happen, please don't hesitate and post a comment.
Version 1.0 - Stable on Win2K/XP except one major problem (see above).