Winapi - JNA-реализация родного WinPro c для Eclipse SWT GUI В окне возникают проблемы - PullRequest
0 голосов
/ 05 февраля 2020

В качестве дополнения к моему недавнему вопросу ( Winapi - SetWindowLongPtr в ShutdownBlockReasonCreate / Destroy реализация собственного кода JNI ) Я хотел бы знать, есть ли шанс реализовать такую ​​же возможность с использованием JNA (последний выпуск 5.5 .0 - https://github.com/java-native-access/jna).

Поскольку я не смог найти ничего, что относится к SetWindowSubclass() в do c (http://java-native-access.github.io/jna/5.5.0/javadoc/) I пришлось использовать SetWindowLongPtr().

После некоторых онлайн-исследований, вот некоторые из моих фрагментов кода, отвечающих за предполагаемую функциональность:

    private static LONG_PTR baseWndProc;
    private static HWND hWnd;

    public static void initiateWindowsShutdownHook() {
        Display.getDefault().syncExec(()->{
            hWnd = new WinDef.HWND(Pointer.createConstant(Display.getDefault().getActiveShell().handle));
        });

        baseWndProc = IWindowsAPIUtil.WSBINSTANCE.SetWindowLongPtr(
                hWnd, IWindowsAPIUtil.GWL_WNDPROC, new IWindowsAPIUtil.WNDPROC() {

            @Override
            public LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam) {
                switch(uMsg) {
                case IWindowsAPIUtil.WM_QUERYENDSESSION:
                    Logger.logWarning("Shutdown initiated");
                    IWindowsAPIUtil.WSBINSTANCE.PostMessage(hWnd, User32.WM_CLOSE, wParam, lParam);
                    return new LRESULT(0);
                }
                return IWindowsAPIUtil.WSBINSTANCE.CallWindowProc(baseWndProc, hWnd, uMsg, wParam, lParam);
            }
        });
    }


    public interface IWindowsAPIUtil extends User32 {

        public static final IWindowsAPIUtil WSBINSTANCE = 
                (IWindowsAPIUtil) Native.loadLibrary("user32", IWindowsAPIUtil.class, W32APIOptions.UNICODE_OPTIONS);

        interface WNDPROC extends StdCallCallback{
            LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam);
        }

        public static final int GWL_WNDPROC = -4;
        public static final int WM_QUERYENDSESSION = 17;

        LONG_PTR SetWindowLongPtr(HWND hWnd, int nIndex, WNDPROC wndProc);
        LONG_PTR GetWindowLongPtr(HWND hWnd, int nIndex);
        LRESULT CallWindowProc(LONG_PTR proc, HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam);
        void PostMessage(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam);
    }

И мой новый собственный код на c ++ теперь выглядит как это:

Примечание: Так как в этом упражнении я реорганизовал только часть обратного вызова (в оригинальном нативном коде c ++) в JNA, поэтому нет необходимости в SetWindowSubclass() здесь

#include <windows.h>

#include <jni.h>

#include <iostream>
#include "com_app_project_winapi_WindowsAPI.h"


#include <commctrl.h>

using namespace std;

namespace {
    // Default reason text. The actual reason text should be defined by application logic not the native code
    LPCWSTR SHUTDOWN_REASON = L"Application is still saving ...";
}


JNIEXPORT void JNICALL Java_com_app_project_winapi_WindowsAPI_shutdownBlockReasonCreate(JNIEnv *env, jclass cls, jstring title, jstring reasonText) {
    cout << "In shutdownBlockReasonCreate method" << endl;

    const jchar *str = env->GetStringChars(title, NULL);
    HWND hWnd = FindWindowW(NULL, (LPCWSTR)str);
    env->ReleaseStringChars(title, str);
    if (hWnd == NULL) {
        return;
    }

    ShutdownBlockReasonCreate(hWnd, SHUTDOWN_REASON);

    return;
}

JNIEXPORT void JNICALL Java_com_app_project_winapi_WindowsAPI_shutdownBlockReasonDestroy(JNIEnv *env, jclass cls, jstring title) {
    cout << "In shutdownBlockReasonDestroy method" << endl;

    const jchar *str = env->GetStringChars(title, NULL);
    HWND hWnd = FindWindowW(NULL, (LPCWSTR)str);
    env->ReleaseStringChars(title, str);
    if (hWnd == NULL) {
        return;
    }

    ShutdownBlockReasonDestroy(hWnd);

    return;
}

Из моего кода вы, вероятно, видите, что я использую Eclipse SWT для моего GUI приложения. После сохранения кода и запуска приложения возникает следующая проблема:

  1. Хотя blockReasonCreate активируется во время первого сохранения приложения (целью этой функции является блокирование выключения при сохранении), оно больше не активируется в последующее сохранение. И текст причины говорит: «Это приложение предотвращает отключение», вместо текста причины, который был передан.
  2. В качестве расширенного поведения, приведенного выше, мое приложение GUI зависает, и окно не может быть закрыто, пока я принудительно закрыть его из диспетчера задач

Я попытался сделать следующее:

  1. Заменить "baseWndPro c" LONG_PTR на GetWindowLongPtr() в CallWindowProc(). К сожалению, не сработало
  2. У меня есть подозрение, что у меня та же проблема с SetWindowLongPtr(), что и в прошлый раз. Но, как описано ранее, JNA, по-видимому, не имеет соответствующего метода SetWindowSubclass(), поэтому у меня нет идей.

Кстати, решение нативного кода последнего времени все еще работает отлично. Но в целях удобства обслуживания все функции, реализованные в Java, были бы идеальными.

Очень признателен всем, кто уделил время моей проблеме!

Приветствия

1 Ответ

1 голос
/ 10 февраля 2020

Вы всегда должны готовить минимальный образец, который воспроизводит проблему и может быть использован потенциальными помощниками. Обдумать код, который вырван из контекста, сложно.

В вашем случае вы создаете обратный вызов как анонимный класс (new IWindowsAPIUtil.WNDPROC() /* ... */ в вашем примере). Этот обратный вызов сразу же становится подходящим для сборки мусора после возврата на SetWindowLongPtr. JVM не будет запускать G C сразу, но будет.

Не определено, что будет делать OS / JVM при попытке вызвать обратный вызов, который был GCed. Документация JNA здесь ясна:

Если нативный код пытается вызвать обратный вызов, который был G C 'd, вы, вероятно, взломаете sh ВМ. Если не существует метода для отмены регистрации обратного вызова (например, atexit в библиотеке C), вы должны убедиться, что вы всегда сохраняете действующую ссылку на объект обратного вызова.

Поэтому вы должны сохранять сильный ссылка на обратный вызов на стороне java (например, используйте поле stati c, которое будет иметь то же время жизни, что и окно).

...