Home News Products Download Buy Support About JNIEasy Download JNIEasy Buy JNIEasy JNIEasy Support
JNIEasy Tutorial : a Win32 Example
Introduction
Where to start
Overview of the steps to create the window
Window class registration
The WndClassEx structure class
The WindowProc callback class
Window class registration (cont.)
Proxy code generation (User32 class)
Verification of the window class registration
Window creation
Processing messages
Unregistering the window class
Conclusion

Last update: 2008 January 28  

Introduction

In this tutorial we will see how to create a Win32 window. The process to create a window in Java with JNIEasy is not very much different to create the same in a C/C++ environment, basically the Win32 C elements needed (structures, constants, method headers) will be copied in the Java side and "Javatized" (removing the * of pointers, data type changes etc). People familiarized with Win32 programming will see the code absolutely familiar.

The source code in this example is included in the JNIEasy's distribution and ready to be executed with the provided .bat with minor configuration changes (program locations) or with Ant with several minor folder changes in the /conf/conf.properties file: properties MSCInitEnv, LinuxEmuBin and GCC_Win32_x86 if you want to compile again the DLL (is already compiled) with the native C/C++ examples (Visual C++ 6+ or MinGW/MSYS or cygwin is needed). This example is pure Java but is included alongside C/C++ based examples. If you have NetBeans 5.0 or upper the distribution folder is ready to be opened as a project, if you have any problem with previous versions remove the generated /nbproject folder, the Ant build.xml provided is not based on Ant tasks specific to NetBeans.

This tutorial shows many features of JNIEasy and the most transparent way of native programming with the framework, but is not a full featured example (there is no C++ classes for instance). The framework offers a very rich class model to a fine grain control of the native memory too, see the official documentation, online and bundled with the distribution, and the remaining examples included in the distribution too.


Where to start

The execution of the example starts with the main method of RunExamples class.

        package examples;
        import com.innowhere.jnieasy.core.JNIEasy;
        import examples.manual.ManualExamples;
        import examples.win32exam.RunWin32Examples;

        public class RunExamples
        {
            public static void main(String[] args) throws Exception
            {
                JNIEasy.get().load();

                ManualExamples.runAll();
                RunWin32Examples.createWin32Window();
            }
        }
        

The call JNIEasy.get().load() initializes the JNIEasy framework, this must be the performed before any use of the JNIEasy classes. The method JNIEasy.get() obtains the JNIEasy singleton object, this is the root to obtain most of the framework objects.

The execution of this Win32 example starts in the call RunWin32Examples.createWin32Window().


Overview of the steps to create the window

The class RunWin32Examples:

        package examples.win32exam;

        import com.innowhere.jnieasy.core.util.NativeCapableUtil;
        import examples.win32exam.win32.user.User32;
        import examples.win32exam.win32.user.WndClassEx;

        public class RunWin32Examples
        {
            public RunWin32Examples()
            {
            }

            public static void createWin32Window()
            {
                short winClassAtom = WindowExample.registerWindowClass();

                testWindowClassRegistration(); // optional

                WindowExample window = WindowExample.createWindow();
                window.show();

                WindowExample.waitMessages();

                WindowExample.unregisterWindowClass();
            }

            public static void testWindowClassRegistration()
            {
                WndClassEx wndClass2 = new WndClassEx();
                int hInst = WindowExample.getModuleInstance();
                int res = User32.GetClassInfoEx(hInst,WindowExample.className,wndClass2);
                System.out.println("Must be true: " + (res != 0));
                System.out.println("Must be true: " + WindowExample.className.equals(wndClass2.getClassName()));
                System.out.println("Must be true: " + (WindowExample.wndProc == wndClass2.getWndProc()));
                System.out.println("Must be true: " + (NativeCapableUtil.sizeOf(wndClass2) == 48));
            }
        }
        

The method createWin32Window() performs the window creation and destruction.

First the window class to be used in our window is registered calling the method WindowExample.registerWindowClass().

Then this registry is verified using a new WndClassEx object, this class is the Java view of the native Win32 WNDCLASSEX structure.

The call WindowExample.createWindow() creates the new window, this window is not visible by default. To make it visible the method show() is called.

