9

I need to read data from a legacy database file produced by Visual Basic 6. From the legacy software I found that file was written using Put and passing sort of records as parameters to the Put function. These structures are defined as follows:

Type THE_TYPE
    FIELD_1 As Single
    FIELD_2 As String * 20
    FIELD_3(1 To 50) As Single
    FIELD_4(1 To 10) As String * 1
End Type

My Types are larger and more complex but I've put in THE_TYPE the different definitions I have in my project. I've found that importing Microsoft.VisualBasic gives me access to VB functions similar to those used to write the file, so I'm opening and closing the file with FileSystem.OpenFile() and .CloseFile(); now I need to finally read data contained and since the original function was:

Public RecordContent As THE_TYPE
[...] 
Get #1, recordNumber, RecordContent 

I suppose I can use something similar, like Microsoft.VisualBasic.FileSystem.FileGet().
So the question is, how do I define a container, I suppose a class, similar to the original VB6 Type THE_TYPE? How do I call .FileGet() to correctly fill this object?

GSerg
  • 76,472
  • 17
  • 159
  • 346
ccalboni
  • 12,120
  • 5
  • 30
  • 38
  • While GSerg may have pointed you in the right direction, know that there are big differences in the way `Put` works between the `Open` modes of `Random` and `Binary`. See http://msdn.microsoft.com/en-us/library/aa266212(v=VS.60).aspx and http://msdn.microsoft.com/en-us/library/aa266177(v=vs.60).aspx for details. I bring this up because if the `String` and array members are all of fixed size, I wondered if the VB6 app used the `Open ... Binary` mode. – rskar Mar 27 '12 at 13:44
  • 1
    +1 For importing `Microsoft.VisualBasic` and using `FileSystem`. You'd be amazed [how](http://stackoverflow.com/questions/7290976/vb6-how-are-binary-files-encoded-using-put-statement) [often](http://stackoverflow.com/questions/8886096/converting-quick-basic-to-vb-net-random-access-files) people overlook those! – MarkJ Mar 27 '12 at 19:02

1 Answers1

10

The key is defining attributes properly on the structure declaration in VB.NET. Provided the structure will always be initialized by FileGet, you don't need to manually initialize its fixed fields in a constructor, otherwise, you do.

Structure THE_TYPE
    Public FIELD_1 As Single
    <VBFixedString(20), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=20)> Public FIELD_2 As String
    <VBFixedArray(49)> Public FIELD_3 As Single()
    <VBFixedArray(9)> Public FIELD_4 As Char()
End Structure

Obviously, the arrays will have to start from zero, so the upper bounds are shifted down.

Reading from the file:

Dim d As System.ValueType = New THE_TYPE()

FileOpen(1, "...", OpenMode.Random, OpenAccess.Read, OpenShare.Default, 234)
FileGet(1, d, 1)
FileClose(1)

234 is the size of the structure in VB6. It's bigger in VB.NET, so you want to hardcode that.

GSerg
  • 76,472
  • 17
  • 159
  • 346
  • +1. I have not tested this, and hardcoding 234 might be safest, but would `Marshal.Sizeof` return the correct structure size? – MarkJ Mar 27 '12 at 19:03
  • @MarkJ No, in this case it returns same as `Len`, which is 244. This is probably because `Char` is two bytes in .net. – GSerg Mar 27 '12 at 19:05
  • TYVM!! In C#: `System.ValueType data = new UserDataStruct();` Than: `FileSystem.FileGet(fileId, ref data);` Will solve '**Error CS1503 Argument 2: cannot convert from** 'ref A' to 'ref System.ValueType'' – ShloEmi Aug 25 '19 at 15:05
  • @GSerg Your solution has worked for me, but in my converted project, I found a more complex data structure. `VB6 code: Channels(1 To 2, 1 To 10, 1 To 2) As String * 1` do you know how I can represent this in vb6 for it to work with the FileGet? i Tried : Dim Channels(,,) As Char : 'Object of type 'System.Char[]' cannot be converted to type 'System.Char[,,]'.' – Dev May 12 '20 at 07:16
  • 2
    @Decoder94 `VBFixedArray` supports up to two dimensions. Try declaring the array as dynamically sized so that `Put` writes the [array descriptor](https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/put-statement#remarks) too which can be read by `Get`. – GSerg May 12 '20 at 07:37
  • Thanks @GSerg I have no control over how the file is written, can I get around this without needing to modify put?(my software only reads no writing) – Dev May 12 '20 at 07:48
  • 2
    @Decoder94 Try `VBFixedArray(40)> Dim Channels() As Char`. – GSerg May 12 '20 at 07:52
  • That works, I have to change the way I access the data in the structure but that's fine. but how did you come up with the number 40? is there a formula? – Dev May 12 '20 at 09:50
  • 2
    @Decoder94 You have 40 elements in the array (2 * 10 * 2), 1 char each. That's 40 chars. – GSerg May 12 '20 at 10:32
  • 1
    @GSerg I have this:Elements(1 To 2, 1 To 22, 1 To 2) As Integer in my vb6 structure and in VB.Net I have converted to Dim Elements() As Short, these are integers does the formula you gave work for this instance? because integers can have 3 digits on so on, whereas chars is one character? – Dev May 12 '20 at 15:30
  • 1
    @Decoder94 An `Integer` is two bytes. Textual representation of these two bytes ("[42](https://en.wikipedia.org/wiki/Decimal)", "[2A](https://en.wikipedia.org/wiki/Hexadecimal)", "[XLII](https://en.wikipedia.org/wiki/Roman_numerals)") does not matter. You have correctly matched the number of elements (2 * 22 * 2 = 88) and the size of the element (`Integer` = `Short`), the rest should not concern you. – GSerg May 12 '20 at 15:52