3 Examples
Detailed examples on how to use Comet
3.1 Comet examples
This chapter describes in detail som examples on Comet usage; the simpler ones first and the most advanced last.
Four examples are given:
- Browsing to a specified address
- Opening Excel, dumping some data, showing a graph
- Calling a function in a C++ library
Source code for these are included in the distribution, in the directory
comet/examples
.The abbreviations VB and VBA are used for Visual Basic and Visual Basic for Applications.
3.2 Requirements
The first example requires that Internet Explorer 4.0 or later is installed.
Example two requires Excel from Office 97 or Office 2000.
The last example can be run as it is, but to modify the COM-library, Visual C++ 5.0 or later is required.
3.3 Example one, opening a browser to a specific URL
This example shows how to open a browser (Internet Explorer), and navigate through it to a specific address.
To get the COM interface for the browser, we use a tool such as OLE/COM Object Viewer, which is included in Microsoft's Windows Platform SDK, Visual C and Visual Basic.
Checking the interface for Internet Explorer, we find a couple of things that we need. First, we need the class ID. Then we need the name and parameter list of the funcions and properties required to create and use a browser.
Since starting a browser is not a performance-critical task, we can use the slowest and safest way to do it from Erlang. This means starting the
erl_com
as a port process, and using the IDispatch interface to access Internet Explorer.Although Internet Explorer provides a dual interface, (that is an interface with both a method table and an IDispatch-interface), the IDispatch interface is safer and slower. Giving it a bad parameter list, returns an error code, rather than a core dump.
To use a COM object, we have to start the server (which starts the port) and start a thread. Then we can create the object, and do what we want with it.
To be able to use constants, we put the source in a module, rather than call it interactively in the Erlang shell.
-module(win_browse). -include("erl_com.hrl"). -export([open/1, example/0]). open(Url) -> {ok, Pid}= erl_com:start_process(), T= erl_com:new_thread(Pid), Obj= erl_com:create_dispatch(T, "InternetExplorer.Application", ?CLSCTX_LOCAL_SERVER), erl_com:invoke(Obj, "Navigate", [Url]), erl_com:property_put(Obj, "Visible", true), Obj. example() -> open("www.erlang.org").The internet explorer application has a dispatch interface, that implements the IWebBrowser interface. There are a lot of methods. We use the
Navigate
method to open a specific URL, and theVisible
property to show the browser. (By default, the browser is created invisible, like other Microsoft programs used from COM.)3.4 Example two, making a graph in Excel
In this example, we also start an instance of the Excel application. We use the program name "Excel.Application", which can be used instead of a class ID. This selects the Excel that is installed; Excel from Office 97 or Office 2000.
The easiest way to do anything with Excel is to first record a VBA macro. The resulting VBA macro is shown in figure 1. This macro is manually rewritten a bit to make it simpler. We try it out, and the result is shown in figure 2.
Now, to perform this into Erlang, we have two choices: either we can call the VB code as a subroutine using COM from Erlang, or we can reimplement the VB macro in Erlang. Since this is a user's guide, we of course choose the latter.
To get to the interfaces, we use OLE/COM Object Viewer, and get the IDL for Excel. There is an Excel type library available. We do not want all of it because it is huge. We just pick the needed interfaces, which are
_Application
,_Graph
and_Range
. We also extract some enums, which are constants used for parameters in the COM calls.There are some tricky issues when calling COM from Erlang
First, VB handles releasing of COM interfaces implicitly. Erlang and COM does not do this, so we have to make calls to
erl_com:release/1
for every interface we get. For instance, every_Range
we get from the property_Application.Range
, has to be released. We do this in the helper functiondata_to_column/3
.Secondly, when an interface is returned, it is returned as an integer. This integer is actually an index into an interface array contained in the
erl_com_drv
port program. When calling functions inerl_com
, we have to provide both the pid and the thread number, so there is a helper functionerl_com::package_interface/2
, that repackages the interface integer with given thread or other interface. When giving the interface as a parameter to a COM function (througherl_com:call
orerl_com:invoke
), however, the interface should be converted to a pointer, which is done with the tuple notation for COM types:{vt_unknown, Interface}
.
When Excel is started, we execute a series of Excel commands to enter data and to draw a graph. The commands are translated from a VBA macro that we got using Excel's standard macro recorder.
We use some constants that are needed for the Excel commands. These are taken from Visual Basic's code generation from the Excel interfaces. Although these can be fetched from Excel using COM,
erl_com
does not yet support this. (Future releases will include code-generation that will greatly simplify using big COM-interfaces.-module(xc). -author('jakob@erix.ericsson.se'). -include("erl_com.hrl"). %% enum XlChartFormat -define(XlPieExploded, 69). -define(XlPie, 5). %% enum XlChartLocation -define(xlLocationAsNewSheet, 1). -define(xlLocationAsObject, 2). -define(xlLocationAutomatic, 3. %% enum XlRowCol -define(xlColumns, 2). -define(xlRows, 1). -export([populate_area/4, f/3, make_graph/6, sample1/0]). to_cell_col(C) when C > 26 -> [C / 26 + 64, C rem 26 + 64]; to_cell_col(C) -> [C+64]. populate_area(E, _, _, []) -> ok; populate_area(E, Row, Col, [Data | Resten]) -> Cell= to_cell_col(Col)++integer_to_list(Row), io:format(" ~s ~n ", [Cell]), N= erl_com:property_get(E, "range", [Cell]), Range= erl_com:package_interface(E, N), erl_com:property_put(Range, "Value", Data), erl_com:release(Range), populate_area(E, Row+1, Col, Resten). f(E, _, []) -> ok; f(E, Startcell, [Data | Resten]) -> {R, C}= Startcell, Cell= "R"++integer_to_list(R)++"C"++integer_to_list(C), io:format(" ~p ~n ", [Cell]), f(E, {R+1, C}, Resten). make_graph(E, Row1, Col1, Row2, Col2, Title) -> Charts = erl_com:package_interface(E, erl_com:property_get(E, "Charts")), erl_com:invoke(Charts, "Add"), ActiveChart= erl_com:package_interface(E, erl_com:property_get (E, "ActiveChart")), erl_com:property_put(ActiveChart, "ChartType", {vt_i4, ?XlPieExploded}), erl_com:invoke(ActiveChart, "Location", [{vt_i4, ?xlLocationAsObject}, "Sheet1"]), Chart= erl_com:package_interface(E, erl_com:property_get(E, "ActiveChart")), R= to_cell_col(Col1)++integer_to_list(Row1)++":" ++to_cell_col(Col2)++integer_to_list(Row2), io:format(" ~s ~n ", [R]), Range= erl_com:property_get(E, "Range", [R]), erl_com:invoke(Chart, "SetSourceData", [{vt_unknown, Range}, {vt_i4, ?xlColumns}]), erl_com:property_put(Chart, "HasTitle", true), ChartTitle= erl_com:package_interface(E, erl_com:property_get (Chart, "ChartTitle")), erl_com:property_put(ChartTitle, "Caption", Title). %erl_com:release(erl_com:package_interface(E, Range)), %erl_com:release(ActiveChart), %erl_com:release(Charts). sample1() -> {ok, Pid}= erl_com:start_process(), T= erl_com:new_thread(Pid), E= erl_com:create_dispatch(T, "Excel.Application", ?CLSCTX_LOCAL_SERVER), erl_com:property_put(E, "Visible", true), Wb= erl_com:package_interface(T, erl_com:property_get(E, "Workbooks")), erl_com:invoke(Wb, "Add"), populate_area(E, 1, 1, ["Erlang", "Java", "C++"]), populate_area(E, 1, 2, ["25", "100", "250"]), make_graph(E, 1, 1, 3, 2, "Programming errors, by programming language"), {T, E, Wb}.3.5 Example three, calling a COM object in C++
To be done.