admin 管理员组

文章数量: 1086019


2024年4月18日发(作者:transport同义词和近义词)

在WINCC中使用WinSock控件进行TCP/IP通讯的例程

目录

一、WinSock控件介绍(以VB语言表述) ................................................................................ 1

1、WinSock控件的主要属性 ................................................................................................. 1

2、WinSock控件的主要方法 ................................................................................................. 2

3、WinSock控件的主要事件 ................................................................................................. 3

二、WinSock控件在WINCC中的使用 ........................................................................................ 3

1、WinSock控件注册 ............................................................................................................. 3

2、在WinCC中添加WinSock控件 ...................................................................................... 4

三、服务器端程序介绍 ................................................................................................................... 4

四、WinCC画面模板与结构变量的配合使用 .............................................................................. 6

1、结构变量定义 ..................................................................................................................... 6

2、画面模板组态 ..................................................................................................................... 7

3、调用画面模板及修改变量前缀 ......................................................................................... 7

4、将画面模板中的对象连接到变量 ..................................................................................... 8

五、建立TCP/IP连接 ..................................................................................................................... 8

六、接收数据包的处理 ................................................................................................................. 10

附:在VBS中进行数据处理的局限性及变通解决方法 ........................................................... 12

摘要:

关键词:VB、VBS、WINCC、WINSOCK、DLL、UNICODE、ANSI、ASCII、画面模板、

结构变量、数据转换。

该文档的软件环境:

Microsoft Windows XP Professional 版本2002 SP3

SIMATIC WinCC V6.2 SP2 ASIA

‘SIMATIC STEP7 V5.4+SP5+HF3 Chinese

TCP&UDP测试工具 V1.02

目的:

使用WINCC用户程序作为客户端程序,与服务器通讯,通讯协议为标准TCP/IP协议,

取得服务器发送过来的数据包,按数据包格式文本规定,解析数据包数据,并将相关数据显

示在用户程序画面中。

一、WinSock控件介绍(以VB语言表述)

1、WinSock控件的主要属性

1) Protocol属性

通过Protocol属性可以设置WinSock控件连接远程计算机使用的协议。可选的协议是

TCP和UDP对应的VB的常量分别是sckTCPProtocol和sckUDPProtocol,Winsock控件默

认协议是TCP。

注意:虽然可以在运行时设置协议,但必须在连接未建立或断开连接后。

2) SocketHandle属性

SocketHandle返回当前socket连接的句柄,这是只读属性。

3) RemoteHostIP属性

RemoteHostIP属性返回远程计算机的IP地址。在客户端,当使用了控件的Connect

方法后,远程计算机的IP地址就赋给了RemoteHostIP属性,而在服务器端,当ConnectRequest

事件后,远程计算机(客户端)的IP地址就赋给了这个属性。如果使用的是UDP协议那么

当DataArrival事件后,发送UDP报文的计算机的IP才赋给了这个属性。

4) ByteReceived属性

1

返回当前接收缓冲区中的字节数

5) State属性

返回WinSock控件当前的状态

常数

sckClosed

SckOpen

SckListening

sckConnectionPending

sckResolvingHost

sckHostResolved

sckConnecting

sckConnected

sckClosing

sckError

2、WinSock控件的主要方法

1) Bind方法

用Bind方法可以把一个端口号固定为本控件使用,使得别的应用程序不能再使用这

个端口。

2) Listen方法

Listen方法只在使用TCP协议时有用。它将应用程序置于监听检测状态。

3) Connect方法

当本地计算机希望和远程计算机建立连接时,就可以调用Connect方法。

Connect方法调用的规范为:

Connect RemoteHost,RemotePort

4) Accept方法

当服务器接收到客户端的连接请求后,服务器有权决定是否接受客户端的请求。

5) SendData方法

当连接建立后,要发送数据就可以调用SendData方法,该方法只有一个参数,就是

0

1

2

3

4

5

6

7

8

9

缺省值,关闭

打开

侦听

连接挂起

识别主机

已识别主机

正在连接

已连接

同级人员正在关闭连接

错误

描述

2

要发送的数据。

6) GetData方法

当本地计算机接收到远程计算机的数据时,数据存放在缓冲区中,要从缓冲区中取出

