zoukankan      html  css  js  c++  java
  • 奇技淫巧之Delphi和JavaScript互通

    http://www.raysoftware.cn/?p=305

    Delphi2010以后增加了新的RTTI信息,也就是通过RTTI可以在运行时获取/调用对象的公开成员或者函数.

    ScriptControl可以添加外部的对象,这个对象是个IDispatch接口,脚本调用的时候实际上是调用IDispatch的Invoke方法.

    那么我们只要实现了IDispatch的Invoke方法,在里面通过RTTI再转而调用Delphi对象的Public方法即可.通过这个可以代理任何Delphi的对象.

    仅仅调用Delphi对象似乎还不够完美,对象事件如果能关联到脚本的函数就更好了.那好,封装一个事件代理的类就可以.

    例子如下:

    procedure TForm1.FormCreate(Sender: TObject);

    begin

      Fscript := CreateScriptControl();

      // 把Form1当成一个对象添加到Script中

      Fscript.AddObject(Self.Name, SA(Self), true);

      

      Fscript.AddCode('function Form1_OnMouseMove(Sender, shift, x, y)' //

        + '{' // 在JS里面直接调用Form1上的任何Public的东西就都可以了,JS里面几乎没有类型的概念.事件的参数随便.计算也随便

        + 'Form1.Button1.Caption = "x:"+x+";"+"y:"+y +";" + "shift:" + shift;' //

        + '}' //

        + 'function Button1_Click(Sender)' //

        + '{' //调用Delphi对象的方法

        + 'Form1.SetBounds(0,0,800,480);' //

        + '}' //

        );

      

      //关联Delphi的事件到JS的函数

      Self.OnMouseMove := TEventDispatch.Create<TMouseMoveEvent>(Self, Fscript,

        'Form1_OnMouseMove');

      Button1.OnClick := TEventDispatch.Create<TNotifyEvent>(Button1, Fscript,

        'Button1_Click');

    end;

    看上去很爽吧.

    不过这个仅供我自己玩的,代码实现的比较毛糙,也没有经过严格的测试,甚至自己也没从头到尾再检查一次.如果有需要实用的朋友最好谨慎,肯定有细节问题要解决.

    另外这个ScriptControl仅仅有32位的,在64位Windows上的system32里面并没有这个DLL,仅仅在SysWow64中才有.也就是说如果你要开发64位Windows程序就不能用了.当然如果是在64位Windows中运行的32位程序则没问题.

    下面是代码,写的比较丑.

    {

      让Delphi使用windows自带的scriptcontrol,在javascript中可以调用delphi的对象,

      并且可以使用事件.

      wr960204武稀松 2013

    }

    unit ScriptObjectUtilsWithRTTI;

      

    interface

      

    {

      是否使用外部的MSScriptControl_TLB单元.我把这个单元的接口声明都放在后面了,

      可以避免引入ActiveX等单元

      如果觉得我的声明太旧或者有问题,可以打开这个开关,使用外部自己Import生成的单元

    }

    { .$DEFINE Use_External_TLB }

    { 这个开关是使用LoadLibrary方式加载COM DLL,也就及时COM组件没有注册也可以创建COM对象 }

    {$DEFINE COMOBJ_FROMDLL}

      

    uses

    {$IFDEF Use_External_TLB}

      MSScriptControl_TLB,

    {$ENDIF}

      System.ObjAuto,

      System.Classes, System.RTTI, System.Variants,

      Winapi.Windows, Winapi.ActiveX, System.TypInfo;

      

    type

    {$REGION 'MSScriptControl_TLB'}

    {$IFDEF Use_External_TLB}

      IScriptControl = MSScriptControl_TLB.IScriptControl;

    {$ELSE}

      ScriptControlStates = TOleEnum;

      IScriptModuleCollection = IDispatch;

      IScriptError = IDispatch;

      IScriptProcedureCollection = IDispatch;

      

      IScriptControl = interface(IDispatch)

        ['{0E59F1D3-1FBE-11D0-8FF2-00A0D10038BC}']

        function Get_Language: WideString; safecall;

        procedure Set_Language(const pbstrLanguage: WideString); safecall;

        function Get_State: ScriptControlStates; safecall;

        procedure Set_State(pssState: ScriptControlStates); safecall;

        procedure Set_SitehWnd(phwnd: Integer); safecall;

        function Get_SitehWnd: Integer; safecall;

        function Get_Timeout: Integer; safecall;

        procedure Set_Timeout(plMilleseconds: Integer); safecall;

        function Get_AllowUI: WordBool; safecall;

        procedure Set_AllowUI(pfAllowUI: WordBool); safecall;

        function Get_UseSafeSubset: WordBool; safecall;

        procedure Set_UseSafeSubset(pfUseSafeSubset: WordBool); safecall;

        function Get_Modules: IScriptModuleCollection; safecall;

        function Get_Error: IScriptError; safecall;

        function Get_CodeObject: IDispatch; safecall;

        function Get_Procedures: IScriptProcedureCollection; safecall;

        procedure _AboutBox; safecall;

        procedure AddObject(const Name: WideString; const Object_: IDispatch;

          AddMembers: WordBool); safecall;

        procedure Reset; safecall;

        procedure AddCode(const Code: WideString); safecall;

        function Eval(const Expression: WideString): OleVariant; safecall;

        procedure ExecuteStatement(const Statement: WideString); safecall;

        function Run(const ProcedureName: WideString; var Parameters: PSafeArray)

          : OleVariant; safecall;

        property Language: WideString read Get_Language write Set_Language;

        property State: ScriptControlStates read Get_State write Set_State;

        property SitehWnd: Integer read Get_SitehWnd write Set_SitehWnd;

        property Timeout: Integer read Get_Timeout write Set_Timeout;

        property AllowUI: WordBool read Get_AllowUI write Set_AllowUI;

        property UseSafeSubset: WordBool read Get_UseSafeSubset

          write Set_UseSafeSubset;

        property Modules: IScriptModuleCollection read Get_Modules;

        property Error: IScriptError read Get_Error;

        property CodeObject: IDispatch read Get_CodeObject;

        property Procedures: IScriptProcedureCollection read Get_Procedures;

      end;

    {$ENDIF}

    {$ENDREGION 'MSScriptControl_TLB'}

      

      { 事件代理的泛型类,可以把Delphi的事件映射到Javascript的函数上.

        注意,这是一个TComponent的派生类.如果不指定Ownder的话要手工释放的.

      }

      TEventDispatch = class(TComponent)

      private

        FScriptControl: IScriptControl;

        FScriptFuncName: string;

        FInternalDispatcher: TMethod;

        FRttiContext: TRttiContext;

        FRttiType: TRttiMethodType;

        procedure InternalInvoke(Params: PParameters; StackSize: Integer);

        function ValueToVariant(Value: TValue): Variant;

        constructor Create(AOwner: TComponent; ATTypeInfo: PTypeInfo);

          reintroduce; overload;

      public

        class function Create<T>(AOwner: TComponent; ScriptControl: IScriptControl;

          ScriptFuncName: String): T; reintroduce; overload;

      

        destructor Destroy; override;

      

      end;

      

      { 很普通,创建一个MSWindows自带的ScriptControl实例,默认脚本是Javascript }

    function CreateScriptControl(ScriptName: String = 'javascript'): IScriptControl;

    { 创建对象的IDispatch的代理, Owned表示这个IDispatch拥有代理对象的生杀大权,当代理的IDispatch

      释放的时候这个Obj也会被释放掉 }

    function SA(Obj: TObject; Owned: Boolean): IDispatch; overload;

    { 创建对象的IDispatch的代理 }

    function SA(Obj: TObject): IDispatch; overload;

      

    implementation

      

    uses

    {$IFNDEF COMOBJ_FROMDLL}

      System.Win.ComObj,

    {$ENDIF}

      System.SysUtils;

      

    function CreateScriptControl(ScriptName: String): IScriptControl;

    const

      CLASS_ScriptControl: TGUID = '{0E59F1D5-1FBE-11D0-8FF2-00A0D10038BC}';

    {$IFDEF COMOBJ_FROMDLL}

      MSSCRIPTMODULE = 'msscript.ocx';

    var

      DllGetClassObject: function(const clsid, IID: TGUID; var Obj)

        : HRESULT; stdcall;

      ClassFactory: IClassFactory;

      hLibInst: HMODULE;

      hr: HRESULT;

    begin

      Result := nil;

      hLibInst := GetModuleHandle(MSSCRIPTMODULE);

      if hLibInst = 0 then

        hLibInst := LoadLibrary(MSSCRIPTMODULE);

      if hLibInst = 0 then

        Exit;

      DllGetClassObject := GetProcAddress(hLibInst, 'DllGetClassObject');

      if Assigned(DllGetClassObject) then

      begin

        hr := DllGetClassObject(CLASS_ScriptControl, IClassFactory, ClassFactory);

        if hr = S_OK then

        begin

          hr := ClassFactory.CreateInstance(nil, IScriptControl, Result);

          if (hr = S_OK) and (Result <> nil) then

            Result.Language := ScriptName;

        end;

      end;

    end;

    {$ELSE}

      

    begin

      Result := CreateComObject(CLASS_ScriptControl) as IScriptControl;

      if Result <> nil then

        Result.Language := ScriptName;

    end;

    {$ENDIF}

      

    type

      TDispatchKind = (dkMethod, dkProperty, dkSubComponent);

      

      TDispatchInfo = record

        Instance: TObject;

        case Kind: TDispatchKind of

          dkMethod:

            (MethodInfo: TRttiMethod);

          dkProperty:

            (PropInfo: TRttiProperty);

          dkSubComponent:

            (ComponentInfo: NativeInt);

      end;

      

      TDispatchInfos = array of TDispatchInfo;

      

      {

        IDispatch代理类.通过RTTI可以把Delphi对象的成员/属性/函数映射给IDispatch.

        而且忽略调用协议.

      }

      TScriptObjectAdapter = class(TInterfacedObject, IDispatch)

      private

        //

        FRttiContext: TRttiContext;

        FRttiType: TRttiType;

        FDispatchInfoCount: Integer;

        FDispatchInfos: TDispatchInfos;

        FComponentNames: TStrings;

        FInstance: TObject;

        FOwned: Boolean;

        function AllocDispID(AKind: TDispatchKind; Value: Pointer;

          AInstance: TObject): TDispID;

      protected

        property Instance: TObject read FInstance;

      public

        { IDispatch }

        function GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount: Integer;

          LocaleID: Integer; DispIDs: Pointer): HRESULT; virtual; stdcall;

        function GetTypeInfo(Index: Integer; LocaleID: Integer; out TypeInfo)

          : HRESULT; stdcall;

        function GetTypeInfoCount(out Count: Integer): HRESULT; stdcall;

        function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;

          Flags: Word; var Params; VarResult: Pointer; ExcepInfo: Pointer;

          ArgErr: Pointer): HRESULT; virtual; stdcall;

      public

        constructor Create(Instance: TObject; Owned: Boolean = False);

        destructor Destroy; override;

      end;

      

    function SA(Obj: TObject; Owned: Boolean): IDispatch;

    begin

      Result := TScriptObjectAdapter.Create(Obj, Owned);

    end;

      

    function SA(Obj: TObject): IDispatch;

    begin

      Result := TScriptObjectAdapter.Create(Obj, False);

    end;

      

    const

      ofDispIDOffset = 100;

      

      { TScriptObjectAdapter }

      

    function TScriptObjectAdapter.AllocDispID(AKind: TDispatchKind; Value: Pointer;

      AInstance: TObject): TDispID;

    var

      I: Integer;

      dispatchInfo: TDispatchInfo;

    begin

      for I := FDispatchInfoCount - 1 downto 0 do

        with FDispatchInfos[I] do

          if (Kind = AKind) and (MethodInfo = Value) then

          begin

            // Already have a dispid for this methodinfo

            Result := ofDispIDOffset + I;

            Exit;

          end;

      if FDispatchInfoCount = Length(FDispatchInfos) then

        SetLength(FDispatchInfos, Length(FDispatchInfos) + 10);

      Result := ofDispIDOffset + FDispatchInfoCount;

      with dispatchInfo do

      begin

        Instance := AInstance;

        Kind := AKind;

        MethodInfo := Value;

      end;

      FDispatchInfos[FDispatchInfoCount] := dispatchInfo;

      Inc(FDispatchInfoCount);

    end;

      

    constructor TScriptObjectAdapter.Create(Instance: TObject; Owned: Boolean);

    begin

      inherited Create;

      FComponentNames := TStringList.Create;

      FInstance := Instance;

      FOwned := Owned;

      FRttiContext := TRttiContext.Create;

      FRttiType := FRttiContext.GetType(FInstance.ClassType);

    end;

      

    destructor TScriptObjectAdapter.Destroy;

    begin

      if FOwned then

        FInstance.Free;

      FRttiContext.Free;

      FComponentNames.Free;

      inherited Destroy;

    end;

      

    function TScriptObjectAdapter.GetIDsOfNames(const IID: TGUID; Names: Pointer;

      NameCount, LocaleID: Integer; DispIDs: Pointer): HRESULT;

    type

      PNames = ^TNames;

      TNames = array [0 .. 100] of POleStr;

      PDispIDs = ^TDispIDs;

      TDispIDs = array [0 .. 100] of Cardinal;

    var

      Name: String;

      MethodInfo: TRttiMethod;

      PropertInfo: TRttiProperty;

      ComponentInfo: TComponent;

      lDispId: TDispID;

    begin

      Result := S_OK;

      lDispId := -1;

      Name := WideCharToString(PNames(Names)^[0]);

      

      MethodInfo := FRttiType.GetMethod(Name);

      // MethodInfo.Invoke(FInstance, ['']);

      if MethodInfo <> nil then

      begin

        lDispId := AllocDispID(dkMethod, MethodInfo, FInstance);

      end

      else

      begin

        PropertInfo := FRttiType.GetProperty(Name);

        if PropertInfo <> nil then

        begin

          lDispId := AllocDispID(dkProperty, PropertInfo, FInstance);

        end

        else if FInstance is TComponent then

        begin

          ComponentInfo := TComponent(FInstance).FindComponent(Name);

          if ComponentInfo <> nil then

          begin

      

            lDispId := AllocDispID(dkSubComponent, Pointer(FComponentNames.Add(Name)

              ), FInstance);

          end;

        end;

      end;

      if lDispId >= ofDispIDOffset then

      begin

        Result := S_OK;

        PDispIDs(DispIDs)^[0] := lDispId;

      end;

    end;

      

    function TScriptObjectAdapter.GetTypeInfo(Index, LocaleID: Integer;

      out TypeInfo): HRESULT;

    begin

      Result := E_NOTIMPL;

    end;

      

    function TScriptObjectAdapter.GetTypeInfoCount(out Count: Integer): HRESULT;

    begin

      Result := E_NOTIMPL;

    end;

      

    function TScriptObjectAdapter.Invoke(DispID: Integer; const IID: TGUID;

      LocaleID: Integer; Flags: Word; var Params;

      VarResult, ExcepInfo, ArgErr: Pointer): HRESULT;

    type

      PVariantArray = ^TVariantArray;

      TVariantArray = array [0 .. 65535] of Variant;

      PIntegerArray = ^TIntegerArray;

      TIntegerArray = array [0 .. 65535] of Integer;

    var

      Parms: PDispParams;

      TempRet: Variant;

      dispatchInfo: TDispatchInfo;

      lParams: TArray<TValue>;

      paramInfos: TArray<TRttiParameter>;

      I: Integer;

      component: TComponent;

      propertyValue: TValue;

      _SetValue: NativeInt;

      tmpv: Variant;

    begin

      Result := S_OK;

      

      Parms := @Params;

      try

        if VarResult = nil then

          VarResult := @TempRet;

        if (DispID - ofDispIDOffset >= 0) and

          (DispID - ofDispIDOffset < FDispatchInfoCount) then

        begin

          dispatchInfo := FDispatchInfos[DispID - ofDispIDOffset];

          case dispatchInfo.Kind of

            dkProperty:

              begin

                if Flags and (DISPATCH_PROPERTYPUTREF or DISPATCH_PROPERTYPUT) <> 0

                then

                  if (Parms.cNamedArgs <> 1) or

                    (PIntegerArray(Parms.rgdispidNamedArgs)^[0] <>

                    DISPID_PROPERTYPUT) then

                    Result := DISP_E_MEMBERNOTFOUND

                  else

                  begin

                    propertyValue := TValue.Empty;

                    case dispatchInfo.PropInfo.PropertyType.Handle^.Kind of

                      tkInt64, tkInteger:

                        propertyValue :=

                          TValue.FromOrdinal

                          (dispatchInfo.PropInfo.PropertyType.Handle,

                          PVariantArray(Parms.rgvarg)^[0]);

                      tkFloat:

                        propertyValue := TValue.From<Extended>

                          (PVariantArray(Parms.rgvarg)^[0]);

                      tkString, tkUString, tkLString, tkWString:

                        propertyValue :=

                          TValue.From<String>(PVariantArray(Parms.rgvarg)^[0]);

                      tkSet:

                        begin

                          _SetValue := PVariantArray(Parms.rgvarg)^[0];

                          TValue.Make(_SetValue,

                            dispatchInfo.PropInfo.PropertyType.Handle,

                            propertyValue);

                        end;

                    else

                      propertyValue :=

                        TValue.FromVariant(PVariantArray(Parms.rgvarg)^[0]);

                    end;

      

                    dispatchInfo.PropInfo.SetValue(dispatchInfo.Instance,

                      propertyValue);

                  end

                else if Parms.cArgs <> 0 then

                  Result := DISP_E_BADPARAMCOUNT

                else if dispatchInfo.PropInfo.PropertyType.Handle^.Kind = tkClass

                then

                  POleVariant(VarResult)^ :=

                    SA(dispatchInfo.PropInfo.GetValue(dispatchInfo.Instance)

                    .AsObject()) as IDispatch

                else

                  POleVariant(VarResult)^ := dispatchInfo.PropInfo.GetValue

                    (dispatchInfo.Instance).AsVariant;

              end;

            dkMethod:

              begin

                paramInfos := dispatchInfo.MethodInfo.GetParameters;

                SetLength(lParams, Length(paramInfos));

                for I := Low(paramInfos) to High(paramInfos) do

                  if I < Parms.cArgs then

                  begin

                    //因为IDispatch是COM对象,一般是stdcall或者safecall,参数是由右到左传递的

                    tmpv := PVariantArray(Parms.rgvarg)^[Parms.cArgs - 1 - I];

                    lParams[I] := TValue.FromVariant(tmpv);

                  end

                  else //不足的参数补空

                  begin

                    TValue.Make(0, paramInfos[I].ParamType.Handle, lParams[I]);

                  end;

      

                if (dispatchInfo.MethodInfo.ReturnType <> nil) and

                  (dispatchInfo.MethodInfo.ReturnType.Handle^.Kind = tkClass) then

                begin

                  POleVariant(VarResult)^ :=

                    SA(dispatchInfo.MethodInfo.Invoke(dispatchInfo.Instance,

                    lParams).AsObject()) as IDispatch;

                end

                else

                begin

                  POleVariant(VarResult)^ := dispatchInfo.MethodInfo.Invoke

                    (dispatchInfo.Instance, lParams).AsVariant();

                end;

              end;

            dkSubComponent:

              begin

                component := TComponent(dispatchInfo.Instance)

                  .FindComponent(FComponentNames[dispatchInfo.ComponentInfo]);

                if component = nil then

                  Result := DISP_E_MEMBERNOTFOUND;

      

                POleVariant(VarResult)^ := SA(component) as IDispatch;

              end;

          end;

        end

        else

          Result := DISP_E_MEMBERNOTFOUND;

      except

        if ExcepInfo <> nil then

        begin

          FillChar(ExcepInfo^, SizeOf(TExcepInfo), 0);

          with TExcepInfo(ExcepInfo^) do

          begin

            bstrSource := StringToOleStr(ClassName);

            if ExceptObject is Exception then

              bstrDescription := StringToOleStr(Exception(ExceptObject).Message);

            scode := E_FAIL;

          end;

        end;

        Result := DISP_E_EXCEPTION;

      end;

    end;

      

    { TEventDispatch<T> }

      

    class function TEventDispatch.Create<T>(AOwner: TComponent;

      ScriptControl: IScriptControl; ScriptFuncName: String): T;

    type

      PT = ^T;

    var

      ed: TEventDispatch;

    begin

      ed := TEventDispatch.Create(AOwner, TypeInfo(T));

      ed.FScriptControl := ScriptControl;

      ed.FScriptFuncName := ScriptFuncName;

      Result := PT(@ed.FInternalDispatcher)^;

    end;

      

    constructor TEventDispatch.Create(AOwner: TComponent; ATTypeInfo: PTypeInfo);

    var

      LRttiType: TRttiType;

    begin

      FRttiContext := TRttiContext.Create;

      LRttiType := FRttiContext.GetType(ATTypeInfo);

      if not(LRttiType is TRttiMethodType) then

      begin

        raise Exception.Create('T only is Method(Member function)!');

      end;

      FRttiType := TRttiMethodType(LRttiType);

      Inherited Create(AOwner);

      FInternalDispatcher := CreateMethodPointer(InternalInvoke,

        GetTypeData(FRttiType.Handle));

    end;

      

    destructor TEventDispatch.Destroy;

    begin

      ReleaseMethodPointer(FInternalDispatcher);

      inherited Destroy;

    end;

      

    function TEventDispatch.ValueToVariant(Value: TValue): Variant;

    var

      _SetValue: Int64Rec;

    begin

      Result := EmptyParam;

      case Value.TypeInfo^.Kind of

        tkClass:

          Result := SA(Value.AsObject);

        tkInteger:

          Result := Value.AsInteger;

        tkString, tkLString, tkChar, tkUString:

          Result := Value.AsString;

        tkSet:

          begin

            Value.ExtractRawData(@_SetValue);

            case Value.DataSize of

              1:

                Result := _SetValue.Bytes[0];

              2:

                Result := _SetValue.Words[0];

              4:

                Result := _SetValue.Cardinals[0];

              8:

                Result := Int64(_SetValue);

            end;

          end;

      else

        Result := Value.AsVariant;

      end;

      

    end;

      

    function GetParamSize(TypeInfo: PTypeInfo): Integer;

    begin

      if TypeInfo = nil then

        Exit(0);

      

      case TypeInfo^.Kind of

        tkInteger, tkEnumeration, tkChar, tkWChar, tkSet:

          case GetTypeData(TypeInfo)^.OrdType of

            otSByte, otUByte:

              Exit(1);

            otSWord, otUWord:

              Exit(2);

            otSLong, otULong:

              Exit(4);

          else

            Exit(0);

          end;

        tkFloat:

          case GetTypeData(TypeInfo)^.FloatType of

            ftSingle:

              Exit(4);

            ftDouble:

              Exit(8);

            ftExtended:

              Exit(SizeOf(Extended));

            ftComp:

              Exit(8);

            ftCurr:

              Exit(8);

          else

            Exit(0);

          end;

        tkClass, tkClassRef:

          Exit(SizeOf(Pointer));

        tkInterface:

          Exit(-SizeOf(Pointer));

        tkMethod:

          Exit(SizeOf(TMethod));

        tkInt64:

          Exit(8);

        tkDynArray, tkUString, tkLString, tkWString:

          Exit(-SizeOf(Pointer));

        tkString:

          Exit(GetTypeData(TypeInfo)^.MaxLength + 1);

      

        tkPointer:

          Exit(SizeOf(Pointer));

        tkRecord:

          if IsManaged(TypeInfo) then

            Exit(-GetTypeData(TypeInfo)^.RecSize)

          else

            Exit(GetTypeData(TypeInfo)^.RecSize);

        tkArray:

          Exit(GetTypeData(TypeInfo)^.ArrayData.Size);

        tkVariant:

          Exit(-SizeOf(Variant));

      else

        Exit(0);

      end;

      

    end;

      

    procedure TEventDispatch.InternalInvoke(Params: PParameters;

      StackSize: Integer);

    var

      lRttiParameters, tmp: TArray<TRttiParameter>;

      lRttiParam: TRttiParameter;

      lParamValues: TArray<TValue>;

      I, ParamSize: Integer;

      PStack: PByte;

      test: string;

      ParamIsByRef: Boolean;

      RegParamIndexs: array [0 .. 2] of Byte;

      RegParamIndex: Integer;

      v, tmpv: Variant;

      ParameterArray: PSafeArray;

    begin

      tmp := FRttiType.GetParameters;

      SetLength(lRttiParameters, Length(tmp) + 1);

      lRttiParameters[0] := nil;

      for I := Low(tmp) to High(tmp) do

        lRttiParameters[I + 1] := tmp[I];

      

      SetLength(lParamValues, Length(lRttiParameters));

      PStack := @Params.Stack[0];

      if (FRttiType.CallingConvention = ccReg) then

      begin

        // 看那些参数用了寄存器传输

        FillChar(RegParamIndexs, SizeOf(RegParamIndexs), -1);

        RegParamIndexs[0] := 0;

        RegParamIndex := 1;

        for I := 1 to High(lRttiParameters) do

        begin

          lRttiParam := lRttiParameters[I];

          ParamSize := GetParamSize(lRttiParam.ParamType.Handle);

          ParamIsByRef := (lRttiParam <> nil) and

            (([pfVar, pfConst, pfOut] * lRttiParam.Flags) <> []);

          if ((ParamSize <= SizeOf(Pointer)) and

            (not(lRttiParam.ParamType.Handle.Kind in [tkFloat]))) or (ParamIsByRef)

          then

          begin

            RegParamIndexs[RegParamIndex] := I;

            if (RegParamIndex = High(RegParamIndexs)) or (I = High(lRttiParameters))

            then

              Break;

            Inc(RegParamIndex);

          end;

      

        end;

        for I := High(lRttiParameters) downto Low(lRttiParameters) do

        begin

          lRttiParam := lRttiParameters[I];

      

          if I = 0 then

            TValue.Make(Params.EAXRegister, TypeInfo(TObject), lParamValues[I])

          else

          begin

            ParamIsByRef := (lRttiParam <> nil) and

              (([pfVar, pfConst, pfOut] * lRttiParam.Flags) <> []);

            ParamSize := GetParamSize(lRttiParam.ParamType.Handle);

            if (ParamSize < SizeOf(Pointer)) or (ParamIsByRef) then

              ParamSize := SizeOf(Pointer);

            if (I in [RegParamIndexs[0], RegParamIndexs[1], RegParamIndexs[2]]) then

            begin

              if ParamIsByRef then

              begin

                TValue.Make(Pointer(Params.Registers[RegParamIndex]),

                  lRttiParameters[I].ParamType.Handle, lParamValues[I]);

              end

              else

              begin

                TValue.Make(Params.Registers[RegParamIndex],

                  lRttiParameters[I].ParamType.Handle, lParamValues[I]);

              end;

              Dec(RegParamIndex);

            end

            else

            begin

              if ParamIsByRef then

                TValue.Make(PPointer(PStack)^, lRttiParameters[I].ParamType.Handle,

                  lParamValues[I])

              else

                TValue.Make(PStack, lRttiParameters[I].ParamType.Handle,

                  lParamValues[I]);

              Inc(PStack, ParamSize);

            end;

          end;

        end;

      end

      else

      begin

        for I := Low(lRttiParameters) to High(lRttiParameters) do

        begin

          ParamIsByRef := (lRttiParameters[I] <> nil) and

            (([pfVar, pfConst, pfOut] * lRttiParameters[I].Flags) <> []);

          if I = 0 then

          begin // Self

            ParamSize := SizeOf(TObject);

            TValue.Make(PStack, TypeInfo(TObject), lParamValues[I]);

          end

          else

          begin

            ParamSize := GetParamSize(lRttiParameters[I].ParamType.Handle);

            if ParamSize < SizeOf(Pointer) then

              ParamSize := SizeOf(Pointer);

      

            // TValue.Make(PStack, lRttiParameters[I].ParamType.Handle,  lParamValues[I]);

            if ParamIsByRef then

              TValue.Make(PPointer(PStack)^, lRttiParameters[I].ParamType.Handle,

                lParamValues[I])

            else

              TValue.Make(PStack, lRttiParameters[I].ParamType.Handle,

                lParamValues[I]);

          end;

          Inc(PStack, ParamSize);

        end;

      end;

      

      if (FScriptControl <> nil) and (FScriptFuncName <> '') then

      begin

        v := VarArrayCreate([0, Length(lParamValues) - 1], varVariant);

        for I := 1 to Length(lParamValues) - 1 do

        begin

          test := lRttiParameters[I].Name;

          tmpv := ValueToVariant(lParamValues[I]);

          v[I - 1] := tmpv;

        end;

        ParameterArray := PSafeArray(TVarData(v).VArray);

        FScriptControl.Run(FScriptFuncName, ParameterArray);

      end;

    end; 

  • 相关阅读:
    深度系统安装wine
    Android应用真正的入口在哪里?
    推荐系统算法概览
    Android面试题:Scrollview内嵌一个Button他的事件消费是怎样的?Move,Down,Up分别被哪个组件消费?
    Java面试题:多线程交替打印字符串
    EventBus使用初体验
    Andoird面试题:A活动启动B活动,他们的生命周期各是怎么样的?
    Android在开发过程中如何选择compileSdkVersion,minSdkVersion和targetSdkVersion
    华为2020暑期实习面经(已拿Offer)
    工商银行软件开发中心2020暑期实习面经(已拿Offer)
  • 原文地址:https://www.cnblogs.com/blogpro/p/11452564.html
Copyright © 2011-2022 走看看