前言:本文用于PowerBuilder12.6程序员,PowerBuilder最初由Sybase发布,能快捷开发C/S程序或者多层应用系统。SAP以58亿美元现金收购Sybase,PowerBuilder 12.6是SAP收购Sybase后与2014发布的版本。这是第三部分:声明语句。
声明语句
1.变量声明
在PowerBuilder 脚本使用变量之前,必须声明之,予之一个数据类型和一个名称。
变量可以是标准数据类型、结构、或者对象。对象可以是Browser展示的系统对象,也可以是自己定义的继承自系统对象的对象。对于大多数变量,声明之后就能赋值,在脚本内总是可以赋值。
1.1变量声明位置
可以通过选择变量声明的位置来决定PowerScript 变量的范围。变量实例有其他访问关键字,限制指定脚本访问变量。
下表展示了变量的四种范围:
范围 | 描述 |
全局Global | 应用程序内任何地方都可以访问。独立于任何对象的定义。 |
实例Instance | 从属于一个对象,并和这个对象的实例关联,可以认为是这个对象的属性。变量实例有访问关键词,决定其他对象的代码是否能访问他们。他们能从属于应用程序对象、窗口、用户对象菜单。 |
共享Shared | 从属于对象定义,并存在于该对象的所有实例中。当对象关闭和打开时共享(Shared )变量仍然保持他们的值。 共享(Shared )变量总是私有的(private)。他们只能在对象脚本和对象关联控件中可访问。他们可以属于应用对象(application object)、窗口(window)、用户对象(user object)或者菜单(menu)。 |
本地Local | 临时变量,只能在定义的脚本中访问。当脚本执行完成的时候变量常常不存在了。 |
(1)全局、实例和共享变量的声明
全局Global,实例instance, 和共享shared 变量可以定义在Application, Window, User Object, or Menu绘制区的Script视图窗口中。全局变量也可以定义在Function 绘制去中:
- 从Script视图的第一个下拉列表中选择Declare
- 从Script视图的第二个下拉列表中选择变量类型
- 在Script视图的脚本区中键入声明
(2)本地变量的声明
在对象或者控件的脚本中可以声明对象或者控件的本地变量。
(3)SQL游标的声明
也可声明SQL游标为全局、共享、实例或者本地。在Script视图中打开指定脚本或者选择变量声明范围,键入DECLARESQL语句,或者从PainterBar /弹出菜单中选择Paste SQL。
1.2变量使用
为了在PowerBuilder 脚本中使用或者设置变量值,需要命名这个变量。变量必须被编译器知晓,换句话说,变量必须在可见范围内。
可以在需要其值的地方使用变量,用作函数参数,或者用于赋值语句中。
(1)如何寻找变量?
当PowerBuilder 执行脚本,查找变量的无条件引用时,按照一下顺序搜索变量:
- 本地变量local variable
- 共享变量shared variable
- 全局变量global variable
- 实例变量instance variable一旦PowerBuilder 按指定名称找到变量,就可以使用变量值。
(2)全局变量的引用
引用全局变量,需要在脚本中指定其名称。如果全局变量有和本地变量或者共享变量一样的名称,本地变量或共享变量优先。这个时候要引用全局变量,使用全局操作符(::),如下:
::全局变量名称
例如,下面这句比较本地变量值和全局变量值:
IF total < ::total THEN ...
(3)引用实例变量
如果应用程序打开了对象的实例,则可引用实例变量。根据情况,可能需要限定实例变量的名称。
不使用对象名称来限定实例变量的情况如下:
- 针对应用程序级别的变量,在程序对象的脚本中
- 针对窗口级别的变量,在窗口自身的脚本中,以及在这个窗口的控件的脚本中
- 针对用户对象级别的变量,在用户对象自身的脚本中,以及在这个用户对象的控件的脚本中
- 针对菜单级别的变量,在菜单对象的脚本中,菜单对象可以是商机菜单,或者菜单脚本
例如,如果 w_emp有一个实例变量 EmpID,那么不需要限定名称w_emp就可以引用 EmpID,如下:
sle_id.Text = EmpID
其他情况都需要含有点号的限定名称,如下:
object.instancevariable
这适用于Public实例变量,不能引用对象之外的Private实例变量,不管是否使用限定名称。
例如,从窗口外部脚本中引用w_emp 的实例变量 EmpID ,需要使用窗口名称来限定变量,如下:
sle_ID.Text = w_emp.EmpID
另外一个情况,引用必须限定。假设w_emp 有一个实例变量EmpID ,在w_emp 中有一个CommandButton ,在其Clicked 脚本中声明了本地变量EmpID ,这时候必须限定,如下:
Parent.EmpID
(4)使用代词作为名称限定符:
可以总是使用限定名称来引用对象变量,这样可以避免模糊引用。
通用代码中,可使用代词This 和Parent 来限定引用对象。不使用具体名称来引用队形可以让脚本更通用。
- 在窗口脚本中的窗口变量:In a window script, use the pronoun This to qualify the name of a window instance variable. 在窗口脚本中,使用代词This来限定窗口实例变量的名称。例如,如果窗口已有实例变量index,那么下面这两句在窗口脚本内是等效的,条件是没有名为index的本地变量和全局变量。
index = 5
This.index = 5
- 在控件脚本中的窗口变量:窗口控件的脚本中,使用代词Parent 来限定窗口实例变量的名称,窗口是控件的父对象,下面这个例子中,两句是等效的,只要没有名为index的本地变量或者全局变量:
index = 5
Parent.index = 5
- 命名错误如果本地变量或者全局变量存在名称index,那么无限定名称引用本地或者全局变量,会出现一个程序错误,编译器提示一个消息。
1.3变量声明语法
最简单的方式是,数据类型+变量名称,如下:
datatype variablename
(1)完整语法
完整语法允许指定access关键词以及初始值。数组和一些数据类型,例如blob和decimal,可以接受其他信息:
{ 访问类型} 数据类型{ { 大小}} { { 精度}} 变量名称1 { = 值1 }
{, 变量名称2 { = 值2 } }
变量声明参数说明 | |
参数 | 描述 |
访问类型 (可选) | (仅仅针对实例变量) 用于指定变量的访问类型的关键词 |
数据类型 | 变量的数据类型。可以指定为标准数据类型、系统对象或者已经定义的结构。 对于blob和 decimal,可以指定变量数据的大小或者精度。 |
{ 大小 } (可选) | (仅仅针对blob)blob的字节数量。如果不指定大小,那么blob初始大小为0,PowerBuilder 在运行时调整其大小。 如果指定了大小,那么PowerBuilder 会截断blob数据。 |
{ 精度 } (可选) | (仅仅针对decimal) 使用大括号限定的一个数字,用于指定小数点后数字的数量。如果没有指定精度,那么变量就会接受脚本中指定的精度。 |
变量名称 | 变量的名称,必须是PowerScript有效标识符。 使用同样的数据类型来定义其他变量,应该使用逗号分隔其他变量名称。每个变量都可以有初始值。 |
值1、值2 (可选) | 对应数据类型的文字或者表达式,作为变量的初始值。Blob类型不能有初始值。 |
示例:
- 实例变量声明
integer ii_total = 100 // 总成本
date id_date // 购买日期
- 全局变量声明
string gs_name
- 共享变量声明
time st_process_start
string ss_process_name
- 本地变量声明
string ls_city = "Boston"
integer li_count
- Blob变量声明
下面的ib_Emp_Picture 是一个blob变量,初始长度为0.当赋值时长度会调整:
blob ib_Emp_Picture
下面的ib_Emp_Picture a blob 指定了100字节固定长度:
• blob{100} ib_Emp_Picture
- 小数变量声明
下面的语句声明了共享变量sc_Amount 和sc_dollars_accumulated,小数点后面带两位数字:
decimal{2} sc_Amount
decimal{2} sc_dollars_accumulated
下面这句声明了 lc_Rate1 and lc_Rate2 ,小数点后面带四位数字:
dec{4} lc_Rate1, lc_Rate2
下面这句声明了 lc_Balance ,小数点后面带0位数字:
decimal{0} lc_Balance
下面这句没有指定lc_Result小数的位数,但是,在lc_Op1 乘以lc_Op2 赋值给lc_Result之后,就带有四位小数的数字了:
dec lc_Result
dec{2} lc_Op1, lc_Op2
lc_Result = lc_Op1 * lc_Op2
(2)变量的数据类型
PowerScript 变量可以声明为以下数据类型之一:
- 标准数据类型 (例如,整数、字符串)
- 控件或者对象 (例如,window、CommandButton).
- 自行定义的对象或者结构 (例如,自行定义的窗口:mywindow),这个对象必须在库中,在应用程序库搜索路径中。
(3)变量名称
在规划良好的应用程序中,规范决定了如何命名变量。命名规范可以让脚本更容易理解,避免名称冲突。典型的方法是引入前缀来标识范围和数据类型。
- X 和Y 作为变量名称尽管可以把 x 和 y 作为变量名称,但是PowerBuilder也把它们当做对象的屏幕坐标属性。如果使用它们作为变量,而忘记声明它们,程序员不会获得编译器的错误提示。相反,PowerBuilder 假定程序员想移动对象,可能导致不可以预料的结果。
(4)变量的初始化
声明变量时,可以指定初始值,或者接受默认的初始值。
- 默认初始值
如果声明时不初始化,PowerBuilder 会设置变量为默认初始值,如下表:
变量的默认初始值 | |
数据类型 | 默认初始值 |
Blob | 0长度,空 |
Char (或者character) | ASCII码,0 |
Boolean | false |
Date | 1900-01-01 (1900年1月1日,) |
DateTime | 1900-01-01 00:00:00 |
Numeric (byte, integer, long, longlong, decimal, real, double, UnsignedInteger, 和UnsignedLong) | 0 |
String | 空串 ("") |
Time | 00:00:00 (午夜) |
- 指定文本作为初始值
声明时初始化变量,放置等号以及和数据类型相匹配的文本。
警告:不要用函数的返回值来初始化
不应该通过全局用户定义的函数来初始化变量,因为可能不能编译这个函数,或者可能导致赋值混乱。例如:不能使用下句:
integer i = f_return_one()
尽管可以使用全局系统函数或者表达式来初始化变量,但是必须让声明语句和赋值语句分离。
下面这个例子声明了 li_count 为整数,初始值为5:
integer li_count=5
下面这个例子声明了li_a 和li_b 为整数,初始化为5和10:
integer li_a=5, li_b=10
下面这个例子初始化 ls_method ,初始化为 "UPS":
string ls_method="UPS"
下面这个例子初始化 ls_headers 为三个使用tab键分开的三个单词:
string ls_headers = "Name~tAddress~tCity"
下面这个例子初始化 li_a 为1 ,li_c 为100, 而li_b 使用默认的初始值0:
integer li_a=1, li_b, li_c=100
下面这个例子声明 ld_StartDate 为日期date,并初始化为2004年2月1日:
date ld_StartDate = 2004-02-01
- 使用表达式初始化
可以使用现成的变量或者表达式来初始化变量,
integer i = 100
integer j = i
以上第二个变量使用表达式来初始化,初始化在编译时确定,而不是运行时。
如果表达式的值在运行时有变化,则不应该在声明时初始化,而是让声明和赋值分为两句。下面这句中, d_date 值是编译时的日期:
date d_date = Today( )
而下面这句,d_date 的值就是运行时的日期:
date d_date
d_date = Today( )
- 共享变量如何初始化
当使用共享变量时,变量初始化于对象实例首次打开之时。 当对象关闭时共享变量持续存在直到退出应用程序。如果不退出应用程序而再次打开对象,共享变量将会持续拥有该值。
例如,如果在窗口脚本中设置共享变量Count为20,那么关闭窗口,但不退出应用程序,然后重新打开窗口,Count将依旧为20.
如果存在多个窗口实例,Count变量在每个窗口实例中均为20.因为共享变量是在窗口的所有实例中共享的,在任何一个实例中修改Count,其他实例也会修改。
- 实例变量如何初始化
当对窗口、菜单、应用程序对象定义实例变量时,实例变量初始化于对象打开之时。初始化值是其数据类型的默认值,或者是变量声明中指定的值。
当关闭对象时,实例对象不再存在。当再次打开对象时,实例变量重新初始化。
何时使用窗口多个实例?实例变量在窗口的每个实例中有不同的值,例如:要设置一个基于窗口实例的内容的标志,就会使用实例变量。
何时使用共享变量替代?如果需要一个变量做完成如下目的就应该使用共享变量而不是实例变量:
- 在对象的多个实例中保持相同的值
- 对象关闭后持续存在
(5)实例变量的访问方式
声明变量的通用语法显示程序员可以在实例变量的声明语句中指定访问关键词。本节描述这些关键词。
当指定变量的访问权限时,可以控制变量的“能见度”或者它的访问视野。访问方式决定了在哪一个脚本中能识别变量名称。
对于指定的访问权限,可以控制修饰关键词的正常访问。修饰语指定了哪一个脚本能读取变量值和哪一个脚本能修改变量值。
语法如下:
{ 访问权限 } { 读取方式} { 修改方式} 数据类型变量名称
下表描述了能用来指定访问权限的参数。
实例变量访问权限的声明参数 | |
参数 | 描述 |
访问权限 | 变量名称在何地能被识别的关键词,可能值包括: PUBLIC – (默认) 应用程序的任何脚本都能引用变量。而在其他对象的脚本中,可使用点符号来限定变量名称和识别对象归属。 PROTECTED – 变量声明的对象及其子孙对象可以引用变量。 PRIVATE – 在变量声明的对象脚本中可以引用变量,而子孙对象不能引用变量 |
读取方式 | 限定变量读取方式的关键词,可能值包括: PROTECTEDREAD – 仅仅对象及其子孙可以读变量 PRIVATEREAD – 仅仅对象自身可以读变量 当访问权限是PUBLIC时,可以使用其中一个关键词。当访问权限是PROTECTED时,仅仅能使用PRIVATEREAD. 不能指定访问权限为PRIVATE , 因为PRIVATE已经限定死了 如果读取方式被省略,那么任何脚本都能读取变量。 |
修改方式 | 变量修改方式关键词,可能值包括: PROTECTEDWRITE – 变量声明的对象及其子孙可以修改变量 PRIVATEWRITE – 变量声明的对象可以修改变量 当访问权限为PUBLIC时可以指定两者之一,当访问权限是PROTECTED时,仅仅可以指定PRIVATEWRITE。访问权限不能是PRIVATE,因为PRIVATE已经限定死了。如果修改方式被省略,那么任何脚本都可以修改变量。 |
数据类型 | 有效的数据类型 |
变量名称 | 有效的标识符 |
- 用法
这三种访问修饰符能够给予更多的对象访问控制能力,典型的用法是声明public变量,仅仅允许脚本内对象修改它,如下:
public protectedwrite integer ii_count
可以批量声明有同样访问方式的变量。
当查看输出对象语法时,可以发现访问修饰符有SYSTEMREAD 和SYSTEMWRITE。只有PowerBuilder系统本省能带这些修饰符来访问变量。不能在脚本中引用带这些修饰符的变量,也不能在自己的定义中使用这些修饰符。
声明实例变量的方法是:在绘制区中选择Declare>Instance Variables。
- 示例
下面这几句使用访问权限关键词来控制脚本访问变量的能力:
private integer ii_a, ii_n
public integer ii_Subtotal
protected integer ii_WinCount
下面这个protected 变量仅能在对象自身脚本中被修改,而子孙能读取之:
protected privatewrite string is_label
下面这句默认使用了public访问权限,仅能在对象自身的脚本中被修改:
privatewrite real ir_accum, ir_current_data
下面这句定义了一个整数,仅仅对象自身能读写,但是名称的访问方式还是预留为public:
public privateread privatewrite integer ii_reserved
Private变量不能在对象之外被识别:假设定义了一个窗口w_emp,并定义了private 整数变量 ii_int:
private integer ii_int
然后,在另外一个脚本中定义了窗口的一个实例叫 w_myemp. 如果引用private 变量ii_int,那么程序员会获得一个编译器警告:变量未定义。因为变量是私有的,它不能在窗口本身之外被识别:
w_emp w_myemp
w_myemp.ii_int = 1 // 错误:变量未定义
Public变量携带访问方式限定:假定定义了窗口w_emp 并在其内定义了带有私有修改范式的公有整形变量ii_int:
public privatewrite integer ii_int
然后,同样在另外一个脚本中定义了窗口的一个实例叫 w_myemp,同样引用ii_int,那么编译器警告:不能写变量,尽管这个变量声明为公有的,但是修改方式是私有的:
w_emp w_myemp
w_myemp.ii_int = 1 // Cannot write to variable
(6)访问权限关键词的第二种格式
访问权限关键词的第二种格式是,采用组声明方式来指定访问权限,访问权限是一种标签,关键词后面跟冒号,如下:
访问权限:
{ 读取方式} { 修改方式} 数据类型变量名称
{ 访问权限 } { 读取方式} { 修改方式} 数据类型变量名称
{ 读取方式} { 修改方式} 数据类型变量名称
在标签组声明中,可以重载访问权限。
- 示例
下面这几句,实例变量的访问权限由标签指定,直到另外一个标签才结束:
Private:
integer ii_a=10, ii_b=24
string is_Name, is_Address1
Protected:
integer ii_Units
double idb_Results
string is_Lname
Public:
integer ii_Weight
string is_Locatinotallow="Home"
private integer ii_test
下面这几句中,访问权限均为保护模式,但后两个变量限定了修改方式:
Protected:
integer ii_Units
privatewrite double idb_Results
privatewrite string is_Lname
2.常量声明
任何标准数据类型的PowerScript变量声明均可以声明为常数,只要使用关键词CONSTANT ,并赋予一个初始值,语法如下:
CONSTANT { 访问方式} 数据类型常量名称 = 值
下表说明了用于声明常量的参数:
参数 | 描述 |
CONSTANT | 声明一个常量。CONSTANT关键词可以放在访问方式关键词的前面或者后面 |
访问方式 | 仅仅针对实例变量,指定常量访问方式的关键词 |
数据类型 | 常量的标准数据类型。对于小数,应该使用包含可选值的括号来指定数据精度。Blob不能是常量。 |
常量名称 | 常量的名称,必须是一个有效的标识符 |
值 | 对应数据类型的文字或者表达式,也是常量的值,此处的值是必须提供的。 |
- 用法
当声明常量时,初始值是必须的。否则,编译器会报错。在声明常量之后赋值,也会产生编译错误。
- 示例
尽管PowerScript大小写不敏感,下面的例子还是使用大写命名规范:
constant string LS_HOMECITY = "Boston"
constant real LR_PI = 3.14159265
3.数组声明
数组是简单数据类型的元素有序集合。数组可以有一道多个维度。一维数组可以有固定或者可变长度,多维数组总是固定长度。数组的每个维度都可以有2,147,483,647字节长的元素。
任何简单变量声明都可以成为数组,只要在变量名后面指定括号。对于固定大小的数组,在括号里面指定维度的大小。
(1)语法
{ 访问方式} 数据类型变量名称{ 维度1, ..., 维度n } { = { 值列表} }
下表描述了数组变量的参数:
参数 | 描述 |
访问方式 | (仅仅针对实例变量) 指定变量访问方式关键词 |
数据类型 | 变量的数据类型。可指定标准数据类型、系统对象、自行定义的结构。 对于小数,可以指定精度,放在数据类型之后的括号中,精度可选,例如: decimal {2} variablename [ ] 对于blob, 不支持固定长度,如果在数据类型后指定长度,那么会被忽略。 |
变量名称 | 变量的名称,必须是有效标识符。 可以使用同一数据类型定义多个数组,多个数组使用逗号分隔。 |
[ { 维度1, ..., 维度n } ] | 中括号内指定一到多个整数,作为维度的长度。 对于可变长度数组,总是一维数组。 对于固定长度数组,维度数量由整数的数量来决定,可获得的内存大小决定了维度数量最大值。可使用TO来指定元素数量的范围,规定维度的边界: (注意上边界必须比下边界大) [维度1下边界 TO 维度1上边界 {, ... , |
{ 值列表 } | 初始值列表,对应数组的每个位置。使用逗号分隔,整个列表使用括号标记。值的数量不能比数组位置多。值的数据类型必须和数组声明的数据类型匹配。 |
- 示例
下面几句会创建变长数组:
integer li_stats[ ] // 整数数组
decimal {2} ld_prices[ ] // 带两位精度的小数数组
blob lb_data[ ] // 变长blob数组
date ld_birthdays[ ] // 日期数组
string ls_city[ ] // 字符串数组,其中每个字符串均是可变长度
下面语句声明了小数的可变长度数组,精度没有指定,因此每个元素的精度在赋值的时候决定:
dec lc_limit[ ]
下面几句创建了固定长度的一维数组:
integer li_TaxCode[3] // 三个整数的数组
string ls_day[7] // 7个字符串的数组
blob ib_image[10] // 10个blob元素的数组
dec{2} lc_Cost[10] // 10个小数的数组,每个元素有两位小数位数
decimal lc_price[20] // 20个小数的数组,精度在赋值时确定
下面几句使用TO来限定索引值的范围,都是固定长度的数组:
real lr_Rate[2 to 5] // 4个实数的数组,索引为2到5
integer li_Qty[0 to 2] // 3个整数的数组
string ls_Test[-2 to 2] // 5个字符串的数组
integer li_year[76 to 96] // 21个整数的数组
string ls_name[-10 to 15] // 26个字符串的数组
下面几句展示了TO的不正确使用。在数组维度中,第二个数字必须比第一个数字大:
integer li_count[10 to 5] // 无效语句,因为 10比5更大
integer li_price[-10 to -20] // 无效语句,因为-10比-20更大
以下几个例子展示了多维数组。
下面这句创建了含有6个元素的2维整数数组。单个元素依次是 li_score[1,1], li_score[1,2], li_score[1,3], li_score[2,1], li_score[2,2], and li_score[2,3]:
integer li_score[2,3]
下面这句定义了二维数组,维度索引分别为 1 到5 、10 到25:
integer li_RunRate[1 to 5, 10 to 25]
下面这句创建了3维数组,有45,000个元素:
long ll_days[3, 300, 50]
下面这句指定了第2、3维的下标范围:
integer li_staff[100, 0 to 20, -5 to 5]
以下是两种多维数据声明示例:
string ls_plant[3,10] // 二维数组,含有30个字符串元素
dec{2} lc_rate[3,4] // 两位数组,12个带2位小数位数的小数元素
下面这句创建了三维数组:
decimal{3} lc_first[10],lc_second[15,5],lc_third[ ]
(2)数组元素值
PowerBuilder初始化每个数组元素为对应数据类型的默认值。例如:
integer li_TaxCode[3]
元素 li_TaxCode[1], li_TaxCode[2], 和li_TaxCode[3] 都被初始化为0。
- 简单数组
在简单一维数组中,可以指定默认值,要使用逗号隔开,使用括号标记。可以不必初始化所有元素,但是不能在没有初始化第一个元素的情况下初始化中间或者末端的元素。
可以在声明数组之后使用同样的语法来初始化数组:
integer li_Arr[]Li_Arr = {1, 2, 3, 4}
- 多维数组
在多维数组中,仍然可以提供简单的逗号分隔的列表值。当初始化时,第一个维度是优先变化的,最后一个维度是最后变化的。
- 示例
实例1
下面这句声明带了三个元素的初始化:
real lr_Rate[3]={1.20, 2.40, 4.80}
实例2
下面这句初始化了二维数组:
integer li_units[3,4] = {1,2,3, 1,2,3, 1,2,3, 1,2,3}
结果是:
Li_units[1,1], [1,2], [1,3], 和[1,4] 都是 1
Li_units[2,1], [2,2], [2,3], 和[2,4] 都是 2
Li_units[3,1], [3,2], [3,3], 和[3,4] 都是 3
实例3
下面这句初始化了三维数组的上半部分:
integer li_units[3,4,2] = &
{1,2,3, 1,2,3, 1,2,3, 1,2,3}
结果为:
Li_units[1,1,1], [1,2,1], [1,3,1], 和[1,4,1] 都是1
Li_units[2,1,1], [2,2,1], [2,3,1], 和[2,4,1] 都是2
Li_units[3,1,1], [3,2,1], [3,3,1], 和[3,4,1] 都是3
Li_units[1,1,2], [1,2,2], [1,3,2], 和[1,4,2] 都是 0
Li_units[2,1,2], [2,2,2], [2,3,2], 和[2,4,2] 都是0
Li_units[3,1,2], [3,2,2], [3,3,2], 和[3,4,2] 都是 0
(3)变长数组的长度
变长数组包含的方括号没有数字,PowerBuilder在执行时确定数组长度。仅仅一维数组能声明为变长数组。
因为没有声明长度,所以不能使用TO来修改数组的边界,变长数组的下边界永远是1.
- EAServer组件的数组TO从句
当产生EAServer组件代理时,包含使用TO从句的数组,代理对象描述了单个值的范围,因为CORBA IDL不支持TO从句。例如:
Int ar1[5 TO 10]
实际上声明了Int ar1[6],[6]表示数组元素的数量。客户端应用程序必须声明单值数组而不是范围数组。
- 如何分配内存
初始化变长数组的元素,需要给这些元素分配内存。就像给固定长度数组指定初始值一样,通过括号包围的列表来完成初始化。下面的语句设置code[1] 为 11, code[2]为 242, 以及code[3]为 27。这个数组有3个初始值,数组长度为3,但是如果给数据初始化时,对应更高位置的元素,那么数组长度将大于3.
integer li_code[ ]={11,242,27} //长度为3
long ll_price[ ]
ll_price[100] = 2000
ll_price[50] = 3000
ll_price[110] = 5000 //长度为110
当上面这些语句第一次执行时,内存分配如下:
- 语句 ll_price[100]=2000 会分配内存给100个long数值: ll_price[1] 到ll_price[100], 然后分配0 (数值的默认值) 给ll_price[1] 到ll_price[99] ,最后分配2000 给ll_price[100]。
- 语句 ll_price[50]=3000 不会分配更多内存,但会赋值3000 给第50个元素。
- 语句ll_price[110]=5000将会分配内存给10个long数值:ll_price[101] 到ll_price[110],然后分配0给 ll_price[101] 到ll_price[109] ,最后分配5000 给ll_price[110]。
(4)两个数组之间的赋值
两个数组之间赋值采用如下规则:
- 一维数组
对于无边界数组,目标数组和源数据相同:
integer a[ ], b[ ]
a = {1,2,3,4}
b = a
对于有边界数组,如果源数组较小,从源数组拷贝元素的值到目标数组,多余的值被设置为0,例如下面的例子中,b[5] 和b[6] 被赋值为0:
integer a[ ], b[6]
a = {1,2,3,4}
b = a
如果源数组较大,从源数组拷贝值到目标数组,指导目标数组满。例如下面这个例子,数组b 仅仅用了数组a的头三个元素:
integer a[ ], b[3]
a = {1,2,3,4}
b = a
- 多维数组
PowerBuilder按列序存储多维数据,即第一个下标最先变化:[1,1], [2,1], [3,1]
当拷贝第一个数组到另外一个数组时,PowerBuilder按列序对源数组进行排序,让其变成一维数组。然后PowerBuilder按照一定规则来拷贝这个一维数组给目标数组。不是所有的数组赋值都是允许的,必须按照如下规则来进行:
第一,多维数组之间的拷贝
如果两个数组的维度匹配,目标数组成为源数组的精确拷贝:
integer a[2,10], b[2,10]
a = b
如果两个数组都是多维的,但是维度的长度不同,那么赋值不被允许,编译器会报错:
integer a[2,10], b[4,10]
a = b // 编译错误
第二,一维数组到多维数据
一维数组能赋值给多维数组,按列序映射到多维数据:
integer a[ ], b[2,2]
b = a
第三,多维数组到一维数组
多维数组也能赋值给一维数组。源数组按列序排序,然后赋值给目标数组:
integer a[ ], b[2,2]
a = b
- 实例
假定声明了三个数组 (a, b, 和c)。第一个(c)是无边界的一维数组;另外两个(a和b)是有不同维度的多维数据:
integer c[ ], a[2,2], b[3,3] = {1,2,3,4,5,6,7,8,9}
数组b的值如下表:
1 对b[1,1] | 4 对b[1,2] | 7 对b[1,3] |
2 对b[2,1] | 5 对b[2,2] | 8 对b[2,3] |
3 对b[3,1] | 6 对b[3,2] | 9 对b[3,3] |
下面这句会导致编译错误,因为a和b有不同的维度:
a = b // 编译错误
下面这句明确从b线性化后拷贝到c:
c = b
然后把线性化版本c拷贝到a:
a = c
数组a值如下表排列:
1 for a[1,1] | 3 for a[1,2] |
2 for a[2,1] | 4 for a[2,2] |
使用列表来初始化数组a,结果是一样的:
integer a[2,2] = {1,2,3,4}
(5)使用数组列表来给数组赋值
PowerBuilder中,数组列表是用于初始化数组的使用括号标记的值列表。数组列表描述了一维数组,其值可以赋值给目标数组,但是需要采用上一小节提到的赋值规则。
- 实例
下面这句,使用大括号标记的数组列表对变长数组初始化,初始值为四个值:
integer a[ ] = {1,2,3,4}
下面这句定长数组前四个值初始化为数组列表,其余值为0:
integer a[10] = {1,2,3,4}
下面这句,定长数组初始化,只使用数组列表的前四个值,数组列表中其余的值被忽略:
integer a[4] = {1,2,3,4,5,6,7,8}
下面这句,值1/2/3被赋值给第一列,其余的值赋值给第二列,注意第一维是行数,第二维是列数:
integer a[3,2] = {1,2,3,4,5,6}
1 | 4 |
2 | 5 |
3 | 6 |
如果把三维数组看着是页,每页有行和列,那么第一页的第一列有值1和2,第一页的第二列有值3和4,第二页的第一列有值5和6,第二页第二列均为0:
integer a[2,2,2] = {1,2,3,4,5,6}
1 | 3 | 5 | 0 | |
2 | 4 | 6 | 0 |
(6)寻址数组时产生的错误
- 定长数组
在PowerBuilder中引用数组元素超过声明长度的边界时,会产生运行时错误。例如:
int test[10]
test[11]=50 // 导致运行时错误
test[0]=50 // 导致运行时错误
int trial[5,10]
trial [6,2]=75 // 导致运行时错误
trial [4,11]=75 // 导致运行时错误
变长数组
赋值给变长数组的元素时,如果超出数组长度,那么数组长度会随之增长。但是,访问元素时超出上边界或者低于下边界,会产生运行时错误:
integer li_stock[ ]
li_stock[50]=200 // 构建有50个元素的数组
IF li_stock[51]=0 then Beep(1) // 导致运行时错误
IF li_stock[0]=0 then Beep(1) // 导致运行时错误
4.声明外部函数
外部函数是不用PowerScript语言写出来的函数,存在动态链接库中。在windows操作系统,动态库带有DLL扩展名,如果发布在Powerbuilder中编写的组件到UNIX服务器,那么动态库的扩展名为.so,.sl或者.a,具体依赖于具体的UNIX操作系统。可以使用外部函数,但是在使用前,必须声明为如下两种类型之一:
- 全局外部函数:可以在应用程序任何地方使用
- 本地外部函数:这些函数是为特殊类型的窗口、菜单、用户对象、用户定义函数定义的,也是对象定义的一个部分。总是可以在对象本身的脚本中使用。也可以使这些函数被其他脚本访问。
1.1语法
(1)外部函数语法
{ 访问方式} FUNCTION 返回数据类型名称( { { REF } 参数1类型 参数1,
..., { REF } 参数n类型 参数n } ) LIBRARY "库名"
ALIAS FOR "外部名称{;ansi}"
(2)外部子程序语法
除了不返回值之外,和外部函数语法相同:
{ 访问方式} SUBROUTINE 名称 ( { { REF } 参数1类型 参数1, ...,
{ REF } 参数n类型 参数n } ) LIBRARY "库名"
ALIAS FOR "外部名称{;ansi}"
(3)声明参数说明
参数 | 描述 |
访问方式 | (仅用于本地外部函数) Public, Protected, 或者Private,用来指定访问方式(等级),默认为Public. |
FUNCTION或者SUBROUTINE | 关键词,用于指定调用类型,决定返回值处理的方式。如果有返回值则使用,否则使用SUBROUTINE,返回VOID相当于没有返回值。 |
返回数据类型 | 返回值的数据类型 |
名称 | 函数或者子程序的名称,存在于DLL文件中。函数名称不能包含特殊字符,例如 @ 字符, 因为他们会导致编译错误。使用ALIAS FOR从句来关联DLL中的名称和此处的名称。 |
REF | 关键词,表示采用引用方式来传递参数。函数可以存值到arg参数中。 |
参数类型 参数 | 参数对,含参数的类型和名称。参数列表必须匹配DLL中的定义。每个参数对可以使用REF来标记引用方式。 |
LIBRARY"库名" | 关键词,后面跟动态库的名称字符串:库名。Windows操作系统中库名指动态链接库DLL文件的名称,而UNIX的EAServer中,指.so, .sl, 或者.a文件的名称 |
ALIAS FOR"外部名称" (可选) | 关键词,之后跟定义在动态库中的函数名称字符串。如果动态库中的名称不是你想要在脚本中使用的名称,或者数据库中的名称不是合法的PowerScript名称,那么需指定ALIAS FOR "外部名称"以便于建立PowerScript名称和外部名称之间的联系。 |
;ansi | 如果函数传递字符串作为参数,或者返回字符串,字符串使用ANSI编码,那么这个关键词是必须的。 即使程序员使用ANSI函数的默认名称,但是如果想使用ANSI编码指定字符串,也必须始终使用ALIAS关键字,因为必须使用ANSI关键字限定ALIAS。 |
1.2用法
指定本地函数的访问方式:
在声明一个本地外部函数时,可以指定它的访问级别,这个级别可以在脚本中访问到所定义的函数。
下表描述了使用本地函数的位置:
本地外部函数的访问权限的级别 | |
级别 | 使用本地函数的位置 |
Public | 应用程序中所有脚本均可访问之 |
Private | 函数声明所在对象事件的脚本可访问之。对象的子孙不能使用函数 |
Protected | 函数声明所在对象及其子孙的脚本可访问之 |
对本地外部函数使用access关键字的方法与对实例变量使用access-right关键字相同。
1.3运行时动态库的可用性
为了在Windows操作系统中让PowerBuilder应用程序可访问DLL,DLL必须在以下目录之一:
- 当前目录
- Windows目录
- Windows System目录
- 在DOS路径中的目录
如果部署PowerBuilder 自定义类的用户对象作为 EAServer 组件,必须保证任何引用的动态库 都能在服务器上能被访问。如果声明时没有指定动态库的位置,应该保证动态库安装在可以访问的位置:
- 在Windows服务器上,DLL必须是在服务器可执行文件的应用程序路径里面(通过环境变量控制)
- 在UNIX服务器上,共享库的位置必须在服务器库路径环境变量中列出 (例如, 在Solaris上LD_LIBRARY_PATH ) ,或者库文件必须位于EAServer安装的lib目录中。
1.4示例
在PowerBuilder附带的示例应用程序中,在名为u_external_function_win32的用户对象中,外部函数被声明为本地外部函数。调用函数的脚本是用户对象函数,但因为它们是同一个用户对象的一部分,所以不需要使用对象点符号来调用它们。
- 实例1
下面这句允许PowerBuilder 调用WINMM.DLL中的函数来请求播放声音:
//播放声音
FUNCTION boolean sndPlaySoundA (string SoundName, uint Flags)
LIBRARY "WINMM.DLL"
ALIAS FOR "sndPlaySoundA;ansi"
FUNCTION uint waveOutGetNumDevs () LIBRARY "WINMM.DLL"
在PowerBuilder提供的示例程序中,有一个名为uf_playsound的函数会调用外部函数。Uf_playsound有两个参数(as_filename和ai_option)传递给sndPlaySoundA。
ai_option的值在Windows文档中定义,如下注释所示:
//Options 定义在 mmystem.h文件中
//这些可以放在一起
//#define SND_SYNC 0x0000
//同步播放 (默认)
//#define SND_ASYNC 0x0001
//异步播放
//#define SND_NODEFAULT 0x0002
//不使用默认声音
//#define SND_MEMORY 0x0004
//lpszSoundName 指向一个内存文件
//#define SND_LOOP 0x0008
//循环声音直到下一个sndPlaySound
//#define SND_NOSTOP 0x0010
//不停止现有的声音播放
uint lui_numdevs
lui_numdevs = WaveOutGetNumDevs()
IF lui_numdevs > 0 THEN
sndPlaySoundA(as_filename,ai_option)
RETURN 1
ELSE
RETURN -1
END IF
- 实例2
这是Windows GetSysColor函数的声明:
FUNCTION ulong GetSysColor (int index) LIBRARY "USER32.DLL"
下面的语句调用外部函数GetSysColor,index参数和返回值的含义在Windows文档中有说明。
RETURN GetSysColor (ai_index)
- 实例3
下面这句声明了 Windows GetSysColor函数:
FUNCTION int GetSystemMetrics (int index) LIBRARY "USER32.DLL"
下面这两句调用外部函数来获取屏幕高度和宽度:
RETURN GetSystemMetrics(1)
RETURN GetSystemMetrics(0)
1.5外部函数参数的数据类型
在PowerBuilder中声明外部函数时,参数的数据类型必须与函数源定义中声明的数据类型相对应。本节记录外部函数中的数据类型与PowerBuilder中的数据类型之间的对应关系。它还包括在按值传递结构时关于字节对齐的信息。
使用这些表可以找出在外部函数声明中使用的PowerBuilder数据类型。所选择的PowerBuilder数据类型取决于函数源代码中的数据类型。第一列列出源代码中的数据类型。第二列描述数据类型,以便程序员确切地知道它是什么。第三列列出了应该在外部函数声明中使用的PowerBuilder数据类型。
(1)布尔型
Windows上的BOOL和Boolean是16位有符号的类型。两者在PowerBuilder中都声明为布尔值。
(2)指针型
PowerBuilder 指针的数据类型 | ||
源代码中的数据类型 | 长度、符号、精度 | PowerBuilder 数据类型 |
* (任意指针) | 32位指针 | Long |
char * | 变长的字节数组 | Blob |
Windows 32-bit FAR 指针, 诸如LPBYTE, LPDWORD, LPINT, LPLONG, LPVOID, and LPWORD, 在PowerBuilder中是当做long 数据类型来声明的。 HANDLE 是定义为32位无符号整数,在PowerBuilder 作为UnsignedLong类型来声明的。
近指针类型 (例如 PSTR 和NPSTR) 不被PowerBuilder支持。
(3)字符和字符串
PowerBuilder 字符和字符串相关数据类型 | ||
源代码中的数据类型 | 长度、符号和精度 | PowerBuilder 数据类型 |
char | 8 位,有符号 | Char |
string | 32位指针,指向变长的字节数字,使用null结尾 | String |
Windows 32位 FAR 指针LPSTR在PowerBuilder中作为字符串被声明的。
- 参数的引用
通过引用将字符串传递给外部函数时,所有内存管理都在PowerBuilder中完成。字符串变量必须足够长以保存返回值。为了确保这是正确的,首先声明字符串变量,然后使用Space函数用与期望函数返回的最大字符数相等的空格填充变量。
(4)定点值
PowerBuilder 定点值数据类型 | ||
源代码中的数据类型 | 长度、符号和精度 | PowerBuilder 数据类型 |
byte | 8 位, 无符号 | Byte |
short | 16 位, 有符号 | Integer |
unsigned short | 16 位, 无符号 | UnsignedInteger |
int | 32 位, 有符号 | Long |
unsigned int | 32 位, 无符号 | UnsignedLong |
long | 32 位, 有符号 | Long |
unsigned long | 32 位, 无符号 | UnsignedLong |
longlong | 64 位, 有符号 | LongLong |
Windows定义的WORD是在PowerBuilder中声明的unsignedteger, Windows定义的DWORD是UnsignedLong。不能调用具有short类型返回值或参数的外部函数。
(5)浮点值
PowerBuilder 浮点值的数据类型 | ||
源代码中的数据类型 | 长度、符号和精度 | PowerBuilder 数据类型 |
float | 32 位, 单精度 | Real |
double | 64 位, 双精度 | Double |
PowerBuilder在windows操作系统中不支持80位双精度。
(6)日期和时间
PowerBuilder数据类型Date、DateTime和Time是结构,在C外部函数中没有直接等效的定义。
(7)结构传值
可以传递PowerBuilder结构到外部C函数中,条件是他们有和结构组件同样的定义和对齐方式。DLL或者共享库必须使用字节对齐方式来编译 ,没有添加填充来对齐结构中的字段。
1.6外部函数调用
(1)全局外部函数
在PowerBuilder中,通过使用与调用用户定义的全局函数和系统函数相同的语法,程序员调用全局外部函数。与其他全局函数一样,全局外部函数可以被触发或投递,但不能动态调用。
(2)本地外部函数
通过使用与调用对象函数相同的语法,程序员调用本地函数。它们可以被触发、投递或者动态调用。
1.7外部函数定义源
可以使用32位操作系统中支持标准调用顺序的语言来编写外部函数。如果调用Windows自己写的库中的函数,不要忘记需要导出(export)函数。根据编译器的不同,可以在函数原型或者DEF文件中导出。
使用_stdcall协定
C 和C++ 编译器一般支持集中调用规范,包括 _cdecl (C语言的默认调用规范), _stdcall (Windows API的标准调用规范), _fastcall, 和thiscall。PowerBuilder和许多其他Windows开发工具一样,要求使用WINAPI (_stdcall) 方式来导出外部函数。试图使用不同的调用规范可能导致应用程序崩溃。
当创建自己的C 或者C++ DLL容纳能用于PowerBuilder的函数,需保证他们使用Windows API函数调用标准规范。
例如,如果正在使用DEF文件来导出函数定义,那么可以按如下格式声明函数:
LONG WINAPI myFunc()
{
...
};
5.声明存储过程
PowerBuilder可以使用点符号来调用无结果集的存储过程,和RPC一样:
object.function
可以用此方式调用Sybase, Oracle, Informix, 和其他 ODBC 数据库的存储过程,ODBC 数据库必须要支持存储过程。
RPC提供了Oracle PL/SQL 表和参数支持,有输入输出定义。可调用这些重新定义的RPC过程。
RPC过程应用于事务对象,语法如下:
FUNCTION rtndatatype functionname ( { { REF } datatype1 arg1,...,
{ REF } datatypen argn } ) RPCFUNC { ALIAS FOR "spname" }
SUBROUTINE functionname ( { { REF } datatype1 arg1 , ...,
{ REF } datatypen argn } ) RPCFUNC { ALIAS FOR "spname" }
RPC 声明参数说明 | |
参数 | 描述 |
FUNCTION或者SUBROUTINE | 关键词,用于指定调用类型,决定返回值处理方式。如果有返回值使用FUNCTION来声明,如果无返回或者返回VOID,则使用SUBROUTINE。 |
rtndatatype | 在FUNCTION声明中, 函数返回值的数据类型 |
functionname | 数据库过程的名称,用于在PowerBuilder中调用。如果DBMS中的名称不同,那么应该使用ALIAS FOR来关联。 |
REF | 指定引用传参方式。存储过程可以储存值在arg中。 当以引用方式传递字符串时,所有内存管理在 PowerBuilder中完成。字符串变量必须足够长以便于容纳返回值。为了保证之,首先声明字符串变量,然后使用Space函数来填充变量,填充内容使用空格,长度为期望返回的最大字符数量。 |
datatype arg | 参数的数据类型和名称。整个参数表必须匹配存储过程。每个参数对都可以使用REF来表明引用方式。 |
RPCFUNC | 关键词,指示声明是针对DBMS的存储过程的,而不是DLL中的外部函数 |
ALIAS FOR"spname" (optional) | 关键词,其后跟数据库过程的名称字符串。用于关联PowerScript名称和数据库过程名称,尤其适合两边使用相同名称时不符合各自规则的情况。 |
5.1用法
如果函数不返回值(例如,返回Void),则将声明指定为子程序而不是函数。
RPC声明总是与事务对象相关联。可将它们声明为本地外部函数。“Declare Local External Functions”对话框中有一个“Procedures”按钮(如果连接的数据库支持存储过程),可以通过该按钮访问数据库中的存储过程列表。
5.2示例
- 实例1
下面这句,GIVE_RAISE_PROC存储过程的声明是在User Object 绘制区的事务对象中声明的(声明放在同一行中):
FUNCTION double GIVE_RAISE(ref double SALARY) RPCFUNC ALIAS FOR "GIVE_RAISE_PROC"
下面的代码调用脚本找那个的函数:
double val = 20000
double rv
rv = SQLCA.give_raise(val)
- 实例2
存储过程SPM8的声明不需要ALIAS FOR从句,因为PowerBuilder和DBMS的名称是相同的:
FUNCTION integer SPM8(integer value) RPCFUNC
下面的代码调用SPM8:
int myresult
myresult = SQLCA.spm8(myresult)
IF SQLCA.sqlcode <> 0 THEN
messagebox("Error", SQLCA.sqlerrtext)
END IF