数据,可以使用GetData方法。GetData方法调用规范如下:

GetData data,[type,][maxLen]

它从缓冲区中取得最长为maxLen的数据,并以type类型存放在data中,GetData取

得数据后,就把相应的缓冲区清空。

7) PeekData方法

和GetData方法类似,但PeekData在取得数据后并不把缓冲区清空。

3、WinSock控件的主要事件

1) ConnectRequest事件

当本地计算机接收到远程计算机发送的连接请求时,控件的ConnectRequest事件将

会被触发。

2) SendProgress事件

当一端的计算机正在向另一端的计算机发送数据时,SendProgress事件将被触发。

SendProgress事件记录了当前状态下已发送的字节数和剩余字节数。

3) SendComplete事件

当所有数据发送完成时,被触发。

4) DataArrival事件

当建立连接后,接受到了新数据就会触发这个事件。

注意:如果在接受到新数据前,缓冲区中非空,就不会触发这个事件。

5) Error事件

当在工作中发生任何错误都会触发这个事件。

二、WinSock控件在WINCC中的使用

1、WinSock控件注册

在WinCC中使用WinSock控件前,需要先进行注册。

注册方法如下:

使用记事本新建一个后缀名为reg的文件,编辑文件,加入以下文本:

3

[HKEY_CLASSES_ROOTLicenses2c49f800-c2dd-11cf-9ad6-0080c7e7b78d]

@="mlrljgrlhltlngjlthrligklpkrhllglqlrk"

保存文件退出。

先将拷贝到system32下

再将注册表文件添加到注册表

然后"运行",输入"regsvr32 ",确定。

2、在WinCC中添加WinSock控件

在WinCC图形编辑器中打开需要显示服务器数据的画面,选择“对象选项板”的“控

件”选项卡,在选项卡中选择“添加/删除”,在“选择OCX控件”对话框中选择“Microsoft

WinSock Control, version 6.0”进行注册。

在对象选项板中的WinSock控件拖入画面中。

三、服务器端程序介绍

这里所要通讯的服务器端程序是运行于南车资阳机车有限公司生产的V280/285系列

船用柴油机机旁控制屏监控系统程序。

以下是协议文本:

通讯方式:TCP/IP

侦听端口:9105

机旁柜IP地址定义:由用户根据具体网络配置决定

PAC数据采集周期:1秒

上位机记取数据周期:1秒

数据包格式定义如下:

上位机发送命令到PAC的数据包定义:本数据包由上位机发送到PAC,PAC根据上

位机发送的的命令执行,同时返回最新的采集数据到上位机。

数据长度:10字节。

数据类型:byte。

数据包详细定义:命令代码1字节+备用代码1字节+设定转速4字节(single)+备

用1字节

其中命令代码意义:1=“读数据”;18=“转速升”;19=“转速降”。

4

PAC发送到上位机的数据包定义:

数据长度:650个字节。

数据类型:BYTE。

数据包内容详细定义及代码示意如下:

起始传80个浮点数,每个浮点数占用四个字节,高位在后,低位在前。

00000000h: 33 33 E7 41 00 00 E8 41 66 66 EA 41 66 66 EA 41 ;

00000010h: 33 33 EB 41 66 66 E6 41 00 00 E4 41 CD CC E0 41 ;

00000020h: 66 66 DA 41 00 00 DC 41 00 00 DC 41 00 00 DC 41 ;

00000030h: 66 66 DA 41 CD CC D4 41 CD CC D4 41 00 00 D0 41 ;

00000040h: 00 00 DC 41 00 00 DC 41 33 33 DB 41 CD CC D8 41 ;

00000050h: 9A 3F 1C 46 9A 3F 1C 46 9A 3F 1C 46 9A 3F 1C 46 ;

00000060h: 65 66 89 41 32 B3 95 41 CE CC 94 41 CC 2C 95 41 ;

00000070h: CD 2C 8C 41 33 93 91 41 01 E0 91 41 99 19 92 41 ;

00000080h: 01 C0 8F 41 01 00 8E 41 9A 59 94 41 00 00 00 00 ;

00000090h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

