经过编译生成的PowerBuilder应用程序需要的运行环境

问：我的程序在PB下，可正常运行，单步试过也没出错。但是，编译成.EXE 文件,运行时连不上数据库(SQL ANYWHERE 的本地库) 提示:data transaction information not valid 不知哪里出了问题,有没有可能是我的PB盗版得不完全？或缺少一些DLL文件?或者,在生成.EXE文件时,需要做一些特殊的操作,请指教.谢谢!

答：经过编译生成的PowerBuilder应用程序需要如下运行环境:
PowerBuilder 运行时动态连接库( Runtime Library )
在PowerBuilder 6.0中需要如下DLL库:
PBVM60.DLL (PowerBuilder 虚拟机)(必需)
PBDWE60.DLL (DataWindow 引擎)(可选)
PBRTC60.DLL (Rich Text Control)(可选)
PBTRA60.DLL (DLL used for tracing db calls)(可选)
与所需连接的数据库的直连接口(Native Driver) 或 ODBC接口(ODBC Driver)
PBSYC60.DLL (连接Sybase 的直连接口)
PBO7360.DLL (连接Oracle7.3的直连接口)
所需连接的数据库的Client端(如Sybase的Open Client, Oracle 的SQL Net) 以上是Sybase公司的建议。
我的经验是，编译好的程序必须有pbdwe60.dll和pbvm60.dll才行。
另外，如果使用ODBC联结数据库则还需pbodb60.dll，如果使用的专用接口，则需对应数据库的DLL，如Oracle7.3用PBO7360.DLL，Sybase用PBSYC60.DLL


检测数据窗口是否被修改

如何检测数据窗口中数据是否被修改

if dw_wh.deletedcount() + dw_wh.modifiedcount() > 0 then
integer li_ret
li_ret = messagebox("注意","数据已经被修改，是否存盘？",Question!,YesNoCancel!)
choose case li_ret
case 1
if dw_wh.update() = 1 then
commit;
return 0
else
rollback;
messagebox("出错","存盘时出错！请检查数据的正确性。")
return 1
end if
case 2
return 0
case 3
return 1
end choose
end if


PB6.0与VFP6.0的连接问题

问：PB6.0与VFP6.0通过ODBC连接，设置数据源为包含DBF文件的目录，在连接时出现Catalog tables could be created and are not available for use.信息。按OK键后可以与定义的数据目录连接，但看不到目录下的DBF文件。设置数据源为DBC文件，情况一样。问题出在哪里？另，在此种情况下，用PB建立新表，存盘后新表会出现在PBL运行的目录下，而不是存在定义的DBF目录下。为什么？我有急用，请各位朋友帮忙，万分感谢
答：You must cp PB system table in your DBF directory ,and in ODBC Profile Setup you make a change at system options .....Owner is PUBLIC thats ok!


如何能够在PB运行中得到任意表的结构？

问：如何能够在PB运行中得到任意表的结构？如域名，属性等。。。谢谢！

答：直接访问DBMS的系统表或系统视图。
以ORACLE为例.SYS.ALL_TAB_COLUMNS等视图中包含你要的信息。


大文本报表显示自动折行问题

问：在大作《请教高手，大文本报表显示自动折行问题？》中提到：
在做report时，如果显示文字超过水平边界，希望能自动折行，但控件属性无法设置，如果文本里有换行符可以，请问，有否高招，设置其显示的自动折行属性。

答： 由于只有MultiLineEidt能使大文本自动折行,所以需要将每行的数据都加到各自的mle中,这里假设浏览的行数是固定的,因此只需要知道显示的顶行行号即可实现:
dw_1.Object.DataWindow.FirstRowOnPage 属性正好可以解决这一问题.
实现方法:
在浏览窗口添加一个实例变量: string is_FirstRowOnPage = "" .用于记录顶行行号在浏览窗口加一个新的函数 integer wf_refreshtext() :
//假设dw_1为浏览数据窗口,mle_1, mle_2, mle_3为显示大文本的多行编辑框

string ls_row
long i, ll_FirstRowOnPage

ls_Row = dw_1.Object.DataWindow.FirstRowOnPage
If ls_Row = is_FirstRowOnPage then
return 0 //如果顶行行号未变,不用刷新
Else
is_FirstRowOnPage = ls_Row
ll_FirstRowOnPage = Long( ls_Row )
mle_1.text = dw_1.object.Intro_Pic[ll_FirstRowOnPage ]
mle_2.text = dw_1.object.Intro_Pic[ll_FirstRowOnPage + 1]
mle_3.text = dw_1.object.Intro_Pic[ll_FirstRowOnPage + 2]
return 1
End If

函数作好后,需要在可能改变数据窗口显示的地方调用,主要有两个地方:
数据窗口的rowfocuschanged事件和scrollvertical事件

上面说的方法其折行以单词为单位,因此在显示中文时如果不含空格就不能折行
如果只是作为显示的话,有一个变通的方法:
在需要显示大文本的地方放一个多行编辑框(MultiLineEdit):mle_1
在含有大文本数据列的数据窗口控件的rowfocuschanged事件中加入如下脚本:
mle_1.text = this.object.Intro_Pic[currentrow]


如何使用pb7中的进度条控件?
问：如何使用pb7中的进度条控件?

答：pb7中进度条控件两种:1 HprogressBar 2 vprogressbar
他有一个smoothscroll的属性,可以设置平滑,还有一个position属性设置当前位置然后在程序中根据代码进行的位置来设置就行了。要使用循环的哦。


怎样在程序中为datawindow加计算域？

怎样在程序中为datawindow加计算域或line？

答：给个例子：

//以下程序加一计算域sum(dept_id for all)
string ls_modrow
dw_1.Modify("DataWindow.summary.Height=64")
ls_modrow = 'Create compute(band=summary font.charset="0" font.face="MS
Sans Serif" font.family="2" font.height="-8"
font.pitch="2" font.weight="400" background.mode="1"
background.color="536870912" color="0" x="9" y="4"
height="52" width="297" format="[general]"
expression="sum(dept_id for all)" alignment="1" border="0"
crosstab.repeat=no )~r~n'
dw_1.modify( ls_modrow )

//以下程序加一条线
string ls_line
dw_1.Modify("DataWindow.detail.Height=332")
ls_line = 'Create line(band=detail background.mode="2"
background.color="16777215" pen.style="0" pen.width="5"
pen.color="0" x1="37" y1="320" x2="1458"
y2="316" )~r~n'
dw_1.modify( ls_line )

dw_1.settransobject(sqlca)
dw_1.retrieve()


PB + Sybase SQL Anywhere C/S配置

问：在大作《请教高手！！！》中提到：
本人用PB的SQL Anywhere 5,在局域网选一台pc用Network server安装,选另一pc用Network client安装。在服务器上创建数据库及一表后，在客户端配置ODBC时,在database file 选Network方式，系统不认！在服务器端启动数据库后，在客户端点击DB Profile-ODBC- 选Database server-connect 后，系统显示：
SQLSTATE=08001
[sybase][ODBC]unable to connect to database server:
unable to start database engine.
请问高手，我该如何解决？非常感谢！

答：请教高手？Sorry，其实我只是PB菜鸟。
记得大概半个多月前我在这儿发过一个贴子，里面谈及我的C/S配置，可适没人回应。
我的做法如下：
（1）环境：NT4.0＋sql anywhere5.5＋PB6.5
（2）服务器以server方式安装sql anywhere，用dbsrv50启动数据库服务
（3）客户机以client方式安装sql anywhere，新建odbc数据源（名称与下面ini文件内的配置一致，数据库为服务器上的数据库，选择network方式）
（4）客户机安装PB开发之应用软件，包括:exe文件，pbd动态链接库,dll支持库,一个ini用户配置文件（内有[database]配置）
（5）在客户机桌面上建立程序之快捷方式，点击图标，数十秒后程序成功启动。
其实我也不清楚这是否算是一个合理的C／S应用，Kokuc在电脑报上发表的文章相信你也看过，相当有用，可作参考。就我上面的做法看来，关键之处是配置ODBC数据源时，database startup一节，按BROWSE按钮，选“网上邻居”下服务器上的数据库。再选client单选钮。

以上看法，仅供参考。有问题欢迎再交流


请教SQL ANYWHERE数据库为何越删越大？

问：我用SQL ANYWHERE 来管理数据。 在数据库装满记录时，my.db 为 30M左右、 而在我删除所有记录后，my.db 反而变为了40 M...如此说来，程序运行岂不是耗费越来越大的空间？这样的程序怎么能用呢？

答：SYBASE SQL ANYWHERE 确实存在这样的问题，可以用ISQL中Database Tools的工具Rebuild Database 或 SQL ANYWHERE 目录下的rebuild.bat来减肥。


关于数据管道的问题

问：我的程序中用到了数据管道，在集成环境运行一切则OK，但编译成EXE文件后运行出错，pipeline.start()错误码= -1，不知何因？还请指教为盼!

答：好象以前碰过这个问题，你试试：
1、不编译成 Machine Code,而是 P CODE, 然后选中所有的 PBL，编译成 PBD。
2、将pbrtc60.dll文件拷贝到你的应用程序目录下。


如何在Sybase SQL Anywhere 服务器增加用户和设置数据库的操作权限？

答：不知以下的介绍不知对你是否有用：

SQL anywhere数据库的默认用户名和口令是DBA和SQL，要对数据库安全负责的话，就应当更改口令或用户名，不然大家得到你的数据库，都能打开它。

启动sybase central,connect数据库后，在数据库项目列表中，有一项users & groups,打开它，有以下功能：

add user---添加用户及口令，指定级别（可选DBA）
add group---添加用户组
dba---对已有的用户，进行修改，打开后有四个标签页 1、general--修改用户口令，2、authorities--设置用户级别，其中DBA为系统管理员 resource为可以创建数据库级别 3、membership---指定用户组 4、设定对每个表的操作级别。
dbo和public---添加或删除组中的用户。

因为SQL anywhere一般是小型应用，通常只用修改dba用户的口令，然后在PBL的open script事件中加入：
SQLCA.DBMS ="odbc"
SQLCA.DbParm= "connectstring='dsn=数据源名;uid=dba;pwd=新口令'"
connect using sqlca;
即可连接数据库，编译成EXE文件后，再将ODBC中的数据源描述UID和PWD内容去掉，别人就查不到你的数据库口令了。

我们在连接数据库的时候，还有一种方法是use integrated login ,是按登录到NT服务器域的用户及口令来登录数据库，如果在数据库创建了同域用户一样的用户和口令，用这一项就可以直接连接数据库。

sybase central是一个数据库管理中心，功能十分强大，可以实现诸如创建数据库、压缩、备份、改变库结构（这个功能相当实用，例如你在PB的table画板下，不能改变字段类型、长度只能缩小不能扩大，在sybase central下你想做什么都可以）等等许多数据库操作。


怎样调用Windows API取得应用安装后的路径？

问：请教高手，怎样调用Windows API取得应用安装后的路径？
答：此路径,就是当前路径
可以使用GetCurrentDirectory()获得
在local extend funtion 中定义：
FUNCTION ulong GetCurrentDirectoryA(ulong BufferLen,ref string currentdir) Library "Kernel32.dll"
在脚本中加入：
string ls_dir
ulong l_buf
l_buf=100
ls_dir=space(l_buf)
GetCurrentDirectoryA(l_buf,ls_dir)
messagebox("path",ls_dir)


如何实现打印完一个DataWindow后不换页？
问：在PB中如何实现打印完一个datawindow后不换页，实现多个数据窗口打印在同一页？

