问题
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)
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.
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,
- "File" option when clicked, no longer show menu;
- When resizing the window, half the window become dark when you try to resize it back
- When closing the window, the underlying process still running
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.
Thanks in advance!
#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;
}
回答1:
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.
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()
.
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);
}
来源:https://stackoverflow.com/questions/59658987/winapi-setwindowlongptr-in-shutdownblockreasoncreate-destroy-implementation