cnuernber / dtype-next

A Clojure library designed to aid in the implementation of high performance algorithms and systems.
Other
319 stars 18 forks source link

How do you pass a null pointer to a C function or include one in a struct? #58

Closed emassey0135 closed 2 years ago

emassey0135 commented 2 years ago

I am currently trying to create a GUI application in Clojure using the Win32 API. I am using tech.v3.datatype.ffi, and compiling the program with GraalVM Native Image. Many functions in the Win32 API have parameters that can either be a pointer to an object or NULL, which means that the object passed by this parameter is not necessary for that particular invocation. An example of this is the MessageBoxW function, whose first parameter can be a pointer to a window handle to set the parent window of the message box, or NULL, which makes the message box have no parent window. However, when I try to create a null pointer and pass it as the first parameter to MessageBoxW, it fails when I run the executable created by native-image. I used the following code:

(ns my-ns (:require
[tech.v3.datatype.ffi :as dt-ffi])
(:gen-class))
(defn string->utf-16 [str]
(dt-ffi/string->c str {:encoding :utf-16LE}))
(def windows-api (dt-ffi/define-library-interface 
{:MessageBoxW {:rettype :int32 :argtypes [['hWnd :pointer] ['lpText :pointer] ['lpCaption :pointer] ['uType :int32]]}}))
(def mb-ok 0)
(defn -main [& args]
(MessageBoxW (tech.v3.datatype.ffi.Pointer. 0) (string->utf-16 "This is an FFI call.") (string->utf-16 "Alert") mb-ok))

Compiling this code succeeds, but I get the following error when I run the generated executable: "Exception in thread "main" java.lang.Exception: Pointer value is zero!". When I replace [hWnd :pointer] in the library declaration with ['hWnd :size-t], and (tech.v3.datatype.ffi.Pointer. 0) with 0 in the function call, it works, but this means I cannot call MessageBoxW with a window handle if I need to later. How do I define a function that can either take a pointer to an object or a null pointer? There are also a lot of structs that have values like this.

cnuernber commented 2 years ago

Bold! I like it :-) - Use :pointer? in your function definition. This allows null pointers to pass through.

emassey0135 commented 2 years ago

Thank you, that fixes the issue. However, maybe you should mention what the :pointer? type is used for somewhere in the documentation for tech.v3.datatype.ffi, perhaps under "Things to Consider", since that would make this information easy to find.

cnuernber commented 2 years ago

100% agreed.