# erl_com

### MODULE

erl_com

### MODULE SUMMARY

Comet gen_server with API to Erlang COM client.

### DESCRIPTION

The erl_com module is a gen_server that exposes an API to the port program and port driver that is used to call COM services from Erlang.

There is a mapping between types in Erlang and types in COM. The following table shows how Erlang types are converted by the port program to COM types.

     COM type       Erlang type             Comment
     --------       -----------             -------
     VT_I4          integer()
     VT_U4          integer()
     VT_BOOL        true | false
     VT_BSTR        string()                 Strings are
                                             translated between
                                             Ascii and Unicode
     VT_DATE        {integer(), integer(), integer()}
                                             Same format as returned
                                             from now()
                    {{Year, Month, Day}, {Hour, Min, Sec}}
                                             Date and time,
                                             with integers in tuples
     VT_PTR         {vt_*, out}              Any output parameter,
                                             including return value
     VT_I1          {vt_i1, integer()}
     VT_U1          {vt_u1, integer()}
     VT_I2          {vt_i2, integer()}
     VT_U2          {vt_u2, integer()}
     VT_R8          float()
     VT_R4          {vt_r4, float()}
     VT_CY          {vt_cy, float()}         Note that the precision
                                             is lower on float()
     VT_DECIMAL     {vt_decimal, float()}    -"-
     VT_UNKNOWN     integer()                Should be sent to
                                             package_interface
     VT_DISPATCH    integer()                -"-
     other types    unsupported

Some of the internal Erlang types map to types in COM. Most types in COM, however, have no corresponding type in Erlang. In these cases, a special tuple is used, of the form `{ComType, Value}`, where `ComType` is the corresponding type-name as defined in `ole2.h` in the Microsoft Windows SDK.

In the functions below, the `ComInterface` is used. It is a tagged tuple, that identifies a COM interface in the port driver.

    ComInterface = {com_interface, pid(), ThreadNo, InterfaceNo}
    ThreadNo = InterfaceNo = integer()

### EXPORTS

`start_program() -> {ok, Pid}`
`start_program(ServerName) -> {ok, Pid}`
`get_program(ServerName) -> {ok, Pid}`

Types:

`Pid = pid()`
`ServerName = atom()`

Starts a new server, and initializes the COM port. Also starts one thread for running COM calls.

This function starts the COM port as a port-program, in a separate process. The erl_com gen_server uses (as usual in Erlang), a pipe to communicate with the port. This has the benifit that a crash in the COM port, will not crash the emulator.

Each `erl_com` server starts a separate port-program.

The server can be started with or without a registered name.

Normally, only one `erl_com` server is started on a node, using the `get_program/1` call, with possibly several threads for several clients. The only reason to start more than one server on the same node is if one crashes, then the others will keep on running.

This way to launch Comet can be used when:

- The COM server is not 100% certain and crashproof.
- The overhead of using a separate port process is acceptable.

Since this way is safer, it is the preferred way of using comet.

`start_driver() -> {ok, Pid}`
`start_driver(ServerName) -> {ok, Pid}`
`get_driver(ServerName) -> {ok, Pid}`

Types:

`Pid = pid()`
`ServerName = atom`

Starts a new server, and initializes the COM port. Also starts one thread for running COM calls.

The port is loaded as a port driver. This is the most efficient way to use COM, since the com port resides in the same process as the Erlang emulator. However this also means that crashing COM-objects will bring down the emulator.

The server can be started with or without a registered name. There is no advantage of having two servers on the same node.

The `get_driver/1` call, gets a named process, or starts one if no one is running.

This way to launch Comet should only be used in two situations:

- When the COM servers are in separate processes, where they will not bring down the emulator in case of a crash.
- When the COM server is well-known and unlikely to crash, and the overhead of using a separate port process is unacceptable.

`get_or_start(Name, ProgramFlag) -> {ok, Pid}`

Types:

`Name = atom()`
`ProgramFlag = program | driver`

Calls `get_program` or `get_driver`, depening on the `ProgramFlag` parameter.

`stop(ServerRef) -> ok`

