Winapi - SetWindowLongPtr in ShutdownBlockReasonCreate/Destroy implementation of JNI native code
我目前正在处理一个涉及使用 SWT 的 Eclipse RCP 的 Java 项目,并试图通过在保存时向 Windows 环境中的用户提供有意义的消息来处理正常关闭.我应该使用 ShutdownBlockReasonCreate 和 ShutdownBLockReasonDestroy API 来实现这一点,但经过一些研究后,我不得不在我非常陌生的 C++ 本机代码中实现它们.因为它们在 JNA 中不可用,并且 Eclipse SWT 不提供这种现成的功能(很想知道)
I'm currently working on a Java project involving Eclipse RCP using SWT and was trying to handle graceful shutdown by providing meaningful messages to user in Windows environment when saving. I was supposed to use ShutdownBlockReasonCreate and ShutdownBLockReasonDestroy APIs to achieve this but after some research I had to implement them in C++ native code which I'm very new to. As they are not available in JNA and Eclipse SWT does not provide such capability off-the-shelf (would love to know otherwise)
经过所有努力后,我能够将一个有效的 C++ 代码(如下)放在一起来控制 SWT 窗口(通过引用另一个实现 https://github.com/seraphy/JavaGracefulShutdownForWin7).但是我偶然发现了一个与 WindowProc CALLBACK 相关的问题.来自 Java 背景,这些语法花了我一段时间才理解.但我有点明白它想要做什么.因为这是我们需要处理 WM_QUERYENDSESSION 和 WM_ENDSESSION 消息的地方.
After going through all the effort I was able to pull together a working C++ code (as follows) to get control of the SWT window (by referencing another implementation https://github.com/seraphy/JavaGracefulShutdownForWin7). However I stumble upon an issue which relates to WindowProc CALLBACK. Coming from Java background these syntax took me a while to understand. But I sort of get what it's trying to do. Because this is where we need to handle WM_QUERYENDSESSION and WM_ENDSESSION messages.
但在此之前,我想在这篇文章中讨论的问题与 Windows API SetWindowLongPtr 相关,您可以在 Java_com_app_project_winapi_WindowsAPI_shutdownBlockReasonCreate(JNIEnv *env, jclasscls, jstring title)
函数.如您所见,我将其注释掉了,只是因为在 ShutdownBlockReasonCreate(hWnd, SHUTDOWN_REASON)
之后调用此方法时,我的窗口往往表现得非常奇怪.例如,
But before getting to that, the problem I want to talk about in this post is specifically related to the windows API SetWindowLongPtr as you can see in the Java_com_app_project_winapi_WindowsAPI_shutdownBlockReasonCreate(JNIEnv *env, jclass cls, jstring title)
function. As you can see I commented it out, simply because my window tends to behave very strange when this method was called after ShutdownBlockReasonCreate(hWnd, SHUTDOWN_REASON)
. For example,
- 点击文件"选项,不再显示菜单;
- 调整窗口大小时,当您尝试调整窗口大小时,一半窗口会变暗调整大小
- 关闭窗口时,底层进程仍在运行
是的,我需要使用此方法来激活窗口的控制以接收操作系统消息,但随后它开始与已经构建的 Eclipse SWT 窗口混淆.有谁知道我是否正确实施了这整件事?还是我跑偏了?SetWindowLongPtr 究竟做了什么?我找不到任何好的参考资料,也无法从阅读 Microsoft Doc 中获得太多收获.
Yes I need to use this method in order to activate the control of the window for receiving os messages, but then it started to mess up with the Eclipse SWT window that was already built. Does anyone know if I have implemented this whole thing correctly? Or am I off-track? What does SetWindowLongPtr do exactly? I couldn't find any good reference and I couldn't get much out of reading the Microsoft Doc.
提前致谢!
#include <jni.h>
#include <iostream>
#include "com_app_project_winapi_WindowsAPI.h"
#include <windows.h>
using namespace std;
namespace {
LPCWSTR SHUTDOWN_REASON = L"Application is still saving ...";
LRESULT CALLBACK AppWndProc(
_In_ HWND hWnd,
_In_ UINT message,
_In_ WPARAM wParam,
_In_ LPARAM lParam
) {
switch (message) {
// Not doing anything yet
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
JNIEXPORT void JNICALL Java_com_app_project_winapi_WindowsAPI_shutdownBlockReasonCreate(JNIEnv *env, jclass cls, jstring title) {
cout << "shutdownblockreason create" << endl;
const char *str = NULL;
str = (env)->GetStringUTFChars(title, 0);
HWND hWnd = FindWindow(NULL, str);
(env)->ReleaseStringUTFChars(title, str);
if (hWnd == NULL) {
return;
}
ShutdownBlockReasonCreate(hWnd, SHUTDOWN_REASON);
//SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(AppWndProc));
return;
}
JNIEXPORT void JNICALL Java_com_app_project_winapi_WindowsAPI_shutdownBlockReasonDestroy(JNIEnv *env, jclass cls, jstring title) {
cout << "shutdownblockreason destroy" << endl;
const char *str = NULL;
str = (env)->GetStringUTFChars(title, 0);
HWND hWnd = FindWindow(NULL, str);
(env)->ReleaseStringUTFChars(title, str);
if (hWnd == NULL) {
return;
}
ShutdownBlockReasonDestroy(hWnd);
return;
}
推荐答案
首先,你调用的是 ANSI 版本的 FindWindow()
,不接受 UTF-8 字符串.请改用 Unicode 版本,它接受 UTF-16 字符串.Java 字符串本身使用 UTF-16 作为其公共接口,因此您无需浪费时间将它们不必要地转换为 UTF-8.
Firstly, you are calling the ANSI version of FindWindow()
, which doesn't accept UTF-8 strings. Use the Unicode version instead, which accepts UTF-16 strings. Java strings natively use UTF-16 for their public interface, so you don't need to waste time converting them to UTF-8 unnecessarily.
第二,你的窗口在调用SetWindowLongPtr()
因为你的 AppWndProc()
需要使用 CallWindowProc()
而不是 DefWindowProc()
调用您之前的窗口过程更换.此外,当您使用 AppWndProc()
完成后,您不会恢复之前的窗口过程.
Second, your window fails to operate correctly after calling SetWindowLongPtr()
because your AppWndProc()
needs to use CallWindowProc()
instead of DefWindowProc()
to call the previous window procedure that you replaced. Also, you are not restoring the previous window procedure when you are done using AppWndProc()
.
第三,你应该使用 <代码>SetWindowSubclass() 而不是 SetWindowLongPtr()
.请参阅 缺点旧子类化方法和更安全的子类化.
Third, you should be using SetWindowSubclass()
instead of SetWindowLongPtr()
. See Disadvantages of the Old Subclassing Approach and Safer subclassing.
话虽如此,请尝试更多类似的东西:
With that said, try something more like this instead:
#include <jni.h>
#include <iostream>
#include "com_app_project_winapi_WindowsAPI.h"
#include <windows.h>
#include <commctrl.h>
namespace {
LPCWSTR SHUTDOWN_REASON = L"Application is still saving ...";
/*
WNDPROC PrevWndProc = NULL;
LRESULT CALLBACK AppWndProc(
_In_ HWND hWnd,
_In_ UINT message,
_In_ WPARAM wParam,
_In_ LPARAM lParam
) {
*/
LRESULT CALLBACK AppWndProc(
_In_ HWND hWnd,
_In_ UINT message,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ UINT_PTR uIdSubclass,
_In_ DWORD_PTR dwRefData
) {
switch (message) {
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, AppWndProc, uIdSubclass);
break;
//...
}
//return CallWindowProc(PrevWndProc, hWnd, message, wParam, lParam);
return DefSubclassProc(hWnd, message, wParam, lParam);
}
}
JNIEXPORT void JNICALL Java_com_app_project_winapi_WindowsAPI_shutdownBlockReasonCreate(JNIEnv *env, jclass cls, jstring title) {
std::cout << "shutdownblockreason create" << std::endl;
const jchar *str = env->GetStringChars(title, NULL);
HWND hWnd = FindWindowW(NULL, (LPCWSTR) str);
env->ReleaseStringChars(title, str);
if (!hWnd) {
return;
}
ShutdownBlockReasonCreate(hWnd, SHUTDOWN_REASON);
//PrevWndProc = (WNDPROC) SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(AppWndProc));
SetWindowSubclass(hWnd, &AppWndProc, 1, 0);
}
JNIEXPORT void JNICALL Java_com_app_project_winapi_WindowsAPI_shutdownBlockReasonDestroy(JNIEnv *env, jclass cls, jstring title) {
std::cout << "shutdownblockreason destroy" << std::endl;
const jchar *str = env->GetStringChars(title, NULL);
HWND hWnd = FindWindowW(NULL, (LPCWSTR) str);
env->ReleaseStringChars(title, str);
if (!hWnd) {
return;
}
//SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(PrevWndProc));
RemoveWindowSubclass(hWnd, &AppWndProc, 1);
ShutdownBlockReasonDestroy(hWnd);
}
相关文章