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

标签 delphi .net-4.0 c#-4.0 com-interop delphi-7

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

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

.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;
    }
}

假设我有实现每个接口(interface)的单独类,并且包含这些类的程序集成功编译和注册,我将类型库导入 Delphi,创建 UserMaintenance_TLB.pas 和 UserInfo_TLB.pas。然而,我看到了一些意想不到的东西:虽然我在 .NET 中定义的接口(interface)存在(以“I”开头的接口(interface)),但 Delphi 生成了另一组接口(interface)(以“_”开头的接口(interface))。 Delphi 根本不使用我在 .NET 中声明的 I 接口(interface)。

德尔福
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 生成的新接口(interface) _UserInfo。我不能将我的 UserInfo 变量声明为 IUserInfo 类型,因为 TUserInfo 没有实现该接口(interface)。我也不能将其声明为 _UserInfo 类型,因为 UserMaintenance.LoadUser 需要一个 IUserInfo 实例。

诚然,这是我实际问题的一个非常简化的示例,但我认为它充分说明了问题。

所以我的问题是:有什么方法可以强制 Delphi 中的接口(interface)类型与 .NET 中声明的接口(interface)保持一致?还是有另一种方法可以将 UserInfo 的实例传递给 UserMaintenance?

最佳答案

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

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

鉴于上述情况,可以对这个对象说以下事情。
您使用 Delphi COM 对象向导创建了一个新的 COM 对象。
Interface IMyObject 被定义为默认接口(interface),而 TMyObject 是具体类
这将实现该接口(interface)。
IMyNewInterface 是在其他地方定义的辅助接口(interface),您已表明您的
对象实现。

您可以使用此对象执行以下操作
 var
   I1: IMyObject;
   I2: IMyNewInterface;
   T: TMyObject;
 begin
   I1:=TMyObject.Create;
   I2:=TMyObject.Create;
   T:=TMyObject.Create;
 end;

您可以做这些事情,因为 TMyObject 实现了这些接口(interface)。还,
因为这些是引用计数的,所以你不必从内存中释放它们,当
方法范围结束,对象的生命周期也结束 - 除了最后一个
因为您使用的是 OBJECT REFERENCE 而不是 INTERFACE REFERENCE,所以您必须释放
您创建的对象。

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

您将通过执行以下操作来完成该任务:

如果您已经完成了 TUserMaintenance.Create(nil) 调用并拥有该对象的实例,
获取默认接口(interface),然后将其“强制转换”为适当的实现接口(interface)
 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;

关于delphi - 如何将 .NET COM 对象的实例从 Delphi 传递到另一个 .NET COM 对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3962096/

相关文章:

.net - 如何正确访问 C++/CLI initonly TimeSpan 字段?

delphi - FMX : dropping selfmade component on form duplicates subcomponents

delphi - 当磁盘可用空间发生变化时如何收到通知?

delphi - TDBCtrlGrid 是通用数据感知组件吗?

c# - ObjectQuery<T> 或 IQueryable<T>

c# - 代码分析警告CA1506 : "Avoid excessive class coupling"如何解决

c# - 是否使用 "Optional, DefaultParameterValue"属性?

c#-4.0 - 从 AuthorizationFilterAttribute 访问全局媒体类型格式化程序时 MissingMethodException

c# - 你如何使用出生日期计算 C# 中的年龄(考虑闰年)

delphi - Delphi 是否存在无锁队列 "multiple producers-single consumer"?