Types:

`ServerRef = Name | Pid`
`Name = atom()`
`Node = atom()`
`Pid = pid()`
`Thread = integer()`
`Error = {com_error, Errcode}`
`Errcode = string()`

Shuts the `erl_com` server down. This will stop any threads. Interfaces should be released before.

(Remember COM has no garbage collection!)

`new_thread(ServerRef | PrevComThread) -> ComThread`

Types:

`ServerRef = Name | Pid`
`PrevComThread = ComThread = {com_thread, ServerRef, ThreadNo}`
`Thread = integer()`

Creates a new Windows thread that can be used to create and manipulate COM objects. This is done automatically after `erl_com` is started. One thread is created.

To allow COM calls to take time without blocking the emulator, `erl_com` allows multi-threaded execution. The maximum number of threads is 60. However, creating more than a few is not useful for practical reasons.

When a COM-thread is created, it is suspended with a select function (which is called WaitForMultipleObjects in the Win32 API). Calling any COM-functions from the thread, is done by setting up a parameter buffer and signaling an event, that wakes up the thread.

The return value is a tuple that includes `Thread`, a thread index that is an integer between 0 and 60, which is unique for each thread, and allocated incrementally. Thread index values will be reused if a thread is ended.

All COM calls are asynchronous from the emulators view, they are never called from the emulator main thread, and thus only blocks the calling Erlang process.

`end_thread(ComThread) -> ok`

Types:

`ComThread = {com_thread, ServerRef, ThreadNo}`
`ThreadNo = integer()`

Ends a thread previously created with `new_thread`. If the thread has any interfaces, these must be released before the thread is ended, otherwise resource leakage can occur. (Remember COM has no garbage collection!)

The thread is signaled and will exit. The thread index will be marked as available, internally in the port program.

`create_object(ThreadOrServer, Class) -> ComInterface`
`create_object(ThreadOrServer, Class, Ctx) -> ComInterface`
`create_object(ThreadOrServer, Class, RefID) -> ComInterface`
`create_object(ThreadOrServer, Class, RefID, Ctx) -> ComInterface`
`create_dispatch(ThreadOrServer, Class) -> ComInterface`
`create_dispatch(ThreadOrServer, Class, Ctx) -> ComInterface`

Types:

`ThreadOrServer = ComThread | ServerRef`
`ServerRef = Pid | Name`
`Pid = pid()`
`Name = atom()`
`ComThread = {com_thread, Pid, ThreadNo}`
`Class = string()`
`RefID = string()`
`Ctx = integer()`
`ThreadNo = integer()`
`InterfaceNum = integer()`

This function creates a COM object. It calls the Win32 API function, `CoCreateInstance`. Refer to Windows documentation. The string `Class` can be either a GUID for a class, or a COM program string. Values for the `Ctx` are defined in `erl_com.hrl`. If no `Ctx` is given, all flags are set to one (using any local service).

When successful, this function creates a COM object, and returns a tuple `ComInterface`, which is a handle for the object, that is used for calling methods, and releasing the object.

The `create_dispatch` variant creates an object with the `IDispatch` interface. The interface wanted can be specified in the `RefID` parameters.

`get_object(ThreadOrServer, Name) -> ComInterface`
`get_object(ThreadOrServer, Name, Interface) -> ComInterface`
`get_dispatch(ThreadOrServer, Name) -> ComInterface`

Types:

`ThreadOrServer = ComThread | ServerRef`
`ServerRef = Pid | Name`
`Pid = pid()`
`Name = atom()`
`ComThread = {com_thread, Pid, ThreadNo}`
`Name = string()`
`Interface = string()`

This function gets a COM object. It calls the Win32 API function, `CoGetObject`. Refer to Windows documentation. The string `name` is a name that is used to get the object using a moniker. The `bindOptions` parameter of `CoGetObject` always contains default values.

When successful, this function references a COM object, and returns a tuple `ComInterface`, which is a handle for the object, that is used for calling methods, and releasing the object.

