Using the APIs described below, Creo Elements/Direct Modeling extensions written in Lisp can communicate with COM/.NET add-ins or with generic COM/.NET components. Developers can use this to drive COM or .NET components from Lisp and extend Creo Elements/Direct Modeling's built-in functionality.
All Lisp interoperability APIs are in the Lisp package .net. To call such an API, either the package has to specified explicitly, or its symbols must be made available via use-package:
;; Alternative 1: Prefix the API call with the package name (.net:get-property app "Version") ;; Alternative 2: use-package (in-package :myextension) (use-package :.net) ... (get-property app "Version")
The available APIs will work with all COM/.NET components which are scriptable, i.e. those which provide an automation (IDispatch) interface. When working with automation objects, the Lisp interoperability layer and the Lisp garbage collector automatically take care of COM-style reference counting. Automation objects which have been instantiated through Lisp, but are no longer used from Lisp code, are released automatically the next time the Lisp garbage collector runs.
Caveat: References to automation objects in global Lisp variables will keep the automation object alive for as long as the client (in this case: Creo Elements/Direct Modeling) runs!
Functionality in pure .NET components can be called from Lisp by first registering the .NET component as a COM component (using the regasm tool) and then using the .NET component through its COM wrapper.
For most of the APIs presented below, the interoperability layer will (by default) handle COM or .NET errors by reporting a run-time error to the user, i.e. a message box with error details will be displayed.
Run-time error signalling can be suppressed by setting the variable .net:*report-errors* to a value of nil. In this case, errors will be reported via the function's return value, i.e. the caller must take care to check the return values and act accordingly.
Instantiates an arbitrary automation object.
(.net:create-object serverspec)
If the specified automation server is not running yet, it will be loaded in the background. Otherwise, a reference to an already running server instance is returned.
(setq app (.net:create-object "CoCreate.OsdmServer")) (setq excel (.net:create-object "Excel.Application")) (setq FileSystemObject (.net:create-object "0D43FE01-F093-11CF-8940-00A0C9054228"))
Connect to the IApplication interface of the Creo Elements/Direct Modeling object model.
(.net:osdm-application)
Using the .net:osdm-application API is the recommended way to retrieve a reference to the IApplication interface of the Creo Elements/Direct Modeling object model.
(setq app (.net:osdm-application))
Retrieves a reference to an already running automation object server.
(.net:get-active-object serverspec)
Retrieves a reference to an already running instance of the specified automation object server. If the automation object server is not running, no attempts will be made to load or start it.
(setq excel (.net:get-active-object "Excel.Application"))
Retrieves a reference to an automation object through a moniker.
(.net:get-object moniker)
Retrieves a reference to an automation object which is described by a moniker - in most cases, a filename.
(setq excel (.net:get-object "c:\\temp\\spreadsheet.xls"))
When calling functions in interfaces implemented by automation servers, Lisp objects can be passed as parameters. Those objects are converted into automation-compatible types (variants) in the Lisp interoperability layer. On the way back, values returned from the automation functions are converted into Lisp objects.
Sometimes, there is no direct 1:1 relationship between Lisp datatypes and automation datatypes. For example, there is no equivalent of a Lisp list in an automation server. Also, the Lisp interoperability layer does not try to exhaustively map all conceivable types, but only a subset which covers the vast majority of all practical cases.
When calling COM/.NET functionality from Lisp, the interoperability layer converts Lisp types into automation-compatible types (variants) according to the following table:
Lisp type | Automation type |
---|---|
Keyword :empty | VT_EMPTY |
Keyword :null | VT_NULL |
string | VT_BSTR |
fixnum | VT_I4 |
short-float | VT_R4 |
long-float, ratio | VT_R8 |
character | VT_UI2 |
list, vector | VT_VECTOR|VT_VARIANT |
LCOMINTERFACE | VT_DISPATCH |
.NET:NOTHING | VT_DISPATCH with a null pointer |
t/nil | VT_BOOL |
symbol | Reference parameter |
SEL_ITEM structure ((:selection-item sel_item)) * | VT_DISPATCH carrying an ISelectionItem interface pointer |
list of SEL_ITEMs ((:selection-item sel_item))* | VT_ARRAY of VT_DISPATCH elements, each carrying an ISelectionItem interface pointer |
other Lisp structures | marshalling not supported. |
Automation functions can have multiple "out" (return) parameters. For example, there could be a function which returns three double values specifying the X, Y and Z component of a vector:
HRESULT GetVector([out]double *x, [out]double *y, [out]double *y); /* IDL notation */
Such a function can be called from Lisp like this:
(let ((x 0.0) (y 0.0) (z 0.0)) (.net:invoke obj "GetVector" 'x 'y 'z))
Specifying a symbol as a parameter signals to the Lisp interoperability layer that a reference to a variable should be passed, rather than the variable itself. The called function can use those references to return multiple values.
*)In the Lisp-based Developer's Kit (also known as "Integration Kit"), objects in Creo Elements/Direct Modeling (be they structural, topological or geometrical) are usually referred to through a Lisp structure called SEL_ITEM. This structure serves as a handle to an object and is returned by Integration Kit functions when a reference to an object is requested, for example as the result of a selection in a dialog.
The .NET/COM API provides the SelectionItem object and the associated ISelectionItem interface as the equivalent of the SEL_ITEM structure in the Lisp world. Because of the special role of SEL_ITEM structures in the Integration Kit, the Lisp interoperability layer provides a shortcut notation which directly converts Lisp's SEL_ITEM structure into SelectionItem objects.
(.net:invoke obj "SomeFunction" (:selection-item sel_item))
When returning automation-compatible types from COM/.NET functionality to Lisp, the interoperability layer converts them into Lisp types according to the following table:
Automation type | Lisp type |
---|---|
VT_EMPTY | Keyword :empty |
VT_NULL | Keyword :null |
VT_U1, VT_I1, VT_UI2, VT_I2 | character |
VT_I4, VT_UI4, VT_INT, VT_UINT | fixnum |
VT_ERROR | fixnum |
VT_BSTR | string |
VT_R4 | short-float |
VT_R8 | long-float |
VT_BOOL | t/nil |
VT_DISPATCH, VT_UNKNOWN 1 2 | LCOMINTERFACE, SEL_ITEM or nil |
VT_DATE, VT_CY | string |
VT_VECTOR, 1-dimensional VT_ARRAY | vector |
1)When calling .NET APIs that return selection item objects via invoke, invoke-addin or get-property, those objects are returned either as a LCOMINTERFACE, or can be requested to be converted into SEL_ITEM structures. Conversion to SEL_ITEM requires to pass the :raw-return-values nil key-value pair as first parameter to the function call.
2)A VT_DISPATCH or VT_UNKNOWN pointer that is null will be converted into nil. Note that this conversion is not symmetric, i.e. nil will convert to a VT_BOOL. In order to pass a null pointer to a COM function, use the .NET:nothing constant.
Invokes a function in an interface implemented by an automation server.
(.net:invoke objectref function &optional :raw-return-values t/nil args...)
Converts all args into automation-compatible data types according to the table above, then calls function in the automation object specified by objectref.
If the called function has only one return value, it is returned as the result of the invoke call.
The caller can pass variables by reference rather by value, which allows automation functions to have multiple return values. To indicate by-reference semantics, use the following pattern:
(setf foo 42) ;; establish integer variable (invoke obj "Foobar" 'foo) ;; foo passed by reference
All return values from automation functions are converted according to Conversion of automation datatypes into Lisp objects.
By default, if an error occurs while calling the specified function, a Lisp run-time error will be reported. If .net:*report-errors* has been set to nil, invoke returns nil when an error occurs.
(let ((app (.net:osdm-application))) (.net:invoke app :LockUserInterface) (.net:invoke app "ILoadFile" "c:/temp/foo.pkg") (.net:invoke app 'UnlockUserInterface))
Gets the value of an automation object property.
(.net:get-property objectref property &optional :raw-return-values t/nil &optional args)
This function inquires the value of an automation object property.
The value of the inquired property is returned as the result of the get-property call. It is converted according to the rules detailled in Conversion of automation datatypes into Lisp objects.
By default, if a communication error occurs while retrieving the specified property, a Lisp run-time error will be reported. If .net:*report-errors* has been set to nil, get-property returns nil when an error occurs.
(print (.net:get-property (.net:osdm-application) "Version"))
Sets a property of an automation object.
(.net:set-property objectref property newval) (.net:set-property-ref objectref property newval)
This function sets the value of an automation object property.
The property setter may return a value which is converted according to the rules detailled in Conversion of automation datatypes into Lisp objects.
By default, if a communication error occurs while setting the specified property, a Lisp run-time error will be reported. If .net:*report-errors* has been set to nil, set-property returns nil when an error occurs.
(.net:set-property (create-object "Excel.Application") "Visible" t)
Iterates over a collection of items and executes a function for each item.
(foreach collection function)
foreach iterates over all items in a collection of objects. For each item in the collection, it will call function, which receives the item as an input parameter.
;; Example: Using foreach with an "inline" lambda form (let* ((fs (create-object "Scripting.FileSystemObject")))) (foreach (get-property fs "Drives") #'(lambda(item) (format t " ~A" (get-property item "DriveLetter"))))
;; Example: Nested foreach, once with reference to ;; separate function, once using a lambda expression (defun per-workbook(item) (foreach (get-property item "Worksheets") #'(lambda(sheet) (invoke sheet "Close")))) (defun close-all-sheets() (let* ((excel (create-object "Excel.Application"))) (foreach (get-property excel "Workbooks") 'per-workbook)))
Converts a Lisp SEL_ITEM structure into an object of type ISelectionItem.
(.net:selection-item sel_item)
Integration Kit functionality identifies objects using SEL_ITEM structures. The .NET API has an equivalent object type which provides an ISelectionItem interface. This function converts a Lisp SEL_ITEM object into an object of type ISelectionItem.
(setf entity (.net:invoke (.net:selection-item sel-item) "IReferencedEntity"))
Queries an Automation object for a given interface.
(.net:query-interface objectref interfaceid)
Use query-interface to check if a given Automation object implements an interface.
(setf face (.net:query-interface entity "594C09BB-6427-472C-83CE-DE14EA5E5000")) ;; Check if an entity is a face. 594C09BB-6427-472C-83CE-DE14EA5E5000 is the IID for ;; CoCreate.Osdm.IFace.
Connects an automation object to a Lisp event handler.
(.net:connect-object objectref eventhandler)
Use connect-object to receive event notifications from Automation objects.
(defun my-event-handler(event-id event-args) (format t "Event ID = ~A, event arguments = ~A~%" event-id event-args)) (setf event-sink (.net:connect-object some-object 'my-event-handler))
Disconnect an automation object from a Lisp event handler.
(.net:disconnect-object objectref)
Use disconnect-object if you no longer want to receive event notifications from an Automation object.
(defun my-event-handler(event-id event-args) (format t "Event ID = ~A, event arguments = ~A~%" event-id event-args)) (setf event-sink (.net:connect-object some-object 'my-event-handler)) ... (.net:disconnect-object event-sink)
Constant which represents an IDispatch pointer that is null.
.net:nothing
Sometimes the need arises to pass a null pointer to a COM function. nil would be an intuitive way to do this, but this is a boolean value which is converted into a VT_BOOL. Instead, the .net:nothing constant must be used.
;; get all side faces of a sheet-metal part, providing neither a neighbour face ;; nor a bounding box as filter. (setf faces (.net:invoke sheet-part "InqISideFaces" .net:nothing .net:nothing))
Activates a COM/.NET addin.
(.net:acquire-addin progid)
Loads and activates COM/.NET add-in modules which were previously installed for OSDM. If the add-in is already active, that instance is shared. For details on how to create and deploy add-ins, please consult the documentation on API client types.
(.net:acquire-addin "CoCreate.TestAddin")
Invokes functionality defined in an add-in.
(invoke-addin progid function params...)
Calls a function defined in a COM/.NET add-in. For details on how to build such a function, consult the documentation on Integrating Lisp and Add-ins.
The Lisp interoperability layer converts Lisp values into automation-compatible data types (so-called variants) according to this table.
The interoperability layer converts automation-compatible return values into Lisp types according to this table.
By default, if an error occurs while calling the specified addin function, a Lisp run-time error will be reported. If .net:*report-errors* has been set to nil, invoke-addin returns nil when an error occurs.
(setf retval (.net:invoke-addin "CoCreate.TestAddin" "ExportXML" (list (sd-pathname-to-obj "/p1")))) ;; now process return value in retval
(.net:release-addin progid)
Releases a connection to the specified add-in. If no other connections are open, the add-in is deactivated and unloaded. For details on how to create and deploy add-ins, please consult the documentation on API client types.
(.net:release-addin "CoCreate.TestAddin")