Почему нельзя передать интерфейс Marshaled как целое число (или указатель)
Я передал ref интерфейса из надстроек Visio в MyCOMServer ( интерфейс Marshalling в 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 ответа
Когда у вас есть объект, который реализует несколько интерфейсов, и вы преобразуетесь между ними, вы получите разные адреса. Это связано с тем, как найти методы этих интерфейсов.
Допустим, у вас есть два интерфейса и класс, который их реализует, методы показывают только сообщение с именем метода:
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);
Я только догадываюсь, так как я не знаю много о COM, но приведение интерфейса к целому числу или указателю портит внутренний подсчет ссылок. Вероятно, ваш интерфейс будет освобожден, что объясняет нарушение прав доступа.
РЕДАКТИРОВАТЬ: Интересно, что Pointer(Vis) := Pointer(i)
работает в любом случае. Не должен ли актер создать временный объект. Может быть поэтому Vis
не назначается?