答：首先将datawindow的print输出到一个打印文件中，把其他的print也输入这个prn，然后,打印这个打印文件就行了。
如：
datawindow_control1.object.datawindow.print.filename="c:\printfilepath\exam_1.prn"
datawindow_control2.object.datawindow.print.filename="c:\printfilepath\exam_1.prn"
dw_1.print()
dw_2.print()
run("print //d:\\printservename\sharename "c:\printfilepath\exam_1.prn")

//d:\\printservename\sharename打印机名


实现HTML格式帮助文件的相关调用 2000年04月30日

如何实现HTML格式帮助文件的相关调用

在一个有关例程的提示下，在程序中相关调用HTML帮助文件的“难题”终于被我“攻克”了，现写出来与大家分享：
1、定义全局API函数：
Function Long HtmlHelpA (long hwnd ,string lpHelpFile , long wCommand, string dwData) Library "hhctrl.ocx"
2、定义全局变量：
long HH_DISPLAY_TOPIC = 0
3、在功能模块的“帮助”按钮的clicked事件中写下以下代码：
htmlhelpA(handle(parent), "chm_name>chm_win_name", HH_DISPLAY_TOPIC, "htm_name")
例如：chm帮助文件的名字是help.chm，help.chm的主窗口名是mywin，要调出的相关页面是bj_modi.htm，相应的脚本是：
htmlhelpA(handle(parent), "help.chm>mywin", HH_DISPLAY_TOPIC, "bj_modi.htm")
4、至于在菜单中启动帮助文件，用run("hh help.chm")命令即可。

但愿以上介绍对有相同困惑的朋友有所帮助。


怎样让DW的列在点击时标题的边框由3D raise改为 3D lower？

问：我想做一个象excel那样能当鼠标点一下那列时,标题的边框由3D raise改为 3D lower,请问怎么实现？

答：
1、首先定义Instance变量用于保存上次的点击对象，这样便可以恢复样式。
string is_objectname

2、在数据窗口中利用GetObjectAtPointer函数得到当前用户点击的对象，然后改变相应的样式，具体代码如下：

//数据窗口Clicked事件

String dwobjectname

//得到当前点击的对象
dwobjectname = this.GetObjectAtPointer()

//恢复上次点击对象的样式
if not isnull(is_objectname) and trim(is_objectname) <> "" then
dw_1.modify(is_objectname + ".border = 6")
end if

long ll_pos

//分别读取对象名称与行数
ll_pos = pos(dwobjectname,'~t')

is_objectname = left(dwobjectname,ll_pos - 1)

//如果用户点击文本，则返回。
if right(is_objectname,2) = "_t" then return

string ls_row

ls_row = right(dwobjectname,len(dwobjectname) - ll_pos)

//修改点击对象当前行的样式
dw_1.modify(is_objectname + ".border = '6~tIf(getrow()=" + ls_row + ",5,6)'")

本方法可以应用到别的风格设计上，如果用户使用别的方式切换焦点时，还必须在相应的实践中编程。


怎样带参数运行PB应用程序？

问：请问PB编译后的程序可否带参数运行，就象DOS中的dir命令：dir/w的形式，如果可以，PB中怎样接收呢？请指教！

答：PB中带参数运行应用程序一般都是用在Application 的 Open 事件里访问 CommandLine 参数的办法，根据参数CommandLine 的不同做不同的操作。如程序myapp ，运行：myapp hello。hello就是命令行参数。


关于日期显示的问题

问：哪位大虾有办法使string(date('2000-05-04'))='2000-05-04'？我用string(date('2000-05-04'))得到的结果是00-05-04,但有没有办法使等到的结果是2000-05-04呢？

答：你应该到控制面板里的区域设置中设置好日期的格式,OK了吗？


能否把按钮的死板样换得好看一些？

问：能否把按钮的长方形的死板样换得好看一些，敬请赐教!

答：使用图片按钮,再将图片的不规则范围以外的地方设为与背景一样的在,图片按钮按下和弹起时使用不同的图片,如立体,立体凹下两种


数据窗口中的按钮问题

问：数据窗口中加入update按钮，执行时点击却无效果，请高手不吝指教，多谢多谢！
try to use dwcontrol.accepttext() to accept the data that inputed/modified?

答：在dw的clicked事件中判断dwo.name是否是你的按钮名称，是的话执行你想要的操作。
（为何不把update按钮放在窗口中？）


如何在PowerBuilder中创建图标栏应用

答：推荐有关文章

在PowerBuilder中创建图标栏应用

---- 在一些应用埸合，常需要在Windows 9.X或Winnt中的图标栏中显示出公司/应用程序的图标，在单击图标时弹出相应的窗口，就象金山词霸一样。下面介绍采用PowerBuilder中在Windows 9.x和Windows NT下显示图标栏图标的方法。
一、 建立一个应用程序
---- 在创建程序前，先准备一个用于在图标栏中显示的图标文件(.ICO)，并准备一个用于弹出窗口显示的图片(BMP 256色)。启动PowerBuilder，创建一个应用(Application)，取应用名Starsoft(任意)，注意采用模板生成却缺省窗口。
二、 创建要显示的图标窗口
---- 创建一个用于显示成图标的窗口，取名为W_systray，再在上面放置一张BMP图片，式样如下图所示(略)
---- 设置其它窗口的有关信息，如窗口名等。

三、 编程
---- 1、定义全局外部函数
---- PowerBuilder本身并不提供用于在图标栏中把窗口显示成图标的函数，但可以通过Windows API调用来实现，有关在PowerBuilder调用相应的API函数如下(定义在全局外部函数中)：

Public Function Integer Shell_NotifyIcon
(Long dwMessage, Any lpData) Library "shell32"
Alias For "Shell_NotifyIconA"
Public Function Long LoadImage (Long hInst,
String lpsz, Long un1, Long n1, Long n2, Long un2)
Library "user32" Alias For "LoadImageA"
Public Function Long DestroyIcon (Long hIcon)
Library "user32" Alias For "DestroyIcon"
Public Function Long SetForegroundWindow (Long hwnd)
Library "user32" Alias For "SetForegroundWindow"
Public Function Long OpenIcon (Long hwnd)
Library "user32" Alias For "OpenIcon"
---- 2、应用入口
---- 在Application的Open Scripts中，输入下列函数：

---- Open (win_systray)

---- 3、窗口函数和Scripts

---- 在W_systray窗口中，创建一个名称为notifyicondata的结构，定义如下：项目 数据类型

Cbsize Long
Hwnd Long
Uid Long
Uflags Long
Ucallbackmessage Long
Hicon Long
Sztip any
---- 再定义三个函数：
---- 1、AddToTray()，参数：无；返回值：布尔值(True/False)

// Boolean AddToTray ()
Any nid
if hIcon = 0 then
// the icon has not been loaded yet
hIcon = LoadImage(0, "SysTray.ico",
IMAGE_ICON, 0, 0, LR_LOADFROMFILE)
end if

if hIcon = 0 then
MessageBox ("错误", "不能加载图标!")
Return False
else
nid = SetNotifyIconData (Handle (This), 0,
NIF_MESSAGEorNIF_ICONorNIF_TIP, WM_MOUSEMOVE,
hIcon, "TestTip")
Shell_NotifyIcon (NIM_ADD, nid)

Return True
end if
---- 2、RemoveFromTray ()；参数：无；返回值：无
// RemoveFromTray ()
Any nid
nid = SetNotifyIconData (Handle (This), 0,
NIF_MESSAGEorNIF_ICONorNIF_TIP, 0, hIcon, "")
Shell_NotifyIcon (NIM_DELETE, nid)
if hIcon < > 0 then DestroyIcon (hIcon)
hIcon = 0
---- 3、SetNotifyIconData (Long hWnd, Long ID, Long Flags,
Long CallbackMessage, Long Icon, String Tip)；
返回值：notifyicondata
// NotifyIconData SetNotifyIconData (Long hWnd, Long ID,
Long Flags, Long CallbackMessage, Long Icon, String Tip)
Char MyTip [64]
NotifyIconData NidTemp
NidTemp.cbSize = 88 // Len (NidTemp)
NidTemp.hWnd = hWnd
NidTemp.uID = ID
NidTemp.uFlags = Flags
NidTemp.uCallbackMessage = CallbackMessage
NidTemp.hIcon = Icon
MyTip = Tip + Char (0)
NidTemp.szTip = MyTip
return NidTemp
最后，在窗口的有关事件中输入函数：
Open 事件
if AddToTray () then Visible = False
Close 事件
RemoveFromTray ()
CloseQuery 事件
if CanClose then
Return 0
else
Visible = False
Return 0
End if
Resize事件
if sizetype = 1 then Visible = False
四、 测试
---- 完成上述工作后，就可以运行并加以测试了。进入运行，在图标栏中就注册了一个图标，单击就弹出窗口，窗口示例如下图所示(略)：
---- 这时，最小化该窗口，不会在Windows 9.x或Windows NT的任务栏中显示，而是缩小成一个在图标栏中的图标。
---- 上述示例在Windows 9.8、Windows NT V4.0和PowerBuilder　6.5下测试通过。


怎样实现计算机对KB和MOUSE输入不响应？

问：在大作《 怎样实现计算机对KB和MOUSE输入不响应？》中提到：要求：按某一键后，计算机对KB和MOUSE输入不响应； 按某一键后，计算机对KB和MOUSE输入才响应。

答：在keydown与Mouse事件中编程，用户按下某键后设置当前状态（变量）为允许响应还是不响应，最好使用系统热键。
在事件中return 1，就不会响应却省操作了。

if status then
//处理
else
return 1
end if


Powerbuilder中怎样实现用代码配置ODBC

答：介绍一篇通过代码配置ODBC的文章，触类旁通，你自己细细思考吧！
Powerbuilder中实现用代码配置ODBC
一、 引言
Powerbuilder是客户端的开发工具，在实际应用中必须与数据库管理系统配合起来才能运行应用程序，它与数据库的连接可以通过ODBC接口实现。在ODBC中配置好数据源后，便可在程序代码中连接数据库并对数据库进行各种操作了。目前的大部分PB参考书都只介绍了ODBC的手工配置方法，而没有介绍如何在代码中进行ODBC的配置，这样当程序最终完成并分发到用户手中后，还需要为用户配置ODBC，显得既麻烦又不符合专业软件的要求。那么，怎样才能在代码中实现对ODBC的配置呢？其实，Windows中所有的ODBC数据源在系统注册表中都有记载，因此只要对注册表中的项目进行相应的修改即可。与ODBC相关的键中，HKEY_LOCAL_MACHINE\Software\ODBC\ODBCINST.INI\ODBCDRIVER中记录了所有已安装的ODBC驱动程序；HKEY_LOCAL_MACHINE\Software\ODBC\ODBCINST.INI中记录了各种ODBC驱动程序的相关信息；HKEY_CURRENT_USER\Software\ODBC\ODBC.INI\ODBC
Data Sources记录了各种数据源的类型；HKEY_CURRENT_USER\Software\ODBC\ODBC.INI则记录了各种数据源的详细信息。因此，只要在代码中对这些键进行配置，便可达到手工配置ODBC的目的。

二、在代码中配置ODBC的实现
下面以Powerbuilder内置的Sybase SQL Anywhere5.0数据库为例介绍ODBC的代码配置方法。
1、 找出数据库驱动程序及相关文件
首先找到相应的数据库驱动程序，这些文件可以在注册表的HKEY_LOCAL_MACHINE\Software\ODBC\ODBCINST.INI下相应键中找到。如Sybase
SQL Anywhere5.0为dbeng50.EXE，WOD50T.DLL，以及一些其他相关的动态链接库，此例中为Dbl50t.dll，Wl50ent.dll，Wtr50t.dll，Wodbc.gid，Wtr50t.dll。这些文件必须与最终的可执行程序一起安装到用户的计算机上，可以与应用程序放在一起，也可以放在一个特定的目录下（本例为Windows的system目录下）。在调试时可先将这些文件复制到指定的目录下。
2、 在程序中用代码配置ODBC
在应用的open事件中对ODBC配置的代码如下：
Ulong ul_num
Int answer,answer1,answer2,answer3,answer4,answer5,ansapp
String ls_driver,ls_start,ls_location,apppath,db_path,odbcstr
//获取操作系统的system目录,并保存到ls_location变量
answer=RegistryGet(“KYEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Setup”,”SysDir”,RegString!ls_location)
IF answer=-1 THEN
Messagebox(‘错误’,’应用程序无法获取Windows的系统目录，系统统将终止运行！’,Stopsign!)
RETURN
END IF
//判断Sybase SQL Anywhere5.0的驱动程序是否正确安装
ls_driver=ls_location+’\WOD50T.DLL’
ls_start=ls_location+’\dbeng50.EXE’
IF NOT (FileExists(ls_driver) AND FileExists(ls_start)) THEN
Messagebox(‘错误’,’系统中没有安装SQL Anywhere的驱动程序，系统将终止运行！’,StopSign!)
RETURN
END IF
//设置ODBC\ODBCINST.INI\ODBC DRIVERS
answer=RegistrySet(‘ HKEY_LOCAL_MACHINE\Software\ODBC\ODBCINST.INI\ODBCDRIVER’,’Sybase
SQL Anywhere 5.0’,RegString!,’Installed’)
IF answer=-1 THEN
Messagebox(‘错误’,’应用程序无法设置ODBC DRIVERS，系统将终止运行！’,StopSign!)
RETURN
END IF
//设置ODBC\ODBCINST.INI\
answer1=RegistrySet(‘ HKEY_LOCAL_MACHINE\Software\ODBC\ODBCINST.INI\Sybase SQL Anywhere
5.0’,’CPTimeout’,RegString!,’not pooled’)
answer2=RegistrySet(‘ HKEY_LOCAL_MACHINE\Software\ODBC\ODBCINST.INI\Sybase SQL Anywhere
5.0’,’Driver’,RegString!,ls_driver)
answer3=RegistrySet(‘ HKEY_LOCAL_MACHINE\Software\ODBC\ODBCINST.INI\Sybase SQL Anywhere
5.0’,’Setup’,RegString!,ls_driver)
IF answer1=-1 OR answer2=-1 OR answer3=-1 THEN
Messagebox(‘错误’,’应用程序无法设置ODBCINST.INI，系统将终止运行！’,StopSign!)
RETURN
END IF
//设置ODBC DATA SOURCE名称（anysql为数据源名）
answer=RegistrySet(‘ HKEY_LOCAL_MACHINE\Software\ODBC\ODBCINST.INI\ODBC Data Sources’,’anysql’,RegString!,’Sybase
SQL Anywhere 5.0’)
IF answer=-1 THEN
Messagebox(‘错误’,’应用程序无法设置ODBC DATA SOURCE名称，系统将终止运行！’,StopSign!)
RETURN
END IF
//获取应用程序的安装路径（ybinput为应用程序安装到用户计算机上后在注册表中注册的应用程序名，yibiao.db为应用程序的数据库名，与应用程序在同一目录下，anysql为数据源名）
ansapp=RegistryGet(“KYEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App
Paths\ybinput”,”Path”,RegString!app_path)
IF ansapp=-1 THEN
Messagebox(‘错误’,’应用程序无法获取安装路径名，系统将终止运行！’,StopSign!)
RETURN
ELSE
db_path=app_path+’yibiao.db’
END IF
//设置ODBC.INI的细节
answer1=RegistrySet(‘HKEY_current_user\software\odbc\odbc.ini\anysql’,’driver’,RegString!,ls_driver)
answer2=RegistrySet(‘HKEY_current_user\software\odbc\odbc.ini\anysql’,’start’,RegString!,ls_start)
answer3=RegistrySet(‘HKEY_current_user\software\odbc\odbc.ini\anysql’,’autostop’,RegString!,’yes’)
answer4=RegistrySet(‘HKEY_current_user\software\odbc\odbc.ini\anysql’,’DataBaseFile’,RegString!,db_path)
answer5=RegistrySet(‘HKEY_current_user\software\odbc\odbc.ini\anysql’,’DataBaseName’,RegString!,’yibiao’)
IF answer1=-1 OR answer2=-1 OR answer3=-1 OR answer4=-1 OR answer5=-1 THEN
Messagebox(‘错误’,’应用程序无法设置ODBC.INI细节，系统将终止运行！’,StopSign!)
RETURN
END IF
odbcstr=’dsn=anysql;databasename=yibiao;databasefile=’+db_path
sqlca.dbms=’odbc’
sqlca.databse=’yibiao’
sqlca.dbparm=”connectstring=’”+odbcstr+”;uid=dba;pwd=sql’”
connect using sqlca;
open(w_main)

该程序在Powerbuilder6.0下运行通过。


如何恢复SQL_SERVER数据库？

问：在大作《如何恢复SQL_SERVER数据库？斑竹教我！》中提到：
硬盘问题损坏,更换硬盘后,重新安装SERVER。原数据库存在另一硬盘(有一 XXX.DAT文件)。请教我如何将此数据库恢复到新SERVER上？谢谢!!!

答：连上数据库后，在SQL SERVER 的 Query Analyzer执行：
exec sp_attch_db "database_name","path\xxx.dat","path\xxx.log"


数据窗口的自动折行设置

答：自动折行设置：
1、在DataWindow Painter中打开DataWindow;
2、在需设定自动折行的列上双击鼠标, 弹开此列的属性窗口；
3、 选择Position标签, 选中Autosize Height 多选框；
4、 选择Edit标签, 不选中Auto Horz Scroll多选框；
5、 单击OK按钮, 保存所做的修改；
6、 点中Detail Band (即写有Detail的灰色长带), 单击鼠标右键, 选择Properties... 菜单项；
7、 选中Autosize Height多选框；
8、 单击OK按钮, 保存所做的修改；
9、 保存DataWindow。


动态切换数据窗口对象编译后运行为何出错？

问：诸位专家，有一问题：在数据窗口dw_1中用 dw_1.dataobject="dw_sj"来改变对象，在PB7环境下运行一切OK。但当创建工程和应用程序后，run该应用程序（exe文件）时，数据窗口dw_1的数据对象dw_sj不能出现。本人甚为奇怪？一直还没有查出原因？请赐教。

答：当程序中用到了动态加入的对象，如dw_1.dataobject="dw_sj"（数据窗口对象"dw_sj"即为动态加入），PB编译时是扫描不到的，这时可以将此对象写入到资源文件中来解决：
用记事本创建资源文件dw_object.pbr
写入：c:\myprogram.pbl(dw_sj)
编译时将此文件选入Resource File Name处。


如何从客户端获得服务器的时间

如何从客户端获得服务器的时间。

答：呵呵，接招吧——>
看你是什么数据库了，
ORACLE：select sysdate from sys.dual;
SQLServer、Sybase：select getdate() from tablename;


如何打印条形码？

问：各位朋友：我要打印条形码，但是条码机不是一个设备，请问我怎样做？谢谢！

答：
1）下载条码字体（陶清的网站有）；
2）建一个label风格的数据窗口，列对象的字体选择条码字体即可。


解 SQL ANYWHERE 密码的方法

问：求解SQL ANYWHERE密码的方法。

答：用regedit打开注册表，在\HKEY_CURRENT_USER\SOFTWARE\ODBC\ODBC.INI\中相应ODBC配置的PWD值。


如何在PB中加载声音文件？

问：请教各位，如何将声音文件在PB中播放，请具体说明一下好吗？

