Has anyone else noticed this behaviour in Delphi using QueryInterface?

349 views Asked by At

Here are my types...

unit unitTestInterfaces;

interface
type
  IFoo = interface
    ['{169AF568-4568-429A-A8F6-C69F4BBCC6F0}']
    function TestFoo1:string;
    function TestFoo:string;
  end;

  IBah = interface
    ['{C03E4E20-2D13-45E5-BBC6-9FDE12116F95}']
    function TestBah:string;
    function TestBah1:string;
  end;

  TFooBah = class(TInterfacedObject, IFoo, IBah)
    //IFoo
    function TestFoo1:string;
    function TestFoo:string;

    //IBah
    function TestBah1:string;
    function TestBah:string;
  end;

implementation

{ TFooBah }

function TFooBah.TestBah: string;
begin
  Result := 'TestBah';
end;

function TFooBah.TestBah1: string;
begin
  Result := 'TestBah1';
end;

function TFooBah.TestFoo: string;
begin
  Result := 'TestFoo';
end;

function TFooBah.TestFoo1: string;
begin
  Result := 'TestFoo1';
end;

end.

And here is my code to run the example...

var
  fb:TFooBah;
  f:IFoo;
  b:IBah;
begin
  try
    fb := TFooBah.Create;

    /// Notice we've used IBah here instead of IFoo, our writeln() still outputs the
    /// result from the TestBah() function, presumably because it's the "first" method
    /// in the IBah interface, just as TestFoo1() is the "first" method in the IFoo
    /// interface.
    (fb as IUnknown).QueryInterface(IBah,f); //So why bother with this way at all??
    //f := fb as IBah; //causes compile error
    //f := fb; //works as expected
    if Assigned(f)then
    begin
      writeln(f.TestFoo1); //wouldn't expect this to work since "f" holds reference to IBah, which doesn't have TestFoo1()
    end;

    (fb as IUnknown).QueryInterface(IBah,b);
    if Assigned(f) then
    begin
      writeln(b.TestBah1);
    end;

  except on E:Exception do
    writeln(E.Message);
  end;
end.

It seems that in the first call to QueryInterface, even though we are assigning the wrong type of interface to the "f" variable, it will still try to execute the 'first' method of whatever it's pointing to, as opposed to the method with the name "TestFoo1". Using f := fb works as expected, so is there a reason we would ever use QueryInterface instead of the syntax f := fb?

3

There are 3 answers

1
Uwe Raabe On

I guess you are breaking the rules here:

QueryInterface will put the interface into f which you requested. You are responsible that f is of the appropriate type. As the second parameter is untyped the compiler cannot warn you about your fault.

0
Ritsaert Hornstra On

Please note that f := Fb as IFoo, the call Supports( Fb, IFoo ) etc tec all call QueryInterface in the background. So the QueryInterface method is used but you get the nice syntax with autocasting, is, as and methods like support.

0
Muhammad Alkarouri On

I would argue that the better syntax is neither the QueryInterface one nor the f := fb one. It is the one you commented out:

f := fb as IBah; //causes compile error

and that is precisely because it has type checking, which covers the problem with QueryInterface that it doesn't check its arguments.