Integrate Windows Local Application into eclipse RCP Program

Source: Internet
Author: User

Http://www.ibm.com/developerworks/cn/opensource/os-cn-eclrcp/index.html? CA = drs-cn-0605

Windows applications are rich, and sometimes some functions required by our eclipse RCP program have been implemented by some existing windows local applications, we hope to be able to reuse these features in our RCP program. One of the simplest reuse methods is to embed a local application window in our RCP window. To enable a Windows Local Application to run in our RCP program, we can use the reparent mechanism provided by windows. The main process of implementing window embedding using this mechanism is: first, start the windows program to be embedded in our program, and then we try to get the main window handle after the program starts, then, set the RCP program window to the parent window of the Windows program main window.

Because we need to start a Windows Local program and obtain its main window handle, these can only be implemented using Windows Local call, so we first use Windows Local call to implement the corresponding function, then we use JNI for calling.

JNI Introduction

The full name of JNI is Java Native interface, and the JNI standard is part of the Java platform. It is used to interact Java code with code written in other languages. The following describes how to use JNI:

Compile a Java method with native Declaration

The following uses helloworld as an example:

Listing 1. Hello World Java code

                
public class HelloWorld {
static {
System.loadLibrary(“helloworld”);
}

public native void print();

public static void main(String[] args) {
HelloWorld hello = new HelloWorld();
hello.print();
}
}

Compile the Java code and generate the C/C ++ header file:

Compile this Java class: javac helloworld. java, and then generate the extension. h header file. Java provides the command javah to generate the header file: javah-JNI helloworld. The following list shows the content of the generated header file:

Listing 2. Hello World C ++ header file

                
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: print
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

Use C/C ++ to implement local methods and compile them into dynamic library files

The header file of C/C ++ has been generated. The function declared in the header file is implemented below. The specific implementation code is shown in the following list, in the sample code, only one line of text "helloworld" is output ":

Listing 3. Hello World C ++ implementation code

                
#include "HelloWorld.h"
#include <stdio.h>

JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv * env, jobject obj)
{
printf("Hello World");
}

The next step is to compile the C ++ code into a dynamic library file. In the helloworld. cpp file directory, use the VC compiler CL command to compile:

CL-I % java_home %/include/Win32-LD helloworld. cpp-fehelloworld. dll

Note: The generated DLL file name is configured after option-Fe. Here is helloworld. dll, because the previous name we used in the helloworld. Java file loadlibary is helloworld. Make sure that the name here is the same as the name of the previous load. In addition, you need to add the-I % java_home %/include/Win32 parameter, because JNI is introduced when compiling the local method in step 4. h file, so you need to add the path of these header files here.

After completing these steps, you can run this program: Java helloworld. The result is the output string "helloworld" on the console ".

Implementation window reparent

The previous section describes how to use JNI. Next, we will introduce how to use JNI to start a Windows Local Application and set its main window as a subwindow of the specified window. First, create a Java class, as shown in the following list:

public class ReparentUtil {
static{
System.loadLibrary("reparent");
}
public static native int startAndReparent(int parentWnd,
String command,String wndClass);
}

Here, system. loadlibrary ("reparent") is used to load the dynamic library named reparent. We will use the specific implementation method startandreparent (…) in this dynamic library (...).

Startandreparent defines a method to start a Windows program and reparent its window to the window we specified. Where:

  • Int parentwnd: parent window handle
  • String command: Windows program startup command
  • String wndclass: Windows program Main Window Type

Some programs will create multiple top-level windows after startup, so we need to specify a main window type here to distinguish different top-level windows. This method is a local method. We will use C ++ to generate a dynamic library named reparent. dll. This method exists in this dynamic library.

The C ++ function corresponding to this Java function is java_com_reparent_reparentutil_startandreparent (jnienv * ENV, jclass classobj, jint parent, jstring command, jstring wndclass). This function mainly implements two functions:

  • Start Windows applications;
  • Obtains the main window handle of a Windows application;
  • Set the windows application main window to the Child Window of the specified window.

Start Windows Applications

Next let's take a look at the implementation of the Windows application. We will first convert the Java string parameter passed in by the function into a C string. This process is mainly implemented by getstringchars.

