通过JNA发送一个user-definedWindows-message
Send a user-defined Windows-message through JNA
我想要一个简单但困难的事情:从 JNA 端发送一个特定的 user-defined Windows-message 以便 message-loop (GetMessage()
/DispatchMessage()
) 在 C++ 端,然后消息循环将被中断。实际上它应该通过在 Swing GUI 中单击按钮来执行。我的问题和考虑:
1) 例如,假设我在 C++ 端将自己的消息定义为 #define WM_CUSTOM_MSG (WM_USER+42)
,当然在 C++ 端的 message-loop 中添加适当的 if-statement 用于中断。但我的目的是从 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();
}
}
}
然后只需创建这个虚拟 class 的 object 并在按钮事件中使用我的 JFrame
的标题作为参数调用 sendInterruptMessage
。
可悲的是,双方都没有检测到任何影响。我非常确定我犯了一些致命错误,因为我对 Windows 和 JNA 编程的经验还不够丰富。那么,你能告诉我,至少我在概念上是正确的,像这样实现预期的结果吗?或者 Java 端只是一些编程错误。谢谢!
P.S。如果它可以在不发送自定义消息但使用一些标准 Windows 消息的情况下实现,那很好,我只是想确定,这条消息唯一做的就是中断我的 C++ 消息循环。
当用 JNA 映射 WINAPI 函数时,您必须注意将 Windows API 定义与适当的 Java/JNA 类型完全匹配。您的某些映射是错误的。 SendMessage
的最后两个参数是 WPARAM
和 LPARAM
,您似乎在 sendInterruptMessage()
映射中使用了它们。这些正在调用 already-mapped SendMessage
function in the JNA project。您在自己的 SendMessage
映射中放入什么并不重要,因为您甚至没有调用它,因为您使用的是不同的类型。删除那个。还要从超类中删除已放入 @Override
的两个方法。你没有理由覆盖他们已经在做的事情。
实际上,进一步检查后,您似乎在 2017 年 from this answer. However, the mappings you need were added to the JNA project 复制了别人在 2014 年编写的代码。复制代码而不了解它的作用并不是成功的秘诀。不需要您的整个界面。直接调用JNA的User32
接口即可。
在您对 FindWindowEx
的调用中,您为前三个参数传递了 null,并且只在第四个参数中放置了一个字符串。这似乎不匹配 the API which does not appear to allow null as the third argument。您是否检查过 returned 句柄是否为空?可能是这样,根据 API,您可以使用 GetLastError
查看该错误代码,这可能与不正确的参数有关。
第三个参数的文档指定,
If lpszClass is a string, it specifies the window class name. The
class name can be any name registered with RegisterClass or
RegisterClassEx, or any of the predefined control-class names, or it
can be MAKEINTATOM(0x8000). In this latter case, 0x8000 is the atom
for a menu class. For more information, see the Remarks section of
this topic.
您似乎没有按要求执行此操作。
如果第一个调用成功(这会让我感到惊讶),那么从现有的 SendMessage
调用中评估 LRESULT
return 并查看它是否指示成功或生成错误代码。事实上,您可能向它传递了一个空句柄,在这种情况下什么也没有发生也就不足为奇了。
首先检查指示 success/failure 的方法 return 值并评估错误代码应该有助于您进行调试。
最后,正如 iinspectable in the comments, the code you're passing isn't a custom message. According to the documentation、
所指出的
Message-identifier values are used as follows:
The system reserves message-identifier values in the range 0x0000 through 0x03FF (the value of WM_USER – 1) for system-defined
messages. Applications cannot use these values for private messages.
Values in the range 0x0400 (the value of WM_USER) through 0x7FFF are available for message identifiers for private window classes.
If your application is marked version 4.0, you can use message-identifier values in the range 0x8000 (WM_APP) through 0xBFFF
for private messages.
- The system returns a message identifier in the range 0xC000 through 0xFFFF when an application calls the RegisterWindowMessage function
to register a message. The message identifier returned by this
function is guaranteed to be unique throughout the system. Use of
this function prevents conflicts that can arise if other applications
use the same message identifier for different purposes.
阅读 WINAPI 文档以了解每个参数的预期值将帮助您找到要传递的正确值,这对于从您的代码中获得预期结果至关重要。
我想要一个简单但困难的事情:从 JNA 端发送一个特定的 user-defined Windows-message 以便 message-loop (GetMessage()
/DispatchMessage()
) 在 C++ 端,然后消息循环将被中断。实际上它应该通过在 Swing GUI 中单击按钮来执行。我的问题和考虑:
1) 例如,假设我在 C++ 端将自己的消息定义为 #define WM_CUSTOM_MSG (WM_USER+42)
,当然在 C++ 端的 message-loop 中添加适当的 if-statement 用于中断。但我的目的是从 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();
}
}
}
然后只需创建这个虚拟 class 的 object 并在按钮事件中使用我的 JFrame
的标题作为参数调用 sendInterruptMessage
。
可悲的是,双方都没有检测到任何影响。我非常确定我犯了一些致命错误,因为我对 Windows 和 JNA 编程的经验还不够丰富。那么,你能告诉我,至少我在概念上是正确的,像这样实现预期的结果吗?或者 Java 端只是一些编程错误。谢谢!
P.S。如果它可以在不发送自定义消息但使用一些标准 Windows 消息的情况下实现,那很好,我只是想确定,这条消息唯一做的就是中断我的 C++ 消息循环。
当用 JNA 映射 WINAPI 函数时,您必须注意将 Windows API 定义与适当的 Java/JNA 类型完全匹配。您的某些映射是错误的。 SendMessage
的最后两个参数是 WPARAM
和 LPARAM
,您似乎在 sendInterruptMessage()
映射中使用了它们。这些正在调用 already-mapped SendMessage
function in the JNA project。您在自己的 SendMessage
映射中放入什么并不重要,因为您甚至没有调用它,因为您使用的是不同的类型。删除那个。还要从超类中删除已放入 @Override
的两个方法。你没有理由覆盖他们已经在做的事情。
实际上,进一步检查后,您似乎在 2017 年 from this answer. However, the mappings you need were added to the JNA project 复制了别人在 2014 年编写的代码。复制代码而不了解它的作用并不是成功的秘诀。不需要您的整个界面。直接调用JNA的User32
接口即可。
在您对 FindWindowEx
的调用中,您为前三个参数传递了 null,并且只在第四个参数中放置了一个字符串。这似乎不匹配 the API which does not appear to allow null as the third argument。您是否检查过 returned 句柄是否为空?可能是这样,根据 API,您可以使用 GetLastError
查看该错误代码,这可能与不正确的参数有关。
第三个参数的文档指定,
If lpszClass is a string, it specifies the window class name. The class name can be any name registered with RegisterClass or RegisterClassEx, or any of the predefined control-class names, or it can be MAKEINTATOM(0x8000). In this latter case, 0x8000 is the atom for a menu class. For more information, see the Remarks section of this topic.
您似乎没有按要求执行此操作。
如果第一个调用成功(这会让我感到惊讶),那么从现有的 SendMessage
调用中评估 LRESULT
return 并查看它是否指示成功或生成错误代码。事实上,您可能向它传递了一个空句柄,在这种情况下什么也没有发生也就不足为奇了。
首先检查指示 success/failure 的方法 return 值并评估错误代码应该有助于您进行调试。
最后,正如 iinspectable in the comments, the code you're passing isn't a custom message. According to the documentation、
所指出的Message-identifier values are used as follows:
The system reserves message-identifier values in the range 0x0000 through 0x03FF (the value of WM_USER – 1) for system-defined
messages. Applications cannot use these values for private messages.Values in the range 0x0400 (the value of WM_USER) through 0x7FFF are available for message identifiers for private window classes.
If your application is marked version 4.0, you can use message-identifier values in the range 0x8000 (WM_APP) through 0xBFFF for private messages.
- The system returns a message identifier in the range 0xC000 through 0xFFFF when an application calls the RegisterWindowMessage function
to register a message. The message identifier returned by this
function is guaranteed to be unique throughout the system. Use of
this function prevents conflicts that can arise if other applications use the same message identifier for different purposes.
阅读 WINAPI 文档以了解每个参数的预期值将帮助您找到要传递的正确值,这对于从您的代码中获得预期结果至关重要。