VB.NET中将结构体保存到文件的方法

VB.NET中将结构体保存到文件的方法(转) 

 

前段时间,在将一个6.0的程序升级到.NET的过程中就遇到了很多麻烦。这个结构体中包含了定长字符串、数组以及其它一些结构,需要将该结构体作为头文件直接保存到一个二进制文件中,同时还要能从文件中读出该结构体。在6.0中,该结构体如下:

  1. Type OptionType 
  2.   ID As String * 11 
  3.   SamplingMode As Integer 
  4.   StartMode As Byte         
  5.   Freq As Integer             
  6.   Time As Single        
  7.   PreTime As Long     
  8.   Samples As Long    
  9.   TestName As String * 24 
  10.   Chan(1 To ADCOCHANS) As ChanType  
  11.   TimeUnit As Integer 
  12.   VectorUSpan As Single 
  13.   VectorISpan As Single 
  14.   Relat As String * 20 
  15.   Addr As String * 20 
  16.   TestDate As Date 
  17.   TestMemo As String * 320 
  18. End Type 

其中,ChanType是另一个结构体,如下:

  1. Type ChanType 
  2.   ChanNo As String * 10 
  3.   ChanName As String * 10 
  4.   Unit As String * 6 
  5.   Offon As Integer 
  6.   Ratio As Single  
  7.   Factor As Single  
  8.   Dcac As Integer 
  9.   Lower As Single 
  10.   Upper As Single 
  11.   ZeroVal As Single 
  12.   Color As Long 
  13.   Shift As Integer 
  14. End Type 

如果只是简单的将其改写到.NET版本并不难,但若想在.NET中读出以前6.0下保存的二进制文件时,就需要好好的设计一番了。具体涉及到以下几个问题:

1、.NET中已不再直接支持6.0中的定长字符串。不定长的字符串在存储时,需要同时存储一个长度值。

2、.NET中的数据类型有了很大变化,比如Integer是从16位变为32位,DateTime以整数形式保存而不再是以前的Double型

。。。

在此不一一列举,有兴趣的可以参看微软出的相关文档。下面是改写后的结构体:

  1. <StructLayoutAttribute(LayoutKind.Sequential, CharSet:=CharSet.Ansi, Pack:=1)> _ 
  2. Public Structure StructOption 
  3.     <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=11)> _ 
  4.     Public ID As String 
  5.     Public SamplingMode As Short 
  6.     Public StartMode As Byte 
  7.     Public Freq As Short 
  8.     Public Time As Single 
  9.     Public PreTime As Integer 
  10.     Public Samples As Integer 
  11.     <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=24)> _ 
  12.     Public TestName As String 
  13.     <MarshalAs(UnmanagedType.ByValArray, SizeConst:=44)> _ 
  14.     Public Chan() As StructChan 
  15.     Public TimeUnit As Short 
  16.     Public VectorUSpan As Single 
  17.     Public VectorISpan As Single 
  18.     <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=20)> _ 
  19.     Public Relat As String 
  20.     <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=20)> _ 
  21.     Public Addr As String 
  22.     Public TestDate As Date 
  23.     <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=320)> _ 
  24.     Public TestMemo As String 
  25.  
  26.     Public Shared Function CreateInstance() As StructOption 
  27.         Dim ret As New StructOption 
  28.         Dim newChan As New StructChan 
  29.         ReDim ret.Chan(ADCOCHANS - 1) 
  30.         For i As Integer = 0 To ADCOCHANS - 1 
  31.             ret.Chan(i) = newChan 
  32.         Next 
  33.         Return ret 
  34.     End Function 
  35. End Structure 

  1. <StructLayoutAttribute(LayoutKind.Sequential, CharSet:=CharSet.Ansi, Pack:=1)> _ 
  2. Public Structure StructChan 
  3.     <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=10)> _ 
  4.     Public ChanNo As String 
  5.     <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=10)> _ 
  6.     Public ChanName As String 
  7.     <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=6)> _ 
  8.     Public Unit As String 
  9.     Public Offon As Short 
  10.     Public Ratio As Single 
  11.     Public factor As Single 
  12.     Public Dcac As Short 
  13.     Public Lower As Single 
  14.     Public Upper As Single 
  15.     Public ZeroVal As Single 
  16.     Public Color As Integer 
  17.     Public Shift As Short 
  18. End Structure 

