Передать необработанный указатель на функцию, где определение JNA ожидает обратного вызова?

#java #winapi #callback #jna

#java #winapi #обратный вызов #jna

Вопрос:

Я работаю с интерфейсом JNA, который ожидает Callback . У меня есть необработанный адрес встроенной функции g , которая является функцией, которую я хочу установить в качестве обратного вызова. Я вызываю собственную функцию f , которая ожидает обратного вызова, и я хотел бы, чтобы JNA перенаправила мой необработанный указатель на g в качестве обратного вызова, просто передав адрес g через f . Возможно ли это с помощью JNA?

Иллюстрация

Вот конкретная иллюстрация того, что я имею в виду. Я работаю с Win32 и хочу зарегистрировать класс window, для которого по умолчанию используется оконная процедура DefWindowProc . В обычном курсе я бы сделал следующее на C, чтобы зарегистрировать класс window, имеющий оконную процедуру по умолчанию:

 WNDCLASSEX wcex;
ZeroMemory(amp;wcex, sizeof(WNDCLASSEX));
wcex.cbSize = sizeof(WNDCLASSEX);
...
wcex.lpfnWndProc = LoadLibrary("user32", "DefWindowProcA");
...
ATOM atom = RegisterClassEx(amp;wcex);
  

Однако иногда я хочу зарегистрировать класс window с другой оконной процедурой. На C я бы сделал именно то, что указано выше, за исключением:

 wcex.lpfnWndProc = MyWindowProc; // Address of my custom window procedure
  

Надеюсь, теперь понятно, в чем сложность JNA. Я пишу Java-код, подобный:

 WNDCLASSEX wcex = new WNDCLASSEX.ByReference();
wcex.cbSize = WNDCLASSEX.size();
...
wcex.lpfnWndProc = new MyWindowProc(); // where MyWindowProc implements the Callback interface;
                                       // but what if I want to just set it to the address of
                                       // DefWindowProcA?
ATOM atom = User32.RegisterClassEx(wcex);
  

ПРЕДОСТЕРЕЖЕНИЕ

Я знаю, что могу определить две альтернативные версии функции f в Java, одна из которых принимает Callback , а другая принимает Pointer , и передать свой адрес g в Pointer версию. Я также знаю, что могу создать «обратный вызов оболочки» для DefWindowProcA. По разным причинам это неадекватные решения.

Комментарии:

1. Напишите адаптер. Напишите реализацию обратного вызова, которая вызывает указатель на вашу функцию.

2. @David Пожалуйста, прочтите вопрос до конца. OP уже сказал, что они не считают подход «обратного вызова оболочки» приемлемым. Кроме того, это также был подход к решению для начальной итерации моего ответа, и OP явно прокомментировал, что они хотели чего-то другого.

3. @Chris Я умею читать. Просто это нужно сделать так, как я говорю. Я не вижу, как необработанный указатель может каким-то образом быть Callback .

4. @DavidHeffernan На фундаментальном уровне (т. Е., если смотреть за пределы JNA) обратные вызовы являются указателями. На уровне JNA, конечно, они не совпадают, поскольку Callback имеет this указатель, а Pointer (и более конкретно, Function ) нет. Однако, с подходом marshal / unmarshal, который использует мой post, JNA создает прокси-объект для Callback , который позволяет ему делегировать правильно.

5. @DavidHeffernan (и OP тоже): Я собрал небольшую забавную программу для тестирования обратных вызовов, как на стороне Java, так и на стороне C с каламбурным типом: gist.github.com/cky/7397db4908fa9616539b

Ответ №1:

Чтобы иметь возможность использовать внешний указатель как Callback , вы вводите каламбур так, как это делают программисты C: с объединениями.

 public static class WindowProcUnion extends Union {
    public Pointer ptr;
    public WinUser.WindowProc wndProc;

    public WindowProcUnion(Pointer ptr) {
        this.ptr = ptr;
        setType("ptr");
        write();
        setType("wndProc");
        read();
    }
}
  

Теперь вы можете прочитать wndProc поле и получить обратно полезный обратный вызов оконной процедуры.


Для этого варианта использования, DefWindowProc фактически экспортируется через User32 класс, так что вы можете просто создать обратный вызов, чтобы вызвать его напрямую:

 public static class DefWindowProc implements WinUser.WindowProc {
    @Override
    public LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
        return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
}
  

Взгляните на Win32WindowDemo класс для примера оконной процедуры, которая делегирует DefWindowProc .

Комментарии:

1.Спасибо @Chris Jester-Young. Это не ответило на мой вопрос, но научило меня некоторым вещам, которых я не знал, что было очень полезно. Я «действительно хочу» использовать внешний указатель в качестве обратного вызова. Это часть спецификации работы, которую я выполняю. Однако дело не только в этом. Я хочу иметь возможность передавать либо экземпляр Callback , либо необработанный адрес функции, которая находится в DLL, той же функции, в зависимости от того, что у меня есть под рукой на данный момент.

2. Я полностью понимаю. На моей работе у нас был случай, когда наша сторонняя библиотека ожидала обратного вызова в виде глобальной переменной, которую я должен был получить и установить через JNA. Я попытаюсь адаптировать наш рабочий код к вашему случаю. Возможно, сначала мне придется провести некоторое тестирование, так что потерпите.

3. Конечно. Как я уже сказал, мне приходилось делать что-то подобное для рабочего кода, так что вся тема для меня ни в коем случае не нова. И тогда мне пришлось потратить много времени на изучение исходного кода JNA, чтобы выяснить, как его запустить в первую очередь, поэтому я только рад поделиться техникой с другими. 😀