Scheduled Maintenance on Friday, March 19th, 2010

We will be performing maintenance on some database and web application servers for migration to our new data center location with faster connectivity after 7pm Pacific time on Friday and throughout Saturday March 20th, 2010.

We will keep our systems up as much as possible during this maintenance time. We will keep the outage as short as possible, and we apologize for the inconvenience.

C++Builder, 1, Borland C++Builder White Paper: Application Development with C++Builder and Delphi

By: Borland Staff

Abstract: C++Builder and Delphi have a unique relationship that allows programmers to easily share code between the two environments.

Application Development with C++Builder and Delphi
Written by Charlie Calvert

Note: The views and information expressed in this document represent those of its author(s) who is solely responsible for its content. Borland does not make or give any representation or warranty with respect such content.

Using COM to link Delphi Code into C++Builder

The last few sections have shown that it is trivial to link Delphi units and components directly into C++Builder. Another great way to share code between the two tools is via COM and OLE. You might want to use COM and OLE if you want to share your code not only between Delphi and C++Builder, but also between Delphi and Visual Basic, or other OLE aware tools such as Word, Excel, Visual C++, etc.

There are three key ways to link Delphi COM objects into C++Builder:

  1. Use OLE Automation or Dual Interfaces
  2. Use Delphi 97 to create ActiveX controls
  3. Write raw COM or OLE code to link in Delphi objects

The first two methods shown above are supported automatically by C++Builder. The third method requires you to dig into the details of OLE.

One simple way to link in Delphi objects via COM is to use OLE Automation. C++Builder fully supports OLE automation, and it provides a simple technique for accessing the methods of an Automation Object.

Automation Objects can be accessed in one of two fashions: You can access them via a COM IDispatch interface, or via dual interfaces. C++Builder has built in support for IDispatch, via the CreateOleObject VCL function and the Variant template class. This is the simplest method for accessing Automation Objects, and it is the one you should use unless speed is an extremely important issue for you.

For instance, suppose you have a Delphi IDispatch interface that exports the following methods:

ITest = interface(IDispatch)

['{1746E520-E2D4-11CF-BD2F-0020AF0E5B81}']

function Get_Value: Integer; safecall;

procedure Set_Value(Value: Integer); safecall;

function Get_Name: WideString; safecall;

procedure Set_Name(const Value: WideString); safecall;

procedure Prompt(const text: WideString); safecall;

procedure VarTest(var v1, v2, v3: Variant); safecall;

property Value: Integer read Get_Value write Set_Value;

property Name: WideString read Get_Name write Set_Name;

end;

Assume further that the DLL that contains this interface is referenced in the registry in association with CLSID that has a ProgID called "TestLib.Test".

Here is how you can retrieve the interface and call the Prompt method from inside C++Builder:

void __fastcall TForm1::Button1Click(TObject *Sender)

{

Variant V = CreateOleObject("TestLib.Test");

V.OleProcedure("Prompt", "Sammy");

}

This code first retrieves an instance of IDispatch inside a Variant. It then uses a method of the Variant class to call the Prompt method from the Delphi interface.

Here is how you would call the VarTest method:

void __fastcall TForm1::Button1Click(TObject *Sender)

{

Variant V1, V2, V3;

Variant V = CreateOleObject("TestLib.Test");

V1 = 5;

V2 = "Sam";

V3 = V;

V.OleProcedure("VarTest", V1, V2, V3);

}

Delphi users looking at this code should note that Variant is a C++ class declared in SYSDEFS.H. It is not a simple type as it is when used inside Delphi.

If you want to use dual interfaces in C++Builder, then you can get very fast access to Automation Objects that reside in a DLL. You can also access local servers, that is Automation Objects in an executable, more quickly via dual interfaces, but the built in overhead with local servers is so great that the improvement in speed given by dual interfaces is less noticeable.

There are no built in tools for accessing dual interfaces in C++Builder. Obviously it will be possible to use them (after all, you can do anything in C++), but you will have to lay the ground work yourself. (The basic technique is very similar to the one outlined below in the section titled "Mapping Virtual Method Tables".)

Using Delphi ActiveX Controls in C++Builder
C++Builder has built in support for ActiveX controls. You can load them into the environment and use them just as if they were native components. To get started:

  1. Choose "Component | Install" from the menu.
  2. In the Install Components dialog select the OCX button to bring up the Import OLE Control dialog.
  3. All of the registered components on your system will appear in the Import OLE Control dialog. If you want to register a new component, select the Register button in the Import OLE Control dialog.
  4. After selecting the control you want to import, press the OK button.
  5. Your new control will now be listed at the bottom of the Installed Components dialog, and a interface source file will be placed in the LIB directory.
  6. Press OK to recompile CMPLIB32 and add your control the Component Palette. By default, the new ActiveX control will appear in the OCX page of the Component Palette.

