#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, чтобы выяснить, как его запустить в первую очередь, поэтому я только рад поделиться техникой с другими. 😀