Difference between revisions of "COM Integration"
(overloaded methods) |
|||
Line 148: | Line 148: | ||
[[COMCallMethod]](h,"Result") | [[COMCallMethod]](h,"Result") | ||
+ | |||
+ | == Overloaded methods == | ||
+ | |||
+ | The .NET libraries allow functions to be overloaded. For example, in the [http://msdn.microsoft.com/en-us/library/system.random(v=vs.110).aspx System.Random] class, there are three overloads for the method <code>Next</code>: | ||
+ | int Next(); | ||
+ | int Next(int); | ||
+ | int Next(int,int); | ||
+ | |||
+ | You can call .NET methodsusing COM -- the .NET library wraps these -- but, COM doesn't support overloading. When .NET wraps overloaded methods, it adds an underscore number suffix starting with 2 to create unique names. It the example of <code>Next</code>, it exposes three methods | ||
+ | int Next(); | ||
+ | int Next_2(int,int) | ||
+ | int Next_3(int) | ||
+ | |||
+ | They get numbered in the order they are declared, which you generally have to way of knowing unless you just try them and experiment. As an illustration, the following uses the .NET class [http://msdn.microsoft.com/en-us/library/system.random(v=vs.110).aspx System.Random] to generate an integer between 200 and 300: | ||
+ | |||
+ | [[Var]] sysRand := [[COMCreateObject]]("System.Random") Do sysRand->Next_2(200,300) | ||
<div id="COMPutProperty"> | <div id="COMPutProperty"> |
Revision as of 16:03, 19 March 2014
new to Analytica 4.6
Requires Analytica Enterprise, Optimizer or ADE.
The Microsoft Component Object Model (COM) provides a framework for interprocess communication within the Windows operating system. The features described here allow your model to call other applications that expose a COM automation interface (known as the IDispatch
interface). You can write your own COM servers in C++, C#, VB, and a host of other languages, and then call them, or you can use existing interfaces to call applications from other vendors.
The features here allow your Analytica model to call other applications. This is different from having other applications call your Analytica model. The latter is what ADE provides.
COMCreateObject(name,server,flags,user,password)
The first step when using COM is to instantiate a COM object, which is done using the function 'COMCreateObject (note: You can also obtain an Excel::_Workbook object using SpreadsheetOpen, which can be used as a COM object). Only the first parameter is required, and it is rare that you would need to worry about the remaining parameters.
Parameters:
- «name» : The textual name of the COM class being instantiated. This can be a name such as "WScript.Shell" or "ADE4.CAEngine", or it can be a clsid such as "clsid:72C24DD5-D70A-438B-8A42-98424B88AFB8".
- «server»: (optional text) The name of the computer where the object will run. Allows remote invocation, or can be "localhost".
- «flags»: (optional integer bitfield). Additional esoteric flags (see the CLSCTX enumeration in Microsoft documentation). These can be added together. Some notable flags are:
- 0x1 = Allow in-process component (but be same bit-ness)
- 0x4 = Allow out-of-process component
- 0x40000 = Allow 32-bit component
- 0x80000 = Allow 64-bit component
- «user»: (optional text) the account name that the component will run under.
- «password»: (optional text) the account password to obtain credentials for the «user» account.
In most cases, the instantiated object runs on the same machine as the Analytica calling process, however, remote invocation is possible, known as DCOM. When the «server» parameter is not specified, the target computer is determined by the DCOMCNFG settings for that component. By running the DCOMCNFG program, or entering "DCOM Config" in the Microsoft Component Services, you can configure specific COM components to run on specific remote servers. The «server» parameter overrides that setting. The COM component will usually need to be installed on both machines.
The programs that implement COM objects can either run in-process or out-of-process and can be 32-bit or 64-bit programs. Some COM servers provide both in-process and out-of-process versions and some provide both 32-bit and 64-bit versions. If you don't specify any of the optional flags, it'll set them to allow any possible combination that it finds to run (allowing either 32- or 64-bit and in-process or out-of-process). You can use these flags to force a specific type to run. For example:
COMCreateObject("MyObject", flags:0x1 + 0x80000 )
would force the creation of a MyObject
in an out-of-process 64-bit server, whereas without the flags it might have created an in-process 32-bit instance. Some COM servers provide only one type. For example, ABODB classes can only be used in-process.
Not all combinations of flags are valid. Here are some basic constraints:
- Any remote invocation (on a different machine) is automatically out-of-process.
- Any in-process invocation must have the same bitness (32-bit or 64-bit) as the Analytica or ADE program you are using. (Out-of-process bitness can differ).
- 64-bit objects can only be used when you are running Windows 64-bit.
- When neither «force64bit» nor «force32bit» are specified, it defaults to the same bitness as the calling process when both are available.
Other considerations
Security
TBD. The user should be given control over whether COM objects are allowed to run, so that if you receive a model from someone else, it can't inflict arbitrary damage. Also, there should be restrictions on the ACP server as to what objects are allowed to run, and who can configure this.
Run As Impersonation
It is possible to run out-of-process COM objects under a different identity. This can be configured in DCOMCNFG, or you can specify the «user» and «password» parameters.
Error Handling
When you try to instantiate a COM object, it may fail with an error that terminates computation. There are situations where you don't want to terminate, but you might instead fall back on some non-COM-based algorithm instead. To do this, you can catch the error using the Try function:
Try( COMCreateObject("MySearchComponent"), Catch: Null )
Ctrl+Break
When Analytica is waiting for an object to instantiate, or for a method/property call to return, Ctrl+break should work, allowing the user to abort that operation. It is up to the object how long a method call will take, and when apartment threaded, a new call to the object will hang until it is done.
COMCallMethod(«object»,methodName,param1,param2,param3,...,resultIndex:I,J,...;concurrent)
This calls a method of a COM object that has already been instantiated using COMCreateObject (or SpreadsheetOpen). The return value is the value returned by the method.
Methods can only be invoked for «object»s that implement the IDispatch
interface.
Example
Var ade := COMCreateObject("ADE4.CAEngine");
COMCallMethod(ade,"SendCommand","news");
COMCallMethod(ade,"OutputBuffer")
In the above example, the SendCommand
method is invoked with the parameter "news". Its return value is ignored. Then the OutputBuffer
property is accessed, and its value is returned.
Array-valued results
When a method returns an array (known as a SafeArray in COM lingo), the dimensions of that array need to be mapped to Analytica indexes. If you do not specify the «resultIndex» parameter, the function creates local indexes with the names .dim1
, .dim2
, etc. Alternatively, you can specify the indexes to use for the result via the «resultIndex» parameter. If your result index does not match the length of the corresponding dimension of the array that is returned, the array is truncated (when your index is too short) or result is padded with Nulls (if your index is too long). If you specify too many indexes (i.e., you provide 3 indexes but the result is only 2-D), the extra indexes are ignored. If you don't specify enough indexes, then local indexes are created as before.
Example:
COMCallMethod(reuters,"GetPriceHistory","AAPL",1-Jan-2012,Today(),resultIndex:Time,DateOpenCloseHighLow)
This hypothetical call returns the stock price history for a given stock from a start date to an end-date, where the result is a 2-D array. The first dimension of the result has one row for each date, and the columns are ['Date','Open','High','Low','Close']. The «resultIndex» parameter here maps these to existing Analytica indexes (named Time and DateOpenCloseHighLow).
Array-valued parameters
When you pass Analytica arrays as parameters, the call is iterated (invoked multiple times) for each atomic combination of parameters, and the results from each call are collected. Also, if the «object» parameter is array valued (an array of objects), the call is iterated, calling the method for each object. Hence, array-abstraction extends to COM method calls.
When a method expects an array-valued parameter, you must package it using the COMArray function, which specifies which indexes are consumed along with the order of the indexes for 2-D or greater arrays. Any indexes that are not included within the COMArray call are iterated over.
Examples
COMCallMethod( mathObj, "PrincipleEigenVector", COMArray(A,I,J), resultIndex: I )
This hypothetical method accepts a 2-D matrix and returns a 1-D vector.
COMCallMethod( reuters, "OpenPriceOnGivenDates", Stock, COMArray(Time,Time), resultIndex: Time)
This hypothetical method accepts a stock symbol and a 1-D array of dates. It returns a vector containing the stock price on each date. The model may send this an array of stock symbols. The array will be called once for each stock, and the results collected in an array having all the dimensions of Stock.
Pass-by Value
All parameters are passed-by-value. The option to pass-by-reference is not available. This limits the methods that can be called to those without pass-by-reference parameters (a.k.a., "out" parameters).
Data Types
Parameters are passed as COM VARIANTs. COM Objects themselves can be passed as parameters and result values can return COM objects. Hence, it is possible to have methods that return a new COM object, and then that object can be used as the first parameter in a call to CallCOMObject.
The Invoke Method operator
A new ->
operator provides a syntactic convenience for CallCOMObject. The use of the operator looks like this:
obj->method(param1,param2,..)
Names parameters
The COM Automation specification has a concept of named parameters. This is not supported by CallCOMObject. Parameters are passed by position only.
Error Handling
Errors in the COM layer cause an error message to appear and evaluation to terminate.
COM objects may themselves implement error handling in various ways, such as by including an ErrorCode
property that the caller can check after a method call, etc. These are just return values and are not interpreted as errors by Analytica.
Concurrent calls
When you specify the optional «concurrent» as true, COMCallMethod returns right away, and returns a special COM object that encapsulates the state of the computation. Your model can then work on something else, or launch other concurrent calls. Your model will need to eventually ask the object for the result, or it can query the object to find out it the call has finished yet.
The special object returned supports these properties and methods:
- (property, read-only)
IsDone
- (property, read-only)
HasError
- (method)
Result()
{ Blocks until done, issues an error if there was an error }
This example creates three parallel computations, one for each molecule. Two of the molecule folds are computed on this computer, and one is computed on a remote computer named "Computer2". A lengthy "CalculateFold" method is called, and the three computations run in parallel. The final line waits for them all to complete and merges the results into a single array. The local variable, h
, holds the special COM object that tracks the state of the concurrent computations.
Index molecule := ["JunK","JanA","TcpK"]; Var computer := Array(molecule,["LocalHost","LocalHost","Computer2"]); Var obj := COMCreateObject("ProteinFolder",server:computer); Var h := COMCallMethod(obj,"CalculateFold",molecule,concurrent:true); COMCallMethod(h,"Result")
Overloaded methods
The .NET libraries allow functions to be overloaded. For example, in the System.Random class, there are three overloads for the method Next
:
int Next(); int Next(int); int Next(int,int);
You can call .NET methodsusing COM -- the .NET library wraps these -- but, COM doesn't support overloading. When .NET wraps overloaded methods, it adds an underscore number suffix starting with 2 to create unique names. It the example of Next
, it exposes three methods
int Next(); int Next_2(int,int) int Next_3(int)
They get numbered in the order they are declared, which you generally have to way of knowing unless you just try them and experiment. As an illustration, the following uses the .NET class System.Random to generate an integer between 200 and 300:
Var sysRand := COMCreateObject("System.Random") Do sysRand->Next_2(200,300)
COMPutProperty(«object»,propertyName,value,params:param1,param2,param3,...)
Sets a property of a COM object to «value».
Most properties are simple properties, so that COMPutProperty is akin to an assignment. In these cases, the «params» parameters are not used. In less common cases, a COM object may have an indexed property that accepts parameters, similar to array coordinates for the property (although they need not be array coordinates), and these indexed parameters are specified in «params».
COMPutProperty calls IDispatch::Invoke
with the DISPATCH_PROPERTYPUT flag. No method for calling with DISPATCH_PROPERTYPUTREF will be provided.
No return value. In other respects, same as COMCallMethod.
obj->propertyName := value
obj->propertyName(param1,param2) := value
COMGetProperty(«object»,propertyName,param1,param2,param3,...,resultIndex:I,J,...)
Returns the value of a property. This is the same as COMCallMethod, except that it is invoked with the DISPATCH_PROPERTYGET flag. The following syntactic shortcut can be used for an non-indexed, scalar-valued property:
obj->propertyName
If parameters need to be passed, then you must call the function explicitly.
TypeOf
You can test whether a scalar value holds a COM object using TypeOf(X,true)="COM Object"
.
Limitations
The Analytica-COM integration facility comes with several notable limitations.
- Objects must support the IDispatch interface (this is known as ActiveX automation). All calls and property accesses are automation calls. Native COM calls are not supported.
- Methods calls with reference parameters are not supported. Calls have a single return value only.
- Expression Assist cannot "see" the names of the properties and methods supported by an object, so it can't assist you with possible method-name completions while typing. Also, non-existent method errors (from bad names) aren't detected until evaluation time.
Enable comment auto-refresher