我尝试使用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();
}
});
}
}
感谢任何帮助,谢谢!
答案 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,该接口扩展了提供的接口。