000000a0h: FC FF 0A 42 F6 FF FB 41 9A 3F 1C C6 9A 3F 1C C6 ;

000000b0h: 9A 3F 1C C6 9A 3F 1C C6 9A 3F 1C C6 A3 FF 89 40 ;

000000c0h: 9A 3F 1C C6 9A 3F 1C C6 9A 3F 1C C6 0F 00 B5 41 ;

000000d0h: 74 FF EF 3F 9A 3F 1C C6 66 66 B8 40 9A 99 FD 41 ;

000000e0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

000000f0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000100h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000110h: 8F C2 04 42 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000120h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000130h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;.

续:

PAC第一组IO输入点,2字节;PAC第二组IO输入点,2字节;PAC第一组IO输

出点,1字节;PAC第二组IO输出点,1字节;PAC运行状态,1字节;PAC当前执行的

命令,1字节;备用,1字节;当前设定转速,4字节;报警数目,1字节;报警信息,300

字节;备用,16字节。

00000140h: 05 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 ;.

00000150h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;.

00000160h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000170h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000180h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000190h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

000001a0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

000001b0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

000001c0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

000001d0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

5

000001e0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

000001f0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000200h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;.

00000210h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000220h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000230h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000240h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000250h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000260h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000270h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ;

00000280h: 00 00 00 00 00 00 00 00 00 00

四、WinCC画面模板与结构变量的配合使用

本用户程序需要实时采集显示三台同类型柴油机的运行数据,由于三台柴油机的数据

采集和显示是相同的,而WinCC画面模板与结构变量配合使用可以在一个画面中根据条件

显示具有相同类型参数的多个对象,避免反复组态相同画面布局的工作,减少项目后期某些

细节部位的修改而带来的工作量,所以本用户程序采用画面模板与结构变量配合使用方法进

行组态设计。

《WinCC中的画面模板》一文提出四种使用画面模板的方法,这里采用第三种方法,

即“使用变量前缀的画面窗口”。

组态步骤如下所示:

1、结构变量定义

在WinCC中定义一个结构变量,结构变量名称为sDieselData。结构变量包括64个

FLOAT型变量(数据包上传80个模拟量,但实际使用只有小于64个,出于编程方便起见,

使用8*8=64个单精度浮点变量),16个BIT型变量(只使用PAC的输入点、其它开关量

不使用),1个SHORT变量(反映通讯状态)。

64个单精度浮点型变量的命名格式为:

fTag11、fTag12……fTag18

fTag21、fTag22……fTag28

………………………………

fTag81、fTag82……fTag88

16个BIT型变量命名格式为biTag1、biTag2……biTag16。

1个SHORT型变量名称为TXStatus。

结构变量定义完成后,根据结构变量定义三个内部结构变量,分别命名为“CNB”、

6

“YYB”、“SXB”。

2、画面模板组态

新建一个画面,画面名称为,并在画面中加入一个WinSock控件、一

些文字和输入/输入域,以及其它有些相关控件。如下图如示:

3、调用画面模板及修改变量前缀

新建的画面模板需要在主画面的一个子画面窗口中显示。主画面文件名称为

“”,子画面窗口名称为“pMainArea”。在用户程序运行时,有一个界面中显示

了三台柴油机的图标,点击某一个柴油机图标,子画面切换到相应的柴油机状态及参数界面。

图标的鼠标动作C代码如下:

#include "apdefap.h"

void OnClick(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName)

{

#pragma option(mbcs)

#define PIC_0 "pMain"

#define PIC_1 ""

7

SetPropChar(PIC_0,"pMainArea","TagPrefix","CNB."); ‘此处”CNB.”在其余两处

为”YYB.”、”SXB.”

SetPictureName(PIC_0,"pMainArea",PIC_1);

}

上面代码中关健代码就是:SetPropChar(PIC_0,"pMainArea","TagPrefix","CNB.")。该行

语句设置了子窗口pMainArea的变量前缀为”CNB.”。当子窗口设置了变量前缀后,画面窗

口中的对象连接变量时,变量一般会自动加上前缀(连接包括动态对话框、变量、C脚本及

VBS脚本)。

注意:在设置了子窗口的变量前缀后,如果要切换显示不带变量前缀的画面,则需设

