COM开发问题(300分)

  • 主题发起人 主题发起人 JamesBond_L
  • 开始时间 开始时间
J

JamesBond_L

Unregistered / Unconfirmed
GUEST, unregistred user!
好久没来大富翁了,这段时间遇到问题,希望有人来帮忙解答。
1.在我记忆中,COM组件好像可以直接调试,调试方法类似Dll的调试,但是我发现在我的开发环境调试不了,不知道是不是我记忆的类似dll的调试方法调试的?
2.COM组件的事件问题。COM提供连接点的事件模型,但是我看了网上好多关于COM事件的实现时,多数都是客户端要增加一个专门的类去处理COM的事件,
http://www.informit.com/articles/article.asp?p=130494&seqNum=5&rl=1
但是我发现ADOConnection的事件代码,非常的简单,知识在TADOConnection里面实现了ConnectionEventsVT接口以后,在把ConnectionEventsVT的接口实例通过:
OleCheck(ConnectionPoint.Advise(Self as IUnknown, FConnEventsID));
这样就连接上去了,不知道它是怎么实现的。
 
我也在关注类似的问题,学习一下,帮你顶上去:)
 
自己顶。。。。
 
1,尝试下在project options中的include remote debug symbols选上
2.两者是一致的,你所提供网址的那个是把Dispatch Event连接过程封装成一个组件.TAdoConnection中用的不是Dispatch Event.看起来好像不一样,实际连接的过程是一样的.
你提供的那个,在InterfaceConnection中
if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then
if Succeeded(CPC.FindConnectionPoint(IID, CP)) then
CP.Advise(Sink, Connection);
Ado的,ConnectionPoint函数
OleCheck(ConnectionObject.QueryInterface(IConnectionPointContainer,
ConnPtContainer));
OleCheck(ConnPtContainer.FindConnectionPoint(DIID_ConnectionEvents, Result));
---
OleCheck(ConnectionPoint.Advise(Self as IUnknown, FConnEventsID));
 
铁盒子,你好!
第一个问题按你的方法解决了,谢谢!
但是第二个问题,我不是很明白你的意思,我都试过定义接口、和双接口的方式来尝试传递客户端的实现接口,但是都不行,不知道是哪里存在问题,你处能否提供简单的例子?其实ADO的TADOConnection是含
ConnectionEventsVt = interface;
ConnectionEvents = dispinterface;
两种的,不过就是不知道两种接口内部怎么样用了。
 
要调试COM,你的windwos的默认调试程序要设置成DELPHI才行。
 
ADO的Connection对象提供了两种事件接口ConnectionEventsVt和ConnectionEvents.
ConnectionEventsVt是普通的接口,ADO对象得到接口后以intf.WillConnect(...) 的方式调用,ConnectionEvents为DispInterface,只能通过Invoke函数传入相应的方法编号方式来调用,intf.Invoke(willConnect的DispId,...)

delphi中TAdoConnection是响应ConnectionEventsVt事件,所以TAdoConnection实现了ConnectionEventsVt接口.
响应DispInterface类型的接口,要求接受者实现IDispath接口,在Invoke方法中根据Dispid判别是哪个事件
连接的代码都是一样的查询IConnectionPointContainer,然后FindConnectionPoint,再Advise.
 
我想像Delphi那样“响应ConnectionEventsVt事件”,我想做到“实现了ConnectionEventsVt接口”就能实现事件,因为“响应DispInterface类型的接口,要求接受者实现IDispath接口,在Invoke方法中根据Dispid判别是哪个事件”我觉得那样的办法真是很笨的。。。
现在的问题是,我要怎么样做组件才能实行第一种啊?
Delphi里面有个想到,可以产生事件的接口,但是那样好像就是第二种的。
我曾经增加一个接口定义。然后组件:
unit uEventCOMImpl;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
ComObj, ActiveX, AxCtrls, Classes, COMEvent_TLB, StdVcl, Dialogs, Windows, SysUtils;

type
TEventCOM = class(TAutoObject, IConnectionPointContainer, IEventCOM)
private
{ Private declarations }
FConnectionPoints: TConnectionPoints;
FConnectionPoint: TConnectionPoint;
FEvents: IEventCOMEvents;
FEvent: IEventCOMEvent;
{ note: FEvents maintains a *single* event sink. For access to more
than one event sink, use FConnectionPoint.SinkList, and iterate
through the list of sinks. }
public
procedure Initialize; override;
protected
function Get_Event: IUnknown; safecall;
procedure Set_Event(const Value: IUnknown); safecall;
procedure BeginEvent; safecall;
procedure EndEvent; safecall;

{ Protected declarations }
property ConnectionPoints: TConnectionPoints read FConnectionPoints
implements IConnectionPointContainer;
procedure EventSinkChanged(const EventSink: IUnknown); override;
end;

implementation

uses ComServ;

procedure TEventCOM.EventSinkChanged(const EventSink: IUnknown);
begin
inherited EventSinkChanged(EventSink);
Windows.OutputDebugString('EventSinkChanged1');
//FEvents := EventSink as IEventCOMEvents;
FEvent := EventSink as IEventCOMEvent;
Windows.OutputDebugString('EventSinkChanged2');
end;

