Windows OLE/COM with jextract code generation
Duncan Gittins
duncan.gittins at gmail.com
Wed Feb 17 17:59:45 UTC 2021
On 17/02/2021 12:58, Jorn Vernee wrote:
> Hi Duncan,
>
> Thanks for bringing this up. Virtual calls are an interesting
> use-case, and a while ago we added support for linking virtual calls
> to CLinker, where you get back a method handle that takes an
> additional leading Addressable parameter that represents the function
> that should be called.
>
> On the jextract front though, there is a relatively low-hanging fruit
> that we could also grab for this. IUnknownVtbl is essentially a struct
> with a bunch of function pointers. A simplified example would be:
>
> struct Foo {
> void (*cb)(int);
> };
>
> Now maybe you get one of these structs back from a native call, and
> then you want to call 'cb'.
>
> Jextract currently generates getters and setters for those kinds of
> function pointer fields, which would allow you to manually get the
> function address and then manually link a MethodHandle to call it, as
> you've found out. But, we could, in the case of function pointer
> types, also add another kind of accessor to call the function
> directly, and wire up the right MethodHandle behind the scenes.
>
> So, your case could theoretically be turned from:
>
> Addressable vtFuncPtr = (Addressable)
> Ole32_h.IUnknownVtbl.QueryInterface$VH().get(vtable);
> MethodType mt= MethodType.methodType(int.class,
> MemoryAddress.class, MemoryAddress.class, MemoryAddress.class);
> FunctionDescriptor fd = FunctionDescriptor.of(CLinker.C_LONG,
> CLinker.C_POINTER, CLinker.C_POINTER, CLinker.C_POINTER);
> MethodHandle queryInterfaceMH =
> CLinker.getInstance().downcallHandle(vtFuncPtr, mt, fd);
>
> Into something like:
>
> MemoryAddress hresult = Ole32_h.IUnknownVtbl.QueryInterface(vtable);
>
That extra code generation would be helpful. I used the method handle
from vtable in above code to create version of the interface added by
jextract:
Ole32_h.IUnknownVtbl.QueryInterface queryInterface =
(This,riid,ppvObject) -> {
var mh$ = queryInterfaceMH;
try {
return (int)mh$.invokeExact(This, riid, ppvObject);
} catch (Throwable ex$) {
throw new AssertionError("should not reach here", ex$);
}
};
... and then call QI was simple:
int hRes = queryInterface.apply(comObj.address(),
riid.address(), queryRes.address());
Duncan
> Maurizio has filed an issue for this [1].
>
> Thanks,
> Jorn
>
> [1] : https://bugs.openjdk.java.net/browse/JDK-8261906
>
> On 16/02/2021 19:13, Duncan Gittins wrote:
>> This may be outside the scope of jextract (too Windows specific), or
>> a dumb question (then I'll send it to the usual place for dumb
>> questions: stackoverflow).
>>
>> I'm converting my handwritten Panama Java application over to use the
>> corresponding data structures generated by jextract, but can't find
>> definitions which could simplify the MethodHandle lookups for
>> referencing Windows OLE/COM API calls. The jextract output of Ole32.h
>> (which contains only "#include <objbase.h>") used is generated from:
>>
>> set "WINKIT=c:\Program Files (x86)\Windows
>> Kits\10\Include\10.0.18362.0"
>> jextract -source -lole32 -t duncan.win -d java -I
>> "%WINKIT%\um" Ole32.h
>>
>> Retrieving any COM object IUnknown is easy with appropriate values
>> for class+iid guids as follows:
>>
>> MemoryAddress comObj =
>> Ole32_h.CoCreateInstance(rclsid.address(), pUnkOuter, dwClsContext,
>> riid.address(), ptrComObj.address());
>>
>> ... and read the COM object vTable by following IUnknown.lpVtbl
>> pointer to IUnknownVTbl:
>>
>> MemorySegment memseg = Ole32_h.IUnknown.ofAddressRestricted(comObj);
>> MemoryAddress pVTableAddr = Ole32_h.IUnknown.lpVtbl$get(memseg);
>> MemorySegment vtable =
>> Ole32_h.IUnknownVtbl.ofAddressRestricted(pVTableAddr);
>>
>> At this point I need downcallHandle for every COM method pointer in
>> vtable - QueryInterface / AddRef... I have this for "QueryInterface"
>> but is there a way to avoid my hardcoded mt/fd declarations for all
>> each method handle?
>>
>> Addressable vtFuncPtr = (Addressable)
>> Ole32_h.IUnknownVtbl.QueryInterface$VH().get(vtable);
>> MethodType mt= MethodType.methodType(int.class,
>> MemoryAddress.class, MemoryAddress.class, MemoryAddress.class);
>> FunctionDescriptor fd = FunctionDescriptor.of(CLinker.C_LONG,
>> CLinker.C_POINTER, CLinker.C_POINTER, CLinker.C_POINTER);
>> MethodHandle queryInterfaceMH =
>> CLinker.getInstance().downcallHandle(vtFuncPtr, mt, fd);
>>
>> The interface IUnknownVtbl.QueryInterface has support for upcalls (-
>> so maybe I could implement QueryInterface for a COM object in Java),
>> and has "fd" internally as xxx_constants.QueryInterface $FUNC, but no
>> equivalent to "mt" or downcalls of this specific comObj - say as
>> "IUnknownVtbl.QueryInterface$MH(vtFuncPtr)".
>>
>> Kind regards
>>
>> Duncan Gittins
More information about the panama-dev
mailing list