views:

72

answers:

1

I have upgraded some VB6 code, which uses fixed length strings in custom types, to VB .NET by using the UpgradeWizard and am having trouble with the use of the LSet method that I was hoping someone could help me out with.

The Existing VB6 code (type declarations);

Public Type MyType
    PROP1       As String * 15
    PROP2       As String * 25
End Type

Public Type MyTypeBuffer
    Buffer As String * 40
End Type

Example usage;

LSet instOfMyTypeBuffer.Buffer = ...
LSet instOfMyType = instOfMyTypeBuffer

What would be an appropriate way to upgrade this to .NET?

Using the UpgradeWizard, I get the following;

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
 _
Public Structure MyType
    <VBFixedString(15),System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr,SizeConst:=15)> _
    Dim PROP1 As FixedLengthString

    <VBFixedString(25),System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr,SizeConst:=25)> _
    Dim PROP2 As FixedLengthString

    Public Shared Function CreateInstance() As MyType
        Dim result As New MyType
        result.PROP1 = New FixedLengthString(15)
        result.PROP2 = New FixedLengthString(25)
        Return result
    End Function
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
 _
Public Structure MyTypeBuffer
    <VBFixedString(CLVHDR_REC_LENGTH),System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr,SizeConst:=40)> _
    Dim Buffer As FixedLengthString
    Public Shared Function CreateInstance() As MyTypeBuffer
        Dim result As New MyTypeBuffer
        result.Buffer = New FixedLengthString(40)
        Return result
    End Function
End Structure

FixedLengthString is coming from the namespace Microsoft.VisualBasic.Compatibility.VB6.

Where the Upgrade Wizard fails is when it comes to LSet. It produced the following;

instOfMyTypeBuffer.Buffer = LSet(...)
instOfMyType = LSet(instOfMyTypeBuffer)

Which fails to compile, giving these errors;

Value of type 'String' cannot be converted to 'Microsoft.VisualBasic.Compatibility.VB6.FixedLengthString'

Argument not specified for parameter 'Length' of 'Public Function LSet(Source As String, Length As Integer) As String'

Value of type 'MyTypeBuffer' cannot be converted to 'String'

So, I can use ToString() to get part of the way there, but there is still the issue of the LSet method call itself. What should I do to recreate the original functionality? Has the Upgrade Wizard given me a totally inappropriate conversion, or is it salvageable into something usable?

+1  A: 

LSet is a rather quirky statement in VB6: see description in the manual.

  • When used on strings, it left-aligns the string in the original string and replaces any leftover characters with spaces.
  • When used on user-defined types, it just copies the memory from one user-defined type over the other, even if they had different definitions. This is not recommended.

It's being used in a particularly quirky way in the code you have.

  1. LSet instOfMyTypeBuffer.Buffer = ...
    This is redundant both in the VB6 and the migrated Vb.Net. When you assign a new value to a fixed-length string, it always pads out with spaces anyway!
    So just change to this (in either VB6 or VB.Net)
    instOfMyTypeBuffer.Buffer = ...
  2. LSet instOfMyType = instOfMyTypeBuffer
    More interesting. This copies the memory from an instance of one type into an instance of another type, with no checks. Gulp!
    Looking at the definitions of the types, I think this simply puts the first 15 characters from instOfMyBuffer into instOfMyType.PROP1 and the remaining 25 characters into instOfMyType.PROP2.
    I have occasionally seen this used as an ugly way of processing fixed-length string records read from a file. For example the first fifteen characters might be a person's first name, and the next 25 the last name.
    You could just replace with this code (in either VB6 or VB.Net).
    instOfMyType.PROP1 = Left(instOfMyBuffer.Buffer, 15)
    instOfMyType.PROP2 = Mid(instOfMyBuffer.Buffer, 16)

Hans suggested ditching the fixed-length strings. If that's easy - and it depends on the rest of your code base, it might be easy, or it might be hard - it's good advice.

MarkJ
@MarkJ : Thanks for your explanation - this is legacy code that I've been tasked with migrating and it's my first experience with this kind of data handling. I did a bit of research myself and came across a C# solution; http://www.codeproject.com/KB/cs/gil_structs.aspx Do you think something like this might also be appropriate? I guess that it's still not really in the spirit of .NET, but do you think it might do the trick?
C.McAtackney
@C.McAtackney Good find - I think that might do the trick. Possibly it might need to "pin" the buffer to stop the .Net framework moving it part way through? Like Hans does in [his other answer](http://stackoverflow.com/questions/3863191/loading-binary-data-into-a-structure/3863658#3863658). In any case it's still just as ugly, incomprehensible and potentially risky as the original code was! I would recommend using `Left()` and `Mid()` instead, or the .Net equivalent `.Substring()`
MarkJ
@MarkJ : I implemented Hans' approach and it worked well. I then went back and scrutinised the original code and can't come up with any other explanation as to why LSet was used in this way other than convenience. As such, I've decided to implement my own FixedLengthString class and a parent type which maps the input string onto a Dictionary<string,FixedLengthString>. It works well, and I've included some use of String.PadRight(...) to recreate the original behaviour. Thanks for all the help, much appreciated.
C.McAtackney
MarkJ