I'm working with a JNA interface that expects a Callback
. I have the raw address of a native function g
, which is the function I want to install as a callback. I am calling native function f
, which expects a callback, and I would like JNA to marshall my raw pointer to g
as the callback by just passing the address of g
through to f
. Is this possible with JNA?
ILLUSTRATION
Here's a concrete illustration of what I mean. I'm working with Win32 and I want to register a window class whose default window procedure is DefWindowProc
. In the ordinary course I would do the following in C to register a window class having the default window procedure:
WNDCLASSEX wcex;
ZeroMemory(&wcex, sizeof(WNDCLASSEX));
wcex.cbSize = sizeof(WNDCLASSEX);
...
wcex.lpfnWndProc = LoadLibrary("user32", "DefWindowProcA");
...
ATOM atom = RegisterClassEx(&wcex);
However, sometimes I want to register a window class with a different window procedure. In C, I would do exactly the above except:
wcex.lpfnWndProc = MyWindowProc; // Address of my custom window procedure
Hopefully it is now clear what the difficulty is in JNA. I am writing Java code similar to:
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);
CAVEAT
I am aware that I can define two alternate versions of the function f
in Java, one of which takes a Callback
and one of which takes a Pointer
, and pass my address to g
to the Pointer
version. I'm also aware that I can create a "wrapper callback" for DefWindowProcA. For various reasons, these are not adequate solutions.
To be able to use an external pointer as a Callback
, you type-pun the way C programmers do: with unions.
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();
}
}
You can now read the wndProc
field and get back a usable window procedure callback.
For this use case, DefWindowProc
is actually exported via the User32
class, so you can just create a callback to invoke it directly:
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);
}
}
Have a look at the Win32WindowDemo
class for an example of a window procedure that delegates to DefWindowProc
.