如何将.NET COM对象的实例从Delphi传递到另一个.NET COM对象?

如何将.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

我有一个用Delphi7编写的遗留应用程序。我们正在向应用程序添加新模块。这些模块在Visual Studio 2010、.NET 4、C#中编写,并通过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已经生成了另一组接口(以“_”开头的接口)。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;