In this moment the window is shown and ready to listen events, the call WindowExample.waitMessages() enters the current thread in a loop to listen and dispatch the Windows events received, this loop ends anr returns when the window is closed by the user (or by Windows).

Finally the call WindowExample.unregisterWindowClass() unregisters the window class.


Window class registration

The window class registration is performed by the method WindowExample.registerWindowClass(), this is the code:

        package examples.win32exam;

        import com.innowhere.jnieasy.core.JNIEasy;
        import com.innowhere.jnieasy.core.util.NativeCapableUtil;
        import examples.win32exam.win32.gdi.*;
        import examples.win32exam.win32.user.*;

        public class WindowExample
        {
            public static final WindowProc wndProc = new WindowProcConcrete();
            public static final String className = "JNIEasy Demo Class";

            protected int hWnd;

            public WindowExample(int hWnd)
            {
                this.hWnd = hWnd;
            }

            public static int getModuleInstance()
            {
                return (int)JNIEasy.get().getJNIEasyLib().getHandle();
            }

            public static short registerWindowClass()
            {
                int hInst = getModuleInstance();

                WndClassEx wndClass = new WndClassEx();
                JNIEasy.get().getNativeManager().makeNative(wndClass);
                wndClass.setCBSize((int)NativeCapableUtil.sizeOf(wndClass));
                wndClass.setStyle( CSConst.CS_HREDRAW | CSConst.CS_VREDRAW );
                wndClass.setWndProc( wndProc );
                wndClass.setCBClsExtra( 0 );
                wndClass.setCBWndExtra ( 0 );
                wndClass.setHInstance(hInst);
                wndClass.setHIcon(User32.LoadIcon(0,IDIConst.IDI_APPLICATION));
                wndClass.setHCursor(User32.LoadCursor(0,IDCConst.IDC_ARROW));
                wndClass.setHbrBackground(GDI32.GetStockObject(GDIStockConst.WHITE_BRUSH));
                wndClass.setMenuName(null);
                wndClass.setClassName(className);
                wndClass.setHIconSm(User32.LoadIcon(0,IDIConst.IDI_APPLICATION));

                short winClassAtom = User32.RegisterClassEx(wndClass);
                return winClassAtom;
            }

            . . .
        }
        

The call getModuleInstance() obtains the DLL handle where the WindowProc callback resides, this callback is developed with Java but in the native side is seen as a function compiled inside the JNIEasy.dll library (of course it is not really true, but the memory is "owned" by JNIEasy.dll). The implementation of getModuleInstance() is:

            public static int getModuleInstance()
            {
                return (int)JNIEasy.get().getJNIEasyLib().getHandle();
            }
        

where the call getJNIEasyLib() obtains a DynamicLibrary object describing the framework DLL. The DLL module handle is obtained with getHandle().


The WndClassEx structure class

To register the window class is necessary to fill a Win32 WNDCLASSEX structure, this is reflected in Java with the class WndClassEx, this class is absolutely symmetric to the native structure:

        /*
        typedef struct _WNDCLASSEX { 

            UINT    cbSize;
            UINT    style; 
            WNDPROC lpfnWndProc; 
            int     cbClsExtra; 
            int     cbWndExtra; 

            HANDLE  hInstance; 
            HICON   hIcon; 
            HCURSOR hCursor; 
            HBRUSH  hbrBackground; 
            LPCTSTR lpszMenuName; 

            LPCTSTR lpszClassName; 
            HICON   hIconSm;
        } WNDCLASSEX; 
         */

        public class WndClassEx
        {
            protected int cbSize;                   // UINT    cbSize  

            protected int style;                    // UINT    style
            protected WindowProc lpfnWndProc;       // WNDPROC lpfnWndProc
            protected int cbClsExtra;               // int     cbClsExtra

            protected int cbWndExtra;               // int     cbWndExtra
            protected int hInstance;                // HANDLE  hInstance
            protected int hIcon;                    // HICON   hIcon

            protected int hCursor;                  // HCURSOR hCursor
            protected int hbrBackground;            // HBRUSH  hbrBackground
            protected String lpszMenuName;          // LPCTSTR lpszMenuName

            protected String lpszClassName;         // LPCTSTR lpszClassName
            protected int hIconSm;                  // HICON   hIconSm

            public WndClassEx()
            {
            }
            ... /* Get and Set methods */

        }
        

