【问题标题】:Troubleshooting COM error "The parameter is incorrect"排查 COM 错误“参数不正确”
【发布时间】: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 对象。

标签: .net delphi com


【解决方案1】:

看起来这个在包里。感谢@EricBrown 和@RemyLebeau,我能够让它工作。

Remy 建议将 FRespList 的实例化从:

var
  FRespList: TFXResponseListener;

到:

var 
  FRespList: IFXResponseListener;

...成功了。所以当我使用

实例化
FRespList := TFXResponseListener.Create;

...我得到了正确的结果,一切都是胖的、愚蠢的和快乐的。

我还需要指出,分配给 dispinterface 的 GUID 必须用于我创建的 IDispatch 接口后代,否则,我们将返回“参数不正确。”。想知道这是否记录在某处......

也有点奇怪,当我处理完我的 IDispatch 后代时,我将它归零。这将 TAutoIntfObject 后代的引用计数减少到 2。这会导致内存泄漏。我测试并尝试了不同的方法来让这个引用计数为 dec,最后不得不做一些奇怪的事情:

**procedure** TMyClass.Destroy; // <- changed from **destructor** TMyClass.Destroy;
begin
  inherited Destroy; // <- had to call this first!
  // then...
  while Self.RefCount > 0 do
    IUnknown(Self)._Release;
end;

...然后调用 MyClassInstance.Destroy;让课程在没有 AV 和内存泄漏的情况下免费。

如果有人知道为什么我的班级有 3 次引用,为什么 FreeAndNil(MyClassInstance) 和 IMyClass := Nil;由于访问冲突和/或内存泄漏,引用计数为 2,为什么我必须首先销毁继承的,为什么我必须将类类型转换为 IUnknown 并手动释放实例,请说出来。

在另一个接口/类对上,我仍然从 TServerEventDispatch 获得内存泄漏。这是 Delphi 在 tlb.pas 文件中自动创建的类之一。如果有人有建议...

COM...你一定会喜欢的!

我想给 Eric Brown 和 Remy Lebeau 加分,但我不知道该怎么做。

所以...谢谢大家!

【讨论】:

    猜你喜欢
    • 2010-11-16
    • 1970-01-01
    • 1970-01-01
    • 2021-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-03
    • 1970-01-01
    相关资源
    最近更新 更多