简体   繁体   English

VB6:二进制文件是如何编码的?使用Put语句

[英]VB6: How are binary files encoded? using Put statement

I have this code 我有这个代码

  Open WritingPath & "\FplDb.txt" For Random As #1 Len = Len(WpRec)
    For i = 1 To 99
      WpRec.WpIndex = FplDB(i, 1)
      WpRec.WpName = FplDB(i, 2)
      WpRec.WpLat = FplDB(i, 3)
      WpRec.WpLon = FplDB(i, 4)
      WpRec.WpLatDir = FplDB(i, 5)
      WpRec.WpLonDir = FplDB(i, 6)
      Put #1, i, WpRec
   Next i
   Close #1
   SaveOk = 1
   FplSave = SaveOk
   Exit Function

This function makes binary serialization of a matrix of 99 structs (WpRec) to file, using "Open" and "Put" statements. 此函数使用“Open”和“Put”语句对99个结构(WpRec)的矩阵进行二进制序列化。 But I didn't get how it is encoded... It is important to me because I need to rewrite the same serialization in C# but I need to know what encoding method is used for that so I can do the same in C#.... 但是我没有得到它的编码方式......这对我很重要因为我需要在C#中重写相同的序列化但是我需要知道使用什么编码方法,所以我可以在C#中做同样的事情。 ..

The tricky bit in VB6 was that you were allowed to declare structures with fixed length strings so that you could write records containing strings that didn't need a length prefix. VB6中的棘手问题是你被允许声明具有固定长度字符串的结构,这样你就可以编写包含不需要长度前缀的字符串的记录。 The length of the string buffer was encoded into the type instead of needing to be written out with the record. 字符串缓冲区的长度编码为类型,而不是需要与记录一起写出。 This allowed for fixed size records. 这允许固定大小的记录。 In .NET, this has kind of been left behind in the sense that VB.NET has a mechanism to support it for backward compatibility, but it's not really intended for C# as far as I can tell: How to declare a fixed-length string in VB.NET? 在.NET中,由于VB.NET有一种支持它向后兼容的机制,所以它有点落后,但据我所知,它并不是真正用于C#: 如何声明一个固定长度的字符串在VB.NET中? .

.NET seems to have a preference for generally writing out strings with a length prefix, meaning that records are generally variable-length. .NET似乎倾向于通常使用长度前缀写出字符串,这意味着记录通常是可变长度的。 This is suggested by the implementation of BinaryReader.ReadString . 这是由BinaryReader.ReadString的实现建议的。

However, you can use System.BitConverter to get finer control over how records are serialized and de-serialized as bytes (System.IO.BinaryReader and System.IO.BinaryWriter are probably not useful since they make assumptions that strings have a length prefix). 但是,您可以使用System.BitConverter更好地控制如何将记录序列化和反序列化为字节(System.IO.BinaryReader和System.IO.BinaryWriter可能没用,因为它们假设字符串具有长度前缀) 。 Keep in mind that a VB6 Integer maps to a .NET Int16 and a VB6 Long is a .Net Int32. 请记住,VB6 Integer映射到.NET Int16,VB6 Long是.Net Int32。 I don't know exactly how you have defined your VB6 structure, but here's one possible implementation as an example: 我不知道你是如何定义VB6结构的,但这里有一个可能的实现作为例子:

class Program
{
  static void Main(string[] args)
  {
     WpRecType[] WpRec = new WpRecType[3];
     WpRec[0] = new WpRecType();
     WpRec[0].WpIndex = 0;
     WpRec[0].WpName = "New York";
     WpRec[0].WpLat = 40.783f;
     WpRec[0].WpLon = 73.967f;
     WpRec[0].WpLatDir = 1;
     WpRec[0].WpLonDir = 1;
     WpRec[1] = new WpRecType();
     WpRec[1].WpIndex = 1;
     WpRec[1].WpName = "Minneapolis";
     WpRec[1].WpLat = 44.983f;
     WpRec[1].WpLon = 93.233f;
     WpRec[1].WpLatDir = 1;
     WpRec[1].WpLonDir = 1;
     WpRec[2] = new WpRecType();
     WpRec[2].WpIndex = 2;
     WpRec[2].WpName = "Moscow";
     WpRec[2].WpLat = 55.75f;
     WpRec[2].WpLon = 37.6f;
     WpRec[2].WpLatDir = 1;
     WpRec[2].WpLonDir = 2;
     byte[] buffer = new byte[WpRecType.RecordSize];
     using (System.IO.FileStream stm = 
        new System.IO.FileStream(@"C:\Users\Public\Documents\FplDb.dat",
        System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite))
     {
        WpRec[0].SerializeInto(buffer);
        stm.Write(buffer, 0, buffer.Length);
        WpRec[1].SerializeInto(buffer);
        stm.Write(buffer, 0, buffer.Length);
        WpRec[2].SerializeInto(buffer);
        stm.Write(buffer, 0, buffer.Length);

        // Seek to record #1, load and display it
        stm.Seek(WpRecType.RecordSize * 1, System.IO.SeekOrigin.Begin);
        stm.Read(buffer, 0, WpRecType.RecordSize);
        WpRecType rec = new WpRecType(buffer);
        Console.WriteLine("[{0}] {1}: {2} {3}, {4} {5}", rec.WpIndex, rec.WpName,
           rec.WpLat, (rec.WpLatDir == 1) ? "N" : "S",
           rec.WpLon, (rec.WpLonDir == 1) ? "W" : "E");
     }
  }
}

