C# 应该如何实现delphi COM类型库中定义的方法,以便它可以返回另一个COM对象?
我正在从现有的Delphi遗留代码开发一个COM Dll,以便在C.NET中使用。我已经向类型库添加了适当的接口,COM服务器已经成功注册,并且可以从C环境中查看和创建对象。 但是,一些现有的类具有定制的构造函数,这是必需的。因此,我还添加了一个Helper类作为COM对象,它包含应该构造和返回其他COM对象的方法,但返回类型与创建的对象不兼容,程序崩溃 我正在使用COM对象向导,代码几乎是由Delphi本身生成的。例如,通过定义ISomeObject接口,将生成其相应的名为SomeObject的CoClass和Delphi类TSomeObject。TSomeObject实现ISomeObject包括属性和方法的实现。我的意图是使用Helper对象创建一个TSomeObject实例,并在C环境中使用它 创建辅助对象及其方法的C代码如下所示:C# 应该如何实现delphi COM类型库中定义的方法,以便它可以返回另一个COM对象?,c#,delphi,com,C#,Delphi,Com,我正在从现有的Delphi遗留代码开发一个COM Dll,以便在C.NET中使用。我已经向类型库添加了适当的接口,COM服务器已经成功注册,并且可以从C环境中查看和创建对象。 但是,一些现有的类具有定制的构造函数,这是必需的。因此,我还添加了一个Helper类作为COM对象,它包含应该构造和返回其他COM对象的方法,但返回类型与创建的对象不兼容,程序崩溃 我正在使用COM对象向导,代码几乎是由Delphi本身生成的。例如,通过定义ISomeObject接口,将生成其相应的名为SomeObject
Helper helper = new Helper;
SomeObject = helper.CreateSomeObject(Param1, Param2);
当我将返回类型设置为SomeObject的方法添加到类型库中的IHelper接口时,将生成以下代码减去正文
函数THelper.CreateSomeObjectParam1,Param2:SomeObject
开始
结果:=TSomeProject.CreateParam1,Param2//此行不是由COM对象向导生成的
终止
由于不兼容,上面的代码崩溃并出现错误
在调试时,我意识到结果的类型是指针作为对象。我尝试将TSomeProject.Create的输出类型转换为使用as操作数的SomeProject,但无效
问题是如何通过返回类型为SomeObject的方法返回TSomeObject的实例。更改方法签名以返回接口而不是SomeObject类的实例。您可以在类型库编辑器中这样做: 生成的代码将是:
function THelper.CreateSomeObject: ISomeObject;
begin
end;
编辑1
根据评论:尽管你在问题中提供了很多文本,但仍然缺少一些基本信息。您已经提到了一些指针操作,但在开发COM服务器的典型场景中,您不应该处理这些操作
所以我试着自己在我安装的Delphi7最旧的Delphi版本中重新创建您的场景。我在Delphi中创建了一个与上面类似的COM服务器ActiveX库项目。我对CreateSomeObject方法的实现是:
方法TSomeObject.HelloWorld的实现并不重要。然后我通过IDE函数Run>RegisterActiveX服务器注册了服务器。之后,我在Delphi中创建了示例控制台应用程序,导入类型库项目>导入类型库,并向主程序添加了几行代码:
uses
ActiveX, COMTest_TLB;
var
_Helper: IHelper;
_SomeObject: ISomeObject;
begin
CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
_Helper := CoHelper.Create;
_SomeObject := _Helper.CreateSomeObject;
_SomeObject.HelloWorld;
end.
控制台应用程序运行完成,未出现崩溃或意外结果。到现在为止,一直都还不错。然后,我参考COMTest库创建了示例C.NET控制台应用程序.NET 4.5.2:
使用制度;
使用COMTest;
班级计划
{
[状态线程]
静态环[]args
{
var helper=新的helper;
var someObject=helper.CreateSomeObject;
someObject.HelloWorld;
}
}
实际上,应用程序因AccessViolationException而崩溃。通过将主机应用程序设置为.NET控制台应用程序并在项目的链接器选项中启用远程调试符号,我可以快速设置COM服务器的调试。我相信您已经了解了这一点。TSomeObject实例的创建进展顺利,但分配给Result的操作失败
在本例中,当为托管类型接口的变量赋值时,有一些编译器的魔力。它首先清除目的地,如果目的地不是nil,它基本上是对_Release的调用。令我惊讶的是,在.NET控制台应用程序客户端的情况下,它不是!因此,我修改了实施方案,以:
function THelper.CreateSomeObject: ISomeObject;
begin
Pointer(Result) := nil;
Result := TSomeObject.Create;
end;
在第一次将结果用作接口之前清除结果就完成了这项任务。我还没有深入了解为什么会发生这种情况,但我肯定会这样做。我还将使用更新的Delphi版本进行检查,并在这个答案的另一个编辑中发布我的发现
在这里,您可以在Delphi中找到一些与COM开发相关的优秀资源。
免责声明:我和那个网站没有任何关系,我只是觉得它非常有用
编辑2
COM接口方法应按约定返回HRESULT。这是COM中报告错误的默认机制。从方法返回附加值应该通过带有[Out]修饰符的参数来实现。或者,一个参数可以用[Out,RetVal]修饰符标记,通常是最后一个用来指示方法返回值的修饰符。请注意,[Out]参数是通过引用传递的,您必须在类型库编辑器中将附加星号*符号附加到类型名称后进行指示。因此,ISomeObject*变为[Out]ISomeObject**
Delphi支持safecall调用约定,因此它可以通过在方法中包装任何未捕获的异常并允许[Out,RetVal]参数成为返回值,从而从方法签名中消除HRESULT返回值。但这仅适用于双接口。请参见从IDispatch派生的接口的“标志”选项卡。为了避免实现IDispatch方法,您可以将轻量级COM对象转换为automation对象TAutoObject,如果您还没有这样做的话。在向ActiveX库添加新项时,可以通过选择“自动化对象”选项而不是“COM对象”来实现这一点
这就是转换为safecall时方法定义的样子:
下面是生成的代码,它的实现非常简单:
type
THelper = class(TAutoObject, IHelper)
protected
function CreateSomeObject: ISomeObject; safecall;
end;
function THelper.CreateSomeObject: ISomeObject;
begin
Result := TSomeObject.Create;
end;
谢谢@Peter Wolf的回复,但我已经尝试过了,它不起作用。不起作用不是一个有用的问题描述。亲爱的@Peter,谢谢你的详尽回答,谢谢你为模拟这个问题所付出的一切努力。我真的很感激。你的解决方案完全有效!我不熟悉自动化对象,但我一定会尝试一下。
type
THelper = class(TAutoObject, IHelper)
protected
function CreateSomeObject: ISomeObject; safecall;
end;
function THelper.CreateSomeObject: ISomeObject;
begin
Result := TSomeObject.Create;
end;