JNIEXPORT jint JNICALL Java_com_reparent_ReparentUtil_startAndReparent
(JNIEnv *env, jclass classobj, jint parent, jstring command,
jstring wndClass){
jboolean isCopy=FALSE;
PROCESS_INFORMATION pInfo;
STARTUPINFO sInfo;

int hParentWnd;

jsize len = ( *env ).GetStringLength(command);
const jchar *commandstr = (*env).GetStringChars(command,&isCopy);
const jchar *wndClassStr = NULL;
char commandcstr[200];
int size = 0;
size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)commandstr,
len, commandcstr,(len*2+1), NULL, NULL );
(*env).ReleaseStringChars(command, commandstr);
if(size==0){
return 0;
}
commandcstr[size] = 0;

if(wndClass!=NULL){
wndClassStr = (*env).GetStringChars(wndClass,&isCopy);
if(wndClassStr!=NULL){
len = (*env).GetStringLength(wndClass);
size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)wndClassStr,
len, wndClassName,(len*2+1), NULL, NULL );
wndClassName[size] = 0;
(*env).ReleaseStringChars(wndClass, wndClassStr);
}
}

Next, we use the Windows API: CreateProcess function to start the application we want to integrate.

sInfo.cb                     =   sizeof(STARTUPINFO);   
sInfo.lpReserved = NULL;
sInfo.lpReserved2 = NULL;
sInfo.cbReserved2 = 0;
sInfo.lpDesktop = NULL;
sInfo.lpTitle = NULL;
sInfo.dwFlags = 0;
sInfo.dwX = 0;
sInfo.dwY = 0;
sInfo.dwFillAttribute = 0;
sInfo.wShowWindow = SW_HIDE;

if(!CreateProcess(NULL,commandcstr,NULL,NULL, TRUE,0,NULL,NULL,&sInfo,&pInfo))
{
printf("ERROR: Cannot launch child process/n");
release();
return 0;
}

The CreateProcess function is defined as follows:

BOOL CreateProcess (
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes。
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);

Lpapplicationname: a string that points to a null end and is used to specify the executable module. Lpcommandline: Specifies the command line to be run at the end of a null value. Lpprocessattributes: points to a security_attributes struct, which determines whether the returned handle can be inherited by the quilt process. Lpthreadattributes: points to a security_attributes struct, which determines whether the returned handle can be inherited by the quilt process. Binherithandles: indicates whether the new process inherits the handle from the called process. Dwcreationflags: Specifies the appended identifier used to control priority classes and Process Creation. Lpenvironment: point to the Environment block of a new process. Lpcurrentdirectory: point to a null-ending string used to indicate the working path of the child process. Lpstartupinfo: point to the startupinfo struct used to determine how the new process's main form is displayed. Lpprocessinformation: point to a process_information structure used to receive the recognition information of new processes.

Obtains the main window handle of an application.

To obtain the main window handle of the started program, we need to use a Windows system hook to intercept the events created in the window before calling CreateProcess:

hHook = SetWindowsHookEx(WH_SHELL, ShellProc,(HINSTANCE)hDllHandle,NULL);

Here, the hook type we use is wh_shell. These hooks can intercept all events created or activated in top-level windows. The second parameter of the function is the event processing function. Our processing function is shellproc. We will introduce it later.

After the application is started, we need to obtain the main window of the application to continue running. Synchronization between processes is required here. In our main process, we need to wait. After the main window of the application is created, we send a message to notify our main process to continue execution.

We use Windows events for synchronization. We first call createevent to create an event, and then call waitforsingleobject () to wait for the event status to change. In our shellproc processing function, once we get the handle of the application's main window, we will change the event status to notify the main process to continue executing.

The following code creates an event. We have created an event named Global/waitwindowcreatedevent:

SECURITY_ATTRIBUTES secuAtt;
secuAtt.bInheritHandle = TRUE;
secuAtt.lpSecurityDescriptor = NULL;
secuAtt.nLength = sizeof(SECURITY_ATTRIBUTES);
hEvent = CreateEvent(&secuAtt,FALSE,FALSE,TEXT("Global/WaitWindowCreatedEvent"));

Wait for the event status to change and call the following code:

WaitForSingleObject(hEvent,1000*60);

To avoid infinite waiting, we set the maximum waiting time to 60 seconds.

Next let's look at the shellproc processing code. In this function, we mainly want to obtain the main window of the application. According to the definition of Windows wh_shell Hook, the first parameter of the hook processing function is the event type, and the second parameter is the window handle. First, determine whether the window type is hshell_windowcreated, and then determine whether the process Number of the corresponding window is equal to the application we started. If you need to determine the window type. Once we find the main application window, we call setevent to notify the main process to continue execution.