class WpRecType
{
  public short WpIndex;
  public string WpName;
  public Single WpLat;
  public Single WpLon;
  public byte WpLatDir;
  public byte WpLonDir;

  const int WpNameBytes = 40; // 20 unicode characters
  public const int RecordSize = WpNameBytes + 12;

  public void SerializeInto(byte[] target)
  {
     int position = 0;
     target.Initialize();
     BitConverter.GetBytes(WpIndex).CopyTo(target, position);
     position += 2;
     System.Text.Encoding.Unicode.GetBytes(WpName).CopyTo(target, position);
     position += WpNameBytes;
     BitConverter.GetBytes(WpLat).CopyTo(target, position);
     position += 4;
     BitConverter.GetBytes(WpLon).CopyTo(target, position);
     position += 4;
     target[position++] = WpLatDir;
     target[position++] = WpLonDir;
  }

  public void Deserialize(byte[] source)
  {
     int position = 0;
     WpIndex = BitConverter.ToInt16(source, position);
     position += 2;
     WpName = System.Text.Encoding.Unicode.GetString(source, position, WpNameBytes);
     position += WpNameBytes;
     WpLat = BitConverter.ToSingle(source, position);
     position += 4;
     WpLon = BitConverter.ToSingle(source, position);
     position += 4;
     WpLatDir = source[position++];
     WpLonDir = source[position++];
  }

  public WpRecType()
  {
  }

  public WpRecType(byte[] source)
  {
     Deserialize(source);
  }
}

Add a reference to Microsoft.VisualBasic and use FilePut 添加对Microsoft.VisualBasic的引用并使用FilePut

It is designed to assist with compatibility with VB6 旨在帮助兼容VB6

The VB6 code in your question would be something like this in C# (I haven't compiled this) 您的问题中的VB6代码在C#中会是这样的(我还没编译过)

Microsoft.VisualBasic.FileOpen (1, WritingPath & "\FplDb.txt", OpenMode.Random, 
  RecordLength:=Marshal.SizeOf(WpRec))
for (i = 1; i < 100 ; i++) {
  WpRec.WpIndex = FplDB(i, 1)
  WpRec.WpName = FplDB(i, 2)
  WpRec.WpLat = FplDB(i, 3)
  WpRec.WpLon = FplDB(i, 4)
  WpRec.WpLatDir = FplDB(i, 5)
  WpRec.WpLonDir = FplDB(i, 6)
  Microsoft.VisualBasic.FilePut(1, WpRec, i)
}
Microsoft.VisualBasic.FileClose(1)

I think Marshal.SizeOf(WpRec) returns the same value that Len(WpRec) will return in VB6 - do check this though. 我认为Marshal.SizeOf(WpRec)返回的值与Len(WpRec)将在VB6中返回的值相同 - 请检查一下。

The put statement in VB6 does not do any encoding. VB6中的put语句不进行任何编码。 It saves a structure just as it is stored internally in memory. 它保存了一个结构,就像它存储在内存中一样。 For example, put saves a double as a 64-bit floating point value, just as it is represented in memory. 例如, put将double保存为64位浮点值,就像它在内存中表示一样。 In your example, the members of WpRec are stored in the put statement just as WpRec is stored in memory. 在您的示例中,WpRec的成员存储在put语句中,就像WpRec存储在内存中一样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM