利用 Winsock 控件实现局域网通信

从 0 开始编写一段用于通信的程序,必须对相关的网络协议及其他的一些较底层的技术有较深入的了解,这显然有明显的难度。而利用 Winsock 控件,一切就不同了,它已经替你封装了所有烦琐的技术细节,并提供了访问 TCP 和 UDP 网络服务的方便途径。你只需通过设置控件的属性并调用其方法就可轻易连接到一台远程计算机中,并且还可以双向交换数据,而这一切都不需你了解 TCP 的细节或调用低级的Winsock APIs。
  Winsock 控件可以供 Microsoft Acess、Visual Basic,Visual C++或 Visual Foxpro 的开发人员使用。
  本文以 Visual Basic 6 企业版为开发环境来向大家介绍一下 Winsock 控件的初步应用。
  Winsock控件可以使用两种协议:TCP 协议和 UDP 协议,下面来分别介绍。
  TCP 协议即数据传输协议,它允许创建和维护与远程计算机的连接,使其彼此可以进行数据传输。利用TCP协议通讯必须分别建立客户应用程序和服务器应用程序。
  在创建客户应用程序时,必须知道服务器计算机名或其 IP 地址(存于 RemoteHost 属性)、及服务器计算机进行侦听的端口(存于 RemotePort 属性),然后调用 Connect 方法。
  创建服务器应用程序时,就应相应设置一个侦听端口(LocalPort属性)并调用listen方法。当客户机需要连接时(connect),就会发生ConnectionRequest事件。为了完成连接,你可以在ConnectionRequest事件中调用Accept方法。建立连接后,任何一方计算机都可以发送、接收对方数据。如果你要发送数据,需调用SendData方法。当接收到数据时,会发生DataArrival事件,调用 DataArrival 事件中的 GetData 方法就可以获得对方传送的数据。
  说了这么多,大家可能还是不太了解,让我用程序来详细说明。
  如果只有两台计算机,那十分容易。假设甲机为客户机,乙机为服务器,且其 IP 为192.192.192.1,接收端口为1200(任意选一个未被使用的端口即可)。首先在甲机客户端程序中加入一个 Winsock 控件,起名为 sckconnect,并设置其属性:RemoteHost=“192.192.192.1”,(即甲机IP地址), RemotePort=1200(即甲机侦听端口);再在乙机服务器程序中加入一个名为sckserver(0)的Winsock控件,其LocalPort=1200。
  在乙机服务器程序中Form_Load()加入
  sckserver(0).bind sckserver(0).LocalPort ‘与本地端口绑定
  sckserver(0).listern ‘ 侦听
  如果要传输数据,两机必须先建立连接。建立连接的程序如下:
  甲机客户机要先请求连接
  sckconnect.connect sckconnect.RemoteHost, sckconnect.RemotePort
  此句执行时会触发服务器程序中的ConnectRequest事件,在此过程中决定是否建立连接,其代码如下:
  Private sub sckserver_connectionrequest(index as Integer,Byval requestid as long)
  if sckserver.count=1 then
  load sckserver(1)
  sckserver(1).accept requestId
  end if
  end sub
  连接建立好以后,甲机或乙机都可以应用SendData方法来传送数据。例如,如果是甲机要传送一个叫string的字符串,只需在代码中加入:
  sckconnect.SendData string
  甲机传送数据后,会触发乙机的DataArrival事件,在其过程中用GetData方法可以收到传送的数据:
  Private sub sckserver_DataArrival(Index as integer,Byval BytesTotal as long)
  dim sdata as string
  sckserver(1).GetData sdata,vbstring
  end sub
  最后别忘了在关闭程序前要先关闭Winsock控件
  privat sub form_unload(cancel as integer)
  if sckconnect.state <>sckclosed then
  sckconnect.close
  end if
  end sub
  这只是最简单的情况,如果有多台计算机,那就稍微复杂一些,客户端程序可以不做改动,而服务器端程序需要略做改动:
  Private sub sckserver_connectrequest(Index as Integer,Byval requestid as long)
  dim sip as string
  dim I as integer
  sip=sckserver(0).RemoteHostIP ‘获得登录者的IP地址
  I=1
  Do while I<=sckserver.ubound ‘检查是否已经有该地址的记录   If sckserver(I).RemoteHostIP=sip then ‘如有,不必加载新的控件   Sckserver(I).Accept requestid   Exit sub   End if   I=I+1   Loop   Load sckserver(I) ‘否则,加载新的控件   Scksrver(I).accept requestID   End sub   注意到:以上的信息交谈实际上都发生在客户机与服务器之间,如果要做成聊天室那样,每个人的话都可以被别人“听到”,那就要在服务器端的DataArrival事件中,把接收到的客户机传来的数据,转发给所有客户机即可。   其循环转发信息的代码如下:   For I=1 to sckserver.count   if sckserver(I).state<>sckclosed then
  sckserver(I).SendData sdata
  end if
  next I
  怎么样,这样我们就作好了自己的通信软件!
  不过,不知大家注意到没有,上述程序都需要有一台计算机做为服务器,但如果我们的局域网中没有哪台计算机是可以常开的,也就是说,如果服务器端程序没有运行的话,其他客户端程序也没有办法通信。而这种情况却可能是经常出现的!至少,我所用的局域网那就是这样的。难道这样我们就无法享受局域网通信的乐趣了吗?
  不要急,记得吗,我们的Winsock控件还有另一个主角:UDP协议。
  UDP协议也称为用户数据报文协议,是一个无连接协议。何谓无连接协议?就是说利用此协议连接时,不必象TCP协议那样:需要服务器端侦听,客户机端请求连接,服务器端建立连接后双方才能通信。另外,UDP应用程序可以是客户机,也可以是服务器程序,而不必向TCP应用程序那样必须分别建立客户机程序和服务器程序。
  下面,来简述一下UDP协议通信的过程:UDP协议中,为了在甲乙两机中传输数据,必须先分别设置两机的LocalPort属性;再将甲机的RemoteHost属性设置为乙机的IP地址,RemotePort属性设置为乙机的LocalPort属性值,此时甲机调用SendData方法就可以传送数据了,乙机同样使用DataArrival事件中的GetData方法来获取甲机发送给乙机的信息。如想乙机向甲机传送数据,只需仿照上面的过程设置即可。
  用UDP协议来传输信息较TCP协议来说简单的多,它无须侦听(LISTEN),也无须请求连接(CONNECT),就象我们平时发信一样,只要写好地址及收信人姓名并发送出去即可。我们可以借此来编写一个局域网中的信息传送程序,下面来简单介绍以下程序中想实现的功能及其基本思想:
  首先,我们一定想让程序的图标显示在system tray中而不显示在任务栏中吧!VB光盘中在common\tools\vb\unsupport\systemTray 中有一个现成的程序,我们只要把它编译成systray.ocx 控件,然后在编写自己的程序时添加此控件即可。其使用方法十分简单,它已经定义好了鼠标单击、双击等事件,你只需编写相应的鼠标事件即可,这里不再多说。如果想自己编写托盘程序,见在系统托盘中显示
  程序的关键是:UDP协议在通讯时要知道对方的IP和Port,这要如何实现呢?最简单的方法是建立一个配置文件,里面放置了局域网上每台计算机的名字、IP和Port,在程序初始化时读出所有信息,在程序中只要知道向谁通信,读出其对应的IP和Port即可。
  我们知道了每台计算机的IP和Port,但我们怎样才能知道其它计算机是否在线呢,否则发出信息别人收不到怎么办?我们可以把此程序放在启动菜单中,让其一开机就自动启动,并最小化,放于窗口右下角的system tray中。在程序刚开始运行时,它会自动向它从配置文件中所知道的所有IP发一条信息:“我来了!”,如果有计算机在线,它会自动返回一条信息:“欢迎!”,如此则两机通信成功,它们会分别把对方的名字加入到自己的可通信人名单中去;如果有计算机关机,程序在退出之前会自动向所有人告别:“再见!”,接收到此信息的计算机会自动把发送信息的计算机的名称从自己的可通信人名单中去除。这样,如果某人不在线,你将无法发送信息给它;如果除了你以外,其他人都没有开机,那你的可发送人名单中将没有任何人。而其它人只要一上线,会自动去你那里“登记”,其他人只要一离线,会自动去你那里“告别”,你可以据此知道他人是否正在使用计算机,你甚至可以以此程序来统计他人的每天上机时间,不错吧!
  好了,一个局域网通信的程序的基本模型已经有了,并不复杂吧!大家赶快动手吧,来享受用自己的程序来聊天的乐趣!
  不过,要想编写一个出色的程序,不光是要有良好的创意与功能,更重要的是程序的兼容性与容错性。本例中,对错误处理没有做详细的解释,关于这一点,大家可以在Winsock控件的error事件及其帮助中找到满意的答案。
  另外,还可以从以下几方面来考虑功能的扩充:如传送图形、声音等多媒体信息、局域网互传文件(主动传送)、历史通话记录、系统日志、个人上机时间统计等等,而所有的这一切仅仅取决于你的想象力与你的聪明才智!下面,笔者以传送文件为例来讲一讲其功能实现的代码。
  你可以把本地的文件(图形、声音等可以先存成临时文件)以二进制文件的方式来打开它,将其内容全部读入一个byte类型的数组中,本地机代码如下:
  dim myfile () as byte
  dim position as long
  open “filename” for binary as #1
  position=0
  do while not eof(1)
  position=positon+1
  redim preserve myfile (1 to position)
  get #1,,myfile(position)
  loop
  close #1
  再向远程机传送这个字节数组
  sckserver.SendData myfile
  远程机收到这个数组之后,再以二进制文件的方式打开一个新目标文件,将数组内容写入这个新打开的文件,如果是bmp图片就将其放入picture图片框中,如果是wav文件,就播放。这样,局域网中的两个人就可以通过语言、图片、文字来交流了。
  远程机代码如下:
  Private sub sckconnect_DataArrival(byval bytestotal as long)
  dim receivefile(1 to bytestotal)as byte
  sckconnect.GetData receivefile,vbarray+vbbyte
  ’告诉Winsock控件收到的是字节数组类型的数据
  open “c:\temp\文件名” for binary as #1
  for I=1 to bytestotal
  put #1,,remotearray(I)
  next I
  clost #1
  end sub

