📄 ch06.htm
字号:
</FONT><I>Adding a Pascal component to a C++Builder project.</I>
<H4><A NAME="Heading13"></A><FONT COLOR="#000077">Adding a Pascal Component to a
C++Builder Project</FONT></H4>
<P>After you have integrated a Delphi
component into C++Builder, you can then treat
it exactly as if it were a native C++Builder component. That is, you can drop it
onto a form and manipulate its methods with the object inspector. If you need to
reference any of the components methods,
properties, or events in your C++ code,
you should use C++ syntax. The details of this syntax can be surmised by viewing
the header file created when you added the component to the Component Palette.</P>
<P>A sample Delphi component called
<TT>TSmiley</TT> is available in the <TT>Utils</TT>
subdirectory. This component was written by the great Nick Hodges. It is not only
one of the first components ever written in Delphi, but it is also the world's most
famous and most highly acclaimed
Truly Useless Component. <TT>TSmiley</TT> is a mission-critical
component designed for use in distributed n-tier applications created by Fortune
500 companies. All its functionality can be accessed through the <TT>Mood</TT> property,
which can be set
to various values such as <TT>smHappy</TT>, <TT>smSad</TT>, <TT>smIndifferent</TT>,
<TT>smShades</TT>, and <TT>smTongue</TT>.
<H4><A NAME="Heading14"></A><FONT COLOR="#000077">Some Theory on Linking Object Pascal
and C++Builder Code</FONT></H4>
<P>Delphi is an Object Pascal-based language, while C++Builder is obviously a C++-based
language. How is it possible to link Pascal directly into a C++ project?</P>
<P>A good way to begin your exploration of this issue is to understand that Delphi
2.0, Delphi 3.0, BCB, and Borland C++ 5.X use the same compiler. The difference is
simply in how the language is parsed. Delphi parses Object Pascal, tokenizes it,
and then passes it on to the compiler. Borland C++ 5.X and BCB parse C++, tokenize
it,
and then pass it on to the compiler. Underneath the hood, Delphi 2.0, C++Builder,
and Borland C++ are very similar tools. The difference is in how the syntax of the
language looks before it is parsed and tokenized.</P>
<P>Delphi sometimes finishes its
compile and link cycles faster than Borland C++
simply because Pascal is easier to parse, and because Delphi supports a simpler and
more flexible type of binary file (DCUs are easier to make than OBJs). Of course,
the incremental linker found in
C++Builder does much to eliminate this difference
in performance.</P>
<P>Because C++Builder uses the same compiler as Delphi, the road is obviously at
least halfway open to finding compatibility between Delphi and C++. The next stone
on the path is
laid by C++Builder's advantageous use of the Delphi VCL.</P>
<P>C++Builder uses the Delphi VCL in all of its standard projects. It therefore needs
to understand Delphi's types, objects, and syntax. This was made possible in part
by the fact that
Object Pascal is similar in structure to C++, but supports a subset
of the features available in C++. (There are some features of Object Pascal, such
as sets, not supported by C++, but these are relatively minor roadblocks that can
be overcome through
a judicious use of templates and classes.)</P>
<P>The converse of the logic in the last paragraph is not true. There is no need
for Delphi to parse C++. Furthermore, Object Pascal is, in a sense, a subset of C++.
It is therefore easier for C++Builder
to adapt to the features of Delphi than it
is for Delphi to support certain esoteric features of C++ such as multiple inheritance
or function and operator overloading.</P>
<P>Object Pascal is not radically different from C++ in the sense that BASIC is
radically
different from C++. Serious languages like Object Pascal and C++ are part of the
same family of tools, whereas cute and cuddly Visual Basic is from an altogether
different planet.</P>
<P>C++Builder and Delphi share a common heritage in the
way they construct objects,
handle variable types, and define methods. In particular, a Delphi object's VMT is
identical in structure to a C++ object that uses single inheritance. Furthermore,
Delphi supports all the calling conventions, such as
CDECL, PASCAL, and STDCALL,
that are used with C++ functions and methods. Therefore, there is no difference in
the structure of a Delphi function or method and a C++ function or method. They look
the same on the assembler level, as long as C++ is not
using any "advanced features,"
such as function overloading. (Remember that by default, 32-bit Object Pascal uses
the fastcall calling convention, which means that it passes some parameters to functions
in registers. This same convention is
supported by C++Builder, or you can explicitly
use a different convention if you like.)</P>
<P>Finally, there is a direct parallel between most C++ types and the standard Delphi
types. For instance, a C++ <TT>int</TT> is identical to a Delphi
<TT>Integer</TT>.
Other types, such as strings, are either identical, or very similar. For instance,
a C++ <TT>char * (LPSTR)</TT> is identical to a Delphi <TT>PChar</TT>. Furthermore,
a Delphi string is fully compatible with a <TT>char *</TT>, and
the C++Builder <TT>AnsiString</TT>
type is designed to be compatible with, and to mimic, a Delphi <TT>string</TT>. This
parallel structure continues throughout the two languages, and includes complex types
such as structures (records) and arrays.
(What C++ calls a union, Delphi calls a
variable record, and so on.)</P>
<P>There are, however, some differences between the languages. For instance, Delphi
does not support templates, C++Builder does not support <TT>try..finally</TT> blocks
or
<TT>with</TT> statements, and each language implements exception handling and
RTTI slightly differently. These differences are significant, but they do not represent
an impenetrable barrier erected between Delphi and C++Builder.</P>
<P>In this section
you have seen that Delphi and C++Builder both use the same compiler
and support a similar syntax and style of programming. These are the key elements
that make it possible to share code between C++Builder and Delphi.
<H4><A NAME="Heading15"></A><FONT
COLOR="#000077">Using COM to Link Delphi Code in
to C++Builder</FONT></H4>
<P>The last few sections have shown that it is trivial to link Delphi units and components
directly in to C++Builder. Another great way to share code between the two tools
is
through 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++, and so forth.</P>
<P>There are three key ways to link Delphi COM objects into C++Builder:
<DL>
<DD><B>1.</B> Use OLE Automation or dual interfaces.<BR>
<BR>
<B>2. </B>Use Delphi 3.0 to create ActiveX controls, and then load the ActiveX controls
on the C++Builder
Component Palette.<BR>
<BR>
<B>3.</B> Write raw COM or OLE code to link in Delphi objects.
</DL>
<P>The first two methods are supported automatically by C++Builder. The third method
requires you to dig into the details of OLE.</P>
<P>One simple way
to link in Delphi objects through 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.</P>
<P>Automation objects can be accessed in one of two
fashions: You can access them
through a COM <TT>IDispatch</TT> interface or through dual interfaces. C++Builder
has built-in support for <TT>IDispatch</TT> through the <TT>CreateOleObject</TT>
VCL function and the <TT>Variant</TT> 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.</P>
<P>For instance, suppose you have a Delphi 3.0 <TT>IDispatch</TT> interface that
exports the following
methods:</P>
<PRE><FONT COLOR="#0066FF">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;
</FONT></PRE>
<P>Assume further that the DLL that contains this interface is referenced in the
Registry in association with a <TT>CLSID</TT> that has a <TT>ProgID</TT> called
<TT>"TestLib.Test"</TT>.</P>
<P>Here is how you can retrieve the interface and call the <TT>Prompt</TT> method
from inside C++Builder:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::Button1Click(TObject *Sender)
{
Variant V =
CreateOleObject("TestLib.Test");
V.OleProcedure("Prompt", "Beware the Jabberwock, my son!");
}
</FONT></PRE>
<P>This code first retrieves an instance of <TT>IDispatch</TT> inside a variant.
It then uses a method of
the <TT>Variant</TT> class to call the <TT>Prompt</TT> method
from the Delphi interface.</P>
<P>Here is how you would call the <TT>VarTest</TT> method:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::Button1Click(TObject *Sender)
{
Variant
V1, V2, V3;
Variant V = CreateOleObject("TestLib.Test");
V1 = 5;
V2 = "Beware the Jubjub bird, and shun the frumious Bandersnatch!";
V3 = V;
V.OleProcedure("VarTest", V1, V2, V3);
}
</FONT></PRE>
<P>Delphi users looking at this code should note that a <TT>Variant</TT> is a C++
class declared in <TT>Sysdefs.h</TT>. It is not a simple type as it is when used
inside Delphi. This subject is covered in depth in Chapter 3, "C++Builder and
the
VCL."</P>
<P>If you want to use dual interfaces in C++Builder, you can get 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.</P>
<P>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 groundwork yourself. (The basic technique is similar to the
one outlined shortly in the section titled "Mapping Virtual Method Tables.")
<DL>
<DT></DT>
</DL>
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>I cannot ship an example of this
kind of code with this book because Delphi 3.0 had not shipped when we went to press.
<HR>
</BLOCKQUOTE>
<H4><A NAME="Heading17"></A><FONT
COLOR="#000077">Using Delphi ActiveX Controls in
C++Builder</FONT></H4>
<P>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,
do the
following:
<DL>
<DD><B>1. </B>Choose Component | Install from the menu.<BR>
<BR>
<B>2.</B> In the Install Components dialog, select the OCX button to bring up the
Import OLE Control dialog.<BR>
<BR>
<B>3. </B>All the registered components on
your system will appear in the Import
OLE Control dialog, as shown in Figure 6.3. If you want to register a new component,
select the Register button in the Import OLE Control dialog.<BR>
<BR>
<B>4. </B>After selecting the control you want to
import, press the OK button.<BR>
<BR>
<B>5. </B>Your new control will now be listed at the bottom of the Installed Components
dialog, and an interface source file will be placed in the LIB directory.<BR>
<BR>
<B>6. </B>Press OK to recompile
<TT>CMPLIB32</TT> and add your control to the Component
Palette. By default, the new ActiveX control will appear in the OCX page of the Component
Palette.
</DL>
<P><BR>
<A NAME="Heading18"></A><A HREF="06ebu03.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/06/06ebu03.jpg">FIGURE 6.3.</A><FONT
COLOR="#000077">
</FONT><I>The registered components as seen when adding an ActiveX control to BCB.</I>
<OL>
<P>
</OL>
<P>If you don't want to use the previously shown automated method, you can also use
the C++ language to build your own OLE
containers. This is, of course, a difficult
process, and the technique outlined earlier 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 in to your C++Builder
project either directly or through libraries
or DLLs.
<H4><A NAME="Heading19"></A><FONT COLOR="#000077">Using the Windows API to Link in
Delphi COM and OLE Objects</FONT></H4>
<P>Delphi 3.0 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.</P>
<P>C++Builder does not have the built-in support for COM that you find in Delphi
3.0. However, it does have
access to all the OLE code you find in Borland C++ 5.x,
the Windows SDK, the MSDN, or in commercially available books and libraries. It will
access most of the key OLE code found in MFC or the new ActiveX Template Library.</P>
<P>The following is a
definition for a simple Delphi COM object that will compile
in either Delphi 2.0 or Delphi 3.0. For that matter, it will also compile fine under
BCB.</P>
<PRE><FONT COLOR="#0066FF">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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -