Winapi - SetWindowLongPtr in ShutdownBlockReasonCreate / Destroy JNI 本机代码的实现
Winapi - SetWindowLongPtr in ShutdownBlockReasonCreate / Destroy implementation of JNI native code
我目前正在使用 SWT 开发一个涉及 Eclipse RCP 的 Java 项目,并试图通过在保存时向 Windows 环境中的用户提供有意义的消息来处理正常关闭。我被支持osed 使用 ShutdownBlockReasonCreate 和 ShutdownBLockReasonDestroy APIs 来实现这个但是经过一些研究我不得不用我很陌生的 C++ 本机代码实现它们。由于它们在 JNA 和 Eclipse SWT 中不可用,因此不提供现成的这种功能(否则很想知道)
经过所有的努力后,我能够将一个有效的 C++ 代码(如下所示)组合在一起以控制 SWT window(通过引用另一个实现 https://github.com/seraphy/JavaGracefulShutdownForWin7)。但是我偶然发现了一个与 WindowProc CALLBACK 相关的问题。来自 Java 背景,这些语法让我花了一段时间才理解。但我有点明白它想做什么。因为这是我们需要处理 WM_QUERYENDSESSION 和 WM_ENDSESSION 消息的地方。
但在开始之前,我想在这个post 中讨论的问题具体与windows API SetWindowLongPtr 正如您在 Java_com_app_project_winapi_WindowsAPI_shutdownBlockReasonCreate(JNIEnv *env, jclass cls, jstring title)
函数中看到的那样。如您所见,我将其注释掉了,只是因为在 ShutdownBlockReasonCreate(hWnd, SHUTDOWN_REASON)
之后调用此方法时,我的 window 往往表现得很奇怪。例如,
- "File"选项被点击时,不再显示菜单;
- 调整 window 大小时,window 的一半会变暗
重新调整大小
- 当 closing window 时,底层进程仍然 运行
是的,我需要使用此方法来激活 window 的控制以接收 os 消息,但随后它开始与 Eclipse SWT 混淆 window那已经建成了。有谁知道我是否正确地实施了这整件事?还是我跑偏了? SetWindowLongPtr 究竟做了什么?我找不到任何好的参考资料,也无法从阅读 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 version of FindWindow()
, which doesn't accept UTF-8 strings. Use the Unicode version,它接受 UTF-16 字符串。 Java 字符串在其 public 接口中原生使用 UTF-16,因此您无需浪费时间将它们不必要地转换为 UTF-8。
其次,您的 window 在调用 SetWindowLongPtr()
because your AppWndProc()
needs to use CallWindowProc()
instead of DefWindowProc()
以调用您替换的先前 window 过程后无法正确运行。此外,当您使用完 AppWndProc()
时,您并没有恢复之前的 window 过程。
第三,你应该使用SetWindowSubclass()
instead of SetWindowLongPtr()
. See Disadvantages of the Old Subclassing Approach and Safer subclassing。
话虽如此,请尝试更类似的方法:
#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);
}
我目前正在使用 SWT 开发一个涉及 Eclipse RCP 的 Java 项目,并试图通过在保存时向 Windows 环境中的用户提供有意义的消息来处理正常关闭。我被支持osed 使用 ShutdownBlockReasonCreate 和 ShutdownBLockReasonDestroy APIs 来实现这个但是经过一些研究我不得不用我很陌生的 C++ 本机代码实现它们。由于它们在 JNA 和 Eclipse SWT 中不可用,因此不提供现成的这种功能(否则很想知道)
经过所有的努力后,我能够将一个有效的 C++ 代码(如下所示)组合在一起以控制 SWT window(通过引用另一个实现 https://github.com/seraphy/JavaGracefulShutdownForWin7)。但是我偶然发现了一个与 WindowProc CALLBACK 相关的问题。来自 Java 背景,这些语法让我花了一段时间才理解。但我有点明白它想做什么。因为这是我们需要处理 WM_QUERYENDSESSION 和 WM_ENDSESSION 消息的地方。
但在开始之前,我想在这个post 中讨论的问题具体与windows API SetWindowLongPtr 正如您在 Java_com_app_project_winapi_WindowsAPI_shutdownBlockReasonCreate(JNIEnv *env, jclass cls, jstring title)
函数中看到的那样。如您所见,我将其注释掉了,只是因为在 ShutdownBlockReasonCreate(hWnd, SHUTDOWN_REASON)
之后调用此方法时,我的 window 往往表现得很奇怪。例如,
- "File"选项被点击时,不再显示菜单;
- 调整 window 大小时,window 的一半会变暗 重新调整大小
- 当 closing window 时,底层进程仍然 运行
是的,我需要使用此方法来激活 window 的控制以接收 os 消息,但随后它开始与 Eclipse SWT 混淆 window那已经建成了。有谁知道我是否正确地实施了这整件事?还是我跑偏了? SetWindowLongPtr 究竟做了什么?我找不到任何好的参考资料,也无法从阅读 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 version of FindWindow()
, which doesn't accept UTF-8 strings. Use the Unicode version,它接受 UTF-16 字符串。 Java 字符串在其 public 接口中原生使用 UTF-16,因此您无需浪费时间将它们不必要地转换为 UTF-8。
其次,您的 window 在调用 SetWindowLongPtr()
because your AppWndProc()
needs to use CallWindowProc()
instead of DefWindowProc()
以调用您替换的先前 window 过程后无法正确运行。此外,当您使用完 AppWndProc()
时,您并没有恢复之前的 window 过程。
第三,你应该使用SetWindowSubclass()
instead of SetWindowLongPtr()
. See Disadvantages of the Old Subclassing Approach and Safer subclassing。
话虽如此,请尝试更类似的方法:
#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);
}