注册表(Registry)

用Windows系统提供的注册表编辑器regedit.exe来认识登录数据库(Registry)。

1、Key和SubKey

注册表编辑器运行时我们可以看到它的窗口结构和资源管理器很像,左边窗格的每个文件夹图标表示一个Key。Key下面还有Subkey。我们习惯上采用文件夹的路径表示法。e.g:HKEY_LOCAL_MACHINE底下的”Software”Subkey表示成HKEY_LOCAL_MACHINE\Software。

2、Value、Value Name、Value Data和Default Value

用Regedit.exe打开HKEY_CLASSES_ROOT\.txt这个Key,在右窗格中显示的是Key的Value,Value可能有很多,对某个特定的Value来将,它有两个属性–Value Name和Value Data,如在此例中,HKEY_CLASSES_ROOT\.txt有一个Value–>”Content Type”,这个Value的Value Name为”Content Type”,Value Data为”text/plain”,某些Key,还有缺省Value–Default Value,如此例中,HKEY_CLASSES_ROOT\.txt这个Key的Default Value就是我们看到的”默认”[Value Name],”txtfile”[Value Data]。

3、存取Registry,先取Key Handle

在了解了Registry的结构之后,接下来的事就是如何存取它了。就像我们存取文件必须指明文件的存取路径[目录]一样,存取Registry必须先指明Key.Key在Regedit.exe中看到的是一长串的字符串,例如: “HKEY_LOCAL_MACHINE\Software\Microsoft\Windows”,

