跟我学用VB编程

Author: 杜国梁 Date: 1997-01-01

#3  Visual Basic属于第四代编程语言,它提供了一个易学易用的应用程序集成开发环境,在这个环境中,用户可设计界面,编写代码,调试程序,最后可把应用程序编译成可执行文件,脱离Visual Basic环境而能在Windows中独立运行。
  Microsoft Visual Basic目前最高版本为4·0,但广为流行的是3·0版。本讲座主要针对3·0版(但程序可在4·0版上运行),向读者介绍一些使用VB编写基本应用程序的知识与技巧。而有关本软件的具体使用方法则不再介绍,读者可参考有关书籍。
  本讲座分五讲:1·VB应用程序界面设计;2·有关文件操作的程序设计;3·图形编程初步;4·一个实用的VB应用程序;5·进一步学习VB编程的建议。
#1  第一讲  VB应用程序界面设计 
  应用程序的界面是应用程序的外观,用户提供应用程序首先就与界面打交道,界面的好坏直接影响了它的推广与应用。下面向读者介绍一种受欢迎的界面元素——对象拖放操作的设计方法,读者可从中举一反三。
#2  本讲涉及的概念:
#3  控件:即控制部件,这是和用户交互的标准部件,命令按钮、文本框、列表框、滚动条、菜单等都属此类。
#3  事件:就是系统可以感知的用户操作信息。
#2  对象的拖放操作的设计
  在Windows和Windows 95中都可用鼠标拖动对象执行某种功能。例如,可将一个应用程序从一个程序组中拖到另一个程序组中,在文件管理器中,可将文件从一个位置拖动到另一个程序组中,在文件管理器中,可将文件从一个位置拖到另一个位置,从而实现文件的复制与移动。这种界面比使用命令行更易于使用。因此如果用户的应用程序有这样的界面无疑会更加吸引人。现以一个查看图形文件的应用程序为例,来说明拖对象操作的一般设计方法。在本例中,从文件列表中将图形文件拖到图片框中,从而在图片框中显示该图形,这样就可以快速查看图形文件了。
  Visual Basic为控件提供了被拖动的潜在功能。一个控件能否被鼠标拖动,取决于控件的DragMode属性值的设置。当将该属性设为1时,则该控件可被鼠标拖动,同时该控件不再接收Click和MouseDown事件,这称为自动拖动。而为0时,要拖动控件,必须使用该控件的Drag方法,即在程序代码中必须有如下语句:对象名.Drag,这称为手工拖动。不管是自动拖动还是使用Drag方法进行手工拖动,只有当编写了用Move方法使控件重新定位的代码时,控件才能产生实际的移动。否则,尽管控件可被拖动,但当释放鼠标按钮时,控件就又回到原处,与未拖动无异。
  一个对象被拖动然后放下时,在松开鼠标时鼠标指针所指的对象上(不是被拖动的对象)会接收到DragDrop事件,而拖动过程中,被拖动对象经过的对象都会接收到DragOver事件。这样就可以在这两个事件过程中编写代码,实现一定的功能。
  在对象被拖动时,对象的外形变为一个灰线的轮廓。可以利用DragIcon(有DragMode属性的对象,就有DragIcon属性)改变对象被拖动时的形象。这一属性既可在设计阶段利用属性窗口,给该属性指定一个图标文件名(·ICO文件),也可在运行时利用LoadPictrue()函数来装入图标文件。
