clisp-ole

OLE bindings for CLisp
git clone https://logand.com/git/clisp-ole.git/
Log | Files | Refs | README

ole.lisp (21183B)


      1 ;;; CLisp OLE Automation interface
      2 ;;;
      3 ;;; Copyright (C) 2005 Tomas Hlavaty <kvietaag@seznam.cz>
      4 ;;;
      5 ;;; This is Free Software, covered by the GNU GPL (v2)
      6 ;;; See http://www.gnu.org/copyleft/gpl.html
      7 
      8 (defpackage "OLE"
      9   (:use "LISP" "FFI")
     10   (:export "INIT" "DONE" "CREATE" "RELEASE"
     11 		   "INVOKE" "INVOKE-GET" "INVOKE-PUT" "INVOKE-METHOD"
     12 		   "WITH-OLE" "WITH-IUNKNOWN" "IUNKNOWN" "IDISPATCH"))
     13 
     14 (in-package "OLE")
     15 
     16 (pushnew :ole *features*)
     17 
     18 (default-foreign-language :stdc)
     19 
     20 (c-lines "
     21 #include <windows.h>
     22 #include <ole2.h>
     23 ")
     24 
     25 ;;; win32api constants are loaded from c header files
     26 
     27 (defconstant +cfiles+ "c:/cygwin/usr/include/w32api/*.h") ; update this!
     28 
     29 (defun load-win32api-constants (cfiles)
     30   (let ((n 0))
     31 	(dolist (filename (directory cfiles) n)
     32 	  (flet ((match (line regexp &optional hex)
     33 			   (multiple-value-bind (all key value)
     34 				   (regexp:match regexp line :extended t :ignore-case t)
     35 				 (when all
     36 				   (let ((sym (read-from-string
     37 							   (regexp:match-string line key))))
     38 					 (eval
     39 					  `(defconstant ,sym
     40 						,(read-from-string
     41 						  (concatenate 'string
     42 									   (if hex "#x" "")
     43 									   (regexp:match-string line value)))))
     44 					 (incf n))))))
     45 		(with-open-file (in filename)
     46 		  (loop for line = (read-line in nil nil)
     47 				while line
     48 				do (or (match line "^#define ([a-zA-Z0-9_]+) ([0-9]+)$")
     49 					   (match line "#define ([a-zA-Z0-9_]+) \\((-[0-9]+)\\)$")
     50 					   (match line "^#define ([a-zA-Z0-9_]+) 0x([0-9]+)$" t))))))))
     51 
     52 ;;(load-win32api-constants +cfiles+))
     53 
     54 ;;; types
     55 
     56 (defconstant +wencoding+ charset:unicode-16-little-endian) ; used by OLE
     57 
     58 (defconstant CP_ACP 0)
     59 (defconstant LOCALE_USER_DEFAULT 1024)
     60 (defconstant LOCALE_SYSTEM_DEFAULT 2048)
     61 (defconstant DISPATCH_METHOD 1)
     62 (defconstant DISPATCH_PROPERTYGET 2)
     63 (defconstant DISPATCH_PROPERTYPUT 4)
     64 (defconstant DISPID_PROPERTYPUT -3)
     65 
     66 (def-c-type HRESULT ulong)
     67 (def-c-type WORD ushort)
     68 (def-c-type DWORD ulong)
     69 (def-c-type PVOID (c-ptr-null nil))
     70 (def-c-type BSTR c-pointer)
     71 (def-c-type DISPID long)
     72 (def-c-type LCID DWORD)
     73 
     74 (def-c-enum VARTYPE
     75 	(VT_EMPTY 0) VT_NULL VT_I2 VT_I4 VT_R4 VT_R8 VT_CY VT_DATE VT_BSTR
     76 	VT_DISPATCH VT_ERROR VT_BOOL VT_VARIANT VT_UNKNOWN VT_DECIMAL
     77 	(VT_I1 16) VT_UI1 VT_UI2 VT_UI4 VT_I8 VT_UI8 VT_INT VT_UINT VT_VOID
     78 	VT_HRESULT VT_PTR VT_SAFEARRAY VT_CARRAY VT_USERDEFINED VT_LPSTR
     79 	VT_LPWSTR (VT_RECORD 36) (VT_INT_PTR 37) (VT_UINT_PTR 38) (VT_FILETIME 64)
     80 	VT_BLOB VT_STREAM VT_STORAGE VT_STREAMED_OBJECT VT_STORED_OBJECT
     81 	VT_BLOB_OBJECT VT_CF VT_CLSID (VT_BSTR_BLOB #xfff) (VT_VECTOR #x1000)
     82 	(VT_ARRAY #x2000) (VT_BYREF #x4000) (VT_RESERVED #x8000)
     83 	(VT_ILLEGAL #xffff) (VT_ILLEGALMASKED #xfff) (VT_TYPEMASK #xfff))
     84 
     85 (def-c-struct SAFEARRAYBOUND
     86   (cElements ULONG)
     87   (lLbound LONG))
     88 
     89 (def-c-struct SAFEARRAY
     90   (cDims USHORT)
     91   (fFeatures USHORT)
     92   (cbElements ULONG)
     93   (cLocks ULONG)
     94   (pvData PVOID)
     95   (rgsabound (c-array SAFEARRAYBOUND 1))) ; what about following bounds?
     96 
     97 ;;(sizeof 'SAFEARRAY)
     98 
     99 ;; (def-c-struct VARIANT
    100 ;;   (vt ushort) ;VARTYPE)
    101 ;;   (wReserved1 WORD)
    102 ;;   (wReserved2 WORD)
    103 ;;   (wReserved3 WORD)
    104 ;;   (val (c-union
    105 ;; 		(bstrVal BSTR)
    106 ;; 		(pdispVal c-pointer) ;(pdispVal LPDISPATCH)
    107 ;; 		(parray (c-ptr SAFEARRAY))
    108 ;; 		(dblVal double-float)))) ; to get the right size only
    109 
    110 (def-c-type VARIANT (c-array uint8 16)) ; must be equal to (%variant-size)
    111 
    112 ;;(sizeof 'VARIANT)
    113 
    114 (def-c-struct GUID
    115   (data1 ulong)
    116   (data2 ushort)
    117   (data3 ushort)
    118   (data4 (c-array uchar 8)))
    119 
    120 (def-c-var IID_NULL (:type GUID) (:name "IID_NULL") (:read-only t))
    121 (def-c-var IID_IDispatch (:type GUID) (:name "IID_IDispatch") (:read-only t))
    122 
    123 (def-c-enum CLSCTX
    124 	(CLSCTX_INPROC_SERVER 1) (CLSCTX_INPROC_HANDLER 2) (CLSCTX_LOCAL_SERVER 4)
    125 	(CLSCTX_INPROC_SERVER16 8) (CLSCTX_REMOTE_SERVER 16))
    126 
    127 (def-c-struct DISPPARAMS
    128   (rgvarg (c-array-ptr VARIANT))
    129   (rgdispidNamedArgs (c-array-ptr DISPID))
    130   (cArgs UINT)
    131   (cNamedArgs UINT))
    132 
    133 ;;; (de)initialisation
    134 
    135 (def-call-out %CoInitialize (:name "CoInitialize")
    136   (:arguments (reserved c-pointer)) (:return-type HRESULT))
    137 
    138 (def-call-out %CoUninitialize (:name "CoUninitialize"))
    139 
    140 (defun init ()
    141   (%CoInitialize nil))
    142 
    143 (defun done ()
    144   (%CoUninitialize))
    145 
    146 (defparameter *ole-objects* nil)
    147 
    148 (defmacro with-ole (vars &body body)
    149   `(let ((*ole-objects* (make-hash-table :key-type 'foreign-address
    150 										 :value-type 'iunknown)))
    151 	(ole:init)
    152 	(unwind-protect (let* ,vars ,@body)
    153 	  (maphash #'(lambda (key val)
    154 				   (declare (ignore key))
    155 				   (ole%Release val))
    156 			   *ole-objects*)
    157 	  (clrhash *ole-objects*)
    158 	  (ole:done))))
    159 
    160 ;;; objects
    161 
    162 (def-call-out %CLSIDFromProgID (:name "CLSIDFromProgID")
    163   (:arguments (wprogid c-pointer) (clsid (c-ptr GUID) :out :alloca))
    164   (:return-type HRESULT))
    165 
    166 (def-call-out %CoCreateInstance (:name "CoCreateInstance")
    167   (:arguments (clsid (c-ptr GUID)) (unknown c-pointer) (context DWORD)
    168 			  (iid c-pointer) (dispatch c-pointer))
    169   (:return-type HRESULT))
    170 
    171 (defmacro with-wstring ((wstr str) &body body)
    172   `(with-foreign-string (,wstr elems bytes ,str :encoding +wencoding+)
    173 	(declare (ignore elems bytes))
    174 	,@body))
    175 
    176 (defun create (progid &optional raw)
    177   (with-wstring (wprogid progid)
    178 	(multiple-value-bind (result clsid)
    179 		(%CLSIDFromProgID wprogid)
    180 	  (with-c-var (dispatch 'c-pointer)
    181 		(let ((result (%CoCreateInstance clsid nil CLSCTX_LOCAL_SERVER
    182 										 (c-var-address IID_IDispatch)
    183 										 (c-var-address dispatch))))
    184 		  (when (= 0 result) ; ok
    185 			(if raw
    186 				dispatch
    187 				(make-instance 'IDispatch :interface dispatch))))))))
    188 
    189 (defmacro with-c-pointer ((var ptr type) &body body)
    190   (let ((pvar (gensym)))
    191 	`(with-c-var (,pvar 'c-pointer)
    192 	  (setf ,pvar ,ptr)
    193 	  (let ((,var (cast ,pvar ,type)))
    194 		,@body))))
    195 
    196 (defmacro def-ole-method (name (this type) niface iface-size nfn vtable-size
    197 						  &key arguments return-type)
    198   `(defmethod ,(intern (concatenate 'string "OLE%" (symbol-name name)) "OLE")
    199 	,(append (list (list this type))
    200 			 (loop for arg in arguments
    201 				   when (not (eq :out (third arg)))
    202 				   collect (first arg)))
    203 	;; with interface
    204 	(with-c-pointer (iface (interface ,this)
    205 					 '(c-ptr (c-array-max c-pointer ,iface-size)))
    206 	  ;; with vtable
    207 	  (with-c-pointer (vtable (aref iface ,niface)
    208 							  '(c-ptr (c-array-max c-pointer ,vtable-size)))
    209 		;; with method
    210 		(with-c-pointer (fn (aref vtable ,nfn)
    211 							'(c-function ,(append
    212 										   '(:arguments (interface c-pointer))
    213 										   arguments)
    214 							  (:return-type ,return-type)))
    215 		  ;; call the method
    216 		  (funcall fn (interface ,this)
    217 				   ,@(loop for arg in arguments
    218 						   when (not (eq :out (third arg)))
    219 						   collect (first arg))))))))
    220 
    221 ;;; IUnknown
    222 
    223 ;;; Q: are we always calling the right "virtual" method?
    224 
    225 (defclass IUnknown ()
    226   ((interface :type 'foreign-address :initarg :interface :accessor interface))
    227   (:documentation "OLE IUnknown interface."))
    228 
    229 (defmethod initialize-instance :after ((self IUnknown) &rest args)
    230   (declare (ignore args))
    231   (let ((iface (interface self)))
    232 	(if iface ;; what about duplicate objects with same iface?
    233 		(setf (gethash iface *ole-objects*) self)
    234 		(error "IUnknown interface pointer cannot be null!" self))))
    235 
    236 (def-ole-method QueryInterface (this IUnknown) 0 1 0 3
    237 				:arguments ((iid (c-ptr GUID)) (object c-pointer)) ; why c-pointer only?
    238 				:return-type HRESULT)
    239 ;;(def-ole-method QueryInterface (this IUnknown) 0 1 0 3
    240 ;;				:arguments ((iid (c-ptr GUID)) (object (c-ptr c-pointer)))
    241 ;;				:return-type HRESULT)
    242 (def-ole-method AddRef         (this IUnknown) 0 1 1 3 :return-type ulong)
    243 (def-ole-method Release        (this IUnknown) 0 1 2 3 :return-type ulong)
    244 
    245 (defmacro with-iunknown ((var cmd) &body body)
    246   `(let ((,var ,cmd))
    247 	(unwind-protect (progn ,@body)
    248 	  (ole%Release ,var))))
    249 
    250 ;;(with-iunknown (a (create "excel.application")) t)
    251 
    252 ;;; too complicated usage QueryInterface, handle arg conversion in def-ole-method?
    253 ;; (with-iunknown (a (create "excel.application"))
    254 ;;   (with-c-var (p 'c-pointer)
    255 ;; 	(let ((result (QueryInterface a IID_IDispatch (c-var-address p)))) ; addref automatically!
    256 ;; 	  (format t "QueryInterface ~a ~a" result p)
    257 ;; 	  (when (= 0 result) ; ok but I have p=nil:-(
    258 ;; 		(let ((o (make-instance 'IDispatch :interface p)))
    259 ;; 		  (Release o)
    260 ;; 		  o)))))
    261 
    262 ;;; IDispatch
    263 
    264 (defclass IDispatch (IUnknown)
    265   ()
    266   (:documentation "OLE IDispatch interface."))
    267 
    268 ;;(def-ole-method GetTypeInfoCount (this IDispatch) 0 1 3 7 :arguments ((count (c-ptr uint))) :return-type HRESULT)
    269 ;;(def-ole-method GetTypeInfo      (this IDispatch) 0 1 4 7 :arguments ((type uint) ()):return-type ulong)
    270 
    271 (def-ole-method GetIDsOfNames
    272 	(this IDispatch) 0 1 5 7
    273 	:arguments ((iid (c-ptr GUID)) (pwname c-pointer) (n uint) (locale LCID)
    274 				(id (c-ptr DISPID) :out :alloca))
    275 	:return-type HRESULT)
    276 
    277 (def-ole-method Invoke
    278 	(this IDispatch) 0 1 6 7
    279 	:arguments ((id DISPID) (iid (c-ptr GUID)) (locale LCID)
    280 				(type WORD) (dp (c-ptr DISPPARAMS))
    281 				(result (c-ptr VARIANT) :out :alloca)
    282 				(excepinfo c-pointer) (nn c-pointer))
    283 	:return-type HRESULT)
    284 
    285 ;;(init)
    286 ;;(setq a (make-instance 'IDispatch :interface nil))
    287 ;;(setq a (create "excel.application"))
    288 ;;(QueryInterface a)
    289 ;;(interface a)
    290 
    291 ;;; variants
    292 
    293 (def-call-out %VariantClear (:name "VariantClear")
    294   (:arguments (this (c-ptr VARIANT) :in-out)))
    295 
    296 (def-call-out %VariantCopy (:name "VariantCopy")
    297   (:arguments (out (c-ptr VARIANT) :out :alloca)
    298 			  (in (c-ptr VARIANT))))
    299 
    300 (def-call-out %VariantChangeType1 (:name "VariantChangeType")
    301   (:arguments (out (c-ptr VARIANT) :out :alloca)
    302 			  (in (c-ptr VARIANT))
    303  			  (n ushort) ; have to be 1!
    304 			  (type VARTYPE)))
    305 
    306 (def-call-out %SysAllocString (:name "SysAllocString")
    307   (:arguments (bstr BSTR)) (:return-type c-pointer))
    308 
    309 (def-call-out %SysFreeString (:name "SysFreeString")
    310   (:arguments (bstr BSTR)))
    311 
    312 (def-call-out %SysStringLen (:name "SysStringLen")
    313   (:arguments (bstr BSTR)) (:return-type uint))
    314 
    315 (def-call-out %WideCharToMultiByte (:name "WideCharToMultiByte")
    316   (:arguments (cp uint) (x DWORD) (bstr BSTR) (nbstr int)
    317 			  (str c-pointer) (nstr int) (cstr c-pointer) (pbool c-pointer))
    318   (:return-type int))
    319 ;;(UINT,DWORD,LPCWSTR,int,LPSTR,int,LPCSTR,LPBOOL); 
    320 
    321 (defun bstr2lisp (bstr)
    322   (let ((n (%SysStringLen bstr)))
    323 	(with-c-var (str `(c-array char ,n))
    324 	  (%WideCharToMultiByte CP_ACP 0 bstr -1 (c-var-address str) n nil nil)
    325 	  (ext:convert-string-from-bytes str custom:*default-file-encoding*))))
    326 
    327 (defmacro with-variant ((v a) &body body)
    328   `(with-c-var (,v 'VARIANT)
    329 	(let ((,a (c-var-address ,v)))
    330 	  ,@body)))
    331 
    332 (c-lines "
    333 void variant_set_string (VARIANT *this, BSTR value)
    334 {
    335   this->vt = VT_BSTR;
    336   this->bstrVal = value;
    337 }
    338 
    339 void variant_set_dispatch (VARIANT *this, IDispatch *value)
    340 {
    341   this->vt = VT_DISPATCH;
    342   this->pdispVal = value;
    343 }
    344 
    345 void variant_set_safearray (VARIANT *this, SAFEARRAY *value)
    346 {
    347   this->vt = VT_ARRAY | VT_VARIANT; //VT_SAFEARRAY;
    348   this->parray = value;
    349 }
    350 
    351 BSTR variant_get_string (VARIANT *this)
    352 {
    353   return this->bstrVal;
    354 }
    355 
    356 IDispatch *variant_get_dispatch (VARIANT *this)
    357 {
    358   return this->pdispVal;
    359 }
    360 
    361 SAFEARRAY *variant_get_safearray (VARIANT *this)
    362 {
    363   return this->parray;
    364 }
    365 
    366 int variant_size (void)
    367 {
    368   return sizeof (VARIANT);
    369 }
    370 
    371 int variant_type (VARIANT *this)
    372 {
    373   return this->vt;
    374 }
    375 ")
    376 
    377 (def-call-out %variant-set-string (:name "variant_set_string")
    378   (:arguments (this (c-ptr VARIANT) :in-out) (bstr BSTR)))
    379 
    380 (def-call-out %variant-set-dispatch (:name "variant_set_dispatch")
    381   (:arguments (this (c-ptr VARIANT) :in-out) (dispatch c-pointer)))
    382 
    383 (def-call-out %variant-set-safearray (:name "variant_set_safearray")
    384   (:arguments (this (c-ptr VARIANT) :in-out) (safearray c-pointer)))
    385 
    386 (def-call-out %variant-get-string (:name "variant_get_string")
    387   (:arguments (this (c-ptr VARIANT))) (:return-type BSTR))
    388 
    389 (def-call-out %variant-get-dispatch (:name "variant_get_dispatch")
    390   (:arguments (this (c-ptr VARIANT))) (:return-type c-pointer))
    391 
    392 (def-call-out %variant-get-safearray (:name "variant_get_safearray")
    393   (:arguments (this (c-ptr VARIANT))) (:return-type c-pointer))
    394 
    395 (def-call-out %variant-size (:name "variant_size")
    396   (:return-type int))
    397 
    398 (def-call-out %variant-type (:name "variant_type")
    399   (:arguments (this (c-ptr VARIANT))) (:return-type int))
    400 
    401 (defun make-variant ()
    402   (make-array (%variant-size)
    403 			  :element-type '(unsigned-byte 8) :initial-element 0))
    404 
    405 ;;(%variant-type (%variant-set-string (make-variant) nil))
    406 
    407 (defun variant-string (val)
    408   (with-wstring (wstr val)
    409 	(%variant-set-string (make-variant) (%SysAllocString wstr))))
    410 
    411 ;;(%variant-type (variant-string "1"))
    412 
    413 (defun variant-dispatch (val)
    414   (%variant-set-dispatch (make-variant) (interface val)))
    415 
    416 (defun variant-safearray (val)
    417   (%variant-set-safearray (make-variant) (safearray-from-lisp val)))
    418 
    419 (defun variant-convert (var vt)
    420   (%VariantChangeType1 var 1 vt))
    421 
    422 ;;(variant-convert (variant-string "1") VT_R8)
    423 ;;(variant-convert (variant-string "1.23") VT_R8)
    424 
    425 (defun variant-number (val)
    426   (variant-convert (variant-string (format nil "~a" val)) VT_R8))
    427 
    428 ;;(variant-number 1)
    429 ;;(variant-number 1.23)
    430 ;;(variant-convert (variant-string "1") VT_R8)
    431 ;;(variant-convert (variant-number 1) VT_BSTR)
    432 ;;(bstr2lisp (%variant-get-string (variant-convert (variant-number 1) VT_BSTR)))
    433 ;;(bstr2lisp (%variant-get-string (variant-convert (variant-number 1.23) VT_BSTR)))
    434 ;;(bstr2lisp (%variant-get-string (variant-convert (variant-number 3.141592653589) VT_BSTR)))
    435 
    436 (defun variant-new (val)
    437   (ctypecase val
    438 	(real (variant-number val))
    439 	(string (variant-string val))
    440 	(idispatch (variant-dispatch val))
    441 	(array (variant-safearray val))))
    442 
    443 ;;(variant-new 1)
    444 ;;(variant-new 1.23)
    445 ;;(variant-new 3.141592653589)
    446 ;;(variant-new "hello")
    447 
    448 (defun variant-get (var &optional raw)
    449   (ecase (enum-from-value 'VARTYPE (%variant-type var))
    450 	(VT_EMPTY (values-list nil))
    451 	(VT_NULL nil)
    452 	(VT_BSTR (bstr2lisp (%variant-get-string var)))
    453 	(VT_DISPATCH
    454 	 (let ((dispatch (%variant-get-dispatch var)))
    455 	   (if raw
    456 		   dispatch
    457 		   (make-instance 'IDispatch :interface dispatch))))
    458 	((VT_DATE VT_CY) ; convert to string
    459 	 (variant-get (variant-convert var VT_BSTR)))
    460 	(VT_BOOL ; read from string and convert to nil|t
    461 	 (let ((tmp (variant-convert var VT_BSTR)))
    462 	   (values (if (= 0 (read-from-string (bstr2lisp (%variant-get-string tmp))))
    463 				   nil t))))
    464 	((VT_I2 VT_I4 VT_R4 VT_R8 VT_DECIMAL VT_I1 VT_UI1 VT_UI2 VT_UI4
    465 			VT_I8 VT_UI8 VT_INT VT_UINT) ; read from string
    466 	 (let ((tmp (variant-convert var VT_BSTR)))
    467 	   (values (read-from-string (bstr2lisp (%variant-get-string tmp))))))
    468 	(VT_SAFEARRAY (safearray-to-lisp (%variant-get-safearray var) raw))))
    469 
    470 ;;(variant-get (variant-new 1))
    471 ;;(variant-get (variant-new 1.23))
    472 ;;(variant-get (variant-new 3.141592653589))
    473 ;;(variant-get (variant-new "hello"))
    474 ;;(variant-get (variant-new #2A((1 2) ("a" "b"))))
    475 
    476 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    477 
    478 (deftype variant () '(array (unsigned-byte 8) 16))
    479 ;;(make-array 2 :element-type 'VARIANT)
    480 
    481 (defun invoke (this type name args &optional raw)
    482   (let* ((n (length args))
    483 		 (params (make-array n :element-type 'VARIANT)))
    484 	;; set up params
    485 	(loop for arg in args
    486 		  for i from 0
    487 		  do (setf (aref params i) (variant-new arg)))
    488 	;; get DISPID for name passed
    489 	(with-wstring (wname name)
    490 	  (with-c-var (wname2 'c-pointer)
    491 		(setf wname2 wname)
    492 		(multiple-value-bind (hresult id)
    493 			(ole%GetIDsOfNames this IID_NULL (c-var-address wname2) 1 LOCALE_USER_DEFAULT)
    494 		  ;; call invoke
    495 		  (let ((dp (make-DISPPARAMS :cArgs n :rgvarg params
    496 									 :cNamedArgs 0 :rgdispidNamedArgs nil)))
    497 			;; handle special-case for property-puts!
    498 			(when (not (zerop (logand type DISPATCH_PROPERTYPUT)))
    499 			  (setf (DISPPARAMS-cNamedArgs dp) 1)
    500 			  (setf (DISPPARAMS-rgdispidNamedArgs dp)
    501 					(make-array 1 :initial-element DISPID_PROPERTYPUT)))
    502 			(multiple-value-bind (hresult result)
    503 				(ole%Invoke this id IID_NULL LOCALE_SYSTEM_DEFAULT type dp nil nil)
    504 			  ;; convert result to lisp
    505 			  (variant-get result raw))))))))
    506 
    507 (defun invoke-get (this name &rest args)
    508   (invoke this DISPATCH_PROPERTYGET name args))
    509 
    510 (defun invoke-put (this name &rest args)
    511   (invoke this DISPATCH_PROPERTYPUT name args))
    512 
    513 (defun invoke-method (this name &rest args)
    514   (invoke this DISPATCH_METHOD name args))
    515 
    516 ;;(with-ole ()
    517 ;;  (with-iunknown (excel (create "excel.application"))
    518 ;;	(invoke-put excel "visible" 1)))
    519 
    520 ;;; SafeArray support
    521 ;;;
    522 ;;; A SafeArray is represented by c-pointer as it is created and
    523 ;;; destroyed outside lisp.
    524 
    525 (def-call-out %SafeArrayCreate (:name "SafeArrayCreate")
    526   (:arguments (type VARTYPE) (ndim uint)
    527 			  (bounds (c-array-ptr SAFEARRAYBOUND)))
    528   (:return-type c-pointer))
    529 
    530 (def-call-out %SafeArrayPutElement (:name "SafeArrayPutElement")
    531   (:arguments (safearray c-pointer) (subscripts (c-array-ptr long))
    532 			  (value (c-ptr VARIANT))))
    533 
    534 (def-call-out %SafeArrayGetElement (:name "SafeArrayGetElement")
    535   (:arguments (safearray c-pointer) (subscripts (c-array-ptr long))
    536 			  (value (c-ptr VARIANT) :out :alloca)))
    537 
    538 (def-call-out %SafeArrayDestroy (:name "SafeArrayDestroy")
    539   (:arguments (safearray c-pointer)))
    540 
    541 (def-call-out %SafeArrayGetDim (:name "SafeArrayGetDim")
    542   (:arguments (safearray c-pointer)) (:return-type uint))
    543 
    544 (def-call-out %SafeArrayGetLBound (:name "SafeArrayGetLBound")
    545   (:arguments (safearray c-pointer) (dim uint)
    546 			  (lbounds (c-ptr long) :out :alloca)))
    547 
    548 (def-call-out %SafeArrayGetUBound (:name "SafeArrayGetUBound")
    549   (:arguments (safearray c-pointer) (dim uint)
    550 			  (ubound (c-ptr long) :out :alloca)))
    551 
    552 (defun safearray-create (dimensions)
    553   (let* ((rank (length dimensions))
    554 		 (bounds (make-array rank :element-type 'SAFEARRAYBOUND)))
    555 	(loop for n in dimensions
    556 		  for i from 0
    557 		  do (setf (aref bounds i) (make-SAFEARRAYBOUND :celements n :llbound 0)))
    558 	(%SafeArrayCreate VT_VARIANT rank bounds)))
    559 
    560 (defmacro with-safearray (safearray &body body)
    561   `(unwind-protect (progn ,@body)
    562 	(%SafeArrayDestroy ,safearray)))
    563 
    564 ;;; Unfortunatelly, I can't use ROW-MAJOR_AREF for iteration through
    565 ;;; array elements because anything like that doesn't exist in winapi
    566 ;;; and I can't restore subscripts from index.
    567 ;;;
    568 ;;;   (let ((a (make-array '(1 2 3))))
    569 ;;;     (dotimes (i (array-total-size a) a)
    570 ;;; 	  (setf (row-major-aref a i) i)))
    571 
    572 (defun for-all-elements (dims fn)
    573   "Call FN for all elements of an array with dimensions DIMS."
    574   (let* ((rank (length dims))
    575 		 (mods (make-array rank :element-type 'integer)))
    576 	;; set up mods
    577 	(dotimes (i rank)
    578 	  (setf (aref mods i)
    579 			(if (= 0 i)
    580 				1
    581 				(* (aref mods (1- i))
    582 				   (nth (1- i) dims)))))
    583 	;; enumerate endices
    584 	(dotimes (i (reduce #'* dims)) ; for each element of array
    585 	  (let ((subscripts nil))
    586 		(dotimes (j rank) ; for each dimension
    587 		  (push (mod (truncate i (aref mods j))
    588 					 (nth j dims))
    589 				subscripts))
    590 		(funcall fn (nreverse subscripts))))))
    591 
    592 ;;(for-all-elements '(2 3) (lambda (subscripts) (format t "~s~%" subscripts)))
    593 
    594 (defun safearray-from-lisp (array)
    595   "Convert lisp ARRAY to SAFEARRAY."
    596   (let ((safearray (safearray-create (array-dimensions array))))
    597 	(for-all-elements
    598 	 (array-dimensions array)
    599 	 (lambda (subscripts)
    600 	   (let ((variant (variant-new (apply #'aref array subscripts)))
    601 			 (subs (make-array (array-rank array) :initial-contents subscripts)))
    602 		 (%SafeArrayPutElement safearray subs variant))))
    603 	safearray))
    604 
    605 (defun safearray-dimensions (safearray)
    606   (let ((dims nil)
    607 		(rank (%SafeArrayGetDim safearray)))
    608 	(dotimes (i rank (nreverse dims))
    609 	  (let ((lbound (%SafeArrayGetLBound safearray (1+ i)))
    610 			(ubound (%SafeArrayGetUBound safearray (1+ i))))
    611 		(push (- ubound lbound -1) dims)))))
    612 
    613 (defun safearray-to-lisp (safearray &optional raw)
    614   "Convert SAFEARRAY to lisp ARRAY."
    615   (let ((array (make-array (safearray-dimensions safearray))))
    616 	(for-all-elements
    617 	 (array-dimensions array)
    618 	 (lambda (subscripts)
    619 	   (let ((subs (make-array (array-rank array) :initial-contents subscripts)))
    620 		 (setf (apply #'aref array subscripts)
    621 			   (variant-get (%SafeArrayGetElement safearray subs) raw)))))
    622 	array))
    623 
    624 ;; (let ((array (make-array '(4 2 3)
    625 ;; 						 :initial-contents '((("a" "b" "c") (1 2 3))
    626 ;; 											 (("d" "e" "f") (3 1 2))
    627 ;; 											 (("g" "h" "i") (2 3 1))
    628 ;; 											 (("j" "k" "l") (0 0 0))))))
    629 ;;   (let ((safearray (safearray-from-lisp array)))
    630 ;; 	(with-safearray safearray
    631 ;; 	  (safearray-to-lisp safearray))))
    632 
    633 ;;; wrappers and utilities
    634 
    635 ;; (def-ole-interface IUnknown ()
    636 ;;   (QueryInterface (p a b))
    637 ;;   (AddRef (p))
    638 ;;   (Release (p)))
    639 
    640 ;; (def-ole-interface IDispatch (IUnknown)
    641 ;;   (GetTypeInfoCount (p a))
    642 ;;   (GetTypeInfo (p a b c))
    643 ;;   (GetIDsOfNames (p a b c d e))
    644 ;;   (Invoke (p a b c d e f g h)))