但在Windows内部,每个Key都对应一个Key Handle(等于一个长整数值,程序中通常以hKey表示),Windows之所以要用hkey来代表Key是为了让Registry的存取更有效率,因为整数的操作功能优于字符串,所以我们的首要任务就是取得Key的Key Handle(hKey)取得最上层的hKey。首先是位于最上层的Key,这些Key的hKey是固定不变的,其值如下表所示。

Key hKey(Key Handle)

HKEY_CLASSES_ROOT &H80000000

HKEY_CURRENT_USER &H80000001

HKEY_LOCAL_MACHINE &H80000002

HKEY_USERS &H80000003

HKEY_CURRENT_CONFIG &H80000005

HKEY_DYN_DATA &H80000006

如果想取得上述几个Key的SubKey Handle,可以调用RegOpenKey这个API函数。其详细描述如下:

VB声明 Declare Function RegOpenKey Lib “advapi32.dll” Alias “RegOpenKeyA”

(ByVal hKey As Long, ByVal lpSubKey As String, phkResult As Long) As Long

参数类型及说明:

hKey:Key Handle

lpSubKey:SubKey名称或路径

phkResult:若RegOpenKey执行成功,则这一参数返回Subkey的hKey.

返回值: =0,表示成功;≠0,表示失败。[注意这一点与别的API函数不太一样]

