【发布时间】:2014-03-08 18:01:47
【问题描述】:
环境是 Delphi XE2 Enterprise。
与use dispInterface in Delphi with no classid 相关。作为一个不同的问题重新发布,因为我已经远远超出了以前的问题(感谢@EricBrown),现在有一个更具体的问题。
我有一个带有 COM 接口的 .NET dll,我已在计算机上注册并作为类型库导入 Delphi。我正在使用 Delphi 创建的 tlb.pas 文件。
使用我在 IDispatch 接口和 TAutoIntf 后代类中实现的几个 dispInterfaces,我已经成功地初始化了我需要调用 .NET 库的 COM 接口中的方法的类和接口。下面是一些代码来说明声明/实现:
// COM Event Sink GUID
DIID_IResponseListener: TGUID = '{ABC29F08-B628-4747-BA9E-469D408E57B9}';
// *********************************************************************//
// DispIntf: IResponseListener
// Flags: (4096) Dispatchable
// GUID: {ABC29F08-B628-4747-BA9E-469D408E57B9}
// *********************************************************************//
IResponseListener = dispinterface
['{ABC29F08-B628-4747-BA9E-469D408E57B9}']
procedure RequestCompleted(const requestID: WideString; const responseObj: IResponse); dispid 1610744833;
procedure RequestFailed(const requestID: WideString; const error: WideString); dispid 1610744834;
procedure TablesUpdates(const responseObj: IResponse); dispid 1610744835;
end;
...my implementation:
IFXResponseListener = interface(IDispatch)
['{3204D3F7-5DF2-4470-89D5-D34F4F6F0381}']
procedure RequestCompleted(const requestID: WideString; const responseObj: IResponse); dispid 1610744833; safecall;
procedure RequestFailed(const requestID: WideString; const error: WideString); dispid 1610744834; safecall;
procedure TablesUpdates(const responseObj: IResponse); dispid 1610744835; safecall;
end;
TFXResponseListener = class(TAutoIntfObject,IFXResponseListener)
private
FDisp: IDispatch;
FResp: IResponse;
function GetResponseListIntf: IResponseListener;
public
constructor Create;
destructor Destroy;
published
procedure RequestCompleted(const requestID: WideString; const responseObj: IResponse); safecall;
procedure RequestFailed(const requestID: WideString; const error: WideString); safecall;
procedure TablesUpdates(const responseObj: IResponse); safecall;
property ResponseObj: IResponse read FResp;
property ListenerDispIntf: IResponseListener read GetResponseListIntf;
end;
...
{ TFXResponseListener }
constructor TFXResponseListener.Create;
var
TypeLib: ITypeLib;
begin
OleCheck(LoadRegTypeLib(LIBID_fxcore2_com,fxcore2_comMajorVersion,fxcore2_comMinorVersion,0,TypeLib));
inherited Create(TypeLib,DIID_IResponseListener);
end;
destructor TFXResponseListener.Destroy;
begin
inherited;
end;
function TFXResponseListener.GetResponseListIntf: IResponseListener;
begin
FDisp := Self as IFXResponseListener;
FDisp._AddRef;
Result := IResponseListener(FDisp);
end;
procedure TFXResponseListener.RequestCompleted(const requestID: WideString; const responseObj: IResponse);
begin
showmessage('Completed: ' + requestID);
FResp := responseObj;
end;
procedure TFXResponseListener.RequestFailed(const requestID: WideString; const error: WideString);
begin
showmessage('Failed: ' + requestID);
end;
procedure TFXResponseListener.TablesUpdates(const responseObj: IResponse);
begin
showmessage('TablesUpdates');
end;
这里是我尝试使用界面的地方:
FRespList := TFXResponseListener.Create;
try
FSess.subscribeResponse(FRespList.ListenerDispIntf);
except
// errors out here with 'The parameter is incorrect'
end;
追溯至 System.Win.ComObj 的内部,在第 1793 行它调用:
Status := Dispatch.Invoke(DispID, GUID_NULL, 0, InvKind, DispParams, Result, @ExcepInfo, nil);
此时DispID参数有效(1610743816),GUID_NULL为'(0, 0, 0, (0, 0, 0, 0, 0, 0, 0, 0))',InvKind为'1 ',DispParams 是 '($2152FE8, nil, 1, 0)',结果是 '$12FE24',@ExcepInfo 是 '$12FDE0'。
在 System.pas 的第 30133 行中,TInterfacedObject.QueryInterface 被调用了两次。在第二次运行时,它返回结果“E_NOINTERFACE”,并出现可怕的“参数不正确”消息。
我真的不知道从哪里开始,但我希望 COM 专家和/或 Delphi 专家能够审查这个并发现一些问题。
我还想知道是否存在 .NET 框架版本或其他问题。我正在使用框架版本 4.5.1;不确定如何确定 .NET 程序集是否符合此版本的框架,或者是否确实需要早期版本。
任何相关的 .NET/COM 调试技术将不胜感激。
另外请注意,我可以使用不同的 dispinterface/IDispatch/TAutoIntfObj 后代类和类似的调用来复制此问题,与“T”相同。
谢谢。
【问题讨论】:
-
FRespList声明为什么?FSess.subscribeResponse()?当它返回E_NOINTERFACE时,QueryInterface()被要求提供什么指导?该 guid 是标准 guid 还是在类型库中定义?另外,TFXResponseListener.GetResponseListIntf()的实现在我看来非常可疑,是您创建的还是 Delphi 自动生成的? -
FRespList 被声明为 TFXResponseListener。 FSess 是来自类型库的 ISession 接口。 Session subscribeResponse 过程采用一个参数,即定义为 dispInterface 的 IResponseListener。 TFXResponseListener.GetResponseListIntf 是我定义的,就像所有 TFXResponseListener 类和 IFXResponseListener 接口一样。 QueryInterface 正在测试这个:(3288121758, 43376, 4562, (139, 90, 0, 160, 201, 183, 201, 196))`,我不知道如何将其转换为 GUID。
-
奇怪的是它两次命中 TInterfacedObject.QueryInterface。第一次称为 IID 是 `(0, 0, 0, (192, 0, 0, 0, 0, 0, 0, 70))。第二个调用是上面列出的接口。在跟踪过程中,是对 subscribeResponse(IResponseListener) 的调用导致了对 QueryInterface 的 2 次调用。在第一次调用时,它返回 -0-(成功?)的结果
-
COM接口的制作者是这样说的:“你能不能试着类比一下绑定到COM事件……”和“你可以试着在Delphi中类比实现该方法方法 session_RequestCompleted..."。图书馆的制作人不了解Delphi,也没有任何员工了解Delphi。由于我不太擅长 .NET 或 COM,因此很难解决此问题。
-
FRespList应改为IFXResponseListener,并完全去掉ListenerDispIntf属性:FSess.subscribeResponse(FRespList);至于QueryInterface(),第一个guid 是IID_IUnknown,所有COM接口必须实现(TInterfacedObject为您处理)。第二个指导是IID_IManagedObject。因此,subscribeResponse()似乎需要一个 .NET 对象作为侦听器,因此不适用于 Delphi COM 对象。