1
votes

I'm trying to use the CLISP FFI to call some Win32 functions.

I was able to make a call out and have that call a callback in Lisp, but now I can't figure out how to access the values passed to the callback.

One of the values is a pointer to a struct. I have the structure defined ok, I can print it and get a #<FOREIGN-VARIABLE #x00000000> or wrap it in foreign-address and get #<FOREIGN-ADDRESS #x00000000> (not the real value or addr in either), but when I try to deref it I get the following:

*** - FFI:DEREF is only allowed after FFI:FOREIGN-VALUE:
  (FFI:DEREF (FFI:FOREIGN-ADDRESS PRECT*))

I've tried to wrap the foreign-address call in foreign-value, tried to use with-c-place, and other things, but nothing works. I either get nothing printed (in which case I assume there was an error during the call and it borked), or I get that error.

The only documentation I've been able to find is this dffi reference but it has scant examples and the documentation for those functions isn't clear.

Anyone have any pointers? (Pun!) Should I switch to CFFI/UFFI or SBCL even?

Update

I got it to print the struct out once after wrapping the pointer in foreign-value reloading it in the REPL (without exiting), then it seemed to error in the callback for subsequent reloads.

Edit

Still can't get it working; here's the code:

(defpackage "WIN32")
(in-package "WIN32")
(use-package "FFI")

; Listen for a WM_DISPLAYCHANGE message to get notifications of when
; a monitor (setting) is added/removed/changed



(def-c-type BOOL     boolean)
(def-c-type CHAR     char)
(def-c-type DWORD    uint)
(def-c-type HANDLE   c-pointer)
(def-c-type HDC      HANDLE)
(def-c-type HMONITOR HANDLE)
(def-c-type HWND     HANDLE)
(def-c-type LONG     long)
(def-c-type LPARAM   c-pointer)
(def-c-type LPCSTR   c-string)
(def-c-type LPCTSTR  LPCSTR) ; LPCWSTR
;(def-c-type LPCWSTR  )
(def-c-type TCHAR    CHAR) ; WCHAR
;(def-c-type WCHAR    )


(defun symbol-to-keyword (sym)
  (intern (symbol-name sym) :keyword))


(defun affix-to-symbol (prefix sym suffix)
  (intern (concatenate 'string prefix (symbol-name sym) suffix)))


(defun car-symbol-to-keyword (pair)
  (cons
    (symbol-to-keyword (car pair))
    (cdr pair)))



(defmacro def-struct-type (name &rest fields)
  `(progn
    (def-c-struct ,name ,@fields)
    (def-c-type
      ,(affix-to-symbol "P" name "")
      (c-pointer ,name))
    (defconstant
      ,(affix-to-symbol "" name "-INSTANCE")
      '(c-struct ,name ,@(mapcar #'car-symbol-to-keyword fields)))
    ))


;typedef struct _RECT {
;  LONG left;
;  LONG top;
;  LONG right;
;  LONG bottom;
;} RECT, *PRECT;

(def-struct-type RECT
  (left   LONG)
  (top    LONG)
  (right  LONG)
  (bottom LONG))


;BOOL CALLBACK MonitorEnumProc(
;  _In_  HMONITOR hMonitor,
;  _In_  HDC hdcMonitor,
;  _In_  LPRECT lprcMonitor,
;  _In_  LPARAM dwData
;);

;BOOL EnumDisplayMonitors(
;  _In_  HDC hdc,
;  _In_  LPCRECT lprcClip,
;  _In_  MONITORENUMPROC lpfnEnum,
;  _In_  LPARAM dwData
;);


(def-call-out EnumDisplayMonitors
  (:name "EnumDisplayMonitors")
  (:library "User32.dll")
  (:arguments
    (hdc      HDC)
    (lprcClip PRECT)
    (lpfnEnum 
      (c-function
        (:arguments
          (hMonitor    HMONITOR)
          (hdcMonitor  HDC)
          (lprcMonitor PRECT)
          (dwData      LPARAM))
        (:return-type BOOL)
        (:language :stdc-stdcall)))
    (dwData   LPARAM))
  (:return-type BOOL)
  (:language :stdc-stdcall))
(export 'EnumDisplayMonitors)


(defun callback (hmon hdc* prect* data)
  (progn
    (format t "~A ~A ~A ~A~%" hmon hdc* (foreign-address prect*) data)
    t))

(EnumDisplayMonitors nil nil #'callback nil)
1

1 Answers

0
votes

Use slot and c-var-object

See slot and c-var-object.

More examples

You can find more examples in

Future

Indeed, these days sbcl is better maintained than clisp, so you might want to consider switching.