Introduction
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 CGUIBeanDlg::OnInitDialog()
.
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 Attach_JavaFrame()
.
1 void CEmbeddedJFrame::init()
2 {
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";
7
8
9 theAdapter.initf(beanjar, "codeproject", "Editor");
10 JNIEnv *env = theAdapter.getJVMLoader()->getENV();
11
12
13 srand((unsigned)time(NULL));
14 CString title;
15 title.Format("bean_Editor_%d%d", rand(), rand());
16
17
18 frame = new JFrame((const char*)title);
19
20 frame->setBounds(-100,-100,0,0);
21 frame->show();
22 frame->setVisible(JNI_FALSE);
23 frame->add(theAdapter.getBeanInstance());
24
25
26 Attach_JavaFrame((const char*)title);
27
28 dockundock();
29
30
31 frame->setVisible(JNI_TRUE);
32 }
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_NCHITTEST
and 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 InitApplication()
.
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.
Links
History
Version 1.0 - Stable on Win2K/XP except one major problem (see above).