Search code examples
javawinapicallbackjna

Pass a raw function pointer where the JNA definition expects a Callback?


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.


Solution

  • 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.