用JNA替换WndProc失败,出现异常

时间:2012-04-22 10:54:32

标签: java windows jna wndproc

我尝试使用JNA在Java程序中捕获Windows消息以注入我自己的WndProc方法。因为我只对几种消息类型感兴趣,所以我必须将消息转发到之前的WndProc。在我的测试中(Java 1.7.0_03,64位,在Windows 7上),只要我将鼠标移到创建的窗口上,这个转发就会失败,给我以下异常:

com.sun.jna.LastErrorException: [2]The system cannot find the file specified.
    at com.sun.jna.Native.invokeLong(Native Method)
    at com.sun.jna.Function.invoke(Function.java:347)
    at com.sun.jna.Function.invoke(Function.java:276)
    at com.sun.jna.Library$Handler.invoke(Library.java:216)
    at $Proxy0.CallWindowProc(Unknown Source)
    at JnaWinEvents$1.callback(JnaWinEvents.java:102)
    at ...

我认为这很奇怪,因为我尝试访问哪个文件?我猜某些内存访问有一些完全错误...... :(

我正在使用最新版本的JNA,3.4.0。我在这里或在互联网上的其他地方找到的许多代码示例似乎都使用JNA 3.2.x(在拆分为jna.jar和platform.jar之前的任何版本),其中User32定义了一些非常适合此类工作的方法。在较新版本的JNA /平台中,缺少这些方法。这就是我自己定义大多数类型的原因,只使用jna.jar,而不是platform.jar。 以下是我用来测试的代码,它生成了Exception。知道出了什么问题以及为什么会发生异常吗?

import javax.swing.*;
import com.sun.jna.*;
import com.sun.jna.win32.*;

public class JnaWinEvents extends JFrame {

public LONG_PTR prevWndProc = null; // so it won't get GC'ed
public User32.WNDPROC wndProcCallbackListener = null; // so it won't get GC'ed

public JnaWinEvents() {
    this.add(new JLabel("Hello StackExchange!"));
    this.pack();
    this.setVisible(true);
    setupEventsListener();
}

public static class LONG_PTR extends IntegerType {
    public LONG_PTR() {
        this(0);
    }
    public LONG_PTR(long value) {
        super(Pointer.SIZE, value);
    }
}

static class HANDLE extends PointerType implements NativeMapped {
}

public static class HWND extends HANDLE {
    public HWND() {
    }
}

public static class UINT_PTR extends IntegerType {
    public UINT_PTR() {
        super(Pointer.SIZE);
    }
    public UINT_PTR(long value) {
        super(Pointer.SIZE, value);
    }
    public Pointer toPointer() {
        return Pointer.createConstant(longValue());
    }
}

public static class WPARAM extends UINT_PTR {
    public WPARAM() {
        this(0);
    }
    public WPARAM(long value) {
        super(value);
    }
}

public static class LPARAM extends LONG_PTR {
    public LPARAM() {
        this(0);
    }
    public LPARAM(long value) {
        super(value);
    }
}

public static class LRESULT extends LONG_PTR {
    public LRESULT() {
        this(0);
    }
    public LRESULT(long value) {
        super(value);
    }
}

public interface User32 extends StdCallLibrary {
    static int GWL_WNDPROC = -4;

    User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class, W32APIOptions.DEFAULT_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, LPARAM lParam) throws LastErrorException;

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


private void setupEventsListener() {
    HWND hWnd = new HWND();
    hWnd.setPointer(Native.getWindowPointer(this));
    this.prevWndProc = User32.INSTANCE.GetWindowLongPtr(hWnd, User32.GWL_WNDPROC);
    this.wndProcCallbackListener = new User32.WNDPROC()
    {
        @Override
        public LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam)
        {
            System.out.println(hWnd + "\t" + uMsg + "\t" + wParam + "\t" + lParam);

            //Call the window's actual WndProc so the events get processed.
            return User32.INSTANCE.CallWindowProc(prevWndProc, hWnd, uMsg, wParam, lParam);
        }

        @Override
        protected void finalize() throws Throwable {
            System.out.println("FINALIZE!!!!");
            super.finalize();
        }
    };

    //Set the WndProc function to use our callback listener instead of the window's one. 
    LONG_PTR result = User32.INSTANCE.SetWindowLongPtr(hWnd, User32.GWL_WNDPROC, wndProcCallbackListener);
    System.out.println("setting my window proc, result = " + result);
    System.out.println("old pointer = " + this.prevWndProc);
}

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            new JnaWinEvents();
        }
    });
}
}

感谢任何帮助,谢谢!

2 个答案:

答案 0 :(得分:1)

问题出在接口定义中。看here

答案 1 :(得分:0)

我最近遇到了JNA的somme问题(因为我试图遵循基于旧版本的教程)。我只是设置了一个工作代码的骨架,我不会在这里发布,因为它与你的非常相似。不同的是我使用的是platform.jar。你为什么不用它?

编辑:抱歉延迟。 您需要以这种方式定义回调过程(为您提供StdCallBack)

public class WndProc implements StdCallCallback {
    public LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam) {
    }
}

在某处宣布

private final WndProc procedure = new WndProc();

然后在适当的时候链接它。

user32.SetWindowLongPtr(window, com.sun.jna.platform.win32.WinUser.GWL_WNDPROC, procedure);

您可能需要在自定义User32接口中声明SetWindowLongPtr,该接口扩展了提供的接口。