UNO registery and Bootstrapping
OpenOffice.org can be extended; you can add features using a number of different programming languages, including OOoBasic, C++, Java, and Python. These added features are called Add-Ons, components, Add-ins (discussed here) and we can wonder how Openoffice.org knows something about the corresponding code ? The tools involved in this chapter are regview, regcomp, regmerge and idlc.
Why UNO registry ?
It is common for programmer to develop a program and link it with a static library. If the library is a dynamic one the linking problem is let to Operating System : this OS have to know if the library is already loaded and if not where it lies. Let us be more concrete with an example.
In the example above in Figure above our program example.cxx calls something in mylib.so or mylib.dll (the arrow means an interaction). This is done in a very simple way if both are compiled with same language : under Linux we use a -Lmylib.so when compiling example.cxx (more here). Then when you launch example binary executable when it needs what is in mylib.so it asks OS to load it. The case is a little more complicated if mylib.so is compiled with an other language than C++ : you have to learn how to pass parameters in C++ to reach the content of mylib.so. This is feasible changing a little example.cxx code. But what happens if you have a binary executable program and you want to give him new features with a dynamic library ? This can be done with OpenOffice.org as already mentioned.
What kind of problems OpenOffice has then to carry out ?
First of all, it is impossible for Openoffice.org to know in advance what is the name of the library (it's a name you choose). Then OpenOffice.org must provide a mechanism to know its name : this is one of the goals of registry. Is the name of the dynamic library enough ? No, if you imagine you can extend with two or more libraries : it is important to know that foo1 procedure/method comes from foo_lib1 library and foo2 from foo_lib2... In OpenOffice.org this is generalized saying such an interface comes from such a library ... There is a lot of other problems to resolve before making it working properly. For example a problem with the name of procedure/methods : if you write it in C or in C++ the exported names will differ.
And the parameters, how do you pass them ? Imagine OOoCalc call something in mylib.so : it is unimaginable to ask OOocalc users to write something describing how parameters are passed in their OOoCalc formula ! OpenOffice has to know how pass a parameter in your library. This is done with what is called implementation environment. This done with specifying that every dynamic library has to export :
//Listing 1 How to get the Implementation Environnement in a dynamic library
// C++
extern "C"
void SAL_CALL component_getImplementationEnvironment(
const sal_Char ** ppEnvTypeName,
uno_Environment ** ppEnv )
The passing parameters way is fixed by extern C. This is not a decoration but an obligation. If not present a ["component_getImplementationEnvironment" could not be found] error will be thrown when you try to registry (with regmerge for instance).
Registery uses rdb files in a binary file format. Binaries format are difficult to read by human and then a tool is provided to give the information in human readable form (see regview in further section).
How to use UNO registeries
Documentation for registries already exist at : uno registeries. More information at : ooomacros where these tools are available :
- Add On Tool
Author: Bernard Marcelly
- Add On Installer
Author: Didier Lachièze, with code from Danny Brewer, Bernard Marcelly and Andrew Brown
- Basic Library Installer
Author: Danny Brewer, with code from Andrew Brown & Didier Lachièze
In my opinion, the simpler way to registery is to modify the file unorc (under linux) or uno.ini (under Windows). You have to put the file your_library.uno.rdb in <OOo>/program directory, and editing/modifying the file <OOo>/program/unorc (under linux) adding (last line your_library.uno.rdb) :
#unorc or uno.ini [Bootstrap] UNO_SHARED_PACKAGES=${$SYSBINDIR/bootstraprc:BaseInstallation}/share/uno_packages UNO_SHARED_PACKAGES_CACHE=$UNO_SHARED_PACKAGES/cache UNO_USER_PACKAGES=${$SYSBINDIR/bootstraprc:UserInstallation}/user/uno_packages UNO_USER_PACKAGES_CACHE=$UNO_USER_PACKAGES/cache UNO_TYPES=$SYSBINDIR/types.rdb ?$UNO_SHARED_PACKAGES_CACHE/types.rdb ?$UNO_USER_PACKAGES_CACHE/types.rdb UNO_SERVICES= ?$UNO_USER_PACKAGES_CACHE/services.rdb ?$UNO_SHARED_PACKAGES_CACHE/services.rdb $SYSBINDIR/services.rdb ?$SYSBINDIR/your_library.uno.rdb
Don't forget to register the location of your_library.uno.so in the your_library.uno.rdb. This is done with regcomp tool :
regcomp -Register -r your_library.uno.rdb -c <somewhere>/your_library.uno.so
An other way is to use pkgchk or the recent unopkg for Ooo2.0x.
The Bootstrap
Bootstrapping can be defined in saying it's the way to obtain a service manager. We present two different examples of bootstraping.
C++ UNO bootstrapping via defaultBootstrap_InitialComponentContext()
We have have already described this bootstrap method because it concerns the classical SDK example we started with in chapter 4 (see <OpenOffice.org1.1_SDK>/examples/DevelopersGuide/ProfUNO/CppBinding). We provide here two different views to explain how it works.
Interfaces ans Services with defaultBootstrap_InitialComponentContext()
First we recall with the Figure below how we program it. It's easy to see the first C++ instruction is
//C++
defaultBootstrap_InitialComponentContext();
But when drawing details from the figure we cannot infer how it works and particularly with registery. It's only because Figure below is a C++/Java programmer point of view.
defaultBootstrap_InitialComponentContext() is a buit-in function which return a com.sun.star.uno.XComponentContext interface, as alrady explained. This interface has a method "getServiceManager" which allow us obtaining service manager (as a com.sun.star.lang.XMultiComponentFactory type variable). It's an interface whch contain method "createInstanceWithContext" among others, wich uses two parameters, the string "com.sun.star.bridge.UnoUrlResolver" and the previous variable of type com.sun.star.uno.XComponentContext. We can follow the schematic until the bottom, but we let it as an excercise (See also com.sun.star.uno.XInterface, com.sun.star.bridge.XUnoUrlResolver and com.sun.star.lang.XMultiServiceFactory interfaces).
How defaultBootstrap_InitialComponentContext() bootstrapping works
To see what happens during the beginning of bootstrap, we go and see the binary in <OpenOffice.org1.1_SDK>/LINUXexample.out/bin where binaries are constructed by default makefiles. We see three files :
- office_connect the binary executable
- office_connectrc : file with this content :
UNO_TYPES=$SYSBINDIR/office_connect.rdb UNO_SERVICES=$SYSBINDIR/office_connect.rdb
- office_connect.rdb
The files construction will perhaps help us to understand beter. We give again the compilation chain of office_connect in Figure 1.4 which shows us how office_connect.rdb is constructed :
- a regmerge starting from types.rdb
- a regcomp to register somes classics uno.so files.
And now to learn how office_connect.rdb is used we have to search in makefile how the binary is launched : the line
office_connect.run : $(OUT_COMP_BIN)/$(OUTBIN) $(OUT_COMP_BIN)/office_connectrc cd $(OUT_COMP_BIN) && $(OUTBIN)
is expanded as
cd ../../../../LINUXexample.out/bin && office_connect
but shows also a dependance to office_connectrc. We want to go further and see how things work.
- the office_connectrc is automaticly loaded. One way to check that is to rename this file (as office_connectrc2 for instance) which gives as result
make: *** [office_connect.run] Erreur 134
- renaming only office_connect.rdb (as office_connect2.rdb for instance) gives the same error :
make: *** [office_connect.run] Erreur 134
- renaming office_connect.rdb as office_connect2.rdb and changing office_connectrc content, as seen below, works well.
UNO_TYPES=$SYSBINDIR/office_connect2.rdb UNO_SERVICES=$SYSBINDIR/office_connect2.rdb
Conclusion : we have found how it works : when you launch a binary it automaticaly load office_connectrc which provide the information where office_connect.rdb file is.
C++ UNO bootstrapping via Bootstrap_InitialComponentContext()
The documentLoader example (see <OpenOffice.org1.1_SDK>/examples/cpp/DocumentLoader) uses an other way to bootstrap. We want to describe it now. As usualy we first begin with program framework in the Figure below.
In this second case we clearly see that DocumentLoader.rdb file is involved and loaded (created in makefile with regmerge
utility). The only difference is, we find again a rdb file but not a rc file. The consequence is the presence of a regcomp command specific to regitery the binary/executable file.
This example will not work if OpenOffice.org is not running. In this example, the interaction between OpenOffice.org and some.bin is through the network. In other words, the programs can run on different computers. You must run OpenOffice.org in such a way it waits for an UNO connection (here only from localhost) :
<Ooo>/program/soffice "-accept=socket,host=localhost,port=8100;urp;StarOffice.ServiceManager"
The new ::cppu::bootstrap() function
As explained in Developer's Guide a new function is available now for bootstrapping. See the corresponding code in Developer's Guide. I have found also in oooForum the snippet below which illustrate how it works.
Reference <XComponentContext> x_component_context(::cppu::bootstrap());
_x_multi_component_factory_client = Reference <XMultiComponentFactory> (x_component_context->getServiceManager());
_x_interface = Reference <XInterface>(_x_multi_component_factory_client->createInstanceWithContext(OUString::createFromAscii("com.sun.star.bridge.UnoUrlResolver" ), x_component_context));
Reference <XUnoUrlResolver> resolver(_x_interface, UNO_QUERY);
_x_interface = Reference <XInterface> (resolver->resolve(connection_string), UNO_QUERY);
Reference <XPropertySet> x_prop_set(_x_interface, UNO_QUERY);
x_prop_set->getPropertyValue(OUString::createFromAscii("DefaultContext")) >>= x_component_context;
Reference <XMultiComponentFactory> x_multi_component_factory_server(x_component_context->getServiceManager());
_x_component_loader = Reference <XComponentLoader>(x_multi_component_factory_server->createInstanceWithContext(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.Desktop")), x_component_context), UNO_QUERY);
If you want to use this new function, have a look in, <OOo_SDK>examples/DevelopersGuide/ProfUno/SimpleBootstrap_cpp, and as usual modify the file SimpleBootstrap_cpp.cxx without modifying makefile. Template:Documentation/Note
Playing with regview
This page is a short article on something that should have a lot more information written on it. If you know anything else about it, you are STRONGLY encouraged to add the information in. If you are unsure of proper formatting or style, add it to the talk page or this page itself as you think best and others will help. |
How can we registry in C++ ?
It is easy to find an example with registry. As first example we can take the counter example. Why it's an interesting example is only because it's a two files example : counter.cxx and countermain.cxx. Counter.cxx is compiled in counter.uno.so (counter.uno.dll in windows plateforms) and a counter.uno.rdb file is created. As shown above this file is for registry.
But what is new is, registry will be performed by the second program : countermain.cxx. And then reading this file gives us the possibility to view how this is done in C++ (see the below)
//Listing 2 Countermain.cxx example
// C++
....
// register my counter component
Reference< XImplementationRegistration > xImplReg(
xMgr->createInstanceWithContext(OUString::createFromAscii(
"com.sun.star.registry.ImplementationRegistration"), xContext), UNO_QUERY);
OSL_ENSURE( xImplReg.is(),
"### cannot get service instance of \"com.sun.star.registry.ImplementationRegistration\"!" );
if (xImplReg.is())
{
xImplReg->registerImplementation(
OUString::createFromAscii("com.sun.star.loader.SharedLibrary"),
// loader for component
#ifdef UNX
#ifdef MACOSX
OUString::createFromAscii("counter.uno.dylib"), // component location
#else
OUString::createFromAscii("counter.uno.so"), // component location
#endif
#else
OUString::createFromAscii("counter.uno.dll"), // component location
#endif
Reference< XSimpleRegistry >() // registry omitted,
// defaulting to service manager registry used
);
An other look into countermain.cxx file is showing the bootstrap is achieved like in the DocumentLoader example and then provide the XComponentContext (xContext) and XMultiComponentFactory (xMgr) useful for registry. We cannot continue without wondering what is XImplementationRegistration interface ? We gives again the corresponding IDL file :
// IDL
module com { module sun { module star { module registry {
interface XImplementationRegistration: com::sun::star::uno::XInterface
{
void registerImplementation( [in] string aImplementationLoader,
[in] string aLocation,
[in] com::sun::star::registry::XSimpleRegistry xReg )
raises( com::sun::star::registry::CannotRegisterImplementationException );
boolean revokeImplementation( [in] string aLocation,
[in] com::sun::star::registry::XSimpleRegistry xReg );
sequence<string> getImplementations( [in] string aImplementationLoader,
[in] string aLocation );
sequence<string> checkInstantiation( [in] string implementationName );
};
}; }; }; };
We see new methods and among others one we want to test: getImplementations. We then add this code in the countermain.cxx
//Listing 3 The countermain.cxx example modified
// C++
Sequence <OUString> OUStrs =xImplReg->getImplementations(
OUString::createFromAscii("com.sun.star.loader.SharedLibrary"),
OUString::createFromAscii("counter.uno.so"));
for (int i=0;i<OUStrs.getLength();i++){
OString toPrintOut = OUStringToOString(OUStrs[i],RTL_TEXTENCODING_ASCII_US);
printf("-- %s\n",toPrintOut.pData->buffer);
}
which prints out :
< MyCounterImpl ctor called > 42,43,42 -- com.sun.star.comp.example.cpp.Counter < MyCounterImpl dtor called >
This means : if I use SharedLibrary service to load counter.uno.so, I will have the implementation name « com.sun.star.comp.example.cpp.Counter ». This information is stored in registery as Developpers Guide states. In our example :
[smoutou@p3 counter]$ regview ../../../LINUXexample.out/bin/counter.uno.rdb /IMPLEMENTATIONS
gives the result :
Listing 5 Content of the key /IMPLEMENTATION Registry "file:///home/smoutou/OpenOffice.org1.1_SDK/LINUXexample.out/bin/counter.uno.rdb": /IMPLEMENTATIONS / com.sun.star.comp.example.cpp.Counter / UNO / ACTIVATOR Value: Type = RG_VALUETYPE_STRING Size = 34 Data = "com.sun.star.loader.SharedLibrary" / SERVICES / foo.Counter / LOCATION Value: Type = RG_VALUETYPE_STRING Size = 15 Data = "counter.uno.so"
where we see the association between « com.sun.star.comp.example.cpp.Counter » name and a dynamic library (under Linux) « counter.uno.so »
It is possible to get the service name :
Listing 6 Retrieving the Service Name [smoutou@p3 counter]$ regview ../../../LINUXexample.out/bin/counter.uno.rdb /IMPLEMENTATIONS/com.sun.star.comp.example.cpp.Counter/UNO/SERVICES Registry "file:///home/smoutou/OpenOffice.org1.1_SDK/LINUXexample.out/bin/counter.uno.rdb": /IMPLEMENTATIONS/com.sun.star.comp.example.cpp.Counter/UNO/SERVICES / foo.Counter [smoutou@p3 counter]$
Here the result is « foo.Counter ». We want to retrieve the information of Listing 6 but with C++ programming. Again you can modify the countermain.cxx example (just after the last line given in Listing 2) as shown below :
//Listing 7 Retrieving the Service Name in C++
// C++
Sequence <OUString> OUStrs =xImplReg->getImplementations(
OUString::createFromAscii("com.sun.star.loader.SharedLibrary"),
OUString::createFromAscii("counter.uno.so"));
for (int i=0;i<OUStrs.getLength();i++){
OString toPrintOut = OUStringToOString(OUStrs[i],RTL_TEXTENCODING_ASCII_US);
printf("-- %s\n",toPrintOut.pData->buffer);
}
OUStrs = xImplReg->checkInstantiation(
OUString::createFromAscii("foo.Counter"));
printf("****\n");
for (int i=0;i<OUStrs.getLength();i++){
OString toPrintOut = OUStringToOString(OUStrs[i],RTL_TEXTENCODING_ASCII_US);
printf("** %s\n",toPrintOut.pData->buffer);
}
Reference< XSimpleRegistry > xSimpleReg(
xMgr->createInstanceWithContext(OUString::createFromAscii("com.sun.star.registry.SimpleRegistry"), xContext), UNO_QUERY);
if(xSimpleReg.is())printf("OK XSimpleRegistry\n");
xSimpleReg->open(OUString::createFromAscii(
"file:///home/smoutou/OpenOffice.org1.1_SDK/LINUXexample.out/bin/counter.uno.rdb"),
false,false);
// How to retrieve all the keys :
Reference< XRegistryKey > xRegKey= xSimpleReg->getRootKey();
if(xRegKey.is())printf("OK XRegistryKey\n");
OUStrs = xRegKey->getKeyNames();
printf("++++ \n");
for (int i=0;i<OUStrs.getLength();i++){
OString toPrintOut = OUStringToOString(OUStrs[i],RTL_TEXTENCODING_ASCII_US);
printf("+++ %s\n",toPrintOut.pData->buffer);
// prints out : /UCR /IMPLEMENTATIONS /SERVICES
}
// How to get a sub-key
xRegKey=xRegKey->openKey(
OUString::createFromAscii("/IMPLEMENTATIONS/com.sun.star.comp.example.cpp.Counter/UNO/SERVICES"));
OUStrs = xRegKey->getKeyNames();
printf("$$$$ \n");
for (int i=0;i<OUStrs.getLength();i++){
OString toPrintOut = OUStringToOString(OUStrs[i],RTL_TEXTENCODING_ASCII_US);
printf("$$$ %s\n",toPrintOut.pData->buffer);
// prints out :
// /IMPLEMENTATIONS/com.sun.star.comp.example.cpp.Counter/UNO/SERVICES/foo/Counter
}
xRegKey->closeKey();
xSimpleReg->close();
which prints out
-- com.sun.star.comp.example.cpp.Counter **** OK XSimpleRegistry OK XRegistryKey ++++ +++ /UCR +++ /IMPLEMENTATIONS +++ /SERVICES $$$$ $$$ /IMPLEMENTATIONS/com.sun.star.comp.example.cpp.Counter/UNO/SERVICES/foo.Counter
Registry tools in C++
Introduction
I want to retrieve and decode Binary information My choice is Xinterface for a test. I wonder if it's possible to retrieve all the information given by regview tool :
[smoutou@p3 counter]$ regview ../../../LINUXexample.out/bin/counter.uno.rdb /UCR/com/sun/star/uno/XInterface Registry "file:///home/smoutou/OpenOffice.org1.1_SDK/LINUXexample.out/bin/counter.uno.rdb": /UCR/com/sun/star/uno/XInterface Value: Type = RG_VALUETYPE_BINARY Size = 326 Data = minor version: 0 major version: 1 type: 'interface' uik: { 0x00000000-0x0000-0x0000-0x00000000-0x00000000 } name: 'com/sun/star/uno/XInterface' super name: '' Doku: "" IDL source file: "/home/gb/rpm/BUILD/oo_1.1rc4_src/udkapi/com/sun/star/uno/XInterface.idl" number of fields: 0 number of methods: 3 method #0: any queryInterface([in] type aType) Doku: "" method #1: [oneway] void acquire() Doku: "" method #2: [oneway] void release() Doku: "" number of references: 0 [smoutou@p3 counter]$
C++ Code to recover Information in a Binary Sequence
See the listing below to learn how we can recover binary information in registery.
//Listing 8 Retrieving Information in a Binary Sequence
// C++
....
Reference< XSimpleRegistry > xSimpleReg(
xMgr->createInstanceWithContext(OUString::createFromAscii(
"com.sun.star.registry.SimpleRegistry"), xContext), UNO_QUERY);
OSL_ENSURE(xSimpleReg.is(),"NOK XSimpleRegistry\n");
xSimpleReg->open(OUString::createFromAscii(
"file:///home/smoutou/OpenOffice.org1.1_SDK/LINUXexample.out/bin/counter.uno.rdb"),
false,false);
// How to retrieve all the keys :
Reference< XRegistryKey > xRegKey= xSimpleReg->getRootKey();
OSL_ENSURE(xRegKey.is(),"NOK XRegistryKey\n");
// How to get a sub-key
xRegKey=xRegKey->openKey(
OUString::createFromAscii("/UCR/com/sun/star/uno/XInterface"));
printf("//// \n");
OUString OUStr;
OString toPrintOut;
Sequence< sal_Int8 > seqByte;
// still a lot to do in this switch :
switch (xRegKey->getValueType()){
case RegistryValueType_LONG : printf("long\n");break;
case RegistryValueType_ASCII : printf("ascii\n");
OUStr = xRegKey->getAsciiValue();
toPrintOut = OUStringToOString(OUStr,RTL_TEXTENCODING_ASCII_US);
printf("/// %s\n",toPrintOut.pData->buffer);
break;
case RegistryValueType_STRING : printf("string\n");break;
case RegistryValueType_BINARY : printf("RG_VALUETYPE_BINARY\n");
seqByte = xRegKey->getBinaryValue();
printDEBUGMODE(seqByte);
break;
case RegistryValueType_LONGLIST : printf("long list\n");break;
case RegistryValueType_ASCIILIST : printf("ascii list\n");break;
case RegistryValueType_STRINGLIST : printf("string list\n");break;
case RegistryValueType_NOT_DEFINED : printf("not defined\n");break;
}
....
// with the procedure
void printDEBUGMODE(Sequence < sal_Int8 > seqBytes){
sal_Int8 Hexa[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
int lin,col;
sal_Int8 line[80];
sal_Int32 len;
len=seqBytes.getLength();
// print the lenght in decimal and with 4 hexadecimal digits (C notation 0x....)
printf("length : %d Ox%X%X%X%X\n",len,len>>12&0x000F,len>>8&0x000F,len>>4&0x000F,len&0x000F);
len = (len/16)*16; // retire le modulo 16
for(lin=0;lin<len;lin=lin+16){
for(col=0;col<16;col++){
line[3*col]=Hexa[seqBytes[lin+col]>>4];
line[3*col+1]=Hexa[seqBytes[lin+col]&0x0F];
line[3*col+2]=' ';
if ((seqBytes[lin+col]<128)&&(seqBytes[lin+col]>20)) line[50+col]=seqBytes[lin+col];
else line[50+col]='.';
} /* end of for */
line[66]=0; /* end of string...*/
line[48]=' ';line[49]=' ';
for (int i=0;i<66;i++)
printf("%c",static_cast< char >(line[i]));
printf("\n");
} /* end of for */
// the last line is more complicated because not complete
for (lin=len;lin<seqBytes.getLength();lin++){
col=lin-len;
line[3*col]=Hexa[seqBytes[lin]>>4];
line[3*col+1]=Hexa[seqBytes[lin]&0x0F];
line[3*col+2]=' ';
if ((seqBytes[lin]<128)&&(seqBytes[lin]>20)) line[50+col]=seqBytes[lin];
else line[50+col]='.';
}
for (++col;col<16;col++){
line[3*col]=' ';line[3*col+1]=' ';line[3*col+2]=' ';line[50+col]=' ';
}
line[66]=0; /* end of string...*/
line[48]=' ';line[49]=' ';
for (int i=0;i<66;i++)
printf("%c",static_cast< char >(line[i]));
printf("\n");
}
A carrefully look at the listing above shows we transform the sequence of bytes in a format which recalls the old DOS debug tool.
//// RG_VALUETYPE_BINARY length : 326 Ox0146 12 34 56 78 00 00 01 46 00 00 00 01 00 06 00 00 .4Vx...F........ 00 01 00 01 00 02 00 00 00 03 00 00 00 0B 00 00 ................ 00 22 00 0C 63 6F 6D 2F 73 75 6E 2F 73 74 61 72 ."..com/sun/star 2F 75 6E 6F 2F 58 49 6E 74 65 72 66 61 63 65 00 /uno/XInterface. 00 00 00 16 00 0D 00 00 00 00 00 00 00 00 00 00 ............... 00 00 00 00 00 00 00 00 00 4E 00 0C 2F 68 6F 6D .........N../hom 65 2F 67 62 2F 72 70 6D 2F 42 55 49 4C 44 2F 6F e/gb/rpm/BUILD/o 6F 5F 31 2E 31 72 63 34 5F 73 72 63 2F 75 64 6B o_1.1rc4_src/udk 61 70 69 2F 63 6F 6D 2F 73 75 6E 2F 73 74 61 72 api/com/sun/star 2F 75 6E 6F 2F 58 49 6E 74 65 72 66 61 63 65 2E /uno/XInterface. 69 64 6C 00 00 00 00 15 00 0C 71 75 65 72 79 49 idl......queryI 6E 74 65 72 66 61 63 65 00 00 00 00 0A 00 0C 61 nterface.......a 6E 79 00 00 00 00 0B 00 0C 74 79 70 65 00 00 00 ny.......type... 00 0C 00 0C 61 54 79 70 65 00 00 00 00 0E 00 0C ....aType....... 61 63 71 75 69 72 65 00 00 00 00 0B 00 0C 76 6F acquire.......vo 69 64 00 00 00 00 0E 00 0C 72 65 6C 65 61 73 65 id.......release 00 00 00 00 0B 00 0C 76 6F 69 64 00 00 00 00 03 .......void..... 00 05 00 03 00 14 00 03 00 04 00 05 00 00 00 01 ................ 00 06 00 01 00 07 00 00 00 0E 00 01 00 08 00 09 ................ 00 00 00 00 00 00 00 0E 00 01 00 0A 00 0B 00 00 ................ 00 00 00 00 00 00 ...... [smoutou@p3 counter]$
We see it's possible to understand how these bytes are constituted to hold the information we look for, but it will take a while to investigate further. I prefere have a look into source code and find : « OOB680_m5/registry/source/reflcnst.hxx »
Examining the OOo source code
For instance before every name we see a « 00 0C » It's effecftively defined in reflcnst.hxx file as :
//Listing 9 reflcnst.hxx File (extract)
// C++
...
enum CPInfoTag
{
CP_TAG_INVALID = RT_TYPE_NONE,
CP_TAG_CONST_BOOL = RT_TYPE_BOOL,
CP_TAG_CONST_BYTE = RT_TYPE_BYTE,
CP_TAG_CONST_INT16 = RT_TYPE_INT16,
CP_TAG_CONST_UINT16 = RT_TYPE_UINT16,
CP_TAG_CONST_INT32 = RT_TYPE_INT32,
CP_TAG_CONST_UINT32 = RT_TYPE_UINT32,
CP_TAG_CONST_INT64 = RT_TYPE_INT64,
CP_TAG_CONST_UINT64 = RT_TYPE_UINT64,
CP_TAG_CONST_FLOAT = RT_TYPE_FLOAT,
CP_TAG_CONST_DOUBLE = RT_TYPE_DOUBLE,
CP_TAG_CONST_STRING = RT_TYPE_STRING,
CP_TAG_UTF8_NAME,
CP_TAG_UIK
};
....
which learns us this is an info tag of type CP_TAG_UTF8_NAME. If we want to decode header we use this extract :
//Listing 10 reflcnst.hxx File (extract)
// C++
extern const sal_uInt32 magic;
extern const sal_uInt16 minorVersion;
extern const sal_uInt16 majorVersion;
#define OFFSET_MAGIC 0
#define OFFSET_SIZE (OFFSET_MAGIC + sizeof(magic))
#define OFFSET_MINOR_VERSION (OFFSET_SIZE + sizeof(sal_uInt32))
#define OFFSET_MAJOR_VERSION (OFFSET_MINOR_VERSION + sizeof(minorVersion))
#define OFFSET_N_ENTRIES (OFFSET_MAJOR_VERSION + sizeof(sal_uInt16))
#define OFFSET_TYPE_SOURCE (OFFSET_N_ENTRIES + sizeof(sal_uInt16))
#define OFFSET_TYPE_CLASS (OFFSET_TYPE_SOURCE + sizeof(sal_uInt16))
#define OFFSET_THIS_TYPE (OFFSET_TYPE_CLASS + sizeof(sal_uInt16))
#define OFFSET_UIK (OFFSET_THIS_TYPE + sizeof(sal_uInt16))
#define OFFSET_DOKU (OFFSET_UIK + sizeof(sal_uInt16))
#define OFFSET_FILENAME (OFFSET_DOKU + sizeof(sal_uInt16))
#define OFFSET_N_SUPERTYPES (OFFSET_FILENAME + sizeof(sal_uInt16))
#define OFFSET_SUPERTYPES (OFFSET_N_SUPERTYPES + sizeof(sal_uInt16))
#define OFFSET_CP_SIZE (OFFSET_SUPERTYPES + sizeof(sal_uInt16))
#define OFFSET_CP (OFFSET_CP_SIZE + sizeof(sal_uInt16))
which allows to explain our header :
Field name | Value |
MAGIC | 12 34 56 78 |
SIZE | 00 00 01 46 |
MINOR_VERSION | 00 00 |
MAJOR_VERSION | 00 01 |
N_ENTRIES | 00 06 |
TYPE_SOURCE | 00 00 |
TYPE_CLASS | 00 01 |
THIS_TYPE | 00 01 |
UIK | 00 02 |
DOKU | 00 00 |
FILENAME | 00 03 |
N_SUPERTYPES | 00 00 |
SUPERTYPES | 00 0B |
CP_SIZE | 00 00 |
CP | 00 22 |
And so on... Is it important to go further ? Not at all, it was only to « play » a little. In fact we can directly recover this information with CoreReflection service (I am not absolutely sure of that). We give in the next section an overview in Ooobasic of this service.
How do we see the registries in OOoBasic ?
OooBasic allow many things and in particular to explore registry. Danny Brewer's code shows us how to work around ? We have a look at his code :
REM ***** BASIC *****
Sub Main
oReflection = createUnoService( "com.sun.star.reflection.CoreReflection" )
oInfo = oReflection.forName( "nom.dannybrewer.test.XDannysCalcFunctions1" )
aMethods = oInfo.getMethods()
For i = 0 To UBound( aMethods )
oMethod = aMethods( i )
Print oMethod.getName()
Next
End Sub
We then create an idl file :
// IDL
#include <com/sun/star/uno/XInterface.idl>
#include <com/sun/star/lang/XInitialization.idl>
module my_module
{
interface XSomething : com::sun::star::uno::XInterface
{
long getCount();
void setCount( [in] long nCount );
long increment();
long decrement();
};
service MyService
{
interface XSomething;
};
};
With this IDL file, we create a rdb file and register this rdb file. Now OpenOffice.org works as if we have an add-on even if we have not written its code. We cannot launch this add-on (without its code !) but we can see it. We then modify the above Danny's program :
REM Listing 11
REM ***** BASIC *****
'From Danny : http://www.oooforum.org/forum/viewtopic.php?t=8737
Sub Main
oReflection = createUnoService( "com.sun.star.reflection.CoreReflection" )
oInfo = oReflection.forName( "my_module.XSomething" )
aMethods = oInfo.getMethods()
'XRay.XRay oInfo
print UBound( aMethods )+1 & " methods : ****"
For i = 0 To UBound( aMethods )
oMethod = aMethods( i )
Print oMethod.getName()
Next
aTypes = oInfo.getTypes()
print UBound( aTypes )+1 & " types : ****"
For i = 0 To UBound( aTypes )
oType = aTypes( i )
Print "Types : " + oType.getName()
Next
aInterfaces = oInfo.getInterfaces()
print UBound( aInterfaces )+1 & " interfaces : ****"
For i = 0 To UBound( aInterfaces )
oInterface = aInterfaces( i )
Print "Interfaces : " + oInterface.getName()
Next
End Sub
launching this modified program produces :
7 methods : **** queryInterface acquire release getCount setCount increment decrement 3 types : **** Types : com.sun.star.reflection.XIdlClass Types : com.sun.star.lang.XTypeProvider Types : com.sun.star.uno.XWeak 0 Interfaces : ****
We can see among others the four methods given in the IDL file.
It is possible to retrieve more information on the methods for instance. If you want all the information given in the IDL file you can use OOoBasic code like that :
For i = LBound( oMethods ) To UBound( oMethods )
oMethod = oMethods( i )
' Check the method's interface to see if
' these aren't the droids you're looking for.
sString = oMethod.getReturnType().getName() & " " & oMethod.getName() & "("
parametersInfo = oMethod.getParameterInfos()
if UBound(parametersInfo) > 0 then
for ii=0 to UBound(parametersInfo)
if parametersInfo(ii).aMode = com.sun.star.reflection.ParamMode.IN Then
stringType = "IN "
elseif parametersInfo(ii).aMode = com.sun.star.reflection.ParamMode.OUT Then
stringType = "OUT "
else
stringType = "INOUT "
end if
sString = sString & stringType & ParametersInfo(ii).aType.getName() & " " & parametersInfo(ii).aName
if ii < UBound(parametersInfo) Then sString = sString & ", "
Next
end if
sString = sString & ")"
' something to do with sString here
Next
To go further in C++ see XdlRefection Interface
See Also
- C++ Language Binding in Developer's Guide
- C++ and UNO tutorial
- UNO tutorial
- UNO IDL
- Uno/Article/Types&Reflection
- Writing a Program to Control OpenOffice.org, by Franco Pingiori — Part 1 and Part 2, Linux Journal