The macro UINT is resolved in Win32 as unsigned int, the Java int is the most symmetric data type (same size), JNIEasy copies the native unsigned integer to the signed Java int with no bit change, to get a real unsigned value the NativePrimitiveUtil.toUnsigned(int) method can be used. Macros like HICON are resolved in Win32 as pointers to undocumented structures, in Win32 they are usually treated as integer data (the address), like an identity number, because the pointed structure is hidden and allocated/deallocated by Win32 methods, the most appropriated data type in Java is int because in Win32 pointers are 32 bit numbers.

The LPCTSTR macro is resolved in Win32 as a const char* pointer, in a JNIEasy managed attribute a String reference is equivalent to a char* pointer if declared as ANSI in the XML enhancer descriptor or wchar_t* if UNICODE, by default uses ANSI.

The macro WNDPROC is a pointer to method, it is reflected in JNIEasy as a reference to WindowProc objects, in our example the Java WindowProc class contains a method exported as a callback callable from C/C++ like a C method with a symmetric WNDPROC method signature, a WindowProc reference is seen like a native method pointer to this simulated C method.

The WndClassEx is a user defined "native capable" class, it needs to be enhanced using the JNIEasy enhancer before running (a specific Ant task is included in the examples bundle of the JNIEasy distribution), or may be enhanced using the on-load enhancer (not used in this example). The XML enhancer descriptor instructs to JNIEasy how the Java class is reflected in the C/C++ side.

The WndClassEx.jnieasy.enh.xml file declares the WndClassEx class as a native structure class, and describes what fields are native (reflected in the native side) and how are seen in the native side (the native memory layout), by default all non-static and non-transient fields are declared as native, primitive types are native memory self-described, and the WindowProc data type native description of its signature is described in the XML enhancer specific file. So the WndClassEx.jnieasy.enh.xml barely needs to declare the WndClassEx class as a native structure:

        <?xml version="1.0" encoding="UTF-8"?>

        <jniEasyEnhancer version="1.1"

            xmlns="http://www.innowhere.com/jnieasy/enhancer"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.innowhere.com/jnieasy/enhancer 
                ../../../../../schemas/JNIEasy.enh.xsd">

            <package name="examples.win32exam.win32.user">

                <imports />
                <class name="WndClassEx"  type="structure" >
                </class>
            </package>

        </jniEasyEnhancer>
        

The xmlns, xmlns:xsi xsi:schemaLocation are XMLSchema declarations and are optional and may be used in development time to validate the schema using a XML tool (Java IDEs usually have this type of validation "out the box"), the JNIEasy runtime does not perform a full XML schema validation (to gain in performance), only looks for the expected elements and attributes. The XMLSchema does not ensure the XML enhancer descriptor is functionally valid.


The WindowProc callback class

In our example the WindowProc class is designed to be the base class of concrete Java callback classes:

        package examples.win32exam.win32.user;

        public class WindowProc
        {
            public WindowProc()
            {
            }

            /*    

            LRESULT CALLBACK WindowProc(
              HWND hwnd,      // handle to window
              UINT uMsg,      // message identifier
              WPARAM wParam,  // first message parameter
              LPARAM lParam   // second message parameter

            );
            */

            public int onCall(int hwnd, int uMsg, int wParam, int lParam)
            {
                throw new RuntimeException("Overload this method");
            }
        }
        

A WindowProc, or derived class, (native) instance "compiles" on the fly a new C method, the reference to this object is a method pointer to this concrete callback, two instances are two methods with two different addresses.

