ADO2.20 Class 实战:连接、命令与记录集核心用法与避坑指南
简介ADO2.20 Class 是一套面向 Visual C 开发者的 ActiveX Data Objects 类库最初由国外开发者在 CodeProject 发布旨在简化 VC 程序对 SQL Server、Oracle、Access 等数据源的访问让开发者无需深入底层 ADO 接口即可完成数据库操作。资源包共 3 个文件包含 1 个 cpp 实现文件、1 个 h 头文件以及 1 个 mht 说明文档压缩包约 105KB体积轻巧便于集成到现有工程。其中头文件提供类定义与接口声明cpp 文件封装了 Connection、Command、Recordset 等核心对象的常用操作mht 文档则保留了原版使用说明与示例参考。目前已有 126 人学习关注适合不熟悉 ADO 或需要快速为 VC 项目添加数据库访问能力的开发者通过阅读源码即可理解连接管理、查询执行与结果集遍历的封装思路并直接复用到实际工程中。1. ADO2.20 Class从连接字符串到记录集关闭的完整链路如果你维护过 VB6 或早期 ASP 项目大概率见过ADODB.Connection、ADODB.Recordset这类写法。ADO2.20 是 Windows 平台上 MDAC 组件里一个非常经典的版本很多老系统的数据访问层至今还跑在它上面。问题在于这套东西的文档大多停留在“能跑就行”的层面真正遇到连接池耗尽、游标类型选错导致内存暴涨、Recordset没关干净引发句柄泄漏时很多人只能靠猜。这篇笔记围绕 ADO2.20 的 Class 使用展开把连接对象、命令对象、记录集对象三者的关系拆开讲给出可复现的代码和参数配置适合正在维护老系统、或者需要把老数据访问层迁移到新环境的工程师。核心不是背 API而是搞清楚每个对象在什么场景下该用什么游标、什么锁、什么超时。2. ADO2.20 三个核心 Class 的职责边界与选型2.1 Connection、Command、Recordset 到底谁管什么很多人写 ADO 的习惯是一个Connection打开然后所有查询都靠Connection.Execute返回一个Recordset从头用到尾。这种写法在小数据量下没问题但一旦涉及参数化查询、存储过程、批量更新就会暴露出职责混乱的问题。Connection的职责只有三件事建立物理连接、管理事务边界、控制连接超时。它不应该承担 SQL 拼接和结果集遍历的工作。Command的职责是封装一条可带参数的指令它可以绑定到某个Connection上执行也可以独立定义后再指定活动连接。Recordset则是结果集的载体它的行为完全由CursorType和LockType两个属性决定。我一般会这样划分凡是需要重复执行、带用户输入的 SQL一律走Command加Parameters只有一次性、无参数的简单查询才用Connection.Execute图省事。这个边界一旦立住后面排查注入风险和性能问题会轻松很多。2.2 游标类型和锁类型的组合矩阵CursorType有四个常用取值LockType也有四个组合起来不是随便选的。下面这张表是我在实际项目里验证过的组合建议CursorType常量值适用场景推荐 LockTypeadOpenForwardOnly0只读遍历、导出adLockReadOnlyadOpenStatic3需要行数统计的只读列表adLockReadOnlyadOpenKeyset1需要看到他人修改的交互界面adLockOptimisticadOpenDynamic2极少用开销大adLockOptimistic默认的adOpenForwardOnly加adLockReadOnly是性能最好的组合因为它不维护游标位置只做单向流式读取。一旦你把CursorType改成adOpenStaticADO 会在客户端或服务端物化整个结果集数据量大时内存直接翻倍。adOpenDynamic更是重灾区它会持续跟踪其他会话的增删改网络往返次数成倍增加。2.3 用 Command 对象做参数化查询的最小可跑代码下面这段 VBScript 风格的代码演示了从连接建立到参数绑定的完整流程。注意Parameters.Append的顺序必须和 SQL 里?占位符的顺序一致这是 ADO 的一个硬性约束。Dim conn, cmd, rs Set conn CreateObject(ADODB.Connection) conn.ConnectionString ProviderSQLOLEDB;Data Source127.0.0.1;Initial CatalogTestDB;User IDsa;Password***; conn.ConnectionTimeout 10 conn.CommandTimeout 30 conn.Open Set cmd CreateObject(ADODB.Command) Set cmd.ActiveConnection conn cmd.CommandType 1 adCmdText cmd.CommandText SELECT OrderId, Amount FROM Orders WHERE CustomerId ? AND Status ? 参数顺序必须与 SQL 中 ? 出现顺序一致 cmd.Parameters.Append cmd.CreateParameter(CustomerId, 3, 1, 4, 1001) adInteger, adParamInput cmd.Parameters.Append cmd.CreateParameter(Status, 200, 1, 20, PAID) adVarChar Set rs cmd.Execute Do While Not rs.EOF WScript.Echo rs.Fields(OrderId).Value - rs.Fields(Amount).Value rs.MoveNext Loop rs.Close Set rs Nothing Set cmd Nothing conn.Close Set conn Nothing逻辑说明CreateParameter的第二个参数是数据类型3代表adInteger200代表adVarChar第三个参数1是adParamInput表示输入参数第四个参数是长度整数类型给 4 即可字符串类型必须给足长度否则会被截断。参数说明里最容易翻车的是字符串长度很多人写255结果实际字段只有20虽然不报错但会浪费内存。2.4 连接字符串里的三个必调参数连接字符串不是越长越好但下面三个参数我建议显式写上ConnectionTimeout默认 15 秒内网可以降到 5 秒避免网络抖动时线程被长时间挂住。CommandTimeout默认 30 秒复杂报表查询可以调到 120 秒但不要设 0无限等待。OLE DB Services如果不需要连接池和自动事务可以设为-4关闭部分服务减少资源占用。提示连接字符串里的密码如果明文写在代码里任何能读到文件的人都能拿到。老系统迁移时优先把认证方式换成集成认证或者至少把密码放到受控的配置文件里。3. 把 ADO2.20 跑起来环境准备与最小验证3.1 确认 MDAC 版本和 ADO 类型库注册状态ADO2.20 对应的类型库文件通常是msado20.tlb注册在系统目录下。在动手写代码之前先用一条命令确认它是否可用reg query HKCR\ADODB.Connection\CLSID /ve如果返回一个{...}形式的 CLSID说明 ADO 组件已注册。如果报“找不到指定的注册表项”说明 MDAC 组件缺失或被卸载需要重新安装对应版本。注意不同 Windows 版本自带的 MDAC 版本不同老系统上常见的是 2.8但类型库接口仍然兼容 ADO2.20 的调用方式。3.2 用一段最小脚本验证连接和查询环境确认后不要急着写业务代码先用最小脚本验证连接是否通、查询是否返回预期结果。下面这段代码只做一件事连上数据库查一行系统时间。Dim conn, rs Set conn CreateObject(ADODB.Connection) conn.Open ProviderSQLOLEDB;Data Source127.0.0.1;Initial Catalogmaster;User IDsa;Password***; Set rs conn.Execute(SELECT GETDATE() AS Now) If Not rs.EOF Then WScript.Echo Server time: rs.Fields(Now).Value End If rs.Close conn.Close Set rs Nothing Set conn Nothing逻辑说明conn.Execute直接返回一个只读向前的Recordset适合这种一次性查询。参数说明Data Source可以写 IP 也可以写实例名如果端口不是默认的 1433需要写成127.0.0.1,1433的形式。这一步跑通之后再往上叠加Command和参数化查询排查范围会小很多。3.3 记录集遍历时的字段访问方式对比遍历Recordset时字段访问有两种写法rs.Fields(Name).Value和rs(Name).Value。后者是前者的语法糖但性能上几乎没有差别。真正影响性能的是Fields集合的索引方式用字符串名称访问比用数字索引慢因为每次都要做名称查找。如果循环体里要反复访问同一个字段我一般会先把Field对象取出来Dim fldAmount Set fldAmount rs.Fields(Amount) Do While Not rs.EOF total total fldAmount.Value rs.MoveNext Loop这样在循环内部就不再重复查找字段名。数据量上万行时这个习惯能省下可观的 CPU 时间。3.4 事务边界该放在 Connection 还是 Command 上事务只能由Connection对象开启和提交Command本身不管理事务。常见做法是conn.BeginTrans On Error Resume Next cmd.Execute If Err.Number 0 Then conn.RollbackTrans Else conn.CommitTrans End If注意BeginTrans之后同一个Connection上的所有Command都处于同一事务中。如果中间某个Command用了不同的连接事务不会覆盖它。这是老系统里经常出现的“部分提交”问题的根源。4. 避坑ADO2.20 使用中最容易翻车的五个场景4.1 现象连接池耗尽报“超出最大连接数”原因每次查询都新建Connection且没有显式Close或者Recordset没有关闭导致连接无法归还池中。ADO 的连接池依赖Connection对象完全释放后才回收物理连接。解决确保每个Connection在Finally或错误处理分支里都执行Close并且把对象设为Nothing。如果用了Recordset先关Recordset再关Connection。可以在连接字符串里加OLE DB Services-2显式启用池化但根本还是靠代码纪律。4.2 现象查询大表时内存暴涨进程被系统杀掉原因CursorType用了adOpenStatic或adOpenKeysetADO 在客户端物化了整个结果集。数据量几十万行时内存占用会达到原始数据的好几倍。解决只读遍历一律用adOpenForwardOnly加adLockReadOnly。如果确实需要行数用单独的SELECT COUNT(*)查询不要靠Recordset.RecordCount。注意adOpenForwardOnly下RecordCount返回 -1这是正常行为。4.3 现象参数化查询报“参数对象未找到”或结果为空原因Parameters.Append的顺序和 SQL 里?的顺序不一致或者字符串参数的长度设得太小导致值被截断。ADO 对参数顺序是敏感的它不按名称匹配。解决按 SQL 中?出现的顺序逐个Append字符串参数的长度给足实际字段长度。调试时可以先打印cmd.CommandText和每个参数的Value、Size确认无误再执行。4.4 现象事务回滚后部分数据仍然被写入原因事务期间有Command使用了另一个Connection或者Recordset的LockType是adLockBatchOptimistic但没调用UpdateBatch。解决确保事务内的所有操作共用同一个Connection。如果用了批量更新提交事务前必须显式调用UpdateBatch否则更改只停留在本地缓存。4.5 现象在 64 位系统上调用 ADO 报“类未注册”原因ADO2.20 的类型库是 32 位的64 位进程无法直接加载。很多老代码在 32 位环境下正常迁移到 64 位就挂。解决把宿主进程编译为 32 位或者改用更高版本的 ADO 类型库。如果必须用 64 位需要确认系统里注册了对应的 64 位 MDAC 组件但老版本 ADO 对 64 位的支持并不完整常见做法是保持 32 位运行环境。5. 进阶用连接复用和批量操作把吞吐量提上去5.1 连接复用不是“一直开着”而是“用完立刻还”很多人误以为连接复用就是把Connection对象做成全局单例一直开着不关。这在单线程脚本里勉强能用但在多线程或高并发场景下会引发状态污染。正确的做法是每次操作从池里取一个连接用完立刻Close让 ADO 自己决定物理连接是复用还是销毁。连接池的命中率可以通过性能计数器观察如果命中率长期低于 80%说明连接持有时间过长。5.2 批量插入时用 UpdateBatch 替代逐条 Execute逐条INSERT的开销主要在网络往返和事务日志刷盘。如果一次要插入几百条可以用adLockBatchOptimistic游标配合AddNew和UpdateBatchDim rs Set rs CreateObject(ADODB.Recordset) rs.CursorType 3 adOpenStatic rs.LockType 4 adLockBatchOptimistic rs.Open SELECT * FROM Orders WHERE 10, conn Dim i For i 1 To 500 rs.AddNew rs.Fields(OrderId).Value i rs.Fields(Amount).Value i * 10 rs.Update Next rs.UpdateBatch rs.Close逻辑说明WHERE 10只取结构不取数据AddNew在本地缓存里追加行UpdateBatch一次性提交。参数说明UpdateBatch的参数可以指定冲突处理方式默认是adAffectAll。注意批量提交时如果某一行违反约束整个批次可能失败需要配合错误处理逐行排查。5.3 用 Command 的 Prepared 属性减少重复解析如果同一条带参数的 SQL 要执行很多次可以把Command对象的Prepared属性设为True让数据提供者缓存执行计划cmd.Prepared True Dim j For j 1 To 1000 cmd.Parameters(CustomerId).Value j Set rs cmd.Execute 处理结果 rs.Close Next这样数据库只需要解析一次 SQL后续执行直接复用计划。代价是首次执行稍慢且会占用提供者端的缓存资源。对于执行次数少于 10 次的查询不建议开启。5.4 一个验证连接是否真正归还的小技巧在连接字符串里加上OLE DB Services-2启用池化后可以通过查询系统视图确认当前会话数SELECT COUNT(*) AS SessionCount FROM sys.dm_exec_sessions WHERE is_user_process 1在脚本执行前后各查一次如果执行后会话数没有回落说明连接没有正确归还。这个办法比看代码更直接适合在压测阶段快速定位泄漏点。我自己的习惯是任何用到 ADO 的脚本写完第一件事就是跑一遍“打开-查询-关闭”的循环用系统视图确认会话数归零再往上加业务逻辑。这个习惯帮我省掉了至少三次深夜排查连接泄漏的麻烦。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Claude Code用不好?问题不在工具,而在两个关键误区