或许你已经注意到了,在改写的结构体中使用了一些属性,必须使用这些属性,才能构造出与VB6一模一样的结构体(至少说保存到文件中时时一样的结构)。

.NET中将结构体保存至文件的方法有两种:

一种是为结构体添加<Serializable()>属性,然后使用System.Runtime.Serialization.Formatters.Binary.BinaryFormatter中的Serialize方法将其串行化为文件流,并可用Deserialize方法将其还原为结构体;

另一种方法便是本文要讨论的,即结构体与字节数组之间的转化,因为在VB6中保存结构体时是按字节存储的,要做到同样效果需要将结构体其转化为字节数组。

再回到改写后的结构体中,为了定义属性,首先需要导入命名空间

Imports System.Runtime.InteropServices

接下来的结构体属性必不可少:

<StructLayoutAttribute(LayoutKind.Sequential, CharSet:=CharSet.Ansi, Pack:=1)>

对于定长字符串,需要添加如下属性:
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=11)>

注意,这里,字符串只能保存10个字符

对于定长数组,同样需要添加属性:

<MarshalAs(UnmanagedType.ByValArray, SizeConst:=44)>

另外,在使用前,需要对定长数组的长度进行ReDim,且长度必须与SizeConst相等。事实上,如果你定义了更长的长度,它会只截取SizeConst长度进行保存

接下来便是结构体与字节数组之间的转化,以及从文件读取结构体的源代码(注:部分源码根据网络上的C#版本改写)

  1. Private Sub StructToBytes(ByVal objStruct As Object, ByRef bytes() As Byte
  2.     '得到结构体长度 
  3.     Dim size As Integer = Marshal.SizeOf(objStruct) 
  4.     '重新定义数组大小 
  5.     ReDim bytes(size) 
  6.     '分配结构体大小的内存空间 
  7.     Dim ptrStruct As IntPtr = Marshal.AllocHGlobal(size) 
  8.     '将结构体放入内存空间 
  9.     Marshal.StructureToPtr(objStruct, ptrStruct, False
  10.     '将内存拷贝到字节数组 
  11.     Marshal.Copy(ptrStruct, bytes, 0, size) 
  12.     '释放内存空间 
  13.     Marshal.FreeHGlobal(ptrStruct) 
  14. End Sub 
  15. ''' <summary> 
  16. ''' 字节数组bytes()转化为结构体 
  17.  
  18. Private Function BytesToStruct(ByRef bytes() As Byte, ByVal mytype As Type) As Object 
  19.     '获取结构体大小 
  20.     Dim size As Integer = Marshal.SizeOf(mytype) 
  21.     'bytes数组长度小于结构体大小 
  22.     If (size > bytes.Length) Then 
  23.         '返回空 
  24.         Return Nothing 
  25.     End If 
  26.     '分配结构体大小的内存空间 
  27.     Dim ptrStruct As IntPtr = Marshal.AllocHGlobal(size) 
  28.     '将数组拷贝到内存 
  29.     Marshal.Copy(bytes, 0, ptrStruct, size) 
  30.     '将内存转换为目标结构体 
  31.     Dim obj As Object = Marshal.PtrToStructure(ptrStruct, mytype) 
  32.     '释放内存 
  33.     Marshal.FreeHGlobal(ptrStruct) 
  34.     Return obj 
  35. End Function 
  36.  
  37. ''' <summary> 
  38. ''' 从文件中读取结构体 
  39. ''' </summary> 
  40. ''' <returns>结构体</returns> 
  41. ''' <remarks></remarks> 
  42. Private Function FileToStruct(ByVal strPath As String, ByVal mytype As Type) As Object 
  43.     '获得结构体大小 
  44.     Dim size As Integer = Marshal.SizeOf(mytype) 
  45.     '打开文件流 
  46.     Dim fs As New FileStream(strPath, FileMode.Open) 
  47.     '读取size个字节 
  48.     Dim bytes(size) As Byte 
  49.     fs.Read(bytes, 0, size) 
  50.     '将读取的字节转化为结构体 
  51.     Dim obj As Object = BytesToStruct(bytes, mytype) 
  52.    fs.Close() 
  53.  
  54.     Return obj 
  55. End Function 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值