The WindowProc is a user defined "native capable" class, it needs to be enhanced. The WindowProc.jnieasy.enh.xml file declares the WindowProc class as a native callback class, locates the WindowProc's method to be called from C/C++ and explains how the parameters and return types are seen in the C/C++ side, in this case the native expression of primitive types is self-described, no more "native information" is needed, and the return type is got from the class.

        <?xml version="1.0" encoding="UTF-8"?>

        <jniEasyEnhancer version="1.1"

            xmlns="http://www.innowhere.com/jnieasy/enhancer"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.innowhere.com/jnieasy/enhancer 
                ../../../../../schemas/JNIEasy.enh.xsd">

            <package name="examples.win32exam.win32.user">

                <imports />
                <class name="WindowProc" type="callback" >
                    <method name="onCall" callConv="std_call" params="int, int, int, int" >
                    </method>

                </class>
            </package>

        </jniEasyEnhancer>
        

The attribute callConv="std_call" declares the method call convention __stdcall, the standard in Win32 methods; JNIEasy allows the "c_call" convention too (the convention used by the ISO C methods).


Window class registration (cont.)

Returning to the WindowExample.registerWindowClass() method, the code fragment:

        WndClassEx wndClass = new WndClassEx();
        JNIEasy.get().getNativeManager().makeNative(wndClass);
        

makes native a new WndClassEx instance, before the call makeNative(wndClass) the WndClassEx object works as a normal Java object, after the instance is made native, a native instance has a native memory part associated, any modification of any native field modifies the "native memory field" too. In our example the makeNative(Object) call is not strictly necessary because the WndClassEx instance is passed as a parameter to the native method User32.GetClassInfoEx(), before to pass the parameter to the native side the framework ensures the object is native.

As the WndClassEx instance is native immediately after the creation, the framework ensures that any native capable object referenced from a native field of a native instance is made native, this is the case of the method call:

        wndClass.setWndProc( wndProc );
        

The framework makes native the pointed object of wndProc (a WindowProcConcrete object) before set to the WndClassEx.wndProc native field, the native side of the field is set, in this example, with the native address (method address) of the native WindowProcConcrete object-callback.

Any native object frees its related native memory automatically when it is finalized by the garbage collector, in our example, the native memory of WndClassEx instance is freed automatically when the wndClass reference goes out of the scope avoiding native memory leaks and no need to call any type of delete/free sentence. The WindowExample attribute, wndProc, is a final static WindowProc reference, the instance pointed, a WindowProcConcrete object-callback, is global and never goes out the scope, is not different to a concrete, normal C method (of course in Java); in our example the window class uses this WindowProc callback, every window created will use this callback method to process the received messages.

Optionally the native instance can be freed explicitly with the method NativeManager.free(Object), after that the freed instance is again a non-native object.

After setting the desired properties of the WndClassEx structure, the object is passed as parameter in the call User32.RegisterClassEx(wndClass). This method is a Java proxy of the Win32 method (defined inside the User32.dll):

        ATOM RegisterClassEx(
          CONST WNDCLASSEX *lpwcx  // address of structure with class data
        );
        

Proxy code generation (User32 class)

The User32 code generated class is a proxy of the User32.dll (in our example only a very small group or methods are defined). Code generation of Java proxy methods is an alternative to method enhancement (empty Java methods can be enhanced to be proxies of native methods, C and C++), code generation is more appropriate than enhancement with very big DLLs (DLLs with many C exported methods), because every method generated is only initialized/registered in the framework when called the first time (a typical Win32 application uses a small part of the Win32 API), in the enhanced version the method is initialized/registered when the container class is loaded; the enhancement is more appropriate to methods declared in C++ classes, structures or unions.


Verification of the window class registration

The method RunWin32Examples.testWindowClassRegistration() illustrates how the method User32.GetClassInfoEx() fills the native memory associated to the object pointed by wndClass2 (this object is made native before to pass to the "real" User32.dll method), when the get methods are called (attributes read more precisely) the framework automatically set the Java fields with the current values of the native memory.

Is specially interesting the call: wndClass2.getWndProc(), a simple read of the WndClassEx.wndProc field, this invocation returns a reference to the original object WindowProcConcrete registered. Why? because Windows set the address of the WindowProc method that was registered in the WNDCLASSEX native memory field, JNIEasy keeps track in a "weak" registry of the Java native objects owner of their associated native memory (the address is recorded alongside the object), a native object not a owner of the associated native memory is "attached", several native objects may be attached to the same address/memory zone, but JNIEasy search first in the address registry to locate the object owner of an address, if exists, before creating a new object and attaching it to the required native address.