#2  示例:以拖动方式显示图形文件的图框
  在本例中利用鼠标的拖放操作实现图形文件中的图形的显示。用户从文件列表中拖动一个图形文件并在图片框中将其放下,于是在图片框中显示该图形文件的图形。由于文件列表框中的文件名不能被拖动,在本例中使用一个标签控件代替被拖动的文件,被拖动的实际上是标签控件而不是文件。本例使用的是手工拖动。
  设计好的窗口如附图所示。窗体的左上是驱动器列表框、目录列表框和文件列表框,右面是一个图片框,用来显示图形,一个命令按钮用于退出运行,右上角还有一个标签控件,用做为被拖动的文件的替身。此外还有一些标签,用来显示控件名称,如“驱动器:”、“目录:”等窗体上的文字说明。
  本例的程序代码如下:
  窗口上的命令按钮用于退出,所以只需一句代码。
  Sub Command1-Click()
  End
  End Sub
  与文件有关的控件,文件列表框、目录列表框和驱动器列表框需要联合动作,其中的代码已程式化。
  Sub Dir1-Change()   `在目录列表框Dir1中的任何变化都反映在File1中
  File1.Path=dir1.Path
  lblDir.Caption=“目录:”
  End Sub
  Sub Drive1-Change()   `驱动器列表框,与目录表框协同工作
  dir1.Path=Drive1.drive
  End Sub
  在以下的所有Drag Over事件过程中,被拖动的文件经过一个控件时,光标改变形状,给出一个被拖动的图景。
  Sub Dir1-DragOver(Source As Control,X As Single,Y As Single,State As Integer)
  if State=0 Then Source.MousePointer=12   `被拖动的控件正处于本控件之上
  if State=1 Then Source.MousePointer=0   `离开本控件
  End Sub
  Sub Drive1-DragOver(Source As Control,X As Single,Y As Single,State As Integer)
  if State=0 Then Source.MousePointer=12   `被拖动的控件正处于本控件之上
  if State=1 Then Source.MousePointer=0   `离开本控件
  End Sub
  在以下的File1-DragOver过程中,若被拖动的是图标文件,则显示图标作为lblDrag标签的DragIcon属性,以便看出被拖动的是一个什么图标。在利用file1.Path定位文件时,要注意,与后面的文件名接合时,应该插入一个反斜杠。
  这里要说明的是,被拖动的实际上并不是一个文件名,只有控件可被拖动,控件内部的元素是不能单独被拖动的。这里使用了一个标签作为文件名的替身。如果按下鼠标时,选择的是一个图标文件,则将该图标设置为标签lblDrag的DragIcon属性。下面的过程中,Right$()函数找出File1.Name属性中的最后四个字符是否为“.ico”,若是,则是图标文件,于是将此图标设置为标签lblDrag的DragIcon的属性。
  Sub File1-DragOver (Source As Control,X As Single,Y As Single,State As Integer)
  On Error Resume Next
  if State=0 And Right$(File1.FileName,4)=".ico" Then
  lblDrag.DragIcon=LoadPicture(File1.Path+"\"+File1.FileName)
  if Err Then MsgBox"不能装入此图标"
  Elseif State=1 Then
  lblDrag.DragIcon=LoadPicture()   `使用MousePointer=12时的图标
  End if
  End Sub
  当鼠标按下选择文件时,引发File1-MouseDown事件过程。利用此事件过程启动签lblDrag的手工拖动,同时利用Move方法,移动此标签的矩形框,作为被选择文件的替身,就好象是文件被拖动一样,实际被拖动的却是一个标签控件。
  Sub File-MouseDown(Button As Integer,Shift As Integer,X As Single,Y As Single)
  Dim DY   `声明变量
  DY=TextHeight("A")   `利用TextHeight方法得到文本行的高度
  lblDrag.MoveFile1.Left,File1.Top+Y-DY/2,File1.Width,DY
  lblDrag.Drag   `使标签lblDrag可被拖动(启动手工拖动)
  End Sub
  Sub Form-DragOver(Source As Control,X As Single,Y As Single,State As Integer)if State=0 Then Source,MousePointer=12
  `State=0表示被拖动对象在本控件上,改变鼠标形状。12代表
  if State=1 Then Source.MousePointer=0
  `State=1表示被拖动控件已离开了本控件,鼠标恢复原来的缺少开头(缺少形状)
  End Sub
  Sub Form-Load()
  Picture1.AutoSize=-1   `使图片框随着其中的图形改变大小
  lblDrag.Visible=0   `使lblDrag标签可见
  File1.Pattern="*.bmp;*.ICO;*.WMF"
  '设置文件列表框中显示的文件类型为图形文件
  End Sub
  当被拖动对象(标签lblDrag)在图片框上被释放时,引发Picture1-DragDrop过程,将所选图形装入图片框,这就好象是图形文件被拖动到图片框一样。
  Sub Picture1-DragDrop(Source As Control,X As Single,Y As Single)
  On Error Resume Next
  Picture1.Picture=LoadPicrure(File1.Path+"\:+File1.FileName)
  if Err Then MsgBox“不能装入指定的图形。”
  End Sub
  这里有一个练习,试编制一个通用过程,在装入一个窗体时能使该窗体居中。有兴趣的读者不妨试试.
#1  第二讲  VB中的文件操作
  输入/输出是任何程序设计语言都要处理的问题,VB也不例外。VB不仅从先辈处继承了执行操作系统处理文件的语句和函数,还增加了创建新文件和打开已有文件进行读写的语句和函数。下面,我们就从用Line Input #和Print #语句编写读写顺序文件的示例来领会VB对文件的操作。
  窗体上有一个文本框在上部,三个命令按钮在下部。文本框的MultiLine属性设置为True,ScrollBar属性设置为3(水平与竖直滚动条都有)。命令按钮的Name属性分别为Command1、Command2和Command3。Command1用于打开文件,利用Line Input #语句来读文本文件。读取的内容放入文本框中,这样用户还可修改它。如果文本框中的内容被修改过,则Text1_Change() 过程即被调用。在这个事件过程里,可以将一个窗体级的变量(用做文件内容被修改的标记)由False置为True,只有当此变量为True时,才向文件中写入数据。另外若没有找到指定的文件时,也不能向文件中写入数据,这由窗体级的变量FileFound来标记。
  当用户单击Command1按钮时,首先弹出一个输入框,要求输入一个文本文件名(带全路径),输入确认后,则可在文本框中显示出所选文件的内容。读文件使用了Line Input语句。用Do...Loop循环每次读入一行,以EOF函数来检查文件是否结束从而控制循环的执行。程序如下:
  Sub Command1_Click ()
  Dim Drive, Msg, NL, TextLine  `声明变量,下面用到的filename是窗体级变量
  NL = Chr(13) & Chr(10)  `定义回车换行符
  Msg = "你想查看哪个文件?请给出全路径来。"
  filename = UCase(InputBox(Msg))   `获得用户输入并转换为大写  
  If Dir(filename) <> "" Then  `用Dir函数检测文件是否存在
  FileFound = True  
  Open filename For Input As #1 `文件若存在则打开它
  Do While Not EOF(1)
  Line Input #1, TextLine   `读入一行,下面Text1是文本框
  text1.Text = text1.Text & TextLine & NL  `读取的内容放入文本框的Text属性中
  Loop
  REM   text1.Text = Input(LOF(1), 1)  
  Close #1   `关闭文件  
  Else   `指定文件不存在  
  Msg = "文件不存在。"  
  MsgBox Msg   `显示信息  
  End If
  End Sub
  在这个程序段中,请注意REM开头的一行,用这行可代表Do...Loop循环的功能,用一条Input函数调用就可将整个指定的文件读入文本框中,而且比用循环快得多。当然如果使用了这行,应将原循环中的各条语句注释掉。
  下面是其余的代码:
  Dim TextChanged As Integer, FileFound As Integer `放在窗体的Declaration部分
  Dim filename As String  `窗体级变量用于保存文件名
  Form_Load()  
  TextChanged = False   `变量初始化处理  
  FileFound = False
  End Sub
  Text1_Change()  
  TextChanged = True   `当文件内容被修改,自动调用这一过程
  End Sub
  Command2_Click()   `当用户单击“保存文件”按钮时调用  
  If Not (FileFound And TextChanged) Then  `如果文件不存在或没有变化
  Beep   `发声报警
  Exit Sub  `退出本过程  
  Else   `文件存在并发生变化
  Open filename For Output As #1  `打开文件准备写入
  Print #1 Text1.text  `向 #1文件写入
  Close #1  `关闭该文件  
  End If
  End Sub
  Command3_Click()   `退出按钮  
  End
  End Sub
  按F5可运行程序,前面的图是指定显示Word6.ini(Word 6.0 的初始化文件)时的运行结果。由于文本框内建有基本的编辑功能,故不需要为编辑文件而编写代码,只要利用内建的编辑功能即可。当然,文本框内建的编辑功能十分有限,这只是示例,如果读者需要更为强大的功能,必须为编辑文件编写代码(调用Windows API函数)。另外文本框中可以装得下的字符个数是32K,文件如大于32K,用这一方法就无法显示了。
#2  本讲练习题
  如何利用VB中的内部函数替换一个给定字符串的某几个字符?
#2  上讲练习参考答案
  在装入窗体时调用下面的自定义过程,即可使装入的窗体在屏幕上居中:
  Sub centerForm (x As Form)  `x代表要居中的窗体变量
  Screen.MousePointer = 11  `鼠标为沙漏状
  x.Top = Screen.Height / 2 - x.Height / 2
  x.Left = Screen.Width / 2 - x.Width / 2
  Screen.MousePointer = 0   `鼠标恢复标准状态
  End Sub
  例如下面代码可使Form1居中显示:
  Load Form1  `装入Form1,但并不显示出来
  CenterForm Form1 `使Form1居中
  Form1.Show  `使Form1显示出来  
#1  第三讲  VB中的图形编程
  图形是Windows的关键部件,没有图形就没有Windows。VB提供了丰富的图形功能,窗体和图片框都可以成为程序员的“图板”,在其上可“写字”、“作画”。图形除与窗体、图片框对象有关外,VB的图形方法还可以用于打印机对象。在软件设计中适当使用VB中的图形方法,会使软件界面大大改观,增强软件界面的友好性和软件的吸引力。这里给出一个利用图形方法修饰软件封面窗体的实例。
#2  示例:软件封面
  软件封面的特点是在屏幕固定位置上显示一个有关应用程序的名称、作者以及版权信息等。它不需要在标题栏中显示标题,窗口尺寸不需伸缩,也不需要系统控制菜单。如有可能还可以利用三维外观加强用户的印象。如果读者使用过Word 6.0中文版,你一定会对它的启动封面有极深的印象。在Visual Basic 3.0专业版中有三维控件,利用它们可以创建具有三维外观的控件对象。但是对于窗体,还不能直接显示为三维外观。利用VB中的Line方法,通过在窗体的边界上画线,可以制造出三维外观。在以下的例子中,我们就来建立一个能够产生类似Word 6.0中文版软件封面的演示程序。本例中的封面窗体如图。
  软件封面是用一个窗体做成的。在系统装入主窗口的过程中首先显示封面窗体(其Name属性设为frmCover),且封面在装入主窗口时一直显示在上面。待主窗口装入完毕,再将封面窗体关闭。具有软件封面特性的窗体,应将其ControlBox属性设为False(0),这样窗体上不显示系统控件菜单框,还要将MaxButton和MinButton属性也设为False,使其不显示最大化按钮和最小化按钮。而将其BorderStyle属性设为1(固定的单线边界)。这里要特别指出的是Caption属性,即使按以上设定,若Caption属性不为“空”(连空格都没有的空字符串),则窗体仍要显示标题栏。例如Caption=“ ”(一个空格符),这时会显示一个带标题栏的窗体,但标题栏中没有任何字符。因而在用属性窗口设置Caption属性时,一定要将设置框中的所有内容清除,包括空格,使光标停在设置框的最左边。
  封面窗体上放置两个控件:一个是Panel3d控件(Panel3d1,三维面板),用来显示软件标题,另一个是标签控件(Label1),用来显示提示信息。控件的属性按上图设计。为了得到三维面板的三维外观,应将Bevellnner属性设置为1(凹陷),Bevelouter设置为2(凸起),BevelWidth和BorderWidth均设置为1(象素)。
  为使封面具有三维凸起的外观,在frmCover(作为封面显示的窗体)的Form_Load()事件过程中,利用画线的图形方法—Line来画出封面的外框,上和左边界用浅灰色线画出,而下和右边界用黑色线画出,就有凸起的感觉,反过来则有凹陷下去的感觉。为使封面显示时处于屏幕的中心,在Form_Load()中重置了封面窗体的Left和Top属性。请参见程序中的注释。以下是frmCover的窗体模块代码。打开此窗体的代码窗口,在Declaration部分,输入以下代码:
  Const DARKGRAY=64   `深色的颜色值
  Const LIGHTGRAY=255 `浅灰色的颜色值
  在Form_Load()事件过程中输入以下代码:
  Sub Form_Load () `frmCover窗体
  Dim TLshade As Long, BRshade As Long
  Dim framwidth As Integer
  Dim i As Integer, T As Integer, L As Integer
  Dim W As Integer, H As Integer
  frmCover.Left=(screen.Width - Width) / 2 
  `左右置中   
  frmCover.Top=(screen.Height - Height) / 2
  `上下置中   
  TLshade&=RGB(lightgray, lightgray, lightgray)
  `左上角的近似于白色的浅灰色
  BRshade&=RGB(darkgray, darkgray, darkgray)
  `右下角的黑色   
  frmCover.ScaleMode=3: framwidth=4   
  `画的边框宽度为4个象素
  frmCover.AutoRedraw=True `本属性为True时
  才能画线   
  For i=1 To framwidth         `以下循环画出作为                        
  软件封面窗体的三维外观   
  T=i
  L=i
  H=ScaleHeight
  W=ScaleWidth
  Line (L, T)-(L, H - i), TLshade                          
  `画左边界的亮色外框
  Line (L, T)-(W - i, T), TLshade                          
  `画上边界的亮色外框
  Line (W - L, T)-(W - L, H - T), BRshade                          
  `画右边界的黑色阴影
  Line (L, H - L)-(W - L, H - T), BRshade                          
  `画下边界的黑色阴影
  Next i
  End Sub
  本例中除了封面窗体之外,还应有一个主窗体,为了演示的目的而设,其中只有一个定时器控件,用于预定时间之后,卸出封面窗体。定时器控件的属性在代码中设置。此窗体的Name属性为frmMain。有一个标题为“主窗口”。在装入主窗口的同时显示封面,而当主窗口装入完毕时,要卸出封面。为保持在装入主窗口的过程中封面一直处于主窗口的上面,这里调用了Windows API函数(SetWindowPos)来完成这一任务。打开frmMain的窗体的代码窗口,在Declaration部分输入以下代码:
  Const SWP_NOMOVE=2 `以下四句均为符号常数的定义
  Const SWP_NOSIZE=1
  Const FLAGS=SWP_NOMOVE Or SWP_NOSIZE
  Const HWND_TOPMOST=-1
  Declare Sub SetwindowPos Lib "User" (ByVal  hWnd As Integer,ByVal  hWndInsertAfter As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal wFlags As Integer)   `从Declare开始的三行,在代码中应在同一行上输入。
  在主窗口激活后,激活定时器控件。这是应用Form_Activate()事件过程的目的。
  Sub Form_Activate ()   `以下程序为frmMain窗体模块
  timer1.Interval=2000 `设置封面处在上面的时间
  timer1.Enabled=True   `激活定时器
  End Sub
  在下面Form_Load()过程中,先将鼠标的形状改为沙漏状,表示让用户等待。后面的代码就是装入封面并使其保持在上面。
  Sub Form_Load ()  `frmMain窗体
  screen.MousePointer=11   `沙漏状
  frmCover.Show`显示封面
  frmCover.Refresh`下面的API函数的调用,使封面保持在主窗口的上部
  SetwindowPos frmCover.hWnd,HWND_ TOPMOST, 0, 0, 0, 0, FLAGS
  End Sub
  定时器控件的事件过程使封面显示一段时间以后卸出。
  Sub Timer1_Timer ()
  Enabled=False`使定时器失效
  Unload frmCover   `卸出封面
  screen.MousePointer=0`鼠标形状返回正常状态(指针)
  End Sub
  当以上代码输入完毕,可按F5键运行程序,于是显示如图所示的封面(图中未显示主窗体)。
#2  本讲思考练习题:
  如何利用图形方法(Circle方法),设计一个程序,运行结果能够显示出Circle方法的使用方法。
#2  上讲思考题答案:
  可以使用下面的Mid语句(相当于内部过程)来修改字符串中的字符。此语句的用法如下(Mid$语句还有其它用法):
  Mid[$](StrVar, StartPos [,n])=Strg
  在此语句中,带$与不带$符功能是等价的。此语句的作用是,将StrVar中的字符串从第StartPos个字符位置开始用Strg代表的字符替代。StartPos与n必须在1和65535之间。其中的参数—StrVar必须是一个字符串变量名,而不能是其它表达式。n表示等号右边的Strg代表的字符串中有多少字符用于替换StrVar中的内容。如果省略了它,则使用所有的Strg字符串中的字符。不管使用不使用n这个参数,被替换的字符数总是小于或是等于StrVar中的字符数,即此语句不能改变原字符串变量中字符的个数。下面程序段就可用于替换一个字符串中的某些字符:
  Test="此语句不能改变原字符串变量中字符的个数。"   `给出要修改的字符串
  Start=Instr(Test, "不能")`找出“不能”在原字符串中的开始位置
  Mid(Test, Start, 4)="可以"   `用“可以”替换“不能”,一个汉字是两个字符
  以上语句运行之后,Test字符串变量中的内容变为:“此语句可以改变原字符串变量中字符的个数。”
#1  第四讲 一个实用的VB应用程序
  本讲向读者介绍一个用Visual Basic 编写的有实用价值的应用程序,为修改中文Windows码表输入法中的五笔字型编码表。首先假定读者已有了五笔字型的码表源文件,本程序可以帮助用户修改编码表文件,将用户输入的词组按排序插入到源文件中,然后调用码表生成器,将修改后的码表文件进行编译。本程序只有一个窗体,仍然属于简单程序之列,很适合初学者练习。
  本程序界面由一个窗体组成,如图4所示。图4左半部用于输入待添加的词组编码,文本框用于输入,其前面的说明是标签控件。而右半部用来完成本程序的主要功能。主要功能由右上部的三个按钮来承担。其功能显示在按钮的标题上。右下部两个按钮属于辅助功能。除了窗体上的可见控件之外,还有一个运行时不可见的通用对话框控件,用于打开编码文件。
  程序代码文本框主要用于输入,应编写它的KeyPress事件过程,用于滤除不符合要求的输入内容,从而使程序运行起来更为稳定;旋转控件(SpinButton,“编号”文本框右部的上下箭头控件)的两个事件过程用于将词组的编号增1或减1,当达到最大数目时,会提醒用户注意,并使计数的增加停止。由于篇幅所限,上述过程的代码已略去。本程序的主要事件过程是窗体右上部三个命令按钮的Click事件过程。它们实现程序的主要功能。
  以下是主要代码部分,每个程序中都有较为详细的注释,供读者阅读程序时参考。
  `声明部分,位于General 对象的Declaration部分。
  Dim filenm$,newfile$,WinPrompt$
  Dim bianma$()   `声明了两个动态数组用于保存输入的词组与编码
  Dim cizu$()  `前者用于存放编码,后者用于存放汉语词组
  Dim m As Integer `声明窗体级的变量,m中存放输入的词组个数
  Dim ml As Integer
  Declare Function GetModuleUsage% Lib "Kernel" (ByVal hModule%)
  `声明部分结束,以下是各个事件过程
  Sub Form_Load () `装入窗体,程序初始化
  WinPrompt$=pnl3dStateBar.Caption
  End Sub
  Sub cmd3dOpen_Click ()  `“打开编码文件”按钮的事件过程
  cmdialog1.DialogTitle="打开编码源文件"  `设置通用对话框的关键属性
  cmdialog1.Filter="编码文件|*.txt"
  cmdialog1.Action=1 `显示打开文件对话框
  filenm$=cmdialog1.Filename
  End Sub
  Sub cmd3dModify_Click ()`修改编码表按钮的事件过程,
  Dim msg$  `是本程序的主要过程
  NL=Chr(10)  `换行符
  msg$="请输入修改后的编码表源文件名,如果不想使"
  msg$=msg$ & NL & "用新名可回车使用原来的文件名。"
  newfile$=InputBox(msg$)   `用InputBox函数获得用户输入的文件名
  If newfile$="  " Then `如果用户以回车应答,则使用原来的文件名
  newfile$=filenm$
  End If
  Open filenm$ For Input As #1`打开码表源文件,以供修改
  Open "Temp.txt" For Output As #2`打开一个临时文件
  For j=0 To m-2 `本循环用于将输入的词组按编码排序
  cizumin$=cizu$(j)
  bianmamin$=bianma$(j)
  For i=j+1 To m-1
  If bianmamin$>bianma$(i) Then
  cizu1$=cizu$(i)
  bianma1$=bianma$(i)
  cizu$(i)=cizumin$
  bianma$(i)=bianmamin$
  cizumin$=cizu1$
  bianmamin$=bianma1$
  End If
  Next i
  cizu$(j)=cizumin$
  bianma$(j)=bianmamin$
  Next j
  flag=True`设置一个标记
  screen.MousePointer=11   `将鼠标变成沙漏
  k1=0
  j1=0
  strg$=" "
  `本段程序用于将输入的新词组按编码排序插入到码表中
  While Not EOF(1) And strg$ <> ""
  bianma3$=bianma$(k1)
  Line Input #1,strg$  `从码表文件中读入一行
  j1=j1+1
  If flag Then
  midstr$=Mid$(strg$,1,1)
  k=1
  While Asc(midstr$)>127   `从一行中取出编码,去掉汉字
  k=k+1
  midstr$=Mid$(strg$,k,1) `汉字的ASCII>127
  Wend
  strg1$=Mid(strg$,k,Len(strg$)-k+1)   `strg1$中存放着读入行的编码
  If bianma3$>strg1$ Then   `将新的编码与原编码比较
  Print #2,strg$  `将原来内容写入临时文件
  Else 
  Print #2,cizu$(k1) & bianma3$`否则写入新编码
  Print #2,strg$  `再写入原来文件中的词组及编码
  pnl3dStateBar.Caption="已经插入了" & Str$(k1+1) & "个词。"
  k1=k1+1    
  If k1 >=m Then
  flag=False
  End If
  End If
  Else
  Print #2,strg$   `将原文件中的其余部分写入临时文件
  End If
  Wend
  If EOF(1) And k1 <=m-1 Then  `若已到文件尾,还有没写入的新词组
  For i=k1 To m-1  `则将新词组增加到临时文件的尾部
  Print #2,cizu$(i) & bianma$(i)
  pnl3dStateBar.Caption="已经插入了" & Str$(i+1) & "个词。"
  Next i
  End If
  Close #1 `关闭原编码文件
  Close #2 `关闭临时文件 
  screen.MousePointer=0   `将鼠标形状改为原状   
  `利用Shell函数调用DOS功能,命令可查阅CopyDel.bat文件。
  X%=Shell("copydel.pif " &  filenm$ & " " & newfile$,2)
  While GetModuleUsage(X%)>0`CopyDel.PIF执行完成了吗?
  z%=DoEvents() `如未完成调用DoEvents函数
  Wend
  MsgBox "插入完毕!新的编码表文件为" & newfile$  `显示完成信息。
  End Sub
  Sub cmd3DConvert_Click ()   `“编译码表文件”按钮的事件过程
  X%=Shell("Convmb.exe")  `调用码表编译器
  SendKeys app.Path & "\" & newfile$  `将刚修改过的编码表文件名输入编译器
  SendKeys "%(C)" `相当于按下Alt+C键,开始转换编码表文件
  SendKeys "%(D)" `相当于按下Alt+D键,转换完成
  While GetModuleUsage(X%)>0`码表编译器运行结束了吗?
  `如未结束调用DoEvents函数,以便Windows 可以处理新的事件过程
  z%=DoEvents()   
  Wend 
  newfile$=Left(newfile$,Len(newfile$)-4)  `获得文件名(去掉后缀)
  newfile$=UCase$(newfile$) `将文件名改为大写
  MsgBox "新的编译对的码表文件为" & newfile$ & ".MB"  `显示码表文件名
  End Sub
#2  本讲练习题:
  编写本文中未列出的文本框的KeyPress事件过程,以便滤除错误的输入。对于“汉语词组”文本框,应滤除汉字以外的字符,对于“编码”文本框应滤除26个英文字母以外的所有字符。
#2  上讲练习答案:
  用四条语句来演示,语句与运行结果都表现在图5中了。读者可对照理解。请注意,当省略一个不在语句未尾的参数时,分隔参数的逗号不能省略。
#1  第五讲  关于进一步学习VISUAL BASIC的建议
  关于VB编程的内容在本讲座中只涉及到很少一部分。需要进一步学习的内容可以列举出很多来。比如:VB对数据库的访问,VB对OLE 2.0的支持,还有VB的多媒体编程以及调用Windows API函数等等。而且,VB也在不断地升级,VB 4.0(其中包括32位程序的开发环境,可以开发Windows 95下的32位应用程序)早已发行,VB 5.0也已上市,这些升级通常都意味着功能的不断增强,要掌握这些新的功能也需要进一步地学习。但笔者认为,由于VB是Windows 环境中的编程工具,因而对Windows 环境的了解就十分重要。所以学习如何调用Windows API函数对于利用VB进行Windows应用程序设计来说是最重要的。
#2  一、什么是WINDOWS API函数
  所谓的Windows API 函数,是Windows 本身用来提供图形用户界面和操纵Windows 环境的其它方面(如虚拟内存和多任务管理)的所有的过程。每个Windows 的主要成分都是一个动态链接库(Dynamic Link Liberary,简写为DLL),Windows 中的主要DLL包括:(1)KERNEL.EXE (内核库);(2)USER.EXE(用户界面管理库);(3)GDI.EXE(图形设备界面库)。除此以外,还有其它动态链接库,如处理声音的SOUND.DRV,以及鲜为人知的Toolhelp.DLL(本讲实例中使用了该库)。这些动态链接库中包括有七百多个例程。作为VB程序员的任务就是要知道哪些过程有用和如何使用它们。其中一些过程很容易使用,也不需要了解很多的Windows 的内部机制。但也有些与Windows 的结构与机制有十分密切的关系,要有相当的Windows 的内部操作的知识才能安全有效地使用它们。
#2  二、调用API函数的步骤
  Windows API函数对于初学者来说听起来有点吓人,但在VB中进行调用却是十分简单的,所有细节都由VB给代劳了。抛开函数功能的细节不谈,仅从调用的技术角度来讲,确实十分简单。首先找出需要调用的API函数,这可通过某些参考书,或是Visual Basic软件包中的Win 3.1 SDK Help进行查找。其次利用Declare语句在全局模块或是代码模块或是窗体模块的声明(Declaration)部分(视此函数调用的有效范围而定)声明所需调用的函数。这种声明代码不需用户编制,只需打开VB程序组中的“Win 3.1 API Help”(对于VB 3.0,若是VB 4.0,则使用API Veiwer程序)按函数名从中复制代码即可完成。如果用户使用的是其它应用程序的动态链接库中的函数,就要按照Declare语句的语法进行声明。声明之后,用户就可象调用VB的内部函数一样在自己的程序中调用API函数。能否正确调用的关键是正确的将API函数所需的参数传递进去,而调用起作用的关键,是正确地处理函数返回的参数。由于API函数是由C(C++)语言写成的,其数据类型及调用约定与VB不同,必须小心处理,特别是对于字符串型的数据更要特别注意。由于本讲只是建议性质的,因而略去了技术细节,请读者参考有关书籍。下面给出一个实例来,以便管中窥豹。
#2  三、一个实例
  本实例设计一个Windows资源指示器。运行时可指示出可用系统资源(所谓系统资源是指内存中的一个64K区域,其中保存着Windows运行中的关键数据,当这个区域中的自由内存数等于或少于总值的25%时,Windows 变得不稳定,常引起非正常退出)及可用自由内存量。VB中没有办法获得这些数据,现由调用Windows  API函数来完成。当本程序窗口最小化时,窗口的标题显示出这两个数据。按图6所示设计本实例的窗体。
  窗体中包括三个标签控件,用来说明下面的面板的功能,另有三个三维面板控件(Panel3d),第一个用来显示可用资源的百分比,第二个用来显示可用内存占总内存的百分比,第三个用来显示可用内存量。窗体上还有一个运行时不可见的定时器控件,以便实时采集和显示以上三种数据。程序代码包括两个模块:窗体模块和代码模块。代码模块(文件名为TypeModule.BAS)中的代码如下:
  `本模块中包括了调用Toolhelp.DLL中的函数所需的数据结构的定义及声明
  Type MemManInfo
  size As Long
  LargestFreeBlock As Long  
  MaxPagesAvailable As Long
  MaxPagesLockable As Long
  TotalLinearSpace As Long
  TotalUnlockedPages As Long
  FreePages As Long
  TotalPages As Long
  FreeLinearSpace As Long
  SwapFilePages As Long
  PageSize As Integer
  End Type
  Global Minfo As MemManInfo
  `-下面代码声明函数GetFreeSpace,用来获得自由内存量---
  Declare Function GetFreeSpace Lib "kernel" (ByVal wFlags As Integer) As Long
  `-下面代码声明函数GetFreeSystemResources,获取可用系统资源-
  Declare Function GetFreeSystemResources Lib "user" (ByVal fuSysResource As Integer) As Integer
  `-下面代码声明函数MemManInfo,此函数位于鲜为人知的toolhelp.dll中,可用来获取与系统内存有关的信息
  Declare Function MemManInfo Lib "toolhelp.dll" (X As MemManInfo) As Integer
  `-以下是各事件过程的代码
  Sub Form_Load ()
  frmMonitor.Caption="系统资源指示器"
  Panel3d1.FloodType=1 `从左到右显示彩色条
  Panel3d2.FloodType=1 `同上
  timer_monitor.Interval=100
  timer_monitor.Enabled=True
  End Sub
  `-定时器控件的定时过程,也是本实例的主过程-----
  Sub timer_monitor_timer ()
  `计算系统的可用的资源
  resources=GetFreeSystemResources(0)
  result=MemManInfo(Minfo)
  avail_mem=(GetFreeSpace(0) / 1024)  `除以1024化为以KB为单位,下同
  `计算内存总量及可用内存量(包括物理内存及虚拟内存)
  total_mem=((Minfo.TotalPages+Minfo.SwapFilePages) * Minfo.PageSize) / 1024
  If frmMonitor.windowstate=0 Then
  frmMonitor.Caption="系统资源指示器"
  Else
  frmMonitor.Caption=Format$(resources, "##") & "%" & Chr$(10) & Format$(avail_mem, "##,###") 
  Exit Sub
  End If
  `根据可用的系统资源设置资源指示器(名为panel_Rsc的三维面板)的颜色
  Select Case resources
  Case 51 To 100
  panel_Rsc.FloodColor=RGB(0, 255, 0) `绿色(安全)
  Case 26 To 50
  panel_Rsc.FloodColor=RGB(0, 255, 255)  `黄色(临界状态)
  Case Else
  panel_Rsc.FloodColor=RGB(255, 0, 0) `红色(处于危险状态)
  End Select
  `更新系统的资源显示值
  panel_Rsc.FloodPercent=resources
  `根据可用内存量来设置可用内存百分指示器(三维面板panel_mem1)的颜色
  Select Case ((avail_mem / total_mem) * 100)  `根据可用内存占总量的百分比
  Case 51 To 100 `选择不同的显示颜色
  panel_mem1.FloodColor=RGB(0, 255, 0) `绿色(安全)
  Case 26 To 50
  panel_mem1.FloodColor=RGB(0, 255, 255)  `黄色(临界状态)
  Case Else
  panel_mem1.FloodColor=RGB(255, 0, 0) `红色(处于危险状态)
  End Select
  `更新内存量的显示(三维面板panel_mem2用来显示可用内存量)
  panel_mem1.FloodPercent=((avail_mem / total_mem) * 100) `用色条显示百分比
  panel_mem2.Caption=Format$(avail_mem, "##,###")`数字显示内存量
  End Sub
#2  上一讲练习答案:
  设输入汉语词组的文本框名为txtChn,而输入编码的文本框名为txtBianma。下面的KeyPress过程可实现给定的要求:
  Sub txtBianma_KeyPress (keyascii As Integer)
  If keyascii > 127 Then
  Beep
  MsgBox "请转为英文输入方式!"
  keyascii=0
  Exit Sub
  End If
  txtbianma.SetFocus
  End Sub
  Sub txtChn_KeyPress (keyascii As Integer)
  If keyascii < 127 And keyascii <> 8 Then
  Beep  
  MsgBox "请转为中文输入方式!"
  keyascii=0
  Exit Sub
  End If
  txtChn.SetFocus
  End Sub