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_UNKNOWN integer() Should be sent to package_interface VT_DISPATCH integer() Should be sent to package_interface 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.
Pid = pid()
ServerName = {local, atom()} | {global, atom()}
Starts a new server, and initializes the COM port. 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.
This way to launch Comet should only be used in two situations:
start_process() -> {ok, Pid}
start_process(ServerName) -> {ok, Pid}
Pid = pid()
ServerName = {local, atom()} | {global, atom()}
Starts a new server, and initializes the COM port.
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
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,
with possibly several threads for several clients. The
only reason to start two servers on the same node is if one might
crash, then the other will keep running.
This way to launch Comet should be used when:
Since this way is safer, it is the preferred way of using Comet.
ServerRef = Name | {Name, Node} | {global, Name} | Pid
Name = atom()
Node = atom()
Pid = pid()
Thread = integer()
Error = {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) -> ComThread | Error
ComThread = {com_thread, ServerRef, Thread}
Thread = integer()
Error = {error, Errcode}
Errcode = string()
Creates a new Windows thread that can be used to create and
manipulate COM objects. This must be done after
erl_com
is started. At least one thread has to be 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.
end_thread(ComThread) -> ok | Error
ComThread = {com_thread, ServerRef, Thread}
Thread = integer()
Error = {error, Errcode}
Errcode = string()
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(ComThread, Class, Ctx) -> ComInterface | Error
create_object(ComThread, Class, RefID, Ctx) -> ComInterface | Error
create_dispatch(ComThread, Class, Ctx) -> ComInterface | Error
ComThread = {com_thread, ServerRef, Thread}
Class = string()
Ctx = integer()
Thread = integer()
Error = {error, Errcode}
Errcode = string()
ComInterface = {com_interface, ServerRef, Thread, InterfaceNum}
InterfaceNum = integer()
This function creates a COM object. It calls the Win32 API function,
CoCreateFunction
. Refer to Windows documentation and COM books.
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
.
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.
In the case of an error, the COM error code is returned as part of the
Errcode
string, as a hexadecimal number.
The create_dispatch
variant creates an object with the
IDispatch
interface. The interface wanted can be specified in
the RefID
parameters. The default is IUnknown
.
query_interface(ComInterface, Iid)
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.
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.
com_call(ComInterface, MethodOffs, Pars)
MethodOffs = integer()
Pars = list()
This is the way to call a method in a COM interface. Beware that the parameter types must match the types in the COM interface function.
Note that return values are handled with out
parameters
when using com_call/3
. (As opposed to invoke/3
.
invoke(ComInterface, MethodID, Pars)
invoke(ComInterface, MethodName, Pars)
There are two ways to call a method in a COM interface. A dual- or
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 much slower.
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, MethodID, Value)
property_put(ComInterface, MethodID, [Parameters], Value)
property_put(ComInterface, MethodName, Value)
property_put(ComInterface, MethodName, [Parameters], Value)
To set a property value through the dispatch-interface, this function is used.