procedure TEventCOM.Initialize;
begin
inherited Initialize;
FEvents := nil;
FConnectionPoints := TConnectionPoints.Create(Self);
if AutoFactory.EventTypeInfo <> nil then
FConnectionPoint := FConnectionPoints.CreateConnectionPoint(
AutoFactory.EventIID, ckSingle, EventConnect)
else FConnectionPoint := nil;
end;


procedure TEventCOM.BeginEvent;
var
StrTemp: WideString;
begin
Windows.OutputDebugString('procedure TEventCOM.BeginEvent;1');

if Assigned(FEvents) then
begin
FEvents.BeginEvent(StrTemp, StrTemp);
ShowMessage(StrTemp);
end;

if Assigned(FEvent) then
begin
FEvent.BeginEvent(StrTemp, StrTemp);
ShowMessage(StrTemp);
end;

Windows.OutputDebugString('procedure TEventCOM.BeginEvent;2');
end;

procedure TEventCOM.EndEvent;
begin
if Assigned(FEvents) then
begin
FEvents.EndEvent;
end;
end;

function TEventCOM.Get_Event: IUnknown;
begin
Result := FEvent as IUnknown;
end;

procedure TEventCOM.Set_Event(const Value: IUnknown);
begin
FEvent := Value as IEventCOMEvent;
end;

initialization
TAutoObjectFactory.Create(ComServer, TEventCOM, Class_EventCOM,
ciMultiInstance, tmApartment);
end.

客户端实现接口和通过InterfaceConnect传递进去:
TMyEventCOM = class(TComponent, IUnknown, IEventCOMEvent)
private
FObj: IEventCOM;
FConnEventsID: Integer;
protected
procedure BeginEvent(const OutStr: WideString; var InStr: WideString); safecall;
procedure EndEvent; safecall;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure myBeginEvent; overload;
procedure myEndEvent; overload;
end;

procedure TMyEventCOM.BeginEvent(const OutStr: WideString; var InStr: WideString);
begin
InStr := 'InStr: ' + OutStr;
end;

constructor TMyEventCOM.Create(AOwner: TComponent);
procedure InterfaceConnect(const Source: IUnknown; const IID: TIID;
const Sink: IUnknown; var Connection: Longint);
var
CPC: IConnectionPointContainer;
CP: IConnectionPoint;
begin
Connection := 0;
if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then
if Succeeded(CPC.FindConnectionPoint(IID, CP)) then
CP.Advise(Sink, Connection);
end;
begin
inherited Create(AOwner);
FObj := CoEventCOM.Create;
FObj.Event := Self;
InterfaceConnect(FObj, IID_IEventCOMEvent, Self, FConnEventsID);
end;

destructor TMyEventCOM.Destroy;
begin
InterfaceDisconnect(FObj, IID_IEventCOMEvent, FConnEventsID);
inherited;
end;

procedure TMyEventCOM.EndEvent;
begin

end;

procedure TMyEventCOM.myBeginEvent;
begin
FObj.BeginEvent;
end;

procedure TMyEventCOM.myEndEvent;
begin
FObj.EndEvent;
end;
最终调用失败。。。出现灾难性错误。。。不知道你是否实际做过这样的组件呢,而这样的问题你是怎么解决的?
 
这个我没有试过.
按原则应该这样作.
定义变量
FMyEvent:IMyEvent;
在Initialize中加入
FMyConectionPoint:= FConnnectionPoints.CreateConnectionPoint(你的事件接口IID,ckSingle,MyEventConnect);

procedure TEventCom.MyEventConnect(const Sink: IUnknown; Connecting: Boolean);
begin
if Connecting then
begin
OleCheck(Sink.QueryInterface(你的事件接口IID, FMyEvent));
end
else
begin
FMyEvent := nil;
end;
end;
 
铁盒子,你好!
搞定了,非常感谢你!
之前我曾经去跟源码,但是没有发现问题所在,今天你说的成功以后,我再跟源码,发行原来实际TAutoObject的EventSink属性即可以取得你提到的MyEventConnect函数查询的FMyEvent接口,而EventSinkChanged实际目的应该是为了脚本语言实现COM的事件而需要双接口而给出的,如果我不需要,则不用理会它了。现在我调整如下:

unit uEventCOMImpl;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
ComObj, ActiveX, AxCtrls, Classes, COMEvent_TLB, StdVcl, Dialogs, Windows, SysUtils;

type
TEventCOM = class(TAutoObject, IConnectionPointContainer, IEventCOM)
private
{ Private declarations }
FConnectionPoints: TConnectionPoints;
FConnectionPoint: TConnectionPoint;
FEvents: IEventCOMEvents;
FEvent: IEventCOMEvent;
{ note: FEvents maintains a *single* event sink. For access to more
than one event sink, use FConnectionPoint.SinkList, and iterate
through the list of sinks. }
public
procedure Initialize; override;
procedure MyEventConnect(const Sink: IUnknown; Connecting: Boolean);
protected
function Get_Event: IUnknown; safecall;
procedure Set_Event(const Value: IUnknown); safecall;
procedure BeginEvent; safecall;
procedure EndEvent; safecall;