置子窗口的变量前缀为空。例外OpenPrevPicture()函数不受影响。

4、将画面模板中的对象连接到变量

将画面模板中的对象连接到相应的变量,组态方法与常规组态相似,不同之处在于变

更量名称需要去掉前缀,在脚本中所使用的变量同样不要带变量前缀,因为变量前缀会自动

加上。在组态时会提示变量不存在,忽略即可。

五、建立TCP/IP连接

要求是在柴油机状态及参数画面显示时自动建立TCP/IP连接,如果连接没有建立,

则将相应的结构变量清零,并且每隔两秒重建连接。

连接建立后,发送读数据命令数据包。

在画面对象属性的“显示”属性中加入如下VBS代码(触发器选择2s周期):

Function Visible_Trigger(Byval Item)

Dim Obj_SockClient

Dim cnvt

Dim obj_Screen

Dim obj_pScreen

Dim Tag_intTXStatus

Dim i,j

Dim Tag_f,Tag_b

‘获得当前窗口中的WinSock对象

Set Obj_SockClient = ScreenItems("objSock")

‘获得显示当前画面的子窗口对象

Set obj_Screen = s("pMain").ScreenItems("pMainArea")

‘这是一个转换DLL,后面还要讲到

Set cnvt = CreateObject("onvert")

8

‘以下是建立连接前的参数初始化工作,远程地址和端口号根据实际情况修改,此处设

置是为了方便测试工作,可使用TCP&UDP测试工具进行测试。

Obj_ol = "sckTCPProtocol"

Select Case obj_fix

Case "CNB."

Obj_Host = "192.168.1.100"

Obj_Port = 4002

Case "YYB."

Obj_Host = "192.168.1.100"

Obj_Port = 4003

Case "SXB."

Obj_Host = "192.168.1.100"

Obj_Port = 4004

Case Else

Exit Function

End Select

‘连接没有建立、连接错误、同级人员正在关闭连接,这三种情况下,先关闭连接,再

尝试建立连接。

If (Obj_ = 0) Or (Obj_ = 9) Or (Obj_ = 8)

Then

Obj_

Obj_t

End If

‘如果连接没有建立,则使相应的FLOAT型结构变量清零。

If Obj_ <> 7 Then

For i = 1 To 8

For j = 1 To 8

Set Tag_f = ("fTag" & i & j)

Tag_ = 0

Tag_,1

Next

Next

‘如果连接没有建立,则使相应的BIT型结构变量复位。

For i = 1 To 15

Set Tag_b = ("biTag" & i)

Tag_ = False

Tag_ ,1

Next

Else

‘如果连接建立,则发送讯数据命令数据包

9

Obj_ta tr2vOctetStr("00000000")

End If

‘写入当前连接状态字

Set Tag_intTXStatus = ("TXStatus")

Tag_ = Obj_

Tag_ , 1

End Function

六、接收数据包的处理

当建立连接后,接受到了新数据就会触发这个DataArrival事件。这里只对前64个

模拟量数据、16个开关量输入数据、10条汉字报警信息进行解析,并将解析后的数据赋值

给相应的变量,汉字报警信息直接在窗口中的S7FlatEditBox控件中显示。

DataArrival事件的VBS代码如下:

Sub DataArrival(Byval Item, Byval bytesTotal)

On Error Resume Next

Dim Obj_SockClient

Dim cnvt

Dim obj_OutText

Dim strReceive

Dim strFromRec,strFromRec1

Dim i,j

Dim Tag_f,Tag_b

Dim strForBool,iForBoolCheck

Set Obj_SockClient = ScreenItems("objSock")

Set cnvt = CreateObject("onvert")

Set obj_OutText = ScreenItems("objOutText")

‘接收数据包字节数应为650字节,否则不处理

If bytesTotal = 650 Then

‘从接收缓冲区取得数据,并清空缓冲区

a strReceive

strFromRec = ""

'提取前333个字节所包含的信息,用于模拟量和开关量解析

For i = 0 To 333 'UBound(strReceive)

‘将接收到的BYTE()型数据转换为String型数据

strFromRec = strFromRec & Right("0" & Hex(Ascb(Midb(strReceive , i + 1 ,1))) , 2 )

