Почему нельзя передать интерфейс Marshaled как целое число (или указатель) - PullRequest
0 голосов
/ 18 марта 2010

Я передал ref интерфейса из надстроек Visio в MyCOMServer ( Выделение интерфейса в Delphi необходимо передать интерфейс в качестве указателя во внутреннем методе MyCOMServer. Я пытаюсь передать интерфейс во внутренний метод в качестве указателя интерфейса , но после обратного приведения при попытке вызвать метод интерфейса я получаю исключение. Простой пример (блок Fisrt выполняется без ошибки, но во втором блоке я получаю исключение после обращения к свойству интерфейса IVApplication):

procedure TMyCOMServer.test(const Interface_:IDispatch); stdcall;
var 
  IMy:_IMyInterface;
  V: Variant;
  Str: String;
  I: integer;
  Vis: IVApplication;
begin 

  ......
  {First code Block}
  Self.QuaryInterface(_IMyInterface,IMy);
  str := IMy.ApplicationName;
  V := Integer(IMy);
  i := V;
  Pointer(IMy) :=  Pointer(i);
  str := IMy.SomeProperty; // normal completion

  {Second code Block}
  str := (Interface_ as IVApplication).Path;
  V := Interface_;
  I := V;
  Pointer(Vis) :=  Pointer(i);
  str := Vis.Path;  // 'access violation at 0x76358e29: read of address 0xfeeefeee' 

end;

Почему я не могу так поступить?

Ответы [ 2 ]

1 голос
/ 18 марта 2010

Когда у вас есть объект, который реализует несколько интерфейсов, и вы преобразуете между ними, вы получите разные адреса. Это связано с тем, как найти методы этих интерфейсов.

Допустим, у вас есть два интерфейса и класс, который их реализует, методы показывают только сообщение с именем метода:

type
  IMyIntfA = interface
    ['{21ADE2EF-55BB-4B78-A23F-9BB92BE55683}']
    procedure A;
    procedure X;
  end;

  IMyIntfB = interface
    ['{7E1B90CF-569B-4DD1-8E46-7E7255D2373A}']
    procedure B;
  end;

  TMyObject = class(TInterfacedObject, IMyIntfA, IMyIntfB, IUnknown)
  public
    procedure A;
    procedure X;
    procedure B;
  end;

Когда вы говорите компилятору вызывать A из IMyIntfA, он знает, что A находится по адресу IMyIntfA плюс смещение. То же самое относится и к вызову метода B из IMyIntfB. Но вы делаете, что помещаете ссылку на IMyIntfB в переменную IMyIntfA, а затем вызываете метод A. В результате адрес метода, который вычисляет компилятор, совершенно неверен.

var
  lIntfA: IMyInterfaceA;
  lIntfB: IMyInterfaceB;
begin
  lIntfA := TMyObject.Create; //TMyObject implements IMyInterfA, IMyInterfB
  lInfB := lIntfA as IMyInterfaceB;

  if Integer(lIntfA) <> Integer(lIntfB) then
    ShowMessage('I told you so');

  Pointer(lIntfA) := Pointer(lIntfB);

  lIntfA.A; //procedure B is called, because B is at "Offset 1", like A
  lIntfA.X; //total mayhem, X is at "Offset 2", but there is nothing at IMyIntfB + offset 2
end;

PS: Я не гуру, и я не знаю технических деталей о том, как все реализовано. Это только грубое объяснение, которое должно дать вам представление о том, почему ваш код работает неправильно. Если вы хотите, чтобы ваш код был успешным, сделайте это:

Vis := Interface_ as IVApplication;
Str := (Vis.Path);
0 голосов
/ 18 марта 2010

Я только догадываюсь, так как я не знаю много о COM, но приведение интерфейса к целому числу или указателю портит внутренний подсчет ссылок. Вероятно, ваш интерфейс будет освобожден, что объясняет нарушение прав доступа.

РЕДАКТИРОВАТЬ: Интересно, что Pointer(Vis) := Pointer(i) работает в любом случае. Не должен ли актер создать временный объект. Может быть, поэтому Vis не назначается?

...