LRESULT CALLBACK ShellProc(int nCode,WPARAM wParam,LPARAM lParam){
if(nCode==HSHELL_WINDOWCREATED && childInstanceId!=0){
HWND hwnd=HWND(wParam);
DWORD pid;
HANDLE childEvent;
char classname[100];
GetWindowThreadProcessId(hwnd,&pid);
if(pid==childInstanceId){
if(wndClassName[0]!=0){
int count = GetClassName(hwnd,classname,100);
classname[count] = 0;
if(strcmp(classname,wndClassName)!=0){
return CallNextHookEx(hHook, nCode,
wParam, lParam);
}
}
hChildWnd = hwnd;
ShowWindow(hChildWnd,SW_HIDE);
childEvent = OpenEvent(EVENT_ALL_ACCESS,
TRUE,TEXT("Global/WaitWindowCreatedEvent"));
if(childEvent!=0){
SetEvent(childEvent);
}
}
}
return CallNextHookEx(hHook, nCode, wParam, lParam);
}

Set the windows application main window to a subwindow of the specified window

After obtaining the main window handle of the application, at the end of the java_com_reparent_reparentutil_startandreparent function, we call the setparent function of windows to set it as our subwindow, at the same time, adjust the size of the application window so that it can be displayed in our window. To avoid window flickering, we first hide the window, reparent, and then display it. To remove the window bar of the application, we need to change the window type of the application to ws_popup.

if(hChildWnd!=0){
RECT rect;
GetWindowRect((HWND)hParentWnd,&rect);
ShowWindow(hChildWnd,SW_HIDE);
SetParent(hChildWnd,(HWND)hParentWnd);
SetWindowPos(hChildWnd,(HWND)0,0,0,
rect.right-rect.left,rect.bottom-rect.top,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS |
SWP_SHOWWINDOW | SWP_NOSENDCHANGING | SWP_DEFERERASE);
SetWindowLong(hChildWnd,GWL_STYLE,WS_POPUP);
ShowWindow(hChildWnd,SW_SHOW);
}

Wrap windows application windows to SWT controls

After the startandreparent method is implemented, we can embed a Windows Local Application into our SWT window as long as the SWT window handle is passed in. For ease of use, we can wrap a Windows Local Application into a SWT control, so that we can use the windows of a Windows application just like using a common SWT control. Next we will look at how to pack windows application windows.

First, we define a control that inherits from canvas. We use it as the parent window of the local application window and manage it at the same time. We mainly need to manage the following aspects:

  • Window Creation: When we create a SWT window, we need to create a local application window.
  • Window destruction: When we destroy the SWT window, we also need to destroy the local application window.
  • Focus control: when the focus is obtained in our SWT window, we need to set the focus to the local application window.
  • Window Size Change: when the position or size of our SWT window changes, we need to notify the local application window to change its position or size.

First, let's look at the creation and destruction of the window. We need to listen to the painting and dispose events of the SWT window, create a local application window in the response to the paint event, and close the local application window in the response to the dispose event. It may take a long time to create a local application window. To avoid blocking the UI thread, we put it in a thread for execution. The following is a list:

