Scripter工作室开发者向导
版权2002 Automa tmssoftware.com
概要
Scripter工作室是把脚本能力加到你的应用程序的一套的Delphi/C++Builder部件。Scripter工作室能使你的最终用户使用可视化工具书写和执行自己的脚本。包含的组件有:
TatPascalScripter - 非视觉组件,执行Pascal语法的脚本。
TatBasicScripter - 非视觉组件,执行Basic语法的脚本。
TatPascalFormScripter - 窗口认识Pascal脚本,继承自TatPascalScripter组件。
TatBasicFormScripter - 窗口认识Basic脚本,继承自TatBasicScripter组件。
TatScriptDebugDlg - 脚本高度的对话组件。
TAdvMemo - 语法高亮Memo,在程序运行时编辑脚本。
TatPascalScripter和TatBasicScripter都是从TatCustomScripter组件继承下来的,都使用相同的属性和方法来运行脚本。
脚本有下列特征:
·在运行时间解释Pascal和Basic语言。
·脚本能访问任何Delphi对象,包括属性和方法。
·脚本支持try...except和try...finally异常处理。
·允许脚本读/写Delphi变量和常量。
·允许Delphi代码访问(读/写)脚本变量。
·你能建造(从Delphi代码)你自己的类,包含属性和方法,并在脚本中使用。
·Delphi系统的大多数程序(转化,日期,格式化,字符串操作)已经包括(IntToStr,FormatDateTime,Copy,Delete等等)。
·你能保存/装载编译了的代码,这样你不需要每次运行脚本的时候都需要源代码。
·调试能力(断点,跟踪,运行到光标,暂停,中断,等等)
·安全的线程
·COM(微软普通的对象模型)支持
语言特征
Pascal语法
TatPascalScripter组件执行脚本的Pascal语法。Pascal语法支持:
· begin .. end
· procedure and function
· if .. then .. else
· for .. to .. do .. step
· while .. do
· repeat .. until
· try .. except and try .. finally
· case
· array constructors (x:=[ 1, 2, 3 ];)
· ^ , * , / , and , + , - , or , <> , >=, <= , = , > , < , div , mod , xor , shl , shr
· 访问对象属性和方法(ObjectName.SubObject.Property)
脚本结构
脚本结构用2个主要的块组成:
1、过程和函数声明;
2、主要的块。
两个块是可选择的,但是在脚本中至少要有一个。主要的块不需要begin...end。它可以是一个单一的声明。一些例子:
脚本1:
procedure DoSomething;
begin
CallSomething;
end;
begin
CallSomethingElse;
end;
脚本2:
begin
CallSomethingElse;
end;
脚本3:
function MyFunction;
begin
result:='Ok!';
end;
脚本4:
CallSomethingElse;
和Pascal一样,声明应该由“;”结尾。begin...end块是允许集合一批声明。
标识符
在脚本的标识符名字(变量名字,函数和过程名字,等等。)跟随Pascal最普通的规则:
应该从一个字符开始(a..z或A..Z),或“_”,和继之以包含数字字母的字符或“_”字符。
不能包含任何别的字符或空格。
有效的标识符:
VarName
_Some
V1A2
无效的标识符:
2Var
My Name
Some-more
This,is,not,valid
赋值声明
赋值语句和Pascal一样,赋值语句是使用“:=”。例子:
MyVar:=2;
Button.Caption:='This ' + 'is ok.';
字符串
字符串是同pascal一样使用单引号(')字符。双引号(")不能使用。你也能使用(#)字符宣布一个字符在一个字符串的内部,不需要使用(+)操作符增加一个字符到一个字符串。一些例子:
A:='This is a text';
Str:='Text '+'concat';
B:='String with CR and LF char at the end'#13#10;
C:='String with '#33#34' characters in the middle';
注释
在脚本内能插入注释。你能使用//字符或(* *)或{}。使用//字符注释只能注释一行。
//This is a comment before ShowMessage
ShowMessage('Ok');
(* This is another comment *)
ShowMessage('More ok!');
{ And this is a comment
with two lines }
ShowMessage('End of okays');
变量
不需要在脚本中声明变数类型。这样,你宣布变量只需要使用var命令和变量的名字。如果脚本的OptionExplicit属性被设置为false,那么就不需要声明变量。在这种情况下,变数是暗示声明。如果你想要脚本上有更多的控制,设置OptionExplicit属性为true。如果OptionExplicit属性为true,而在脚本中使用了变量但不进行声明,将会唤醒一个编译错误。例子:
脚本1:
procedure Msg;
var S;
begin
S:='Hello world!';
ShowMessage(S);
end;
脚本2:
var A;
begin
A:=0;
A:=A+1;
end;
脚本3:
var S;
S:='Hello World!';
ShowMessage(S);
如果脚本属性OptionExplicit被设置为false,那么var声明不需要出现在任何脚本的上方。
索引
字符串,数组和数组属性可以使用索引“[”并且“]”字符。更多例子:
MyChar:=MyStr[2];
MyStr[1]:='A';
MyArray[1,2]:=1530;
Lines.Strings[2]:='Some text';
数组
脚本支持数组和variant数组,使用方法参考Pascal。注意:数组的索引是从0开始的。更多例子:
NewArray := [ 2,4,6,8 ];
Num:=NewArray[1]; //Num receives "4"
MultiArray := [ ['green','red','blue'] , ['apple','orange','lemon'] ];
Str:=MultiArray[0,2]; //Str receives 'blue'
MultiArray[1,1]:='new orange';
If语句
if语句有2种形式:if..then和if..then..else。更多例子:
if J <> 0 then Result := I/J;
if J = 0 then Exit else Result := I/J;
if J <> 0 then
begin
Result := I/J;
Count := Count + 1;
end
else
Done := True;
While语句
同Pascal。更多例子:
while Data[I] <> X do I := I + 1;
while I > 0 do
begin
if Odd(I) then Z := Z * X;
I := I div 2;
X := Sqr(X);
end;
while not Eof(InputFile) do
begin
Readln(InputFile, Line);
Process(Line);
end;
repeat语句
同Pascal。更多例子:
repeat
K := I mod J;
I := J;
J := K;
until J = 0;
repeat
Write('Enter a value (0..9): ');
Readln(I);
until (I >= 0) and (I <= 9);
for语句
同Pascal。更多例子:
脚本1:
for c:=1 to 10 do
a:=a+c;
脚本2:
for i:=a to b do
begin
j:=i^2;
sum:=sum+j;
end;
case语句
同Pascal。更多例子:
case uppercase(Fruit) of
'lime': ShowMessage('green');
'orange': ShowMessage('orange');
'apple': ShowMessage('red');
else
ShowMessage('black');
end;
函数和过程说明
函数和过程的宣告同Delphi的非常相似,唯一的差别只在于不需要指定变量的类型。函数的返回值是使用暗示的Result变量。参数传地址也能被使用,只是不需要规定变量类型。一些例子:
procedure HelloWord;
begin
ShowMessage('Hello world!');
end;
procedure UpcaseMessage(Msg);
begin
ShowMessage(Uppercase(Msg));
end;
function TodayAsString;
begin
result:=DateToStr(Date);
end;
function Max(A,B);
begin
if A>B then
result:=A
else
result:=B;
end;
procedure SwapValues(var A, B);
Var Temp;
begin
Temp:=A;
A:=B;
B:=Temp;
end;
Basic的语法
TatBasicScripter组件执行Basic语法的脚本。Basic语法支持:
· sub .. end and function .. end declarations
· byref and dim directives
· if .. then .. else .. end constructor
· for .. to .. step .. next constructor
· do .. while .. loop and do .. loop .. while constructors
· do .. until .. loop and do .. loop .. until constructors
· ^ , * , / , and , + , - , or , <> , >=, <= , = , > , < , div , mod , xor , shl , shr operators
· try .. except and try .. finally blocks
· select case .. end select constructor
· array constructors (x:=[ 1, 2, 3 ];)
· exit statement
· access to object properties and methods ( ObjectName.SubObject.Property )
脚本结构
同Pascal的脚本结构。一些例子:
脚本1:
SUB DoSomething
CallSomething
END SUB
CallSomethingElse
脚本2:
CallSomethingElse
脚本3:
FUNCTION MyFunction
MyFunction = "Ok!"
END FUNCTION
同正常Basic一样,所声明的一个单行能由“:”字符分开。
标识符
同Basic,标识符一些例子:
有效的标识符:
VarName
_Some
V1A2
无效的标识符:
2Var
My Name
Some-more
This,is,not,valid
赋值声明
使用“=”字符。一些例子:
MyVar = 2
Button.Caption = "This " + "is ok."
字符串
使用(")字符。一些例子:
A = "This is a text"
Str = "Text "+"concat"
注释
使用(')或(REM)字符。一些例子:
' This is a comment before ShowMessage
ShowMessage("Ok")
REM This is another comment
ShowMessage("More ok!")
' And this is a comment
' with two lines
ShowMessage("End of okays")
变量
不需要在脚本中声明变数类型。这样,你宣布变量只需要使用DIM命令和变量的名字。如果脚本的OptionExplicit属性被设置为false,那么就不需要声明变量。在这种情况下,变数是暗示声明。如果你想要脚本上有更多的控制,设置OptionExplicit属性为true。如果OptionExplicit属性为true,而在脚本中使用了变量但不进行声明,将会唤醒一个编译错误。一些例子:
脚本1:
SUB Msg
DIM S
S = "Hello world!"
ShowMessage(S)
END SUB
脚本2:
DIM A
A = 0
A = A+1
ShowMessage(A)
如果脚本属性OptionExplicit被设置为false,那么DIM声明不需要出现在任何脚本的上方。
索引
字符串,数组和数组属性可以使用索引“[”并且“]”字符。更多例子:
MyChar = MyStr[2]
MyStr[1] = "A"
MyArray[1,2] = 1530
Lines.Strings[2] = "Some text"
数组
脚本支持数组和variant数组,同Pascal脚本的一样。注意:数组的索引是从0开始的。更多例子:
NewArray = [ 2,4,6,8 ]
Num = NewArray[1] //Num receives "4"
MultiArray = [ ["green","red","blue"] , ["apple","orange","lemon"] ]
Str = MultiArray[0,2] //Str receives 'blue'
MultiArray[1,1] = "new orange"
If声明
if语句有2种形式:if...then..end if和if...then...else..end if。更多例子:
IF J <> 0 THEN Result = I/J END IF
IF J = 0 THEN Exit ELSE Result := I/J END IF
IF J <> 0 THEN
Result = I/J
Count = Count + 1
ELSE
Done = True
END IF
While声明
同Basic。更多例子:
WHILE (Data[I] <> X) I = I + 1 END WHILE
WHILE (I > 0)
IF Odd(I) THEN Z = Z * X END IF
X = Sqr(X)
END WHILE
WHILE (not Eof(InputFile))
Readln(InputFile, Line)
Process(Line)
END WHILE
Loop声明
支持loop语句,功能同Basic中的loop语句。可能的语法是:
DO WHILE expr statements LOOP
DO UNTIL expr statements LOOP
DO statements LOOP WHILE expr
DO statement LOOP UNTIL expr
更多例子:
DO
K = I mod J
I = J
J = K
LOOP UNTIL J = 0
DO UNTIL I >= 0
Write("Enter a value (0..9): ")
Readln(I)
LOOP
DO
K = I mod J
I = J
J = K
LOOP WHILE J <> 0
DO WHILE I < 0
Write("Enter a value (0..9): ")
Readln(I)
LOOP
For声明
同Basic。更多例子:
脚本1:
FOR c = 1 TO 10 STEP 2
a = a + c
NEXT
脚本2:
FOR I = a TO b
j = i ^ 2
sum = sum + j
NEXT
Select声明
功能同Basic中的Select语句。可能的语法是:
SELECT CASE selectorExpression
CASE caseexpr1
statement1
…
CASE caseexprn
statementn
CASE ELSE
elsestatement
END SELECT
更多例子:
SELECT CASE uppercase(Fruit)
CASE "lime" ShowMessage("green")
CASE "orange"
ShowMessage("orange")
CASE "apple" ShowMessage("red")
CASE ELSE
ShowMessage("black")
END SELECT
函数和过程声明
函数和附属的声明与Basic相似。函数的返回值是使用暗示的变量,该变量同函数名一样。参数传地址也能被使用,使用BYREF命令。一些例子:
SUB HelloWord
ShowMessage("Hello world!")
END SUB
SUB UpcaseMessage(Msg)
ShowMessage(Uppercase(Msg))
END SUB
FUNCTION TodayAsString
TodayAsString = DateToStr(Date)
END FUNCTION
FUNCTION Max(A,B)
IF A>B THEN
MAX = A
ELSE
MAX = B
END IF
END FUNCTION
SUB SwapValues(BYREF A, B)
DIM TEMP
TEMP = A
A = B
B = TEMP
END SUB
脚本的工作
现在我们讲到了脚本的具体使用方法。
开始
开始脚本,只需要把脚本赋值给TatCustomScripter组件的SourceCode属性,并调用Execute方法。下面是一个简单的例子:
Scripter.SourceCode.Text:='ShowMessage(''Hello world!'');';
Scripter.Execute;
现在我们变得灵活一点。在窗口上放一个Tmemo组件,并把代码改为下面这样:
Scripter.SourceCode:=Memo1.Lines;
Scripter.Execute;
现在你可以在程序运行的时候在Memo组件中输入脚本,并运行它了。
公共任务
在脚本中调用程序中的子程序
如果脚本要直接调用程序中的一个或多个函数和过程,你只要使用ExecuteSubRouti方法就可以了:
Pascal示例 脚本: procedure DisplayHelloWorld; begin ShowMessage('Hello world!'); end;
procedure DisplayByeWorld; begin ShowMessage('Bye world!'); end; |
Basic示例 脚本: sub DisplayHelloWorld ShowMessage(“Hello world!”); end sub
sub DisplayByeWorld ShowMessage(“Bye world!”); end sub |
代码: Scripter.ExecuteSubRoutine('DisplayHelloWorld'); Scripter.ExecuteSubRoutine('DisplayByeWorld'); |
上面的代码将显示“Hello word!”和“Bye world!”消息框。
取得脚本返回的值
如果Delphi要取得脚本中的过程或函数返回的值,使用ExecuteSubRoutine方法。见下例:
Pascal示例 脚本: function Calculate; begin result:=(10+6)/4; end; |
Basic示例 脚本: function Calculate Calculate = (10+6)/4 end function
|
代码: FunctionValue:=Scripter.ExecuteSubRoutine('Calculate'); |
如果Delphi要取得脚本所返回的值。见下例:
脚本:
result:=(10+6)/4;
代码:
FunctionValue:=Scripter.Execute;
在Basic中要达到同样的效果,就要使用保留字“MAIN”:"MAIN = (10+6)/4"。
传递参数到脚本
如果要把Delphi变量的值传递给脚本,还是同样使用ExecuteSubRoutine方法见下例:
Pascal示例 脚本: function Double(Num); begin result:=Num*2; end; |
Basic示例 脚本: function Double(Num) Double = Num*2 End function
|
CODE: FunctionValue:=Scripter.ExecuteSubRoutine('Double',5); |
FunctionValue接收到10。如果你要传递更多的变量,就要使用一个Variant数组:
脚本:
function MaxValue(A,B);
begin
if A>B then
result:=A
else
result:=B;
end;
代码:
FunctionValue:=Scripter.ExecuteSubRoutine('MaxValue',VarArrayOf([5,8]));
脚本不需要参数类型,你只需要声明他们的名字就行了。
访问Delphi对象
注册组件
Scripter工作室最大的特点就是能访问Delphi对象。这使你在脚本中能有很大的灵活性,比如改变Delphi对象的属性,调用对象的方法等等。但是,每一个Delphi对象都必须经过注册后才能在脚本中访问。例如,你要改变窗口的标题(名字是Form1),如果你试着执行下面的脚本:
脚本:
Form1.Caption:='New caption';
你只会得到“Unknown identifier or variable not declared: Form1”的错误消息。要让脚本能够正常工作,必须使用AddComponent方法:
代码:
Scripter.AddComponent(Form1);
现在你再执行脚本:
Form1.Caption:='New caption';
窗口的标题就被改变了。
访问published属性
在增加一个组件后,你可以访问它的所有Published属性,这也是为什么窗口的标题可以改变的原因。否则你必须注册属性,实际上,Published属性已经被注册,除非你不在脚本中使用它。
类注册结构
脚本可以调用对象的方法和属性,但是所要调用的方法和属性必须在脚本中注册,而管理注册的关键的属性就是TatCustomScripter.Classes属性,这属性保存所有已注册的类(TatClass对象),它依次序保存所有已登记的属性和方法(TatClass.Methods and TatClass.Properties)。每种已注册的方法和属性都有一个名字和一个包装方法(Delphi写代码将句柄方法和属性)。
当你已注册一个Form1组件在一个例子上时,脚本自动注册TForm类在Classes属性,和注册所有published属性在它内部,要访问方法和Public属性,你必须注册它们,参见下列例子:
调用方法
调用一个对象方法,你必须注册它。例如,如果你重新创建了一个名字叫Form2的窗口,并且想要调用Form2窗口的ShowModal方法,因此我们必须使用AddComponent方法增加窗口到脚本,然后注册ShowModal方法:
代码:
procedure Tform1.ShowModalProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(TCustomForm(CurrentObject).ShowModal);
end;
procedure TForm1.PrepareScript;
begin
Scripter.AddComponent(Form2);
With Scripter.DefineClass(TCustomForm) do
begin
DefineMethod('ShowModal',0,tkInteger,nil,ShowModalProc);
end;
end;
脚本:
ShowResult:=Form2.ShowModal;
这个例子有许多新概念。首先,组件增加了AddComponent方法;然后,调用DefineClass方法注册TCustomForm类。DefineClass方法自动检查TCustomForm类是否已经被注册,这样你不需要测试它。
最后,使用DefineMethod方法注册ShowModal。DefineMethod的宣告是:
function DefineMethod(AName:string; AArgCount:integer; AResultDataType: TatTypeKind;
AResultClass:TClass; AProc:TMachineProc; AIsClassMethod:boolean=false): TatMethod;
AName接收“ShowModal” - 它是用于脚本的方法的名字。
AArgCount接收0 - 数字输入意见对于那方法。(没有,ShowModal过程在这情况下)
AResultDataType接收tkInteger - 它是方法返回的数据类型。ShowModal返回一个整数,如果ShowModal方法不是一个函数而是一个过程,AResultDataType将会收到tkNone。
AResultClass接收nil - 如果方法返回一个对象(不是这个情况),那么AResultClass必须包含对象类型。例如,TField。
AProc接收ShowModalProc - 这个方法书写用户包装ShowModal工作的方法。
最后,有ShowModalProc方法,它是一个包装工作的方法:它执行一次ShowModal调用,在这种情况下,它使用一些TatVirtualMachine类的有用的方法和属性。
属性CurrentObject - 包含对象方法的实例。因此,它包含一个TCustomForm的实例。
在这种情况下,返回值被送回TCustomForm.ShowModal方法。
更多方法调用例子
除了先前的例子,这一个举例说明怎么注册和调用方法所接收到的参数和返回类。
在这个例子,FieldByName:
脚本:
AField:=Table1.FieldByName('CustNo');
ShowMessage(AField.DisplayLabel);
代码:
procedure TForm1.FieldByNameProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(integer(TDataset(CurrentObject).FieldByName(GetInputArgAsString(0))));
end;
procedure TForm1.PrepareScript;
begin
Scripter.AddComponent(Table1);
With Scripter.DefineClass(TDataset) do
begin
DefineMethod('FieldByName',1,tkClass,TField,FieldByNameProc);
end;
end;
类似先前的例子,一些注释:
- FieldByName方法是注册在TDataset类,任何TDataset后代都允许在脚本内使用FieldByName方法。如果FieldByName已注册在一个TTable类,并且组件是一个TQuery,那么脚本将不认可这个方法。
- DefineMethod调用定义了的FieldByName收到一个参数,它的返回类型是tkClass,和类返回是TField。
- 在FieldByNameProc内,调用GetInputArgAsString方法是为了顺序取得输入参数。索引0表示第一个参数,为了接收2个或多个参数,使用GetInputArg(1)、GetInputArg(2),等等。
- 在使用ReturnOutputArg的情况下,我们需要舍弃TField返回的整数值,这必须不返回任何对象,这是因为ReturnOutputArg接收任何一个Variant类型,和对象必须舍弃整数值。
访问非published属性
就和方法一样,非公布的属性必须注册,其注册机制和注册方法很相似,其差别我们必须指出一个包装取得属性值和另外一个设置属性值。在下列例子,那个TField 类的“AsFloat”属性已登记:
脚本:
AField:=Table1.FieldByName('Company');
ShowMessage(AField.Value);
代码:
procedure TForm1.GetFieldValueProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(TField(CurrentObject).Value);
end;
procedure TForm1.SetFieldValueProc(AMachine: TatVirtualMachine);
begin
With AMachine do
TField(CurrentObject).Value:=GetInputArg(0);
end;
procedure TForm1.PrepareScript;
begin
With Scripter.DefineClass(TField) do
begin
DefineProp('Value',tkVariant,GetFieldValueProc,SetFieldValueProc);
end;
end;
DefineProp是调用经过一个tkVariant标志那值属性是变量类型,然后通过两个方法GetFieldValueProc和SetFieldValueProc,依次读写TField对象的属性值。注意在SetFieldValueProc方法是使用GetInputArg(代替GetInputArgAsString),这是因为GetInputArg返回一个变量。
注册索引属性
一个属性可以被索引,特别当它是一个TCollection子孙时。这适用于dataset fields, grid columns, string items,等等。TStrings对象的Strings属性加进其他改变到Memo内容:
脚本:
ShowMessage(Memo1.Lines.Strings[3]);
Memo1.Lines.Strings[3]:=Memo1.Lines.Strings[3]+' with more text added';
//This is a comment
代码:
procedure TForm1.GetStringsProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(TStrings(CurrentObject).Strings[GetArrayIndex(0)]);
end;
procedure TForm1.SetStringsProc(AMachine: TatVirtualMachine);
begin
With AMachine do
TStrings(CurrentObject).Strings[GetArrayIndex(0)]:=GetInputArgAsString(0);
end;
procedure TForm1.PrepareScript;
begin
Scripter.AddComponent(Memo1);
With Scripter.DefineClass(TStrings) do
begin
DefineProp('Strings',tkString,GetStringsProc,SetStringsProc,nil,false,1);
end;
end;
访问Delphi函数、变量和常量
除了能访问Delphi对象,脚本还允许访问全局常量、全局变量、过程和函数,其访问机制和访问Delphi对象非常相似。实际上,脚本在内部认为过程和函数是方法,全局变量和全局常量是属性。
注册全局常量
注册一个常量在脚本内是一个非常简单的工作:使用AddConstant方法增加一个常量和名字在脚本内。
代码:
Scripter.AddConstant('MaxInt',MaxInt);
Scripter.AddConstant('Pi',pi);
Scripter.AddConstant('MyBirthday',EncodeDate(1992,5,30));
脚本:
ShowMessage('Max integer is '+IntToStr(MaxInt));
ShowMessage('Value of pi is '+FloatToStr(pi));
ShowMessage('I was born on '+DateToStr(MyBirthday));
访问全局变量
要在脚本中注册一个全局变量,你必须使用AddVariable方法,与增加全局常量相类似:传递变量名字和变量自己。另外,你也可以用增加属性的方法增加变量,使用一个包装方法取得变量值和设置变量值。
代码:
var
MyVar: Variant;
ZipCode: string[15];
procedure TForm1.GetZipCodeProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(ZipCode);
end;
procedure TForm1.SetZipCodeProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ZipCode:=GetInputArgAsString(0);
end;
procedure TForm1.PrepareScript;
begin
Scripter.AddVariable('ShortDateFormat',ShortDateFormat);
Scripter.AddVariable('MyVar',MyVar);
Scripter.DefineProp('ZipCode',tkString,GetZipCodeProc,SetZipCodeProc);
Scripter.AddObject('Application',Application);
end;
procedure TForm1.Run1Click(Sender: TObject);
begin
PrepareScript;
MyVar:='Old value';
ZipCode:='987654321';
Application.Tag:=10;
Scripter.SourceCode:=Memo1.Lines;
Scripter.Execute;
ShowMessage('Value of MyVar variable in Delphi is '+VarToStr(MyVar));
ShowMessage('Value of ZipCode variable in Delphi is '+VarToStr(ZipCode));
end;
脚本:
ShowMessage('Today is '+DateToStr(Date)+' in old short date format');
ShortDateFormat:='dd-mmmm-yyyy';
ShowMessage('Now today is '+DateToStr(Date)+' in new short date format');
ShowMessage('My var value was "'+MyVar+'"');
MyVar:='My new var value';
ShowMessage('Old Zip code is '+ZipCode);
ZipCode:='109020';
ShowMessage('Application tag is '+IntToStr(Application.Tag));
调用函数和过程
在脚本中,函数和过程是增加如同方法,差别是你不能增加过程在任何一个类,但是在脚本自己,使用DefineMethod方法,下面的例子说明如何增加QuotedStr和StringOfChar方法:
脚本:
ShowMessage(QuotedStr(StringOfChar('+',3)));
代码:
{ TSomeLibrary }
procedure TSomeLibrary.Init;
begin
Scripter.DefineMethod('QuotedStr',1,tkString,nil,QuotedStrProc);
Scripter.DefineMethod('StringOfChar',2,tkString,nil,StringOfCharProc);
end;
procedure TSomeLibrary.QuotedStrProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(QuotedStr(GetInputArgAsString(0)));
End;
procedure TSomeLibrary.StringOfCharProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(StringOfChar(GetInputArgAsString(0)[1],GetInputArgAsInteger(1)));
end;
procedure TForm1.Run1Click(Sender: TObject);
begin
Scripter.AddLibrary(TSomeLibrary);
Scripter.SourceCode:=Memo1.Lines;
Scripter.Execute;
end;
使用Libraries
在脚本中,你可以使用Libraries中已注册的函数和方法,看下面的2段代码,第一段使用库和第二段使用一般的文档机制:
代码1:
type
TExampleLibrary = class(TatScripterLibrary)
protected
procedure CurrToStrProc(AMachine: TatVirtualMachine);
procedure Init; override;
end;
procedure TExampleLibrary.Init;
begin
Scripter.DefineMethod('CurrToStr',1,tkInteger,nil,CurrToStrProc);
end;
procedure TExampleLibrary.CurrToStrProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(CurrToStr(GetInputArgAsFloat(0)));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Scripter.AddLibrary(TExampleLibrary);
Scripter.SourceCode:=Memo1.Lines;
Scripter.Execute;
end;
代码2:
procedure TForm1.PrepareScript;
begin
Scripter.DefineMethod('CurrToStr',1,tkInteger,nil,CurrToStrProc);
end;
procedure TForm1.CurrToStrProc(AMachine: TatVirtualMachine);
begin
With AMachine do
ReturnOutputArg(CurrToStr(GetInputArgAsFloat(0)));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
PrepareScript;
Scripter.SourceCode:=Memo1.Lines;
Scripter.Execute;
end;
两段代码都做同样的事情:增加CurrToStr过程到脚本,注意脚本的初始化方法(代码1的初始化和代码2的PrepareScript)是一样的,和CurrToStrProc方法-没有差别。这两段代码间的差别是:
- 类和方法的属于,在代码1,方法属于一个专门的类TExampleLibrary,它是从TatScripterLibrary继承下来的,在代码2,它属于当前窗口(TForm1)。
- 在代码1,脚本是增加一个类TExampleLibrary到它自己,使用AddLibrary方法,在代码2,PrepareScript方法是直接调用。
除了使用AddLibrary方法,你可以使用RegisterScripterLibrary过程,见例子:
RegisterScripterLibrary(TExampleLibrary);
RegisterScripterLibrary是注册Library到一个全局列表到一个全局过程,所有脚本都会自动应用到Library,如果你想所有脚本组件都能使用Library,就使用RegisterScripterLibrary。使用AddLibrary只能增加一个Library到指定的组件。
TatPascalFormScripter组件和TatBasicFormScripter组件
TatPascalFormScripter组件和TatBasicFormScripter组件是从TatPascalScripter组件和TatBasicScripter组件继承下来的。它们都拥有祖先的所有功能,同时又注册了自己所特有的功能。
因此,如果你想要在窗口中使用脚本访问组件,类似于按钮、编辑框等等,你可以在脚本中注册窗口组件。
调试脚本
Scripter工作室包含组件和方法允许在程序运行时间调试脚本。主要有2种方法调试脚本:1、使用脚本组件方法和属性;2、使用调试组件。使用方法和属性能留下更多的自由给程序员,并且你可以使用他们建立一个属于你自己的调试环境。使用组件可以进行更高级别的调试,在大多数情况下你只需要拖动一个组件和在开始调试前调动一个方法就行了。
使用调试的方法和属性
脚本组件有几个允许调试脚本的方法和属性,在Delphi代码内你可以按照自己的需要使用他们,他们的列表在下面:
property Running: boolean;
读/写属性,当正在执行脚本的时候,Running是True。注意脚本可以暂停,但它仍然在运行,设置Running属性为True与调用Execute方法的效果一样。
property Paused: boolean read GetPaused write SetPaused;
读/写属性,使用它可以暂停脚本运行或取得脚本运行的状态。
procedure DebugTraceIntoLine;
执行当前行,如果当前行包含一个子程序调用,执行指针转到子程序的第一行。
procedure DebugStepOverLine;
执行当前行和执行指针转到下一行代码,如果当前行包含一个子程序调用,它运行完整个子程序,和Delphi中的Step Over选项相似。
procedure DebugRunUntilReturn;
执行代码直到当前子程序(过程、函数或主脚本块)是完成,执行指针停止在调用子程序的下一行。
procedure DebugRunToLine(ALine:integer);
执行脚本直到指定行,类似于Delphi的Run to Cursor选项。
function DebugToggleBreakLine(ALine:integer):pSimplifiedCode;
激活/禁止一个指定行的断点,如果断点设置是True,执行停止在断点所设置的行。
function DebugExecutionLine:integer;
返回调试行的计数值。
procedure Halt;
停止脚本执行,不管执行指针在那里。
property Halted: boolean read GetHalted;
在调用Halt方法或脚本已经停止后,该属性返回True。
property BreakPoints: TatScriptBreakPoints read GetBreakPoints;
包含脚本中设置的断点,你可以使用Items[Index]属性或者BreakPointByLine(ALine: integer)方法访问断点,一旦你访问断点,你可以设置属性Enabled(如果断点是活跃的,就会显示出来)和PassCount(它显示有多少次执行经过断点,直到执行结束)。
property OnDebugHook: TNotifyEvent read GetOnDebugHook write SetOnDebugHook;
在调试期间(step over,step into等等),每执行一行都会调用OnDebugHook事件。
property OnPauseChanged: TNotifyEvent read GetOnPauseChanged write SetOnPauseChanged;
property OnRunningChanged: TNotifyEvent read GetOnRunningChanged write SetOnRunningChanged;
只要paused或running属性改变,事件都会被调用。
使用调试组件
Scripter工作室有一个特定的调试组件,它是TatScriptDebugDlg,它的使用非常简单:拖动它到窗口上和访问它的Scripter属性在一个存在的脚本组件,调用Execute方法和debug对话框将出现,显示脚本源代码和一个工具条出现在窗口顶端,你可以使用工具条上的按钮或快捷键执行调试行为(run, pause, step over, and so on),快捷键和Delphi的一样。
F4 – Run to cursor
F5 – Toggle breakpoint
F7 – Step into
F8 – Step Over
F9 – Run
Shift+F9 - Pause
Ctrl+F2 – Reset
Shift+F11 – Run until return