当我整理古老文件的时候挖出了18年前设计的DOS版的简单联系簿程式的时候,不禁感叹岁月不留人。为了避免这古老的程式湮灭在漫漫的时间长河里,我决定把它贴在网站大公开,权当教学样本了。
由于MS-DOS 6.22以及QuickBASIC实在太久远了,Windows XP 32位版也许还能直接运行由QuickBASCI创造出来的16位程式,但64位版的Windows上干脆只能用虚拟机器(Virtual PC)或者DOSBox了。

废话表过,言归正传,让我们看看这古老程式的源码。
该程式用QuickBASIC编写,储存为BAS文件。
' Contribute by Republic of Galaxy Community (Malaysia) at 1999 ' Undertake by Hidden Laboratory Community (Malaysia) at 2009 ' 星海共和国社区(马来西亚)呈现于1999年 ' 由被隐藏的实验室(马来西亚)承续于2009年 ' ' Program Name: Simple Contact Book for DOS ' 程式名称:DOS版简单联系簿 ' ' English: ' This sample is for education purpose, demonstrate ' how to save variable's into CSV text file. ' you may free to use, distribute and modify. ' ' Chinese (GB2312): ' 此样本用于教育目的,示范如何把那些变量储存进CSV文本文件。 ' 你可以自由的使用、分发以及修改。 ' --- this area generate by Microsoft QuickBASIC --- ' --- 这区域是由微软QuickBASIC生成的 --- DECLARE SUB WelcomeMessage (TotalRecord%) DECLARE SUB DisplayBoard (vName$, vTel$, vEmail$, vRoad$, vPostcode$, vTown$, vState$, RecordNo%) DECLARE SUB InputRecord (MyFile$) DECLARE FUNCTION KeyPressed! () ' --- end of auto generate --- ' --- 自动生成结束 --- ' declare some variable ' 宣告一些变量 DIM i% DIM MyName$, Tel$, Email$, Road$, Postcode$, Town$, State$ DIM m%, n% DIM WelcomeFlag, InnerEnd, LoopEnd, GrandEnd, ProgramEnd DIM FileName$ ' Setup target file ' 设置目标文件 FileName$ = "record1.csv" ' Program start... ' 程式开始。。。 CLS ' Auto create blank file if target file no existed ' 当目标文件不存在,自动创造空白文件 OPEN FileName$ FOR APPEND AS #1 CLOSE #1 LOCATE 11, 25 PRINT "Please wait..." WelcomeFlag = -1 ProgramEnd = 0 WHILE NOT ProgramEnd i% = 0 OPEN FileName$ FOR INPUT AS #1 ' Loading file into memory ' 加载文件到记忆 WHILE NOT EOF(1) i% = i% + 1 INPUT #1, MyName$(i%), Tel$(i%), Email$(i%), Road$(i%), Postcode$(i%), Town$(i%), State$(i%) WEND CLOSE #1 ' if WelcomeFlag is true, call the welcome message ' 若WelcomeFlag为真,呼叫欢迎信息 IF WelcomeFlag THEN CALL WelcomeMessage(i%) ' Set WelcomeFlag as false ' 设置WelcomeFlag为贾 WelcomeFlag = 0 ' Call that display board subroutine ' 呼叫展示板子程序 CALL DisplayBoard(MyName$(i%), Tel$(i%), Email$(i%), Road$(i%), Postcode$(i%), Town$(i%), State$(i%), i%) GrandEnd = 0 WHILE NOT GrandEnd LoopEnd = 0 WHILE NOT LoopEnd SELECT CASE KeyPressed ' When ENTER key pressed ' 当 ENTER 键被按下 CASE 13 CALL InputRecord(FileName$) LoopEnd = -1 GrandEnd = -1 ' When ESC key pressed ' 当 ESC 键被按下 CASE 27 LoopEnd = -1 GrandEnd = -1 ProgramEnd = -1 ' When + key pressed ' 当 + 键被按下 CASE 43 m% = m% + 1 IF m% > i% THEN m% = 1 END IF CALL DisplayBoard(MyName$(m%), Tel$(m%), Email$(m%), Road$(m%), Postcode$(m%), Town$(m%), State$(m%), m%) ' When - key pressed ' 当 - 键被按下 CASE 45 m% = m% - 1 IF m% < 1 THEN m% = i% END IF CALL DisplayBoard(MyName$(m%), Tel$(m%), Email$(m%), Road$(m%), Postcode$(m%), Town$(m%), State$(m%), m%) ' When X key pressed ' 当 X 键被按下 CASE 88, 120 LOCATE 21, 1 PRINT "Delete the record?(Y/N)" InnerEnd = 0 WHILE NOT InnerEnd SELECT CASE KeyPressed ' When Y key pressed ' 当 Y 键被按下 CASE 89, 121 ' Make current variable to empty if wish to delete current contact record ' 把想要删除的当前联系纪录的变量变成空 MyName$(m%) = "" OPEN FileName$ FOR OUTPUT AS #1 FOR n% = 0 TO i% ' Only variable MyNames$ with content will rewrite into CSV text file ' Record wish to delete has been exclude from write back ' 只有有内容的变量MyNames$才会被重写进CSV文本文件 ' 想要删除的纪录将会被排除在外不会写回 IF LEN(MyName$(n%)) > 0 THEN WRITE #1, MyName$(n%), Tel$(n%), Email$(n%), Road$(n%), Postcode$(n%), Town$(n%), State$(n%) END IF NEXT CLOSE #1 LOCATE 22, 1 PRINT "Deleting completed!" SLEEP 2 InnerEnd = -1 LoopEnd = -1 GrandEnd = -1 ' Erase all content inside variable ' Because variable GrandEnd has been set to true ' They will go back to loop and reload new record from CSV file ' 删除全部变量中的内容 ' 由于变量GrandEnd已经设置为真 ' 将会回到循环并重新自CSV文件加载 ERASE MyName$, Tel$, Email$, Road$, Postcode$, Town$, State$ ' When N key pressed ' 当 N 键被按下 CASE 78, 110 CALL DisplayBoard(MyName$(m%), Tel$(m%), Email$(m%), Road$(m%), Postcode$(m%), Town$(m%), State$(m%), m%) InnerEnd = -1 END SELECT WEND END SELECT WEND WEND WEND CLS END ' Program End... ' 程式结束。。。 SUB DisplayBoard (vName$, vTel$, vEmail$, vRoad$, vPostcode$, vTown$, vState$, RecordNo%) ' Subroutine to display main page ' 展示主页的子程序 CLS LOCATE 2, 1 PRINT "Name: "; vName$ LOCATE 4, 1 PRINT "Telephone: "; vTel$ LOCATE 6, 1 PRINT "E-Mail: "; vEmail$ LOCATE 8, 1 PRINT "Address:" LOCATE 10, 10 PRINT vRoad$ LOCATE 12, 10 PRINT vPostcode$ LOCATE 14, 10 PRINT vTown$ LOCATE 16, 10 PRINT vState$ LOCATE 18, 1 PRINT "Record Number:"; RecordNo% LOCATE 23, 1 PRINT "<Exit=ESC> <Seek=+ or -> <Input=ENTER> <Delete=x>" END SUB SUB InputRecord (MyFile$) ' Subroutine to enter new contact ' 输入新联系的子程序 ' Declare some variable for this subroutine ' 宣告一些变量于这个子程序 DIM uName$, uTel$, uEmail$, uRoad$, uPostcode$, uTown$, uState$ DIM vName$, vTel$, vEmail$, vRoad$, vPostcode$, vTown$, vState$ DIM LoopEnd, FlagEnd FlagEnd = 0 WHILE NOT FlagEnd CLS LOCATE 2, 10 PRINT "Process record..." LOCATE 4, 10 LINE INPUT "Name: "; vName$ LOCATE 6, 10 LINE INPUT "Telephone number: "; vTel$ LOCATE 8, 10 LINE INPUT "E-mail: "; vEmail$ LOCATE 10, 10 PRINT "Address" LOCATE 10, 20 LINE INPUT "Road: "; vRoad$ LOCATE 12, 20 LINE INPUT "Postcode: "; vPostcode$ LOCATE 14, 20 LINE INPUT "Town: "; vTown$ LOCATE 16, 20 LINE INPUT "State: "; vState$ ' Convert all inputed data to capital character or small capital character ' 把全部所输入的数据转成大号字或小号字 uName$ = UCASE$(vName$) uTel$ = UCASE$(vTel$) uEmail$ = LCASE$(vEmail$) uRoad$ = UCASE$(vRoad$) uPostcode$ = UCASE$(vPostcode$) uTown$ = UCASE$(vTown$) uState$ = UCASE$(vState$) LOCATE 22, 10 PRINT "Save Record?(Y/N)" LoopEnd = 0 WHILE NOT LoopEnd SELECT CASE KeyPressed ' When Y key pressed ' 当 Y 键被按下 CASE 89, 121 ' Write and add all inputed data into CSV text file OPEN MyFile$ FOR APPEND AS #1 WRITE #1, uName$, uTel$, uEmail$, uRoad$, uPostcode$, uTown$, uState$ CLOSE #1 LoopEnd = -1 ' When N key pressed ' 当 N 键被按下 CASE 78, 110 LoopEnd = -1 END SELECT WEND LOCATE 23, 10 PRINT "Continue to Input?(Y/N)" LoopEnd = 0 WHILE NOT LoopEnd SELECT CASE KeyPressed ' When Y key pressed ' 当 Y 键被按下 CASE 89, 121 LoopEnd = -1 ' When N key pressed ' 当 N 键被按下 CASE 78, 110 FlagEnd = -1 LoopEnd = -1 END SELECT WEND CLS WEND END SUB FUNCTION KeyPressed ' function to capture signal from keyboard ' 捕捉键盘讯号的功能 DIM ScanKey$ ScanKey$ = INKEY$ IF ScanKey$ <> "" THEN ' Transform keyboard signal to integer ASCII number ' 转换键盘讯号到ASCII整数号码 KeyPressed = ASC(ScanKey$) ScanKey$ = "" ELSE KeyPressed = 0 END IF END FUNCTION SUB WelcomeMessage (TotalRecord%) ' Subroutine to display welcome message ' 展示欢迎信息的子程序 CLS PRINT "Simple Contact Book for DOS 1.6" PRINT "Hidden Laboratory Community 1999" LOCATE 22, 1 PRINT "Total record is "; TotalRecord% LOCATE 23, 1 PRINT "Please any key to continue" LOCATE 23, 1 PRINT "Please any key to continue" WHILE INKEY$ = "" WEND END SUB
只是啊,一部份超有钱的用户使用的电脑高于Intel Pentium 4,更大的记忆体,最新的操作系统Windows 10,绝对不可能安装到虚拟机器,DOSBox也不是什么人都会配置。所以对他们来说运行QuickBASIC几乎不可能。
不用担心!我为了这个情形把这古老的程式给“进化”成Visual BASIC 2015.Net了,大家可以花几个月的时间下载社区版Visual Studio 2015.Net ISO文件并安装到电脑然后试试运行以下的代码,或花一点钱拜托大城市的人寄DVD光盘来。
(社区版Visual Studio 2017.Net目前没有ISO文件,依靠微软安装器下载的话以马来西亚网络的状态几乎是不可能的任务。)

