疑难解答COM错误“参数不正确” [英] Troubleshooting COM error "The parameter is incorrect"
问题描述
环境是Delphi XE2 Enterprise。
关于在Delphi中使用dispInterface,没有classid 。重新发布为一个不同的问题,因为我已经超越了以前的问题(感谢@EricBrown),现在有一个更具体的问题。
我有一个.NET dll与COM接口,我已在计算机上注册,并作为类型库导入到Delphi。我使用Delphi创建的tlb.pas文件。
使用我在IDispatch接口和TAutoIntf后代类中实现的几个dispInterfaces,成功地初始化了我需要在COM接口中调用方法到.NET库的类和接口。下面是一些代码来说明声明/实现:
// COM事件接收器GUID
DIID_IResponseListener:TGUID = {ABC29F08-B628-4747-BA9E-469D408E57B9}';
// **************************************** ***************************** //
// DispIntf:IResponseListener
//标志:(4096 )可分派
// GUID:{ABC29F08-B628-4747-BA9E-469D408E57B9}
// *********************** ********************************************** //
IResponseListener = dispinterface
['{ABC29F08-B628-4747-BA9E-469D408E57B9}']
过程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;
...我的实现:
IFXResponseListener = interface(IDispatch)
['{3204D3F7-5DF2-4470-89D5-D34F4F6F0381}']
过程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;
TFXResponseListener = class(TAutoIntfObject,IFXResponseListener)
private
FDisp:IDispatch;
FResp:IResponse;
function GetResponseListIntf:IResponseListener;
public
constructor创建;
destructor Destroy;
published
procedure RequestCompleted(const requestID:WideString; const responseObj:IResponse);安全报
procedure RequestFailed(const requestID:WideString; const error:WideString);安全报
procedure TablesUpdates(const responseObj:IResponse);安全报
属性ResponseObj:IResponse read FResp;
属性ListenerDispIntf:IResponseListener read GetResponseListIntf;
end;
...
{TFXResponseListener}
构造函数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;
结果:= 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
//错误在这里与'参数不正确'
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,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'。
谢谢。
看起来这个是在包里。感谢@EricBrown和@RemyLebeau,我能够让这个工作。
Remy的建议改变FRespList的实例化:
var
FRespList:TFXResponseListener;
到:
code> var
FRespList:IFXResponseListener;
...所以当我实例化使用
FRespList:= TFXResponseListener.Create;
...我得到正确的结果,一切都很胖,
我还需要指出,分配给dispinterface的GUID必须用于我创建的IDispatch接口后代,否则我们回到'参数不正确。
也有点奇怪,当我完成了我的IDispatch后代我没有它。这会将TAutoIntfObject后代的引用计数减少为 2 。这会导致内存泄漏。我测试和尝试不同的事情,以获得这个引用计数,最终不得不做一些奇怪:
**过程** TMyClass 。破坏; //< - 从** destructor **改变TMyClass.Destroy;
begin
inherited; //< - 不得不调用这个!
// then ...
while Self.RefCount> 0 do
IUnknown(Self)._ Release;
end;
...然后调用MyClassInstance.Destroy;
如果有人知道为什么我有3个引用我的类,为什么FreeAndNil(MyClassInstance)和IMyClass:= Nil;离开引用计数2与访问违反和/或内存泄漏,为什么我不得不摧毁继承的第一,为什么我必须类型转换类作为IUnknown和手动释放实例,请大声说。
在另一个接口/类对上,我仍然从TServerEventDispatch获取一个mem泄漏。这是Delphi在tlb.pas文件中自动创建的类之一。如果有人有建议...
我'
所以...谢谢你!
Environment is Delphi XE2 Enterprise.
Relates to use dispInterface in Delphi with no classid. Re-posting as a different question as I have moved well beyond the previous issues (thanks to @EricBrown) and now have a more specific problem.
I've got a .NET dll with a COM interface that I've registered on the computer and have imported as a type library into Delphi. I'm using the tlb.pas file that Delphi created.
Working with a couple of dispInterfaces that I've implemented in IDispatch interfaces and TAutoIntf descendant classes, I've successfully initialized the classes and interfaces that I need to call methods in the COM interface to the .NET library. Here is some code to illustrate the declaration/implementation:
// 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;
Here is where I attempt to use the interface:
FRespList := TFXResponseListener.Create;
try
FSess.subscribeResponse(FRespList.ListenerDispIntf);
except
// errors out here with 'The parameter is incorrect'
end;
Tracing this down into the bowels of System.Win.ComObj, at line 1793 it calls:
Status := Dispatch.Invoke(DispID, GUID_NULL, 0, InvKind, DispParams, Result, @ExcepInfo, nil);
At this point, the DispID parameter is valid (1610743816), GUID_NULL is '(0, 0, 0, (0, 0, 0, 0, 0, 0, 0, 0))', InvKind is '1', DispParams is '($2152FE8, nil, 1, 0)', Result is '$12FE24', @ExcepInfo is '$12FDE0'.
In System.pas, line 30133, TInterfacedObject.QueryInterface is called twice. On the second run it returns the result 'E_NOINTERFACE', and the dreaded 'The parameter is incorrect' message appears.
I'm really not sure where to go from here, but I'm hoping a COM expert and/or Delphi expert can review this and see something amiss.
I'm also wondering if there is a .NET framework version or other issue. I'm using framework version 4.5.1; not sure how to determine if the .NET assembly is kosher with this version of the framework or if it really needs an earlier version.
Any relevant .NET/COM debugging techniques would be greatly appreciated.
Also please note, I can duplicate this issue with a different dispinterface/IDispatch/TAutoIntfObj descendant class and similar call, identical to a 'T'.
Thanks.
Looks like this one is in the bag. Thanks to @EricBrown and @RemyLebeau, I was able to get this to work.
Remy's suggestion of changing the instantiation of FRespList from:
var
FRespList: TFXResponseListener;
to:
var
FRespList: IFXResponseListener;
...did the trick. So when I instantiate using
FRespList := TFXResponseListener.Create;
...I get the right result, and everything is fat, dumb and happy.
Also I need to point out that the GUID that is assigned to the dispinterface has to be used for the IDispatch interface descendant that I created, otherwise, we're back to 'The parameter is incorrect.'. Wonder if that's documented somewhere...
Also kind of weird, when I'm done with my IDispatch descendant I nil it. That reduces the refcount on the TAutoIntfObject descendant to 2. Which results in a memory leak. I tested and tried different things to get this refcount to dec, finally had to do some weirdness:
**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;
...and then call MyClassInstance.Destroy; to get the class to free without AV's nor mem leaks.
If anyone knows why I had 3 references to my class, why FreeAndNil(MyClassInstance) and IMyClass := Nil; leaves a refcount of 2 with an Access Violation and/or a memory leak, why I had to destroy the inherited first, why I had to typecast the class as an IUnknown and manually release the instances, please speak up.
On another interface/class pair I still get a mem leak from TServerEventDispatch. This is one of the classes that Delphi auto-created in the tlb.pas file. If anyone has a suggestion...
COM... ya' gotta love it!
I'd like to give any points to both Eric Brown and Remy Lebeau, but I don't know how.
So... THANKS GUYS!
这篇关于疑难解答COM错误“参数不正确”的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!