The `get_dispatch` variant gets an object with the `IDispatch` interface. Other interface wanted can be specified in the `Interface` parameter.

`query_interface(ComInterface, Iid)`

Types:

`Iid = string()`

Calls `query_interface` on the given interface. Note that in COM, an object is also considered an interface.

This function is used to see what interfaces an object implements and to do down-casting.

`release(ComInterface)`

In COM, all interfaces are reference-counted. The `release` function decrements the reference counter, and releases the interface (or object) if it reaches zero. Note that it is important to release all objects created, and interfaces acquired. Otherwise resource leaking will occur. Future versions of `comet` may provide for GC of COM objects.

This function in `erl_com` also returns the `ComInterface` tuple, after `release` it is not allowed to use the `ComInterface`.

`com_call(ComInterface, MethodOffs)`
`com_call(ComInterface, MethodOffs, Pars)`

Types:

`MethodOffs = integer()`
`Pars = list()`

This is the way to call a method in a virtual COM interface. Beware that the parameter types must match the types in the COM interface function. Any type errors, or bad parameter counts, will crash the COM driver.

Note that return values are handled with `out` parameters when using `com_call/3`. (As opposed to `invoke/3`).

This function should not be called explicitly, only from generated code (see `com_gen`).

### EXPORTS

`invoke(ComInterface, MethodName, Pars)`
`invoke(ComInterface, MethodID, Pars)`

Types:

`MethodName = string()`
`MethodID = integer()`

There are two ways to call a method in a COM interface. A dispatch-interface, has a method invoke, that is used to call methods. This method is intended for interpreted languages. The invoke method is safer than `com_call`, but also slower.

In many cases, the overhead of using invoke, is not significant. Therefore, it should be preferred, since it has parameter checking, better error messages, etc.

The return vaule sometimes needs a bit of processing. In particular, an interface is returned as an integer only, and the function `package_interface` must be called (see below).

`property_get(ComInterface, MethodID)`
`property_get(ComInterface, MethodID, [Parameters])`
`property_get(ComInterface, MethodName)`
`property_get(ComInterface, MethodName, [Parameters])`

To get a property value through the dispatch-interface, this function is used.

`property_put(ComInterface, MethodName, Value)`
`property_put(ComInterface, MethodName, [Parameters], Value)`
`property_put(ComInterface, MethodID, Value)`
`property_put(ComInterface, MethodID, [Parameters], Value)`

To set a property value through the dispatch-interface, this function is used.

`property_put_ref(ComInterface, MethodName, Value)`
`property_put_ref(ComInterface, MethodName, [Parameters], Value)`
`property_put_ref(ComInterface, MethodID, Value)`
`property_put_ref(ComInterface, MethodID, [Parameters], Value)`

To set a property reference through the dispatch-interface, this function is used.

`package_interface(ThreadOrInterface, NewIntfNum) -> NewComInterface`

Types:

`ThreadOrInterface = ComThread | ComInterface`

This function converts an interface number, as returned from `erl_com` when interface-returning COM calls are made, into an interface tuple. This interface tuple can be used in other COM calls.

Note that this function is called in generated code (see `com_gen`).

`get_method_id(DispatchInterface, MethodName) -> MethodID`

Types:

`DispatchInterface = ComInterface`
`MethodName = string()`
`MethodID = integer()`

Finds the ID of a method (or property), given its name. The interface must be a dispatch-interface.

`get_interface_info(ComInterface, VirtualOrDispatch) -> TypeInfo`
`get_interface_info(ComInterface, TypeName, VirtualOrDispatch) -> TypeInfo`

Types:

