如何将.NET COM对象的实例从Delphi传递到另一个.NET COM对象?
我有一个用Delphi7编写的遗留应用程序。我们正在向应用程序添加新模块。这些模块在Visual Studio 2010、.NET 4、C#中编写,并通过COM向应用程序公开 我已经成功地定义了一个类,注册了程序集,导出了类型库,将类型库导入到Delphi中,在Delphi中创建了COM客户端并执行了模块。现在是棘手的部分:我想把另一个对象(上面定义的)作为参数传递给第一个模块上的方法 .NET如何将.NET COM对象的实例从Delphi传递到另一个.NET COM对象?,delphi,.net-4.0,c#-4.0,com-interop,delphi-7,Delphi,.net 4.0,C# 4.0,Com Interop,Delphi 7,我有一个用Delphi7编写的遗留应用程序。我们正在向应用程序添加新模块。这些模块在Visual Studio 2010、.NET 4、C#中编写,并通过COM向应用程序公开 我已经成功地定义了一个类,注册了程序集,导出了类型库,将类型库导入到Delphi中,在Delphi中创建了COM客户端并执行了模块。现在是棘手的部分:我想把另一个对象(上面定义的)作为参数传递给第一个模块上的方法 .NET [ComVisible(true)] [InterfaceType(ComInterfaceType
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("11111111-1111-1111-1111-AAAAAAAAAAAA")]
public interface IUserMaintenance
{
bool AssignUser(IUserInfo);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
[Guid("11111111-1111-1111-1111-BBBBBBBBBBBB")]
public class UserMaintenance: IUserMaintenance
{
private IUserInfo _UserInfo;
public bool AssignUser(IUserInfo userInfo)
{
_UserInfo = userInfo;
LoadUser();
}
private void LoadUser()
{
//load user data from database using _UserInfo.UserName
}
}
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("22222222-2222-2222-2222-AAAAAAAAAAAA")]
public interface IUserInfo
{
void Initialize(string userName);
string UserName { get; }
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
[Guid("22222222-2222-2222-2222-BBBBBBBBBBBB")]
public class UserInfo: IUserInfo
{
public string UserName { get; private set };
public void Initialize(string userName)
{
UserName = userName;
}
}
假设我有单独的类实现每个接口,并且包含这些类的程序集成功编译和注册,我将类型库导入Delphi,创建UserMaintenance_TLB.pas和UserInfo_TLB.pas。然而,我看到了一些意想不到的事情:当我在.NET中定义的接口存在时(以“I”开头的接口),Delphi已经生成了另一组接口(以“_”开头的接口)。Delphi根本不使用我在.NET中声明的I接口
Delphi
UserMaintenance_TLB.pas
// *********************************************************************//
// Forward declaration of types defined in TypeLibrary
// *********************************************************************//
IUserMaintenance = interface;
IUserMaintenanceDisp = dispinterface;
_UserMaintenance = interface;
_UserMaintenanceDisp = dispinterface;
UserInfo_TLB.pas
// *********************************************************************//
// Forward declaration of types defined in TypeLibrary
// *********************************************************************//
IUserInfo = interface;
IUserInfoDisp = dispinterface;
_UserInfo = interface;
_UserInfoDisp = dispinterface;
Delphi还创建了相应的Delphi类型:
// *********************************************************************//
// OLE Server Proxy class declaration
// Server Object : TUserMaintenance
// Help String :
// Default Interface: _UserMaintenance
// Def. Intf. DISP? : No
// Event Interface:
// TypeFlags : (2) CanCreate
// *********************************************************************//
TUserMaintenance = class(TOleServer)
private
FIntf: _UserMaintenance;
function GetDefaultInterface: _UserMaintenance;
protected
procedure InitServerData; override;
function Get_ToString: WideString;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Connect; override;
procedure ConnectTo(svrIntf: _UserMaintenance);
procedure Disconnect; override;
function Equals(obj: OleVariant): WordBool;
function GetHashCode: Integer;
function GetType: _Type;
WordBool AssignUser(IUserInfo userInfo);
property DefaultInterface: _UserMaintenance read GetDefaultInterface;
property ToString: WideString read Get_ToString;
published
end;
我想做的是创建一个TUserMaintenance实例,然后将一个TUserInfo实例传递给它。然而,有两件事是显而易见的:Delphi类TUserInfo没有实现IUserInfo,DefaultInterface是Delphi生成的新接口_UserInfo。我无法将UserInfo变量声明为IUserInfo类型,因为TUserInfo未实现该接口。我也不能将其声明为类型_UserInfo,因为UserMaintenance.LoadUser需要一个IUserInfo实例
诚然,这是我实际问题的一个非常简单的例子,但我认为它充分说明了这个问题
所以我的问题是:我有没有办法强制Delphi中的接口类型与.NET中声明的接口保持一致?或者有没有其他方法可以将UserInfo实例传递给UserMaintenance?问题的一部分在于,在Delphi中,对象可以实现接口,但它们本身不在接口对象中。 要理解这一区别,您必须查看接口的原始实现 在Delphi对象上,了解COM使用的引用计数机制。还有一件事 必须理解的是.NET的工作方式不同,所以看起来是 NET中对象的接口与显示的ComVisible方面相同
TYPE
TMyObject = class(TInterfacedObject, IMyObject, IMyNewInterface)
end;
鉴于上述情况,可以对这个对象说以下几句话。
您使用Delphi COM对象向导创建了一个新的COM对象。
接口IMyObject被定义为默认接口,TMyObject是具体类
这将实现该接口。
IMyNewInterface是在其他地方定义的辅助接口,您已经指出了您的
对象实现
可以使用此对象执行以下操作
var
I1: IMyObject;
I2: IMyNewInterface;
T: TMyObject;
begin
I1:=TMyObject.Create;
I2:=TMyObject.Create;
T:=TMyObject.Create;
end;
您可以执行这些操作,因为TMyObject实现了这些接口。也,
因为这些都是引用计数的,所以您不必在需要时从内存中释放它们
方法作用域结束了,对象的生存期也结束了——最后一个除外
因为您使用的是对象引用而不是接口引用,所以必须释放
您创建的对象
考虑到你发布的代码,如果你仔细观察,你会发现同样的情况。
在您的例子中,您的对象完全是在.NET中实现的,所以您要做的是
use是一个Delphi代码包装器,它是一个对象,而不是一个接口。您注意到包装器上没有第二个方法来传递实例 IUserInfo对象的,这是因为该对象未实现接口, (您的.NET对象就是为此而创建的)-在Delphi中需要做的是“选择该接口” 您可以通过执行以下操作来完成该任务: 如果已经执行了TUserMaintenance.Create(nil)调用并拥有该对象的实例, 获取默认接口,然后将其“强制”到适当的实现接口
var
UMDelphiWrapper: TUserMaintenance;
UIDelphiWrapper: TUserInfo;
UI: IUserInfo;
UM: IUserMaintenance;
begin
//this part creates a Delphi OBJECT reference to the Implementation class -
//this is NOT Reference counted, because it doesn't implement an interface.
UIDelphiWrapper:=TUserInfo.Create(nil);
try
//this is the part where you acquire the interface of the object that actually
//implementes this interface - e.g. your .NET class
//not that this is the INTERFACE reference - which WILL be reference counted
UI:=UIDelphiWrapper.DefaultInterface as IUserInfo;
//UI.<Set some properties of your IUserInfo object>
try
//this part creates a Delphi OBJECT reference to the Implementation class -
//this is NOT Reference counted, because it doesn't implement an interface.
UMDelhpiWrapper:=TUserMaintenance.Create(nil);
try
//this is the part where you acquire the interface of the object that actually
//implementes this interface - e.g. your .NET class
//not that this is the INTERFACE reference - which WILL be reference counted
UM:=UMdelphiWrapper.DefaultInterface as IUserMaintenance;
try
//Here, you have an interface type implemented by your .NET class that you are
//sending to the implementation of your management object (Also a .NET class)
UM.SendUser(UI);
//do whatever else you need to do with your interface and user/management .NET object(s)
finally
//this IS a reference counted COM object - no "free" necessary
//this would naturally happen when the reference goes out of scope in the method
//but for clairity sake is set to NIL to explicitly release your reference
UM:=nil;
end;
finally
//This is a delphi object that is NOT reference counted and must be released
FreeAndNil(UMDelphiWrapper);
end;
finally
//this IS a reference counted COM object - no "free" necessary
//this would naturally happen when the reference goes out of scope in the method
//but for clairity sake is set to NIL to explicitly release your reference
UI:=nil;
end;
Finally
//This is a delphi object that is NOT reference counted and must be released
FreeAndNIl(UIDelphiWrapper);
end;
这段代码要干净得多-但是,您必须仍然拥有IUserInfo和IUserMaintenance的准确定义,并且知道对象在COM中注册时的类友好名称
您可以自己通过键入代码来完成此操作-当您从程序集导入COM公开的DLL时,类型导入函数应该为您完成此操作。我在您提供的代码中没有看到实际的实现,但是您应该仍然可以在头文件中找到这些信息。-如果没有,则说明您没有导入正确的程序集,或者Delphi 7导入功能不正确-或者需要刷新(例如,您向.NET实现中添加了新方法,但没有重新注册(使用COM)并重新导入新的程序集类型信息)
希望我能正确理解你的问题。如果您想消除typelibrary中令人困惑的(和不必要的)用户信息,您不应该导出CoClass,而应该只导出接口
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("22222222-2222-2222-2222-AAAAAAAAAAAA")]
public interface IUserInfo
{
}
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[Guid("22222222-2222-2222-2222-BBBBBBBBBBBB")]
public class UserInfo: IUserInfo
{
}
注意CoClass的第一个属性被设置为ClassInterfaceType.None,这样DotNet就不会向typelibrary显示CoClass本身。不过,您可以像往常一样在Delphi中实例化对象:
var
pUserInfo: IUserInfo;
begin
pUserInfo := CoUserInfo.Create;
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("22222222-2222-2222-2222-AAAAAAAAAAAA")]
public interface IUserInfo
{
}
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[Guid("22222222-2222-2222-2222-BBBBBBBBBBBB")]
public class UserInfo: IUserInfo
{
}
var
pUserInfo: IUserInfo;
begin
pUserInfo := CoUserInfo.Create;