2010-10-18 101 views
1

我有一个用Delphi 7编写的遗留应用程序。我们正在向应用程序添加新模块。这些模块是用Visual Studio 2010,.NET 4,C#编写的,并通过COM公开给应用程序。如何将Delphi的.NET COM对象的实例传递给另一个.NET COM对象?

我成功地定义了一个类,注册了程序集,导出了类型库,将类型库导入到Delphi中,在Delphi中创建了COM客户端并执行了该模块。现在出现了棘手的部分:我想通过另一个对象(已被定义 - 注册 - 输出 - 等等 - 等等 - 如上所述)作为第一个模块上的方法的参数。

.NET

[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生成了另一组接口(以“_”开头)。德尔福确实而不是使用我在.NET中声明的I接口。

德尔福

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; 

德尔福也创造了相应的德尔福类型:

// *********************************************************************// 
// 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的方法?

回答

2

问题的一部分,在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实现这些接口你可以做这些事以下。另外,因为这些是引用计数,所以您不必从内存中释放它们,但当方法范围结束时,对象的生命周期也会结束,因此对象的生命周期也是如此 - 因为您使用的是OBJECT REFERENCE而不是一个INTERFACE REFERENCE,你必须释放你创建的OBJECT。

鉴于您发布的代码,如果仔细观察,您实际上会发现相同的情况。 在你的情况下,你的对象完全在.NET中实现,所以你试图使用的是Delphi代码包装器 - 这是一个对象,而不是一个接口。
您注意到在包装上,没有第二种方法将IUserInfo对象的实例 传递给此对象,那是因为此OBJECT没有实现INTERFACE,因此(您的.NET对象被创建为这样做) - 你需要在Delphi做的是“选择界面”

你会做完成这个任务执行以下操作:

如果你已经做了TUserMaintenance.Create(无)呼叫和者的一个实例对象, 得到默认的接口,然后“铸造”到适当的实施接口

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; 

除了实际使用Delphi提供的包装器外,只要知道正确的信息,您就可以直接创建对.NET对象的引用。

var 
    UI: IUserInfo; 
    UM: IUserManager; 

begin 
    UI:=CreateOleObject('YourAssembly.YourImplementationClass') as IUserInfo; 
    UI.SomeProperty:=SomeValue; 
    UM:=CreateOleObject('YourAssembly.YourImplementationClass') as IUserManger; 
    UM.SomeMetohd(UI); 
end; 

此代码干净多了 - 但是,你还必须有IUserInfo和IUserMaintenance的准确定义,以及了解你的对象的类友好名称,因为他们已经与COM注册。

您可以通过输入代码来完成此操作 - 这是在从程序集导入COM公开的DLL时应该为您执行的类型导入函数。我没有在您提供的代码中看到实际的实现,但是您仍然应该在头文件中找到这些信息。 - 如果你不这样做,那么你没有导入正确的程序集,或者Delphi 7导入没有正确运行 - 或者它需要刷新(例如,你为你的.NET实现添加了新的方法,重新注册(使用COM)并重新导入新的装配类型信息)。

type 
    IUserInfo = interface 
    ['22222222-2222-2222-2222-AAAAAAAAAAAA'] 
    //define your methods 
    end; 
1

希望我能正确理解你的问题。如果你想摆脱类型库中令人困惑的(和不必要的)_UserInfo,你不应该导出你的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 
{ 
} 

注组件类的第一属性被设置为ClassInterfaceType.None,这样DOTNET的本身不会透露伴生类的类型库。尽管如此,您仍然可以像Delphi一样在Delphi中实例化对象:

var 
    pUserInfo: IUserInfo; 
begin 
    pUserInfo := CoUserInfo.Create;