再来一个300分:com,你真的只能让c++调用吗?Delphi杀手--多重继承。(300分)

  • 主题发起人 主题发起人 叶不归
  • 开始时间 开始时间

叶不归

Unregistered / Unconfirmed
GUEST, unregistred user!
有这样一个com,无法提取类型库,但有c++的.h文件(里面有该com所有的接口声明),
它还叮嘱道:要调用我的接口函数,请确认在形式如类CC的类中调用。类CC的声明如下:
class CC : public CDialog , public CDataCallback, public CEventCallback
{......}
在这里,CDataCallback和CEventCallback是接口提供的回调类:
class CEventCallback
{
public:
virtual void OnEvent(long lEvent, long lValue) = 0;
};

class CVBIDataCallback
{
public:
virtual void OnReceiveData(DWORD dwHdl, BYTE* pData, int iCount, HRESULT hr, DWORD dwTimeDiff) = 0;
};
多重继承啊,这样的代码如何能用不支持多重继承的Delphi中写出来呢,
多重继承,Delphi的无奈。
各位大吓,有何高见?
 
可以地吧
 
to 来如风
说详细些,我可是一点办法也没有了,会试的方法我都试过了。
 
fonder,如何?详细详细,别说一半啊。
 
C#已经不支持多重继承了

如果要和别的语言进行接口,不应该用C++的类来进行耦合,
而应该用接口来进行耦合
 
LiChaoHui,你说的我不太明白,如何>用接口来进行耦合?能举个例子吗?
 
如果楼主的对象不提供接口进行耦合,那么显然是不想在其他的语言中使用的

接口的使用从一定程度上弥补了Delphi的不支持多重继承,
同时可以使用对象组装对多重继承进行模拟

关于使用接口进行回调的功能,这儿有个例子,是用来截获浏览器中对象标记的事件

unit events_;

interface

{ Subject: Handling DHTML events in your app
Author: Alexei Reatov
Home: http://www.betterbrowser.com/components
Date: 03 Mar 1999

Description:

This example demonstrates how to intercept a specific event of a specific
DHTML element, hosted in a WebBrowser control, using a simple IDispatch
imitating that of a JavaScript function.

DISCLAIMER: THE CODE IS PROVIDED AS IS, WITHOUT ANY WARRANTIES OR
PROMISES OF TECHNICAL SUPPORT FROM THE AUTHOR. USE IT ON YOUR OWN RISK
AND DON'T BLAME ME IF IT DOES NOT WORK AS AVERTIZED. YOU HAVE THIS SOURCE
AND YOU HAVE MICROSOFT DOCUMENTATION, SO IF YOU CAN'T MAKE SENSE OF IT OR
CAN'T FIX A BUG WHEN YOU RUN INTO IT, IT'S YOUR OWN FAULT!
}

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
SHDocVw_TLB, StdCtrls, ActiveX, ComObj, OleCtrls, ExtCtrls;

type

// In this example, only one event is going to be hooked, therefore only
// one IDispatch object will be needed and I'm sticking the interfac to
// the main form. In a more thorough implementation, there would be multiple
// dynamically allocated IDispatches (e.g. created on demand by an invisible
// component providing the Delphi-level event handlers).

TForm1 = class(TForm, IUnknown, IDispatch)
IE: TWebBrowser;
Panel1: TPanel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure IEDocumentComplete(Sender: TObject; pDisp: IDispatch;
var URL: OleVariant);
private
{ Private declarations }
FRefCount: Integer;

{ Points to the old event handler (e.g. JavaScript function) }
FEventDisp: IDispatch;