就像图片展示的效果那样,外表看起来虽然与DOS版的程式毫无差别,但内核可是.Net架构驱动且64位的,无法运行在真正的MS-DOS环境里。
' Contribute by Republic of Galaxy Community (Malaysia) at 1999
' Undertake by Hidden Laboratory Community (Malaysia) at 2009
' 星海共和国社区(马来西亚)呈现于1999年
' 由被隐藏的实验室(马来西亚)承续于2009年
'
' Program Name: Simple Contact Book for Windows Console (Remastered 2017)
' 程式名称:Windows提示符版简单联系簿(2017年复刻版)
'
' English:
' This retro program is for education purpose, and has been
' evolved from QuickBASIC to Visual BASIC.Net 2015
' same as previous version, this program demonstrate
' how to save variable's into CSV text file
' you may free to use, distribute and modify.
'
' Chinese (GB2312):
' 此复古程式用于教育目的,已经从
' QuickBASIC进化到Visual BASIC.Net 2015
' 与之前的版本一样,这程式示范如何把那些变量储存进CSV文本文件。
' 你可以自由的使用、分发以及修改。
Module Module1
Sub Main()
' declare some variable
' 宣告一些变量
Dim i%, m%, n%, LoadNumber%
Dim WelcomeFlag, InnerEnd, LoopEnd, GrandEnd, ProgramEnd As Boolean
Dim FileName$
Dim MyField$()
' Setup target file
' 设置目标文件
FileName$ = "record1.csv"
' Program start...
' 程式开始。。。
System.Console.Clear()
' Auto create blank file if target file no existed
' 当目标文件不存在,自动创造空白文件
FileSystem.FileOpen(1, FileName$, OpenMode.Append)
FileSystem.FileClose(1)
System.Console.SetCursorPosition(24, 10)
System.Console.Write("Please wait...")
WelcomeFlag = True
ProgramEnd = False
While Not ProgramEnd
' Because INPUT statement at VB.Net is different behavior
' with QuickBASIC, so INPUT statement no longer used in
' this program
' 由于VB.Net里的INPUT声明与QuickBASIC的INPUT声明行为有差异
' 所以本程式不再使用INPUT声明
Dim MyReader As New FileIO.TextFieldParser(FileName$)
MyReader.TextFieldType = FileIO.FieldType.Delimited
MyReader.SetDelimiters(",")
MyReader.HasFieldsEnclosedInQuotes = True
i% = IO.File.ReadAllLines(FileName$).Length
' After established text field parser and get lines count number
' assign variable with number of dimension according lines count number
' 建立完文本字段解析器并获得行数之后
' 根据行数给变量分配维数
Dim MyName$(i%)
Dim Tel$(i%)
Dim Email$(i%)
Dim Road$(i%)
Dim Postcode$(i%)
Dim Town$(i%)
Dim State$(i%)
' Loading file into memory
' 加载文件到记忆
For LoadNumber% = 1 To i% Step 1
MyField$ = MyReader.ReadFields()
MyName$(LoadNumber%) = MyField$(0)
Tel$(LoadNumber%) = MyField$(1)
Email$(LoadNumber%) = MyField$(2)
Road$(LoadNumber%) = MyField$(3)
Postcode$(LoadNumber%) = MyField$(4)
Town$(LoadNumber%) = MyField$(5)
State$(LoadNumber%) = MyField$(6)
Next
MyReader.Close()
' if WelcomeFlag is true, call the welcome message
' 若WelcomeFlag为真,呼叫欢迎信息
If WelcomeFlag Then Call WelcomeMessage(i%)
' Set WelcomeFlag as false
' 设置WelcomeFlag为贾
WelcomeFlag = False
' Call that display board subroutine
' 呼叫展示板子程序
Call DisplayBoard(MyName$(i%), Tel$(i%), Email$(i%), Road$(i%), Postcode$(i%), Town$(i%), State$(i%), i%)
GrandEnd = False
While Not GrandEnd
LoopEnd = False
While Not LoopEnd
If System.Console.KeyAvailable Then
Select Case System.Console.ReadKey(True).Key
' When ENTER key pressed
' 当 ENTER 键被按下
Case System.ConsoleKey.Enter
Call InputRecord(FileName$)
LoopEnd = True
GrandEnd = True
' When ESC key pressed
' 当 ESC 键被按下
Case System.ConsoleKey.Escape
LoopEnd = True
GrandEnd = True
ProgramEnd = True
' When + key pressed
' 当 + 键被按下
Case System.ConsoleKey.Add
m% = m% + 1
If m% > i% Then
m% = 1
End If
Call DisplayBoard(MyName$(m%), Tel$(m%), Email$(m%), Road$(m%), Postcode$(m%), Town$(m%), State$(m%), m%)
' When - key pressed
' 当 - 键被按下
Case System.ConsoleKey.Subtract
m% = m% - 1
If m% < 1 Then
m% = i%
End If
Call DisplayBoard(MyName$(m%), Tel$(m%), Email$(m%), Road$(m%), Postcode$(m%), Town$(m%), State$(m%), m%)
' When X key pressed
' 当 X 键被按下
Case System.ConsoleKey.X
System.Console.SetCursorPosition(0, 20)
System.Console.Write("Delete the record?(Y/N)")
InnerEnd = False
While Not InnerEnd
If System.Console.KeyAvailable Then
Select Case System.Console.ReadKey(True).Key
' When Y key pressed
' 当 Y 键被按下
Case System.ConsoleKey.Y
' Make current variable to empty if wish to delete current contact record
' 把想要删除的当前联系纪录的变量变成空
MyName$(m%) = ""
FileSystem.FileOpen(1, FileName$, OpenMode.Output)
For n% = 0 To i%
' Only variable MyNames$ with content will rewrite into CSV text file
' Record wish to delete has been exclude from write back
' 只有有内容的变量MyNames$才会被重写进CSV文本文件
' 想要删除的纪录将会被排除在外不会写回
If Len(MyName$(n%)) > 0 Then
FileSystem.WriteLine(1, MyName$(n%), Tel$(n%), Email$(n%), Road$(n%), Postcode$(n%), Town$(n%), State$(n%))
End If
Next
FileSystem.FileClose(1)
System.Console.SetCursorPosition(0, 21)
System.Console.Write("Deleting completed!")
System.Threading.Thread.Sleep(2000)
InnerEnd = True
LoopEnd = True
GrandEnd = True
' Erase all content inside variable
' Because variable GrandEnd has been set to true
' They will go back to loop and reload new record from CSV file
' 删除全部变量中的内容
' 由于变量GrandEnd已经设置为真
' 将会回到循环并重新自CSV文件加载
Erase MyName$, Tel$, Email$, Road$, Postcode$, Town$, State$
' When N key pressed
' 当 N 键被按下
Case System.ConsoleKey.N
Call DisplayBoard(MyName$(m%), Tel$(m%), Email$(m%), Road$(m%), Postcode$(m%), Town$(m%), State$(m%), m%)
InnerEnd = True
End Select
End If
End While
End Select
End If
End While
End While
End While
System.Console.Clear()
End
' Program End...
' 程式结束。。。
End Sub
Sub DisplayBoard(vName$, vTel$, vEmail$, vRoad$, vPostcode$, vTown$, vState$, RecordNo%)
' Subroutine to display main page
' 展示主页的子程序
System.Console.Clear()
System.Console.SetCursorPosition(0, 1)
System.Console.Write("Name: {0}", vName$)
System.Console.SetCursorPosition(0, 3)
System.Console.Write("Telephone: {0}", vTel$)
System.Console.SetCursorPosition(0, 5)
System.Console.Write("E-Mail: {0}", vEmail$)
System.Console.SetCursorPosition(0, 7)
System.Console.Write("Address:")
System.Console.SetCursorPosition(9, 9)
System.Console.Write(vRoad$)
System.Console.SetCursorPosition(9, 11)
System.Console.Write(vPostcode$)
System.Console.SetCursorPosition(9, 13)
System.Console.Write(vTown$)
System.Console.SetCursorPosition(9, 15)
System.Console.Write(vState$)
System.Console.SetCursorPosition(0, 17)
System.Console.Write("Record Number: {0}", RecordNo%)
System.Console.SetCursorPosition(0, 22)
System.Console.Write("<Exit=ESC> <Seek=+ or -> <Input=ENTER> <Delete=x>")
End Sub
Sub InputRecord(MyFile$)
' Subroutine to enter new contact
' 输入新联系的子程序
' Declare some variable for this subroutine
' 宣告一些变量于这个子程序
Dim uName$, uTel$, uEmail$, uRoad$, uPostcode$, uTown$, uState$
Dim vName$, vTel$, vEmail$, vRoad$, vPostcode$, vTown$, vState$
Dim LoopEnd, FlagEnd As Boolean
FlagEnd = False
While Not FlagEnd
System.Console.Clear()
System.Console.SetCursorPosition(9, 1)
System.Console.Write("Process record...")
System.Console.SetCursorPosition(9, 3)
System.Console.Write("Name: ")
vName$ = System.Console.ReadLine()
System.Console.SetCursorPosition(9, 5)
System.Console.Write("Telephone number: ")
vTel$ = System.Console.ReadLine()
System.Console.SetCursorPosition(9, 7)
System.Console.Write("E-mail: ")
vEmail$ = System.Console.ReadLine()
System.Console.SetCursorPosition(9, 9)
System.Console.Write("Address")
System.Console.SetCursorPosition(19, 9)
System.Console.Write("Road: ")
vRoad$ = System.Console.ReadLine()
System.Console.SetCursorPosition(19, 11)
System.Console.Write("Postcode: ")
vPostcode$ = System.Console.ReadLine()
System.Console.SetCursorPosition(19, 13)
System.Console.Write("Town: ")
vTown$ = System.Console.ReadLine()
System.Console.SetCursorPosition(19, 15)
System.Console.Write("State: ")
vState$ = System.Console.ReadLine()
' Convert all inputed data to capital character or small capital character
' 把全部所输入的数据转成大号字或小号字
uName$ = UCase(vName$)
uTel$ = UCase(vTel$)
uEmail$ = LCase(vEmail$)
uRoad$ = UCase(vRoad$)
uPostcode$ = UCase(vPostcode$)
uTown$ = UCase(vTown$)
uState$ = UCase(vState$)
System.Console.SetCursorPosition(9, 21)
System.Console.Write("Save Record?(Y/N)")
LoopEnd = False
While Not LoopEnd
If System.Console.KeyAvailable Then
Select Case System.Console.ReadKey(True).Key
' When Y key pressed
' 当 Y 键被按下
Case System.ConsoleKey.Y
' Write and add all inputed data into CSV text file
' 把所有输入内容写进CSV文本文件
FileSystem.FileOpen(1, MyFile$, OpenMode.Append)
FileSystem.WriteLine(1, uName$, uTel$, uEmail$, uRoad$, uPostcode$, uTown$, uState$)
FileSystem.FileClose(1)
LoopEnd = True
' When N key pressed
' 当 N 键被按下
Case System.ConsoleKey.N
LoopEnd = True
End Select
End If
End While
System.Console.SetCursorPosition(9, 22)
System.Console.Write("Continue to Input?(Y/N)")
LoopEnd = False
While Not LoopEnd
If System.Console.KeyAvailable Then
Select Case System.Console.ReadKey(True).Key
' When Y key pressed
' 当 Y 键被按下
Case System.ConsoleKey.Y
LoopEnd = True
' When N key pressed
' 当 N 键被按下
Case System.ConsoleKey.N
FlagEnd = True
LoopEnd = True
End Select
End If
End While
System.Console.Clear()
End While
End Sub
Sub WelcomeMessage(TotalRecord%)
' Subroutine to display welcome message
' 展示欢迎信息的子程序
System.Console.Clear()
System.Console.Write("Simple Contact Book for Windows Console 1.6")
System.Console.Write("Hidden Laboratory Community 2017")
System.Console.SetCursorPosition(0, 21)
System.Console.Write("Total record is {0}", TotalRecord%)
System.Console.SetCursorPosition(0, 22)
System.Console.Write("Please any key to continue")
System.Console.ReadKey(True)
End Sub
End Module
同一个古老程式,两种年代相差18年的代码,呵呵,你们可以从中学习一下BASIC系列语言的进化方向。