答：可利用"winmm.dll"(在windows\system"下)。
声明:
Function boolean sndPlaySoundA (string SoundName, ulong Flags) Library "WINMM.DLL"
执行：
sndPlaySoundA("c:\windows\media\Notify.wav",1)


PB中字段间如何进行加减等运算

问：请问在POWERBUILDER中怎样实现几个字段的运算？比如当输入
语文，数学成绩时该学生的总成绩会进行和运算？谢谢

答：if only for display,you can use computed field（expression如：字段1 + 字段2）
the other way is use setitem()


怎么在数据窗口中用左箭头实现shift+tab

问：各位大侠： 我想在PB中获得一个象foxpro那样的编辑环境，即按左箭头光标便移动到当前列的左边一列，按右箭头光标便移动到右边一列，请问有什么办法？特急！！！

答：声明函数：
subroutine keybd_event(uint bVk,uint bScan,long dwFlags,long dwExtraInfo ) library
'user32.dll'

定义用户事件keydown：pbm_dwnkey。
在事件中编程：

integer VK_TAB = 09
integer VK_SHIFT = 16

if key = KeyLeftArrow! then
keybd_event(VK_SHIFT,0,0,0) //按下shift
keybd_event(VK_TAB,0,0,0) //按下tab
keybd_event(VK_TAB,0,2,0) //释放tab
keybd_event(VK_SHIFT,0,2,0) //释放shift
return 1
end if

if key = KeyRightArrow! then
keybd_event(VK_TAB,0,0,0) //按下tab
keybd_event(VK_TAB,0,2,0) //释放tab
return 1
end if

此题由摆渡人工作室站长ferryman回答

kukoc 再提供另一种解决方法（比较原始，但也能实现）：
function boolean GetKeyboardState (ref char kbarray[256]) library "user32.dll"
function boolean SetKeyboardState (ref char kbarray[256]) library "user32.dll"

//脚本：用户事件keydown：pbm_dwnkey

char lc_kb[256]
if key = KeyLeftArrow! then
GetKeyboardState (lc_kb)
lc_kb[17] = Char (128)
SetKeyboardState (lc_kb)
Send (Handle (this), 256, 9, 0)
GetKeyboardState (lc_kb)
lc_kb[17] = Char (0)
SetKeyboardState (lc_kb)
end if


如何控制数据窗口中行背景颜色

问：我想在数据窗口的GRID格式下，根据实际情况控制每一行的背景，却无法实现。请问如何才能实现这种功能？

答：调整detail的属性中的color的expression就可以了，如：
if(getrow() = currentrow(),rgb(0,0,192),rgb(255,255,255))


程序中出现Database transaction information not available的错误？

问：本人使用是PowerBuild 6.5，在链接数据窗口与子窗口时，数据窗口预览有数据，而在运行主窗口下的子窗口时却出现以下错误：
DataWindow Error
Database transaction information not available
call SetTrans or SetTransObject function.
按“确定”后，出现子窗口，但数据窗口不见有数据。

答：你没有：
dw_1.settransobject(sqlca)
或者你没有成功连接数据库


如何带参数执行SQL的系统储存过程？

问：我想在script中执行Sp_DropUser系统存储过程删除MS SQL Server 7某用户。我是这样写的：execute Sp_DropUser(Sp_DropUser语法）,编译提示Sp_DropUser为未定义的系统存储过程。

答：string ls_name

ls_name = 'username'

dECLARE dropUser_proc PROCEDURE FOR Sp_DropUser
@name_in_db =:ls_name;
SQLca.AutoCommit = TRUE
EXECUTE dropUser_proc ;
SQLca.AutoCommit = false


如何改变数据窗口中某些含有特定字符的域的颜色？

问：糊涂蛋在大作《谁能告诉我？》中提到：如何实现对dw数据窗口中的某一列按指定字符串"string"进行检索后，使检索结果中的所有与"string"相匹配的字符显示为一指定颜色（有别于记录中的其它字符颜色）。

答：
//如果符合条件，则显示灰色的背景，否则白色；
本方法同样可以设置该列的字体颜色：其中“column_name”为列名。
dw_1.object.column_name.background.color = "16777215~tif ( fromid = 'string', rgb(192,192,192), rgb(255,255,255))"
也可以是一行都变色：
dw_1.object.Datawindow.detail.color = "16777215~tif ( fromid = 'string', rgb(192,192,192), rgb(255,255,255))"


如何关闭打开的dos窗口？

问：如何关闭打开的dos窗口？ 如：调用run(*.bat)函数，该函数运行完毕后有一个打开的dos窗口，如何关闭？？？

答：提供两种办法：
1、API函数：DestroyWindow
2、send()函数，example:
定义API：
Function ulong FindWindowA (String lpClassName , String lpWindowName) Library "user32.dll"
Function ulong CloseWindow (ulong hwnd ) Library "user32.dll"
脚本：
long lHandler
string sClass
int rtn
setnull(sclass)
lHandler = FindWindowA(sClass, "ms-dos 方式")
If lHandler <> 0 Then
send(lHandler, 16,0, 0)
End If


动态SQL四种类型的语句

动态SQL四种类型的语句格式

问：各位仁兄,由于小妹既健忘又慷慨(把自已卖的PB书全借出去了),以至用到动态SQL时,四种类型的语句竟是一句也记不起一句也找不到,在HELP里转了半天也无所获,不知哪位好心人,能速告知,将不盛感激.谢谢
答：

1.Dynamic SQL Format 1

EXECUTE IMMEDIATE SQLStatement {USING TransactionObject} ;

eg:
string Mysql
Mysql = "CREATE TABLE Employee "&
+"(emp_id integer not null,"&
+"dept_id integer not null, "&
+"emp_fname char(10) not null, "&
+"emp_lname char(20) not null)"
EXECUTE IMMEDIATE :Mysql ;

2.Dynamic SQL Format 2

PREPARE DynamicStagingArea FROM SQLStatement {USING TransactionObject} ;
EXECUTE DynamicStagingArea USING {ParameterList} ;

eg:
INT Emp_id_var = 56
PREPARE SQLSA
FROM "DELETE FROM employee WHERE emp_id=?" ;
EXECUTE SQLSA USING :Emp_id_var ;

3.Dynamic SQL Format 3

DECLARE Cursor | Procedure     DYNAMIC CURSOR | PROCEDURE
     FOR DynamicStagingArea ;
PREPARE DynamicStagingArea FROM SQLStatement     {USING
TransactionObject} ;
OPEN DYNAMIC Cursor     {USING ParameterList} ;
EXECUTE DYNAMIC Procedure    {USING ParameterList} ;
FETCH Cursor | Procedure     INTO HostVariableList ;
CLOSE Cursor | Procedure ;
eg:
integer Emp_id_var

DECLARE my_cursor DYNAMIC CURSOR FOR SQLSA ;
PREPARE SQLSA FROM "SELECT emp_id FROM employee" ;
OPEN DYNAMIC my_cursor ;
FETCH my_cursor INTO :Emp_id_var ;
CLOSE my_cursor ;

4.Dynamic SQL Format 4

DECLARE Cursor | Procedure     DYNAMIC CURSOR | PROCEDURE
     FOR DynamicStagingArea ;
PREPARE DynamicStagingArea FROM SQLStatement    {USING TransactionObject} ;
DESCRIBE DynamicStagingArea    INTO DynamicDescriptionArea ;
OPEN DYNAMIC Cursor | Procedure    USING DESCRIPTOR DynamicDescriptionArea
;
EXECUTE DYNAMIC Cursor | Procedure    USING DESCRIPTOR
DynamicDescriptionArea ;
FETCH Cursor | Procedure     USING DESCRIPTOR DynamicDescriptionArea ;
CLOSE Cursor | Procedure ;

eg:

string Stringvar, Sqlstatement
integer Intvar
Sqlstatement = "SELECT emp_id FROM employee"
PREPARE SQLSA FROM :Sqlstatement ;
DESCRIBE SQLSA INTO SQLDA ;
DECLARE my_cursor DYNAMIC CURSOR FOR SQLSA ;
OPEN DYNAMIC my_cursor USING DESCRIPTOR SQLDA ;
FETCH my_cursor USING DESCRIPTOR SQLDA ;

// If the FETCH is successful, the output
// descriptor array will contain returned
// values from the first row of the result set.
// SQLDA.NumOutputs contains the number of
// output descriptors.
// The SQLDA.OutParmType array will contain
// NumOutput entries and each entry will contain
// an value of the enumerated data type ParmType
// (such as TypeInteger!, or TypeString!).

CHOOSE CASE SQLDA.OutParmType[1]
CASE TypeString!
Stringvar = GetDynamicString(SQLDA, 1)
CASE TypeInteger!
Intvar = GetDynamicNumber(SQLDA, 1)

END CHOOSE
CLOSE my_cursor ;

全是HELP上的，呵呵，谢谢我吧，小妹。


用PowerBuilder开发多媒体播放器

问：本人在pb6.5下可以播放256色（8位）.avi文件，但不能播放24位.avi文件。请教高手。谢谢！！

答：你是用什么作的？
我用MCI32.OCX没有问题啊，贴篇文章给你看看：

用PowerBuilder开发多媒体播放器

河北　黄万璞

    PowerBuilder作为一种优秀的数据库前端开发工具，深受广大数据库开发者的青睐，
现在的数据库涉及到多媒体应用越来越多，而PowerBuilder没有像VB、Delphi那样提供开
发多媒体的控件，但并不是说利用PowerBuilder就不能开发多媒体。下面就介绍利用PowerBuilder
开发多媒体的三种方法(以播放视频文件AVI为例)。
    一、利用OLE方法。打开要播放的多媒体的窗体，点按OLE控件，系统弹出“Insert object”对话框，选择“CreateNew”页，选取视频剪辑，在窗体上放上该OLE控件，
编写窗体Open事件的脚本(比如要播放的视频文件为sample.avi)： ole_1.InserFile(“sample.avi”)运行该窗体，双击该OLE，视频就播放出来了。
    二、利用MCI方法。这种方法要调用Windows的API函数MciSendStringA,因此，首先要声明
该函数。选“Declare/Local External Functions”菜单项，弹出“Declare Local External Functions”对话框，声明局部外部函数：
Function Long mciSendStringA(red string scommand,ref string returnstr,int ilength,UNITucallback)。
在窗体上放上一个命令按钮cb_play， 其Text为“播放”，在cb_play的Click事件输入脚本：
string ls_command,ls_buffer,ls_filename
filename=″sample.avi″
ls_buffer=fill(Char(0),255)
ls_command=″open″＋ls_filemane＋″type avivideo alias avi″ mciSendStringA(ls_command,ls_buffer,255,handle(parent))
ls_command=″play avi notify″
mciSendStringA(ls_command,ls_buffer,255,handle(parent))
    运行窗体,点击该按钮就可以播放视频了。
    三、利用OCX控件。Windows95的系统提供了Amovie.ocx控件，该控件提供了很多属性和函数，
可通过编程修改其属性和调用其函数来控制多媒体，开发出高质量的多媒体播放器。下面我们就以
开发具有播放和停止功能的多媒体播放器为例。
    在要播放的窗体上，点按OLE控件，系统弹出“Insert objet”对话框，选择“Insert control”页，
“选取ActiveMovieControlObject”,在窗体上放上该定制OLE控件，然后在窗体上放两个命令按钮
cb_play和cb_stop，其Text分别赋值为“播放”和“停止”。
    在窗体的open事件输入脚本：
ole_1.Objet.Filename(″sample.avi″)
cb_play的Click事件的脚本为：
ole_1.Object.play()
cb_stop的Click事件的脚本为：
ole_1.object.stop()
    这样简单的多媒体播放器就开发好了，如果想对多媒体进行更多的控制，调用该控件相应的函数
和修改其属性即可。至于Amovie.ocx控件有哪些事件、属性和函数，可通过点按Browser图标来查看。


怎样在程序中运行控制面板中的程序

问：程序中运行控制面板中的'时间/日期'程序,那位高手知道?

答：run ("rundll32.exe shell32.dll,Control_RunDLL Timedate.cpl")

其它的程序就是最后所带的参数（Timedate.cpl）不一样，试试就知道了。


如何取得实际打印页数
问：在打印一个数据窗口时，如果在数据窗口中用函数PAGECOUNT()可以得取该数据窗口的打印页数，但该打印页数跟打印机实际的打印页数并不一样，如何能取得打印机实际的打印页数？

答：请先预览datawindow
dw_1.Modify("DataWindow.Print.preview = true")
再用pagecount()就是了。


在PB中实现登陆SYSBASE后更改登陆密码

问：在PB中实现登陆SYSBASE后更改登陆密码
答：使用系统存储过程：
declare pro_passwd procedure for sybsystemprocs..sp_password
@caller_password=:oldpw,@new_password=:newpw using transp;
oldpw=sle_1.text //在sle_1中放你的旧密码
newpw=sle_2.text　　　//在sle_2中放你的新密码
execute pro_passwd;


如何在pb中获取本机或网络中已配置的打印机的列表?
问：如何在pb中获取本机或网络中已配置的打印机的列表?

答：
phytie说：
这是利用window api函数实现的,在pb7.0实力进阶手册这本书中有例子

pbfan说：
读注册表也可以。找printer

kukoc 补充：打印机清单：
string ls_print[]

integer li_rtn
li_rtn = RegistryKeys("HKEY_LOCAL_MACHINE\Config\0001\System\CurrentControlSet\Control\Print\Printers", ls_print)


命令按钮怎么不能用键盘？

问：在大作《我是新手，请教一个问题，命令按钮怎么不能用键盘?》中提到：
请问：我在设计登陆窗口时，用命令按钮，但只能用鼠标驱动，键盘的Enter键不起作用，这是为什么？各位高手：回答我好吗？

答：（有多条，虽然答案不相同，其实可以从不同的用途来理解，故都罗列于此。）

1：Re: 我是新手，请教一个问题，命令按钮怎么不能用?

    yiyi　 [image] 地说：

    默认是TAB键转移焦点，空格键触发，要变为方向键转移焦点，回车键触发，用以下代码：
    窗口的key事件：
    GRAPHICOBJECT cb_control //定义环境变量
    CHOOSE CASE KEY
    CASE keyrightarrow! //按光标右键右移焦点
    keybd_event(9,0,0,0)
    keybd_event(9,0,2,0)
    CASE keydownarrow! //按光标下键右移焦点
    keybd_event(9,0,0,0)
    keybd_event(9,0,2,0)
    CASE keyleftarrow! //按光标左键左移焦点
    keybd_event(16,0,0,0)
    keybd_event(9,0,0,0)
    keybd_event(16,0,2,0)
    keybd_event(9,0,2,0)
    CASE keyuparrow! //按光标上键左移焦点
    keybd_event(16,0,0,0)
    keybd_event(9,0,0,0)
    keybd_event(16,0,2,0)
    keybd_event(9,0,2,0)
    CASE keyenter! //回车键触发焦点处控件
    cb_control=GETFOCUS()
    IF TypeOf(cb_control)=COMMANDBUTTON! THEN
    Triggerevent(cb_control,clicked!)
    END IF
    END CHOOSE
2: Re:我是新手，请教一个问题，命令按钮怎么不?
lchzh 地说：

在大作《Re: 我是新手，请教一个问题，命令按钮怎么不能用?》中提到：
作为新手，看书要仔细，你的问题很简单：设置CommandButton的Defult属性为True，那么在你按 Enter键时就会促发此按钮的Clicked事件，Cancel属性对应Esc键。或自定义：在按钮的文本前加入"&*"--'*'代表任意字母,如要定义按钮的快捷键为Alt+A,则在其文本中加入"&A"即可.

3：Re: Re: 我是新手，请教一个问题，命令按钮怎么不?

    phytie　phytie@sina.com [image] 地说：

    这样是不行的,default只能用于一个按钮,一般是用于messagebox中,cancel也是同理,'&*'--'*'也不是关键
    最简单就是在按钮的事件中加入一个用户自定义的事件keydown,event code 为pmb_keydown
    这样只要
    在此事件中判断是不是有enter键就行了
    //code
    if key=keyenter! then
    //your clicked code
    end if

4：Re: 我是新手，请教一个问题，命令按钮怎么不能用?

    来自西安的
    阿若 说：

    在WINDOW画板中,选择希望响应ENTER键的BUTTON,选择菜单DECLARE/USER EVENTS,新增系统事件EVENT NAME:Keydown,EVENT ID:pdm_keydown
    然后进入BUTTON的SCRIPT画板,选择KEYDOWN事件写入代码
    if key = KeyEnter! then
    // Add Your Scripts
    end if


PB中如何实现对EXCEL的操作？

问：本人以曾是用VFP 编程的，曾编过VFP&EXCLE的应用如：
Myexcel=createobject('EXCEL.APPLICATION')
MYEXCEL.VISIBLE=.f.
MYEXCEL.WORKBOOKS.ADD
O1=MYEXCEL.ACTIVEWORKBOOK.SHEETS(1)
O1.NAME='IN'
o1.CELLS(1,2).VALUE='abc'

又如：
excel=createobject('excel.application')
EXCEL.VISIBLE=.T.
ExcelFile="D:\MYDOCU~1\DC1999\DC_MDT.XLS"
if not file(ExcelFile)
wait wind 'no file:'+excelfile
return
endif
excel.workbooks.open(ExcelFile)
请问如何用PB实现象上述VFP 的应用，好让我能将资料放到一个电子表格
答：差不多，给个代码：
方法一：
OLEObject ExcelServer
integer ExcelOK
ExcelServer = CREATE OLEObject
ExcelOK = ExcelServer.ConnectToNewObject( "excel.application" )
ExcelServer.Workbooks.Open("c:\book.xls")
ExcelServer.ActiveWorkBook.Sheets("Sheet2").Select
ExcelServer.ActiveSheet.Cells(1,1).value = "书名"
ExcelServer.Application.Visible = TRUE
方法二：先启动excel文件
long handle_1
handle_1 = OpenChannel("Excel", "c:\book.xls")
//建立与EXCEL的通道
setremote("r1c1","书号","excel"," c:\book.xls")
setremote("r1c2","书名","excel"," c:\book.xls")
//更改某一行或某一列的值，同时关闭通道
closechannel(handle_1,handle(parent))


CREATE DATABASE时显示CATALOG TABLES CAN NOT BE created and are not available for use

问：我在PB6.5中，用数据画板中的DATABASE，之后用CREATE DATABASE
（我装的是SQL ANYWHERE 5），但显示CATALOG TABLES CAN NOT
BE created and are not available for use，之后可以让
我用TABLE建表，但建表时只有上半部分，下面的指定EDIT，HEADER，
LABEL等都反百，不给操做，为何？

答：我也发生过这种情况，其实很简单：
是因为你在当前的路径下有两个pbodb60.dll,
一个在你装的PB...\sybase\Shared的目录下，一个可能是你拷贝到你的工程目录下了，
因为你工程目录没有pbodb60.ini，所以会出现你说的问题，
解决方法是，删除你工程下的pbodb60.dll就OK！


用PB如何调用WINDOWS的API

用PB如何调用WINDOWS的API（以播放声音为例）？

问：各位高手请进，不要嫌问题简单！ 请教各位高手：用PB如何调用WINDOWS的API，即具体要在哪几个地方写些代码？
答：调用 API 函数步骤：
1、在适当的位置声明函数，如窗口内，Application内，UserObject内，定义在Local External Function或Global External Function中，如播放声音的：
Function boolean sndPlaySoundA (string SoundName, uint Flags) Library "WINMM.DLL"
Function uint waveOutGetNumDevs () Library "WINMM.DLL"

可以创建一个UserObject,集中声明常用的API及函数本地化，如
u_external_function
Declare
Function boolean sndPlaySoundA (string SoundName, uint Flags) Library "WINMM.DLL"
Function uint waveOutGetNumDevs () Library "WINMM.DLL"

Function
uf_play_sound(string as_wave_name,integer ai_option) //文件名，同步或异步(1/0)

uint lui_numdevs
lui_numdevs = WaveOutGetNumDevs()
If lui_numdevs > 0 Then
sndPlaySoundA(as_filename,ai_option)
return 1
Else
return -1
End If

保存
在程序中定义一个实体：
u_external_function iu_external_function
iu_external_function = create u_external_function
iu_external_function.uf_play_sound('c:\windows\media\ding.wav',1)
试试看，如果有声卡，就会听到“叮”的一声。

其它函数也是如此处理。


如何注册OCX控件

问：我写的系统中带了OCX控件，当系统编译后发行时必须带上该OCX控件，但在用户的机子上必须注册该OCX控件才能使用，否则程序运行会出错，是否要通过程序来注册该OCX控件或者还有其它的方法？
答：提供以下几种方法：
1、在DOS或Windows命令行下运行：regsvr32 ocxname.OCX 注册
2、用Install Shield，在里面选“Self-Registered”，安装程序将自动注册。
3、在PB应用程序中注册：
函数声明：
Function long DllRegisterServer() Library "ocxname.OCX"
调用：
LONG ll_RC
ll_RC = DllRegisterServer()


如何在PB应用程序中创建目录？

答：定义外部函数:
function boolean CreateDirectoryA(ref string pathname,int sa) Library "Kernel32.dll"

代码:
boolean rtn
string l_dir
l_dir = "c:\testdir"
rtn=CreateDirectoryA(l_dir,0)
if rtn then
Messagebox("Succeed","Succeed!" )
else
Messagebox("Failed","Failed!")
End if


PowerBilder如何与Microsoft SqlServer连接
问：在win98下，(or winnt)，PowerBilder如何与 Microsoft SqlServer相连？

答：与此相关文章很多，在此简述大致思路：
1）服务器安装SQL Server并启动，创建数据库。
2）客户机安装SQL Server客户端。
3）启动PB，配置数据源描述（通过直连接口，不要用ODBC）。
4）连接。


如何远程登录到sybase和sqlanywhere数据库

问：如何远程登录到sybase和sqlanywhere数据库,哪位大侠为我指点迷津？

答：
1、先通过拨号连接到服务器。
2、定义连接参数，象局域网一样连接。asa用dbserv驱动。


Sybase SQL Anywhere 5

Sybase SQL Anywhere 5.0数据库的备分与恢复？

问：我用的是SQL ANYWHERE 5.0建库的，请问实现数据备份和恢复时的具体步骤和实现方法是怎样的？请写出具体的程序代码，多谢！
答：lysoftware
备份:
安装sql anywhere后，在sql anywhere5.0\win32\ 目录中可以找到dbbackup.exe。再在ms-dos方式下用命令dbbackup /?或者在sql anywhere中的帮助下查看其命令开关. 如:dbbackup -c "eng=dbname,uid=dba,pwd=sql" -d c:\ ,在pb中用run函数运行即可。
恢复:
我用的是在pb中用run("copy %1 %2")。

yiyi：
如果是sql anywhere数据库，可以在程序中调用dbbackup命令实现；如果是SQL SERVER等数据库，则应在服务器端中备份。


PB + Sql Anywhere 应用程序如何脱离开发环境运行

问：PB + Sql Anywhere 应用程序，请问如何脱离开发环境运行？用户是否需要安装sql anywhere？如何编写脚本配置ODBC数据源？ 请举一例好吗？
答：不用装SQL Anywhere5.0，只要把相关文件(Dbeng50.exe,wl50ent.dll,wtr50t.dll,wod50t.dll)拷贝到当前目录(如 D:\temp)下就OK!

在ODBC.INI中加上：wod50t.dll所在路径
如：

Gzmis.db是数据库名,d:\temp是当前目录。
[Gzmis]
Driver32 = D:\temp\wod50t.dll

在注册表中加上：
HKEY_CURRENT_USER\SoftWare\ODBC\ODBC.INI\Gzmis中加下面几项：
AutoStop "Yes"
DatabaseFile "d:\temp\gzmis.db"
DataBaseName "Gzmis"
Driver "d:\temp\wod50t.dll"
PWD "sql"
Start "dbeng50"
UID "dba"

只要使用两个函数：
setProfileString（）//设置INI.
Registryset（）//设置注册表.

再加上PB的DLL动态库就OK！
如 PWD改了的按改了的值。


如何获取鼠标是否进入控件

如何获取鼠标是否进入控件（按钮tips显示）？

答：现用commandbutton控件举例，其他控件一样：
1.首先声明两个API函数
//获得焦点
Function ulong SetCapture(ulong hWnd) Library "USER32.DLL"
//释放焦点
Function BOOLEAN ReleaseCapture() Library "USER32.DLL"

2.自定义用户事件鼠标在控件上移动mousemove(EventID号为pbm_mousemove)，鼠标进入控件mousewithin，鼠标离开控件mousewithout。

3.声明一些Instance变量
boolean ib_mousecaptured//控件是否已经获得焦点
boolean ib_mousewithin//鼠标是否进入控件
integer ii_losecapture=0//控件是否要失去焦点

4.在mousemove事件中写入
if ii_losecapture > 1 then
if not ib_mousecaptured then//没有捕获鼠标移动
//捕获鼠标移动
SetCapture(handle(this))
ib_mousecaptured = TRUE
else //已经捕获鼠标移动
if xpos < 0 or ypos < 0 or xpos > width or ypos > height then//鼠标不在该控件上
//释放鼠标
ReleaseCapture()
ib_mousecaptured = FALSE
//触发mousewithout事件
this.post event mousewithout()
ib_mousewithin=false
ii_losecapture=0
//this.of_set()
else
if not ib_mousewithin then
//触发mousewithin事件
this.post event mousewithin()
ib_mousewithin=true
end if
end if
end if
else
ii_losecapture=ii_losecapture + 1
end if

5.最后你就可以在mousewithin和mousewithout中写入你想要的效果或事件。

of_set()可能是重置某些值的操作，可以不用它，将它注释掉，一样可以实现诸如鼠标到达按钮时显示tips。


PB是否支持反编译？

问：我想将PB编译后的PBD及EXE文件还原成PBL文件，请问是否能做到？

答：PB不支持反编译。不过你试试：把编译后的PBD文件直接改名为PBL文件（先做好备份），可以在PB下直接使用。里面的的应用、菜单、窗口等对象一个不少，而且还能运行，不过不能看到源码。


Listview和Treeview控件的中文显示乱码

问：有谁用过PB7.0么？我怎么在里面的Listview Control和Treeview Control里不能显示中文(乱码)？

答：试试：
将 ListView 的 FontCharSet 属性设成其他类型。先将ListView 所在的对象 Export 成源码，在源码中找到 ListView 定义部分，将 FontCharSet 属性改成 DefaultCharSet，之后再Import回去。


SQL Anywhere 5

SQL Anywhere 5.0的窗口放到状态栏右下方的输入法旁边

问：有什么方法可以将运行后的SQL Anywhere 5.0的窗口放到状态栏右下方的输入法旁边？

答：那是PB7.0的特性，首先你得安装了Adaptive Server Anywhere 6.0。通过修改注册表，找到注册表中ODBC对应的项，将DRIVER改成DBODBC6.DLL，START改成DBENG6.EXE就可以了。如：
Driver = C:\Program Files\Sybase\SQL Anywhere 5.0\win32\wod50t.dll
Start = C:\PROGRAM FILES\SYBASE\SQL ANYWHERE 5.0\WIN32\dbeng50.exe
改为：
Driver = D:\Sybase\Adaptive Server Anywhere 6.0\win32\dbodbc6.DLL
Start = D:\Sybase\Adaptive Server Anywhere 6.0\win32\dbeng6.exe


单击编辑框选中其中内容

答：多数PB初学者在编程时可能会需要这样的功能：当用户单击单行编辑框时，选中编辑框内的字符，以便用户“替换录入”，相信你和我一样，会想到在控件的getfocus事件中书写代码：this.selecttext(1,len(this.text))。保存后运行，却得不到我们想要的效果。百思不得其解之际，想到了一个另类办法：以pbm_bnclicked为事件ID，创建单行编辑框的自定义事件ue_clicked，代码是：this.selecttext(1,len(this.text))，getfocus事件的代码改为：This.Post Event ue_clicked()。保存后运行，效果出来了！


如何连接ACCESS

如何连接ACCESS、EXCEL

问：请问大侠：我使用的是PB6.5，但是无法连接ACCESS、EXLCEL等时，无法看到该数据库中的表，没有任何表的信息请问如何解决？
答：连接ACCESS数据库
建一数据库aaa.mdb其中表名为abc
选数据库面板后按cancel按纽
选file菜单中的configure ODBC...
在installed drivers中选Microsoft Access Driver(*.mdb)
按create按纽
在数据库中按《选取...》按纽,找到aaa.mdb
在《数据库名：》输入aaa
此时在file菜单中选connect即可。<注：一定要用ODBC接口>
连接Excel与Access大同小异，不过就我感觉你最好还是将Excel转化为Access数据库后再连接，因为它毕竟不是专门的数据库文件，用起来有很多不便。

连接Excel确实会出现此种情况，它只有一个系统表而看不到别的表。
不过可以先将其转化为Access数据库或做一《外部类型数据源》的数据窗口对象后用import() 导入Excel文件也可。Access 数据库是可以正常转化的，没任何毛病。因为我在回答此问题时已做了一个Access 数据库并成功地转化了。


如何在PB中实现串口编程

问：如何在PB中实现串口编程？

答：可以使用mscomm32.ocx控件
脚本如下：

String ls_data

//使用COM1端口。
ole_1.object.CommPort = 1
//设置速率为9600,无奇偶校验，8 位数据，一个停止位。
ole_1.object.Settings = "9600,N,8,1"
//读入整个缓冲区的数据。
ole_1.object.InputLen = 0
打开端口
ole_1.object.PortOpen = True

//发送attention命令
ole_1.object.Output = "ATV1Q0" + char(13)

//等待数据。
Do
Yield()
//从Com端口取数据
ls_data += ole_1.object.Input
LOOP Until(Pos(ls_data, "OK" + char(13) + char (10)) > 0)
//向Com端口发送数据使用Output方法
ole_1.Object.Output = ls_data

//关闭端口。
ole_1.object.PortOpen = FALSE


Sql AnyWhere升迁到SQL Server的其他问题

其实，把基于sql anywhere的软件升级到sql server，除了上次提到的问题之外，另一个需要注意的就是日期、时间问题。
大家知道，sql anywhere要分别有日期和时间类型，而sql server中只有日期时间类型，因此，在数据升迁时，原来只有日期或时间的字段都分别加上了sql server的默认日期或时间。这样，原来在数据窗口的getitemdate()函数就要改为getitemdatetime()了，否则数据会出错。

另外，select getdate() from table_name语句可以提取服务器的当前日期时间，但那是指table_name中有数据的情况，如果table_name中没有记录，提取出来的就是数据库默认的日期时间(1900-01-01 00:00:00)，那显然不是我们想要的结果。


菜单对象继承的有关问题

答：谈谈继承菜单对象的有关注意事项……

1、在继承菜单对象中可以做的事情：
1）在菜单中插入菜单对象。
2）修改已有菜单对象的属性。
3）为父菜单中没有脚本的菜单对象建立脚本。
4）扩展或取代继承下来的菜单对象脚本。

2、在继承菜单对象时不能做的事情：
1）不能改变继承下来的菜单对象的顺序（可以在其中插入或追加新的菜单项，但不能改变原来的菜单对象顺序）。
2）不能删除继承下来的菜单对象。不需要的菜单项可以通过设置属性使它不可见。

3、如何在继承下来的菜单中插入菜单对象？
1）在父菜单中为所有要向右或向下移动位置的菜单对象设置ShiftOver/Down属性。
2）在Menu描绘器中，在后代菜单栏或下拉菜单的末尾输入要插入的菜单对象。
3）在运行时，PowerBuilder会自动把所添加的菜单对象插入在父菜单里指定了ShiftOver/Down属性的所有菜单对象之前。


使Datawindow每页打印固定行

问：请问在pb中如何设置打印分组数据窗口的每一页的行数

答：使Datawindow每页打印固定行
---- 第一步:增加一个计算列,此计算列必须放在Detail段，Expression中输入: ceiling(getrow()/20) <--这里20还可以用全局函数取代，这样可以允许用户任意设置每页打印多少行。

---- 第二步:定义分组,选择菜单Rows->Create Group...

---- 按计算列字段分组，并一定将check box-->New Page On Group Break选中。

---- 第三步:将此计算列设为不可视。

---- 另外，如果需要最后一页不足补空行。也很简单，如下：

long ll_pagerow = 6 //每页打印行数
long ll_count, ll_row
ll_count = dw_report.retrieve(...)
//取得现有报表的总行数
ll_count = ll_pagerow - mod(ll_count, ll_pagerow)
If ll_count < ll_pagerow Then
for ll_row = 1 to ll_count
dw_print.insertrow(0) //补足空行
next
end If


怎样在一个程序中连接两个数据库

问：SOS
怎样在一个程序中连接两个数据库？数据库在两台机器中，一个是SQL6.5，一个是SQL7.0。
答：
你可以这么做
Transaction mytrans1,mytrans2
mytrans1=Create Transaction
mytrans2=Create Transaction
mytrans1.DBMS="驱动程序类型"
mytrans1.DBParm="这里填连接Sql6.5的字串"
mytrans2.DBMS="驱动程序类型"
mytrans1.DBParm="这里填连接Sql7.0的字串"
connect using mytrans1;
connect using mytrans2;
剩下的你知道该怎么做了？

eg:
Transaction mytrans1,mytrans2
mytrans1=Create Transaction
mytrans2=Create Transaction
mytrans1.DBMS = "ODBC"
mytrans1.AutoCommit = False
mytrans1.DBParm = "ConnectString='DSN=YourDsnforSql6.5DB'"
connect using mytrans1;
IF mytrans1.sqlcode = -1 THEN
Messagebox("",Yourmessage)
disconnect using mytrans1;
halt
end if

mytrans2.DBMS = "ODBC"
mytrans2.AutoCommit = False
mytrans2.DBParm = "ConnectString='DSN=YourDsnforSql7.0DB'"
connect using mytrans2;
IF mytrans2.sqlcode = -1 THEN
Messagebox("",Yourmessage)
disconnect using mytrans2;
halt
end if

//If t65 is a table in your sql6.5 Database,t70 is a table in your sql7.0 Database and
//dw_1 and dw_2 are DataWindow Control ,dwo_sql65 and dwo_sql70 are respectedly DataWindow
Object
//for t65 and t70 then
dw_1.dataobject="dwo_sql65"
dw_1.settransobject(mytrans1)
dw_1.retrieve()//It is best to test error
dw_2.dataobject="dwo_sql70"
dw_2.settransobject(mytrans2)
dw_2.retrieve()//It's the same as above
//Then ,you can manipulate the data by dw_1 and dw_2
//Do you understand?


怎样在程序中构造 DropListBox 列表项

答：如要将数据表Brance中记录name字段插入DDLB：
1.建立一个数据窗口对象，如：DW_Brance
2.在窗口的OPEN中：
long i
datastore lds_0 //说明一个数据存储变量
lds_0=create datastore //创建数据存储实例
lds_0.dataobject="dw_Brance " //将部门编码数据对象赋给数据存储实例
lds_0.settransobject(SQLCA) //设置数据存储使用的事务对象
lds_0.retrieve() //读取数据
row0=lds_0.rowcount()//记录个数

for i=1 to row0
//插入ddlb新记录
ddlb_bm.insertitem(lds_0.object.name[i],i)

next

ddlb_bm.SELECTITEM(1)

kukoc注：
可以将此功能作成一个函数，参数为datastore和ddlb，以后每次调用就是了。


怎样给grid样式添加一标题

给grid样式添加一标题

问：本人用PB开发软件时遇到一问题，当我希望把学生成绩表以表格(grid样式)打印输出，在表格前加一该班班级、年级标题时，发现PB中数据窗口grid样式不能添加一标题，有没有其它方法解决，请哪位高手指教！万分感谢！
答：放一个Text(文本框)对象到DataWindow的表头上，
在此Text上点击鼠标右键选择Properties->Postion->layer,将layer改为Foreground,这样你就
可以改变Text的位置和宽度，然后将此Text的内容改为正确的报表标题即可，注意还需要将
此Text的背景改为非透明色，否则会看到表线。
对于标题中文本可以用dw_1.modify("text_name.text = '标题'") 在程序中动态修改。


在运行中怎样刷新datawindow中的DropDownDW？
问：在运行中怎样刷新dataWindow中的DropDownDW
数据？
在dataWindow中定义了其中某些字段的edit属性为DropDownDW，在运行程序时，DropDownDW的值已经改变，但下拉时还是原来的数据，怎样更新？谢谢各位！
答：
DataWindowChild state_child

integer rtncode
rtncode = dw_1.GetChild(' emp_state', state_child)
IF rtncode = -1 THEN MessageBox("Error", "Not a DataWindowChild")
// Establish the connection if not already connected
CONNECT USING SQLCA;
// Set the transaction object for the child
state_child.SetTransObject(SQLCA)
// Populate the child with values for eastern states
state_child.Retrieve()
// Set transaction object for main DW and retrieve
dw_1.SetTransObject(SQLCA)
dw_1.Retrieve()


怎样实现在循环中途退出？

问：我要用PB实现在循环的时候可以点击按钮然后退出循环？就像在VB中的循环中用DoEvent实现的一样？
答：这当然要用Yield()函数了。如：
integer n
//sb_interrupt 是共享变量
sb_interrupt = false
for n = 1 to 3000
yield()
if sb_interrupt then
//sb_interrupt的值在“取消”按纽的Clicked事件中修改为true
messagebox("人家不干了","你真坏！")
sb_interrupt=false
exit
else
//其它处理，在单行编辑器中显示当前n值
sle_1.text = string(n)
//....
end if
next


怎样得到字符串中汉字的个数？

问：怎样得到字符串中汉字的个数？
答：找了一段代码，可以得到字符串中汉字的个数，
For i = 1 to Len(aString)
ls_ch = Mid(aString,i,1)
If Asc(ls_ch) >= 128 then //是汉字
li_num++
i = i+1
End if
Next

最后，li_num就是汉字的个数了，你可以试试。


Grid数据窗口

再谈在Grid数据窗口中，鼠标调整数据宽度时对应的事件

孔夫子问：
蔡大侠说在Grid风格的数据窗口中,当用户用鼠标调整数据宽度时,不对应任何事件,但我确实希望对应一个事件,因为我制作的学生成绩表是grid和一标题(文本框)组合而成的,标题是居中显示方式,当用户用鼠标调整数据宽度时(这种操作很有必要,如打印数据时),标题理应自动居中,因此要在此条件下(调整数据宽度时)编程,即对此事件编程.如果不对应任何事件,该如何解决上述情况?请蔡大侠或哪位高手指点,在此谢了.

蔡毅答：
窗口打开时，保存最后一列的X值；打印前，把最后一列的X值与原来保存的比较，如果有变化的话，把标题文本框的宽度调整为最后一列的X值加上该列的宽度。

孔夫子问：
非常感谢蔡大侠,但这种办法势必要增设计一个按钮,这对于用户来说完全没必要,因为用户在调整了宽度后,文本框中字就自动居中,没必要用户再去点击那个按钮,有没有别的办法?

Kukoc答：
蔡毅说得没有错，不必另外增加按钮，只是在打印按钮前判断就可以了，我有几个应用就是用这种办法。
如果实在要找对应事件的话你试试用这三个user event:pbm_lbuttondown,pbm_mousemvoe,pbm_lbuttonup
如果:
pbm_lbuttondown: 在数据窗口标题栏（getrow() = 0 and GetObjectAtPointer =你的标题）按下鼠标左键，记一个标志；
pbm_mousemvoe: 鼠标移动了，在记一个标志；
pbm_lbuttonup: 这时鼠标左键up，再判断最后一列的位置是否发生变化，如果有，写你的脚本。

你试试吧，不知有没有用。

另：在Grid数据窗口的Clicked事件中加入：
If row = 0 Then return 1
可以禁止调整列宽度。


在run之后判断该程序是否执行完毕

在run语句之后判断该程序是否执行完毕

Guest问：
run("e:\program files\microsoft visual studio\vfp98\vfp6.exe c:\gzhz.prg")
dw_1.reset()
messagebox("hello","按确定键把汇总文件导入！！！")
dw_1.importfile("c:\gzhz.txt")
delete from gzhz;
dw_1.update()
程序中，执行第一句的时候，由于foxpro慢，没等它结束，就执行
了下面的句子。一句一句执行，该咋办？

蔡毅答：
在run语句之后判断该程序是否执行完毕。

定义外部函数：
Function long FindWindowA (String lpClassName , String lpWindowName ) Library "user32.dll"
Function boolean IsWindow (Long hwnd ) Library "user32.dll"

脚本：
run("e:\program files\microsoft visual studio\vfp98\vfp6.exe c:\gzhz.prg",Minimized!)

ll_handle = 0
//循环至窗口打开
Do While ll_handle = 0
ll_handle = FindWindowA("tty","foxpro应用的窗口名") //
yield() //
loop
//等待窗口关闭
Do While isWindow(ll_handle)
Yield()
Loop

（继续下面的语句）

Guest问：
我的gzhz.prg文件如下：
set safe off
use c:\gzhz.dbf
zap
appe from c:\gzhz.txt type deli with tab
sort on xm to c:\gzhz1.dbf(按姓名排序)
use c:\gzhz1.dbf
total on xm to c:\gzhz2.dbf（按姓名汇总）
use c:\gzhz2.dbf
sort on bh to c:\gzhz3.dbf（按编号排序）
use c:\gzhz3.dbf
copy to c:\gzhz.txt type deli with tab（生成一个TXT文件）
quit

那在这一句中，ll_handle = FindWindowA("tty","foxpro应用的窗口名")
我该填啥？

我是小木鱼答：
也可以这样做，在PB中：
run(....)
//延时，判断一下某文本文件中的某值或某文本文件是否存在？
若否则继续延时再判断，直到成功
//继续进行下面的语句。

在VFP中(你的*.prg文件)最后全部执行完毕后再新增一文件或改文本文件中的值。


常见问题解答

PowerBuilder编程俱乐部文章

PowerBuilder编程俱乐部（一） 1999年9月6日
1、PowerBuilder与Oracle 的连接
2、如何将数据窗口出错信息改为中文？
3、PB中如何打开一个文件（如.txt，.doc），就像在资源管理器中双击打开文件一样？
4、数据窗口中用Enter键替代Tab 键切换栏目
5、setfilter的字符型变量匹配
6、怎样得到一个应用程序如Outlook的路径？
PowerBuilder编程俱乐部（二） 1999年11月1日
1、怎样得到数据窗口中的所有列名及其text的文字说明？
2、如何隐藏Sysbase SQL Anywhere 5.0在系统状态条上的的图标？
3、程序在退出时弹出一个确认窗口供用户确认
4、怎样得到程序运行时的路径
5、查找指定的脚本
6、怎样限制应用程序只运行一次？
PowerBuilder编程俱乐部（三） 1999年1月17日
1、怎样返回下拉列表（DropDownListBox）当前选中项的索引号？
2、Sybase SQL Anywhere 5.0 数据库文件拷贝到别的机器不能用？
3、FILE菜单中找不到书中所讲的Create Database选项
4、如何得到下拉数据窗口（DropDownDataWindow）列的显示值？
5、程序中自动控制切换中英文输入法
6、动态实现下拉数据窗口（DropDownDataWindow）的设定
PowerBuilder编程俱乐部（四） 2000年5月8日
1、请问在PB的数据窗口中怎样实现某一列的增量查询功能
2、怎样在PB程序中动态修改表结构？
3、怎样将PB应用的全部源代码打印出来
4、如何在菜单中触发父窗口对象中命令按钮控件的单击事件？
5、如何在复合报表（Composite DataWindow）中引用计算域的值？
6、怎样在PB6.5中使用反三角函数？
PowerBuilder编程俱乐部（五） 2000年7月17日
如何在Sybase SQL Anywhere 服务器增加用户和设置数据库的操作权限？
程序编译成.EXE 文件后运行时连不上数据库(SQL Anywhere的本地库)？
怎样检测数据窗口中数据是否被修改？
在数据窗口中用箭头键实现“Shift＋Tab”按键的功能？按左箭头键光标便移动到当前列的左边一列，按右箭头键光标便移动到右边一列?
动态切换数据窗口对象编译后运行为何出错？
PB编译后的程序带参数运行
PowerBuilder编程俱乐部（六） 2000年9月18日
PB如何调用WINDOWS的API函数
如何在PB应用程序中创建目录？
注册OCX控件
如何带参数执行SQL的系统储存过程？
PowerBuilder编程俱乐部（七） 2000年12月04日
在PB中实现文件的拷贝与删除
SQL ANYWHERE 5.0如何实现数据备份和恢复
如何在PB中实现串口编程？
在PB程序中启动控制面板中的'时间/日期'程序
随意修改动态报表
PowerBuilder编程俱乐部（八） 2000年12月04日
在PB中实现精确的时间控制
PB中怎样实现在循环时可以通过点击按钮终止循环?
如何关闭打开的dos窗口？
数据窗口的多表更新问题
如何在非PB窗口中实现键盘按键和鼠标点击动作的检测？

其他常见问题

71、怎样实现在循环中途退出？ 新
70、在运行中怎样刷新datawindow中的DropDownDW？
69、给grid样式添加一标题
68、怎样在程序中构造 DropListBox 列表项
67、怎样在一个程序中连接两个数据库
66、使Datawindow每页打印固定行
65、菜单对象继承的有关问题
64、Sql AnyWhere升迁到SQL Server的其他问题
63、如何在PB中实现串口编程
62、如何连接ACCESS、EXCEL
61、单击编辑框选中其中内容
60、SQL Anywhere 5.0的窗口放到状态栏右下方的输入法旁边
59、Listview和Treeview控件的中文显示乱码
58、PB是否支持反编译？
57、如何获取鼠标是否进入控件（按钮tips显示）？
56、PB + Sql Anywhere 应用程序如何脱离开发环境运行
55、Sybase SQL Anywhere 5.0数据库的备分与恢复
54、如何远程登录到sybase和sqlanywhere数据库
53、PowerBilder如何与Microsoft SqlServer连接
52、如何在PB应用程序中创建目录？
51、如何注册OCX控件
50、用PB如何调用WINDOWS的API（以播放声音为例）？
49、CREATE DATABASE时显示CATALOG TABLES CAN NOT BE created and are not available for use
48、PB中如何实现对EXCEL的操作？
47、命令按钮怎么不能用键盘响应？
46、如何在pb中获取本机或网络中已配置的打印机的列表？
45、在PB中实现登陆SYSBASE后更改登陆密码
44、如何取得实际打印页数
43、怎样在程序中运行控制面板中的程序
41、用PowerBuilder开发多媒体播放器
40、动态SQL四种类型的语句格式
39、如何关闭打开的（DOS）窗口？
38、如何改变数据窗口中某些含有特定字符的域的颜色？
37、如何带参数执行SQL的系统储存过程？
36、在run语句之后判断该程序是否执行完毕
35、程序中出现Database transaction information not available的错误？
34、如何控制数据窗口中行背景颜色
33、怎么在数据窗口中用左箭头实现shift+tab
32、PB中字段间如何进行加减等运算
31、如何在PB中加载声音文件？
30、解 SQL ANYWHERE 密码的方法
29、如何打印条形码
28、如何从客户端获得服务器的时间
27、动态切换数据窗口对象编译后运行为何出错
26、数据窗口的自动折行设置
25、如何恢复SQL_SERVER数据库
24、Powerbuilder中怎样实现用代码配置ODBC
23、再谈在Grid数据窗口中，鼠标调整数据宽度时对应的事件
22、怎样实现计算机对KB和MOUSE输入不响应
21、如何在PowerBuilder中创建图标栏应用
20、数据窗口中的按钮问题
19、能否把按钮的死板样换得好看一些
18、关于日期显示的问题
17、怎样带参数运行PB应用程序？
16、怎样让DW的列在点击时标题的边框由3D raise改为 3D lower？
15、如何实现HTML格式帮助文件的相关调用
14、如何实现打印完一个DataWindow后不换页？
13、怎样调用Windows API取得应用安装后的路径？
11、怎样得到字符串中汉字的个数？
12、如何在Sybase SQL Anywhere 服务器增加用户和设置数据库的操作权限？
11、关于Access数据库程序类型越界
10、关于数据管道编译后运行出错的问题
9、请教SQL ANYWHERE数据库为何越删越大？
8、PB + Sybase SQL Anywhere C/S配置
7、怎样在程序中为datawindow加计算域或line？
6、如何使用pb7中的进度条控件
5、大文本报表显示自动折行问题
4、如何能够在PB运行中得到任意表的结构
3、PB6.0与VFP6.0的连接问题
2、如何检测数据窗口中数据是否被修改
1、经过编译生成的PowerBuilder应用程序需要的运行环境


PowerBuilder编程俱乐部（一）

    一、问：一个MIS系统采用C／S的开发模式，前台开发工具用PB6.5，后台数据库为Oracle7.3。我通过OBOC却连不上数据库，请问应该如何配置？

    答：PB提供了与当前流行的大型数据库如Oracle、Informix、Sybase等的专用接口，连接时无需通过ODBC。配置步骤具体如下：
    1． 首先要通过SQL＊Net配置数据库别名。
    启动SQL＊Net Easy Configuration后，选择“添加数据库别名” ( Add Database Alias)，输入数据库别名， 如hello。选择协议TCP／IP，输入TCP／IP主机名(TCP／IP Host Name)，可为主机名或IP地址输入数据库名(Database Instance)如ora7。单击“确定”按钮，生成数据库别名(hello)后，按“取消”退出。连接数据库，测试SQL＊Net是否连通。运行SQL＊PLUS，用自己的用户名，如kukoc进入，在用户名处输入kukoc／kukoc@hello 。若进入远程数据库，则SQL＊Net 配置成功。
    2．然后，在PB6.5中配置 DB Profile。
    选择073 Oracle7.3，按Button New，输入Profile Name，如hello。
    输入Server：@hello（此处一定要为@＋开始配置的数据库别名）
    输入Login ID：kukoc
    输入Password：kukoc
    单击确定按钮结束。
    我们也可以通过配置文件（.ini）实现数据库连接，如hello.ini文件中数据库配置如下：
    [Database]
    DBMS=O73 ORACLE 7.3
    Database=
    UserId=
    DatabasePassword=
    LogPassword=kukoc
    ServerName=@hello
    LogId=kukoc
    Lock=
    DbParm=
    Prompt=0

    二、问：我在编写数据窗口录入程序的时候，若用户录入违反了约束（如关键字重复、非空项没输入等），程序老报一大堆英文出错信息，用户也看不明白。我怎样才能屏蔽它，并显示中文的错误信息？

    答：可以自己编写一个错误信息函数，如f_dwerror_message（），参数为错误信息代码（Long型），返回值为1。函数中根据传入的错误代码给出相应的中文错误信息。在数据窗口的dberror事件中调用它：
    return f_dwerror_message (sqldbcode)
    而且，我们还可以将此数据窗口作成用户对象，要用到的地方继承即可。

    三、问：在PB中如何打开一个文件（如.txt，.doc），就像在资源管理器中双击打开文件一样？
    答：可以通过API函数来实现。
    在应用程序的Global External Functions中定义：
    Function long ShellExecuteA (ulong hwnd, string lpOperation, string lpFile, ＆ string
lpParameters, string lpDirectory, long nShowCmd) library “shell32.dll” 　　调用如下：
    String ls_null
    SetNull (ls_null)
    ShellExecuteA(Handle(Parent), ls_null, “c:\doc\hello.txt”, ls_null, ls_null, 1)
    四、问：PB中数据窗口输入栏目（域）间的切换通过按键盘最左边的Tab 键来实现，既不方便又影响录入速度。如何能用Enter键替代Tab 键切换栏目，实现焦点的转移？

    答：由于按Enter键是Windows直接支持的消息，故我们可以使用用户事件来解决此问题。 在用户事件中，PowerBuilder提供的一条pbm_事件对应Windows的一条或几条消息。我们在数据窗的用户事件中选择pbm_dwnProcessEnter并命名为ue_Enterkeydown。当我们按下Enter键时，将触发此事件。
    提供两种方法（推荐用第二种）：
    1.利用数据窗口的SetColumn函数。
    在所定义的ue_Enterkeydown事件下写代码：
    long ll_column_count
    long ll_column
    ll_column_count =long(this.Describe(“DataWindow.Column.Count”))
    ll_column = this.GetColumn()
    if ll_column = ll_column_count then
    return
    else
    this.SetColumn(ll_column ＋ 1)
    end if
    用上面脚本，须注意几点：
    (1) 按Enter键，焦点的切换是按所选列的顺序，而不是按设置的Taborder的顺序。
    (2) 若某列的visible属性为false 或 TabOrder = 0， 则以上方法不起作用。
    若不合以上要求，即所选列的顺序与要录入域顺序不一致，或某些列的visible属性为false或 其TabOrder = 0，此时不能用上述代码，但也可用类似方法实现，只是稍繁琐一点，举例如下：
    long ll_column
    string ls_column_name
    ll_column = this.getcolumn()
    ls_column_name = this.Describe(“＃”＋string(ll_column)＋“.Name”)
    choose case ls_column_name ／／列名
    case ‘no'
    this.SetColumn(‘name')
    case ‘name'
    this.SetColumn(‘age') 　　case ‘age'
    …
    end choose
    2.把消息传递给Tab键，同时忽略Enter键的处理，这种方法最为方便简洁。在ue_Enterkeydown事件中
    编写脚本如下：
    Send(Handle(this),256,9,Long(0,0))
    Return 1

    五、问：Datawindow中要匹配的条件是变化的，如输入一个名字（名字放在变量ls_name中），用dw_name.setfilter(“name = ” ＋ ls_name )不起作用。如何用setfilter将这条记录找出来？

     答：因ls_name 是string型的，filter语句应该这样写：
    dw_name.SetFilter(“name = ‘” ＋ ls_name ＋ “'”)
    dw_name.Filter( )
    当ls_name = “张三”时，脚本实际为：
    dw_name.SetFilter(“name = ‘张三’”)
    dw_name.Filter( )

    六、问：怎样得到一个应用程序如Outlook的路径？

    答：可用RegistryGet()函数从系统注册表中获得。具体用法如下：
    li_return = RegistryGet (“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows ＆
    \CurrentVersion\App Paths\MSIMN.EXE”,“Path" , ls_outlook_path )
    执行此脚本后，Outlook的路径将保存在string型变量ls_outlook_path中。


PowerBuilder编程俱乐部（二）

    一、问：我想做一个数据窗口的组合条件查询，要得到数据窗口中的所有列名及其text的文字说明，请问怎样才能取得？

    答：Describe()函数可以得到数据窗口的多项属性值，用它实现如下：
    long ll_column_count
    long ll_i
    string ls_column[] ／／列名
    string ls_column_text[] ／／text的名字
    ／／得到数据窗口的总列数
    ll_column_count = long(dw_1.Describe(″DataWindow.Column.Count″))
    ／／循环依次读取
    for ll_i = 1 to ll_column_count
    ls_column[ll_i] = dw_1.Describe(″＃″＋string(ll_i)＋″.Name″)
    ls_column_text[ll_i] = dw_1.Describe(ls_column[ll_i] ＋ ″_t.text″)
    next
    说明：数组ls_column[]保存当前数据窗中所有列名
    数组ls_column_text[]保存当前数据窗中列对应的text的文字说明。
    注：数据窗口dw_1的对象（DataObject）中text命名须用默认值，即列名＋ _t的形式，如列名为“name”，则其对应text应取名为“name_t”，否则返回值不正确。

    二、问：在应用程序运行时，数据库Sysbase SQL Anywhere 5.0启动后在系统状态条上会有图标，有时用户会不小心将其关闭，造成系统出错。请问如何隐藏Sysbase SQL Anywhere 5.0在系统状态条上的的图标？

    答：可以通过更改系统注册表来实现。以Powersoft Demo DB V6 为例：
    运行注册程序regedit，找到注册表中HKEY_CURRENT_USER\Software\ODBC\ODBC.INI\Powersoft Demo DB V6中名为Start的项目，如其原值为“C:\Program Files\Sybase\SQL Anywhere 5.0\Win32\dbeng50.exe”，在其后加上参数 －Q，改为“C:\Program Files\Sybase\SQL Anywhere 5.0\Win32\dbeng50.exe －Q”即可。
    对于自己创建的其它Sysbase SQL Anywhere 5.0数据库可用同样方法实现。

    三、问：一般用鼠标单击窗口右上角的“X”时，窗口立即关闭，但有的程序在退出时会弹出一个确认窗口供用户确认，这是如何实现的？

    答：可以利用窗口的CloseQuery事件在用户关闭窗口时进行确认。在CloseQuery事件中加入脚本：
    long ll_return
    ll_return = MessageBox(″提示信息″,″确认退出吗?″, Question! , YesNo! , 1 )
    if ll_return = 2 then
    Return 1 ／／不关闭窗口
    else
    Return 0 ／／关闭窗口
    end if

    四、问：怎样得到程序运行时的路径？

    答：用API函数GetModuleFileNameA()可实现。
    在global external functions声明：
    Function uLong GetModuleFileNameA(long hinstModule, Ref String lpszPath, uLong cchPath) Library ″kernel32.dll″
    使用：
    string ls_AppPath
    int li_ret
    ls_AppPath = Space (128)
    li_ret = GetModuleFileNameA (Handle (GetApplication ()), ls_apppath, 128)
    说明：要编译成可执行文件.exe才可用，否则得到的是Powerbuilder的pb60.exe或PB050.exe的路径。程序路径保存在变量ls_AppPath中。

    五、问：我编写的脚本忘了在哪个窗口了，怎么办？总不能让我把几十个窗口逐个打开，每个事件中来查找吧。

    答：同时选中这几十个窗口，点Entry菜单，再点击Serach菜单项，输入你要查找的脚本内容，点确定即可。系统将列出所有符合要求的窗口、控件、事件及脚本所在的行数。
    查找同样适用于函数、菜单、用户对象等。

    六、问：请问怎样限制应用程序只运行一次？

    答：提供两种实现方法（用于32位操作系统）：
    1．首先在global external functions声明外部函数如下:
    FUNCTION long FindWindowA( ulong Winhandle, string wintitle ) Library ″user32″
    然后在application的 Open 事件中加入如下代码:
    ulong l_handle, lu_class
    string ls_name
    ls_name = ″我的系统″ ／／ 此处ls_name为系统主窗口的标题Title
    l_handle = FindWindowA(lu_class, ls_name)
    if l_handle > 0 then
    MessageBox(″提示信息″, ″应用程序″ ＋ This.AppName ＋ ″已经运行,不能多次启动！″)
    Halt Close
    else
    open(w_main) ／／ 此处为系统主窗口
    end if
    这种方法是PowerBuilder联机帮助中的一个例子，是以系统主窗口的标题Title作为判别依据，若有其它与此Title同名应用程序在运行，再想启动此程序也会报应用程序已经运行。你可以将Title设为“计算器”，然后启动Windows附件中计算器程序，再运行你的PB应用程序试试。
    2．声明外部函数：
    function ulong CreateMutexA (ulong lpMutexAttributes, int bInitialOwner, ref string lpName) library ″kernel32.dll″
    function ulong GetLastError () library ″kernel32.dll″
    然后在application的 Open 事件中加入如下代码:
    ulong ll_mutex, ll_err
    string ls_mutex_name
    if handle (GetApplication (), false) <> 0 then
    ls_mutex_name = this.AppName ＋ char (0)
    ll_mutex = CreateMutexA (0, 0, ls_mutex_name)
    ll_err = GetLastError ()
    if ll_err = 183 then
    ／／ 程序已经运行
    MessageBox (″提示信息″, ″程序已经运行了！″)
    Halt close
    else
    ／／ 程序未运行
    open(w_main)
    end if
    else ／／开发模式
    open(w_main)
    end if
    这种方法必须在应用程序编译成可执行文件.exe后才有效。


PowerBuilder编程俱乐部（三）

一、问：下拉列表（DropDownListBox）中，当选中某一项后，我怎样才能返回当前选中项的索引号？（相当于VB中ComboBox的Index属性）
答：PowerBuilder中下拉列表控件没有Index属性，不能直接引用，得用其它方法实现。现给出两种方法：
1、设一long型实例变量il_index，在下拉列表的SelectionChanged事件中加入：
il_index = index
以后引用il_index即可
2、假设下拉列表名为ddlb_1，则索引号：
li_index = ddlb_1.FindItem(ddlb_1.Text,1)
二、我用的数据库是 Sybase SQL Anywhere 5.0 ，数据库文件在本机能用，拷贝到别的机器却连接不上，是否还需其它配置？
答：假设数据库文件为hello.db，将hello.db与hello.log拷贝过去，再配置ODBC数据源。如果还连不上数据库，则须重新连接事务注册文件hello.log，方法如下：
1、运行MS_DOS模式
2、进入Sybase SQL Anywhere5.0\win32目录，如
C:> cd C:\Program Files\Sybase\SQL Anywhere 5.0\Win32
3、运行命令（假设数据库文件拷贝到D盘根目录下）：
dblog -t d:\hello.log d:\hello.db
三、问： 我在安装好PB之后，以示例Powersoft Demo DB V6连接，打开DATABASE画板，出现Select Table对话框，选择Cancel。 打开FILE菜单中只有：Close、Connect、Configure ODBC、PowerPanel、Print Setup、Exit等菜单项。却没有像书中所讲的Create Database选项，请问这是为什么？
答：这个问题的关键所在是在PowerBuilder所能找到的路径中，必须有dbtl50t.dll这个文件，PB才会出建数据库的选项。 这个文件包含在完整的SQL Anywhere之中，PB安装时只安装SQL Anywhere最基本的引擎部分，不安装这个文件。
解决方法：在PB光盘上有完整的SQL Anywhere 5.0，安装它即可。或者你也可以从其他机上将dbtl50t.dll copy到C:\Windows\System或\SQL Anywhere 5.0\Win32中也可以。
（本题由网易PB版主ilike提供解答）
四、数据窗口中某列的编辑风格设为下拉数据窗口（DropDownDataWindow），有什么办法能得到此列的显示值？用GetItemNumber()函数只能得到实际值。
答：数据窗口中有一个内部函数LookupDisplay()可实现此功能，若列名为"currency"，取第1行的值如下：
dw_1.Describe ( "Evaluate('LookupDisplay(currency)',1)" )
这种方法同样适用于编辑风格为下拉列表（DropDownListBox）、收音机按钮（RadioButtons）等的列。
五、在数据窗口录入数据时，有的域要输英文、有的域要输中文，每次要手工切换，很麻烦，能不能在程序中自动控制切换中英文输入法？
答：可以用API函数实现：
声明API函数：
function boolean ImmSimulateHotKey (ULong hWnd, ULong dwHotKeyID) library "IMM32.dll"
function ulong GetKeyboardLayout(ulong dwLayout) LIBRARY "user32.dll"
function boolean ImmIsIME(uLong hklKeyboardLayout) library "IMM32.DLL"
定义变量：
constant int IME_THotKey_IME_NonIME_Toggle=112
ulong hklCurrent
ulong hnd
切换到英文输入法：
hklCurrent=GetKeyboardLayout(0)
if ImmIsIME(hklCurrent) then
hnd=Handle(parent)
ImmSimulateHotKey(hnd,IME_THotKey_IME_NonIME_Toggle)
end if
切换到中文输入法：
hklCurrent=GetKeyboardLayout(0)
if not ImmIsIME(hklCurrent) then
hnd=Handle(parent)
ImmSimulateHotKey(hnd,IME_THotKey_IME_NonIME_Toggle)
end if
可根据需要，触发两段不同的脚本以达到动态切换中英文输入法的目的。
六、问：在数据窗口对象画板中可以设定列的编辑风格为下拉数据窗口（DropDownDataWindow），请问在程序中可以动态实现这项功能吗？
答：可以的，假设所设定列为部门号"department_id"，相关连的子数据窗口为"d_dddw_dep"，显示列为部门名称"dept_name"，数据列为部门号"dept_id"，实现方法如下：
dw_1.Modify("department_id.DDDW.Name=d_dddw_dep ")
dw_1.Modify("department_id.DDDW.DisplayColumn='dept_name' ")
dw_1.Modify("department_id.DDDW.DataColumn='dept_id' ")
或：
dw_1.object.department_id.dddw.name = "d_dddw_dep"
dw_1.object.department_id.DDDW.DisplayColumn = "dept_name" dw_1.object.department_id.DDDW.DataColumn = "dept_id"
注：PowerBuilder有一个小工具DWSyntax（程序名为：dwsyn60.exe），提供了获得及修改数据窗口、列等的各项属性值的语法，对编程非常有帮助。上述脚本在DWSyntax中都能找到。


PowerBuilder编程俱乐部（四）

    问题一：请问在PB的数据窗口中怎样实现某一列的增量查询功能，就是说在单行编辑器控件中每输入一个字符，数据窗口能找到这一列值左边部分与单行编辑器中内容相等的行，并加亮显示。例如数据窗口是按整型字段“ID”升序排列的，用户在单行编辑器中输入1，则数据窗口滚动到ID字段第一位为“1”的第一行并加亮显示；再在1后输入0，则数据窗口滚动到ID字段前两位为“10”的第一行，依次类推。
    答：增量查询功能实现的关键有两点，一是单行编辑器能实时响应键盘的输入，捕捉每一个按键，获得其中的文本；二是数据窗口的模糊查询功能，即能够找到列ID左边文本与单行编辑器文本相等的行，这可以用“like”来实现。下面给出具体方法：
    1．定义单行编辑器的用户事件ue_enchange，事件的ID为：pbm_enchange。这个事件能响应键盘的输入。
    2．在单行编辑器的ue_enchange事件中编写如下脚本：
    long ll_found_row
    string ls_find
    ls_find = ″string(id) like ″ ＋ ″′″ ＋ this.text ＋ ″％′″ //查找条件（左部分与单行编辑器文本相等）
    ll_found_row = dw_name.Find(ls_find, 1, dw_name.RowCount()) //查找符合条件的行
    if ll_found_row <= 0 then return
    dw_name.ScrollToRow(ll_found_row) //滚动到相匹配的行
    dw_name.SelectRow(0 , false)
    dw_name.SelectRow(ll_found_row , true) //将匹配行加亮显示
    问题二：ORACLE 7.3 ＋ PB，在Oracle的SQL＊PLUS中可以很容易修改表结构，但如何在PB程序中动态修改表结构？比如将某一字段的长度扩大？
    答：用动态SQL语句，举例如下:
    string ls_Mysql
    ls_Mysql=″alter table department modify dept_name varchar2(12)″
    sqlca.autocommit = true
    EXECUTE IMMEDIATE :ls_Mysql;
    sqlca.autocommit = false
    commit;
    以上脚本的作用是把表department的字符型字段dept_name长度加大到12。
    这种方法还可以实现在程序中建表（将ls_Mysql的值改为″create table ...″即可 ）、删除表等操作，也适用于Sybase SQL Anywhere，MS SQL Server等数据库。
    问题三：我想把用PB开发的应用的全部源代码打印出来，请问有什么工具可以实现？
    答：不用其他工具，PB本身就可以做到。举个例子：要打印一个窗口的脚本，在Library画板中右键点击此窗口，选择print菜单项，里面有打印的内容：画面、窗口脚本、窗口控件脚本等，想打什么就选什么吧。
    问题四：如何在菜单中触发父窗口对象中命令按钮控件的单击事件？
    答：先在菜单的父窗口中定义一个自定义事件（如ue_ok，事件ID：pbm_custom01），在ue_ok事件中编写触发父窗口中命令按钮cb_ok的Clicked事件的代码： cb_ok.trigger event(clicked!) 然后在菜单的相应事件中触发该用户事件ue_ok： parentwindow.triggerevent(″ue_ok″)
    问题五：如何在复合报表（Composite DataWindow）中引用计算域的值？
    答：对于一般类型的数据窗口，我们可以用这种办法：先为计算域取名，如“count_id”，取值过程如下（除了分组的数据窗口，一般计算域都用1）：
    long ll_count_id
    ll_count_id = dw_1.getitemnumber(1,″count_id″)
    而对于复合报表，须先用GetChild()函数得到其中的report，再取此report中计算域的值。假设此 report name 为“report_department”（注意不要混淆report name 与子数据窗口本身的名字），计算域名为count_id，则取值过程如下：
    DataWindowChild dwc_child
    long ll_count_id
    dw_1.GetChild(″report_department″
    dwc_child)
    ll_count_id = dwc_child.getitemnumber(1,″count_id″)
    问题六：在用PB6.5编程序时发现PB6.5中没有反三角函数，请问怎么办？
    答：确实如此，PB中只提供了三角函数如sin()、cos()，要想使用反三角函数须借助于数据库。一般数据库如Sybase SQL Anywhere、MS SQL SERVER等都提供反三角函数，使用方法select 函数名(参数) into 变量 ……如在Sybase SQL Anywhere 5 中：
    double ldb_value
    SELECT asin(″search″.″id″)
    INTO :ldb_value
    FROM ″search″
    WHERE ″search″
.″id″ = 1;
    实现了对数据库字段的反三角计算，如果是对一般的内存变量的反三角计算可用变通的办法（此方法由网易PB版主edsion提供）：
    double ldb_value = 1
    double ldb_asin
    SELECT asin( :ldb_value )
    INTO :ldb_asin
    FROM ″department″ ;


PowerBuilder编程俱乐部

    问题一：问：PB6.5所带的数据库Sybase SQL Anywhere 5.0 小巧方便，但不知如何在Sybase SQL Anywhere 服务器增加用户和设置数据库的操作权限？
    答：不知以下的介绍对你是否有用：
    SQL Anywhere数据库的默认用户名和口令是DBA和SQL，要对数据库安全负责的话，就应当更改口令或用户名，不然大家得到你的数据库后都能打开它。
    启动Sybase Central，″connect″数据库后，在数据库项目列表中，有一项″users ＆ groups″，打开它，有以下功能：
    add user：添加用户及口令，指定级别（可选DBA）。
    add group：添加用户组。
    dba。
对已有的用户，进行修改，打开后有四个标签页：
    1．General：修改用户口令
    2．Authorities：设置用户级别，其中DBA为系统管理员；Resource为可以创建数据库级别。
    3．Membership：指定用户组。
    4．Permissions：设定对每个表的操作级别。
    dbo和public：添加或删除组中的用户。
    Sybase Central是一个数据库管理中心，功能十分强大，可以实现诸如创建数据库、压缩、备份、改变库结构（这个功能相当实用，例如你在PB的table画板下，不能改变字段类型、长度只能扩大不能缩小，在Sybase Central下你想做什么都可以）等等许多数据库操作。（此题由yiyi提供答案）
    问题二：我的程序在PB下可正常运行，单步调试也没出错。但是编译成.EXE 文件后运行时连不上数据库(SQL Anywhere的本地库)，不知哪里出了问题，有没有可能是我的PB安装得不完全？缺少一些DLL文件？
    答：经过编译生成的PowerBuilder应用程序需要如下运行环境：
    1PowerBuilder 运行时动态连接库( Runtime Library ) ，在PowerBuilder 6.0中需要如下DLL：
    PBVM60.DLL (PowerBuilder 虚拟机)(必需)
    PBDWE60.DLL (DataWindow 引擎) (可选)
    PBRTC60.DLL (Rich Text Control)(可选)
    PBTRA60.DLL (DLL used for tracing db calls)(可选)
    2与所需连接的数据库的直连接口(Native Driver) 或 ODBC接口(ODBC Driver)：
    PBSYC60.DLL (连接Sybase 的直连接口)
    PBO7360.DLL (连接Oracle7.3的直连接口)
    3所需连接的数据库的Client端(如Sybase的Open Client, Oracle 的SQL Net)
    以上是Sybase公司的建议。
    我的经验是，编译好的程序必须有pbdwe60.dll和pbvm60.dll才行。如果使用了数据管道、超文本控件，还要加pbrtc60.dll。另外，如果使用ODBC连接数据库则还需pbodb60.dll，如果使用专用接口，则必须对应数据库的DLL，如Oracle7.3用PBO7360.DLL，Sybase用PBSYC60.DLL。这些文件必须在你的PB应用程序可搜索的路径内（如Window\System、应用程序目录下等），PB可执行文件才能正常运行。（此题由yiyi回答）
    问题三：怎样检测数据窗口中数据是否被修改，以避免用户不小心关闭窗口而导致数据窗口中大量修改数据未保存而丢失？
    答：在窗口的closequery事件加入如下代码即可：
    if dw_1.deletedcount() ＋ dw_1.modifiedcount() > 0 then
    integer li_ret
    li_ret = messagebox(″注意″,″数据已经被修改，是否存盘？″,Question!,YesNoCancel!)
    choose case li_ret
    case 1
    if dw_1.update() = 1 then
    commit;
    return 0
    else
    rollback;
    messagebox(″出错″,″存盘时出错！请检查数据的正确性。″)
    return 1
    end if
    case 2
    return 0
    case 3
    return 1
    end choose
    end if
    （此题由ferryman回答）
问题四：各位大侠，怎么在数据窗口中用箭头键实现“Shift＋Tab”按键的功能？我想在PB中获得一个像Foxpro那样的编辑环境，即按左箭头键光标便移动到当前列的左边一列，按右箭头键光标便移动到右边一列?
    答：方法一（推荐）：
    声明函数：
    subroutine keybd_event(uint bVk,uint bScan,long dwFlags,long dwExtraInfo ) library ′user32.dll′
    定义用户事件ue_keydown：pbm_dwnkey。
    在事件中编程：
    integer VK_TAB = 09
    integer VK_SHIFT = 16
    if key = KeyLeftArrow! then
     keybd_event(VK_SHIFT,0,0,0) //按下shift
     keybd_event(VK_TAB,0,0,0) //按下tab
     keybd_event(VK_TAB,0,2,0) //释放tab
     keybd_event(VK_SHIFT,0,2,0) //释放shift
     return 1
    end if
    if key = KeyRightArrow! then
     keybd_event(VK_TAB,0,0,0) //按下tab
     keybd_event(VK_TAB,0,2,0) //释放tab
     return 1
    end if
    （此题由ferryman回答）
    方法二（比较原始，但也能实现）：
    声明函数：
    function boolean GetKeyboardState (ref char kbarray[256]) library ″user32.dll″
    function boolean SetKeyboardState (ref char kbarray[256]) library ″user32.dll″
    脚本：用户事件ue_keydown：pbm_dwnkey
    char lc_kb[256]
    if key = KeyLeftArrow! then
     GetKeyboardState (lc_kb)
     lc_kb[17] = Char (128)
     SetKeyboardState (lc_kb)
     Send (Handle (this), 256, 9, 0)
     GetKeyboardState (lc_kb)
     lc_kb[17] = Char (0)
     SetKeyboardState (lc_kb)
    end if
问题五：动态切换数据窗口对象编译后运行为何出错？如在数据窗口dw_1中用 dw_1.dataobject=″dw_sj″来改变对象，在PB7环境下运行一切OK。但当创建工程和应用程序后，运行该应用程序（.EXE文件）时，数据窗口dw_1的数据对象dw_sj不能出现。
    答：当程序中用到了动态加入的对象，如BMP资源文件、数据窗口对象（dw_1.dataobject=″dw_sj″，这里″dw_sj″即为动态加入），PB编译时是扫描不到的，有两种方法解决：
    1．将此对象写入到资源文件中：
    用记事本创建资源文件dw_object.pbr
    写入：c:\myprogram.pbl(dw_sj)
    编译时将此文件选入Resource File Name处。
    2．将应用编译成PBD、DLL文件。
    问题六：请问PB编译后的程序可否带参数运行，就像DOS中的dir命令：dir/w的形式，如果可以，PB中怎样接收呢？
    答：PB中带参数运行应用程序一般都是用在Application 的 Open 事件里访问 CommandLine参数的办法，根据参数CommandLine 的不同做不同的操作。如程序myapp ，运行：myapp hello。hello就是命令行参数。


PowerBuilder编程俱乐部

    问题一：各位高手请进，不要嫌问题简单！请问用PB如何调用WINDOWS的API函数，即具体要在哪 几个地方写些代码？最好能给出实例。
答：调用 API 函数步骤：
1、在适当的位置声明函数，如窗口内，Application内，UserObject内，定义在Local External Function或Global External Function中，如播放声音的：
Function boolean sndPlaySoundA (string SoundName, uint Flags) Library "WINMM.DLL"
Function uint waveOutGetNumDevs () Library "WINMM.DLL"
也可以创建一个UserObject,集中声明常用的API及函数本地化，如定义用户对象 u_external_function：
Declare Local External Function（定义外部函数）:
Function boolean sndPlaySoundA (string SoundName, uint Flags) Library "WINMM.DLL"
Function uint waveOutGetNumDevs () Library "WINMM.DLL"
Declare User Object Function（定义用户对象函数）:
uf_play_sound(string as_wave_name, integer ai_option)
函数内容如下：
//参数：as_wave_name ：wav文件名
// ai_option ：同步或异步(1/0)
uint lui_numdevs
lui_numdevs = WaveOutGetNumDevs()
If lui_numdevs > 0 Then
sndPlaySoundA(as_wave_name,ai_option)
return 1
Else
return -1
End If
2、调用时在程序中定义一个实体并调用其函数：
u_external_function iu_external_function
iu_external_function = create u_external_function
iu_external_function.uf_play_sound('c:\windows\media\ding.wav',1)
试试看，如果有声卡，就会听到“叮”的一声。
其它API函数也是如此处理。关于如何声明，在俱乐部的主页上有一个帮助，在我的主页PB手记http://liulee.yeah.net 的“源码天地”下也有一个集成了常用API函数的用户对象，你可以下载回去参考。
（此题由流方回答）
    问题二：如何在PB应用程序中创建目录？
    答：可以用API函数CreateDirectoryA实现，定义:
    function boolean CreateDirectoryA(ref string pathname,int sa) Library "Kernel32.dll"
    调用代码:
        boolean rtn
        string ls_dir
        ls_dir = "c:\newdir" //要创建的目录名称
        rtn = CreateDirectoryA(ls_dir,0)
        if rtn then
                Messagebox("Succeed","Succeed!" )
        else
                Messagebox("Failed","Failed!")
        End if
    问题三：我写的系统中带了OCX控件，当系统编译后发行时必须带上该OCX控件，但在用户的机子上必须注册该OCX控件才能使用，否则程序运行会出错，请问用什么方法注册该OCX控件？
    答：提供以下几种方法供参考：
    1、在Windows命令行或DOS下运行：regsvr32 ocxname.OCX
    2、用Install Shield 做安装程序时，在里面选“Self-Registered”，安装程序将自动注册。
    3、在PB程序中调用regsvr32实现，安装程序只需负责将OCX拷贝到相应路径下，这个路径PB程序必须可以得到，建议拷贝到Windows系统目录或者与执行文件同目录下。
    具体代码：
    //首先定义API函数，如果OCX控件在与执行文件同目录下，使用GetCurrentDirectory函数；如果OCX控件放在系统目录下，使用GetSystemDirectory函数。
    Function ulong GetCurrentDirectory(ulong nBufferLength,ref string lpBuffer) Library "kernel32.dll" Alias for "GetCurrentDirectoryA"
    Function uint GetSystemDirectory(ref string lpBuffer,uint uSize) Library "kernel32.dll" Alias for "GetSystemDirectoryA"
    //假设OCX文件在Windows系统目录下，在PB应用（Application）的Open事件编写如下代码：
    string ls_PathName,ls_Command
    ulong ll_RequiredBufferSize
    ls_PathName = Space(255)
    ll_RequiredBufferSize = GetSystemDirectory(ls_PathName,255)
    IF ll_RequiredBufferSize=0 or ll_RequiredBufferSize>255 THEN
    //报错信息
    else
        ls_Command = "Regsvr32 /s " + "~"" + ls_PathName + "\Myocx.ocx" + "~""
        Run (ls_Command)
        …
    End if
    Regsvr32后面的"/s"执行参数是为了隐藏注册成功消息框。
    （此种方法由张涛提供）
    4、在PB应用程序中注册：
    函数声明：
    Function long DllRegisterServer() Library "ocxname.OCX"
    调用：
    LONG ll_RC
    ll_RC = DllRegisterServer()
问题四：如何带参数执行SQL的系统储存过程？我想在script中执行系统存储过程Sp_DropUser删除MS SQL Server 7某用户。我是这样写的：execute Sp_DropUser(Sp_DropUser语法)，编译提示Sp_DropUser为未定义的系统存储过程。
答：代码如下：
string ls_name
ls_name = 'username' //要删除的用户名
//定义存储过程
DECLARE dropUser_proc PROCEDURE FOR Sp_DropUser
@name_in_db =:ls_name;
SQLca.AutoCommit = TRUE
EXECUTE dropUser_proc ; //执行
SQLca.AutoCommit = false


PowerBuilder编程俱乐部

    问题一：请问怎样在PB中实现文件的拷贝与删除，要借助外部函数吗？谢谢！
    答：可用API函数实现：
    1、文件拷贝
    函数声明：
    FUNCTION boolean CopyFileA(ref string cfrom, ref string cto, boolean flag) LIBRARY "Kernel32.dll"
    调用：
    string ls_source, ls_des
    boolean lb_flag, lb_rtn

    lb_flag = false
    ls_source = "c:\book.xls" //源文件
    ls_des = "C:\tmp\111.xls" //目标文件
    lb_rtn = CopyFileA(ls_source, ls_des, lb_flag)
    MessageBox("文件拷贝", string(lb_rtn))
    2、删除文件
    函数声明：
    FUNCTION boolean DeleteFileA(ref string filename) LIBRARY "Kernel32.dll"
    调用：
    string ls_file
    boolean lb_rtn
    ls_file = "C:\tmp\111.xls"
    lb_rtn = DeleteFileA(ls_file)
    MessageBox("文件删除", string(lb_rtn))
    或者用PB本身的函数FileDelete()来删除文件。
    问题二：我用的是SQL ANYWHERE 5.0建库的，请问实现数据备份和恢复时的具体步骤和实现方法是怎样的？
    答：备份可以用SQL ANYWHERE 的dbbackup命令来完成。安装sql anywhere 5 后，在sql anywhere5.0\win32\ 目录中可以找到dbbackup.exe。再在Ms-dos方式下用命令dbbackup /? 或者在sql anywhere中的帮助下查看其命令开关。如想将C:\hello目录下的数据库文件hello.db备份到d:\backup目录下，其用法如下:
    dbbackup -c "uid=dba;pwd=sql; dbf=c:\hello\hello.db" d:\backup
    在pb脚本中用run函数运行：
    run("dbbackup -c ~"uid=dba;pwd=sql; dbf=c:\hello\hello.db~" d:\backup")。
    恢复时将备份的文件考回即可。
    详细用法可以查帮助。
    （此题由lysoftware、yiyi提供答案）
    问题三：如何在PB中实现串口编程？
    答：可以利用微软公司提供的通讯控件mscomm32.ocx来实现，使用方法如下：选择菜单Controls→OLE→Insert Control→Microsoft Communications Control，加入后我们点击其OLE Control Properties，可以设置它的基本属性如通讯端口(C)、设置参数 (S) 等，当然我们也可以在程序中动态设置。以下是一段脚本例子：
    String ls_data
    ole_1.object.CommPort = 1 //使用COM1端口
    ole_1.object.Settings = "9600,N,8,1" //设置或返回串行端的波特率、奇偶校验位、 数据位数、停止位
    ole_1.object.InputLen = 0 //初始化Input
    ole_1.object.PortOpen = True //打开端口
    ole_1.Object.Output = "h" //向COM口发送字符

    Do
         Yield() //等待数据
    LOOP Until ole_1.Object.InBufferCount >= 1
    ls_Output = ole_1.Object.Input //取数据

    ole_1.object.PortOpen = FALSE //关闭端口

    （此题由3w提供答案）
问题四：我想在PB程序中启动控制面板中的'时间/日期'程序，哪位高手知道？
    答：run ("rundll32.exe shell32.dll , Control_RunDLL Timedate.cpl")
    其它的程序就是最后所带的参数（Timedate.cpl）不一样，如启动ODBC数据源管理器：
    run ("rundll32.exe shell32.dll , Control_RunDLL Odbccp32.cpl")
问题五：最近我在工作中遇到了以下问题：我的报表是动态生成的，现在希望随意修改各个带区的宽度、位置； 希望随意在报表中增加文本（标题），或对标题栏的文本进行修改；希望能够在报表中划线，改变报表的格式等。希望能为我解答，非常感谢！
    答：PB动态报表其实就是动态数据窗口，我个人认为动态数据窗口的难处在参数的设定，要做到各项都可随意设定。刚开始做时可以手工设置datawindow对象，再在程序中用dw_1.object.datawindow.syntax将其结构倒出来分析，以便有个参考。对于数据窗口中各项的更改可以用Modify()函数来实现，举几个例子：
    1、改变detail区的高度：dw_1.Modify("DataWindow.detail.Height=200")
    2、对标题栏文本的更改(文本名为dept_name_t)：dw_1.modify("dept_name_t.text = '单位名称'")
    3、增加计算域：假设一grid型的数据窗口，其中有个字段为部门号dept_id，我们要增加一个统计部门数的计算域count(dept_id for all)：
    string ls_modrow
    dw_1.Modify("DataWindow.summary.Height=64")
    ls_modrow = 'Create compute(band=summary font.charset="0" font.face="MS Sans Serif" font.family="2" font.height="-8" font.pitch="2" font.weight="400" background.mode="1" background.color="536870912" color="0" x="9" y="4" height="52" width="297" format="[general]" expression="count(dept_id for all)" alignment="1" border="0" crosstab.repeat=no )~r~n'
    dw_1.modify( ls_modrow )
    4、以下程序段让grid数据窗口中网格线不见，并在detail区增加加一条线
    string ls_line
    DW_1.Modify("DataWindow.Grid.Lines='1' ")
    dw_1.Modify("DataWindow.detail.Height=332")
    ls_line = 'Create line(band=detail background.mode="2" background.color="16777215" pen.style="0" pen.width="5" pen.color="0" x1="37" y1="320" x2="1458" y2="316" )~r~n'
    dw_1.modify( ls_line )
    象这种数据窗口动态增加控件的方法，也可以先在数据窗口画板中手工加上去，倒成sytnax后，查看其语法，再反写回去即可。


PowerBuilder编程俱乐部

        问题一、如何在PB中实现精确的时间控制，如使用延时控制？timer函数只能使用一次，idle函数只能是在没有任何键盘和鼠标动作的时候才生效，还有其他函数可以用吗？
答：用API函数Sleep()可以解决，此函数用于通知操作系统忽略当前进程指定时间数，参数为时间（千分之一秒）。
声明函数：
SUBROUTINE Sleep(uint cMilliSeconds) LIBRARY "KERNEL32.dll"
调用： Sleep(2000)//延时2秒
PB8中新增了对应的函数：Sleep ( seconds )。
    问题二、PB中怎样实现在循环时可以通过点击按钮终止循环? 就像在VB的循环中用DoEvent实现的一样?
答：这当然要用Yield()函数了。如：
integer n
//sb_interrupt 是共享变量
sb_interrupt=false
for n=1 to 3000
yield()
if sb_interrupt then
//sb_interrupt的值在“取消”按纽的Clicked事件中修改为true
MessageBox("我不干了","你真坏！")
sb_interrupt=false
exit
else
//其它处理，在单行编辑器中显示当前n值
sle_1.text = string(n)
//....
end if
next
    问题三：如何关闭打开的dos窗口？ 如在PB中调用run(*.bat)
函数，该函数运行完毕后有一个打开的dos窗口，怎样在程序中关闭？
答：定义API：
Function ulong FindWindowA (String lpClassName , String lpWindowName) Library "user32.dll"
Function ulong CloseWindow (ulong hwnd ) Library "user32.dll"
脚本：
long ll_Handler
string ls_Class
string ls_title
SetNull(ls_Class)
ls_title = "ms-dos 方式" //DOS窗口标题，根据具体情况改
ll_Handler = FindWindowA(ls_Class, ls_title )
If ll_Handler <> 0 Then
Send(ll_Handler, 16,0, 0)
End If
如用CloseWindow(ll_Handler) 替换Send(ll_Handler, 16,0, 0) 一句则是将指定标题窗口最小化。
以上方法同样适用于其它类型窗口。以前有网友问过用API函数DestroyWindow，DestroyWindow只适用于关闭PB
本身的窗口（对象），不适用于外部窗口，如在一个button的clicked事件中加上：
DestroyWindow(handle(parent))则关闭button所在窗口。DestroyWindow的声明格式是：
FUNCTION ulong DestroyWindow(ulong hwnd) LIBRARY "user32.dll"
问题四：数据窗口的多表更新问题：我的数据窗口数据源来自两个表，当修改该数据窗口数据项时，
怎样做到两个table都能更新？
答：数据窗口数据源来自两个或两个以上的表，相当于多个表连接建立的一个视图，对于这种数据窗口，PB默
认是不能修改的。当然我们可以通过设置它的Update 属性，数据窗口的Update Properties用来设置数据
窗口是否可Update、可Update的表、可Update列等，但不能同时设置两个表可更新；所以当修改它的数据
项时，不能简单地用dw_1.update()来更新table，我们可以在程序中设置数据窗口可更新的一个表A（及其
可更新列），其他表为不可更新，更新完表A后，再设置另一表B为可更新，表A设置为不可更新，依次
类推。
以PowerBuilder7自带的EAS Demo DB V3数据库为例：
1、新建一个数据窗口d_grid_dep_emp，它的Select语句为
SELECT department.dept_id,
department.dept_name,
employee.emp_id,
employee.emp_fname,
employee.emp_lname
FROM department, employee
WHERE employee.dept_id = department.dept_id
2、设置数据窗口d_grid_dep_emp的属性，将列的taborder改为非0值；并点击菜单Rows——>Update
Properties，设置此数据窗口Allow Updates，Table to Update设为department，Updateable Columns为
department.dept_id，department.dept_name。
3、在窗口中更新数据窗口按钮的clicked事件编写脚本：
long ll_rtn
// 修改Department表(Department表在第2步已设置为可更新)
ll_rtn = dw_1.update(true, false)
If ll_rtn = 1 then
//关闭对Department表的修改
dw_1.Modify("department_dept_name.Update = 'No'")
dw_1.Modify("department_dept_id.Update = 'No'")
dw_1.Modify("department_dept_id.Key = 'No'")
//设置Employee表成为新的可修改表
dw_1.Modify("DataWindow.Table.UpdateTable = 'employee'")
dw_1.Modify("employee_emp_id.Update = 'Yes'")
dw_1.Modify("employee_emp_fname.Update = 'Yes'")
dw_1.Modify("employee_emp_lname.Update = 'Yes'")
dw_1.Modify("employee_emp_id.Key = 'Yes'")
//修改Employee表
ll_rtn = dw_1.Update()
IF ll_rtn = 1 THEN
COMMIT USING SQLCA;
dw_1.retrieve()
messagebox('提示信息','更新成功！')
ELSE
ROLLBACK USING SQLCA;
MessageBox('提示信息', '更新失败！')
END IF
//重置修改标志
dw_1.Modify("department_dept_name.Update = 'Yes'")
dw_1.Modify("department_dept_id.Update = 'Yes'")
dw_1.Modify("department_dept_id.Key = 'Yes'")
dw_1.Modify("DataWindow.Table.UpdateTable = 'department'")
dw_1.Modify("employee_emp_id.Update = 'No'")
dw_1.Modify("employee_emp_fname.Update = 'No'")
dw_1.Modify("employee_emp_lname.Update = 'No'")
dw_1.Modify("employee_emp_id.Key = 'No'")
ELSE
ROLLBACK USING SQLCA;
MessageBox('提示信息', '更新失败！')
END IF
我们还可以将以上功能作成一个函数，在必要的时候调用即可。
问题五：如何在非PB窗口中实现键盘按键和鼠标点击动作的检测？如很多屏幕捕捉软件，都可以在非本软件窗口
中实现热键响应事件。
答：首先调用api函数RegisterHotKey为窗口注册热键。当热键按下后，windows发送wm_hotkey消息给窗口，这
时我们可以在PB窗口的other事件中处理。
定义窗口实例变量：
long atomid
constant integer MOD_ALT = 1 //ALT键
constant integer MOD_CONTROL = 2 //CTRL键
constant integer MOD_SHIFT = 4 //SHIFT键
定义API函数：
FUNCTION Integer GlobalAddAtom(ref string lpString) LIBRARY "kernel32.dll" ALIAS FOR "GlobalAddAtomA"
FUNCTION ulong RegisterHotKey(ulong hwnd,ulong id,ulong fsModifiers,ulong vk) LIBRARY "user32.dll"
在窗口的open事件编写脚本：
//注册热键
long ll_RC
string ls_atomid
ls_atomid = 'myatomid'
atomid = GlobalAddAtom(ls_atomid)
//得到惟一的ID，保证不和其他应用程序发生冲突
ll_RC = RegisterHotKey(Handle(this), atomid, MOD_CONTROL , 70)
//注册的热键为Ctrl+F ，70为'F'
//如果组合键，可用MOD_CONTROL + MOD_ALT等
if ll_RC = 0 then messagebox("Error!","热键注册错误")
//热键注册冲突
在窗口的other事件编写脚本：
//处理系统热键
IF wparam = atomid THEN
//处理
messagebox('hello','你好！')
END IF