1

我目前正在处理一个涉及使用 SWT 的 Eclipse RCP 的 Java 项目,并试图通过在保存时向 Windows 环境中的用户提供有意义的消息来处理正常关闭。我应该使用ShutdownBlockReasonCreateShutdownBLockReasonDestroy API 来实现这一点,但经过一些研究后,我不得不在 C++ 本机代码中实现它们,而我对这些代码还是很陌生。因为它们在 JNA 中不可用,并且 Eclipse SWT 不提供这种现成的功能(很想知道)

经过所有努力后,我能够将一个有效的 C++ 代码(如下)组合在一起来控制 SWT 窗口(通过引用另一个实现https://github.com/seraphy/JavaGracefulShutdownForWin7)。但是我偶然发现了一个与 WindowProc CALLBACK 相关的问题。来自 Java 背景,这些语法花了我一段时间才理解。但我有点明白它想要做什么。因为这是我们需要处理 WM_QUERYENDSESSION 和 WM_ENDSESSION 消息的地方。

但在此之前,我想在这篇文章中讨论的问题与 Windows API SetWindowLongPtr相关,正如您在Java_com_app_project_winapi_WindowsAPI_shutdownBlockReasonCreate(JNIEnv *env, jclass cls, jstring title)函数中看到的那样。如您所见,我将其注释掉,只是因为在调用此方法之后我的窗口往往表现得非常奇怪ShutdownBlockReasonCreate(hWnd, SHUTDOWN_REASON)。例如,

  • 单击“文件”选项,不再显示菜单;
  • 调整窗口大小时,当您尝试调整窗口大小时,一半窗口变暗
  • 关闭窗口时,底层进程仍在运行

是的,我需要使用此方法来激活窗口控制以接收操作系统消息,但随后它开始与已经构建的 Eclipse SWT 窗口混淆。有谁知道我是否正确实施了这整件事?还是我跑偏了?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;
}
4

1 回答 1

2

首先,您调用的是ANSI 版本FindWindow(),它不接受 UTF-8 字符串。请改用Unicode 版本,它接受 UTF-16 字符串。Java 字符串本身使用 UTF-16 作为其公共接口,因此您无需浪费时间将它们不必要地转换为 UTF-8。

其次,您的窗口调用后无法正常运行,SetWindowLongPtr()因为您AppWndProc()需要使用CallWindowProc()而不是DefWindowProc()调用您替换的上一个窗口过程。此外,当您完成使用AppWndProc().

第三,您应该使用SetWindowSubclass()而不是SetWindowLongPtr(). 请参阅旧子类化方法的缺点更安全的子类化。

话虽如此,请尝试更像这样的东西:

#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);
}
于 2020-01-09T08:04:21.537 回答