Window creation

The method WindowExample.createWindow() creates the window:

        public class WindowExample
        {
            ...
            public static WindowExample createWindow()
            {
                int hInst = getModuleInstance();

                JNIEasy.get().getTypeManager().defineMacro("UNICODE");

                int hWnd = User32.CreateWindowEx(0, // extended window style 

                                       className,    // window class name
                                       "Hello World",        // window caption
                                       WSConst.WS_OVERLAPPEDWINDOW,        // window style
                                       CWConst.CW_USEDEFAULT,              // initial x position

                                       CWConst.CW_USEDEFAULT,              // initial y position
                                       CWConst.CW_USEDEFAULT,              // initial x size
                                       CWConst.CW_USEDEFAULT,              // initial y size
                                       0,                       // parent window handle

                                       0,                       // window menu handle
                                       hInst,                      // program instance handle
                                       0);                     // creation parameters        

                return new WindowExample(hWnd);
            }

            public void show()
            {
                User32.ShowWindow(hWnd,SWConst.SW_SHOW);
                User32.UpdateWindow(hWnd);
            }
            ...
        }
        

The call defineMacro("UNICODE") defines the macro "UNICODE", this macro is a custom name (is not predefined by JNIEasy in this context) used to select the real native method to call. Windows has two CreateWindowEx versions: CreateWindowExA and CreateWindowExW, CreateWindowExA expects string parameters (class name and caption) coded as a 1 byte ANSI strings, and the CreateWindowExW expects strings coded with a 2 byte (UNICODE) strings. With the JNIEasy's macro system we can select on runtime the method to call, to achieve that the required XML declarations (enhancer or code generation) must be defined as macro dependent, the final value is resolved on runtime looking for the macro registry. The CreateWindowEx method is defined in the User32.xml file:

            <?xml version="1.0" encoding="UTF-8"?>

            <jniEasyJavaCodeGen version="1.1"
            ...
                <method name="CreateWindowEx"
                        nativeName="ANSI:CreateWindowExA;UNICODE:CreateWindowExW;CreateWindowExA"
                        methodType="C" >

                    <return class="int" />
                    <params>
                        <param class="int" name="dwExStyle" />

                        <param class="String" name="lpClassName" encoding="ANSI:ansi;UNICODE:unicode;ansi" />

                        <param class="String" name="lpWindowName" encoding="ANSI:ansi;UNICODE:unicode;ansi" />
                        <param class="int" name="dwStyle" />

                        <param class="int" name="x" />
                        <param class="int" name="y" />

                        <param class="int" name="nWidth" />
                        <param class="int" name="nHeigth" />

                        <param class="int" name="hwndParent" />
                        <param class="int" name="hMenu" />
                        <param class="int" name="hInstance" />

                        <param class="int" name="lParam" />
                    </params>
                </method>
            ...
            </jniEasyJavaCodeGen>
        

The nativeName="ANSI:CreateWindowExA;UNICODE:CreateWindowExW;CreateWindowExA" declaration is resolved as following:

  1. If the macro with name "ANSI" was defined, use "CreateWindowExA" as native name.
  2. else if "UNICODE" macro was defined, use "CreateWindowExW" as native name.
  3. else use "CreateWindowExA".

The encoding attribute supports this macro syntax too:

  1. If the macro with name "ANSI" was defined, use "ansi" as encoding.
  2. else if "UNICODE" macro was defined, use "unicode" as encoding.
  3. else use "ansi".

Our example defines "UNICODE", but "ANSI" could be defined instead of, or none of them (used ANSI by default as seen in XML). The Java method CreateWindowEx is declared (registered in JNIEasy) and "linked" to the native method in the first call and can not be modified, any macro declaration (used by code generated or enhanced) must be done before the first use of the native element related (before loading the class if enhanced or the first call if a generated method).

The method User32.CreateWindowEx() creates the Win32 window returning the window handle. To instruct Windows to show the window the method WindowExample.show() must be called.

