3

I'm trying to build a java application by using the JNA to gain access to the window messages (for e.g WM_POINTERDOWN). With this option, I will turn my application into a touch-sensitive application. So far my current code gets this window messages but possibly overwrites some other important java native code so that the JFrame doesn't react in the way I expect (for example, while resizing the JFrame to a bigger one, it fills the new added area black).

This is my Listener, which will be called when a new window message arrives:

public MyListener listener = new MyListener() {
        public LRESULT callback(HWND hWnd, int uMsg, WPARAM uParam,
                LPARAM lParam) {

                    //handle the window message here

            return User32.INSTANCE.DefWindowProc(hWnd, uMsg, uParam, lParam);
        }
    };

The Interface MyListener:

import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.win32.StdCallLibrary.StdCallCallback;

public interface MyListener extends StdCallCallback {

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

In this snippet I overwrite the native function of the JFrame, which will be normally called from the OS, with my listener:

HWND hWnd = new HWND();
    hWnd.setPointer(Native.getWindowPointer(this));

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

The class MyUser32:

import com.sun.jna.Callback;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.win32.W32APIOptions;

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

}

Maybe someone has a good idea about this. Thanks.

neminem
  • 2,658
  • 5
  • 27
  • 36
  • Make sure your callback implementation follows windows conventions for [chaining to the next callback](http://msdn.microsoft.com/en-us/library/windows/desktop/ms644959(v=vs.85).aspx#chains). – technomage Feb 18 '14 at 16:54

1 Answers1

0

In your callback procedure you are calling User32.INSTANCE.DefWindowProc, when you should actually call the procedure you overrode with SetWindowLong. Procedure call flow should be as follows:

(root) Default WndProc <- (A) Custom implementation (Swing) <- (B) Your implementation

Your implementation is currently bypassing the A block calling the root implementation directly. This is why the Swing specific stuff is broken.

The base procedure can be obtained by calling GetWindowLong/SetWindowLong funtions (SetWindowLong assigns the new procedure and returns handle to the replaced one) and called using CallWindowProc.

Another thing is that you should use GetWindowLongPtr/SetWindowLongPtr functions instead of the non-ptr-postfixed versions to be compatible with both 32- and 64-bit Windows. See the docs for SetWindowLong:

Note This function has been superseded by the SetWindowLongPtr function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.

So the correct implementation should be something along these lines:

MyUser32 interface:

    public interface MyUser32 extends User32 {

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

        interface WNDPROC extends StdCallCallback {
            LRESULT callback(HWND hWnd, int uMsg, WPARAM uParam, LPARAM lParam);
        }

        LONG_PTR GetWindowLongPtr(HWND hWnd, int nIndex) throws LastErrorException;

        LRESULT CallWindowProc(LONG_PTR proc, HWND hWnd, int uMsg, WPARAM uParam, WinDef.LPARAM lParam) throws LastErrorException;

        LONG_PTR SetWindowLongPtr(HWND hWnd, int nIndex, WNDPROC wndProc) throws LastErrorException;
    }

overriden procedure:

    private LONG_PTR baseWndProc;

    public MyUser32.WNDPROC listener = new MyUser32.WNDPROC () {
            public LRESULT callback(HWND hWnd, int uMsg, WPARAM uParam,
                    LPARAM lParam) {

                // TODO handle the window message

                // calling the base WndProc
                return MyUser32.MYINSTANCE.CallWindowProc(this.baseWndProc, hWnd, uMsg, wParam, lParam);
            }
        };

override:

    this.baseWndProc = MyUser32.MYINSTANCE.SetWindowLongPtr(hWnd, MyUser32.GWL_WNDPROC, this.listener);

or

    this.baseWndProc = MyUser32.MYINSTANCE.GetWindowLongPtr(hWnd, MyUser32.GWL_WNDPROC);
    MyUser32.MYINSTANCE.SetWindowLongPtr(hWnd, MyUser32.GWL_WNDPROC, this.listener);
jannis
  • 4,843
  • 1
  • 23
  • 53