10

Next

'解析出80个模拟量并赋值给相应的变量

For i = 1 To 8

For j = 1 To 8

Set Tag_f = ("fTag" & i & j)

‘此处使用了DLL中的数据转换函数,CvHexStr2vReal2的功能是将十六进制

字符串表示的单精度浮点数转换成实际的浮点数值。该函数后面还要讲到。

Tag_ = tr2vReal2(Mid(strFromRec,((i-1)*8+j-1)*8+1,8))

Tag_,1

Next

Next

'解析开关量输入字节(2字节),并赋值给相应的变量

iForBoolCheck = 0

strForBool = Mid(strFromRec,643,2) & Mid(strFromRec,641,2)

iForBoolCheck = Eval("&H" & strForBool)

For i = 1 To 15

Set Tag_b = ("biTag" & i)

If (iForBoolCheck And 2^(i-1) ) = 0 Then

Tag_ = False

Else

Tag_ = True

End If

Tag_ ,1

Next

End If

'解析汉字报警信息10条(每条信息包含15个汉字)

For i = 0 To 9

For j =0 To 14

‘提取汉字信息,并转换成Unicode字符串。

strFromRec1 = strFromRec1 & MidB(strReceive,i*30+j*2+335,2)

Next

strFromRec1 = strFromRec1 & vbCrLf

Next

obj_ = strFromRec1

End Sub

11

附:在VBS中进行数据处理的局限性及变通解决方法

在VBS中对数据进行转换解析处理,是一个比较普遍的应用问题,并不仅限于在

WINCC中的应用,而VBS在进行数据处理时的局限,也使得单靠VBS自身无法圆满的处

理,需要借助其它手段,而VBS结合外部动态链接库是一个合理的解决方案。

VBS(Microsoft Visual Basic Scripting Edition)是一种脚本语言。可以看作是VB语言

的简化版,可使用操作系统和其它程序所提供的程序库,由操作系统解释运行。WINCC

V6.0首次集成了VBS,可以用来使运行环境动态化,也可以创建动作(action)和过程

(procedure)来动态化图形对象。

注:脚本语言是使用一种特定的描述性语言,依据一定的格式编写的可执行文件,又

称作宏或批处理文件,是为了缩短传统的编写-编译-链接-运行(edit-compile-link-run)过程

而创建的计算机编程语言。

一个脚本通常是解释运行而非编译,

脚本程序在执行时,是由

系统的一个解释器,将其一条条的翻译成机器可识别的指令,并按程序顺序执行。

VBS只有一种数据类型,即Variant(可变的)。VBS在处理数据时,按上下文对其

处理的方式,把数据当作数字或字符串进行处理,也就是VBS觉得它像什么,就把它当作

什么来进行处理。Variant包含的数值信息类型称为子类型,大多数情况下,可将所需的数

据放进Variant中,而Variant也会按照最适用于其包含的数据的方式进行操作。

VBS这种处理数据的方法,有其优点和局限性。优点在于简单方便,局限性在于降低

了可控性,特别是在处理低层数据代码时,难度较高,而有些要求没有办法达到。

比如在WINCC中使用MSCOMM控件进行数据收发时,MSCOMM控件处理的是

BYTE()类型数据,在VBS中会将它作为VARIANT()类型数据进行处理,而这种数据

类型无法通过串口发送出去,而在VBS中没有相应的函数将其转换成BYTE()类型数据。

又比如在VBS中要将一个如“4199999A”的字符串转换成单精度浮点数据,会非常困难,

则类似的转换在C或VB这类高级语言中却很容易。

在进行上述数据处理任务时,结合动态链接库是比较合理的方法。下面详细讲述一般

需要用到的数据转换如何通过VB编写DLL来实现。

在切换到VB中进行编程之前,先了解熟悉VBS中的有关数据类型的处理。

1、在VBS中如何定义变量

在VBS中定义变量,只能使用一种定义方式,即Dim语句,变量类型只有一种,即

VARIANT,而不是象在VB中可以显示定义变量为不同的数据类型,如String,Interger,

Long等。

测试下列一段代码:

12

Dim A , B , C

A = Array(10,20,30)

