Luacom
Luacom
Luacom
(Version 1.3b2)
1 Introduction 3
1.1 Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Who Should Read What (or About the Manual) . . . . . . . . . . . . . . . . . . . . 4
2 Tutorial 5
2.1 Using The LuaCOM library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 Locating COM Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3 Creating Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.4 Getting Help about an Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.5 Methods and Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.6 Releasing Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3 LuaCOM Elements 9
3.1 LuaCOM API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.2 LuaCOM objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2.1 Object Disposal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3 Automation binding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3.1 Implementing dispinterfaces in Lua . . . . . . . . . . . . . . . . . . 15
3.3.2 Using Methods and Properties . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.3.3 Connection Points: handling events . . . . . . . . . . . . . . . . . . . . . . 19
3.3.4 Parameter Passing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.3.5 Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.4 Type Conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.4.1 Boolean values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.4.2 Pointers to IDispatch and LuaCOM objects . . . . . . . . . . . . . . . . 25
3.4.3 Pointers to IUnknown . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.4.4 Arrays and Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.4.5 CURRENCY type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.4.6 DATE type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.4.7 Variants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.4.8 Error Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.5 Other Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.5.1 The Enumerator Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.5.2 The Connection Point Container Object . . . . . . . . . . . . . . . . . . . . 27
3.5.3 The Typelib and Typeinfo Objects . . . . . . . . . . . . . . . . . . . . . . . 27
1
4 Implementing COM objects and controls in Lua 28
4.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.2 Is it really useful? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.3 Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.4 Building a LuaCOM COM server . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.4.1 Specify the component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.4.2 Objects to be exported . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.4.3 Building the type library . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.4.4 Registration Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.4.5 Registering the Component Object . . . . . . . . . . . . . . . . . . . . . . . 31
4.4.6 Implementing and Exposing the Component . . . . . . . . . . . . . . . . . . 31
4.4.7 Initialization and Termination . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.5 Running the COM server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.6 Generating Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.7 Full Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.8 Building a Lua OLE control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
5 Release Information 35
5.1 Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
5.2 Known bugs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
5.3 Future Enhancements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.4 Important issues about LuaCOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.4.1 Problems instantiating COM objects . . . . . . . . . . . . . . . . . . . . . . 36
5.4.2 Releasing COM objects from memory . . . . . . . . . . . . . . . . . . . . . 36
5.4.3 Receiving events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.4.4 Extensible Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.4.5 Visual Basic c issue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.5 History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
6 Reference 41
6.1 The C/C++ API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
6.2 The Lua Standard API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.3 Lua Extended API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
6.4 Enumerator Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
6.5 Type Library Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
6.6 Type Information Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
7 Credits 63
2
Chapter 1
Introduction
LuaCOM is an add-on library to the Lua language that allows Lua programs to use and implement ob-
jects that follow Microsoft’s Component Object Model (COM) specification and use the Automation
technology for property access and method calls.
1.1 Features
Currently, the LuaCOM library supports the following features:
• dynamic instantiation of COM objects registered in the system registry, via the CreateObject
method;
• COM method calls as normal Lua function calls and property accesses as normal table field
accesses;
• ability to read type libraries and to generate HTML documentation on-the-fly for COM objects;
• implementation of OLE controls using Lua tables (needs a Lua GUI toolkit that can create
in-place windows, like IUP);
• use of COM connection point mechanism for bidirectional communication and event handling;
3
1.2 Who Should Read What (or About the Manual)
This manual is mostly a reference manual. Here we document the behavior of LuaCOM in a variety
of situations, some implementation decisions that affect the end-user of the library and its limitations.
When facing some strange behavior in an application built using LuaCOM, the first step is to read all
the chapter 5, where the majority of possible problems are documented. There can be found references
to other sections of the manual, where more detailed information is provided.
Newbies For those who are newcomers, we provide a tutorial section (chapter 2) with a step-by-step
guide to start using LuaCOM. More help and samples can be found in LuaCOM’s home page. Notice
that VBScript code can be easily converted to Lua with LuaCOM.
This manual does not provide information for developers who need deeper technical information
about LuaCOM or who are willing to modify it for some reason. For this kind of information, please
contact the authors.
Knowledge required This manual presumes some knowledge of COM and Automation. We don’t
intend to explain in detail how these technologies work or how they can be used to solve particular
problems. This information can be found easily in the web or in good books.
4
Chapter 2
Tutorial
/*
* Sample C program using luacom
*/
#include <stdio.h>
#include <ole2.h> // needed for CoInitialize and CoUninitialize
#include <lua.h>
#include "luacom.h"
5
luacom_close(L);
lua_close(L);
CoUninitialize(NULL);
return 0;
}
Notice that it’s necessary to initialize COM before lua_open and to terminate it only after the last
lua_close, otherwise fatal errors may occur.
Using Lua 5 to dynamically load LuaCOM is simpler. Just call require("luacom") in your
Lua script, and make sure the file luacom.lua is in your LUA_PATH environment variable, and the
Lua and LuaCOM DLLs (lua-5.0.dll, lualib-5.0.dll and luacom-lua5-1.3b2.dll,
respectively) are in your PATH. Then run your script with the Lua standalone interpreter.
6
type information. If the object has an associated help file, LuaCOM can launch it using the method
ShowHelp:
word = luacom.CreateObject("Word.Application")
assert(word)
luacom.ShowHelp(word)
If the object has an associated type library, LuaCOM can generate and display an HTML file
describing it. This information can also be read using other type library browsers, as OleView.
The method DumpTypeInfo can be used in console applications to list the methods and prop-
erties of the interface. It does not give much information, but can be helpful when playing with an
object.
To read or write simple properties, one must simply use them as if they were normal table fields.
-- Reading properties
value1 = obj1.Value
value1 = obj2.Value
-- writing a property
obj3.Value = value1 + value2
Automation includes support to parametrized properties. These can be accessed (or written) using
accessor functions.
value = obj:getMatrixValue(1,1)
value = value*0,125
obj:setMatrixValue(1, 1, value)
7
obj = luacom.CreateObject("MyApp.MyObj")
-- Here we force an immediate release of the object
obj = nil
collectgarbage()
Notice that if there is any references to the COM object alive in Lua then the application (or
library) that implements it will not exit.
8
Chapter 3
LuaCOM Elements
• LuaCOM API, a set of functions used to do a variety of tasks (library initialization, object
creation, implementation of Automation interfaces in Lua, manipulation of connection points
etc.);
• Automation binding, which translates accesses on LuaCOM objects to COM interface calls and
COM accesses on an interface implemented in Lua to Lua function calls or table accesses;
• LuaCOM type conversion rules, which govern the type conversion between Lua and Automa-
tion types;
• LuaCOM parameter passing rules, which describe how LuaCOM translate a Lua parameter list
to a COM one and vice versa;
9
Standard Lua API
Method Description
CreateObject Creates a LuaCOM object.
11
Extended Lua API
Method Description
CreateLocalObject Creates a LuaCOM object as an
out-of-process server.
12
Standard C/C++ API
Function Description
luacom_open Initializes the LuaCOM library in a
Lua state. It must be called before any
use of LuaCOM features.
13
• when receiving return values from COM, where some of the values are IDispatch pointers.
A LuaCOM object may be passed as a parameter to method calls on other LuaCOM objects, if
these methods expect an argument of type dispinterface. Here is a sample to illustrate this
situation:
There are two kinds of LuaCOM objects: typed and generic ones. The typed ones are those whose
COM object has type information. The generic ones are those whose COM object does not supply
any type information. This distinction is important in some situations.
Caution LuaCOM only tracks references to COM objects. It does not work with the concepts of
“application”, “component”, “process” etc. It does not know even which objects are part of the same
component or application. This has some consequences on the object disposal:
• a component may only consider as “finished” its relationship with LuaCOM when all references
to its objects are released, not only the one created with CreateObject;
• some components have a “Quit” method. This may close the component’s interface, but it could
remain running if there are any references to it. Nevertheless, these references cannot be reliably
used after the “Quit” method has been called. To release the component, one must assign nil
to all references to the component (and its sub-objects) and then call collectgarbage.
14
3.3 Automation binding
The Automation binding is responsible for translating the table accesses to the LuaCOM object into
COM interface calls. Besides that, it also provides a mechanism for implementing dispinterfaces
using ordinary Lua tables.
-- Creates and fills the Lua table that will implement the
-- COM interface
events_table = {}
function events_table:AfterUpdate()
print("AfterUpdate called!")
end
-- Here we implement the interface DCalendarEvents, which is part
-- of the Microsoft(R) Calendar object, whose ProgID is MSCAL.Calendar
events_obj = luacom.ImplInterface(
events_table,
"MSCAL.Calendar",
"DCalendarEvents")
-- Checks for errors
--
if events_obj == nil then
print("Implementation failed")
exit(1)
end
-- Tests the interface: this must generate a call to the events:AfterUpdate
-- defined above
--
events_obj:AfterUpdate()
If the interface to be implemented is described in a stand-alone type library, the method ImplInterfaceFromTy
must be used instead:
-- Creates and fills the Lua table that will implement the
-- Automation interface
hello_table = {}
function hello:Hello()
15
print("Hello World!")
end
-- Here we implement the interface IHello
--
hello_obj = luacom.ImplInterfaceFromTypelib("hello.tlb","IHello")
-- Checks for errors
--
if hello_obj == nil then
print("Implementation failed")
os.exit(1)
end
-- Tests the interface
--
hello_obj:Hello()
Both methods return a LuaCOM object, whose corresponding IDispatch interface is imple-
mented by the supplied table. This LuaCOM object can be passed as an argument to COM methods
who expect a dispinterface or to LuaCOM API methods (like addConnection).
One can also use the NewObject method, which is best suited to the situation where one needs
to create a complete component in Lua and wants to export it, so that it can be accessed through COM
by any running application.
obj = luacom.CreateObject("TEST.Test")
if obj == nil then
exit(1)
end
-- method call
a = obj:Teste(1,2)
-- another one
obj:Teste2(a+1)
It’s important to notice the need of using the colon – “:” – for method calls. Although LuaCOM
does not use the self parameter that Lua passes in this case, its presence is assumed, that is, LuaCOM
always skips the first parameter in the case of method calls; forgetting it may cause nasty bugs. Notice
that this rule doesn’t apply when using the default method of a LuaCOM object stored in a table or in
a property of another LuaCOM object (see section 3.3.2 below).
Accessing properties is much like the same of accessing fields in Lua tables:
obj = luacom.CreateObject("TEST.Test")
if obj == nil then
exit(1)
16
end
-- property access
a = obj.TestData
-- property setting
obj.TestData = a + 1
Properties may also be accessed as methods. This is mandatory when dealing with parameterized
properties, that it, ones that accept (or demand) parameters. A common example of this situation is
the “Item” property of collections.
-- property access
a = obj:TestData()
-- Parametrized property access
b = obj:TestInfo(2)
-- Accessing collections
c = obj.Files:Item(2)
Notice that the colon – “:” – must also be used in this situation.
When accessing properties with method calls, LuaCOM always translates the method call to a
read access (property get). To set the value of a property using a method call, it’s necessary append
the prefix “set”1 to the property name and the new value must be supplied as the last argument.
-- property access
a = obj:TestData()
-- Setting the property
b = obj:setTestInfo(2)
-- Setting a parametrized property
c = obj.Files:setItem(2, "test.txt")
The prefix “get” may also be used, to clarify the code, although it’s not necessary, as the default
behavior is to make a read access.
-- property access
a = obj:getTestData()
b = obj:getTestInfo(2)
c = obj.Files:getItem(2)
Extensible interfaces
LuaCOM allows the use of properties as simple Lua fields just for objects that have type information.
Nevertheless, some objects that have type information describing their interfaces implement proper-
ties that are not described in the type library: these objects implement extensible interfaces. Those
properties can only be used with accessor functions, as shown in section 3.3.2. An example of such
behaviour is found in WMI objects (Windows Management Instrumentation).
1
In a future version it might be allowed to change the prefix.
17
Default methods
A dispinterface can have a default method or property, that is, one that is called when the client
does not specify the method name. LuaCOM calls the default method when the object itself is used
as a function.
excel = luacom.CreateObject("Excel.Application")
excel.Visible = true
excel.Workbooks:Add()
-- Here we call the default method
-- notice we DID NOT use the colon, as
-- the object used is Sheets, not excel
sheet = excel.Sheets(1)
print(sheet.Name)
-- Here we also call the default method
-- We must supply the self parameter
sheets = excel.Sheets
sheet2 = sheets(2)
print(sheet2.Name)
-- Setting values
excel.Sheets(1).Name = "MySheet1"
excel:Quit()
This can be very useful when dealing with collections, as commonly they have a default Item
property.
WARNING: one must be careful not to put the colon when using default methods of LuaCOM
objects contained in table or in other LuaCOM objects (see the sample above).
obj_typ = luacom.CreateObject("Some.TypedObject")
obj_untyp = luacom.CreateObject("Untyped.Object")
-- property read (get)
a = obj_typ.Value
b = obj_untyp:getValue()
-- property write (set)
obj.typ = a + 1
obj_untyp:setValue(b + 1)
interface = {}
18
interface.Test = 1
interface.TestIndex = {2,3}
obj = luacom.ImplInterface(interface, "TEST.Test", "ITest")
-- must print "1"
print(obj.Test)
-- must print nil (if there is no member named Test2)
print(obj.Test2)
-- this writes the filed Test
obj.Test = 1
-- Indexed property read. Must return 3 (remember that
-- indexed tables start at 1 in Lua)
i = obj:TestIndex(2)
-- Sets the indexed field
obj:setTestIndex(2,4)
-- Now must return 4
i = obj:TestIndex(2)
19
-- This should trigger the AfterUpdate event
--
calendar:NextMonth()
The cookie returned by Connect identifies this connection, and can later be used to release the
Connection. A COM object can have several event sinks connected to it simultaneously.
It’s also possible to separately create a LuaCOM object implementing the connection point source
interface and then connect it to the object using addConnection.
20
Message loop To receive events, it is necessary to have a message loop in the thread that owns the
object that is receiving the events. All events are dispatched through a Windows message queue cre-
ated during COM initialization. Without a message loop, the event objects implemented by LuaCOM
, will never receive method calls from the COM objects they are registered with. Out-of-process COM
servers implemented with LuaCOM also need a message loop to be able to service method calls (one
is provided by calling luacom.DetectAutomation).
lua2com situation The translation is done based on the type information of the method (or prop-
erty); it’s done following the order the parameters appear in the type information of the method. The
Lua parameters are used in the same order. For each parameter there are three possibilities:
The parameter is an “in” parameter LuaCOM gets the first Lua parameter not yet converted and
converts it to COM using LuaCOM type conversion engine.
The parameter is an “out” parameter LuaCOM ignores this parameter, as it will only be filled by
the called method. That is, the “out” parameters SHOULD NOT appear in the Lua parameter
list.
The parameter is an “in-out” parameter LuaCOM does the same as for “in” parameters.
When the caller of the method wants to omit a parameter, it must pass the nil value; LuaCOM
then proceeds accordingly, informing the called method about the omission of the parameter. If the
parameter has a default value, it is used instead. Notice that LuaCOM does not complain when one
omits non-optional parameters. In fact, LuaCOM ignores the fact that a parameter is or isn’t optional.
It leaves the responsibility for checking this to the implementation of the called method.
21
com2lua situation When the called method finishes, LuaCOM translates the return value and the
output values (that is, the values of the “out” and “in-out” parameters) to Lua return values. That
is, the method return value is returned to the Lua code as the first return value; the output values
are returned in the order they appear in the parameter list (notice that here we use the Lua feature of
multiple return values). If the method does not have return values, that is, is a “void” method, the
return values will be the output values. If there are no output values either, then there will be no return
values.
The called method can omit the return value or the output values; LuaCOM them will return nil
for each omitted value.
To illustrate these concepts, here follows a sample of these situations. First, we show an excerpt
of an ODL file describing a method of a COM object:
HRESULT TestShort(
[in] short p1, // an "in" parameter
[out] short* p2, // an "out" parameter
[in,out] short* p3, // an "in-out" parameter
[out,retval] short* retval); // the return value
Generic LuaCOM objects When dealing with generic LuaCOM objects, the binding adopts a dif-
ferent policy: all Lua parameters are converted to COM ones as “in-out” parameters. LuaCOM
assumes that these methods always return a value; if the called method does not return anything,
LuaCOM pushes a nil value2 . As all parameters are set as “in-out”, all of them will be returned back
to Lua, modified or not by the called method.
22
is, all “in” an “in-out” COM parameters are translated to parameters to the Lua function call (the
output parameters are ignored). When the call finishes, the first return value is translated as the return
value of the COM method and the other return values are translated as the “in-out” and “out” values,
following the order they appear in the method’s type information. Continuing the previous example,
here we show the implementation of a method callable from COM:
implementation = {}
-- This method receives TWO in/in-out parameters
function implementation:TestShort(p1, p2)
-- the first one is the retval, the second the first out param
-- the third the second out param (in fact, an in-out param)
return p1+p2, p1-p2, p1*p2
end
-- Implements an interface
obj = luacom.ImplInterface(implementation, "TEST.Test", ITest)
-- calls the function implementation:TestShort via COM
r1, r2, r3 = obj:TestShort(1,2)
• ignore the error, just doing nothing or returning some kind of error value.
The third type of error is always translated into a COM exception returned to the server. To ease
debugging, these errors are also logged (if the logging facility has been activated), as the server can
silenty ignore these exceptions, specially in events.
If the LuaCOM library is compiled with VERBOSE
defined, then a lot of informative messages are logged and all errors are displayed within a dialog
box. This helps debug errors inside events on the fly, as these errors are commonly ignored by the
server. Notice that this options slows down LuaCOM and can generate very big log files.
The behaviour of LuaCOM for the other two types can be customized. There is a table called
config inside the LuaCOM table. This table holds three fields related to error handling:
abort on API error if false, LuaCOM silently fails on errors inside API calls. This is NOT true
for errors caused by supplying bad parameters: these always generate calls to lua_error.
The default value for this field is false.
23
abort on error if false, errors inside method calls and property accesses are also ignored, possi-
bly return nil where a return value is expected. The default value for this field is true.
last error every time a run time error occurr LuaCOM sets this field with the text describing the
error. This field can be used to check if some operation failed; just remember to set it to nil
before the operation of interest.
Sample
-- to make all LuaCOM errors runtime errors
luacom.config.abort_on_error = true
luacom.config.abort_on_API_error = true
-- to silently ignore all errors
luacom.config.abort_on_error = false
luacom.config.abort_on_API_error = false
-- catching an ignored error
luacom.config.last_error = nil
obj:RunMethod(x,y)
if luacom.config.last_error then
print("Error!")
exit(1)
end
All errors are also logged. Notice that some of the logged exceptions are not really errors: they
are side-effects of the extensive use of exception handling inside LuaCOM code.
Lua 4 This version of Lua uses the nil value as false and non-nil values as true. As LuaCOM
gives a special meaning for nil values in the parameter list, it can’t use Lua convention for true and
false values; instead, LuaCOM uses the C convention: the true value is a number different from zero
and the false value is the number zero. Here follows a sample:
24
window.Visible = 1
-- this has the same result
windows.Visible = -10
else
window.Visible = 0
end
end
-- Shows window
showWindow(window, 1)
-- Hides window
showWindow(window, nil)
If the table has the conversion tag/metamethod, LuaCOM uses it to guide the conversion. If the
tag/metamethod is a method, LuaCOM calls it, passing the table and the COM type. The method
25
should return a COM object that LuaCOM will pass on. If the tag/metamethod is a table, LuaCOM
will look for a typelib field, an interfacefield , and a coclass field, and pass those as argu-
ments to the ImplInterfaceFromTypelib API call. If the table does not have a typelib field,
LuaCOM will look for a progid field and an interface field, and pass those to the ImplInterface
API call. Either way, LuaCOM will pass the returned object to COM.
3.4.7 Variants
When converting from COM to Lua, the default behavior is to transform variant values to the closest
Lua type. The script can change the conversion from Lua types to a table describing the variant, by
setting the TableVariants field of the luacom table(the LuaCOM namespace) to true. The
tables will have a Type field telling the original type of the variant, and a Value field containing the
conversion to the closest Lua type. Be careful with this feature, as it may break compatibility with
other scripts.
26
--
-- Sample use of enumerators
--
-- Gets an instance
word = luacom.GetObject("Word.Application")
-- Gets an enumerator for the Documents collection
docs_enum = luacom.GetEnumerator(word.Documents)
-- Prints the names of all open documents
doc = docs_enum:Next()
while doc do
print(doc.Name)
doc = docs_enum:Next()
end
The Extended Lua API method pairs allows the traversal of the enumeration using Lua’s for
statement. The sample above can be rewritten this way:
--
-- Sample use of enumerators
--
-- Gets an instance
word = luacom.GetObject("Word.Application")
-- Prints the names of all open documents
for index, doc in luacomE.pairs(word.Documents) do
print(doc.Name)
end
27
Chapter 4
4.1 Introduction
With LuaCOM it is possible to implement full-fledged COM objects and OLE controls using Lua.
Here we understand a COM object as a composite of these parts:
• a server, which implements one or more COM objects;
• registry information, which associates a CLSID (Class ID) to a triple server – type library –
default interface;
• a ProgID (Programmatic Identifier) which is a name associated to a CLSID;
• a type library containing a CoClass element.
The registry information maps a ProgID to a CLSID, which is, in turn, mapped to a server. The
type information describes the component, that is, which interfaces it exposes and what is the default
interface.
LuaCOM simplifies these tasks providing some helper functions to deal with registration and
instantiation of COM servers. LuaCOM suports both local (EXE) and in-process (DLL) servers.
LuaCOM also provides helper functions to register and instantiate OLE controls (with their user
interface embedded in the hosting application). This kind of object needs an in-process server, and a
supported Lua GUI toolkit (IUP, for now).
28
• to use COM objects anywhere a Lua table is expected. For example, a COM object might be
“exported” as a CORBA object, accessible through a network;
• to add and to redefine methods of an instance of a COM object. This might be very useful in the
preceding situations: an object of interest might be incremented and them exported to another
client.
Of course all this flexibility comes at some cost, primarily performance. Anyway, depending on
the application, the performance drawback might be negligible.
LuaCOM does not solve all problems: there is still the need of a type library, which must be build
using third party tools.
4.3 Terminology
To avoid misunderstandings, here we’ll supply the meaning we give to some terms used in this chapter.
We don’t provide formal definitions: we just want to ease the understanding of some concepts. To
better understand these concepts, see COM’s documentation.
Component a piece of software with some functionality that can be used by other components. It’s
composed by a set of objects that implement this functionality.
Component Object an object through which all the functionality of a component can be accessed,
including its other objects. This object may have many interfaces.
Application Object A component object with a interface that comprises all the top-level functional-
ity of a component; the client does not need to use other interfaces of the component object.
This concept simplifies the understanding of a component, as it puts all its functionalities in
an hierarchical manner (an application object together with its sub-objects, which can only be
accessed through methods and properties of the application object).
COM server Some piece of code that implements one or more component objects. A COM server
must tell the other applications and components which component objects it makes available. It
does so exposing them.
OLE control An object that has an user interface, and can be embedded inside other applications that
have OLE containers (usually C++ or VB applications).
CoClass A type library describing a component should have a CoClass entry, specifying some infor-
mation about the component:
• a name, differentiating one CoClass from others in the same type library;
• its CLSID, the unique identifier that distinguishes this component from all others;
• the interfaces of the component object, telling which one is the default. In a typical situ-
ation, only one interface will be supplied; thus the component object could be called an
Application object for that component;
• the source interface, that is, the interface the component uses to send events to the client.
This interface is not implemented by the component: it just uses objects that implement
this interface.
Lua Application Object It’s the Lua table used to implement the Application Object.
29
4.4 Building a LuaCOM COM server
There are some steps to build a COM server using LuaCOM:
2. identify what is going to be exported: Lua application object and its sub-objects;
Example Suppose we have a Lua library that implements the access of databases contained in a
specific DBMS. This library has three types of objects: databases, queries and records. In COM world,
this could be represented by an Application object that opens databases and returns a Database Object.
A Database object has, among others, a Query method. This method receives a SQL statement and
returns a Query object. The Query object is a collection, which can be iterated using the parameterized
property Records, which returns an object of type Record.
30
4.4.5 Registering the Component Object
Before being accessed by other applications, the component object must be registered in the system
registry. This can be done with the RegisterObject API function. This function receives a table
of registration info for the object. See the complete example for the fields of this table.
Implementing the Application Object Here we must use the LuaCOM method NewObject to cre-
ate a COM object and bind it to the table of the Lua Application Object. Them this object must
be made available to other applications through ExposeObject.
Implementing other objects The other objects of the component are obtained via the Lua Appli-
cation Object as return values of functions or as values stored in the fields of the Lua Ap-
plication Object (that is, via property access). These object should be implemented using
ImplInterface. They can be implemented in the initialization (and then be stored some-
where) or can be implemented on-demand (that is, each time a COM object should be return, a
call to ImplInterface is made).
Notice that the fields of the Lua table used to implement COM component will only be accessible
if they are present in the type library. If not, they are invisible to COM.
Termination
The COM server must call (in Lua) RevokeObject for each exposed object. Then it must call the
COM termination functions AFTER lua_close has been called; otherwise fatal errors may occur.
31
4.6 Generating Events
The method NewObject returns a connection point container object. This object allows the compo-
nent to send events to its clients just calling methods on this object, passing the expected parameters.
Return values are not allowed yet.
TestObj = {}
function TestObj:showWindow()
print("Show!")
events:OnShow()
end
function TestObj:hideWindow()
print("Hide!")
events:OnHide()
end
COM = {}
function COM:StartAutomation()
-- creates the object using its default interface
COMAppObject, events, e = luacom.NewObject(TestObj, "TEST.Test")
-- This error will be caught by detectAutomation
if COMAppObject == nil then
error("NewObject failed: "..e)
end
-- Exposes the object
cookie = luacom.ExposeObject(COMAppObject)
if cookie == nil then
error("ExposeObject failed!")
end
end
function COM:Register()
-- fills table with registration information
local reginfo = {}
reginfo.VersionIndependentProgID = "TEST.Test"
reginfo.ProgID = reginfo.VersionIndependentProgID..".1"
reginfo.TypeLib = "test.tlb"
32
reginfo.CoClass = "Test"
reginfo.ComponentName = "Test Component"
reginfo.Arguments = "/Automation"
reginfo.ScriptFile = path_to_script .. "testobj.lua"
-- stores component information in the registry
local res = luacom.RegisterObject(reginfo)
if res == nil then
error("RegisterObject failed!")
end
end
function COM:UnRegister()
-- fills table with registration information
local reginfo = {}
reginfo.VersionIndependentProgID = "TEST.Test"
reginfo.ProgID = reginfo.VersionIndependentProgID..".1"
reginfo.TypeLib = "test.tlb"
reginfo.CoClass = "Test"
-- removes component information from the registry
local res = luacom.UnRegisterObject(reginfo)
if res == nil then
error("UnRegisterObject failed!")
end
end
33
The demo/control directory of the LuaCOM distribution has an example of a control.
34
Chapter 5
Release Information
Here is provided miscellaneous information specific to the current version of LuaCOM. Here are
recorded the current limitations of LuaCOM, its known bugs, the history of modifications since the
former version, technical details etc.
5.1 Limitations
Here are listed the current limitations of LuaCOM, as of the current version, and information about
future relaxation of this restrictions.
• LuaCOM currently supports only exposes COM objects as “single use” objects. That might
be circumvented by exposing many times the same object. This restriction might be removed
under request;
• LuaCOM doesn’t support COM methods with variable number of parameters. This could be
circumvented passing the optional parameters inside a table, but this hasn’t been tested. This
may be implemented under request;
• LuaCOM doesn’t provide access to COM interfaces that doesn’t inherit from IDispatch
interface. That is, only Automation Objects are supported. This restriction is due to the late-
binding feature provided by LuaCOM. It’s possible to provide access to these COM interfaces
via a ”proxy” Automation Object, which translate calls made through automation to vtable
(early-binding) calls. It’s also possible to implement this ”proxy” directly using LuaCOM
C/C++ API, but this hasn’t been tested nor tried;
• LuaCOM only implements late-bound interfaces, but accepts a QueryInterface for early-bound
ones. This erroneous behavior is due to the way a VB client sends events to the server. See
subsection 5.4.5;
35
• when a table of LuaCOM objects (that is, a SAFEARRAY of IDispatch pointers) is passed
as a parameter to a COM object, these LuaCOM objects might not be disposed automatically
and may leak;
• when a COM object implemented in Lua is called from VBScript, the “in-out” parameters of
type SAFEARRAY cannot be modified. If they are, VBScript will complain with a COM error.
• to immediately terminate the server process, it’s necessary to eliminate all references in Lua to
the COM objects residing in this process and then force a garbage-collection cycle;
• sometimes a reference to a COM object may be stored by mistake to a global variable and
then forgot there. This may prevent the server process to exit even when a method like “Quit”
is called. To avoid this problem, one might group all to references to a COM object and its
sub-objects in a single table to avoid “lost” references.
36
5.4.3 Receiving events
When one wishes to receive events and notifications from a COM object, a connection must be es-
tablished using connection points. But that is not enough: the client application must have a message
loop running to get these notifications. For more information, see section 3.3.3.
37
Public WithEvents obj_dummy as MyCOMObject.Application
Public obj as Object
Set obj_dummy = CreateObject("MyCOMObject.Application")
Set obj = obj_dummy
This way the client may call methods of the COM object using the obj variable.
5.5 History
Version 1.3b2
• OLE controls with embedded UI;
Version 1.3b
• Conversion tag/metamethod for tables;
• A method of typelibs returned by GetTypeInfo exports all enumerations of the typelib to a table;
• Identifies when an interface pointer is in fact a local Lua table implementing a COM object;
Version 1.2
• Can be loaded by Lua 5’s require function;
• In-process servers, fully implemented in Lua (no initialization code in C is necessary for in-
process servers, and for local servers using Lua 5);
• byte arrays are now converted from/to strings with embedded zeros;
• LuaCOM now has a limited support for loading and browsing type information and type li-
braries. This includes the ability to import type library constants (enum’s) as Lua globals and
the ability to open the help information associated with a component;
• objects implementing IEnumVARIANT interface are now supported. This means that collec-
tions can be used in LuaCOM in a similar way as the are in VBScript c ;
38
• implemented a log mechanism to simplify debugging;
• LuaCOM now handles correctly COM calls with named parameters1 . This caused problems
when receiving Microsoft Excel c events;
• now it’s possible to specify the context used to create an instance of a COM object (whether it
should be created as a local server or as an in-process server);
• when faced with an IUnknown pointer, LuaCOM now queries it for IDispatch or IEnumVARIANT
interfaces, returning a LuaCOM object instead of an IUnknown pointer;
• improved error-handling: now LuaCOM allows the customization of the actions to be taken
when errors occur;
• LuaCOM now supports the concept of default method: when one uses a reference to a LuaCOM
object as a function, LuaCOM does the function call using the default method of that object;
• part of the LuaAPI of LuaCOM now is implemented in Lua 5. This eases the addition of new
features and avoids cramming the library. Nevertheless, this does not impact those who use the
binary release, as they carry the Lua code precompiled;
• luacom.GetObject now supports the use of monikers. Among other thing, this makes pos-
sible to use WMI and to open document files directly, e.g. luacom.GetObject("myfile.xls");
Version 1.1
• LuaCOM is now compatible with Lua 4 and Lua 5. It’s just a matter of linking with the right
library;
• when used with Lua 5, LuaCOM uses booleans to better match the Automation types;
• all functions of LuaCOM’s Lua API are now grouped together in a single table called luacom,
although they are still accessible globally as luacom <function> in the Lua 4 version of
the library;
• now it’s possible to create instances of Microsoft c Office c applications (Excel c , Powerpoint c
etc.). It was only possible to use them via GetObject; now you can create a new instance of these
applications using luacom.CreateObject;
• when compiled with the NDEBUG flag, LuaCOM does not use any kind of terminal output
anymore (printf, cout etc). This could break some applications.
1
Notice that LuaCOM does not implement named parameters; it just takes them when called from a COM client and
puts them.
39
Version 1.0
• property access modified: now parameterized properties must be accessed as functions using a
prefix to differentiate property read and write. If the prefix is omitted, a property get is assumed;
• better support for implementation of COM objects, including registration and event generation;
• Type conversion engine rewritten. Now it adheres more firmly to the types specified in the type
libraries;
• binding rewritten to better support “out” and “in-out” parameters and to adhere more strictly to
the recommended memory allocation policies for COM;
Version 0.9.2
• removal of LUACOM TRUE and LUACOM FALSE constants; now booleans follow the same
convention of the C language;
• changes in memory allocation policy, to follow more strictly practices recommended in COM
documentation;
• added limited support for implementing and registering COM objects in Lua
Version 0.9.1
• conversion to Lua 4;
• better handling of different kinds of type information (e.g. now can access Microsoft Internet
Explorer c object);
• LuaCOM does not initializes COM libraries anymore; this is left to the user;
• more stringent behavior about the syntax of method calls and property access (methods with “:”
and properties with “.”).
40
Chapter 6
Reference
Description
This function initializes the LuaCOM library, creates the global luacom table and fills it with
LuaCOM methods in the given Lua state. Notice that it’s necessary to initialize COM before, us-
ing OleInitialize or CoInitialize or something like that.
Sample
int main()
{
lua_State *L = lua_open(0);
OleInitialize(NULL);
luacom_open(L);
.
.
.
}
luacom close
Prototype
void luacom_close(lua_State* L);
41
Description
This function is intended to clean up the data structures associated with LuaCOM in a specific Lua
state (L). Currently, it does nothing, but in future releases it will do. So, do not remove from
your code! It must be also called before the COM termination functions (OleUninitialize and
CoInitialize) and before lua close.
Sample
int main()
{
lua_State *L = lua_open(0);
OleInitialize(NULL);
luacom_open(L);
.
.
.
luacom_close(L);
lua_close(L);
OleUninitialize();
}
luacom detectAutomation
Prototype
int luacom_detectAutomation(lua_State *L, int argc, char *argv[]);
Description
This function gets from the top of the Lua stack a table which should hold two fields named “StartAu-
tomation” and “Register” (these fields should contain functions that implement these actions). Then
it searches the command line (provided argc and argv) for the switches “/Automation” or “/Reg-
ister”. If one of these switches is found, it then calls the corresponding function in the Lua table.
Finally it returns a value telling what happened, so the caller function may change its course of action
(if needed).
This function is simply a helper for those implementing Automation servers using LuaCOM. Most
of the work should be done by the Lua code, using the methods RegisterObject, NewObject,
and ExposeObject.
42
Sample
/*
* com_object.cpp
*
* This sample C++ code initializes the libraries and
* the COM engine to export a COM object implemented in Lua
*/
#include <ole2.h>
// libraries
extern "C"
{
#include <lua.h>
#include <lualib.h>
}
#include <luacom.h>
CoInitialize(NULL);
IupOpen();
lua_State *L = lua_open(0);
lua_baselibopen (L);
lua_strlibopen(L);
lua_iolibopen(L);
luacom_open(L);
lua_dofile(L, "implementation.lua");
lua_getglobal(L, "COM");
43
switch(result)
{
case LUACOM_AUTOMATION:
// runs the message loop, as all the needed initialization
// has already been performed
MessageLoop();
break;
case LUACOM_NOAUTOMATION:
// This only works as a COM server
printf("Error. This is a COM server\n");
break;
case LUACOM_REGISTER:
// Notifies that the COM object has been
// registered
printf("COM object successfully registered.");
break;
case LUACOM_AUTOMATION_ERROR:
// detectAutomation found /Automation or /Register but
// the initialization Lua functions returned some error
printf("Error starting Automation");
break;
}
luacom_close(L);
lua_close(L);
CoUninitialize();
return 0;
}
-------
-- implementation.lua
--
-- This is a sample implementation of a COM server in Lua
--
function TestObj:showWindow()
dialog.show()
end
44
function TestObj:hideWindow()
dialog.hide()
end
COM = {}
function COM:Register()
-- fills table with registration information
local reginfo = {}
reginfo.VersionIndependentProgID = "TESTE.Teste"
reginfo.ProgID = reginfo.VersionIndependentProgID..".1"
reginfo.TypeLib = "teste.tlb"
reginfo.CoClass = "Teste"
reginfo.ComponentName = "Test Component"
reginfo.Arguments = "/Automation"
-- stores component information in the registry
local res = luacom.RegisterObject(reginfo)
if res == nil then
error("RegisterObject failed!")
end
end
function COM:UnRegister()
-- fills table with registration information
local reginfo = {}
reginfo.VersionIndependentProgID = "TESTE.Teste"
reginfo.ProgID = reginfo.VersionIndependentProgID..".1"
45
reginfo.TypeLib = "teste.tlb"
reginfo.CoClass = "Teste"
-- removes component information from the registry
local res = luacom.UnRegisterObject(reginfo)
if res == nil then
error("UnRegisterObject failed!")
end
end
luacom IDispatch2LuaCOM
Prototype
int luacom_IDispatch2LuaCOM(lua_State *L, void *pdisp_arg);
Description
This functions takes a pointer to IDispatch, creates a LuaCOM object for it and pushes it in the
Lua stack. This function is useful when one gets an interface for a COM object from C/C++ code
and wants to use it in Lua.
Sample
void CreateAndExport(lua_State* L)
{
// Creates the object
IUnknown *obj = CreateObj();
Description
This method finds the Class ID referenced by the ID parameter and creates an instance of the object
with this Class ID. If there is any problem (ProgID not found, error instantiating object), the method
46
returns nil.
Parameters
Parameter Type
ProgID String
Return Values
Return Item Possible Values
luacom obj LuaCOM object
nil
Sample
inet_obj = luacom.CreateObject("InetCtls.Inet")
if inet_obj == nil then
print("Error! Object could not be created!")
end
Connect
Use
implemented_obj, cookie = luacom.Connect(luacom_obj, implementation_table)
Description
This method finds the default source interface of the object luacom_obj, creates an instance of
this interface whose implementation is given by implementation_table and creates a connec-
tion point between the luacom_obj and the implemented source interface. Any calls made by
the luacom_obj to the source interface implementation will be translated to Lua calls to member
function present in the implementation_table. If the method succeeds, the LuaCOM object
implemented by implementation_table, plus a cookie that identifies the connection, are re-
turned; otherwise, nil is returned.
Notice that, to receive events, it’s necessary to have a Windows message loop.
Parameters
Parameter Type
luacom_obj LuaCOM object
implementation_table Table or userdata
Return Values
Return Item Possible Values
implemented obj LuaCOM object
nil
cookie number
47
Sample
events_handler = {}
function events_handler:NewValue(new_value)
print(new_value)
end
events_obj = luacom.Connect(luacom_obj, events_handler)
ImplInterface
Use
implemented_obj = luacom.ImplInterface(impl_table, ProgID, interface_name)
Description
This method finds the type library associated with the ProgID and tries to find the type information
of an interface called “interface name”. If it does, then creates an object whose implementation is
“impl table”, that is, any method call or property access on this object is translated to calls or access
on the members of the table. Then it makes a LuaCOM object for the implemented interface and
returns it. If there are any problems in the process (ProgID not found, interface not found, interface
isn’t a dispinterface), the method returns nil.
Parameters
Parameter Type
impl_table table or userdata
ProgID string
interface_name string
Return Values
Return Item Possible Values
implemented obj LuaCOM object
nil
Sample
myobject = {}
function myobject:MyMethod()
print("My method!")
end
myobject.Property = "teste"
luacom_obj = luacom.ImplInterface(myobject, "TEST.Test", "ITest")
-- these are done via Lua
myobject:MyMethod()
print(myobject.Property)
-- this call is done through COM
48
luacom_obj:MyMethod()
print(luacom_obj.Property)
ImplInterfaceFromTypelib
Use
impl_obj = luacom.ImplInterfaceFromTypelib(
impl_table,
typelib_path,
interface_name,
coclass_name)
Description
This method loads the type library whose file path is “typelib path” and tries to find the type informa-
tion of an interface called “interface name”. If it does, then creates an object whose implementation
is “impl table”, that is, any method call or property access on this object is translated to calls or access
on the members of the table. Then it makes a LuaCOM object for the implemented interface and re-
turns it. If there are any problems in the process (ProgID not found, interface not found, interface isn’t
a dispinterface), the method returns nil. The “coclass name” parameter is optional; it is only
needed if the resulting LuaCOM object is to be passed to the methods Connect, AddConnection
or ExposeObject. This parameter specifies the Component Object class name to which the inter-
face belongs, as one interface may be used in more than one “coclass”.
Parameters
Parameter Type
impl_table table or userdata
typelib_path string
interface_name string
coclass_name (optional) string
Return Values
Return Item Possible Values
implemented obj LuaCOM object
nil
Sample
myobject = {}
function myobject:MyMethod()
print("My method!")
end
myobject.Property = "teste"
luacom_obj = luacom.ImplInterfaceFromTypelib(myobject, "test.tlb",
"ITest", "Test")
49
-- these are done via Lua
myobject:MyMethod()
print(myobject.Property)
-- this call is done through COM
luacom_obj:MyMethod()
print(luacom_obj.Property)
GetObject
Use
luacom_obj = luacom.GetObject(ProgID)
luacom_obj = luacom.GetObject(moniker)
Description
The first version method finds the Class ID referenced by the ProgID parameter and tries to find a
running instance of the object having this Class ID. If there is any problem (ProgID not found, object
is not running), the method returns nil.
The second version tries to find an object through its moniker. If there is any problem, the method
returns nil.
Parameters
Parameter Type
ProgID/moniker String
Return Values
Return Item Possible Values
luacom obj LuaCOM object
nil
Sample
excel = luacom.GetObject("Excel.Application")
if excel == nil then
print("Error! Could not get object!")
end
NewObject/NewControl
Use
-- Creates a COM object
implemented_obj, events_sink, errmsg = luacom.NewObject(impl_table, ProgID)
-- Creates an OLE control
implemented_obj, events_sink, errmsg = luacom.NewControl(impl_table, ProgID)
50
Description
This method is analogous to ImplInterface, doing just a step further: it locates the default inter-
face for the ProgID and uses its type information. That is, this method creates a Lua implementation
of a COM object’s default interface. This is useful when implementing a complete COM object in
Lua. It also creates a connection point for sending events to the client application and returns it as the
second return value. If there are any problems in the process (ProgID not found, default interface is
not a dispinterface etc), the method returns nil twice and returns the error message as the third
return value.
To send events to the client application, just call methods of the event sink table returned. The
method call will be translated to COM calls to each connection. These calls may contain parameters
(as specified in the type information).
Parameters
Parameter Type
impl_table table or userdata
ProgID string
Return Values
Return Item Possible Values
implemented obj LuaCOM object
nil
event sink event sink table
nil
errmsg error message in the case
of failure
nil
Sample
myobject = {}
function myobject:MyMethod()
print("My method!")
end
myobject.Property = "teste"
51
-- here we sink events
evt:Event1()
ExposeObject
Use
cookie = luacom.ExposeObject(luacom_obj)
Description
This method creates and registers a class factory for luacom obj, so that other running applications
can use it. It returns a cookie that must be used to unregister the object. If the method fails, it returns
nil.
ATTENTION: the object MUST be unregistered (using RevokeObject) before calling luacom close
or lua close, otherwise unhandled exceptions might occur.
Parameters
Parameter Type
luacom obj LuaCOM object
Return Values
Return Item Possible Values
cookie number
nil
Sample
myobject = luacom.NewObject(impl_table, "Word.Application")
cookie = luacom.ExposeObject(myobject)
function end_of_application()
luacom.RevokeObject(cookie)
end
RegisterObject
Use
result = luacom.RegisterObject(registration_info)
Description
This method creates the necessary registry entries for a COM object, using the information in registration info
table. If the component is successfully registered, the method returns a non-nil value.
The registration info table must contain the following fields1 :
1
For a better description of these fields, see COM’s documentation.
52
VersionIndependentProgID This field must contain a string describing the programmatic identifier
for the component, e.g. “MyCompany.MyApplication”.
ProgID The same as VersionIndependentProgID but with a version number, e.g. “MyCompany.MyApplication.2”.
TypeLib The file name of the type library describing the component. This file name should contain a
path, if the type library isn’t in the same folder of the executable. Samples: mytypelib.tlb,
c:\app\test.tlb, test.exe\1 (this last one can be used when the type library is bound
to the executable as a resource).
Control Must be true if the object is an OLE control, and false or nil otherwise.
CoClass The name of the component class. There must be a coclass entry in the type library with
the same name or the registration will fail.
Arguments This field specifies what arguments will be supplied to the component executable when
started via COM. Normally it should contain “/Automation”.
ScriptFile This field specifies the full path of the script file that implements the component. Only
used to register in-process servers.
This method is not a generic “registering tool” for COM components, as it assumes the component
to be registered is implemented by the running executable during registration.
Parameters
Parameter Type
registration info table with registration information
Return Values
Return Item Possible Values
result nil or non-nil value
Sample
-- Lua registration code
function RegisterComponent()
reginfo.VersionIndependentProgID = "TESTE.Teste"
-- Adds version information
reginfo.ProgID = reginfo.VersionIndependentProgID..".1"
reginfo.TypeLib = "teste.tlb"
reginfo.CoClass = "Teste"
reginfo.ComponentName = "Test Component"
reginfo.Arguments = "/Automation"
reginfo.ScriptFile = "teste.lua"
local res = luacom.RegisterObject(reginfo)
return res
end
53
UnRegisterObject
Use
result = luacom.UnRegisterObject(registration_info)
Description
This method removes the registry entries for a COM object, using the information in registration info
table. If the component is successfully unregistered, the method returns a non-nil value.
The registration info table must contain the following fields2 :
VersionIndependentProgID This field must contain a string describing the programmatic identifier
for the component, e.g. “MyCompany.MyApplication”.
ProgID The same as VersionIndependentProgID but with a version number, e.g. “MyCompany.MyApplication.2”.
TypeLib The file name of the type library describing the component. This file name should contain a
path, if the type library isn’t in the same folder of the executable. Samples: mytypelib.tlb,
c:\app\test.tlb, test.exe\1 (this last one can be used when the type library is bound
to the executable as a resource).
CoClass The name of the component class. There must be a coclass entry in the type library with
the same name or the registration will fail.
Parameters
Parameter Type
registration info table with registration information
Return Values
Return Item Possible Values
result nil or non-nil value
Sample
-- Lua registration code
function UnRegisterComponent()
reginfo.VersionIndependentProgID = "TESTE.Teste"
-- Adds version information
reginfo.ProgID = reginfo.VersionIndependentProgID..".1"
reginfo.TypeLib = "teste.tlb"
reginfo.CoClass = "Teste"
local res = luacom.UnRegisterObject(reginfo)
return res
end
2
For a better description of these fields, see COM’s documentation.
54
addConnection
Use
cookie = luacom.addConnection(client, server)
Description
This method connects two LuaCOM objects, setting the server as an event sink for the client,
that is, the client will call methods of the server to notify events (following the COM model). This will
only work if the client supports connection points of the server’s type. If the method succeeds,
it returns the cookie that identifies the connection; otherwise, it throws an error.
Parameters
Parameter Type
client LuaCOM object
server LuaCOM object
Return Values
Return Item Possible Values
cookie number
Sample
obj = luacom.CreateObject("TEST.Test")
event_sink = {}
function event_sink:KeyPress(keynumber)
print(keynumber)
end
event_obj = luacom.ImplInterface(
event_sink, "TEST.Test", "ITestEvents")
releaseConnection
Use
luacom.releaseConnection(client, event_sink, cookie)
Description
This method disconnects a LuaCOM object from an event sink.
55
Parameters
Parameter Type
client LuaCOM object
event sink LuaCOM object
cookie LuaCOM object
Return Values
There are none.
Sample
obj = luacom.CreateObject("TEST.Test")
event_sink = {}
function event_sink:KeyPress(keynumber)
print(keynumber)
end
event_obj = luacom.ImplInterface(
event_sink, "TEST.Test", "ITestEvents")
result = luacom.addConnection(obj, event_obj)
.
.
.
luacom.releaseConnection(obj)
ProgIDfromCLSID
Use
progID = luacom.ProgIDfromCLSID(clsid)
Description
This method is a proxy for the Win32 function ProgIDFromCLSID.
Parameters
Parameter Type
clsid string
Return Values
Return Item Possible Values
progID string
nil
56
Sample
progid = luacom.ProgIDfromCLSID("{8E27C92B-1264-101C-8A2F-040224009C02}")
obj = luacom.CreateObject(progid)
CLSIDfromProgID
Use
clsid = luacom.CLSIDfromProgID(progID)
Description
It’s the inverse of ProgIDfromCLSID.
ShowHelp
Use
luacom.ShowHelp(luacom_obj)
Description
This method tries to locate the luacom obj’s help file in its type information and shows it.
Parameters
Parameter Type
luacom obj LuaCOM object
Return Values
None.
Sample
obj = luacom.CreateObject("TEST.Test")
luacom.ShowHelp(obj)
GetIUnknown
Use
iunknown = luacom.GetIUnknown(luacom_obj)
57
Description
This method returns a userdata holding the IUnknown interface pointer to the COM object be-
hind luacom obj. It’s important to notice that Lua does not duplicates userdata: many calls to
GetIUnknown for the same LuaCOM object will return the same userdata. This means that the
reference count for the IUnknown interface will be incremented only once (that is, the first time the
userdata is pushed) and will be decremented only when all the references to that userdata go out of
scope (that is, when the userdata suffers garbage collection).
One possible use for this method is to check whether two LuaCOM objects reference the same
COM object.
Parameters
Parameter Type
luacom obj LuaCOM object
Return Values
Return Item Possible Values
iunknown userdata with IUnknown
metatable
nil
Sample
-- Creates two LuaCOM objects for the same COM object
-- (a running instance of Microsoft Word(R) )
word1 = luacom.GetObject("Word.Application")
word2 = luacom.GetObject("Word.Application")
-- These two userdata should be the same
unk1 = luacom.GetIUnknown(word1)
unk2 = luacom.GetIUnknown(word2)
assert(unk1 == unk2)
isMember
Use
answer = luacom.isMember(luacom_obj, member_name)
Description
This method returns true (that is, different from nil) if there exists a method or a property of the
luacom obj named member name.
Parameters
Parameter Type
luacom obj LuaCOM object
member name string
58
Return Values
Return Item Possible Values
answer nil or non-nil
Sample
obj = luacom.CreateObject("MyObject.Test")
if luacom.isMember(obj, "Test") then
result = obj:Test()
end
StartLog
Use
result = luacom.StartLog(log_file_name)
Description
This methods activates the log facility of LuaCOM, writing to the log file all errors that occurr. If the
library was compiled with VERBOSE defined, it also logs other informative messages like creation
and destruction of LuaCOM internal objects, method calls etc. This can help track down object
leaks. The method returns true
if the log file could be opened, false
otherwise.
Parameters
Parameter Type
log file name string
Return Values
Return Item Possible Values
result boolean
Sample
ok = luacom.StartLog("luacomlog.txt")
if not ok then
print("log not opened")
end
EndLog
Use
luacom.EndLog()
59
Description
This method stops the log facility (if it has been activated), closing the log file.
Parameters
None.
Return Values
None.
Sample
luacom.EndLog()
GetEnumerator
Use
e = luacom.GetEnumerator(luacom_obj)
Description
This method returns a COM enumerator for a given LuaCOM object (if it provides one). This is the
same as calling the NewEnum
method, at least for the majority of the objects. The enumerator object is described in section 6.4.
Parameters
Parameter Type
luacom obj LuaCOM object
Return Values
Return Item Possible Values
e enumerator object or nil
Sample
-- Prints all sheets of an open Excel Application
excel = luacom.GetObject("Excel.Application")
e = luacom.GetEnumerator(excel.Sheets)
s = e:Next()
while s do
print(s.Name)
s = e:Next()
end
60
6.3 Lua Extended API
pairs
GetType
CreateLocalObject
CreateInprocObject
LoadConstants
FillTypeInfo
FillTypeLib
Methods
Next returns the next object in the enumeration or nil if the end has been reached.
Skip skips the next object, returning true if succeeded of false if not.
Methods
GetDocumentation returns a table containing the fields name, helpstring, helpcontext and
helpfile for the type library.
GetTypeInfoCount returns the number of type descriptions contained in the type library.
GetTypeInfo(n) returns an type information object for the n-th type description.
ShowHelp tries to launch the help file associated with the type library (if any).
61
Methods
GetTypeLib returns the containing type library object.
GetFuncDesc(n) returns a table describing the n-th function of the type description. This table
contains the following fields: memid (dispatch identifier), invkind (invoke kind), Params
(number of parameters), ParamsOpt (number of optional parameters), description, helpfile,
helpcontext, name. Besides that, it stores an array-like table called parameters describ-
ing each parameter of the function, with these fields: name, type.
GetVarDesc(n) returns a table describing the n-th variable (or constant) in the type description. This
table contains the following fields: name, value (for constants only).
GetDocumentation returns a table with documentation for the type description, with the fields name,
helpstring, helpcontext and helpfile.
GetTypeAttr returns a table containing the type attributes for the type description. This table holds
the following fields: GUID, typekind, Funcs (number of functions), Vars (number of
variables or constants) and ImplTypes. There is also a flags field, containing a table that
describes the flags for this type description. This table contains the following boolean fields:
control, appobject, dispatchable, oleautomation, cancreate.
GetImplType(n) For type descriptions of COM classes, this returns the type information object for
the nth interface of the COM class.
GetImplTypeFlags(n) For type descriptions of COM classes, this returns a table containing the im-
plementation flags for the n-th interface belonging to the COM class. This table holds the
following boolean flags: default, source, restricted, defaultvtable.
ExportEnumerations returns a table with all the enumerations in this typelib. The keys are the
enumeration names, and each one of them is a table, keyed by the enumeration values.
62
Chapter 7
Credits
LuaCOM has been developed by Renato Cerqueira, Vinicius Almendra and Fabio Mascarenhas. The
project is sponsored by TeCGraf (Technology Group on Computer Graphics).
63