调用例:

Dim ret As Long, hKey As Long, hKey2 As Long

‘取得”HKEY_LOCAL_MACHINE”底下的”SOFTWARE\Microsoft”这个SubKey Handle.

ret = RegOpenKey(HKEY_LOCAL_MACHINE, “SOFTWARE\Microsoft”, hKey)

If ret = 0 Then ‘If Success

MsgBox “HKLM\SOFTWARE\Microsoft = ” & hKey

End If

‘继续以刚才所取得的”HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft”hKey为参数,再取得它的’SubKey”Windows\CurrentVersion”的handle。

ret = RegOpenKey(hKey, “Windows\CurrentVersion”, hKey2)

If ret = 0 Then

MsgBox “HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion = ” & hKey2

End If

相关的两个API函数是:RegCreateKey[建立SubKey]和RegClose[关闭SubKey]

详细说明:

RegCreateKey函数:

VB声明 Declare Function RegCreateKey Lib “advapi32.dll” Alias “RegCreateKeyA”

(ByVal hKey As Long, ByVal lpSubKey As String, phkResult As Long) As Long

它的参数用法与RegOpenKey一样。所不同的是RegOpenKey只能打开已经有的SubKey,而RegCreateKey则可以建立SubKey,比较特别的是,如果调用RegCreateKey所建立的SubKey是一个已经存在的SubKey,则它的功能和RegOpenKey相同。由于RegCreateKey的这种特性,有的程序员干脆不用RegOpenKey,而用RegCreateKey来统一代替RegOpenKey。

RegClose函数:

Declare Function RegCloseKey Lib “advapi32.dll” (ByVal hKey As Long) As Long

当我们不再存取Registry时,将打开或建立的SubKey关闭是一个比较好的习惯,就正如我们在使用C语言的文件打开函数后必须要关闭一样。