{ IUnknown }
function QueryInterface(const IID: TGUID; out Obj): Integer; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
{ IDispatch }
function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
function GetIDsOfNames(const IID: TGUID; Names: Pointer;
NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
ws: WideString;
v1, v2, v3, v4: OleVariant;
begin
ws := ChangeFileExt(Application.ExeName, '.htm');
IE.Navigate(ws, v1, v2, v3, v4);
end;

function TForm1.QueryInterface(const IID: TGUID; out Obj): HResult;
var
s: string;
begin
if GetInterface(IID, Obj) then
Result := S_OK
else
Result := E_NOINTERFACE;
end;

function TForm1._AddRef: Integer;
begin
Inc(FRefCount);
Result := FRefCount;
end;

function TForm1._Release: Integer;
begin
Dec(FRefCount);
Result := FRefCount;
end;

{ TForm1.IDispatch }

function TForm1.GetTypeInfoCount(out Count: Integer): HResult;
begin
if FEventDisp <> nil then
Result := FEventDisp.GetTypeInfoCount(Count)
else begin
Count := 0;
Result := S_OK;
end;
end;

function TForm1.GetTypeInfo(Index, LocaleID: Integer;
out TypeInfo): HResult;
begin
if FEventDisp <> nil then
Result := FEventDisp.GetTypeInfo(Index, LocaleID, TypeInfo)
else begin
Pointer(TypeInfo) := nil;
Result := E_NOTIMPL;
end
end;

function TForm1.GetIDsOfNames(const IID: TGUID; Names: Pointer;
NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
begin
if FEventDisp <> nil then
Result := FEventDisp.GetIDsOfNames(IID, Names, NameCount, LocaleID, DispIDs)
else
Result := E_NOTIMPL;
end;

var
// These are for debugging only!
cA, cNA: Integer;

function TForm1.Invoke(DispID: Integer; const IID: TGUID;
LocaleID: Integer; Flags: Word; var Params;
VarResult, ExcepInfo, ArgErr: Pointer): HResult;
begin
try
MessageDlg('The event has fired!', mtinformation, [mbok], 0);
except
end;

if FEventDisp <> nil then begin
with TDispParams(Params) do begin
// never used in the code, I just used the lines below in the debugger to
// make sure no args are passed in!
cA := cArgs;
cNA := cNamedArgs;
end;
Result := FEventDisp.Invoke(DispID, IID, LocaleID, Flags, Params,
VarResult, ExcepInfo, ArgErr)
end else
Result := E_NOTIMPL;
end;

// Once the document is loaded, set up the event handler. Note that the
// code below expects to find a certain <A> element named "theLink" in the
// HTML file. In a more generic case, you'd have to scan the DHTML elements
// to find those you want to hook into.
procedure TForm1.IEDocumentComplete(Sender: TObject; pDisp: IDispatch;
var URL: OleVariant);
var
vDoc, vA: OleVariant;
begin
vDoc := IE.Document;
vA := vDoc.All.theLink;
FEventDisp := IDispatch(vA.onclick);
vA.onclick := OleVariant(Self as IDispatch);
end;

end.



 
这个com的确没有接口耦合,我不明白,既然它做成com,不是想实现多语言调用吗?
为何又要做类耦合的限制呢?
还有办法可以解决这个问题吗?
 
可能是作者不太懂COM的设计模式,有没有用过类似的东西的缘故吧
对于这样的东西竟然去使用多重继承,这人的设计思路的确是别出心裁
令人好生佩服
 
hoho,这东东也叫COM?多半是COM新手写的,COM强调的是二进制标准、跨语言独立性,楼主所说的这个COM不用也罢,实在要用就用C++给他再包装一层,变成类组合的方式。
 
请大家看看这个吧:http://www.delphibbs.com/delphibbs/dispq.asp?lid=1694082
如果能回答那个问题,除了那个贴子的300分外,这个贴子的200分也归你了。
 
详细详细,com不会
 
我对楼主不是意思不是非常清楚,但是我提供,但愿有用
Delphi与C++之间函数的共享;

代码的静态链接和动态链接;

对象的共享。


函数的共享

在Delphi中调用C++函数与C++调用Delphi函数相当直接,需要注意的是,Delphi 1默认的函数调用方式是Pascal方式,Delphi 2、Delphi 3的默认方式则是优化的cdecl调用方式,即register方式。要在C++与Delphi程序之间实现函数共享,除非有充分的原因,否则应该使用标准系统调用方式,即stdcall方式。为了使C++编译器不将函数标记为"mang led",使Delphi编译器误认为函数是采用cdecl调用方式,应该在C++代码中,以extern "C "说明被共享的函数,如下例所示:

原型说明:
在C++中:
extern "C" int _stdcall TestFunc();
在Delphi中:
function TestFunc:integer; stdcall;

调用语法:
在C++中:
int i=TestFunc();
在Delphi中:
var i:integer;

begin

i:=TestFunc;

end;

共享函数的参数必须是两种语言都支持的变量类型,这是正确传递参数的前提。诸如Delphi的currency、string、set等变量类型,在C++中没有相对应的变量类型,不能被用作共享函数的参数。可以用PChar类型以值参的方式传递字符串指针,这时用户必须注意字符串空间的回收。
Delphi语言中的变参应被说明成C++中相应变量类型的引用形式,如下:

在Delphi中:
function TestFunc(var i:integer):integer;
在C++中:
int TestFunc(int &i);

代码链接
在Delphi与C++之间实现代码链接可采用静态链接或动态链接的方式。

1.静态链接方式

如果C++程序本身的代码量很小,而且无需担心与C运行库会有交互过程,一般可选用静态链接方式,即把Delphi与C++的目标文件(*.OBJ)链接成最终的可执行文件。具体的方法是使用{$L}编译指令,使Delphi编译器自动读取指定目标文件,说明如下:

function TestFunc:integer;stdcall;
{$L TestFunc.OBJ}

2.动态链接方式

如果C++代码已经相当全面或自成一个完整的子系统,代码量很大,或者用到了C运行库,在这种情况下,应该使用动态链接库(DLL)的方式。此时,在两种语言的源代码中应做如下说明:

在C++中:
int stdcall export TestFunc();
在Delphi中:
function TestFunc:integer; stdcall;
external ‘TestFunc.DLL’;

对象的共享

在C++与Delphi之间的对象共享主要体现在对象方法(Method)的共享方面,这种共享可分为两个层次:对象(Object)级共享与类(Class)级共享。要实现对象级共享,程序设计语言需具备两个前提条件:

能够定义指向由另一语言创建的对象的指针;

可以访问由指针确定的对象中的方法。

要实现类级的共享,则还需考虑:

能够创建由另一种语言定义的类的实例;

可以从堆中释放一个实例所占用的空间;

派生新的类。

以下介绍在Delphi与Borland C++之间实现对象共享的方法。

1.C++共享Delphi对象

要实现从C++调用Delphi对象,首先要在Delphi单元的接口部分以及C++的头文件中说明需要共享的对象的接口,在对象接口中定义该对象包含哪些属性与方法,并说明可供共享的部分。对象的共享,关键在于方法的共享。在Delphi语言中,要使一个对象可以被共享,可以把它说明为两个接口部分,暂称为"共享接口"与"实现接口"。其中共享接口指明对象中哪些方法可被另一种语言所共享;实现接口则继承共享接口,并且在单元实现部分针对实现接口中的方法定义具体的实现。要定义一个可供C++共享的Delphi对象,共享接口的说明应注意:

在Delphi程序里,要共享的方法必须被说明为抽象(abstract),而且虚拟(virtual );

在C++程序里,必须用关键字"virtual"及"=0"后缀,把从Delphi共享的方法说明成"pure virtual";

共享的对象方法必须在两种语言里都被说明成相同的调用方式,通常使用标准系统调用方式(stdcall)。

下面,举例说明这些规则,假设有这样的一个Delphi对象:
TTestObject=class
procedure Proc1(x:integer);
function Func1(x:integer):PChar;
procedure Proc2;
function Func2:integer;
end;

如果C++程序需要共享其中的方法Proc1、Func1,可把上述说明修改成以下形式:
STestObject=class
procedure Proc1(x:integer); virtual; abstract; stdcall;
function Func1(x:integer); virtual; abstract; stdcall;
end;
TTestObject=class(STestObject)
procedure Proc1(x:integer);
fuction Func1(x:integer):PChar;
procedure Proc2;
fuction Func2:integer;
end;

在C++程序中做如下对象原型说明:
class STestObject {
virtual void Proc1(int x)=0;
virtual char *Func1(int x)=0;
};

为了能在C++中成功地访问Delphi定义的类, Delphi接口说明时必须包含一个可共享的"制造函数(Factory Function)"CreateTestObject,该制造函数可被定义在动态链接库或目标文件(.OBJ)中,例如:

Library TestLib;
exports CreateTestObject;
function CreateTestObject:STestObject; stdcall;
begin
Result:=TTestObject.Create;
end;

end.

经过这样的处理,现在可在C++程序中使用这个由Delphi定义的对象,调用方式如下:
extern "C" STestObject stdcall *CreateTestObject();
void UseTestObject(void) {
STestObject *theTestObject=CreateTestObject();
theTestObject->Proc1(10);
Char *str=theTestObject->Func1(0);
}

当调用制造函数CreateTestObject时,实际上已经在Delphi一侧占用了一个对象实例的空间,C++程序在针对该对象的所有处理完成后必须考虑释放这一空间,具体的实现可在Delphi中定义一个类,如上述Proc1的共享方法Free,以此来完成这一任务:
STestObject=class
procedure Proc1(x:integer); virtual; abstract; stdcall;
function Func1(x:integer); virtual; abstract; stdcall;
procedure Free; virtual; abstract; stdcall;
end;

implementation

procedure TTestObject.Free;
begin

end;

end.

2.Delphi共享C++对象
通常,程序员会考虑使用Delphi来编制用户界面,所以Delphi代码调用C++代码似乎显得更加实际些。其实,Delphi共享C++对象的实现方法与上述C++共享Delphi对象非常相似。用同样的共享接口与实现接口说明方法来定义C++的类:

class STestObjedt {
virtual void Proc1(int x)=0;
virtual char *Func1(int x)=0;
};
class TTestObjedt :public STestObject {
void Proc1(int x);
char *Func1(int x);
void Proc2();
int Func2();
void Free();
};

然后实现这些方法。同样地,C++对象需要一个与之对应的制造函数,这里以DLL为例
STestObject stdcall export *CreateTestObject() {
return (STestObject *) new TTestObject.Create;
}

Delphi代码可以通过调用制造函数CreateTestObject,很容易地在C++中创建实例,获得指向该实例的指针值,并以这个指针值来调用对象中的共享方法。当然,在进行完该对象的相关处理后,千万不要忘了调用Free释放占用的空间。
 
咳,算了,用C了。
 
后退
顶部