|Delphi Clinic||C++Builder Gate||Training & Consultancy||Delphi Notes Weblog||Dr.Bob's Webshop|
CORBA stands for Common Object Request Broker Architecture, but I suspect you already know that. In my article about Borland C++Builder 3 and CORBA we tried to communicate with CodeSite. That gave some troubles with runtime libraries etcetera. In the mean time Delphi 4 came by with CORBA support, Wizards, Type Library Editor and the works. However, if you expose an object to CORBA, what basically happens is that the COM interface you wrote with the Type Library Editor is converted into CORBA IDL. This leads to an IDL that looks very much like a COM interface. It does work, but you're more or less locked into Delphi if you use that for your CORBA based clients and servers. Delphi does not support 'native' IDL so to say.
Enter C++Builder 4
Superficially, it seems that C++Builder 4 suffers from the same shortcoming. However, we need to look a bit further into it than just the wizards. If you look at the Project Properties, you will notice a property page that is titled 'CORBA'! Open it and you will see this:
What this basically is are the different options you can set when you use VisiBroker idl2cpp from the command line.
A quick rundown of those options is:
usage: idl2cpp [-options] files...
where options are:
-version Print version
-root_dir<dir_string> Specify dir to write all generated code to
-hdr_dir<dir_string> Specify dir to write generated headers to
-src_dir<dir_string> Specify dir to write generated source to
-client_ext<ext_string> Specify client file extension
-'none' for none,
-server_ext<ext_string> Specify server file extension -'none'
-hdr_suffix<suf_string> Specify header file suffix - default:'hh'
-src_suffix<suf_string> Specify source file suffix - default:'cc'
-no_exceptions Suppress exception generation
-no_tie Suppress generation of tie code
-ptie Generate ptie code
-obj_wrapper Generate typed object wrapper code
-no_stdstream Suppress generation of class stream operators
-pretty_print Generate _pretty_print methods
-type_code_info Generate type code information
-virtual_impl_inh Generate virtual interface impl. inheritance
-export<tag_string> Generate export tags (WinNT/95)
-export_skel<tag_string> Generate export tags in skeletons (WinNT/95)
-incl_files_code Generate code for included files
-corba_inc<include_file> Use include_file instead of 'corba.h'
-impl_base_object<o_str> Use o_str instead of 'CORBA::Object'
Replace 'keyword' with 'replacement'
-preprocess_only Send preprocessed source to stdout;
do not compile
-no_preprocess Do not preprocess the source
-C Retain comments in output
-D Define a macro
-H Print the pathnames of included files
-I<directory> Specify an[other] directory in the include path
-P Suppress #line pragmas
-U Undefine a macro
Some of the options are not covered by the dialog, and are left to the standard setting.
This is what happens if you compile and IDL file in the project manager:
Now we right click oneway.idl, and see what happens if we choose 'compile':
Two files are added to the project. The one ending with the 'c' is the client-side stub, and the one ending with the 's' is the server side stub. These two files are the stepping stone to implementing the server and the client programs. What basically happens is that the client program links in the client stub and calls its routines. The stub takes care of 'marshalling' the data in the proper format and sending it to the server stub. The server stub then 'unmarshals' the data passed to it and calls the stub function. The thing you yourself must do is to fill-in-the-blanks on the server side.
The IDL file looks like this:
void SendToServer(in string theMessage);
On the client side we see a class CodeSite, that has the following member:
virtual void SendToServer(char* _theMessage);
The generated implementation looks like:
void CodeSite::SendToServer(const char* _theMessage)
while( 1 )
_obuf = ___root->_create_request("SendToServer", 1 , 232192);
VISostream& _ostrm = *(VISostream *)(CORBA::MarshalOutBuffer*)_obuf;
_ostrm << _themessage;
_ibuf = ___root->_invoke(_obuf);
catch (const CORBA::TRANSIENT& )
This means that you can create an object on the client side and call the SendToServer message immediately.
The implementation in the client side stub takes care of the marshalling code.
In this case we use streaming to communicate between client and server.
Now let's look at the other side. The header:
// The following operations need to be implemented
virtual void SendToServer(const char* theMessage) = 0;
// Skeleton Operations implemented automatically
static void _SendToServer(void *_obj,
const char *_oper,
and the implementation:
void _sk_CodeSite::_SendToServer(void *_obj,
const char *_oper,
VISistream& _vistrm = _istrm;
CodeSite *_impl = (CodeSite *)_obj;
_vistrm >> theMessage;
VISostream& _ostrm = *(VISostream *)
Do you see the trick here?
On the server side the stub is a pure virtual function, and you must implement it from a class that is derived from the '_sk_CodeSite' class. This implementation model is chosen because I clicked the 'virtual implementation inheritance' option. There are other possibilities to choose from. In the associated help file you can see what the difference is.
CORBA client server in 30 minutes
Now you want to see it work, right? Well, copy the IDL from this page and paste it in oneway.idl. Create a new project using the CORBA client wizard in the Multitier tab page. Click on the wizard, add oneway.idl file and click OK. Then save the project and its files. Next thing is to create the server. Now use the CORBA server wizard instead. Last but not least choose the CORBA Object Implementation wizard. Specify oneway.idl as the IDL file, choose the interface you want to implement, then click OK. On the following page select the things you want to have and click OK again. Now you have a skeleton server application. The last thing we do is connecting the dots. Just open the client project, add the CodeSitePtr member variable to the form class:
Then add the following line to the constructor of the form:
CodeSitePtr = CodeSite::_bind();
Then add a memo field, an edit field and a button to the form.
Add an OnClick handler to the button that retrieves the text from the edit and adds it to the memo, then do the same by sending it to the CodeSitePtr:
Now your client is done.
Next open the server project.
Drop a memo on the form.
Open the object implementation file and add the following to the CodeSiteServer::SendToServer method:
Last but not least, make both projects, fire up the server, fire up some clients and enjoy!
As was expected the support for CORBA in C++Builder is different from the support for CORBA in Delphi. The main reason for this is the good support for C++ in the underlying Visibroker components. Since it should be possible to connect a C++Builder class to Delphi in some way or another, it should be possible to alleviate some of the Delphi 4 shortcomings (that is, a nonexistent VisiBroker idl2pas program) by using C++Builder 4.