Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAVA JNA WindowProc implementation

Tags:

java

jna

wndproc

I'm trying to write a simple application in Java that will communicate with an USB device. The USB device is made by me using a Microchip Microcontroller. The communication is rather simple, since the USB device is from the HID Class, arrays of 64 bytes are exchanged between the computer and the device. My program finds the device based on the product ID and the vendor ID, can write and read 64 bytes, but now I would like to detect when the device is connected or disconnected from the computer.

As I've seen in a C# program provided by Microchip as an example application, the WndProc method is overriden and the WM_DEVICECHANGE message is handled. My question is how can this be done in Java using JNA, how can I override the WindowProc Method and handle messages, if this is possible at all :), but I hope it is :D

Thanks in advance for the answers.

Gabor.

like image 556
Gabor Avatar asked Jan 13 '11 08:01

Gabor


2 Answers

I finally managed to solve the problem :) And I found the following solution:

First extend the User32 interface in the following way

public interface MyUser32 extends User32 {

    public static final MyUser32 MYINSTANCE = (MyUser32) Native.loadLibrary("user32", MyUser32.class, W32APIOptions.UNICODE_OPTIONS);

    /**
     * Sets a new address for the window procedure (value to be set).
     */
    public static final int GWLP_WNDPROC = -4;

    /**
     * Changes an attribute of the specified window
     * @param   hWnd        A handle to the window
     * @param   nIndex      The zero-based offset to the value to be set.
     * @param   callback    The callback function for the value to be set.
     */
    public int SetWindowLong(WinDef.HWND hWnd, int nIndex, Callback callback);
}

Then extend the WinUser interface with the Windows Message code that you need, in my case this is the WM_DEVICECHANGE, because I want to check I the USB Device was attached or detached from the computer.

public interface MyWinUser extends WinUser {
    /**
     * Notifies an application of a change to the hardware configuration of a device or the computer.
     */
    public static final int WM_DEVICECHANGE = 0x0219;
}

Then create an interface with the callback function, which will actually be my WndProc function.

//Create the callback interface 
public interface MyListener extends StdCallCallback {

    public LRESULT callback(HWND hWnd, int uMsg, WPARAM uParam, LPARAM lParam);
}

public MyListener listener = new MyListener()
{
    public LRESULT callback(HWND hWnd, int uMsg, WPARAM uParam, LPARAM lParam)
    {
        if (uMsg == MyWinUser.WM_DEVICECHANGE)
        {
            // TODO Check If my device was attached or detached
            return new LRESULT(1);
        }
        return new LRESULT(0);
    }
};

And then somewhere in the code of the JFrame where you initialize things add the new address for the window procedure with the SetWindowLong function:

    // Get Handle to current window
    HWND hWnd = new HWND();
    hWnd.setPointer(Native.getWindowPointer(this));

    MyUser32.MYINSTANCE.SetWindowLong(hWnd, MyUser32.GWLP_WNDPROC, listener);

This code works nicely, but I have some doubts regarding one thing. I'm not sure if the return value of the callback function is correct. I've read in the MSDN that after handling a WM_DEVICECHANGE message the callback function should return true and I'm not sure that the value i'm currently returning is the one expected by the system, so any suggestions are welcome.

If anyone is interested in the whole code I've written for the HID communication just ask, I would be more than happy to help :)

Cheers, Gabor.

like image 180
Gabor Avatar answered Sep 29 '22 22:09

Gabor


If you don't have an existing window handle you have to create your own window first. And when you create a new window you also have to manage its message pump. Here's an example on how you can do this. JNA's own example code could also be very useful.

Thread thread;
HWND hWnd;
static final int WM_NCCREATE = 0x0081;

void start() {
    thread = new Thread(this::myThread);
    thread.start();
}

void stop() {
    User32.INSTANCE.PostMessage(hWnd, User32.WM_QUIT, null, null);
}

WindowProc callback = new WindowProc() {
    @Override
    public LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam) {
        switch (uMsg) {
        case WM_NCCREATE:
            return new LRESULT(1);

        case User32.WM_DEVICECHANGE:
            return new LRESULT(1);

        default:
            return new LRESULT(0);
        }
    }
};

void myThread() {
    WString className = new WString("myclass");

    WNDCLASSEX wx = new WNDCLASSEX();
    wx.clear();
    wx.lpszClassName = className;
    wx.lpfnWndProc = callback;

    if (User32.INSTANCE.RegisterClassEx(wx).intValue() != 0) {
        hWnd = User32.INSTANCE.CreateWindowEx(0, className, null, 0, 0, 0, 0, 0, null, null, null, null);

        WinUser.MSG msg = new WinUser.MSG();
        msg.clear();

        while (User32.INSTANCE.GetMessage(msg, hWnd, 0, 0) > 0) {
            User32.INSTANCE.TranslateMessage(msg);
            User32.INSTANCE.DispatchMessage(msg);
        }
    }
}
like image 44
Sundae Avatar answered Sep 29 '22 23:09

Sundae