我曾经认为 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;
我在这里感到完全迷失了。
我曾经认为
dispinterface
是 interface
的“子类”,就像 TPersistent
到 TObject
是?如果是,那么如何在同一个项目中具有相同 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
。