正则表达式(Regular Express)

  正则表达式是一种可以用于模式匹配和替换的强有力的工具,在几乎所有的基于UNIX系统的工具中找到正则表达式的身影,例如,vi编辑器,Perl或PHP脚本语言,以及awk或sed shell程序等。此外,象Javascript、VBScript等客户端的脚本语言也提供了对正则表达式的支持。由此可见,正则表达式已经超出了某种语言或某个系统的局限,成为人们广为接受的概念和功能。

  正则表达式可以让用户通过使用一系列的特殊字符构建匹配模式,然后把匹配模式与数据文件、程序输入以及WEB页面的表单输入等目标对象进行比较,根据比较对象中是否包含匹配模式,执行相应的程序。

  举例来说,正则表达式的一个最为普遍的应用就是用于验证用户在线输入的邮件地址的格式是否正确。如果通过正则表达式验证用户邮件地址的格式正确,用户所填写的表单信息将会被正常处理;反之,如果用户输入的邮件地址与正则表达的模式不匹配,将会弹出提示信息,要求用户重新输入正确的邮件地址。由此可见正则表达式在WEB应用的逻辑判断中具有举足轻重的作用。

    基本语法

  在对正则表达式的功能和作用有了初步的了解之后,接下来具体看一下正则表达式的语法格式。

  正则表达式的形式一般如下:

  /love/

  其中位于“/”定界符之间的部分就是将要在目标对象中进行匹配的模式。用户只要把希望查找匹配对象的模式内容放入“/”定界符之间即可。为了能够使用户更加灵活的定制模式内容,正则表达式提供了专门的“元字符”。所谓元字符就是指那些在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。

  较为常用的元字符包括: “+”, “*”,以及 “?”。其中,“+”元字符规定其前导字符必须在目标对象中连续出现一次或多次,“*”元字符规定其前导字符必须在目标对象中出现零次或连续多次,而“?”元字符规定其前导对象必须在目标对象中连续出现零次或一次。

  下面介绍正则表达式元字符的具体应用。

  /fo+/

  因为上述正则表达式中包含“+”元字符,表示可以与目标对象中的 “fool”, “fo”, 或者 “football”等在字母f后面连续出现一个或多个字母o的字符串相匹配。

  /eg*/

  因为上述正则表达式中包含“*”元字符,表示可以与目标对象中的 “easy”, “ego”, 或者 “egg”等在字母e后面连续出现零个或多个字母g的字符串相匹配。

  /Wil?/

  因为上述正则表达式中包含“?”元字符,表示可以与目标对象中的 “Win”, 或者 “Wilson”,等在字母i后面连续出现零个或一个字母l的字符串相匹配。

  除了元字符之外,用户还可以精确指定模式在匹配对象中出现的频率。例如,

  /jim{2,6}/

  上述正则表达式规定字符m可以在匹配对象中连续出现2-6次,因此,上述正则表达式可以同jimmy或jimmmmmy等字符串相匹配。

  在对如何使用正则表达式有了初步了解之后,我们来看一下其它几个重要的元字符的使用方式。

  \s:用于匹配单个空格符,包括tab键和换行符;

  \S:用于匹配除单个空格符之外的所有字符;

  \d:用于匹配从0到9的数字;

  \w:用于匹配字母,数字或下划线字符;

  \W:用于匹配所有与\w不匹配的字符;

  . :用于匹配除换行符之外的所有字符。

  (说明:我们可以把\s和\S以及\w和\W看作互为逆运算)

  下面,我们就通过实例看一下如何在正则表达式中使用上述元字符。

  /\s+/

  上述正则表达式可以用于匹配目标对象中的一个或多个空格字符。

  /\d000/

  如果手中有一份复杂的财务报表,那么就可以通过上述正则表达式轻而易举的查找到所有总额达千元的款项。

  除了以上所介绍的元字符之外,正则表达式中还具有另外一种较为独特的专用字符,即定位符。定位符用于规定匹配模式在目标对象中的出现位置。

  较为常用的定位符包括: “^”, “$”, “\b” 以及 “\B”。其中,“^”定位符规定匹配模式必须出现在目标字符串的开头,“$”定位符规定匹配模式必须出现在目标对象的结尾,\b定位符规定匹配模式必须出现在目标字符串的开头或结尾的两个边界之一,而“\B”定位符则规定匹配对象必须位于目标字符串的开头和结尾两个边界之内,即匹配对象既不能作为目标字符串的开头,也不能作为目标字符串的结尾。同样,我们也可以把“^”和“$”以及“\b”和“\B”看作是互为逆运算的两组定位符。举例来说:

  /^hell/

  因为上述正则表达式中包含“^”定位符,所以可以与目标对象中以 “hell”, “hello”或 “hellhound”开头的字符串相匹配。

  /ar$/

  因为上述正则表达式中包含“$”定位符,所以可以与目标对象中以 “car”, “bar”或 “ar” 结尾的字符串相匹配。

  /\bbom/

  因为上述正则表达式模式以“\b”定位符开头,所以可以与目标对象中以 “bomb”, 或 “bom”开头的字符串相匹配。

  /man\b/

  因为上述正则表达式模式以“\b”定位符结尾,所以可以与目标对象中以 “human”, “woman”或 “man”结尾的字符串相匹配。

  为了能够方便用户更加灵活的设定匹配模式,正则表达式允许使用者在匹配模式中指定某一个范围而不局限于具体的字符。例如:

  /[A-Z]/

  上述正则表达式将会与从A到Z范围内任何一个大写字母相匹配。

  /[a-z]/

  上述正则表达式将会与从a到z范围内任何一个小写字母相匹配。

  /[0-9]/

  上述正则表达式将会与从0到9范围内任何一个数字相匹配。

  /([a-z][A-Z][0-9])+/

  上述正则表达式将会与任何由字母和数字组成的字符串,如 “aB0” 等相匹配。这里需要提醒用户注意的一点就是可以在正则表达式中使用 “()” 把字符串组合在一起。“()”符号包含的内容必须同时出现在目标对象中。因此,上述正则表达式将无法与诸如 “abc”等的字符串匹配,因为“abc”中的最后一个字符为字母而非数字。

  如果我们希望在正则表达式中实现类似编程逻辑中的“或”运算,在多个不同的模式中任选一个进行匹配的话,可以使用管道符 “|”。例如:

  /to|too|2/

  上述正则表达式将会与目标对象中的 “to”, “too”, 或 “2” 相匹配。

  正则表达式中还有一个较为常用的运算符,即否定符 “[^]”。与我们前文所介绍的定位符 “^” 不同,否定符 “[^]”规定目标对象中不能存在模式中所规定的字符串。例如:

  /[^A-C]/

  上述字符串将会与目标对象中除A,B,和C之外的任何字符相匹配。一般来说,当“^”出现在 “[]”内时就被视做否定运算符;而当“^”位于“[]”之外,或没有“[]”时,则应当被视做定位符。

  最后,当用户需要在正则表达式的模式中加入元字符,并查找其匹配对象时,可以使用转义符“\”。例如:

  /Th\*/

  上述正则表达式将会与目标对象中的“Th*”而非“The”等相匹配。