`VirtualOrDispatch = virtual | dispatch`
`TypeName = string()`
`TypeInfo = EnumInfo | InterfaceInfo | ClassInfo`
`EnumInfo = {enum, virtual, TypeId, [EnumMember...], [Subtype...]}`
`TypeId = {Name, IID}`
`Name = IID = string()`
`EnumMember = {EnumName, EnumValue}`
`EnumValue = integer()`
`ClassInfo = {coclass, virtual, TypeId, [], []}`
`InterfaceInfo = DispatchInfo | VirtualInfo`
`DispatchInfo = {dispatch, IntfKind, TypeId, [Func...], [Subtype...]}`
`VirtualInfo = {interface, IntfKind, TypeId, [Func...], [Subtype...]}`
`IntfKind = dual | dispatch | virtual`
`Func = {FuncName, [InvKind], FuncType, IdOrOffset, [Parameter...], ReturnValue}`
`EnumName = FuncName = string()`
`InvKind = func | property_get | property_put | property_put_ref`
`FuncType = virtual | purevirtual | nonvirtual | static | dispatch`
`IdOrOffset = integer`
`ReturnValue = ComType | void`
`Parameter = {ParamName, [ParamFlag...], ComType, DefaultValue`
`ParamName = string()`
`ParamFlag = in | out | lcid | retval | optional | has_default | has_custom_data`
`ComType = vt_i4 | vt_str | ... see above`
`DefaultValue = {ComType, Value} | {}`
`SubType = TypeId`

How about that? If it looks complicated it's because it is.

The `get_interface_info` is used to retrieve information from a COM type library. It is actually a misnomer, it's not just for interfaces, but also for enums and coclasses. Other types of types are unsupported by comet (currently).

Given an interface and a type name, it fetches most of the information available in the typelibrary, using the `ITypeInfo` and `ITypeLib` interfaces. It is kind of an erlang version of the OLE/COM object viewer in the Windows SDK. An interface can be listed as a dispatch or a virtual interface.

This function is used by `com_gen` to provide erlang stub generation from type libraries.

To understand its output, refer to the COM documentation on ITypeInfo and ITypeLib, or to a book.

There is currently no way in comet to retrieve information from a type-library without creating at least one object from it. This might be improved in later releases.

`get_typelib_info(ComInterface) -> TypeLibInfo`

Types:

`TypeLibInfo = {TypeLibName, [TypeInfo...]}`
`TypeInfo = {TypeKind, TypeName, IID}`
`TypeKind = enum | record | module | interface | dispatch | coclass | alias | union`

The `get_typelib_info` function lists all types in a COM type library. It is used by `com_gen` to generate stub code.

Note that only enums, interfaces (including dispatch interfaces) and classes can be used in `get_interface_info`.

`test(ComInterface) -> []`

The `test` function simply makes the COM port to a `DebugBreak()` Win32 API call. This breaks into the debugger (such as Visual C++). It is really handy to debug COM interfaces written in C. It is also useful for finding bugs in comet. (Luckily, there are no bugs left in the code.)

`enum(ComInterface) -> ComEnum`

Types:

`ComEnum = ComInterface`

This is a utility function that calls the `DISPID_ENUM` property on a COM-object, and returns the result as an interface, suitable for `next` and `nexti`.

`next(ComEnum) -> Variant`
`nexti(ComEnum) -> ComInterface`
`intfenum_next(ComEnum) -> ComInterface`

Types:

`ComEnum = ComInterface`

The `next` function calls the `Next` method on an `IEnumVARIANT` interface. The `nexti` function does this too, and also packages the result with `package_interface` (often the `Variant` result is known to be an interface).

The `intfenum_next` calls the `Next` method on an `IEnumIUnknown`, the only difference is the size of the result.

When the iterator reaches the end, an empty tuple `{}` is returned, this is a value that cannot be in a variant.

`map_enum(ComEnum, Fun)`
`map_enumi(ComEnum, IFun)`
`map_intfenumi(ComEnum, IFun)`

Types:

`ComEnum = ComInterface`
`Fun = fun(Variant)`
`IFun = fun(ComInterface)`

These functions maps over a COM iterator (Com enum) and applies the given fun, the values are collected in a list.

The interface functions (`map_enumi` and `map_intfenumi`) uses `nexti` to iterate. They also releases the interface return from `nexti`. (This means that the value parameter of the fun shouldn't be returned or stored anywhere.)

### AUTHORS

Jakob Cederlund - support@erlang.ericsson.se

comet 1.1.1
Copyright © 1991-2002 Ericsson Utvecklings AB