B = Array("10","20","30")

C = Array(CByte(A(0)),CByte(A(1)),CByte(A(2)))

Msgbox typename(A) ‘显示数据类型为Variant()

Msgbox typename(A(0)) ‘显示数据类型为Interger

Msgbox typename(B) ‘显示数据类型为Variant ()

Msgbox typename(B(0)) ‘显示数据类型为String

Msgbox typename(C) ‘显示数据类型为Variant ()

Msgbox typename(C(0)) ‘显示数据类型为Byte

测试结果显示,对于单个变量,虽然不能象在VB中显示定义其数据类型,但VBS可

以大致判断其数据类型,也可以将数据通过强制转换成所需要的数据类型。但不管如何定义

或转换,仅通过VBS中的定义方式和转换函数,无法改变数组的类型,最终结果只有一种

数组类型,即Varaint()。

2、VBS中的字符编码

在VBS中,字符串以Unicode编码表示,编码的实现方案是UTF-16 LE。当VBS调

用低层API函数(VBS不能直接调用,而是通过控件或DLL间接调用)时,大都会由系统

自动将Unicode字符串转换成ANSI编码字符串。如通过Winsock控件发送字符串,在接收

端接收到的字符串实际上是ANSI字符串。

在简体中文WinXP操作系统下,默认的ANSI编码是GBK字符集。

3、VBS的字符转换函数

Chr函数返回与指定的 ANSI 字符代码相对应的字符。

调用格式:Chr(charcode) charcode 参数是可以标识字符的数字。

说明:从0到31的数字表示标准的不可打印的 ASCII 代码。例如,Chr(10) 返回换

行符。

注意:ChrB 函数与包含在字符串中的字节数据一起使用。ChrB 不是返回一个或两个

字节的字符,而总是返回单个字节的字符。ChrW 是为使用 Unicode 字符的 32 位平台提

供的。它的参数是一个 Unicode (宽字符)的字符代码,因此可以避免将 ANSI 转化为

Unicode 字符。

Asc 函数返回与字符串的第一个字母对应的 ANSI 字符代码。

Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,

13

则将发生运行时错误。

注意:AscB 函数和包含字节数据的字符串一起使用。 AscB 不是返回第一个字符的

字符代码,而是返回首字节。 AscW 是为使用 Unicode 字符的 32 位平台提供的。 它返

回 Unicode (宽型)字符代码,因此可以避免从 ANSI 到 Unicode 的代码转换。

总结如下:

chrb/ascb用来做ASCII转换,但并不仅包括前128个字符,而是全单字节字符,所以

应该是用来做单字节字符的转换。

chr/asc用来做ANSI转换,在当前中文简体WINDOWS系统中,即对应于GBK字符集

的转换。在WinXP中文版下的VB6.0中,Chr能够正确转换遇0~128和255的单字节字符

码和GBK字符集中的双字节字符码,超出此范围的字符码或解释成NUL字符,或是其它

未料结果。

chrw/ascw用来做Unicode转换。

4、在VBS中调用MSCOMM和WINSOCK的注意事项

在VBS中可以调用MSCOMM和WINSOCK通讯控件,以完成串口通讯和以太网通

讯。通讯控件可以以字符串或字节数组方式进行数据发送或接收,但由于字节数组在VBS

中不能显示定义,一个自然而然的想法是通过字符串方式进行数据发送或接收。

如果发送的数据范围在0x00~0x7F之间,可以通过Chr函数将十六进制数据转换成相

应的ASCII码字符,并且组合成字符串数据流,由于在此数据范围内的字符码可各系统平

台和软件环境中均可以得到唯一正确的解析,所以这个方法是可行的。

但如果发生冲突的数据超出此范围,再想通过Chr函数(或者ChrB/ChrW函数)将十

六进制数据转换成字符,转换结果将不是我们所预期的结果,这与软件平台和操作系统平台

有关,其中一些字符码被解释成空字符,或者是”?”,还有一些被解释成不同字符集下对应

的不同的字符。所以在VBS中调用通讯控件进行数据发送或接收,使用字符串方式不是合

理的方案。

在了解了VBS中进行数据处理的局限后,自然而然我们想到需要用其它的方法来解决