If you don't want to use the automated method shown above, you can also use the C++ language to build your own OLE containers. This is, of course, a difficult process, and the technique outlined above is usually infinitely preferable. Don't forget that you can use the tools in Borland C++ to create OLE containers, and then link that code into your C++Builder project either directly, or through libraries, or through DLLs.

Using the Windows API to Link in Delphi COM and OLE Objects

Delphi 97 is an extremely powerful tool for creating COM and OLE objects. You can use it to build Automation objects, dual interfaces, ActiveX controls, and all manner of simple (or complex) COM objects.

C++Builder does not yet have the built in support for COM that you find in Delphi 97. However, it does have access to all the OLE code you find in Borland C++ 5.0, the Windows SDK, the MSDN, or in commercially available books and libraries.

The following is a definition for a simple Delphi COM object:

const

CLSID_ITable: TGUID = ( D1:$58BDE140;D2:$88B9;D3:$11CF;D4:($BA,$F3,$00,$80,$C7,$51,$52,$8B));

type

ITable = class(IUnknown)

private

FRefCount: LongInt;

FObjectDestroyed: TObjectDestroyed;

Table: TTable;

public

constructor Create(ObjectDestroyed: TObjectDestroyed);

destructor Destroy; override;

function QueryInterface(const iid: TIID; var obj):

HResult; override; stdcall;

function AddRef: Longint; override; stdcall;

function Release: Longint; override; stdcall;

{ interface }

procedure Open; virtual; stdcall;

procedure Close; virtual; stdcall;

procedure SetDatabaseName(const Name: PChar); virtual; stdcall;

procedure SetTableName(const Name: PChar); virtual; stdcall;

procedure GetStrField(FieldName: PChar; Value: PChar);

virtual; stdcall;

procedure GetIntField(FieldName: PChar; var Value: Integer);

virtual; stdcall;

procedure GetClassName(Value: PChar); virtual; stdcall;

procedure Next; virtual; stdcall;

procedure Prior; virtual; stdcall;

procedure First; virtual; stdcall;

procedure Last; virtual; stdcall;

function EOF: Bool; virtual; stdcall;

end;

Here is the same interface as it would be declared inside a C++ project:

class IDBClass : public IUnknown

{

public:

STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;

STDMETHOD_(ULONG,AddRef) (THIS) PURE;

STDMETHOD_(ULONG,Release) (THIS) PURE;

// Interface

STDMETHOD_(VOID, Open) (THIS) PURE;

STDMETHOD_(VOID, Close) (THIS) PURE;

STDMETHOD_(VOID, SetDatabaseName) (THIS_ LPSTR Name) PURE;

STDMETHOD_(VOID, SetTableName) (THIS_ LPSTR Name) PURE;

STDMETHOD_(VOID, GetStrField) (THIS_ LPSTR FieldName, LPSTR Value)

PURE;

STDMETHOD_(VOID, GetIntField) (THIS_ LPSTR FieldName, int * Value)

PURE;

STDMETHOD_(VOID, GetClassName) (THIS_ LPSTR Name) PURE;

STDMETHOD_(VOID, Next) (THIS) PURE;

STDMETHOD_(VOID, Prior) (THIS) PURE;

STDMETHOD_(VOID, First) (THIS) PURE;

STDMETHOD_(VOID, Last) (THIS) PURE;

STDMETHOD_(BOOL, EOF) (THIS) PURE;

};

Given the above definition, you could write the following C++ code to use the Delphi object in C++:

#pragma argsused

void Window1_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)

{

char CR[2] ="r";

HRESULT hr;

PIDBClass P;

LPSTR S, Temp;

CoInitialize(NULL);

hr = CoCreateInstance(CLSID_IDBClass, NULL, CLSCTX_INPROC_SERVER,

IID_IUnknown, (VOID**) &P);

if (SUCCEEDED(hr))

{

S = (char *)malloc(1000);

Temp = (LPSTR)malloc(100);

P->SetDatabaseName("DBDEMOS");

P->SetTableName("COUNTRY");

P->Open();

strcpy(S, "Countries and their capitals: ");

strcat(S, CR);

strcat(S, CR);

while (!P->EOF())

{

P->GetStrField("NAME", Temp);

strcat(S, Temp);

strcat(S, ": ");

P->GetStrField("CAPITAL", Temp);

strcat(S, Temp);

strcat(S, CR);

P->Next();

}

MessageBox(hwnd, S, "COUNTRY TABLE", MB_OK);

P->Release();

free(S);

free(Temp);

}

}