At this moment the window is not processing messages, because Windows messages are posted in the message stack of the window's creator thread, this thread is responsible to remove these messages and process them. To do this the method WindowExample.waitMessages() is called:

            public static void waitMessages()
            {
                Msg msg = new Msg();
                while (User32.GetMessage(msg,0, 0, 0) != 0)
                {
                    User32.TranslateMessage(msg);
                    User32.DispatchMessage(msg);
                }
            }
        

The loop ends when the thread receives a WM_QUIT message (using User32.PostQuitMessage(0)).

The call User32.DispatchMessage(msg) instructs to Windows to dispatch the message to the WindowProc associated to the target window defined in the message.

Of course the Msg class is a user defined native class previously enhanced:

        package examples.win32exam.win32.user;

        import examples.win32exam.win32.def.Point;

        /*

        typedef struct tagMSG {     // msg 
            HWND   hwnd;     
            UINT   message; 
            WPARAM wParam; 
            LPARAM lParam; 

            DWORD  time; 
            POINT  pt; 
        } MSG; 
        */

        public class Msg
        {
            protected int   hwnd;
            protected int   message;
            protected int   wParam;
            protected int   lParam;
            protected int   time;
            protected Point pt = new Point(); // is embedded (by value)

            public Msg()
            {
            }
            ... /* Gets and sets  */
        

Is specially interesting the "Point pt" reference; JNIEasy has two attribute reference modes:

  • By value: the object pointed is "embedded" into the class/structure/union memory.
  • By pointer: the reference is a pointer in the native memory

By default is "by pointer", in our example the C POINT pt attribute is embedded, then the reflected Java attribute must be declared "by value" (else it would have been declared POINT* pt), the reference mode is declared in the XML enhancer descriptor user/enhancer.xml, in this example the Msg declaration is shared with other Win32 structures (if on-load enhancer was used is highly recommended to use per class XML files):

        <?xml version="1.0" encoding="UTF-8"?>

        <jniEasyEnhancer version="1.1"

            xmlns="http://www.innowhere.com/jnieasy/enhancer"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.innowhere.com/jnieasy/enhancer 
                ../../../../../schemas/JNIEasy.enh.xsd">

            <include file="WindowProc.jnieasy.enh.xml" />
            <include file="WndClassEx.jnieasy.enh.xml" />

            <package name="examples.win32exam.win32.user">

                <imports />
                <class name="Msg"  type="structure" >
                    <field name="pt" varConv="byValue" />
                </class>
                <class name="PaintStruct"  type="structure" >
                    <field name="rcPaint" varConv="byValue" />
                    <field name="rgbReserved" varConv="byValue" length="32" />

                </class>
            </package>
        </jniEasyEnhancer>
        

The attribute varConv="byValue" declares the reference as "by value". This attribute is used to declare as "by value" the parameter pass convention in a method too.


Processing messages

Every message is sent to the registered WindowProcConcrete instance calling the onCall method:

        package examples.win32exam;

        import examples.win32exam.win32.def.*;
        import examples.win32exam.win32.user.*;

        public class WindowProcConcrete extends WindowProc
        {
            public WindowProcConcrete()
            {
                JNIEasy.get().getTypeManager().defineMacro("WINDOWS");
            }

            public int onCall(int hWnd, int msg, int wParam, int lParam)
            {
                switch (msg)
                {
                    case WMConst.WM_CREATE:
                        System.out.println("Window created: " + hWnd);
                        return 0;
                    case WMConst.WM_PAINT:
                    {
                        PaintStruct ps = new PaintStruct();
                        int hDC = User32.BeginPaint(hWnd,ps);

                        Rect rect = new Rect();
                        User32.GetClientRect(hWnd, rect);
                        User32.DrawText(hDC, "Hello World", -1, rect,
                            DTConst.DT_SINGLELINE | DTConst.DT_CENTER | DTConst.DT_VCENTER);

                        User32.EndPaint(hWnd, ps);
                        return 0;
                    }

                    case WMConst.WM_CLOSE:
                    {
                        User32.DestroyWindow(hWnd);
                        return 0;
                    }
                    case WMConst.WM_DESTROY:
                    {
                        User32.PostQuitMessage(0); // Ends the thread because GetMessage() returns 0

                        return 0;
                    }
                    case WMConst.WM_MOUSEMOVE:
                    {
                        int xPos = WinDefUtil.getXLParam(lParam);
                        int yPos = WinDefUtil.getYLParam(lParam);
                        Libc.printf("(x,y)=(%d,%d)\n",new Object[]{new Integer(xPos),new Integer(yPos)});
                        return 0;
                    }
                }
                return User32.DefWindowProc(hWnd, msg, wParam, lParam);
            }
        }
        

The code is absolutely symmetric to the C code. The window keeps running until the user close manually the window.

The defineMacro("WINDOWS") is declared because the Libc.printf method is called, this method is the C printf proxy, a varargs method. This method is declared in XML as cross-platform using "WINDOWS" and "Linux" macros because the DLL/shared object name is different, MSVCRT on Windows, libc.so.6 on Linux and libc.dylib on Mac OS X (we want to use the Windows version obviously) :

        <?xml version="1.0" encoding="UTF-8"?>

        <jniEasyJavaCodeGen version="1.1"
            xmlns="http://www.innowhere.com/jnieasy/jgen"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

            xsi:schemaLocation="http://www.innowhere.com/jnieasy/jgen 
                    ../../../../schemas/JNIEasy.jgen.xsd">

            <fileGen name="Libc">

                <package name="examples.win32exam.libc" >
                    <imports />
                    <class name="Libc"
                            libraryPath="WINDOWS:MSVCRT;Linux:/lib/libc.so.6;MacOSX:/usr/lib/libc.dylib">

                        <!--    
                        int printf(const char* format [ , argument , ...] ); 
                        -->
                        <method name="printf" methodType="C" callConv="c_call" >

                            <return class="int" />
                            <params>
                                <param class="String" name="pattern" />

                                <param class="Object[]" varargs="true" name="args" />

                            </params>
                        </method>
                    </class>
                </package>
            </fileGen>

        </jniEasyJavaCodeGen>

        

The variable number of arguments is declared with the attribute varargs, this attribute declares the Object[] type as a "varargs" parameter (otherwise it would be a pointer to pointer array). In the context of a vararg parameter, Java primitive wrappers (Integer, Boolean etc) are passed as primitives "byte value" (otherwise they would be pointer to primitive arguments, the default native types of primitive wrappers).

Specially interesting is the PaintStruct structure:

        package examples.win32exam.win32.user;
        import examples.win32exam.win32.def.Rect;

        /*

        typedef struct tagPAINTSTRUCT { 
          HDC  hdc; 
          BOOL fErase; 
          RECT rcPaint; 
          BOOL fRestore; 

          BOOL fIncUpdate; 
          BYTE rgbReserved[32]; 
        } PAINTSTRUCT, *PPAINTSTRUCT; 
        */

        public class PaintStruct
        {
            protected int hdc; // HDC

            protected int fErase; // BOOL in Win32 is an int
            protected Rect rcPaint = new Rect(); // is embedded (by value) 

            protected int fRestore;  // BOOL
            protected int fIncUpdate; // BOOL
            protected byte[] rgbReserved = new byte[32]; // is embedded (by value)

            public PaintStruct()
            {
            }

            ... /* Gets and sets methods */
        }
        

The C BYTE rgbReserved[32] attribute is an embedded array (otherwise must be BYTE*), in Java the most appropiated data type is a byte[] array "by value" with 32 elements, this is declared in the XML enhancer descriptor:

                ...
                <class name="PaintStruct"  type="structure" >
                    <field name="rcPaint" varConv="byValue" />
                    <field name="rgbReserved" varConv="byValue" length="32" />

                </class>
                ...
        

The length attribute is mandatory if the array attribute is declared "by value" (to know the memory size).


Unregistering the window class

To finalize the example the method WindowExample.unregisterWindowClass() is called unregistering the window class.

            public static int unregisterWindowClass()
            {
                int hInst = getModuleInstance();
                return User32.UnregisterClass(className,hInst);
            }
        

Conclusion

This tutorial is only a brief overview of JNIEasy, JNIEasy has many more features, read the JNIEasy manual for a complete description of JNIEasy capabilites.

Terms of Use Privacy Statement Contributor Agreement Add to deli.cio.us Single Page Interface logo ItsNat logo Innowhere logo