我曾经认为Delphi中关于Interfaces的类型安全性是通过设置一个唯一的(可选的,但唯一的,如果填充的)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的两个接口?
或者他们来自不同的不相关框架,比如继承TurboPascal class
类型的Delphi新object
类型? _GlobalDisp
似乎没有使用_ApplicationDisp
和Word200.pas
,所以它们就像附录,自动导入但从未实际使用过吗?
我使用_Application
和_ApplicationDisp
制作了该项目并进行了编译。但后来我只想知道如果他们有相同的指南,Delphi如何打字呢?
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;
答案 0 :(得分:3)
dispinterface
实际上只是将IDispatch
用于自动化界面的便捷方式。这就是他们拥有相同GUID的原因 - 它们在幕后完全相同。
使用IDispatch
调用方法时,通常需要调用GetIdsOfNames
来获取方法的调度ID。但由于这些是静态的,如果您知道调度ID,则可以通过跳过该步骤来节省时间。这就是dispinterface
允许你做的事情。
当您在dispinterface
上拨打方法时,您仍然会在Invoke
上拨打IDispatch
,但是您跳过了GetIdsOfNames
的来电。
当您在界面中使用QueryInterface
时,您将获得IDispatch
。然后,您可以将其强制转换为相应的dispinterface
。它仍然是相同的界面,但是当你在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
。