奇技淫巧之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;
奇技淫巧之Delphi和JavaScript互通的更多相关文章
- delphi与javascript互通
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...
- Delphi与Javascript的交互
网络上也有人写了关于Delphi与Javascript的文章,其大多数使用ScriptControl等,均无法达到与Delphi自身融合的效果.我也是在翻阅自己的组件库的时候发现了这个以前收集来的代码 ...
- 开源项目ScriptGate,Delphi与JavaScript相互调用的神器
ScriptGate是一个实现TWebBrowser上的JavaScript和Delphi代码相互调用的库,具体在这里:https://bitbucket.org/freeonterminate/sc ...
- WebViewJavascriptBridge-Obj-C和JavaScript互通消息的桥梁
转载至:http://www.cocoachina.com/ios/20150629/12248.html 译者:@coderyi9 本文翻译自Marcus Westin的开源框架WebViewJav ...
- Delphi,C语言互通脚本引擎研究
基于大神akuma的脚本引擎. 下面是demo
- 教程-delphi的开源json库:superobject,用法简介
困惑一天的问题 一个语句搞定了... 回头细说. superobject中的{$DEFINE UNICODE} 就是它,这是json官方推荐的Delphi处理json的包,地址: http://www ...
- Delphi实现HTMLWebBrowser实现HTML界面
HTML的界面有以下特点:图文混排,格式灵活,可以包含Flash.声音和视频等,实现图文声像的多媒体界面,而且易于建立和维护.另外,HTML的显示环境一般机器上都具备,通常不需要安装额外的软件.当然, ...
- javascript + sql编写SQL客户端工具tabris
祝大家2018新年快乐, 前不久发现了一个创意的脚本JtSQL(java编写) 开源地址为:https://github.com/noear/JtSQL JtSQL 特点:*.结合了JS.SQL.模板 ...
- JS_call_APP native 与 html的交互
1.***** 特点:下个版本的交互准备使用这个(http://www.knowsky.com/884428.html) https://github.com/lifei321/JS-OC http: ...
随机推荐
- 使用 v-html 绑定值
<div id="app03"> <div v-html="message"></div> <!--这里使用v-htm ...
- Mac 终端SSH连接服务器
1.打开终端 2.看是否是处于root目录下,是看第3步:否则执行sudo -i,输入电脑密码 3.执行 ssh root@host(host:ip地址或者域名) 4.如果不是第一次,则已成功连接.第 ...
- 匹配Luhn算法:可用于检测银行卡卡号
匹配Luhn算法:可用于检测银行卡卡号 /** * http://www.cnblogs.com/JnKindle/p/5798974.html * * 匹配Luhn算法:可用于检测银行卡卡号 * * ...
- 【Flutter学习】事件处理与通知之通知(Notification)
一,概述 Notification是Flutter中一个重要的机制,在Widget树中,每一个节点都可以分发通知,通知会沿着当前节点(context)向上传递,所有父节点都可以通过Notificati ...
- springcloud的config
CONFIG服务端 加入依赖: <dependency> <groupId>org.springframework.cloud</groupId> <arti ...
- layui弹出层回调的使用
<%@page language="java" contentType="text/html; charset=UTF-8"%> <%@ in ...
- Struts1.3——使用MyEclipse集成工具开发Struts
前面两篇通过手工写代码,手工配置struts-config.xml文件的方法来开发了一个最简单的struts项目,通过手工的方式旨在学习底层原理细节.当熟悉了以后,我们就不需要再通过纯手工的方式来开发 ...
- python学习笔记:模块——os模块(操作文件/目录)
import os os.rename(old,new) #重命名文件或目录 os.remove(f)#删除文件 os.mkdir('china/beijing') #创建文件夹 os.makedir ...
- activiti7完成当前任务
package com.zcc.acvitivi; import org.activiti.engine.ProcessEngine;import org.activiti.engine.Proces ...
- 厉害了,Spring团队又开源 nohttp 项目!
作者:h4cd 来源:https://www.oschina.net/news/107377/spring-opensource-nohttp Spring 团队开源 nohttp 项目,用以查找.替 ...