简介:这是一份面向工业自动化与组态软件应用场景的PDF技术资料,围绕考试及工程实践需要,讲解如何通过VBS脚本将Access数据库表中的数据写入西门子WinCC变量,从而实现数据实时监控与处理。文档从建立Wincc_Data数据库及数据表入手,逐项说明ODBC数据源Sample的配置方法,并给出完整VBS代码,涵盖ADODB连接、SQL查询、记录集遍历、HMIRuntime.Tags写入、对象释放等关键环节,可用于快速掌握数据库与WinCC变量交互的完整流程。压缩包内仅含1个PDF文件,大小约18KB,轻量集中,便于下载后直接查阅,特别适合需要快速补强WinCC数据接口知识的初学者、相关工程师或备考人员。目前已有342人浏览学习,内容重点突出,既有可直接复用的脚本思路,也包含建库、建表、ODBC配置等排错提示,对完成类似数采或组态联动任务具有直接参考价值。
1. Access 数据进 WinCC:一条考试记录如何变成画面里的变量
实操考试和技能考核现场经常遇到这种需求:考生成绩、题库参数或批次记录存在 Access 数据库里,而监控画面用的是 WinCC,要把数据库里的某一条记录实时显示出来,或者弹窗提示当前考生的分数。Access 数据库数据写入 WinCC 变量,最容易走弯路的做法是去折腾 DDE、OPC 或者 C 脚本直连 SQL,其实在 WinCC 里用 VBS 脚本配合 ADODB 和 ODBC 是最轻的一条通路。这份资源给的正是这个典型链路:建一个名为 Wincc_Data 的 Access 数据库,表中放五个 Tag 字段,配置一个叫 Sample 的 ODBC 数据源,再用十几行 VBS 把 ID=50 那条记录的 Tag1 值写入画面变量 U16Tag1。适合已经在用 WinCC 做画面、但数据库交互还不太熟的工程师,也适合备考工业自动化相关考试的学员照着敲一遍理解变量写入机制。
2. 先打通数据源:Access 表结构、ODBC 数据源与连接字符串三个前提
2.1 数据表怎么建:五个 Tag 字段和一个容易被忽略的 ID 列
我在 Access 2016 里建库时,步骤很简单:新建数据库,命名保存为 Wincc_Data.accdb,然后在“创建 → 表设计”里加字段。正文里列了五个字段 Tag1、Tag2、Tag3、Tag4、Tag5,但看到后面那句select Tag1 from WINCC_DATA where ID = 50,你就知道这个表里其实还隐含了一个 ID 字段。Access 里最省事的做法是直接加一列“自动编号”主键,列名默认就是 ID,这样 where 条件才能成立。
| 字段名 | 类型 | 用途 |
|---|---|---|
| ID | 自动编号(主键) | 定位第几条记录 |
| Tag1 | 数字 - 整型 | 源数据,准备写入 WinCC 变量 U16Tag1 |
| Tag2 | 数字 - 整型 | 备用扩展字段 |
| Tag3 | 数字 - 整型 | 备用扩展字段 |
| Tag4 | 数字 - 整型 | 备用扩展字段 |
| Tag5 | 数字 - 整型 | 备用扩展字段 |
这里有一个新手必踩的坑:如果只建五个 Tag 字段不建 ID 列,SQL 查询一执行就会报“未找到字段 ID”。另外 Tag1 的类型我一般直接选整型,不要用文本。原因是 U16Tag1 是 16 位无符号变量,范围只有 0~65535,文本字段读出来是字符串,写入时要经历隐式转换,一旦内容带空格或非数字字符,写入就会失败。建表时顺手把所有 Tag 字段都设成“数字 - 整型”,后面少折腾很久。建完表后建议手动插入 50 条以上测试数据,确保 ID=50 这条记录真实存在。
2.2 ODBC 数据源配置:32 位与 64 位选错是白忙
ODBC 是微软定义的数据库访问接口标准,它的作用是把 Access 数据库的读写能力暴露给外部程序。WinCC 里的 VBS 脚本本身并不能直接打开 Access 文件,必须先经过 ODBC 数据源做桥接。常见的翻车点不在脚本,而在 ODBC 管理器版本选错。WinCC 7.x 的脚本引擎是 32 位进程,而 Windows 10/11 默认打开的 ODBC 管理器是 64 位。你在 64 位管理器里建好数据源 Sample,32 位的 WinCC 脚本运行时照样找不到。
# 64 位 ODBC 管理器位置 %SystemRoot%\System32\odbcad32.exe # 32 位 ODBC 管理器位置,WinCC 必须用这个 %SystemRoot%\SysWOW64\odbcad32.exe正确操作是 Win+R 直接运行%SystemRoot%\SysWOW64\odbcad32.exe,打开后切到“系统 DSN”页签,点添加,选择 Microsoft Access Driver (*.mdb, *.accdb)。如果列表里找不到这个驱动,说明机器上没装 Microsoft Access Database Engine 可再发行组件,去官网下载对应版本装好再回来刷新。数据源名称必须填 Sample,不能带空格,然后浏览选择刚才的 Wincc_Data.accdb。这里我用系统 DSN 而不是用户 DSN,是为了避免 WinCC 以服务或另一个 Windows 账户启动时读不到用户级数据源,这条血泪经验后面细说。
2.3 连接字符串拆解:Provider=MSDASQL 为什么这样写
正文里的连接字符串只有一行,但每一项都不是摆设:
| 参数 | 作用 |
|---|---|
| Provider=MSDASQL | 指定使用微软的 OLE DB Provider for ODBC,让 ADODB 能通过 ODBC 访问 Access |
| DSN=Sample | 告诉程序去系统或用户数据源中找名为 Sample 的 ODBC 配置 |
| UID=;PWD= | 用户名和密码,Access 默认不设账户所以留空 |
有人会问,能不能不用 DSN,直接在连接字符串里写数据库路径?可以。部署到别的电脑时免 DSN 连接更省事,不用每台机器都重建 ODBC。常见写法是Driver={Microsoft Access Driver (*.mdb, *.accdb)};Dbq=C:\path\Wincc_Data.accdb;。我一般建议第一次调试先用 DSN 方式,因为报错信息更直观;等逻辑跑通了再决定要不要改成免 DSN。连接字符串里每个分号都必须保留,少一个分号 ADODB 就解析失败,这一点在从 PDF 复制代码时特别容易丢。
3. VBS 脚本逐段拆解:连接、查询与写入 U16Tag1 的完整写法
3.1 完整脚本:从连接、查询到写入的逐段对照
下面这段代码就是正文资源的完整实现,我加了中文注释方便对照:
' 声明对象和变量 Dim objConnection Dim objCommand Dim objRecordset Dim strConnectionString Dim strSQL Dim lngValue Dim lngCount ' 1. ODBC 连接字符串 strConnectionString = "Provider=MSDASQL;DSN=Sample;UID=;PWD=;" ' 2. SQL 查询:从 WINCC_DATA 表中取 ID 为 50 的记录 strSQL = "select Tag1 from WINCC_DATA where ID = 50" ' 3. 创建连接对象并打开 Set objConnection = CreateObject("ADODB.Connection") objConnection.ConnectionString = strConnectionString objConnection.Open ' 4. 创建命令对象,绑定连接并执行查询 Set objCommand = CreateObject("ADODB.Command") objCommand.ActiveConnection = objConnection objCommand.CommandText = strSQL Set objRecordset = objCommand.Execute ' 5. 判断记录集是否有返回字段 lngCount = objRecordset.Fields.Count If (lngCount > 0) Then objRecordset.MoveFirst ' 游标移到第一条记录 lngValue = objRecordset.Fields(0).Value ' 取第一个字段的值 HMIRuntime.Tags("U16Tag1").Write lngValue ' 写入 WinCC 变量 Else HMIRuntime.Trace "Selection returned no fields" & vbNewLine End If ' 6. 按顺序释放对象和连接 Set objCommand = Nothing objConnection.Close Set objRecordset = Nothing Set objConnection = Nothing逻辑上分三段理解。第一段是连接三件套:Connection 负责建立通道,Command 负责承载 SQL,Recordset 负责接收查询结果。第二段是关键判断:lngCount拿到的是字段数量而非记录数量,这里用它判断查询是否成功返回数据。第三段是收尾顺序:先释放 Command,再关闭 Connection,最后清空 Recordset 引用,顺序不能反,否则连接会一直占着数据库文件。
参数说明里最值得留意的是Fields(0),它表示取当前记录的第一个字段。查询语句是select Tag1,所以下标 0 就是 Tag1。如果 SQL 改成select Tag2, Tag1,那Fields(0)就变成 Tag2 了,取值会跟着 select 顺序走。另外HMIRuntime.Tags("U16Tag1")这个对象是 WinCC 运行环境的全局入口,变量名必须和变量管理里完全一致,大小写倒是不敏感,但引号里的名字写错一个字母,脚本运行时会直接报“未找到标签”。
3.2 脚本放在哪里:全局脚本动作、画面事件与周期触发
同样的脚本放在不同位置,行为完全不同。最常见的两种落点:一是 WinCC 的 Global Script 动作,通过触发器按周期执行,适合周期性把数据库最新记录刷到变量上;二是画面里按钮的鼠标事件,比如考试开始时点一下按钮加载当前考生成绩,这种单次触发更适合现场人工操作。
我一般这样配周期触发:在 Global Script 里新建动作,把上面的代码贴进去,然后打开动作属性设置触发器,选“周期”并在下方填入执行间隔。注意周期不要小于 1 秒,否则每次扫描周期都要新建数据库连接,Access 单机文件访问扛不住高频读写。更稳的做法是让脚本判断“数据是否变化”再决定要不要写变量,比如比较数据库里记录的时间戳和上次读取的时间戳。如果是画面按钮触发,脚本写法完全一样,只是不需要配置触发器,运行到按钮事件时执行一次就结束。
这里还有个新手常犯的错误:直接点 WinCC 的 VBS 编辑器里“测试运行”按钮,弹出窗口里报语法错误,就以为脚本有问题。实际上 WinCC 对 VBS 的编译环境和外部的 cscript 有细微差异,我在下一章专门讲验证方法时会给一个脱离 WinCC 也能试跑的小技巧。
4. 避坑记录:ODBC、Recordset 与 WinCC 侧五个高频现场
4.1 现象:objConnection.Open 报 80004005,提示找不到 DSN
脚本运行到objConnection.Open直接弹错误对话框,错误码是 80004005,提示信息通常带“未找到数据源名称并且未指定默认驱动程序”。原因基本就是 ODBC 管理器版本选错了,WinCC 的 32 位进程到 64 位 ODBC 注册表里找 Sample,自然找不到。还有一种情况是 DSN 建成了用户 DSN,WinCC 运行服务用的 Windows 账户和建 DSN 的账户不是同一个。解决方法是先在任务管理器里确认 WinCC 进程位数,再用 32 位管理器重建系统 DSN,并且数据源名称严格填 Sample,前后不要多加空格。如果装了 Access 驱动还是看不到驱动列表,检查系统里是否残留多个版本的 Microsoft Access Database Engine,把 32 位和 64 位混装也会导致驱动列表异常。
4.2 现象:SQL 不报错但 lngCount 恒为 0
连接没问题,脚本执行到 If 判断时走了 Else 分支,Trace 打印出 Selection returned no fields。最直接的原因是 ID=50 这条记录根本不存在,表里只有十几条数据,Access 的自动编号未必连续,删过记录后 ID 可能从 49 直接跳到 51。另一个常见原因是表名和字段名匹配问题,SQL 里写的WINCC_DATA和实际保存的表名不一致,Access 对大小写不敏感,但对空格和下划线敏感。解决方法是先在 Access 里用“创建 → 查询设计 → SQL 视图”直接运行一遍select Tag1 from WINCC_DATA where ID = 50,确认能返回一行,再回 WinCC 调试。如果 Access 里也查不到,就加一条测试记录把 ID=50 补齐,或者改 SQL 里的条件为where ID = 1先验证通路。
4.3 现象:写入不报错,但画面上 U16Tag1 的值纹丝不动
这是最迷惑人的一种情况,脚本正常执行,Trace 里也打印出了读到的值,但画面上绑定的 U16Tag1 就是不变,或者变成 0、变成负数。原因通常有两个方向。第一,HMIRuntime.Tags("U16Tag1").Write是异步写,写入动作提交给 WinCC 运行系统后立即返回,画面刷新有延迟,要等一个周期才能看到。第二,写入值超出 U16Tag1 的范围,U16 是无符号 16 位整型,合法范围 0~65535,如果 Tag1 字段里存了负数或超过 65535 的数,写入会被拒绝,而且不报错。第三是 Tag1 字段类型为文本,读出来是字符串,隐式转换失败。解决方法是写之前用CLng显式转换,并先做一个范围检查:
If IsNumeric(objRecordset.Fields(0).Value) Then lngValue = CLng(objRecordset.Fields(0).Value) If lngValue >= 0 And lngValue <= 65535 Then HMIRuntime.Tags("U16Tag1").Write lngValue End If End If这类“写入不报错但画面没反应”的问题,靠看代码是看不出来的,必须回读或加 Trace,我习惯把这种问题划成玄学类问题,后面第六章给完整的验证套路。
4.4 现象:WinCC 编译 VBS 报“语句未结束”或语法错误
从 PDF 或网页复制代码到 WinCC 脚本编辑器时,最容易出现引号变成中文引号、全角分号、多余换行这类肉眼难察觉的问题。VBScript 对语法要求严格,If 必须有配对的 End If,字符串必须用英文双引号包裹,一行写不下要续行时要在行尾加下划线_。如果代码里出现类似HMIRuntime.Trace "xxx" & ·不要硬背语法,先把代码在纯文本编辑器里重新敲一遍引号,再粘贴进 WinCC。本地验证的快捷方式是另存为 .vbs 文件,把HMIRuntime.Tags("U16Tag1").Write lngValue这一行替换成WScript.Echo lngValue`,然后在命令行用 cscript 跑一遍,能过说明语法没问题,再把写变量的那句换回去贴进 WinCC。
4.5 现象:连接 Access 时提示文件正被使用或无法独占打开
Access 是文件型数据库,默认打开方式偏独占。如果你在电脑上开着 Access 窗口查看 Wincc_Data.accdb,同时脚本又去连接,就会报“文件正在使用中”或“无法打开,因为另一个用户已将其锁定”。另一个常见源头是上次脚本异常退出,Access 进程没释放,任务管理器里还挂着 MSACCESS.EXE。解决方法是先关闭所有 Access 窗口,任务管理器结束残留进程,然后在 ODBC 数据源配置里把“独占”选项取消勾选。代码层面也可以在连接字符串里追加Mode=Read|Share Deny None,允许共享只读访问:
strConnectionString = "Provider=MSDASQL;DSN=Sample;UID=;PWD=;Mode=Read|Share Deny None;"加了这个参数后,即使数据库文件被其他程序打开,脚本也能以只读方式读取数据,适合生产环境里数据库文件由第三方软件维护的场景。
5. 扩展写法:循环批量、多字段映射与参数化查询防注入
5.1 批量读写:把第 50 条改造成 ID 区间循环
实际项目不会只读一条记录。考试系统里可能有几百个考生的成绩要分层推送,设备参数有几十个批次要逐一加载。这种场景把单条查询改成循环即可,用Do While配合MoveNext遍历记录集:
strSQL = "select ID, Tag1, Tag2 from WINCC_DATA where ID between 1 and 100 order by ID" Set objRecordset = objCommand.Execute objRecordset.MoveFirst Do While Not objRecordset.EOF lngValue = CLng(objRecordset.Fields("Tag1").Value) HMIRuntime.Tags("U16Tag1").Write lngValue objRecordset.MoveNext Loop这里要注意objRecordset.EOF是记录集结束标志,每次循环末尾必须MoveNext,否则会造成死循环。循环内每次Write都是异步写,如果两次写入的间隔小于 WinCC 画面刷新周期,画面上看到的可能是跳变后的最终值,中间过程值丢失。我一般建议循环里只做最大值和最小值的写入,或者配合 WinCC 的变量归档做趋势记录,不要指望画面逐条显示。批量读取时也不要一次把整表拉回来,数据量大时用where ID between 1 and 100做分页过滤,给 Access 减负。
5.2 多字段映射:把 Tag1~Tag5 同时写入多个 WinCC 变量
正文只写了 Tag1 到 U16Tag1,实际画面经常需要一次更新五个变量。这时可以直接按字段名取值,顺序清晰、不容易因为 select 调整列而错位:
strSQL = "select Tag1, Tag2, Tag3, Tag4, Tag5 from WINCC_DATA where ID = 50" Set objRecordset = objCommand.Execute objRecordset.MoveFirst HMIRuntime.Tags("U16Tag1").Write CLng(objRecordset.Fields("Tag1").Value) HMIRuntime.Tags("U16Tag2").Write CLng(objRecordset.Fields("Tag2").Value) HMIRuntime.Tags("U16Tag3").Write CLng(objRecordset.Fields("Tag3").Value) HMIRuntime.Tags("U16Tag4").Write CLng(objRecordset.Fields("Tag4").Value) HMIRuntime.Tags("U16Tag5").Write CLng(objRecordset.Fields("Tag5").Value)如果你用Fields(0)这种下标方式,一旦 select 里加了其他列,比如改成select ID, Tag1, Tag2, ...,下标就整体错一位,写进 WinCC 变量的值会串。所以字段较多时优先用Fields("字段名"),代码稍微啰嗦一点,但排查时一眼能看出谁对应谁。目标变量名写错一个字母,HMIRuntime.Tags 对象会返回空引用,VBS 里直接报错,建议把这些变量名集中定义成常量放在脚本开头,方便维护。
5.3 参数化查询:用户输入拼 SQL 的注入风险怎么防
当 SQL 条件来自画面输入框时,比如用户输入考号或设备编号,直接拼字符串会引入一个经典安全隐患。很多人图省事写成这样:
strSQL = "select * from WINCC_DATA where TagName = '" & userInput & "'"如果 userInput 里包含单引号和分号,这条 SQL 就可能被恶意构造,这正是常说的 access 注入场景。Access 虽然是单机文件数据库,但 JET/ACE 引擎一样会执行拼接后的 SQL,足以把成绩记录改掉或整表删除。安全做法是用 ADODB.Command 的参数化查询,让引擎把输入当参数而不是 SQL 语句:
Set objCommand = CreateObject("ADODB.Command") objCommand.ActiveConnection = objConnection objCommand.CommandText = "select * from WINCC_DATA where TagName = ?" objCommand.Parameters.Append objCommand.CreateParameter("p1", 202, 1, 50, strSearch) Set objRecordset = objCommand.ExecuteCreateParameter的四个参数里,202 表示字符串类型,1 表示输入参数,50 是最大长度,strSearch 是画面传进来的实际值。通过问号占位符绑定参数后,即使用户输入单引号或1=1这类攻击载荷,也只会被当作普通字符串去匹配字段值。这个写法比拼 SQL 多三行,但生产环境必用。
6. 验证这件事:Trace 输出、回读与画面联动三步确认
脚本写完不等于数据进了变量,我每次都会按三个步骤验证,少一步都可能在现场翻车。第一步是在脚本里插入 HMIRuntime.Trace 输出,把读到的数据库值和写入动作分开打印:
lngValue = CLng(objRecordset.Fields("Tag1").Value) HMIRuntime.Trace "read from db: " & lngValue & vbNewLine If HMIRuntime.Tags("U16Tag1").Write lngValue Then HMIRuntime.Trace "write ok" & vbNewLine Else HMIRuntime.Trace "write fail" & vbNewLine End IfWinCC 里 Trace 的输出会显示在诊断窗口或消息队列里,能看到 read 和 write 两行。这里有个细节,Write的返回值在不同 WinCC 版本里语义有差异,有的返回布尔值,有的返回 0 或非 0 整数,不能绝对依赖它判断成功。
第二步是写完之后立刻回读变量:
lngReadBack = HMIRuntime.Tags("U16Tag1").Read HMIRuntime.Trace "readback: " & lngReadBack & vbNewLine回读值和写入值一致,才能确认变量确实被更新了。注意异步写完成后立刻回读可能读到旧值,最好在 Trace 里输出后加一句WScript.Sleep 200或在画面刷新一个周期后再验证。
第三步是画面联动。在图形编辑器里放一个 I/O 域,连接 U16Tag1,运行 WinCC 后观察这个域显示的数值。如果是考试系统,把考生姓名、成绩分别映射到多个变量,画面就能形成完整的记录展示。也可以通过一个内部布尔变量做“读取成功”信号,截图时能直观看到状态翻转。
有一回现场调试,脚本里 Trace 一直打印 write ok,画面上却纹丝不动,折腾两小时才发现 U16Tag1 这个变量同时被 PLC 侧周期刷新覆盖了,数据库写入的值下一秒就被外部值顶掉。从那以后我每次做数据库写变量,都强制走一遍“先在 Access 里跑 SQL、再 Trace 看值、最后回读比对”这三步,任何一步不对就直接分屏查证,不再凭感觉猜。希望帮到你。
本文还有配套的精品资源,点击获取