在系统托盘中显示

‘建立项目(Project),创制如图级联菜单,分别命名为mnuMain、mnuWork、mnuExit。

Option Explicit
Private Type NOTIFYICONDATA
cbSize As Long
hWnd As Long
uID As Long
uFlags As Long
uCallbackMessage As Long
hIcon As Long
szTip As String * 64
End Type
Dim tnid As NOTIFYICONDATA
Private Const NIM_ADD = &H0
Private Const NIM_MODIFY = &H1
Private Const NIM_DELETE = &H2
Private Const NIM_MOUSEMOVE = &H200
Private Const NIF_MESSAGE = &H1
Private Const NIF_ICON = &H2
Private Const NIF_TIP = &H4
Private Const DOUBLE_CLICK_LEFT = &H203
Private Const BUTTON_LEFT_DOWN = &H201
Private Const BUTTON_LEFT_UP = &H202
Private Const DOUBLE_CLICK_RIGHT = &H206
Private Const BUTTON_RIGHT_DOWN = &H204
Private Const BUTTON_RIGHT_UP = &H205
Private Declare Function Shell_NotifyIcon Lib “shell32” Alias “Shell_NotifyIconA” (ByVal dwMessage As Long, pnid As NOTIFYICONDATA) As Boolean

Private Sub Form_Load()
tnid.cbSize = Len(tnid)
tnid.hWnd = Me.hWnd
tnid.uID = 1&
tnid.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE
tnid.uCallbackMessage = NIM_MOUSEMOVE
tnid.hIcon = Me.Icon
tnid.szTip = “系统托盘样例” & Chr$(0)
Shell_NotifyIcon NIM_ADD, tnid
Me.Hide
App.TaskVisible = False
End Sub

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
‘为相应事件添加对应代码
Dim MSG As Long
MSG = X / Screen.TwipsPerPixelX ‘512
Select Case MSG
Case DOUBLE_CLICK_LEFT ‘515

Case BUTTON_LEFT_DOWN ‘513
OpenForm Me
Case BUTTON_LEFT_UP ‘514

Case DOUBLE_CLICK_RIGHT ‘518

Case BUTTON_RIGHT_DOWN ‘516

Case BUTTON_RIGHT_UP ‘517
PopupMenu mnuMain, , , , mnuWork
End Select
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
EndApp
End Sub

Private Sub Form_Resize()
If Me.WindowState = 1 Then
Me.Hide
App.TaskVisible = False
End If
End Sub

Private Sub mnuExit_Click()
EndApp
End Sub

Private Sub EndApp()
Unload Me
tnid.cbSize = Len(tnid)
tnid.hWnd = Me.hWnd
tnid.uID = 1&
Shell_NotifyIcon NIM_DELETE, tnid
End
End Sub

Private Sub OpenForm(ByVal frmShow As Form)
frmShow.Show
App.TaskVisible = True
End Sub

AutoCAD 的文件 DXF 组码

DXF的基本惯例

