Windows DXGI Experiment
Jorn Vernee
jorn.vernee at oracle.com
Tue Feb 25 16:16:20 UTC 2025
I recommend using the layout as well, rather than messing around with
ByteBuffer. Jextract [1] can automatically derive that for you, as well
as some handy setters. That should allow you to define a `DEFINE_GUID`
method, and then just copy the pre-defined ids from the dxgi.h header
file (unfortunately, the actual _GUID instances don't seem to be include
in the dxgi.dll library).
For example, I can use this jextract command to get the basics to call
the function you're interested in:
$jextract = 'C:\path\to\jextract.ps1'
$I = 'C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0' # for
example
& $jextract `
--output src `
-t org.windows.dxgi `
-I "$I\shared" `
-I "$I\ucrt" `
-I "$I\um" `
-I "$I\winrt" `
-l dxgi `
--include-function CreateDXGIFactory1 `
--include-struct _GUID `
--include-typedef IID `
"<dxgi.h>"
Define the GUID for e.g. IDXGIFactory1:
// copied from dxgi.h
static final MemorySegment IID_IDXGIFactory1 =
DEFINE_GUID(Arena.global(),
0x770aae78,0xf26f,0x4dba,0xa8,0x29,0x25,0x3c,0x83,0xd1,0xb3,0x87);
private static MemorySegment DEFINE_GUID(Arena arena, int data1,
int data2, int data3,
int data4_0, int data4_1, int data4_2, int data4_3,
int data4_4, int data4_5, int data4_6, int data4_7) {
MemorySegment refIID = IID.allocate(arena);
IID.Data1(refIID, data1);
IID.Data2(refIID, (short)data2);
IID.Data3(refIID, (short)data3);
MemorySegment data4 = IID.Data4(refIID);
IID.Data4(data4, 0, (byte) data4_0);
IID.Data4(data4, 1, (byte) data4_1);
IID.Data4(data4, 2, (byte) data4_2);
IID.Data4(data4, 3, (byte) data4_3);
IID.Data4(data4, 4, (byte) data4_4);
IID.Data4(data4, 5, (byte) data4_5);
IID.Data4(data4, 6, (byte) data4_6);
IID.Data4(data4, 7, (byte) data4_7);
return refIID;
}
And then it's just a very simple call:
try (Arena arena = Arena.ofConfined()) {
MemorySegment factoryPointer =
arena.allocate(ValueLayout.ADDRESS);
int hresult = CreateDXGIFactory1(IID_IDXGIFactory1,
factoryPointer);
System.out.println(
"Invocation result: " +
HexFormat.of().toHexDigits(hresult));
} catch (Throwable t) {
t.printStackTrace();
}
Hope that helps,
Jorn
[1]: https://github.com/openjdk/jextract
On 24-2-2025 19:58, Hugo De Vaan wrote:
> Greetings fellow developers,
>
> I've been trying to create a Java layer atop Microsoft's DXGI library
> in an attempt to get Java to work with Desktop Duplication.
> Unfortunately, I can't seem to create a DXGI factory. I can locate the
> symbol just fine, and I've double checked the UUID value for the type
> (using an earlier message in this mailing list, dating back to
> September 2020). When invoking the method handle, I get the 0x80004002
> HRESULT, which translates to "No such interface supported". I'm not
> sure what I'm doing wrong here, so I would appreciate any help.
>
> [code]
> import java.lang.foreign.Arena;
> import java.lang.foreign.FunctionDescriptor;
> import java.lang.foreign.Linker;
> import java.lang.foreign.MemorySegment;
> import java.lang.foreign.SymbolLookup;
> import java.lang.foreign.ValueLayout;
> import java.lang.invoke.MethodHandle;
> import java.nio.ByteBuffer;
> import java.nio.ByteOrder;
> import java.util.HexFormat;
>
> public final class DXGIExperiment {
> public static final void main(String[] args) {
> try {
> System.load("C:\\Windows\\System32\\dxgi.dll");
>
> Linker linker = Linker.nativeLinker();
>
> SymbolLookup symbols = SymbolLookup.libraryLookup(
> "C:\\Windows\\System32\\dxgi.dll", Arena.global());
>
> /* MemoryLayout BIT128 = MemoryLayout.
> sequenceLayout(16, ValueLayout.JAVA_BYTE); */
>
> /* Giving the 128bit UUID directly to the function doesn't
> * work - the layout is not supported:
> *
> * FunctionDescriptor descriptor = FunctionDescriptor.of(
> * ValueLayout.JAVA_INT, BIT128, ValueLayout.ADDRESS);
> */
>
> FunctionDescriptor descriptor = FunctionDescriptor.of(
> ValueLayout.JAVA_INT, ValueLayout.ADDRESS,
> ValueLayout.ADDRESS);
>
> MemorySegment createFactory =
> symbols.find("CreateDXGIFactory1").get();
>
> MethodHandle handle = linker.
> downcallHandle(createFactory, descriptor);
>
> System.out.println("Handle to CreateDXGIFactory1 created.");
>
> byte[] uuidArray = new byte[16];
> ByteBuffer buffer = ByteBuffer.wrap(uuidArray);
> buffer.order(ByteOrder.LITTLE_ENDIAN);
>
> buffer.
> putInt(0x770aae78).
> putInt(0xf26f4dba).
> putInt(0xa829253c).
> putInt(0x83d1b387);
>
> MemorySegment factoryPointer =
> Arena.global().allocate(ValueLayout.ADDRESS);
>
> MemorySegment uuidValue =
> Arena.global().allocate(16);
>
> uuidValue.setAtIndex(ValueLayout.JAVA_INT, 0,
> buffer.getInt(0));
> uuidValue.setAtIndex(ValueLayout.JAVA_INT, 1,
> buffer.getInt(4));
> uuidValue.setAtIndex(ValueLayout.JAVA_INT, 2,
> buffer.getInt(8));
> uuidValue.setAtIndex(ValueLayout.JAVA_INT, 3,
> buffer.getInt(12));
>
> int hresult = (int) handle.
> invokeExact(uuidValue, factoryPointer);
>
> System.out.println(
> "Invocation result: " +
> HexFormat.of().toHexDigits(hresult));
> } catch (Throwable t) {
> t.printStackTrace();
> }
> }
> }
> [/code]
>
> PS: This was tried on Windows 10 Pro, 64 bit, build 19045 and running
> GraalVM 21+35.1
>
> Regards,
> Hugo
More information about the panama-dev
mailing list