2

我尝试使用 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。我在此处或 Internet 上的其他地方找到的许多代码示例似乎使用 JNA 3.2.x(拆分为 jna.jar 和 platform.jar 之前的任何版本),其中 User32 定义了一些非常适合此类工作的方法。在较新版本的 JNA/platform 中,缺少这些方法。这就是为什么我自己定义大部分类型,只使用 jna.jar,而不是 platform.jar。以下是我用来测试的代码,它会生成异常。知道出了什么问题以及为什么会发生异常吗?

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

任何帮助表示赞赏,谢谢!

4

2 回答 2

1

问题在于接口定义。看这里

于 2013-01-01T22:18:29.810 回答
0

我最近在 JNA 上遇到了一些问题(因为我试图按照基于旧版本的教程进行操作)。我只是建立了一个工作代码的骨架,我不会在这里发布它,因为它与你的非常相似。不同之处在于我使用的是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,该接口扩展了提供的接口。

于 2012-05-03T09:25:14.577 回答