Autor Beitrag
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: Mi 07.05.08 21:56 
Kennt jemand eine Implementierung von IDispatch, welche Invoke-Aufrufe gemäss RTTI ausführt? D.h. wenn über IDispatch ein GetIDsOfNames/Invoke-Befehl kommt, schaut die Klasse per RTTI nach, ob die Methode existiert und ruft diese ggf. auf.

Eine Minivariante sähe folgendermassen aus. Im Beispiel werden parameterlose Prozeduren via Variants aufgerufen (siehe Hauptprogramm unten).
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
program RTTITEst;

{$APPTYPE CONSOLE}

{$M+}

uses
  SysUtils,
  Windows,
  ComObj;

type
  TMyClass = class(TInterfacedObject, IDispatch)
  protected
    function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
    function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
    function GetIDsOfNames(const IID: TGUID; Names: Pointer;
      NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
    function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
      Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
  published
    procedure MyTestMethod;
    procedure MyOtherTestMethod;
  end;

type
  POleStrList = ^TOleStrList;
  TOleStrList = array[0..65535] of PWideChar;

  PDispIDList = ^TDispIDList;
  TDispIDList = array[0..65535] of Longint;

function TMyClass.GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount,
  LocaleID: Integer; DispIDs: Pointer): HResult;
var
  P: Pointer;
begin
  P := MethodAddress(String(POleStrList(Names)^[0]));
  if P <> nil then
  begin
    PDispIDList(DispIDs)^[0] := Integer(P);
    Result := S_OK;
  end else
    Result := DISP_E_UNKNOWNNAME;
end;

function TMyClass.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult;
begin
  Result := E_NOTIMPL;
end;

function TMyClass.GetTypeInfoCount(out Count: Integer): HResult;
begin
  Result := E_NOTIMPL;
end;

type
  TProc = procedure of object;

function TMyClass.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
  Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult;
var
  Method: TMethod;
begin
  Method.Code := Pointer(DispID);
  Method.Data := self;
  TProc(Method);
  Result := S_OK;
end;

procedure TMyClass.MyTestMethod;
begin
  Writeln('You called MyTestMethod!');
end;

procedure TMyClass.MyOtherTestMethod;
begin
  Writeln('You called MyOtherTestMethod!');
end;

var
  Test: TMyClass;
  MyVariant: Variant;
begin
  try
    Test := TMyClass.Create;
    Test._AddRef; // Necessary because Delphi doesn't seem to notice that Test is used on the next lines
    try
      MyVariant := Test as IDispatch;
    finally
      Test._Release;
    end;

    MyVariant.MyTestMethod; // redirects to TMyClass.MyTestMethod
    MyVariant.MyOtherTestMethod; // redirects to TMyClass.MyOtherTestMethod
    MyVariant.MyOtherTestMethodThatDoesntExist; // raises exception (because there is no such method in TMyClass)
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
  readln;
end.

Der Testcode erzeugt folgenden Output:
ausblenden Quelltext
1:
2:
3:
You called MyTestMethod!
You called MyOtherTestMethod!
EOleError: Method 'MyOtherTestMethodThatDoesntExist' not supported by automation object

Das ganze soll im Idealfall mit beliebigen Methoden (Prozeduren/Funktionen) mit Parametern, Properties und Default Array Properties funktionieren. Bis zu einem gewissen Grad sollte dies ja eigentlich möglich sein. Nur leider finde ich nichts derartiges. Das ganze auszuprogrammieren habe ich keine Lust, aber sowas müsste es doch eigentlich schon geben (?). Bevor es jemand erwähnt: Ich will keine TypeLibrary definieren sondern über RTTI gehen.
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Do 08.05.08 10:43 
Ganz ohne ASM und Kenntnis der Aufruf-Konventionen wirst Du das mit den RTTI-Mitteln von Delphi nicht hinbekommen ...

Ferner ist zu beachten, dass die RTTI lediglich für published-Methoden erzeugt wird und auch nur für Methoden, denen keine Pointer oder Typen-Lose Parameter übergeben werden. Auch bei Records sieht's eher schlecht mit RTTI aus (laut Doku gibt's keine, laut Compiler-Magic scheint's da was zu geben.

Wenn Du die normale TypeInfo einer Klasse parst (beachte den großen auskommentierten Block in der TypInfo.pas ;-)), solltest Du ein ganzes Stück weiter kommen.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
delfiphan Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: Do 08.05.08 18:40 
Das Beispiel war eigentlich nur dazu da zu zeigen, was ich haben will. Ich wills wie gesagt nicht fertig ausprogrammieren. Die Frage ist lediglich, ob jemand so eine Implementierung kennt oder hat.
Ich sehe z.B. ObjAuto.pas ein ObjectInvoke und interfaces wie IInvokable, die mit RTTI zu tun haben, auf der anderen Seite viele Klassen, die IDispatch implementieren. Die müsste man eigentlich miteinander verbinden können. Sie werden auch oft zusammen erwähnt, weswegen ich denke, dass es sowas schon fast geben muss.
delfiphan Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: Do 08.05.08 19:06 
Ich hab's gefunden! So was musste es ja geben, und es gibt es auch.

Die Lösung befindet sich in der VCL Unit WebSnapObjs.pas. Einfach Klasse von TScriptableObject ableiten und fertig. Exakt was ich wollte :D