【问题标题】:How can I pass an instance of a .NET COM object from Delphi to another .NET COM object?如何将 .NET COM 对象的实例从 Delphi 传递到另一个 .NET COM 对象?
【发布时间】:2010-10-18 18:16:57
【问题描述】:

我有一个用 Delphi 7 编写的旧版应用程序。我们正在向该应用程序添加新模块。这些模块使用 Visual Studio 2010、.NET 4、C# 编写,并通过 COM 公开给应用程序。

我已经成功定义了一个类,注册了程序集,导出了类型库,将类型库导入Delphi,在Delphi中创建了COM客户端并执行了模块。现在到了棘手的部分:我想将 another 对象(如上所述已定义-registered-exported-blah-blah-blah)作为参数传递给第一个模块上的方法。 p>

.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 接口。

德尔福

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 .net-4.0 c#-4.0 com-interop delphi-7


    【解决方案1】:

    Delphi 中的部分问题是,对象可以实现接口,但它们本身并不是接口对象。 要理解这种区别,您必须查看接口的原始实现 在 Delphi 对象上,并了解 COM 使用的引用计数机制。另一件事 必须理解的是 .NET 的工作方式不同,所以看起来 .NET 中对象的接口与提供的 ComVisible 方面相同。

    TYPE
      TMyObject = class(TInterfacedObject, IMyObject, IMyNewInterface)
      end;
    

    鉴于上述情况,可以对这个对象进行以下说明。 您使用 Delphi COM 对象向导创建了一个新的 COM 对象。 Interface 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 中实现,所以您正在尝试 use 是一个 Delphi 代码包装器 - 它是一个 OBJECT,而不是一个 INTERFACE。
    您在包装器上注意到,没有第二种方法可以传递实例 的 IUserInfo 对象,那是因为这个 OBJECT 没有实现 INTERFACE, (您的 .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;
    

    除了实际使用 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 实现中添加了新方法,但没有t 重新注册(使用 COM)并重新导入新的程序集类型信息)。

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

    【讨论】:

      【解决方案2】:

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

      注意 CoClass 的第一个属性设置为 ClassInterfaceType.None,这样 DotNet 不会向类型库显示 CoClass 本身。不过,您可以像往常一样在 Delphi 中实例化您的对象:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-12-05
        • 2019-11-10
        • 2011-09-08
        • 2011-03-05
        • 1970-01-01
        • 2010-12-07
        • 2011-07-19
        • 2011-06-20
        相关资源
        最近更新 更多