Claude Code用不好?问题不在工具,而在两个关键误区

一开始接触 Claude Code 的那两周,我几乎在每一个深夜都对着终端怀疑人生:让它重构一个类,它把同文件里另一段业务逻辑一并“顺带优化”了;让它补测试,它在测试用例里自作主张改掉了被测试函数的返回值;让它…

2026/10/12 20:21:41 阅读更多 →
Context7 使用文档:提升AI编程助手准确性的MCP服务器

Context7 使用文档:提升AI编程助手准确性的MCP服务器

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 20:20:40 阅读更多 →
基于MFC的LALR(1)分析表自动构造工具设计与实现

基于MFC的LALR(1)分析表自动构造工具设计与实现

简介:这是一套基于MFC界面开发的LALR(1)分析表自动构造程序资源包,专门用于编译原理课程设计,面向需要完成语法分析阶段实验的高校学生。程序能够对任意给定的文法自动构造LR(1)项目集规范族,再通过合并同心集得到LALR(1)项目集规…

2026/10/12 20:20:40 阅读更多 →

最新新闻

鸿蒙Rcp网络请求封装:标准化Request工具类实战

鸿蒙Rcp网络请求封装:标准化Request工具类实战

做鸿蒙原生开发,只要应用需要和服务端交互,就绕不开网络请求这一层。HarmonyOS NEXT 自带的新一代网络库 Rcp,解决了老接口在连接复用、上传下载、Cookie 管理上的不少历史遗留问题,但直接用 Rcp 写业务,代码会非常碎。…

