Winapi - SetWindowLongPtr in ShutdownBlockReasonCreate / Destroy JNI 本机代码的实现

Winapi - SetWindowLongPtr in ShutdownBlockReasonCreate / Destroy implementation of JNI native code

我目前正在使用 SWT 开发一个涉及 Eclipse RCP 的 Java 项目,并试图通过在保存时向 Windows 环境中的用户提供有意义的消息来处理正常关闭。我被支持osed 使用 ShutdownBlockReasonCreateShutdownBLockReasonDestroy 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 往往表现得很奇怪。例如,

是的,我需要使用此方法来激活 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);
}