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)))