这些问题,可行的方法是通过DLL外部调用。因为VB简单实用,在实际应用中,一些在

VBS中不能处理或难以处理的任务,可以在VB中进行简单的处理,用VB来创建编译DLL

容易掌握,将这些功能编译进DLL中,可以由VBS调用处理。

14

1、Variant数组转换成Byte数组

如前面提到在VBS中进行定义或转换,只能得到Variant类型数组,而在VB中将

Variant类型数组转换成Byte型数组非常简单,如下面这个函数就可以实现这个功能:

Public Function CvVariantArray2vByteArray(inVarArray As Variant) As Variant

Dim i As Integer, byteArray() As Byte

ReDim byteArray(UBound(inVarArray))

For i = 0 To UBound(inVarArray)

byteArray(i) = inVarArray(i)

Next i

CvVariantArray2vByteArray = byteArray

End Function

在上面这个函数中,仅仅做了两件事,一是定义一个Byte数组,二是将Variant数组

元素拷贝到Byte数组中。

2、十六进制数据格式字符串转换成相应的Byte数组

十六进制数据格式字符串是指字符串中的字符(两个一组)以十六进制数据格式表示,

范围在“00”至“FF”之间,如下面这个字符串:

“EB9000FF”

将这样一个字符串转换成相应的Byte数组,其转换结果是:

BYTE(3) = (0xEB,0x90,0x00,0xFF)

相应的VB函数代码如下所示:

Public Function CvHexStr2vOctetStr(vInstr As Variant) As Variant

' Use this to convert a Variant containing an ASCII encoded Hex string to a Variant Array

of bytes

' this allows vbs to create Variants for ADSTYPE_OCTETSTRING from strings of ASCII

characters 0-9 A-F

Dim vOutArray() As Byte, i As Long

Dim v As Variant

ReDim vOutArray(0 To Len(vInstr) 2 - 1)

For i = 1 To Len(vInstr) 2

vOutArray(i - 1) = Val("&H" & Mid(vInstr, 2 * i - 1, 1)) * 16 + Val("&H" &

Mid(vInstr, 2 * i, 1))

15

Next i

CvHexStr2vOctetStr = vOutArray

End Function

该函数的工作原理如下(以“EB9000FF”为例):

字符串vInstr = “EB9000FF” 是一个Unicode字符串,在内存中的存放的十六进制字节

序为“45-00-42-00-39-00-30-00-30-00-30-00-46-00-46-00”,字符串长是8(Len函数)。

新建一个Byte数组,数组元素个数为4。

Mid函数是从指定字符串中返回指定数目的字符(注意不是字节),该函数以字符为

基本操作元素,不是字节,由于在VB中,字符以Unicode格式(UCS-2/UTF-16 LE,双字

节)表示,For循环开始时(i = 0),Mid(vInstr, 2 * i - 1, 1)首先取出第一个字符,即“E”

(双字节十六进制“0045”,小端序),这个字符是十六制格式的,所以在前面加上“&H”

前缀表示十六进制书写格式,将这个十六进制格式字符经Val函数转换成对应的数字,即

14,由于这个字符是字节中的高4位,所以需要*16,等于224。

类似,取出同一字节中的低4位“B”并转换成相应的数字11,并与高4位数字相加,

最终结果是235,在内存中存放的十六进制字节为“EB”。

循环结束后,得到字节数组(0xEB,0x90,0x00,0xFF)。

3、四字节十六进制数据格式字符串转换成单精度浮点数据

在IEEE 754标准中定义了单精度浮点数采用32位二进制数据(4字节)表示,二进

制数据按位分割成符号位、指数域和尾数域,将浮点数转换成实数,需要按公式进行计算。

如果在VBS中处理这样的转换,需要进行移位、判断、计算等多步处理,比较繁琐,运行

效率也不见得有多高,而在VB中进行这样的转换处理是很方便的。

比如在VBS中使用通讯控件接收数据,在接收到的数据包中有四个字节的数据(9A,99,

E5,41),这四个字节的数据表示一个IEEE单精度浮点数,在字节流中以小端序(LE)传

输(41是高有效字节MSByte),以DWORD表示即为0x41E5999A,转换成单精度浮点数

