1

我有一个 Delphi 6 应用程序,它使用 ActiveX DLL 连接到另一个流行的应用程序(又名“主机”应用程序,因为没有更好的词)。主机应用程序提供集成 DLL。我没有它的源代码或对其进行任何控制。为了使用 DLL,我使用 IDE Import ActiveX Control工具创建了一个 TypeLib。

当宿主应用程序供应商创建新版本的 ActiveX DLL 时,就会出现问题。当我的用户升级到主机应用程序供应商的最新或 beta 版本时,我必须争先恐后地为他们提供我的程序的新版本。否则,由于旧 TypeLib 和新 DLL 之间的差异,当对集成 DLL 进行某些调用时,我的应用程序当然会崩溃。这也导致了维护我的应用程序的多个代码库以维护对仍然使用旧版本主机应用程序的用户的支持的负担。我试图避免大混乱的代码大修,我将集成 DLL 暴露的所有内容都包装起来,以便创建一个可以在运行时适应当前 DLL 版本的代码版本。

这就引出了我的问题。Delphi 生成的 TypeLib(s) 是一大堆 IDispatch 方法和属性。显然,Delphi 编译器在后台将这些转换为 IDispatch.Invoke() 调用。现在我可以在调用 CoCreate() 来创建 ActiveX 对象之前检测主机应用程序的当前版本。那么,有什么方法可以在运行时在两个 DLL TypeLib 定义之间切换?现在我通过编译时条件来完成它,其中包括基于我正在构建的主机应用程序版本的正确 TypeLib。我可以这样做是因为我保留了 TypeLib 的每个版本,并在供应商更新 DLL 时给它一个唯一的名称。但这并不能帮助我在运行时做到这一点。

我无法理解如何做到这一点,因为从定义的变量到方法调用的所有内容都基于编译时的 TypeLib。但是我想知道在 IDispatch 级别是否可以做一些聪明的事情来实现这一点?否则,我将坚持为每个公开的对象创建包装器,这些对象根据主机应用程序的当前版本调用正确的 TypeLib 方法/属性定义。这是一项艰巨的工作,也会导致一些非常复杂的代码。

面对同样的困境,你们是如何解决或应对的?

4

1 回答 1

3

实际的解决方案是使用后期绑定(使用获取引用CreateOleObject并在运行时解析引用)而不是通过类型库使用早期绑定。这意味着只要供应商不删除功能,您的代码将继续工作,无论安装了哪个版本的控件。

有关使用 MS Office 应用程序执行此操作的示例,您可以在Deborah Pate 的网站上看到一些旧的(但仍然准确且可用)的帖子(请参阅下面的注释)。例如,这个Word 使用后期绑定来检索当前正在运行的 Word 实例或创建一个新实例:

var 
  Word: Variant; 
  Filename: OleVariant;
begin 
  try 
    Word := GetActiveOleObject('Word.Application');    
  except 
    Word := CreateOleObject('Word.Application');    
  end; 

  FileName := 'C:\WordDocs\MyFile.doc';
  Word.Documents.Open(FileName, EmptyParam, EmptyParam, EmptyParam,
                      EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                      EmptyParam, EmptyParam);  
  Word.Visible := True;

请注意,这里没有包含类型库,也没有事先声明Word.DocumentsorDocuments.Open方法。这些都在运行时为您解决,如果未实施,将引发异常。

于 2012-12-25T05:46:11.890 回答