COM错误故障排除“参数不正确”

时间:2014-02-11 05:07:17

标签: .net delphi com

环境是Delphi XE2 Enterprise。

use dispInterface in Delphi with no classid有关。重新发布作为一个不同的问题,因为我已经远远超出以前的问题(感谢@EricBrown),现在有一个更具体的问题。

我有一个带有COM接口的.NET dll,我已在计算机上注册并作为类型库导入Delphi。我正在使用Delphi创建的tlb.pas文件。

使用我在IDispatch接口和TAutoIntf后代类中实现的几个dispInterface,我已成功初始化了我需要将COM接口中的方法调用到.NET库的类和接口。以下是一些用于说明声明/实现的代码:

// 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))',InvKind为'1 ',DispParams是'($ 2152FE8,零,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'相同。

感谢。

1 个答案:

答案 0 :(得分:0)

看起来这个就在包里。感谢@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提出任何要点,但我不知道如何。

所以...感谢GUYS!