即为28.7。

这样的字节流在通讯控件接收到以后,存放在Byte数组中,在内存中存储的字节为

9A-99-E5-41,其实这正是IEEE单精度浮点数在内存中的存放格式,如果能将该内存区(4

字节)拷贝给一个float型变量,就可以很方便的完成转换工作。但在VBS中没有直接对内

存区进行操作的命令或函数,所以这个转换工作需要放在DLL中进行。

16

这个四字节的Byte数组,可以直接通过VB函数转换成float。出于学习的目的,先将

Byte数组转换成字符串,再将字符串转换成float。

将Byte数组转换成字符串的工作放在VBS中进行,代码如下:

‘将接收到的BYTE()型数据转换为String型数据

For i = 0 To 3

strFromRec = strFromRec & Right("0" & Hex(AscB(MidB(strReceive , i + 1 ,1))) , 2 )

Next

上面代码中strReceive变量存放的即为接收到的Byte数组数据,在VBS中可以使用

Mid(或MidB)函数直接操作Byte数组(一般用来操作字符串String),其中MidB函数

中的参数以字节为操作对象,MidB函数返回一个ASCII字符(数据类型是字符串,但在字

存中还是一个字节数据,该字节数据为ASCII字符对应的ASCII字符码,范围“00”~“FF”

之间)。

上面的代码是依次提取字节流中的字节,然后通过AscB函数将提取的ASCII字符转

换成对应的ASCII字符码,再用Hex函数将字符码转换成十六进制数据格式表示的字符串。

其中Right函数的使用是为了保持0~F字符码转换后的双字符格式。

将字符串转换成float的VB函数代码如下:

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination

As Any, Source As Any, ByVal Length As Long)

‘------------------------------------------------------------------------------

Public Function CvHexStr2vReal2(vInstr As Variant) As Variant

Dim l As Long

Dim f As Single

Dim str As String

Dim i As Integer

str = ""

For i = 4 To 1 Step -1

str = str & Mid(vInstr, i * 2 - 1, 2)

Next i

l = Val("&H" & str)

CopyMemory f, l, 4

CvHexStr2vReal2 = f

End Function

上面的程序代码中,首先对输入的字符串进行了高低位互换,这是可以理解的,因为

17

对于字符串或字节数组,在内存中的存放是按书写顺序由高到低存放的,在使用Val函数将

字符串表达式传换成对应的整型数据前,字符串中的字节顺序排列是9A99E541,直接使用

Val函数转换成整型数据是0x9A99E541,由于VB中整型数据是小端序存储方式,则在内

存中的存放顺序是,由低地址到高地址是41-E5-99-9A,使用API函数CopyMemory进

行内存拷贝是按字节顺序依次拷贝的,拷贝到浮点数变量内存中也是由低地址到高地址41

-E5-99-9A排列,而VB对浮点型数据内存的解析同样采用小端序方式解析出来,最终

得出的不是正确的结果,所以必须进行高低位字节的转换处理。

函数代码的工作过程是这样的:首先将输入的字符串进行高低字节互换,然后将互换

后的字符串用Val函数按十六进制数据格式转换成整型数据,再用CopyMemory函数将整

型数据内存区拷贝到浮点数内存区,最后函数返回的数据就是转换后的浮点数。

在函数转换处理过程中的变量在内存中的存放情况如下所示:

输入字符串:“9A99E541”

输入字符串在内存中存储的十六进制数据及顺序:

39-00-41-00-39-00-39-00-45-00-35-00-34-00-31-00

高低位互换后的字符串在内存中存储的十六进制数据及顺序:

34-00-31-00-45-00-35-00-39-00-39-00-39-00-41-00

转换成整型后在内存中存储的十六进制数据及顺序:

9A-99-E5-41

转换成单精度数据后在内存中存储的十六进制数据及顺序:

9A-99-E5-41

函数返回结果:28.7

18

参考资料:

1、VBS用户手册

2、WinCC中的画面模板

3、在VB6中用CopyMemory拷贝字符串的种种猫腻

4、关于字符编码,你所需要知道的

5、深入浅出浮点数

19


本文标签: 数据 变量 字节 字符串 画面