Winapi - Eclipse SWT GUI 窗口的本机 WinProc 的 JNA 实现存在问题
Posted
技术标签:
【中文标题】Winapi - Eclipse SWT GUI 窗口的本机 WinProc 的 JNA 实现存在问题【英文标题】:Winapi - JNA implementation of native WinProc for Eclipse SWT GUI Window is having issues 【发布时间】:2020-02-05 03:39:48 【问题描述】:作为我最近的问题 (Winapi - SetWindowLongPtr in ShutdownBlockReasonCreate / Destroy implementation of JNI native code) 的扩展,我想知道是否有机会使用 JNA(最新版本 5.5.0 - https://github.com/java-native-access/jna)实现相同的功能。
由于我在文档 (http://java-native-access.github.io/jna/5.5.0/javadoc/) 中找不到与 SetWindowSubclass()
相关的任何内容,因此我不得不使用 SetWindowLongPtr()
。
在网上做了一些研究后,这是我的一些代码 sn-ps 负责预期的功能:
private static LONG_PTR baseWndProc;
private static HWND hWnd;
public static void initiateWindowsShutdownHook()
Display.getDefault().syncExec(()->
hWnd = new WinDef.HWND(Pointer.createConstant(Display.getDefault().getActiveShell().handle));
);
baseWndProc = IWindowsAPIUtil.WSBINSTANCE.SetWindowLongPtr(
hWnd, IWindowsAPIUtil.GWL_WNDPROC, new IWindowsAPIUtil.WNDPROC()
@Override
public LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam)
switch(uMsg)
case IWindowsAPIUtil.WM_QUERYENDSESSION:
Logger.logWarning("Shutdown initiated");
IWindowsAPIUtil.WSBINSTANCE.PostMessage(hWnd, User32.WM_CLOSE, wParam, lParam);
return new LRESULT(0);
return IWindowsAPIUtil.WSBINSTANCE.CallWindowProc(baseWndProc, hWnd, uMsg, wParam, lParam);
);
public interface IWindowsAPIUtil extends User32
public static final IWindowsAPIUtil WSBINSTANCE =
(IWindowsAPIUtil) Native.loadLibrary("user32", IWindowsAPIUtil.class, W32APIOptions.UNICODE_OPTIONS);
interface WNDPROC extends StdCallCallback
LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam);
public static final int GWL_WNDPROC = -4;
public static final int WM_QUERYENDSESSION = 17;
LONG_PTR SetWindowLongPtr(HWND hWnd, int nIndex, WNDPROC wndProc);
LONG_PTR GetWindowLongPtr(HWND hWnd, int nIndex);
LRESULT CallWindowProc(LONG_PTR proc, HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam);
void PostMessage(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam);
我的新 c++ 原生代码现在看起来像这样:
注意:由于在本练习中我只将回调部分(在原始 c++ 本机代码中)重构为 JNA,因此此处不需要SetWindowSubclass()
#include <windows.h>
#include <jni.h>
#include <iostream>
#include "com_app_project_winapi_WindowsAPI.h"
#include <commctrl.h>
using namespace std;
namespace
// Default reason text. The actual reason text should be defined by application logic not the native code
LPCWSTR SHUTDOWN_REASON = L"Application is still saving ...";
JNIEXPORT void JNICALL Java_com_app_project_winapi_WindowsAPI_shutdownBlockReasonCreate(JNIEnv *env, jclass cls, jstring title, jstring reasonText)
cout << "In shutdownBlockReasonCreate method" << endl;
const jchar *str = env->GetStringChars(title, NULL);
HWND hWnd = FindWindowW(NULL, (LPCWSTR)str);
env->ReleaseStringChars(title, str);
if (hWnd == NULL)
return;
ShutdownBlockReasonCreate(hWnd, SHUTDOWN_REASON);
return;
JNIEXPORT void JNICALL Java_com_app_project_winapi_WindowsAPI_shutdownBlockReasonDestroy(JNIEnv *env, jclass cls, jstring title)
cout << "In shutdownBlockReasonDestroy method" << endl;
const jchar *str = env->GetStringChars(title, NULL);
HWND hWnd = FindWindowW(NULL, (LPCWSTR)str);
env->ReleaseStringChars(title, str);
if (hWnd == NULL)
return;
ShutdownBlockReasonDestroy(hWnd);
return;
从我的代码中,您可能会看到我将 Eclipse SWT 用于我的 GUI 应用程序。保存我的代码并运行应用程序后,我遇到以下问题:
-
虽然在第一次应用程序保存期间激活了 blockReasonCreate(此功能的目的是在保存时阻止关闭),但它在后续保存中不再激活。原因文本显示“此应用正在阻止关闭”,而不是传入的原因文本。
作为上述扩展行为,我的 GUI 应用程序冻结,并且在我从任务管理器强制关闭之前无法关闭窗口
我尝试了以下方法:
-
在
CallWindowProc()
中将“baseWndProc”LONG_PTR 替换为GetWindowLongPtr()
。不幸的是没有工作
我怀疑我在SetWindowLongPtr()
上遇到了和上次一样的问题。但如前所述,JNA 似乎没有提供匹配的 SetWindowSubclass()
方法,所以我没有想法。
顺便说一句,上次的本机代码解决方案仍然可以完美运行。但出于可维护性的目的,将所有功能都用 Java 实现是理想的。
非常感谢任何花时间解决我的问题的人!
干杯
【问题讨论】:
RE:因为我找不到与SetWindowSubclass()
相关的任何内容......我不得不使用SetWindowLongPtr()
。 JNA 由用户贡献支持。如果当前映射中不存在某个方法,您可以/应该添加它并将其贡献给存储库以帮助未来的用户。为什么不自己编写一个Comctl32
类,加载 DLL 并映射您需要的函数?
【参考方案1】:
您应该始终准备一个最小的样本,它可以重现问题并且可以由潜在的帮助者运行。对从上下文中剥离出来的代码进行推理是很困难的。
在您的情况下,您将回调创建为匿名类(示例中的 new IWindowsAPIUtil.WNDPROC() /* ... */
)。在对SetWindowLongPtr
的调用返回后,此回调立即成为垃圾回收的条件。 JVM 不会立即运行 GC,但它会。
当你尝试调用一个被 GC 处理的回调时 OS/JVM 会做什么没有定义。 JNA 文档在这里很清楚:
如果本机代码尝试调用已被 GC 处理的回调,您 可能会使VM崩溃。如果没有办法注销 回调(例如 C 库中的 atexit),您必须确保您 始终保持对回调对象的实时引用。
所以你应该在 java 端保持对回调的强引用(例如使用静态字段,它与窗口具有相同的生命周期)。
【讨论】:
以上是关于Winapi - Eclipse SWT GUI 窗口的本机 WinProc 的 JNA 实现存在问题的主要内容,如果未能解决你的问题,请参考以下文章