我曾经认为 Delphi 中关于接口(interface)的类型安全是通过为其设置唯一的(可选,但如果填写则是唯一的)GUID 来维护的。

那么问题来了:Unspecified error when calling Word CentimetersToPoints via OLE
很少跟进:http://pastebin.ca/2369858

我开始寻找库存 Delphi TWordApplication 组件(即 Word200.pas 单元)。在那里我看到:

// *********************************************************************//
// Interface: _Application
// Flags:     (4560) Hidden Dual NonExtensible OleAutomation Dispatchable
// GUID:      {00020970-0000-0000-C000-000000000046}
// *********************************************************************//
  _Application = interface(IDispatch)
    ['{00020970-0000-0000-C000-000000000046}']
...
    function CentimetersToPoints(Centimeters: Single): Single; safecall;



// *********************************************************************//
// DispIntf:  _ApplicationDisp
// Flags:     (4560) Hidden Dual NonExtensible OleAutomation Dispatchable
// GUID:      {00020970-0000-0000-C000-000000000046}
// *********************************************************************//
  _ApplicationDisp = dispinterface
    ['{00020970-0000-0000-C000-000000000046}']
...
    function CentimetersToPoints(Centimeters: Single): Single; dispid 371;

或类似:
// *********************************************************************//
// Interface: _Global
// Flags:     (4560) Hidden Dual NonExtensible OleAutomation Dispatchable
// GUID:      {000209B9-0000-0000-C000-000000000046}
// *********************************************************************//
  _Global = interface(IDispatch)
    ['{000209B9-0000-0000-C000-000000000046}']
...
    function CentimetersToPoints(Centimeters: Single): Single; safecall;

// *********************************************************************//
// DispIntf:  _GlobalDisp
// Flags:     (4560) Hidden Dual NonExtensible OleAutomation Dispatchable
// GUID:      {000209B9-0000-0000-C000-000000000046}
// *********************************************************************//
  _GlobalDisp = dispinterface
    ['{000209B9-0000-0000-C000-000000000046}']
...
    function CentimetersToPoints(Centimeters: Single): Single; dispid 371;

我在这里感到完全迷失了。

我曾经认为 dispinterfaceinterface 的“子类”,就像 TPersistentTObject 是?如果是,那么如何在同一个项目中具有相同 GUID 的两个接口(interface)?

或者它们来自不同的不相关框架,比如 Delphi 新的 class 类型到继承的 TurboPascal object 类型? _GlobalDisp_ApplicationDisp 似乎都没有在 Word200.pas 中使用,所以它们就像附录一样,自动导入但从未实际使用过吗?

我制作了这个项目,同时使用 _Application_ApplicationDisp 并编译。但是我只是想知道 Delphi 如何 typcast 它,如果他们有相同的 GUID ?
procedure TForm4.Button1Click(Sender: TObject);
 procedure show(const s: Single);
 begin
   ShowMessage(FloatToStr(s));
 end;
begin
  show( WordApplication1.CentimetersToPoints(1.0) );
  show( WordApplication1.Application.CentimetersToPoints(2.0) );
  show( WordApplication1.DefaultInterface.CentimetersToPoints(3.0) );
  show( _ApplicationDisp(WordApplication1.Application).CentimetersToPoints(4.0) );
  show( (WordApplication1.DefaultInterface as _ApplicationDisp).CentimetersToPoints(5.0) );
end;

最佳答案

dispinterface 实际上只是将 IDispatch 用于自动化接口(interface)的一种便捷方式。这就是为什么它们具有相同的 GUID – 它们在幕后完全相同。

当您使用 IDispatch 调用一个方法时,您通常必须调用 GetIdsOfNames 来获取您的方法的调度 ID。但由于这些是静态的,如果您知道调度 ID,您可以跳过该步骤来节省时间。这就是 dispinterface 允许您执行的操作。

当您在 dispinterface 上调用方法时,您最终仍然会在 Invoke 上调用 IDispatch ,但您会跳过对 GetIdsOfNames 的调用。

当您将 QueryInterface 与接口(interface)一起使用时,您将获得 IDispatch 。然后,您可以将其转换为相应的 dispinterface 。它仍然是相同的接口(interface),但是当您调用 dispinterface 上的方法时,您会将该调用保存到 GetIdsOfNames

因此,如果您有 Word 应用程序对象的 IDispatch,例如,您可以编写如下代码:

var
  WordApp: Variant;
  WordDisp: _ApplicationDisp;
....
WordApp := CreateOleObject('Word.Application');
WordDisp := _ApplicationDisp(IDispatch(WordApp));
_ApplicationDisp() 转换只不过是对 IntfCopy 的调用。反过来,这只不过是对 _AddRef 的调用。然后你可以写:
Writeln(WordApp.ProductCode);
Writeln(WordDisp.ProductCode);

两者产生相同的输出。前者在调用 GetIdsOfNames 之前首先调用 Invoke 。后者直接进入 Invoke

10-08 04:49