以下为VB的一些常用代码所有内容为转贴 (贴很长)如果以下的转贴对原作者有不便之处请告之
用Mid$命令超速字符串添加操作
大家都知道,&操作符的执行速度是相当慢的,特别是处理长字符串时。当必须重复地在同一变量上附加字符时,有一个基于Mid$命令的技巧可以使用。基本思路就是:预留一个足够长的空间存放操作的结果。下面是应用这个技术的一个例子。
假设要建立一个字符串,它要附加从1开始的10000个整数:"1 2 3 4 5 6 7 ... 9999
10000"。下面是最简单的实现代码:res = ""For i = 1 to 10000: res = res & Str(i): Next
<>face=宋体>代码虽然简单,但问题也很明显:Res变量将被重分配10000次。下面的代码实现同样的目的,但效果明显好转:
Dim res As StringDim i As LongDim index As Long
’预留足够长的缓冲空间res = Space(90000)
’指针变量,指出在哪里插入字符串index = 1
’循环开始For i = 1 to 10000substr = Str(i)length = Len(substr)
’填充字符串的相应区间段数值Mid$(res, index, length) = substr
’调整指针变量index = index + length
Next
’删除多余字符res = Left$(res, index - 1)
测试表明:在一个333MHz的计算机上,前段代码执行时间为2.2秒,后者仅仅为0.08秒!代码虽然长了些,可是速度却提高了25倍之多。呵呵,由此看来:代码也不可貌相啊
从头开始删除集合项目
删除集合中的所有内容有许多方法,其中有些非常得迅速。来看看一个包含10,000个项目的集合:Dim col As New Collection, i As LongFor i = 1 To 10000 col.Add i, CStr(i)Next
可以从末尾位置为起点删除集合内容,如下:
For i = col.Count To 1 Step -1col.Remove i
Next
也可以从开始位置为起点删除集合内容,如下:
For i = 1 To col.Count Step 1col.Remove iNext
<>face=宋体>试验证明,后者要快于前者百倍多,比如0.06秒比4.1秒。原因在于:当引用接近末尾位置的集合项目时,VB必须要从第1个项目开始遍历整个的项目链。 <>face=宋体>更有趣的是,如果集合项目的数量加倍,那么从末尾开始删除与从头开始删除,前者要比后者花费的时间将成倍增长,比如前者是24秒,后者可能为0.12秒这么短!最后提醒您:删除集合的所有内容的最快方法就是“毁灭”它,使用下面的语句: Set col = New Collection
对于一个包含20,000个项目的集合,上述操作仅仅耗时0.05秒,这比使用最快的循环操作进行删除也要快2倍左右。
用InStr函数实现代码减肥 可以采用“旁门左道”的方式使用Instr函数实现代码的简练。下面是一个典型的例子,检测字符串中是否包含一个元音字母:
1、普通的方法:
If UCase$(char) = "A" Or UCase$(char) = "E" Or UCase$(char) = "I" Or UCase$(char) = "O" Or UCase$(char) = "U" Then
’ it is a vowel
End If
2、更加简练的方法:
If InStr("AaEeIiOoUu", char) Then
’ it is a vowel
End If
同样,通过单词中没有的字符作为分界符,使用InStr来检查变量的内容。下面的例子检查Word中是否包含一个季节的名字: 1、普通的方法:
If LCase$(word) = "winter" Or LCase$(word) = "spring" Or LCase$(word) = _ "summer" Or LCase$(word) = "fall" Then
’ it is a season’s name
End If
2、更加简练的方法:
If Instr(";winter;spring;summer;fall;", ";" & word & ";") Then
’ it is a season’s name
End If
有时候,甚至可以使用InStr来替代Select
Case代码段,但一定要注意参数中的字符数目。下面的例子中,转换数字0到9的相应英文名称为阿拉伯数字: 1、普通的方法:
Select Case LCase$(word)
Case "zero"
result = 0
Case "one"
result = 1
Case "two"
result = 2
Case "three"
result = 3
Case "four"
result = 4
Case "five"
result = 5
Case "six"
result = 6
Case "seven"
result = 7
Case "eight"
result = 8
Case "nine"
result = 9
End Select
2、更加简练的方法:
result = InStr(";zero;;one;;;two;;;three;four;;five;;six;;;seven;eight;nine;", _
";" & LCase$(word) & ";") / 6
精用Boolean表达式,让代码再减肥
当设置基于表达式结果的Boolean型数值时,要避免使用多余的If/Then/Else语句结果。比如:
If SomeVar > SomeOtherVar Then
BoolVal = True
Else
BoolVal = False
End If
上面这段代码就很烦琐,它们完全可以使用下面的一行代码来替代:
BoolVal = (SomeVar > SomeOtherVar)
括号不是必须的,但可以增加可读性。根据表达式中的操作数不同,后者比前者执行起来大约快50%到85%。后者中的括号对速度没有影响。
有时,使用这个技术实现代码的简练并非很明显。关键是要牢记:所有的比较操作结果或者是0(false),或者是-1(True)。所以,下面例子中的2段代码是完全相同的,但是第2段要运行得快些:
1、传统方法: If SomeVar > SomeOtherVar Then
x = x + 1
End If
2、更简练的方法
x = x - (SomeVar > SomeOtherVar)函数名巧做局部变量
很多程序员都没有认识到“在函数本身中使用函数名”的妙处,这就象对待一个局部变量一样。应用这个技巧可以起到临时变量的作用,有时还能加速程序运行。看看下面的代码:
Function Max(arr() As Long) As Long
Dim res As Long, i As Long
res = arr(LBound(arr))
For i = LBound(arr) + 1 To UBound(arr)
If arr(i) > res Then res = arr(i)
Next
Max = res
End Function
去掉res变量,使用函数名称本身这个局部变量,可以使程序更加简练:
Function Max(arr() As Long) As Long
Dim i As Long
Max = arr(LBound(arr))
For i = LBound(arr) + 1 To UBound(arr)
If arr(i) > Max Then Max = arr(i)
Next
End Function
火眼识破隐藏的Variant变量
如果没有用As语句声明变量,默认类型就是Variants,比如:
Dim name ’ this is a variant
或者,当前模块下没有声明Option Explicit语句时,任何变量都是Variants类型。
许多开发者,特别是那些先前是C程序员的人,都会深信下面的语句将声明2个Interger类型变量:
Dim x, y As Integer
而实际上,x被声明为了variant类型。由于variant类型变量要比Integer类型慢很多,所以要特别注意这种情况。正确的一行声明方法是:
Dim x As Integer, y As Integer
GoSub在编译程序中速度变慢
编译为本地代码的VB应用程序中,如果使用 GoSubs 命令,就会比通常的 Subs 或者 Function 调用慢5-6倍;相反,如果是p-code模式,就会相当快。减少DoEvents语句的数量
不要在代码中放置不必要的DoEvents语句,尤其是在时间要求高的循环中。遵循这个原则,至少能在循环中的每N次反复时才执行DoEvents语句,从而增强效率。比如使用下面的语句:
If (loopNdx Mod 10) = 0 Then DoEvents
如果只是使用DoEvents来屏蔽鼠标以及键盘操作,那么就可以在事件队列中存在待处理项目时调用它。通过API函数GetInputState来检查这个条件的发生:
Declare Function GetInputState Lib "user32" Alias "GetInputState" () As Long
’ ...
If GetInputState() Then DoEvents
为常量定义合适的类型
VB在内部使用最简单、最可能的数据类型保存符号数值,这意味着最通常的数字类型-比如0或者1-都按照Integer类型存储。如果在浮点表达式中使用这些常量,可以通过常量的合适类型来加速程序运行,就象下面的代码: value# = value# + 1#.
这个语句强迫编译器按照Double格式存储常量,这样就省却了运行时的隐含转换工作。还有另外的一种处理方法就是:在常量声明时就进行相应类型的定义,代码如下:
Const ONE As Double = 1
And、Or和Xor:让我们来优化表达式
要检测一个整数值的最高有效位是否有数值,通常要使用如下的代码(有二种情况:第一组If判断表明对Integer类型,第二组对Long类型):
If intvalue And &H8000 Then
’ most significant bit is set
End If
If lngvalue And &H80000000 Then
’ most significant bit is set
End If
但由于所有的VB变量都是有符号的,因此,最高有效位也是符号位,不管处理什么类型的数值,通过下面的代码就可以实现检测目的:
If anyvalue < 0 Then
’ most significant bit is set
End If
另外,要检测2个或者更多个数值的符号,只需要通过一个Bit位与符号位的简单表达式就可以完成。下面是应用这个技术的几段具体代码:
1、判断X和Y是否为同符号数值:
If (x < 0 And y < 0) Or (x >= 0 And y >=0) Then ...
’ the optimized approach
If (x Xor y) >= 0 Then
2、判断X、Y和Z是否都为正数
If x >= 0 And y >= 0 And z >= 0 Then ...
’ the optimized approach
If (x Or y Or z) >= 0 Then ...
3、判断X、Y和Z是否都为负数
If x < 0 And y < 0 And z < 0 Then ...
’ the optimized approach
If (x And y And z) < 0 Then ...
4、判断X、Y和Z是否都为0
If x = 0 And y = 0 And z = 0 Then ...
’ the optimized approach
If (x Or y Or z) = 0 Then ...
5、判断X、Y和Z是否都不为0
If x = 0 And y = 0 And z = 0 Then ...
’ the optimized approach
If (x Or y Or z) = 0 Then ...
要使用这些来简单化一个复杂的表达式,必须要完全理解boolean型的操作原理。比如,你可能会认为下面的2行代码在功能上是一致的:
If x <> 0 And y <> 0 Then
If (x And y) Then ...
然而我们可以轻易地证明他们是不同的,比如X=3(二进制=0011),Y=4(二进制=0100)。不过没有关系,遇到这种情况时,我们可以对上面的代码进行局部优化,就能实现目的。代码如下:
If (x <> 0) And y Then ...
静态变量慢于动态变量
在过程中引用静态局部变量要比引用常规局部动态变量慢2-3倍。要想真正地加速过程的执行速度,最彻底的方法就是将所有的静态变量转换为模块级别变量。
这种方法的唯一不足是:过程很少是自包含的,如果要在其他工程中重用,就必须同时拷贝并粘贴这些模块级别变量。
另外的一种处理方法是:在时间要求高的循环前,将静态变量数值装入动态变量中。
善用"Assume No Aliasing"编译选项
据说,如果过程能够2次或多次引用同样的内存地址,那么过程就会包含别名数值。一个典型的例子如下:
Dim g_GlobalVariable As Long
...
Sub ProcWithAliases(x As Long)
x = x + 1
g_GlobalVariable = g_GlobalVariable + 1
End Sub
如果传递给这个过程g_GlobalVariable变量,则将通过一个直接引用以及x参数两种方式修改变量的数值2次。
别名数值经常是不良编程习惯的产物,对于程序优化有害无益。事实上,如果能够完全确认应用程序从来没有使用到别名变量,就可以打开"Assume No Aliasing"高级编译选项,这将告知编译器没有过程可以修改同一内存地址,使编译器产生更加有效率的汇编代码。更特别的是,编译程序将试图缓冲这些数据到CPU的寄存器中,从而明显地加速了程序运行。
总结一下,当遇到以下情况时,就不会有别名数值:(1) 过程不引用任何全局变量 (2) 过程引用了全局变量,但从来不通过ByRef参数类型传递同一变量给过程 (3) 过程含有多个ByRef参数类型,但从来不传递同一变量到其中的2个或者多个之中。
你真正理解"Allow Unrounded Floating Point Operations"选项的含义吗?
来自微软的资料鼓吹:高级优化对话框中的所有编译选项都被认为是不稳定的,它们可能导致不正确的结果,甚至程序崩溃。对于其中的大多数,这种说法是正确的,但是经常有一个叫做"Allow Unrounded Floating Point Operations"的选项能够给予正确的结果,防止应用程序产生bug。考虑下面的代码段: Dim x As Double, y As Double, i As Integer
x = 10 ^ 18
y = x + 1 ’ this can’t be expressed with 64 bits
MsgBox (y = x) ’ 显示 "True" (不正确的结果)
严格地说,由于X和Y变量不包含相同的数值,MsgBox将显示False。可问题是,由于数值1E18与1E18+1都以相同的64位浮点Double类型来表示,它们最终包含了几乎相同的数值,最后的MsgBox结果将是True。
如果打开了"Allow Unrounded Floating Point Operations"编译选项,VB就能重用已在数学协处理器堆栈中的数值,而不是内存中的数值(比如:变量)。因为FPU堆栈具备80位的精度,因此就可以区分出这2个数值的不同:
’ if the program is compiled using the
’ "Allow Unrounded Floating Point Operations" compiler option
MsgBox (y = x) ’ 显示 "False" (正确的结果)
总结一下:当以解释模式、或者编译的p-code模式、或者编译的native代码模式但关掉"Allow Unrounded Floating Point Operations"选项这3种方式运行一个程序时,所有浮点数字运算在内部都以80位的精度进行处理。但如果有一个数值是存储在64位Double变量中,结果就是接近的了,并且,随后使用那个变量的表达式也将产生近似的结果,而不是绝对正确的结果。
相反,如果打开"Allow Unrounded Floating Point Operations"编译选项后运行一段native编译代码,在随后的表达式中VB就经常能重用内部的80位数值,而忽略存储在变量中的当前数值。注意:我们并不能完全控制这个功能,VB也许对此生效,也许就不生效,这要取决于表达式的复杂程度以及最初分配数值语句与随后产生结果的表达式语句的距离远近。
除法运算符"/"与"/"的区别
整数间执行除法运算时,要使用 "/" 而不是 "/"。 "/" 运算符要求返回一个单一数值,所以,表面上看似简单的一行代码:
C% = A% / B%
实际上包含了3个隐含的转换操作:2个为除法运算做准备,从Integer转换到Single;一个完成最后的赋值操作,从Integer转换到Single。但是如果使用了"/"操作符,情况就大不相同了!不仅不会有这么多中间步骤,而且执行速度大大提高。
同时请记住:使用"/"操作符做除法运算时,如果其中之一是Double类型,那么结果就将是Double类型。所以,当2个Integer或者Single类型数值做除法运算时,如果想得到高精度的结果,就需要手工强迫其中之一转换为Double类型:
’结果为 0.3333333
Print 1 / 3
’结果为 0,333333333333333
Print 1 / 3#
使用"$-类型"字符串函数会更快
VB官方文档似乎很鼓励使用"无$"类字符串函数,比如:Left、LTrim或者UCase,而不是实现同样功能的Left$、LTrim$和UCase$函数。但是我们必须认识到:前者返回variant类型的数值,当用于字符串表达式中时,最终必须要转换为字符串(string)类型。
因此,在严格要求时间的代码段中,我们应该使用后者,它们将快5-10%。
妙用Replace函数替代字符串连接操作符&
你大概不知道Replace函数还能这么用吧?比如下面的语句:
MsgBox "Disk not ready." & vbCr & vbCr & _
"Please check that the diskette is in the drive" & vbCr & _
"and that the drive’s door is closed."
可以看出,为了显示完整的字符串含义,要将可打印字符与非打印字符(比如:回车符vbCr)用&符号连接在一起。结果是:长长的字符连接串变得难于阅读。但是,使用Replace函数,可以巧妙地解决这个问题。方法就是:将非打印字符以字符串中不出现的一个可打印字符表示,这样完整地写出整个字符串,然后使用Replace函数替换那个特别的打印字符为非打印字符(比如:回车符vbCr)。代码如下:
MsgBox Replace("Disk not ready.§§Please check that the diskette is in the " _
& "drive§and that the drive’s door is closed.", "§", vbCr)
固定长度字符串数组:赋值快,释放快!
固定长度字符串的处理速度通常慢于可变长度字符串,这是因为所有的VB字符串函数和命令只能识别可变长度字符串。因此,所有固定长度字符串比然被转换为可变长度字符串。
但是,由于固定长度字符串数组占据着一块连续的内存区域,因此在被分配以及释放时,速度明显快于可变长度的数组。比如:在一个Pentium 233MHz机器上,对于一个固定长度为100,000的数组,给其中30个位置分配数值,大约只花费半秒种的时间。而如果是可变长度的数组,同样的操作要耗费8秒之多!后者的删除操作耗时大约0.35秒,但固定长度的数组几乎可以立即“毙命”!如果应用程序中涉及到这么大的一个数组操作,选择固定长度方式数组绝对是确定无疑的了,无论是分配数值,还是释放操作,都可以风驰电掣般完成。未公开的返回数组型函数加速秘诀
在VB6中,函数是能够返回数组对象的。这种情况下,我们不能象返回对象或者数值的其他函数一样使用函数名当做局部变量来存储中间结果,因此不得不生成一个临时局部数组,函数退出前再分配这个数组给函数名,就象下面的代码一样:
’ 返回一个数组,其中含有N个随即元素
’ 并且将平均值保存在AVG中
Function GetRandomArray(ByVal n As Long, avg As Single) As Single()
Dim i As Long, sum As Single
ReDim res(1 To n) As Single
’ 以随机数填充数组,并计算总和
Randomize Timer
For i = 1 To n
res(i) = Rnd
sum = sum + res(i)
Next
’ 赋值结果数组,计算平均值
GetRandomArray = res
avg = sum / n
End Function
难以置信的是,只需要简单地颠倒最后2条语句的顺序,就能使上面这段程序变得快些:
’ ... ’ 赋值结果数组,计算平均值
avg = sum / n
GetRandomArray = res
End Function
例如,在一个Pentium II 333MHz 机器上,当N=100,000时,前段程序运行时间为0.72秒,后段程序则为0.66秒,前后相差10%。
原因何在呢?前段程序中,VB将拷贝res数组到GetRandomArray对应的结果中,当数组很大时,花费的时间是很长的。后段程序中,由于GetRandomArray = res是过程的最后一条语句,VB编译器就能确认res数组不会被再使用,因此将直接交换res和GetRandomArray的地址数值,从而节省了数组元素的物理拷贝操作以及随后的res数组释放操作。
总结如下:当编写返回数组的函数时,一定要将分配临时数组到函数名的语句放在最后,就是其后紧挨者Exit Function 或者End Function的位置。
--------------------------------------------------------------------------------Dim i As Long
ReDim res(0 To UBound(values)) As Integer
For i = 0 To UBound(values)
res(i) = values(i)
Next
ArrayInt = res()
End Function
同时,也可以创建一个子程序段来检测传递给它的数值的类型,并返回正确类型的数组。这种情况下,函数应该定义为返回Variant。
访问简单变量总是快于数组元素值
读写数组中的元素速度通常都慢于访问一个简单变量,因此,如果在一个循环中要重复使用同一数组元素值,就应该分配数组元素值到临时变量中并使用这个变量。下面举一个例子,检测整数数组中是否存在重复项:
Function AnyDuplicates(intArray() As Integer) As Boolean
’如果数组包含重复项,返回True
Dim i As Long, j As Long,
Dim lastItem As Long
Dim value As Integer
’只计算机UBound()一次
lastItem = UBound(intArray)
For i = LBound(intArray) To lastItem
’ 保存intArray(i)到非数组变量中
value = intArray(i)
For j = i + 1 To lastItem
If value = intArray(j) Then
AnyDuplicates = True
Exit Function
End If
Next
Next
’没有发现重复项
AnyDuplicates = False
End Function
上述程序有2层循环,通过缓存intArray(i)的数值到一个普通的、非数组变量中,节省了CPU运行时间。经测试,这将提高80%的速度。
创建新表时,快速拷贝字段
在VB6中,无需离开开发环境就可以创建新的SQL Server和Oracle表。方法很简单:打开DataView窗口,用鼠标右键单击数据库的表文件夹,再选择新表格菜单命令。
当处理相似表格时,就是说具有许多相同字段的表格,我们完全可以在很短的时间内容完成设定操作。具体步骤是:在设计模式下打开源表格,加亮选择要拷贝字段对应的行,按Ctrl-C拷贝信息到粘贴板;然后,在设计模式打开目标表格,将光标置于要粘贴字段所在的位置,按Ctrl-V。
这样,就拷贝了所有的字段名称以及它们所带的属性。 无闪烁地快速附加字符串到textbox控件
附加文本到TextBox或者RichTextBox控件的通常方法是在当前内容上连接上新的字符串:
Text1.Text = Text1.Text & newString
但还有一个更快的方法,并且会减少连接操作的闪烁感,代码如下:
Text1.SelStart = Len(Text1.Text)
Text1.SelText = newString
快速找到选中的OptionButton
OptionButton控件经常是作为控件数组存在的,要快速找到其中的哪一个被选中,可以使用下面的代码:
’假设控件数组包含3个OptionButton控件
intSelected = Option(0).value * 0 - Option(1).value * 1 - Option(2).value * 2
注意,因为第一个操作数总是0,所以上述代码可以精简如下:
intSelected = -Option(1).value - Option(2).value * 2
表单及控件的引用阻止了表单的卸载
当指派表单或者表单上的控件到该表单模块以外的一个对象变量中时,如果要卸载表单,就必须首先将那个变量设置为 to Nothing。也就是说,如果不设置为Nothing,即使看不到这个对象了,但它仍旧是保存在内存中的。
注意:这并非是一个bug,这仅仅是COM引用规则的一个结果。唯一要注意的就是引用的这个控件将阻止整个表单的卸载操作,它将依赖于它的父表单而存在。 重定义编译DLL文件的基地址
许多VB开发者都知道应该在工程属性对话框的“编译”功能页面中定义一个DLL基地址数值。这不同于工程中任何其他DLL或OCX的基地址。
当操作没有源代码的编译DLL或者OCX文件时,可以使用EDITBIN程序修改它的基地址。EDITBIN程序随Visual Studio安装后就有了,可以在主Visual Studio目录的VC98/BIN目录下找到它。比如,以下代码重新设定一个编译DLL文件的基地址为12000000(16进制):
EDITBIN /REBASE:BASE=0x12000000 myfile.dll
同样,EDITBIN程序对可执行文件也有一些处理技巧。 以下是该程序支持的完整功能选项列表(使用EDITBIN /? 可以列出这些):
/BIND[:PATH=path]
/HEAP:reserve[,commit]
/LARGEADDRESSAWARE[:NO]
/NOLOGO
/REBASE[:[BASE=address][,BASEFILE][,DOWN]]
/RELEASE
/SECTION:name[=newname][,[[!]{cdeikomprsuw}][a{1248ptsx}]]
/STACK:reserve[,commit]
/SUBSYSTEM:{NATIVE|WINDOWS|CONSOLE|WINDOWSCE|POSIX}[,#[.##]]
/SWAPRUN:{[!]CD|[!]NET}
/VERSION:#[.#]
/WS:[!]AGGRESSIVE
快速调入TreeView控件以及ListView控件的子项内容
有一个简单但仍未发现的技巧可用于在TreeView控件中装载多个节点,或者在ListView控件中装载多个ListItems。这种方法要比传统做法快。先看看下面这个传统方法:
For i = 1 To 5000
TreeView1.Nodes.Add , , , "Node " & i
Next
改进一下,代替重复引用TreeView1对象的Nodes集合,我们可以先将之保存在临时对象变量中:
Dim nods As MSComctlLib.Nodes
Set nods = TreeView1.Nodes
For i = 1 To 5000
nods.Add , , , "Node " & i
Next
甚至,如果使用With代码块,还可以不需要临时变量:
With TreeView1.Nodes
For i = 1 To 5000
.Add , , , "Node " & i
Next
End With
经测试,优化的循环代码要比传统方法执行速度快40%左右。原因在于:将Nodes集合对象保存在临时变量中,或者应用With代码块后VB将使用隐藏的临时变量后,就可以避免在循环中重复绑定Nodes对象到它的父TreeView1对象上。由于这种绑定是低效率的,因此省却它就能节省大量的执行时间。
同样的道理对于其他ActiveX控件也生效:
ListView控件的ListItems、ListSubItems以及ColumnHeaders集合
Toolbar控件的Buttons和ButtonMenus集合
ImageList的ListImages集合StatusBar控件的Panels集合TabStrip控件的Tabs集合
Friend过程快于Public过程
你可能会非常惊奇:Friend类型过程的执行速度要明显快于Public类型。这可以通过创建一个带有Private类和Public类 (设定Instancing = MultiUse)的ActiveX EXE工程看到,在2个类模块中添加下面的代码:
Public Sub PublicSub(ByVal value As Long)
’
End Sub
Public Function PublicFunction(ByVal value As Long) As Long
’
End Function
Friend Sub FriendSub(ByVal value As Long)
’
End Sub
Friend Function FriendFunction(ByVal value As Long) As Long
’ End Function
然后,在表单模块中创建一个循环,执行每个例程许多次。比如,要在一个Pentium II机器上查看执行时间上的区别,可以调用每个例程1,000,000次。下面是测试的结果:
Private类模块中,反复调用1,000,000次Public Sub或者Function耗费了0.46秒,而调用内容相同的Friend类型模块则分别只有0.05秒和0.06秒。前后竟然相差了8-9倍之多!对于MultiUse类型的Public类模块,也是一样的结果。
对于这个不可思议的结果的可能解释是:Friend型过程没有处理汇集和拆装代码的消耗(Public过程可以从当前工程外被调用,因此COM必须要来回地汇集数据)。 但是在多数情况下,这些时间差别是不明显的,特别是程序中包含一些复杂和耗时的语句时。
即使这样,Friend型过程仍有其他的优势高于Public类型,比如:接受和返回在BAS模块中定义的UDT变量的能力。使用Objptr函数快速查找集合中的对象
ObjPtr函数的一个最简单但是却最有效的用途就是提供快速寻找集合中对象的关键字。假设有一个对象集合,它没有可以当做关键字以从集合中取回的属性。那么,我们就可以使用ObjPtr函数的返回值作为集合中的关键字:
Dim col As New Collection
Dim obj As CPerson
’创建新的CPerson对象,并添加到集合中
Set obj = New CPerson
obj.Name = "John Smith"
col.Add obj, CStr(ObjPtr(obj)) ’关键字必须是字符串
因为任何对象都有一个明确的ObjPtr数值,而且它是不变的,所以,我们可以容易地、快速地从集合中取回它:
’ 删除集合中的对象
col.Remove CStr(ObjPtr(obj))
这个技巧可以适用于任何类型的对象,包括VB中的表单和控件,以及外部对象。
使用ObjPtr检测2个对象变量是否指向同一对象
判断2个对象变量释放指向同一对象的方法是使用Is操作符,代码如下:
If obj1 Is obj2 Then ...
但当2个对象是同一类型时,或者指向同一个二级接口时,我们就可以利用ObjPtr()函数对代码进行一些优化处理:
If ObjPtr(obj1) = ObjPtr(obj2) Then ...
后者的执行速度将比前种方法快40%多。但是请注意,2种方法原本就是很有效率的,只有在时间要求非常严格的上百成千次的循环中,才会体现出这种差别。
读取文件内容的简洁方法
读取text文件的最快方法是使用Input$函数,就象下面的过程:
Function FileText (filename$) As String
Dim handle As Integer
handle = FreeFile
Open filename$ For Input As #handle
FileText = Input$(LOF(handle), handle)
Close #handle
End Function
使用上述方法要比使用Input命令读取文件每一行的方法快很多。下面是应用这个函数读取Autoexec.bat的内容到多行textbox控件的例子:
Text1.Text = FileText("c:/autoexec.bat")
但请注意:当文件包含Ctrl-Z(EOF)字符时,上面的函数代码可能会发生错误。因此,要修改一下代码:
Function FileText(ByVal filename As String) As String
Dim handle As Integer
’ 判断文件存在性
If Len(Dir$(filename)) = 0 Then
Err.Raise 53 ’文件没有找到 End If
’ 以binary模式打开文件
handle = FreeFile
Open filename$ For Binary As #handle
’ 读取内容,关闭文件
FileText = Space$(LOF(handle))
Get #handle, , FileText
Close #handle
End Function
字体对象克隆招法
当要应用一个控件的字体到另一控件时,最直接的方法就是直接赋值:
Set Text2.Font = Text1.Font
但多数情况下这种方法并不奏效,因为这实际上是将同一字体的引用分配给了2个控件。换言之,当随后修改其中之一控件的字体时,另外一个控件也受到影响。因此,要实现我们的目的,需要做的就是克隆字体对象并赋值给需要的控件。
最简单的克隆字体的方法是手工地拷贝所有单独的字体属性,就象下面一样:
Function CloneFont(Font As StdFont) As StdFont
Set CloneFont = New StdFont
CloneFont.Name = Font.Name
CloneFont.Size = Font.Size
CloneFont.Bold = Font.Bold
CloneFont.Italic = Font.Italic
CloneFont.Underline = Font.Underline
CloneFont.Strikethrough = Font.Strikethrough
End Function
’函数的应用
Set Text2.Font = CloneFont(Text1.Font)
如果使用VB6,就可以使用PropertyBag对象快速拷贝所有字体属性,并且代码会很简练、速度也快2倍:
Function CloneFont(Font As StdFont) As StdFont
Dim pb As New PropertyBag
’拷贝字体到PropertyBag对象中
pb.WriteProperty "Font", Font
’恢复字体对象到新控件
Set CloneFont = pb.ReadProperty("Font")
End Function
但是我们还能进一步地对代码进行优化,方法就是使用可被所有StdFont对象识别的隐藏IFont接口。这个接口具有一个Clone方法,用它就可以精确地实现我们的目的。它以非正常方式执行:创建一个克隆Font对象,然后返回相应的引用。这可能是实现克隆目的的最简洁代码了,而且,执行速度也是这里列举的3种方法中最快的一个,要比使用PropertyBag对象的方法快大约3倍左右。来看看具体代码:
Function CloneFont(Font As IFont) As StdFont
Font.Clone CloneFont
End Function
--------------------------------------------------------------------------------API程序源代码(多种功能) ' 本人收集了一些技巧供大家参考,希望斑竹能多放一些时间。'------------------------------------------------------------'按字母或数字顺序排列列表框中的列表项.'将以下代码加入到你的程序中.Sub ReSort(L As Control)Dim P%, PP%, c%, Pre$, s$, V&, NewPos%, CheckIt%Dim TempL$, TempItemData&, S1$For P = 0 To L.ListCount - 1s = L.List(P)For c = 1 To Len(s)V = Val(Mid$(s, c))If V > 0 Then Exit ForNextIf V > 0 ThenIf c > 1 Then Pre = Left$(s, c - 1)NewPos = -1For PP = P + 1 To L.ListCount - 1CheckIt = FalseS1 = L.List(PP)If Pre <> "" ThenIf InStr(S1, Pre) = 1 Then CheckIt = TrueElseIf Val(S1) > 0 Then CheckIt = TrueEnd IfIf CheckIt ThenIf Val(Mid$(S1, c)) < V Then NewPos = PPElseExit ForEnd IfNextIf NewPos > -1 ThenTempL = L.List(P)TempItemData = L.ItemData(P)L.RemoveItem (P)L.AddItem TempL, NewPosL.ItemData(L.NewIndex) = TempItemDataP = P - 1End IfEnd IfNextExit Sub'---------------------------------------------------'Tag属性的妙用.'在VB编程中,我们经常要动态的控制很多不同控件的属性,例如我们要将一个CommandButton阵列共20各控件中的第1、4、6、7、8、11、18、20号删除。该怎么半呢?这时只要将要删除的控件的Tag属性设置为1,然后加入以下代码就可以了。For i = 1 To 20If command1(i).Tag = 1 ThenUnload command1(i)End IfNext i'-----------------------------------------------------'利用VB产生屏幕变暗的效果.'想利用VB编程实现屏幕变暗的效果(向关闭Win95时的效果),只要按下面的步骤来做'1、在FORM1中加入两个CommandButton和一个PictureBox.'2 Print 在FORM1的代码窗口中添加以下代码:Private Type RECTLeft As LongTop As LongRight As LongBottom As LongEnd TypePrivate Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As LongPrivate Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hdc As Long) As LongPrivate Declare Function CreatePatternBrush Lib "gdi32" (ByVal hBitmap As Long) As LongPrivate Declare Function PatBlt Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal dwRop As Long) As LongPrivate Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As LongPrivate Declare Function CreateBitmap Lib "gdi32" (ByVal nWidth As Long, ByVal nHeight As Long, ByVal nPlanes As Long, ByVal nBitCount As Long, lpBits As Any) As LongPrivate Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As LongPrivate Declare Function InvalidateRect Lib "user32" (ByVal hwnd As Long, ByVal lpRect As Long, ByVal bErase As Long) As LongPrivate bybits(1 To 16) As BytePrivate hBitmap As Long, hBrush As LongPrivate hDesktopWnd As LongPrivate Sub Command1_Click()Dim rop As Long, res As LongDim hdc5 As Long, width5 As Long, height5 As Longhdc5 = GetDC(0)width5 = Screen.Width / Screen.TwipsPerPixelXheight5 = Screen.Height / Screen.TwipsPerPixelYrop = &HA000C9Call SelectObject(hdc5, hBrush)res = PatBlt(hdc5, 0, 0, width5, height5, rop)Call DeleteObject(hBrush)res = ReleaseDC(0, hdc5)End SubPrivate Sub Command2_Click()Dim aa As Long
aa = InvalidateRect(0, 0, 1)End SubPrivate Sub FORM_Load()Dim aryDim i As Longary = Array(&H55, &H0, &HAA, &H0, _&H55, &H0, &HAA, &H0, _&H55, &H0, &HAA, &H0, _&H55, &H0, &HAA, &H0)For i = 1 To 16bybits(i) = ary(i - 1)Next ihBitmap = CreateBitmap(8, 8, 1, 1, bybits(1))hBrush = CreatePatternBrush(hBitmap)Picture1.ForeColor = RGB(0, 0, 0)Picture1.BackColor = RGB(255, 255, 255)Picture1.ScaleMode = 3End Sub'运行程序,按Command1就可以使屏幕暗下来,按Command2恢复。'--------------------------------------------------'使两个列表框(ListBox)的选项同步步骤1'在FORM中添加两个ListBox和一个CommandButton一个Timer,不要改动他们的属性.步骤2在FORM中添加如下代码:Private Sub FORM_Load()Dim x As IntegerFor x = 1 To 26list1.AddItem Chr$(x + 64)Next xFor x = 1 To 26List2.AddItem Chr$(x + 64)Next xTimer1.INTERVAL = 1Timer1.Enabled = TrueEnd SubPrivate Sub Command1_Click()EndEnd SubPrivate Sub Timer1_Timer()Static PrevList1Dim TopIndex_List1 As IntegerTopIndex_List1 = list1.TopIndexIf TopIndex_List1 <> PrevList1 ThenList2.TopIndex = TopIndex_List1PrevList1 = TopIndex_List1End IfIf list1.ListIndex <> List2.ListIndex ThenList2.ListIndex = list1.ListIndexEnd IfEnd Sub'运行程序,当选中其中一个列表框中的某一项后,另外一个列表框中的相应项就会被选中.'-------------------------------------------------'获得Win9X下文件的短文件名(8.3文件名)'步骤一 在FORM中加入一个FileListBox,一个DirListBox,一个Label.'步骤二 在FORM中加入以下代码:'Private Declare Function GetShortPathName Lib "kernel32" Alias "GetShortPathNameA" (ByVal'lpszLongPath As String, ByVal lpszShortPath As String, ByVal cchBuffer As Long) As LongPrivate Sub Dir1_Change()File1 = dir1.pathEnd SubPrivate Sub Drive1_Change()dir1 = drive1End SubPrivate Sub File1_Click()Label1.Caption = GetShortFileName(dir1 & "/" & File1)End SubPublic Function GetShortFileName(ByVal FileName As String) As String'converts a long file and path name to old DOS FORMat'PARAMETERS' FileName = the path or filename to convert'RETURNS' String = the DOS compatible name for that particular FileNameDim rc As LongDim ShortPath As StringConst PATH_LEN& = 164'get the short filenameShortPath = String$(PATH_LEN + 1, 0)rc = GetShortPathName(FileName, ShortPath, PATH_LEN)GetShortFileName = Left$(ShortPath, rc)End Function'---------------------------------------------------------------------使指定窗口总处于其他窗口之上'将以下代码加入到FORM中,这个FORM就成为一个在其他所有窗口之上的窗口了.Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As LongConst HWND_TOPMOST = -1Private Sub FORM_Load()SetWindowPos Me.hwnd, HWND_TOPMOST, Me.Left / Screen.TwipsPerPixelX _, Me.Top / Screen.TwipsPerPixelY, Me.Width / Screen.TwipsPerPixelX, _Me.Height / Screen.TwipsPerPixelY, 0End Sub'--------------------------------------------------获得位图文件的信息在FORM中添加一个Picture控件和一个CommandButton控件 , 在Picture控件中加入一个位图文件, 将下面代码加入其中:Private Declare Function GetObject Lib "gdi32" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any) As LongPrivate Declare Function GetBitmapBits Lib "gdi32" (ByVal hBitmap As Long, ByVal dwCount As Long, lpBits As Any) As LongPrivate Type BITMAPbmType As LongbmWidth As LongbmHeight As LongbmWidthBytes As LongbmPlanes As IntegerbmBitsPixel As IntegerbmBits As LongEnd TypePrivate Sub Command1_Click()Dim hBitmap As LongDim res As LongDim bmp As BITMAPDim byteAry() As ByteDim totbyte As Long, i As LonghBitmap = Picture1.Picture.Handleres = GetObject(hBitmap, Len(bmp), bmp) '取得BITMAP的结构totbyte = bmp.bmWidthBytes * bmp.bmHeight '总共要多少BYTE来存图ReDim byteAry(totbyte - 1)'将Picture1中的图信息存到ByteAryres = GetBitmapBits(hBitmap, totbyte, byteAry(0))Debug.Print "Total Bytes Copied :"; resDebug.Print "bmp.bmBits "; bmp.bmBitsDebug.Print "bmp.bmBitsPixel "; bmp.bmBitsPixel '每相素位数Debug.Print "bmp.bmHeight "; bmp.bmHeight '以相素计算图象高度Debug.Print "bmp.bmPlanes "; bmp.bmPlanesDebug.Print "bmp.bmType "; bmp.bmTypeDebug.Print "bmp.bmWidth "; bmp.bmWidth '以相素计算图形宽度Debug.Print "bmp.bmWidthBytes "; bmp.bmWidthBytes '以字节计算的每扫描线长度End Sub'---------------------------------------------------'获得驱动器的卷标'在FORM中添加一个CommandButton控件 , 再加入一下一段代码:Private Declare Function GetVolumeInFORMation Lib "kernel32" Alias "GetVolumeInFORMationA" (ByVal lpRootPathName As String, ByVal lpVolumeNameBuffer As String, ByVal nVolumeNameSize As Long, lpVolumeSerialNumber As Long, lpMaximumComponentLength As Long, lpFileSystemFlags As Long, ByVal lpFileSystemNameBuffer As String, ByVal nFileSystemNameSize As Long) As LongConst FILE_VOLUME_IS_COMPRESSED = &H8000Public Sub GetVolInfo(ByVal path As String)Dim aa As LongDim VolName As StringDim fsysName As StringDim VolSeri As Long, compress As LongDim Sysflag As Long, Maxlen As Long'初试化字符串的长度VolName = String(255, 0)fsysName = String(255, 0)aa = GetVolumeInFORMation(path, VolName, 256, VolSeri, Maxlen, Sysflag, fsysName, 256)VolName = Left(VolName, InStr(1, VolName, Chr(0)) - 1)fsysName = Left(fsysName, InStr(1, fsysName, Chr(0)) - 1)compress = Sysflag And FILE_VOLUME_IS_COMPRESSEDIf compress = 0 ThenMe.Print "未压缩驱动器"ElseMe.Print "压缩驱动器"End IfMe.Print "驱动器卷标 :", VolNameMe.Print "驱动器标号 : ", Hex(VolSeri)Me.Print "驱动器文件系统 (FAT, HPFS, or NTFS)", fsysNameMe.Print "支持的文件名长度", MaxlenEnd SubPrivate Sub Command1_Click()FORM1.Caption = "c:驱动器信息"Call GetVolInfo("c:/")End Sub'---------------------------------------------------将包含有Null结尾的字符串转换为VB字符串在VB编程调用Windows API函数时, 经常会碰到以Null结尾的字符串, 下面是一段将Null结尾字符串转换到VB字符串的函数:Public Function LPSTRToVBString$(ByVal s$)Dim nullpos&nullpos& = InStr(s$, Chr$(0))If nullpos > 0 ThenLPSTRToVBString = Left$(s$, nullpos - 1)ElseLPSTRToVBString = ""End IfEnd Function'---------------------------------------------------启动控制面板命令控制面板模块: Control.Exe命令: rundll32.Exe shell32.dll, Control_RunDLL结果: 显示控制面板窗口?例子:Dim xx = Shell("rundll32.exe shell32.dll,Control_RunDLL")辅助选项模块: access.cpl'命令: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,5'结果: 显示辅助选项/常规。'命令: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,1'结果: 显示辅助选项/键盘。'命令: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,2'结果: 显示辅助选项/声音。'命令: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,3'结果: 显示辅助选项/显示。'命令: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,4'结果: 显示辅助选项/鼠标。'添加新硬件'模块: sysdm.cpl'命令:rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl @1'增加新的打印机'模块: shell32.dll'命令:rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL AddPrinter'添加/删除程序'模块: appwiz.cpl'命令:rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,1'结果:显示安装/卸载。'命令:rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,1'结果:显示安装/卸载。'命令:rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,2'结果: 显示Windows 安装?'命令:rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,3'结果: 显示启动盘?'复制磁盘'模块: diskcopy.dll'命令: rundll32.Exe diskcopy.dll, DiskCopyRunDll'时间/日期'模块: timedate.cpl'命令: rundll32.exe shell32.dll,Control_RunDLL timedate.cpl,,0'结果: 显示设置日期/时间。'命令: rundll32.exe shell32.dll,Control_RunDLL timedate.cpl,,1'结果: 显示设置时间区域?'拨号连接 (DUN)'模块: rnaui.dll'命令: rundll32.exe rnaui.dll,RnaDial 连接_名称'结果: 打开指定的拨号连接?'例子:x = Shell("rundll32.exe rnaui.dll,RnaDial " & "连接_名称", 1)'显示器'模块: desk.cpl'命令: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,0'结果: 背景设置?'命令: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,1'结果: 屏幕保护设置?'命令: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,2'结果: 外观设置?'命令: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,3'结果: 设置窗口?'操纵杆'模块: joy.cpl'命令: rundll32.exe shell32.dll,Control_RunDLL joy.cpl'邮件/传真'模块: mlcfg32.cpl'命令: rundll32.exe shell32.dll,Control_RunDLL mlcfg32.cpl'结果: 出现 MS Exchange 属性设置。'邮局设置'模块: wgpocpl.cpl'命令: rundll32.exe shell32.dll,Control_RunDLL wgpocpl.cpl'结果: 显示 MS Postoffice Workgroup Admin 设置。'主设置'模块: Main.cpl'命令: rundll32.exe shell32.dll,Control_RunDLL main.cpl @0'结果: 显示鼠标属性?'命令: rundll32.exe shell32.dll,Control_RunDLL main.cpl @1'结果: 显示键盘/速度属性。'命令: rundll32.exe shell32.dll,Control_RunDLL main.cpl @1,,1'结果: 显示键盘/语言属性。'命令: rundll32.exe shell32.dll,Control_RunDLL main.cpl @1,,2'结果: 显示键盘/常规属性。'命令: rundll32.exe shell32.dll,Control_RunDLL main.cpl @2'结果: 显示打印机属性?'命令: rundll32.exe shell32.dll,Control_RunDLL main.cpl @3'结果: 显示字体属性?'命令: rundll32.exe shell32.dll,Control_RunDLL main.cpl @4'结果: 显示电源管理属性?'增加 modem'模块: modem.cpl'命令:rundll32.exe shell32.dll,Control_RunDLL modem.cpl,,add'多媒体'模块: mmsys.cpl'命令: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,0'结果: 声音?'命令: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,1'结果: 视频?'命令: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,2'结果: 声音 MIDI?'命令: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,3'结果:CD/音乐。'命令: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,4'结果: 高级?'命令: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl @1'结果: 声音?'网络'模块: netcpl.cpl'命令:rundll32.exe shell32.dll,Control_RunDLL netcpl.cpl'打开方式窗口(Open With)'模块: shell32.dll'命令:rundll32.exe shell32.dll,OpenAs_RunDLL path/filename'口令'模块: password.cpl'命令: rundll32.exe shell32.dll,Control_RunDLL password.cpl'区域设置'模块: intl.cpl'命令: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,0'结果: 区域设置?'命令: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,1'结果: 数字格式设置?'命令: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,2'结果: 金额格式设置?'命令: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,3'结果: 时间格式设置?'命令: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,4'结果: 日期格式设置?'屏幕保护'模块: appwiz.cpl'命令: rundll32.exe desk.cpl,InstallScreenSaver c:/win/system/Flying Windows.scr'结果: 安装屏幕保护并显示预览属性页?'系统设置'模块: sysdm.cpl'命令: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,0'结果: 显示常规设置?'命令: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,1'结果: 显示设备管理设置?'命令: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,2'结果: 显示硬件设置?'命令: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,3'结果: 显示性能设置?'IE4 设置'模块: inetcpl.cpl'命令: rundll32.exe shell32.dll,Control_RunDLL inetcpl.cpl
--------------------------------------------------------------------------------怎样检查声卡的存在Declare Function auxGetNumDevs% Lib "MMSYSTEM" ()' In the appropriate routine:Dim i As Integeri = auxGetNumDevs()If i > 0 Then ' There is at least one sound card on the systemMsgBox "A Sound Card has been detected."Else ' auxGetNumDevs returns a 0 if there is no sound cardMsgBox "There is no Sound Card on this system."End If'---------------------------------------------------如何用API及MMSYSTEM.DLL播放AVI文件Declare Function mciSendString& Lib "MMSYSTEM" (ByVal pstrCommand$, ByVal lpstrReturnStr As Any, ByVal wReturnLen%, ByVal CallBack%)'Add this code to the appropriate event:Dim CmdStr$Dim ReturnVal&' Modify path and filename as necessaryCmdStr$ = "play G:/VFW_CINE/AK1.AVI"ReturnVal& = mciSendString(CmdStr$, 0&, 0, 0&)' To play the AVI 'fullscreen' append to CmdStr$:CmdStr$ = "play G:/VFW_CINE/AK1.AVI fullscreen"'----------------------------------------------------'如何从"SOUND.DRV"中提取声音Declare Function OpenSound% Lib "sound.drv" ()Declare Function VoiceQueueSize% Lib "sound.drv" (ByVal nVoice%, ByVal nByteS)Declare Function SetVoiceSound% Lib "sound.drv" (ByVal nSource%, ByVal Freq&, ByVal nDuration%)Declare Function StartSound% Lib "sound.drv" ()Declare Function CloseSound% Lib "sound.drv" ()Declare Function WaitSoundState% Lib "sound.drv" (ByVal State%)' Add this routine, to be used with SirenSound1 routineSub Sound(ByVal Freq As Long, ByVal Duration As Integer)Dim s As Integer' Shift frequency to high byte.Freq = Freq * 2 ^ 16s = SetVoiceSound(1, Freq, Duration)s = StartSound()While (WaitSoundState(1) <> 0): WendEnd Sub' Here are the 4 sound routines:'* Attention Sound #1 *Sub AttenSound1()Dim Succ, s As IntegerSucc = OpenSound()s = SetVoiceSound(1, 1500 * 2 ^ 16, 50)s = SetVoiceSound(1, 1000 * 2 ^ 16, 50)s = SetVoiceSound(1, 1500 * 2 ^ 16, 100)s = SetVoiceSound(1, 1000 * 2 ^ 16, 100)s = SetVoiceSound(1, 800 * 2 ^ 16, 40)s = StartSound()While (WaitSoundState(1) <> 0): WendSucc = CloseSound()End Sub'* Click Sound #1 *Sub ClickSound1()Dim Succ, s As IntegerSucc = OpenSound()s = SetVoiceSound(1, 200 * 2 ^ 16, 2)s = StartSound()While (WaitSoundState(1) <> 0): WendSucc = CloseSound()End Sub'* Error Sound #1 *Sub ErrorSound1()Dim Succ, s As IntegerSucc = OpenSound()s = SetVoiceSound(1, 200 * 2 ^ 16, 150)s = SetVoiceSound(1, 100 * 2 ^ 16, 100)s = SetVoiceSound(1, 80 * 2 ^ 16, 90)s = StartSound()While (WaitSoundState(1) <> 0): WendSucc = CloseSound()End Sub'* SirenSound #1 *Sub SirenSound1()Dim Succ As IntegerDim j As LongSucc = OpenSound()For j = 440 To 1000 Step 5Call Sound(j, j / 100)Next jFor j = 1000 To 440 Step -5Call Sound(j, j / 100)Next