Einzelnen Beitrag anzeigen

Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#2

Re: Wie ermitteln, ob ein COM-Objekt eine bestimmte Methode

  Alt 20. Sep 2007, 15:01
mit .GetIDsOfNames() geht dies. Ist schon länger her das ich das benutzt habe. Dein COM Object steht ja in einem Variant. Delphi erzeugt nun Code beim Aufruf einer Methode der im Codesegement den Namen der Methode als String ablegt, ebenso Paramater usw.

Falls du den Source für ComObj Delphi5 hast sieht das so aus:

Delphi-Quellcode:
{ Call GetIDsOfNames method on the given IDispatch interface }

procedure GetIDsOfNames(const Dispatch: IDispatch; Names: PChar;
  NameCount: Integer; DispIDs: PDispIDList);

  procedure RaiseNameException;
  begin
    raise EOleError.CreateResFmt(@SNoMethod, [Names]);
  end;

type
  PNamesArray = ^TNamesArray;
  TNamesArray = array[0..0] of PWideChar;
var
  N, SrcLen, DestLen: Integer;
  Src: PChar;
  Dest: PWideChar;
  NameRefs: PNamesArray;
  StackTop: Pointer;
  Temp: Integer;
begin
  Src := Names;
  N := 0;
  asm
    MOV StackTop, ESP
    MOV EAX, NameCount
    INC EAX
    SHL EAX, 2 // sizeof pointer = 4
    SUB ESP, EAX
    LEA EAX, NameRefs
    MOV [EAX], ESP
  end;
  repeat
    SrcLen := StrLen(Src);
    DestLen := MultiByteToWideChar(0, 0, Src, SrcLen, nil, 0) + 1;
    asm
      MOV EAX, DestLen
      ADD EAX, EAX
      ADD EAX, 3 // round up to 4 byte boundary
      AND EAX, not 3
      SUB ESP, EAX
      LEA EAX, Dest
      MOV [EAX], ESP
    end;
    if N = 0 then NameRefs[0] := Dest else NameRefs[NameCount - N] := Dest;
    MultiByteToWideChar(0, 0, Src, SrcLen, Dest, DestLen);
    Dest[DestLen-1] := #0;
    Inc(Src, SrcLen+1);
    Inc(N);
  until N = NameCount;
  Temp := Dispatch.GetIDsOfNames(GUID_NULL, NameRefs, NameCount,
    GetThreadLocale, DispIDs);
  if Temp = Integer(DISP_E_UNKNOWNNAME) then RaiseNameException else OleCheck(Temp);
  asm
    MOV ESP, StackTop
  end;
end;

{ Central call dispatcher }

procedure VarDispInvoke(Result: PVariant; const Instance: Variant;
  CallDesc: PCallDesc; Params: Pointer); cdecl;

  procedure RaiseException;
  begin
    raise EOleError.CreateRes(@SVarNotObject);
  end;

var
  Dispatch: Pointer;
  DispIDs: array[0..MaxDispArgs - 1] of Integer;
begin
  if TVarData(Instance).VType = varDispatch then
    Dispatch := TVarData(Instance).VDispatch
  else if TVarData(Instance).VType = (varDispatch or varByRef) then
    Dispatch := Pointer(TVarData(Instance).VPointer^)
  else RaiseException;
  GetIDsOfNames(IDispatch(Dispatch), @CallDesc^.ArgTypes[CallDesc^.ArgCount],
    CallDesc^.NamedArgCount + 1, @DispIDs);
  if Result <> nil then VarClear(Result^);
  DispatchInvoke(IDispatch(Dispatch), CallDesc, @DispIDs, @Params, Result);
end;
hoffe ich mache mich nicht strafbar ?

Bei einem Source wie

Delphi-Quellcode:
var
  Obj: Variant; // OleVariant
begin
  Obj := CreateCOMObject(); // oder ähnliches
  Obj.Print('Test');
end;
legt der Compiler quasi sowas an

Delphi-Quellcode:
const codesegemnt
  sProcName = 'Print'#0;
const BSS
  sParam = 'Test';
var
  Obj: Variant; // OleVariant
  Result: Variant;
begin
  Obj := CreateCOMObject(); // oder ähnliches
  VarDispInvoke(Result, Obj, sProcName, sParams);
  if Result then ;
end;
Das ist jetzt aber eine sehr abstrahierte Umschreibung.
Es könnte durchaus sein das es auch schon eine entsprechend einfachere Funktion in neueren Delphi Versionen dafür gibt.
Eines steht fest, späte Bindung funktioniert nur mit IDispatch abgeleiteten Interfaces und diese haben auch meistens eine ITypeLib. Über diese werden die DispIDs, Parameter und Namen hinterlegt.

Gruß Hagen
  Mit Zitat antworten Zitat