DXF格式是特定版本AutoCAD图形文件中所包含的全部信息的标记数据的一种表示方法。

标记数据的意思是指在每个数据元素前都带一个称为组码的整数。组码的值表明了其后数据元素的类型,也指出了数据元素对于给定对象(或记录)类型的含意。实际上,图形文件中所有用户指定的信息都能够以DXF文件格式表示。在AutoLISP和ARX应用程序中使用的DXF格式与上述格式基本相同,只是在某些数据组上存在着细微的差别。

如不作特殊说明,本节中所出现的组码都可以应用于DXF文件、AutoLISP应用程序和ARX应用程序。当组码说明对于应用程序和DXF文件有所不同时(或只适用于其中之一),在组码的说明前有如下提示符:

APP只用于应用程序的说明

DXF只用于DXF文件的说明

如果组码说明对DXF文件和应用程序都适用,那么没有提示符;否则将显示适当的提示符。

组码范围

组码将与组码关联的值(组值)定义为整型、浮点数型或字符串型。具体说明如下表:

组码范围   组码范围组值类型

0-9                                    字符串(最多255个字符,对于UNICODE字符串则更少)

10-59                     双精度三维点

60-79                     16位整数值

90-99                     32位整数值

100                                    字符串(最多255个字符,对于UNICODE字符串则更少)

102                                    字符串(最多255个字符,对于UNICODE字符串则更少)

105                                    表示十六进制句柄值的字符串

140-147                 双精度标量浮点值

170-175                 16位整数值

280-289                 8位整数值

300-309                 任意文字字符串

310-319                 表示二进制数据组的十六进制值的字符串

320-329                 表示十六进制句柄值的字符串

330-369                 表示十六进制对象标识符的字符串

999                                    注释(字符串)

1000-1009  字符串(最多255个字符;对于UNICODE字符串则更少)

1010-1059  浮点值

1060-1070  16位整数值

1071                      32位整数值

按数字次序排列的组码

下表给出了组码(或组码范围)及其说明。在表中,“固定”表示该组码的用途固定不变,非固定组码的用途将随上下文变化。

按数字次序排列的图元组码

组码说明

-5APP:persistentreactor链表

-4APP:条件运算符(仅用于ssget)

-3APP:扩展数据(XDATA)标记(固定)

-2APP:图元名引用(固定)

-1APP:图元名。每次打开图形时它都改变,且不被保存。(固定)

0   表示图元类型的文字字符串(固定)

1   图元的主要文字值

2   名称(属性标记、块名称等)

3-4            其他的文字值或名称值

5   图元句柄。最多16位十六进制数字的文字字符串(固定)

6               线型名(固定)

7               文字样式名(固定)

8               图层名(固定)

9               DXF:变量名标识符(仅用于DXF文件的HEADER区域)。

10              主要点。此点为直线或文字图元的起点,圆的圆心等等。

DXF:主要点的X值(其后为Y和Z值的组码20和30)

APP:三维点(三个实数构成的表)

11-18         其他点。

DXF:其他点的X值(其后为Y和Z值的组码21-28和31-38)

APP:三维点(三个实数构成的表)

20,30         DXF:主要点的Y和Z值

21-28,

31-37         DXF:其他点的Y和Z值

38              DXF:如果非零,则为图元的标高。只在R11以前的AutoCAD输出的DXF文件中存在

39              如果非零,则为图元的厚度(固定)

40-48         浮点值(文字高度、比例因子等)

48              线型比例。浮点标量值。缺省值适用于所有图元类型。

49              可重复的浮点值。一个图元中的可变长度表(例如LTYPE表中的虚线长度)中可出现多个组码49。组码7x总是在第一个组码49前出现,用于指定表的长度。

50-58         角度(在DXF文件中单位为度,在AutoLISP和ARX应用程序中单位为弧度)。

60              表示图元可见性的整数值。不赋值或值为0时表示可见;为1时表示不可见。

62              颜色代码(固定)

66              “图元跟随”标志(固定)

67              空间,即模型空间或图纸空间(固定)

68              APP:表示视口打开但不可见、未激活或者关闭。

69              APP:视口标识数字。

70-78         整数值,如重复部分的计数器、标志位或模式等。

90-99         32位整数值