2026/10/13 4:21:04 阅读更多 →
C++守护进程实战:自动重启、心跳检测与防误杀

C++守护进程实战:自动重启、心跳检测与防误杀

简介:面向Windows平台进程保护需求的开发者,这套基于C的守护进程实现,核心目标是保护指定的C#程序不被异常终止。资源以C编写的DLL为主,利用Hook技术拦截系统事件,并通过进程监控在目标程序被关闭时及时介入&#xff0…

2026/10/13 4:21:04 阅读更多 →
TensorFlow feature_columns与交叉特征工程深度解析

TensorFlow feature_columns与交叉特征工程深度解析

1. 项目概述:从TensorFlow 2.0的feature_columns出发,重新理解结构化数据建模的本质“TensorFlow 2.0 feature_columns,交叉特征,estimator,tf1.0实战(第五节)”——这个标题乍看像一段课程目录…

2026/10/13 4:21:04 阅读更多 →
12-Factor全解读:从十二条原则到云原生工程落地

12-Factor全解读:从十二条原则到云原生工程落地

1. 为什么这份文档能被称为SaaS的奠基石第一次认真读完《The Twelve-Factor App》是在一个周五的深夜,当时我正在为一个典型的中型Web项目做架构梳理:同一套代码既跑在传统虚拟机里,又被客户要求在私有化环境部署;配置散落在多个配…

2026/10/13 4:21:04 阅读更多 →
轻量级本地任务调度系统ZTMS实战指南

轻量级本地任务调度系统ZTMS实战指南

1. 项目概述:这不是一个“咸鱼”项目,而是一套轻量级任务调度系统的实操落地“咸鱼ZTMS实例—程序”这个标题乍看有点让人摸不着头脑——“咸鱼”是平台名?代号?还是某种自嘲式命名习惯?其实它背后指向的,是…

2026/10/13 4:21:04 阅读更多 →
重读12-Factor App:云原生时代的SaaS应用运维方法论

重读12-Factor App:云原生时代的SaaS应用运维方法论

阅读笔记写到了第十八篇,这次想聊聊《The Twelve-Factor App》。我和这本小册子的关系挺普通:第一次读是五六年前,当时刚接触云端部署,读下来的感觉是“一份部署规范清单”;如今再读,感觉完全不一样了。对做…

2026/10/13 4:20:04 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →