Depending on how your application needs to interoperate with other software, you may want to build it as a DLL (also referred to as a dynamic library) rather than an executable.
Supply the names of your library's exports in a list value for the deliver keyword :dll-exports. Each name in :dll-exports should be a string naming a Lisp function defined by fli:define-foreign-callable.
The deliver function argument should be
nil, because a dynamic library does not have a startup function.
Supply the file type of the delivered image in the deliver file argument if necessary.
As when delivering a LispWorks executable, start at deliver level 0. Increase the delivery level, if desired, after you have debugged your library. Whenever possible, debug your code running in the LispWorks development image. If the problem only occurs when your code runs inside a dynamic library, you may be able to debug it on your development machine in a dynamic library created by save-image rather than deliver.
A Microsoft Windows application should use
LoadLibrary to load the DLL and
GetProcAddress to find the address of the exported names. On other platforms the application should use
On some platforms there are special requirements for a program that loads a LispWorks dynamic library, as follows:
The program should be linked with
The program should be linked with
The program should be compiled and linked multi-threaded, for example using the
No special requirements.
For more information about the behavior of LispWorks dynamic libraries see the chapter "LispWorks as a dynamic library" in the LispWorks® User Guide and Reference Manual.
The script below creates
-------------------- hello.lisp ------------------------- (in-package "CL-USER") (load-all-patches) ;; The signature of this function is suitable for use with ;; rundll32.exe. (fli:define-foreign-callable ("Hello" :calling-convention :stdcall) ((hwnd w:hwnd) (hinst w:hinstance) (string :pointer) (cmd-show :int)) (capi:display-message "Hello world") ;; quit when library's job is done (dll-quit)) (deliver nil "hello" 0 :dll-exports '("Hello") :interface :capi) ---------------------------------------------------------
You can build the DLL with this command line:
MS-DOS> lispworks-8-0-0-x86-win32.exe -build hello.lisp
and you can test it with this command line:
The Application Builder tool provides another way to build and test
hello.lispand do Works > Build > Build to build the DLL.
rundll32in the Execute pane, enter
hello.dll,Helloin the Arguments pane, and press OK to test the library.
See the example in the LispWorks library at:
This example creates a LispWorks dynamic library and also a test program for loading it on non-Windows platforms.
To build and run the example, follow the instructions in
This example builds a dynamic library which in principle could be loaded by any application and called to calculate square numbers.
For illustrative purposes, we show how to load the dynamic library into the LispWorks development image. This illustrates some platform-specific initialization. Then we use the library, ensure it exits cleanly, and finally delete the dynamic library file.
Note that on non-Windows platforms, to deliver a dynamic library, the build machine must have a C compiler installed.
For convenience the code is presented without external files. To run it, copy each form in turn and enter it at the Listener prompt.
(defvar *dynamic-library-path* (merge-pathnames (make-pathname :name "CalculateSquareExample" :type scm::*object-file-suffix*) (get-temp-directory)))
(defun save-dynamic-library () (let* ((file (open-temp-file :file-type "lisp")) (ns (namestring file))) (format file " (fli:define-foreign-callable (calculate-square :result-type :int) ((arg :int)) (* arg arg)) (deliver nil ~s 5 :dll-exports '(\"calculate_square\"))" (namestring *dynamic-library-path*)) (close file) (sys:call-system-showing-output (list (lisp-image-name) "-build" ns )) (delete-file file nil)))
(fli:define-foreign-function (my-quit-lispworks "QuitLispWorks") ((force :int) (milli-timeout :int)) :result-type :int ;; specifying :module ensures the foreign function finds ;; the function in our module :module 'my-dynamic-library) (fli:define-foreign-function (my-init-lispworks "InitLispWorks") ((milli-timeout :int) (base-address (:pointer-integer :int)) (reserve-size (:pointer-integer :int)) ; really size_t ) :result-type :int :module 'my-dynamic-library) (fli:define-foreign-function calculate-square ((arg :int)) :result-type :int :module 'my-dynamic-library)
(defun run-the-dynamic-library () (fli:register-module 'my-dynamic-library :connection-style :immediate :file-name *dynamic-library-path*) ;; Windows and macOS can detect and resolve memory clashes. ;; On other platforms, tell the library to load at different ;; address (that is, relocate) because otherwise it will use ;; the same address as the running LispWorks development image. ;; Relocation may be needed when loading a LispWorks dynamic ;; library in other applications. #-(or mswindows darwin) (my-init-lispworks 0 #+lispworks-64bit #x5000000000 #+lispworks-32bit #x50000000 0) (dotimes (x 4) (format t "square of ~d = ~d~%" x (calculate-square x))) (my-quit-lispworks 0 1000) (fli:disconnect-module 'my-dynamic-library))
Check the output to see that it computed square numbers.
(delete-file *dynamic-library-path* nil)
On non-Windows platforms, you can supply files to be included in the library via the deliver keyword argument :dll-added-files. This is useful if you need to write wrappers around calls into the library.
You can specify whether your LispWorks dynamic library initializes itself automatically on loading with the deliver keyword argument :automatic-init. For more information see "Initialization of the dynamic library" in the LispWorks® User Guide and Reference Manual.
Delivery User Guide - 01 Dec 2021 19:35:04