100                        子类数据标记(把继承下来的类名当作字符串)。由具体类继承下来的所有对象和图元类都必须有此项。此标记用于分离某个对象中由不同的类定义的数据。它也满足从ARX继承下来的每个独立的具体类的DXF命名需要(请参见子类标记!AL(`XREF_11832_al_u05_c’,1))。

102                        控制字符串,其后为“{<任意名称>”或”}”。除了字符串必须以”{“开始外,它与外部数据组码1002类似。其后可跟任意字符串,且此字符串的解释取决于应用程序。另一个可用的控制字符串为”}”,它标识组的结束。如上所述,除了在执行图形核查操作期间外AutoCAD一般不解释这些字符串;它们仅用于应用程序。

105                        DIMVAR符号表条目对象句柄。

210                        拉伸方向(固定)。

DXF:拉伸方向的X值

APP:三维拉伸方向矢量

220,230      DXF:拉伸方向的Y和Z值

280-289     8位整数值

300-309     任意的文字字符串

310-319     任意二进制数据组,与组码1004具有相同表示法和限制:最长为254个字符的十六进制字符串表示最长为127个字节的数据数据组。

320-329     任意对象句柄。句柄值保留原样,在执行INSERT和XREF操作时它们不被转化。

330-339     软键指针句柄。任意指向同一DXF文件或图形中的其他对象的软键指针,在执行INSERT和XREF操作时被转化。

340-349     硬键指针句柄。任意指向同一DXF文件或图形中的其他对象的硬键指针,在执行INSERT和XREF操作时被转化。

350-359     软键从属句柄。链接到同一DXF文件或图形中其他对象的任意软键从属链接,在执行INSERT和XREF操作时被转化。

360-369     硬键从属句柄。链接到同一DXF文件或图形中其他对象的任意硬键从属链接,在执行INSERT和XREF操作时被转化。

999                        DXF:999组码表示其后为注释字符串行。DXFOUT不在DXF输出文件中包括此组;DXFIN能识别词组码,但忽略其后的注释。通过999组码,用户可以在所编辑的DXF文件中包括注释。

1000          扩展数据中的ASCII字符串(最长255个字节)。

1001          扩展数据的已注册应用程序名(ASCII字符串,最长31个字节)。

1002          扩展数据控制字符串(”{“或”}”)。

1003          扩展数据图层名。

1004          扩展数据中的字节数据组(最长127字节)。

1005          扩展数据中的图元句柄。文字字符串,最多16位十六进制数字。

1010          扩展数据中的点

DXF:X值(其后跟组码1020和1030)

APP:三维点

1020,1030  DXF:点的Y和Z值

1011                      扩展数据中的三维世界空间位置:X值(其后跟组码1021和1031):三维点

1021,1031  DXF:世界空间位置的Y和Z值。

1012          扩展数据中的三维世界空间位移:X值(其后跟组码1022和1032):三维矢量

1022,1032  DXF:世界空间位移的Y和Z值

1013          扩展数据中的三维世界空间方向

DXF:X值(其后跟组码1022和1032)

APP:三维矢量

1023,1033  DXF:世界空间方向的Y和Z值

1040          扩展数据浮点值。

1041          扩展数据距离值。

1042          扩展数据比例因子。

1070          扩展数据16位符号整数。

1071          扩展数据32位符号整数。

对象和图元的组码

在DXF格式中,对象的定义与图元的定义不同:图元有图形表示,而对象则没有图形表示。例如,词典是对象而不是图元。对象通常作为非图形对象来使用,图元则作为图形对象来使用。

在DXF文件中,图元可以出现在BLOCK和ENTITIESE区域中。两个区域中图元的用法一样。某些定义图元的组码始终会出现,而其他的组码仅在它们的值与缺省值不同时才出现。

读取DXF文件的程序不应该假定说明图元的组码是按照给定次序出现的。与说明图元的组码相连的0组码表示此图元已结束。0组码将开始新图元或表示此区域已结束。 注意如果用户以表驱动方式(即忽略未定义的组码,且对图元中的组码次序不做任何假定)编写DXF处理程序,那么该程序将比较容易针对AutoCAD的后续版本做调整。因为AutoCAD的性能将不断得到增强,所以图元中将添加一些新的组码以提供更多的功能。