简介《Advanced Visual Basic高级VB编程》是一套由 VB 专家 Matthew Curland 编写的高阶学习资料包内容覆盖面向对象类设计、事件处理与异常捕获、多线程调度、ADO.NET 数据库访问、COM 自动化、网络与 XML/Web 服务、性能优化还涉及反射、泛型、委托、Lambda 表达式等进阶特性适合已有 VB 语法基础、希望向中高级开发者进阶的程序员。压缩包共 327 个文件大小约 10.33MB类型非常齐全81 个 bas 公共模块提供通用函数34 个 cls 类模块构建业务对象33 个 vbp 工程文件组织项目15 个 frm 窗体承载界面另含大量 odl、ctl、ocx 控件与类型库资源以及 h 头文件、rc 脚本、tlb 类型库和 8 个 exe 演示程序并附 PDF 电子书便于对照编译与理解。目前已有 354 人学习下载。通过这套资料读者能深入掌握高级 VB 的底层运行机制获得可直接编译的完整工程、可复用的类与控件实现还能借鉴作者在委托、反射、线程泵、内存分配、异常处理等方面的封装思路与排错经验尤其适合在 Windows 平台从事 VB 开发或维护老项目的技术人员作为案头参考。 VB这门语言聊起来总有人要问一句这年头还有人用说实话真正做工业自动化、维护老旧系统、写桌面办公辅助工具的人听到这个问题只能笑笑。我手头到现在还维护着七八套VB6写的老系统跑在客户的工控机和Windows XP老机器上一跑就是十几年没动过。所谓“高级VB编程”听起来像个老古董词汇但它真正的价值恰恰在于当别人都追新框架、新语言的时候你能用最少的依赖、最直接的方式把一件具体的事情干成。这篇文章不聊控件摆放也不讲For循环怎么写专门说那些让VB真正“高级”起来的东西调用Windows API、用SendInput模拟键盘输入、用MSComm控件啃串口、用Inet控件下载文件再到WinCC画面跳转这类工业工程集成实战。每一段我都会给可复制的代码再讲清楚背后的原理和踩过的坑。无论你是被迫接手老系统还是在工控、自动化行业里写配套工具这份内容都值得存一份。1. 高级VB编程到底“高级”在哪1.1 VB没死只是换了阵地很多人对VB的印象停留在“拖几个控件、写两行事件”的入门阶段这确实是VB最友好的一面。但真正让VB能在生产环境里活二十年的是它和Windows底层、各种硬件设备、工业组态软件的深度绑定。VB6编译出来的程序不依赖运行时环境拷贝个exe就能跑这一点在工控机上特别值钱——客户现场没有网、没有权限装新环境你拿VB写个小工具U盘拷过去双击就能用别的语言还真不一定做得到。同时VB6的ActiveX控件体系虽然老但生态积累极其深厚。MSComm串口控件、Inet网络控件、Winsock控件、MSFlexGrid表格控件都是当年微软官方出品稳定性和文档完整度比现在很多开源库都好。高级VB编程的核心不是语法技巧而是知道该找哪个控件、哪个API以及怎么把它们组合起来解决实际问题。1.2 三个真正的进阶方向我自己把高级VB编程分成三条线覆盖了绝大多数实际需求系统级操作通过Declare声明调用Windows API比如模拟键盘鼠标、读写注册表、操作窗口句柄、枚举进程。这类操作突破了VB自身的能力边界是“高级感”最直接的来源。控件深度应用不满足于MSComm通信、Inet下载这类基本用法而是理解控件的事件机制、异步模型和状态机写出可靠稳定的代码。跨系统集成VB程序很少孤立运行要跟数据库、Excel、PLC、组态软件比如WinCC对话。这部分考验的是对接口协议和数据格式的理解。这三条线就是这篇文章的主干。1.3 选型思考什么时候该选VB我并不是说VB万能。新项目让我选我也会优先考虑C#、Python。但在这些场景下VB依然是合理甚至最优的选择老系统的增量维护、工控机上快速写辅助工具、需要和WinCC/Siemens组态深度集成的项目、以及团队成员只会VB的环境。说白了高级VB编程是一种“边界能力”它的意义不在于炫技而在于让你在特定场景下不掉链子。掌握了这些再老的语言也能成为你的底牌。2. 系统级操作API声明与SendInput模拟键盘输入2.1 从声明开始VB如何触碰Windows底层VB6本身能做的系统级操作非常有限但它提供了一个后门Declare声明。通过Declare可以调用user32.dll、kernel32.dll里的标准API函数相当于直接跟Windows内核打交道。比如下面这句Private Declare Function keybd_event Lib user32 _ (ByVal bVk As Byte, _ ByVal bScan As Byte, _ ByVal dwFlags As Long, _ ByVal dwExtraInfo As Long) As Long声明之后VB里就能像调用普通函数一样调用keybd_event。关键点在于参数类型必须严格对应API定义Byte对应BYTELong对应LONG字符串如果是ByVal String则对应LPCTSTR。这里最容易出问题的是32位和64位差异VB6是纯32位程序在64位系统上通过SysWOW64兼容层运行绝大多数user32 API都能正常调用但如果声明了错误的参数类型轻则返回错误值重则直接导致程序崩溃。2.2 用SendInput模拟键盘输入的完整代码keybd_event虽然简单但属于旧式模拟方案Windows官方推荐的是SendInput它支持输入事件合并、更精确的时间戳也更容易被目标程序识别。在VB6里SendInput的结构体声明稍微繁琐因为VB6不支持C语言的Union共用体所以常用的做法是退一步用keybd_event搞定大多数需求核心代码如下Private Declare Function keybd_event Lib user32 _ (ByVal bVk As Byte, _ ByVal bScan As Byte, _ ByVal dwFlags As Long, _ ByVal dwExtraInfo As Long) As Long Private Const KEYEVENTF_KEYUP H2 Private Const VK_CONTROL H11 Private Const VK_MENU H12 Alt键 Private Const VK_S H53 Private Sub SendCtrlS() 模拟 Ctrl S keybd_event VK_CONTROL, 0, 0, 0 keybd_event VK_S, 0, 0, 0 keybd_event VK_S, 0, KEYEVENTF_KEYUP, 0 keybd_event VK_CONTROL, 0, KEYEVENTF_KEYUP, 0 End Sub Private Sub SendAltTab() 模拟 Alt Tab 切换窗口 keybd_event VK_MENU, 0, 0, 0 keybd_event VK_TAB, 0, 0, 0 keybd_event VK_TAB, 0, KEYEVENTF_KEYUP, 0 keybd_event VK_MENU, 0, KEYEVENTF_KEYUP, 0 End Sub这套代码的取值来自Windows SDK的虚拟键码表VK_CONTROL是0x11VK_MENU是0x12VK_S是0x53。按键按下和抬起必须成对发送否则目标程序会认为按键一直被按住可能引发连带操作。实际项目里我用这套代码做收银系统的自动对账、连续录入单据稳定性很高。2.3 API调用最容易踩的坑参数类型错误这是最大的坑。VB里Integer是16位C语言的INT是32位如果API声明用错类型传进去的数据就是错乱的。拿不准的时候把API声明和MSDN文档对照着看。ByVal和ByRef混用API声明里没有明确说明时基本都是ByVal传值。把ByVal漏掉API就读到指针地址的值轻则返回垃圾数据重则蓝屏死机。32位兼容性现在新出的电脑基本64位VB6程序跑在WOW64层大部分API没问题但个别涉及驱动、内存直接访问的API会受限。遇到诡异的崩溃先怀疑API兼容性。注意模拟按键类功能只应用在你自己有权限操作的系统和软件上不要拿去做绕过验证之类的操作合规永远是第一位。3. 串口通信实战MSComm控件收发数据3.1 MSComm控件的核心属性与事件串口通信是工控领域绕不开的硬需求。VB6时代最成熟的方案就是微软的MSComm控件Microsoft Comm Control 6.0。它的使用思路非常简洁设置端口号、波特率、数据位、校验位、停止位打开端口然后事件驱动式收数据。核心属性如下CommPort端口号1对应COM12对应COM2。如果超过9要用\\.\COM10这类写法否则会打开失败。Settings通信参数格式是“波特率,校验位,数据位,停止位”比如9600,N,8,1代表9600波特率、无校验、8位数据、1位停止位。InputMode接收数据模式comInputModeText是文本模式comInputModeBinary是二进制模式。读取设备返回的16进制数据时必须用二进制模式。RThreshold接收缓冲区里有多少字节时触发OnComm事件。设为1表示收到1个字节就触发设为0则关闭事件。PortOpen打开或关闭串口。3.2 一个完整的串口收发示例下面是一个我最常用的串口收发模板它做的事情是打开COM1发送一个“AT”指令然后接收设备返回的所有文本并显示在文本框里。这块代码我在多个条码秤、工业仪表项目中直接套用过。Private Sub Form_Load() 初始化串口 MSComm1.CommPort 1 MSComm1.Settings 9600,N,8,1 MSComm1.InputLen 0 读取接收缓冲区全部内容 MSComm1.RThreshold 1 接收缓冲区有1个字节就触发事件 On Error Resume Next MSComm1.PortOpen True If Err.Number 0 Then MsgBox 串口打开失败请检查端口是否被占用。, vbCritical Exit Sub End If On Error GoTo 0 MSComm1.Output AT vbCr 发送AT指令按设备协议加回车 End Sub Private Sub MSComm1_OnComm() 串口事件处理 Dim strData As String Select Case MSComm1.CommEvent Case 2 comEvReceive 接收事件 strData MSComm1.Input Text1.Text Text1.Text strData End Select End Sub Private Sub Form_Unload(Cancel As Integer) 退出时一定要关串口否则其他程序无法使用 If MSComm1.PortOpen Then MSComm1.PortOpen False End If End Sub这里有个细节InputLen设为0才能一次性把缓冲区数据全部读出否则只会读回InputLen指定长度的字节。RThreshold设为1是“收到一个字节就通知”对速度要求不高的文本协议足够用。如果对接的仪表一次返回几十上百个字节而你的程序处理不过来可以适当把RThreshold调大减少事件触发频率。3.3 串口调试的实操心得MSComm最让人头疼的是没有调试信息串口打不开、收不到数据很难直接定位。我的经验是先用串口调试助手确认设备本身有输出排除设备端问题。再确认端口号是否被占用VB6的MSComm对端口占用报错处理不够友好建议加On Error保护。处理二进制数据时直接把收到的字节转成十六进制字符串显示避免乱码问题。十六进制转换函数也一并贴出来Private Function ByteToHex(bytes() As Byte) As String Dim i As Long Dim s As String For i LBound(bytes) To UBound(bytes) s s Right(0 Hex(bytes(i)), 2) Next ByteToHex Trim(s) End Function这套组合基本能解决90%的串口调试问题。剩下的10%往往是设备端协议没对齐比如波特率不一致、帧头帧尾解析错误那就要拿着设备手册逐字节对了。4. 网络请求与文件下载Inet控件和替代方案4.1 Inet控件的典型下载流程VB6原生自带的Inet控件Microsoft Internet Transfer Control封装了HTTP、FTP等协议可以下载文件、提交表单。它最大的特点是异步工作调用Execute后程序不阻塞等服务器响应完成后触发StateChanged事件。对于下载文件这种耗时操作比直接用WinInet API方便很多。常见的下载流程是设置URL和协议然后调用Execute发起请求在StateChanged事件里监听状态码12icResponseCompleted表示响应完成。此时再用GetChunk分批读取数据避免一次性加载大文件导致内存暴涨。4.2 下载文件的完整代码Private Const icResponseCompleted 12 Private Const icByteArray 1 Private Sub Command1_Click() Inet1.Protocol icHTTP 使用HTTP协议 Inet1.URL http://example.com/files/setup.zip Inet1.Execute , GET End Sub Private Sub Inet1_StateChanged(ByVal State As Integer) Dim bData() As Byte Dim fNum As Integer If State icResponseCompleted Then 以二进制方式打开本地文件准备写入 fNum FreeFile Open D:\download\setup.zip For Binary Access Write As #fNum 分批获取数据每批64KB Dim chunkSize As Long chunkSize 65536 Do While True bData Inet1.GetChunk(chunkSize, icByteArray) If UBound(bData) 0 Then Exit Do 没有数据时数组上界为-1 Put #fNum, , bData DoEvents 保持界面响应 Loop Close #fNum MsgBox 下载完成 End If End Sub不同版本的VB对GetChunk返回值处理略不同建议循环判断数组长度而不是依赖返回的字节数。下载过程中DoEvents很重要否则界面会假死。而且Inet控件对重定向支持一般如果URL经历了302跳转可能要动态更新URL再执行一次。4.3 比Inet更省心的替代方案Inet控件的缺点是错误处理弱、超时设置不灵活、有些HTTPS站点尤其是新版TLS会握手失败。其实VB6程序下载文件还有一个更简单的办法用URLDownloadToFile这个API。它不需要控件一个函数直接搞定Private Declare Function URLDownloadToFile Lib urlmon _ Alias URLDownloadToFileA _ (ByVal pCaller As Long, _ ByVal szURL As String, _ ByVal szFileName As String, _ ByVal dwReserved As Long, _ ByVal lpfnCB As Long) As Long Private Sub DownloadFile() Dim lResult As Long lResult URLDownloadToFile(0, _ http://example.com/setup.zip, _ D:\download\setup.zip, 0, 0) If lResult 0 Then MsgBox 下载成功 Else MsgBox 下载失败错误码 Hex(lResult) End If End Sub这个API是同步调用适合文件不是特别大的场景。它内部会处理TLS握手只要系统能正常用IE打开这个链接它基本就能下载。我做升级包下载工具时优先用这个方案代码量少、稳定、好排查。5. 工程集成场景WinCC画面跳转与Office联动5.1 WinCC里用VB脚本实现按钮跳转西门子WinCC组态软件虽然是工业级SCADA平台但它的脚本引擎大量使用VBScriptVB语系所以“VB编程”这个词在工控语境下还会延伸到WinCC脚本里。最典型的场景就是单击按钮跳转到另一幅画面。在WinCC图形编辑器中给按钮的“单击鼠标”事件添加VBS脚本核心就一句话——修改画面窗口的PictureName属性Sub OnClick(ByVal Item) 跳转到主画面 Set objScreen HMIRuntime.ActiveScreen objScreen.ScreenItems(画面窗口1).PictureName 主画面.PDL End Sub如果当前画面上的画面窗口名字叫“画面窗口1”运行到这一句就会加载“主画面.PDL”。这里的要点是必须先通过HMIRuntime.ActiveScreen拿到当前画面对象再通过ScreenItems集合定位画面窗口控件。直接写ScreenItems会找不到对象因为脚本上下文还没有关联到当前画面。还有一种方式是用OpenPicture函数在WinCC的全局脚本里比较常用Sub OnClick(ByVal Item) HMIRuntime.OpenPicture 主画面.PDL, 0, 0, 850, 550, , , 1, 1 End SubOpenPicture支持指定窗口位置和尺寸适合弹出独立窗口的画面跳转。两个方案的区别在于改PictureName是在现有画面窗口内切换界面框架不变OpenPicture是打开新的画面窗口。按项目需要选择没有绝对的对错。5.2 VB操作Excel生成报表除了WinCCVB在办公自动化领域的集成能力也很强。用VB操作Excel生成报表是老传统了核心思路是通过CreateObject创建Excel.Application对象然后填充数据、保存文件。一个精简示例Dim xlApp As Object Dim xlBook As Object Dim xlSheet As Object Set xlApp CreateObject(Excel.Application) xlApp.Visible False 后台运行不显示界面 Set xlBook xlApp.Workbooks.Add Set xlSheet xlBook.Worksheets(1) xlSheet.Cells(1, 1) 编号 xlSheet.Cells(1, 2) 数值 xlSheet.Cells(2, 1) 001 xlSheet.Cells(2, 2) 100 xlBook.SaveAs D:\report.xlsx, 51 51表示xlsx格式 xlBook.Close xlApp.Quit Set xlSheet Nothing Set xlBook Nothing Set xlApp Nothing这里的重点是创建对象后的释放顺序先关工作簿、再退出应用、最后置Nothing。不按这个顺序容易导致Excel进程残留在后台占着文件不释放。另外SaveAs第二个参数51是Open XML格式的枚举值不同格式对应不同数字记不住就直接用xlWorkbookDefault常量在工程中引用Excel对象库后可用。5.3 混合场景下的VB代码组织建议在WinCC、Excel、串口、数据库这些场景混在一起时VB项目的代码组织直接决定后期维护成本。我的建议把API声明集中在单独的标准模块里不要散落在各个窗体中便于统一排查类型错误。串口、网络、文件下载这类通用功能封装成类模块向外暴露简单的方法窗体只管调用。硬编码的路径、端口号、URL统一放配置文件或用Const常量避免项目交接时到处找参数。关键操作加上错误处理VB6的On Error Resume Next不是不能用但要明确知道出错后怎么恢复。有了这套组织方式哪怕代码是“古法编程”结构依然是现代工程化的接手的人不会想骂你。6. 常见问题与排查技巧实录6.1 高频报错与处理方案速查表报错信息或现象常见原因解决办法编译错误Declare语句中不允许使用As AnyVB6不支持As Any多见于64位API声明示例去掉As Any改用具体类型如ByVal hProcess As Long运行时错误5无效的过程调用或参数串口被占用、API传入空指针检查端口占用给API参数加空值判断Inet下载无响应URL不可达、协议选择错误、服务器需要HTTPS但用了HTTP改为HTTPS用URLDownloadToFile验证链接可达性WinCC脚本报“未找到对象”画面窗口名写错或脚本里没有先取ActiveScreen确认ScreenItems集合中的名称逐级引用对象程序在64位系统上崩溃某API声明结构不对或驱动级API被拒绝换成官方标准声明优先用user32、kernel32公开API串口收不到数据波特率不匹配、InputMode错误、RThreshold为0用串口助手确认设备参数检查事件触发条件6.2 调试与定位经验VB6的调试工具比现代IDE弱不少但有几个技巧很实用API调用看不清返回值时先用MsgBox弹出Err.LastDllError这个属性会告诉你API最后返回的系统错误码再查系统错误码表定位问题。串口收不到数据不要盯着代码看先用串口调试助手确认设备在发数据再反查VB程序端口号和参数。Inet控件状态每次变化都会触发StateChanged建议用Debug.Print把这些状态码打印到立即窗口跑一遍就能看到请求卡在哪一步。涉及Excel、数据库等外部组件时打开任务管理器观察进程是否残留。一旦发现Excel进程没退出优先检查释放顺序。这套排查逻辑不管VB项目多老多乱都能帮你在最短时间内缩小怀疑范围。最后再分享一个我多年形成的习惯VB项目一定要建版本备份目录每次改动之前把能跑的版本复制一份。因为在工控现场调试的时候改崩了想回退没有备用的旧版本真的是叫天天不应。高级VB编程走到最后拼的不是语法多花哨而是你踩过的坑够不够多、预案够不够全。希望这篇文章能让你少踩几个我已经趟平的坑。本文还有配套的精品资源点击获取