【问题标题】:Winapi - SetWindowLongPtr in ShutdownBlockReasonCreate / Destroy implementation of JNI native codeWinapi - SetWindowLongPtr in ShutdownBlockReasonCreate / Destroy implementation of JNI native code
【发布时间】:2020-01-09 07:19:50
【问题描述】:

我目前正在处理一个涉及使用 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;
}

【问题讨论】:

  • 您的问题可能源于在您的AppWndProc 中直接调用DefWindowProcM/S documentation 声明:“应用程序必须通过调用 CallWindowProc 将任何未被新窗口过程处理的消息传递给前一个窗口过程。这允许应用程序创建一个窗口过程链。”
  • 不要使用SetWindowLongPtr 子类化窗口,使用SetWindowSubclass
  • 感谢@AdrianMole 和@Jonathan Potter 的及时回复。到目前为止,我发现了一个帖子stackoverflow.com/questions/31648180/…,它也建议使用类似于乔纳森推荐的SetWindowSubclass。当我刚刚尝试时,似乎工作得更好。我会多玩一点。谢谢大家!

标签: c++ winapi java-native-interface save shutdown


【解决方案1】:

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

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

第三,您应该使用SetWindowSubclass() 而不是SetWindowLongPtr()。请参阅 Disadvantages of the Old Subclassing ApproachSafer 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);
}

【讨论】:

  • 感谢@Remy Lebeau,您的及时回复!我会试一试,并根据您的建议进行更多尝试。再次感谢!
  • Java 字符串本身是 UTF-16 对于 Java 9 及更高版本不一定如此,每个 openjdk.java.net/jeps/254 请参阅 stackoverflow.com/questions/9699071/… 进行一些讨论。
  • @AndrewHenle 在内部,他们可能使用 UTF-16 或 Latin-1 进行内存存储,但他们的公共接口仍然是 UTF-16。
猜你喜欢
  • 1970-01-01
  • 2021-10-07
  • 2022-12-02
  • 2019-06-28
  • 1970-01-01
  • 2022-12-02
  • 1970-01-01
  • 1970-01-01
  • 2022-12-01
相关资源
最近更新 更多