Clearly this technique takes a bit of work, but it is not overly difficult. If you understand something about COM objects, it provides a viable method for sharing code between Delphi and C++.

Using DLLs to link Delphi Code into C++Builder
C ++Builder can easily access a Delphi DLL containing functions and procedures. If you want to access a Delphi object implemented inside a DLL from C++Builder then you should read the section below called Mapping Virtual Method tables.

If you have a Delphi DLL that you can call from inside a Delphi project then you do not need to change it at all to call it from inside C++ Builder. To get started, take the Delphi unit that lists the methods in your DLL and add it to your C++ project. C++Builder will automatically compile the unit and generate a C++ header file. In particular, you should link the unit in using the techniques described above in the section called "Ground Rules for Linking Delphi Code into C++Builder Projects."

You will not be able to access data declared in your DLL without first calling a function or procedure. This same limitation applies to all DLLs, regardless of the language used to implement them.

DLLs are useful when you have a large project that you want to divide into several modules. By placing code in DLLs, you can divide your projects into several binary files that can be loaded in and out of memory at will.

Mapping Virtual Method Tables

Delphi objects stored in a DLL are normally out of reach of Ebony projects. However, there is a way to get at them by matching the VMT of the Delphi object to the VMT of a virtual abstract, or "PURE", C++ object.

Consider the following Delphi declaration:

TMyObject = class

function AddOne(i: Integer): Integer; virtual; stdcall;

function AddTwo(i: Integer): Integer; virtual; stdcall;

end;

A virtual abstract version of the same object could be declared in C++ like this:

class __declspec(pascalimplementation) TMyObject: public TObject

{

public:

virtual _stdcall int AddOne(int i) = 0;

virtual _stdcall int AddTwo(int i) = 0;

};

To match up the VMT of the C++ object to the VMT of the Pascal object, all you need to do is retrieve a pointer to the object from the DLL. One way to do this is to export a function from the DLL which is designed for this explicit purpose:

function CreateObject(ID: Integer; var Obj): Boolean;

var

M: TMyObject;

begin

if ID = ID_MyObject then begin

M := TMyObject.Create;

Result := True

end else begin

M := nil;

Result := False

end;

Pointer(Obj) := M;

end;

exports

CreateObject name 'CreateObject';

You can call this method from the Ebony project with the following code:

typedef Boolean (_stdcall *LPCreateObject)(int ID, void *obj);

void __fastcall TForm1::Button1Click(TObject *Sender)

{

TMyObject *MyObject;

LPCreateObject CreateObject;

HANDLE hlib = LoadLibrary("OBJECTLIB.DLL");

CreateObject = (LPCreateObject)GetProcAddress(hlib, "CreateObject");

if (CreateObject(1, &MyObject))

{

int i = MyObject->AddOne(1);

ShowMessage((AnsiString)i);

}

FreeLibrary(hlib);

}

The code shown here first declares a pointer to a function with the same signature as the CreateObject routine in the DLL. It then calls the standard Windows API functions LoadLibrary and GetProcAddress in order to retrieve a pointer to CreateObject. If the call succeeds, you can call CreateObject, return the address of the object you want to call, and then call one of it's methods.

Notice that all the methods in the Pascal object are declared virtual. This is necessary since you want to match up the VMTs, or Virtual Method Tables of the two declarations. If the methods weren't declared virtual, then there would be no virtual method table, and the ploy would not work. You can declare non-virtual methods in your object if you wish, but you will not be able to call these methods from C++.

Note also that the order of the methods you declare is very important. The names of the methods you want to call are not important to the C++ implementation; all that matters is the order in which the methods are declared.

You can use this same technique, or one like it, to export any object from a Delphi DLL into a C++Builder or Borland C++ project. You do not have to use GetProcAddress and LoadLibrary, but could instead import a Delphi unit that exports CreateObject directly into your C++Builder project. However, I use GetProcAddress in this example since it forces you to imagine the exact steps necessary to make this process work. In other words, it forces you to think about the addresses that are being shared between the DLL and the Ebony project.

Summary

In this paper you have learned that C++Builder has an unprecedented ability to access the code from it's sister product, Delphi. As a rule, you can simply link Delphi code directly into you C++Builder projects.

The types of code that can be shared between Delphi and C++Builder include forms, components, ActiveX controls, and simple methods and functions. You can simply link this code directly into your projects without any extra work.

This paper also examined using OLE or DLLs to share code between C++Builder and Delphi. OLE can be useful if you want to share code with not only C++ Builder, but also with other, non-Borland, environments, such as Word, Visual Basic, or Excel. DLLs are a great way to share code if memory management issues are significant. In particular, you can load and unload DLLs from memory during the life of your project.


Server Response from: SC4