JAVA JNA WindowProc implementation

夙愿已清 提交于 2019-11-30 00:58:28

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.

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);
        }
    }
}

You can create COM DLL or OCX of your C# program and use it in the java code. If you create application.

Use JACOB OR JCOM

It will be a bridge between Java and COM Object. Other option is you can use JNI to communicate with DLL and OCX.

Gabor

The solution I posted previously has some problems, unfortunately :(

Since it overrides the WndProc of the window, the controls I added to my Frame weren't working (not surprisingly, because no paint, repaint, etc. messages were handled). Then I realised that instead of returning LRESULT(1) I should call the default window proc (as it is used in Win32 C++ programs), but this still didn't solve the problem, the frame was painted but the buttons weren't working, although I was able to update labels... So I had to abandon this solution too.

After searching some more on the internet I've found a great article here (edit: link is dead, original article can be found here), where a static hidden window is created to handle windows messages. I managed to code it for my application and it works great. (I had to further extend the classes from JNA because several functions were not included. I can post my code if someone is interested.)

Hope this helps.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!