Question

I used to think that type safety in Delphi with regard to Interfaces is maintained by setting a unique (optional, but unique if filled in) GUID to it.

Then there came that question: Unspecified error when calling Word CentimetersToPoints via OLE
Little follow-up on it: http://pastebin.ca/2369858

And i started looking in stock Delphi TWordApplication component (namely Word200.pas unit). And there i see:

// *********************************************************************//
// 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;

or similar:

// *********************************************************************//
// 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;

And i feel totally lost here.

I used to think that dispinterface is "subclass" of interface like TPersistent to TObject is ? If yes, then how can be two interfaces with same GUID in same project ?

Or are they from different unrelated frameworks, like Delphi new class types to inherited TurboPascal object types ? Neither _GlobalDisp nor _ApplicationDisp seems to be used in Word200.pas so are they just like appendix, auto-imported but never actually used ?

I made the project, using both _Application and _ApplicationDisp and it compiles. But then i only wonder how does Delphi typcast it, if they have the SAME 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;
Was it helpful?

Solution

The dispinterface is really just a convenient way to use IDispatch for an automation interface. That's why they have the same GUID – they are exactly the same thing behind the scenes.

When you use IDispatch to invoke a method you typically have to call GetIdsOfNames to obtain the dispatch ID for your method. But since these are static, you can save time by skipping that step, if you know the dispatch ID. And that's what a dispinterface allows you to do.

When you call a method on a dispinterface you still end up calling Invoke on the IDispatch, but you skip the call to GetIdsOfNames.

When you use QueryInterface with an interface, you'll get the IDispatch. You can then cast it to its corresponding dispinterface. It's still the same interface, but when you invoke methods on the dispinterface you'll save that call to GetIdsOfNames.

So, if you have an IDispatch for the Word application object, say, you can write code like this:

var
  WordApp: Variant;
  WordDisp: _ApplicationDisp;
....
WordApp := CreateOleObject('Word.Application');
WordDisp := _ApplicationDisp(IDispatch(WordApp));

The _ApplicationDisp() cast is nothing more than a call to IntfCopy. Which in turn is nothing more than a call to _AddRef. And you can then write:

Writeln(WordApp.ProductCode);
Writeln(WordDisp.ProductCode);

Both produce the same output. The former first calls GetIdsOfNames before calling Invoke. The latter goes straight to Invoke.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top