通过JNA发送用户定义的Windows消息

Send a user-defined Windows-message through JNA

本文关键字:Windows 消息 定义 用户 JNA 通过      更新时间:2023-10-16

我想要一件简单但困难的事情:从JNA端发送一条特定的用户定义的Windows消息,以便该消息被C++端的消息循环(GetMessage()/DispatchMessage())捕获,然后该消息循环将被中断。实际上,它应该通过Swing GUI中的按钮点击来执行。我的问题和考虑:

1) 例如,假设我在C++端将自己的消息定义为#define WM_CUSTOM_MSG (WM_USER+42),当然,在C++端的消息循环中添加一个适当的if语句用于中断。但我的目的是从java发送这个消息。

2) 为此,我写了以下内容:

public class User32Ext {
interface User32Interface extends User32 {
User32Interface INSTANCE = Native.load("user32",
User32Interface.class, W32APIOptions.DEFAULT_OPTIONS);
@Override
HWND FindWindowEx(HWND lpParent, HWND lpChild, String lpClassName, String lpWindowName);
HWND GetTopWindow(HWND hwnd);
HWND GetParent(HWND hwnd);
@Override
HWND GetDesktopWindow();
int SendMessage(HWND hWnd, int Msg, IntByReference wParam, IntByReference lParam);
void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);
void SwitchToThisWindow(HWND hWnd, boolean fAltTab);
}
private final User32Interface u32 = User32Interface.INSTANCE;
public User32Ext() {
super();
// TODO Auto-generated constructor stub
}
public void sendInterruptMessage(final String windowName) {
try {
final User32.HWND hwnd = u32.FindWindowEx(null, null, null, windowName);
final int msg = 0x400 + 42;
final User32.WPARAM wparam = new User32.WPARAM();
final User32.LPARAM lparam = new User32.LPARAM();
final LRESULT res = u32.SendMessage(hwnd, msg, wparam, lparam);
} catch (final RuntimeException e) {
e.printStackTrace();
}
}
}

然后只创建一个这个伪类的对象,并在按钮事件中使用我的JFrame的标题作为参数来调用sendInterruptMessage

遗憾的是,双方都没有发现任何影响。我很确定我犯了一些致命的错误,因为我对Windows和JNA编程不够熟悉。那么,你能告诉我,至少我在概念上是对的,实现这样的期望结果吗?或者只是Java方面存在一些编程错误。谢谢

附言:如果它可以在不发送自定义消息的情况下实现,但使用一些标准的Windows消息,那就太好了,我只想确定,这个消息所做的唯一事情就是中断我的C++消息循环。

在将WINAPI函数映射到JNA时,必须注意将Windows API定义与适当的Java/JNA类型精确匹配。您的某些映射是错误的。SendMessage的最后两个参数是WPARAMLPARAM,它们似乎在sendInterruptMessage()映射中使用。它们正在调用JNA项目中已经映射的SendMessage函数。在自己的SendMessage映射中放入什么并不重要,因为您甚至没有调用它,因为您使用的是不同的类型。删除它。另外,从超类中删除已放置@Override的两个方法。你没有理由覆盖他们已经在做的事情。

事实上,经过进一步检查,你似乎从这个答案中复制了别人在2014年写的代码。然而,您需要的映射已于2017年添加到JNA项目中。在不知道代码的作用的情况下复制代码并不是成功的秘诀。不需要您的整个界面。只需调用JNA的User32接口。

在对FindWindowEx的调用中,前三个参数传递null,第四个参数只放置String。这似乎与API不匹配,后者似乎不允许将null作为第三个参数。您检查过返回的句柄是否为空吗?很可能是这样,根据API,您可以使用GetLastError查看错误代码,该错误代码可能与不正确的参数有关。

第三个参数的文档指定,

如果lpszClass是一个字符串,它会指定窗口类名。这个类名可以是在RegisterClass或RegisterClassEx,或任何预定义的控件类名,或可以是MAKEINTATOM(0x8000)。在后一种情况下,0x8000是原子对于菜单类。有关详细信息,请参阅的备注部分这个主题。

它看起来不像是您在按要求执行此操作。

如果第一次调用成功(这会让我感到惊讶),那么评估现有SendMessage调用的LRESULT返回值,看看它是否指示成功或生成错误代码,这将对您很有指导意义。事实上,您可能正在向它传递一个null句柄,在这种情况下,什么都没有发生也就不足为奇了。

从检查指示成功/失败的方法返回值开始,并评估错误代码应该有助于调试。

最后,正如iinspectable在评论中指出的那样,您传递的代码不是自定义消息。根据文件,

消息标识符值如下所示:

  • 系统为系统定义的
    消息保留范围从0x0000到0x03FF(WM_USER–1的值)的消息标识符值。应用程序不能将这些值用于私人消息。

  • 范围0x0400(WM_USER的值)到0x7FFF中的值可用于专用窗口类的消息标识符。

  • 如果应用程序标记为4.0版,则可以使用0x8000(WM_APP)到0xBFFF范围内的消息标识符值用于私人消息。

  • 当应用程序调用RegisterWindowMessage函数
    来注册消息时,系统会返回范围从0xC000到0xFFFF的消息标识符。此
    函数返回的消息标识符保证在整个系统中是唯一的。使用
    此功能可以防止在其他应用程序中可能出现的冲突将相同的消息标识符用于不同的目的

阅读WINAPI文档以了解每个参数中的预期内容将帮助您找到要传递的正确值,这对于从代码中获得预期结果至关重要。