Delphi 接口版本控制

Delphi 接口版本控制,delphi,dll,interface,com,version,Delphi,Dll,Interface,Com,Version,在我工作的地方,我们刚刚发布了一个功能,它使用了一个严重依赖接口的dll。dll和所有客户端应用程序都是用Delphi编写的。不需要注册。此dll不是正确的com服务器。唯一的限制是,dll和客户端应用程序都可以访问包含接口的单元。这使我们能够在使用此dll的应用程序之间传递复杂数据,而无需借助记录指针、数组或非常胖的函数签名,也无需bpl或完全兼容的COM服务器带来的负担 看起来它解决了我们遇到的一个重大问题,没有任何不利因素。不幸的是,还有一个缺点。接口发布后的任何更改都需要随后重新编译该接

在我工作的地方,我们刚刚发布了一个功能,它使用了一个严重依赖接口的dll。dll和所有客户端应用程序都是用Delphi编写的。不需要注册。此dll不是正确的com服务器。唯一的限制是,dll和客户端应用程序都可以访问包含接口的单元。这使我们能够在使用此dll的应用程序之间传递复杂数据,而无需借助记录指针、数组或非常胖的函数签名,也无需bpl或完全兼容的COM服务器带来的负担

看起来它解决了我们遇到的一个重大问题,没有任何不利因素。不幸的是,还有一个缺点。接口发布后的任何更改都需要随后重新编译该接口的任何使用者。这对于所有属于同一发布周期的项目来说都很好,但是我们的一些项目有不同的发布时间表

我对此做了一些研究,看起来引入一个从以前发布的接口继承而来的新接口而不是修改原始接口是一种常见的做法

type
  IOriginalInterface = interface
  ['{8B598EC1-AD92-4144-A1BE-9062C5EA0748}']
    procedure DoSomething;
  end;

  INewInterface = interface(IOriginalInterface)
  ['{DD9D9DE0-0F87-4BC5-803C-74C8AB0F3E39}']
    procedure DoSomethingElse;
  end;
这确保了根据原始接口编译的旧可执行文件继续工作

我注意到,使用RAD Studio开放工具api,每当引入新接口时,接口都会被重命名。因此,最新接口不是给最新接口一个新名称,而是获得原始接口的名称,并重命名原始接口

type
  IOldInterface = interface
  ['{8B598EC1-AD92-4144-A1BE-9062C5EA0748}']
    procedure DoSomething;
  end;

  IOriginalInterface = interface(IOldInterface)
  ['{DD9D9DE0-0F87-4BC5-803C-74C8AB0F3E39}']
    procedure DoSomethingElse;
  end;
这显然对RAD Studio团队以及第三方扩展供应商都很有效。这可以确保任何重新编译的客户端都将使用最新的接口,而不需要任何代码更改。我认为这是可行的,因为名称是不相关的,在编译代码之后,剩下的就是接口定义,它是使用GUID解析的


话虽如此,这是解决我们现在面临的接口版本控制问题的好方法吗?还有其他问题需要注意吗?

让我解释一下您使用ToolsAPI观察到的情况背后的逻辑,然后您可以确定这一点如何适用于您的情况。你的理由很接近

对于插件和IDE扩展使用的接口,您对接口的版本控制和命名方式是正确的。其思想是现有代码将引用一个特定的接口名称,因为所有现有方法也存在于该接口上。由于旧代码无法引用任何新方法,因此在新接口中包含它们是安全的

type
  IOriginalInterface = interface
  ['{8B598EC1-AD92-4144-A1BE-9062C5EA0748}']
    procedure DoSomething;
  end;

  INewInterface = interface(IOriginalInterface)
  ['{DD9D9DE0-0F87-4BC5-803C-74C8AB0F3E39}']
    procedure DoSomethingElse;
  end;
但是,如果仔细观察,对于要由插件或IDE扩展实现的接口,您会发现情况正好相反。新接口得到新名称,所有旧的现有接口保持不变。这是因为,作为接口的实现者,您必须实现接口的所有方法。根据定义,现有代码不会实现新方法。当IDE需要调用用户实现的方法时,它总是通过查询引入该方法的接口的版本来进行调用,该版本可能不是接口的最新版本。因此,应该将所有祖先接口列为在实现类上实现的接口


总之,IDE的ToolsAPI的规则是,对于插件使用的接口,最新版本的接口总是使用未版本的名称。对于插件实现的接口,新接口总是有一个新名称。

我不是Delphi专家,而是纯COM的专家。重命名接口有点奇怪(创建一个派生的新接口而不更改原始接口是100%标准且非常常见的,如IHTMLDocumentX)。好的,是的,只有guid在部署时才是重要的,但是人类编写的源代码仍然使用名称而不是guid。另外一点需要说明的是,ToolsAPI在与您描述的场景非常不同的场景中运行。ToolsAPI会针对每个新版本的Delphi进行更新和修改,所有链接到它的包都必须重新编译。更重要的是,ToolsAPI支持插件体系结构,而据我所知,对于您的场景,您可以控制所有代码。因此,如果您确实需要进入DLL地狱,您不应该将ToolsAPI方法视为处理DLL地狱的模型。我理解这一点。但是您不需要所有不同的可执行文件来运行完全相同版本的DLL。还是你呢?我希望您不打算将DLL放入system32目录!不,DLL地狱是指一个DLL必须支持同一接口/功能的多个不同版本。这是一场考验人的噩梦。您的建议将所有计划的行为结合在一起。它们将相互依存。更改DLL以修复一个程序的问题可能会中断所有其他程序。您现在需要测试应用程序和DLL的所有可能排列。因此,如果您有10个应用程序,并且在一段时间后可能有10个不同版本的DLL,那么您需要测试100个不同的排列。这是DLL地狱。相比之下,将应用程序的每个新版本绑定到特定版本的DLL意味着将应用程序彼此隔离。您可以更改应用程序/DLL,而不会破坏其他应用程序之一。每次发布新版本的应用程序时,只需使用特定的DLL对其进行测试。