public class NativeControl extends Canvas{
private int childWnd = 0;
private String startCommand = null;
private String wndClassName = null;

private boolean isCreatingNative = false;

public NativeControl(Composite parent, int style) {
super(parent, style);
this.addPaintListener(new PaintListener(){

public void paintControl(PaintEvent arg0) {
this.addPaintListener(new PaintListener(){

public void paintControl(PaintEvent arg0) {
if(childWnd==0 && !isCreatingNative){
isCreatingNative = true;
Thread thread = new Thread(){
public void run(){
childWnd = ReparentUtil.startAndReparent(
NativeControl.this.handle,startCommand,wndClassName);

}
};
thread.start();
}
}
});
}
});
this.addDisposeListener(new DisposeListener(){

public void widgetDisposed(DisposeEvent arg0) {
if(childWnd!=0){
OS.SendMessage(childWnd, OS.WM_CLOSE, 0, 0);
}
}

});

Call reparentutil. startandreparent (nativecontrol. This. Handle, startcommand, wndclassname) in the paintcontrol (paintevent arg0) function to start the windows application and display the application window to the SWT control. When the SWT space is destroyed, windows of Windows applications must also be destroyed. The swt OS class provides the sendmessage method to destroy the window: OS. sendmessage (childwnd, OS. wm_close, 0, 0); childwnd is the handle of the window to be destroyed.

The control of window focus is similar to that of window destruction. We first listen to the focus event of the parent window. Once we get the focus, we set the focus to the window of the local application. At the same time, we need to add a keyboard event listener so that when you press the "tab" key, the focus will jump to our parent window control. The following is a list:

this.addFocusListener(new FocusListener(){

public void focusGained(FocusEvent arg0) {
if(childWnd!=0){
OS.SetForegroundWindow(childWnd);
}
}

public void focusLost(FocusEvent arg0) {

}

});
this.addKeyListener(new KeyListener(){

public void keyPressed(KeyEvent arg0) {


}

public void keyReleased(KeyEvent arg0) {


}

});

The swt OS class provides the setforegroundwindow function to set the focus to a window. The parameter of the function specifies the window handle for setting the focus.

The window size control is similar. We need to listen to window events of the parent window. Once the window size changes, we will adjust the window size of the local application.

this.addControlListener(new ControlListener(){

public void controlMoved(ControlEvent arg0) {

}

public void controlResized(ControlEvent arg0) {
if(childWnd!=0){
Rectangle rect = ((Composite)(arg0.widget)).getClientArea();
OS.SetWindowPos(childWnd, 0, rect.x, rect.y, rect.width, rect.height,
OS.SWP_NOZORDER| OS.SWP_NOACTIVATE | OS.SWP_ASYNCWINDOWPOS);
}
}

});

Similarly, we use the functions provided by SWT to set the window size and position. The setwindowpos parameters are the window handle to be set and the window location size respectively.

Finally, we need to add some methods so that you can set the command for starting the application and the window type of the application.

public void setStartParameters(String startCommand,String wndClassName){
this.startCommand = startCommand;
this.wndClassName = wndClassName;
}

public String getStartCommand() {
return startCommand;
}



public void setStartCommand(String startCommand) {
this.startCommand = startCommand;
}



public String getWndClassName() {
return wndClassName;
}



public void setWndClassName(String wndClassName) {
this.wndClassName = wndClassName;
}

In this way, we developed a SWT control, which can start the specified Windows Local Application and embed the window of the program into the control. The use of this control is the same as that of a common SWT control. The only difference is that the setstartparameters () method must be called before the window is displayed to set the startup command and window type of the Windows Local application.

The following is a simple example of embedding Windows Messager in our SWT window.

public class ReparentTest {

/**
* @param args
*/
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Test dialog");
GridLayout layout = new GridLayout();
layout.numColumns = 1;
shell.setLayout(layout);

Button button = new Button(shell,SWT.None);
button.setLayoutData(new GridData());
button.setText("Test");
NativeControl control = new NativeControl(shell,SWT.NONE);
GridData data = new GridData(GridData.FILL_BOTH);
data.widthHint = 200;
data.heightHint = 200;
data.grabExcessHorizontalSpace = true;
data.grabExcessVerticalSpace = true;
control.setLayoutData(data);
control.setStartParameters
("C://Program Files//Messenger//Msmsgs.exe","MSBLClass");
shell.open();
while(!shell.isDisposed()){
if(!display.readAndDispatch()){
display.sleep();
}
}
}

}

Setstartparameters () method is used to set the path of the program to be started and the window type of the program. Here we start MSN and the corresponding window type is msblclass:

control.setStartParameters("C://Program Files//Messenger//Msmsgs.exe","MSBLClass");

The following is the result of code display. We can stretch and change the size of the window, and the size of the Messager window will also change. When the focus is on the test button, press the "tab" key and the focus will jump to the Messager window.

Figure 1. Image example

Summary

This article introduces the technologies related to integrating a local application window into the eclipse RCP window. The integration of third-party applications discussed in this article is simple because we do not know the code of third-party applications. For example, the menu of the local application is displayed in our SWT parent window, rather than in the main menu of the eclipse RCP application. Sometimes, we also need to integrate our own local application into the eclipse RCP program. The implementation principle is the same as described in this article. The difference is that we can implement more control over our local applications to achieve closer integration. For example, our local application can provide APIs for RCP programs to obtain their own main menus and display their main menus in the main menus of RCP programs.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.