{ Protected declarations }
property ConnectionPoints: TConnectionPoints read FConnectionPoints
implements IConnectionPointContainer;
procedure EventSinkChanged(const EventSink: IUnknown); override;
end;

implementation

uses ComServ;

procedure TEventCOM.EventSinkChanged(const EventSink: IUnknown);
begin
inherited EventSinkChanged(EventSink);
Windows.OutputDebugString('EventSinkChanged1');
//FEvents := EventSink as IEventCOMEvents;
//FEvent := EventSink as IEventCOMEvent;
Windows.OutputDebugString('EventSinkChanged2');
end;

procedure TEventCOM.Initialize;
begin
inherited Initialize;
FEvents := nil;
FConnectionPoints := TConnectionPoints.Create(Self);
if AutoFactory.EventTypeInfo <> nil then
// FConnectionPoint := FConnectionPoints.CreateConnectionPoint(IID_IEventCOMEvent,ckSingle,MyEventConnect)

FConnectionPoint := FConnectionPoints.CreateConnectionPoint(
AutoFactory.EventIID, ckSingle, EventConnect)
else FConnectionPoint := nil;
FEvent := EventSink as IEventCOMEvent; // 通过EventSink取得客户端接口
end;

procedure TEventCOM.MyEventConnect(const Sink: IUnknown; Connecting: Boolean);
begin
if Connecting then
begin
OleCheck(Sink.QueryInterface(IID_IEventCOMEvent, FEvent));
end
else
begin
FEvent := nil;
end;
end;

procedure TEventCOM.BeginEvent;
var
StrTemp: WideString;
begin
Windows.OutputDebugString('procedure TEventCOM.BeginEvent;1');

if Assigned(FEvents) then
begin
FEvents.BeginEvent(StrTemp, StrTemp);
ShowMessage(StrTemp);
end;

if Assigned(FEvent) then
begin
StrTemp := 'StrTemp';
FEvent.BeginEvent(StrTemp, StrTemp);
ShowMessage(StrTemp);
end;

Windows.OutputDebugString('procedure TEventCOM.BeginEvent;2');
end;

procedure TEventCOM.EndEvent;
begin
if Assigned(FEvents) then
begin
FEvents.EndEvent;
end;
end;

function TEventCOM.Get_Event: IUnknown;
begin
Result := FEvent as IUnknown;
end;

procedure TEventCOM.Set_Event(const Value: IUnknown);
begin
FEvent := Value as IEventCOMEvent;
end;

initialization
TAutoObjectFactory.Create(ComServer, TEventCOM, Class_EventCOM,
ciMultiInstance, tmApartment);
end.

希望以后有机会再交流。再次感谢!
 
多人接受答案了。
 
搞错了些地方,为了防止误导,应该是下面“修改”情况:
unit uEventCOMImpl;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
ComObj, ActiveX, AxCtrls, Classes, COMEvent_TLB, StdVcl, Dialogs, Windows, SysUtils;

type
TEventCOM = class(TAutoObject, IConnectionPointContainer, IEventCOM)
private
{ Private declarations }
FConnectionPoints: TConnectionPoints;
FConnectionPoint: TConnectionPoint;
FEvent: IEventCOMEvent;
{ note: FEvents maintains a *single* event sink. For access to more
than one event sink, use FConnectionPoint.SinkList, and iterate
through the list of sinks. }
public
procedure Initialize; override;
protected
procedure BeginEvent; safecall;
procedure EndEvent; safecall;

{ Protected declarations }
property ConnectionPoints: TConnectionPoints read FConnectionPoints
implements IConnectionPointContainer;
procedure EventSinkChanged(const EventSink: IUnknown); override;
end;

implementation

uses ComServ;

procedure TEventCOM.EventSinkChanged(const EventSink: IUnknown);
begin
FEvent := Self.EventSink as IEventCOMEvent;// 修改
end;

procedure TEventCOM.Initialize;
begin
inherited Initialize;
FConnectionPoints := TConnectionPoints.Create(Self);
if AutoFactory.EventTypeInfo <> nil then
begin
FConnectionPoint := FConnectionPoints.CreateConnectionPoint(
AutoFactory.EventIID, ckSingle, EventConnect);
end
else FConnectionPoint := nil;
end;

procedure TEventCOM.BeginEvent;
var
StrTemp: WideString;
begin
Windows.OutputDebugString('procedure TEventCOM.BeginEvent;1');

if Assigned(FEvent) then
begin
StrTemp := 'StrTemp';
FEvent.BeginEvent(StrTemp, StrTemp);
ShowMessage(StrTemp);
end;

Windows.OutputDebugString('procedure TEventCOM.BeginEvent;2');
end;

procedure TEventCOM.EndEvent;
begin
if Assigned(FEvent) then
begin
FEvent.EndEvent;
end;
end;

initialization
TAutoObjectFactory.Create(ComServer, TEventCOM, Class_EventCOM,
ciMultiInstance, tmApartment);
end.
 
后退
顶部