Converting a VB6 module to VB.NET - vb.net

I'm almost done converting a module from VB6 to VB.NET, but I'm having trouble with the following 2 quotes and am wondering if there's any way to go about this:
Structure AUDINPUTARRAY
bytes(5000) As Byte
End Structure
I'm trying to change that bytes line to: Dim bytes(5000) as Byte
but it's not letting me define the size in a structure.
Here's the second one:
Private i As Integer, j As Integer, msg As String * 200, hWaveIn As integer
I haven't a clue on how to convert: msg As String * 200

you cannot declare an initial size in VB.Net , you can set its size later using Redim statement in constructor or wherever needed
Structure AUDINPUTARRAY
Public bytes() As Byte
Public Sub New(ByVal size As Integer)
ReDim bytes(size) ' set size=5000
End Sub
End Structure
In Visual Basic .NET, you cannot declare a string to have a fixed length unless you use the VBFixedStringAttribute Class attribute in the declaration. The code in the preceding example causes an error.
You declare a string without a length. When your code assigns a value to the string, the length of the value determines the length of the string
see http://msdn.microsoft.com/en-us/library/f47b0zy4%28v=vs.71%29.aspx
. so your declarration will become
Private i As Integer, j As Integer, hWaveIn As Integer
<VBFixedString(200)> Private msg As String

You can do this via attributes
Public Structure <StructLayout(LayoutKind.Sequential)> AUDINPUTARRAY
Public <MarshalAs(UnmanagedType.ByValArray, SizeConst := 5000)>
Bytes() As Byte
End Structure

I would suggest that, while refactoring your code from VB6 to .net, that you take another look at whether you even want to emulate the fixed-length msg As String * 200. If you were counting on the fixed-length string so that you could chop characters off of the end, and still have a 200-character record, that's messy code that depends on a function's side effects.
When we converted from VB6 (a still-ongoing process), it made the intent of the code clearer if we explicitly set the string to a 200-byte block of spaces. Perhaps by declaring:
String msg = String(' ', 200)
(if that's valid in VB.net as well as C#).

Related

Widening: Change in representation

Requesting your help to understand the concept of widening better!
I came across the following statement w.r.t 'Widening Conversion' in VB.Net. From the msdn documentation on the topic I found the following: Widening conversions preserve the source value but can change its representation. This occurs if you convert from an integral type to Decimal, or from Char to String. The link to the page is found below:
https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/data-types/widening-and-narrowing-conversions
My Question is as follows: I wrote the following code to understand the meaning of the statement "...preserve the source value but can change its representation". But I see no difference in the output when I print the integer or the decimal. Then what does the phrase mean....what is the meaning of "...can change its representation"?
Module Module1
Sub Main()
Dim i As Integer = 5
Dim d As Decimal = i 'widening
Console.WriteLine(i)
Console.WriteLine(d)
'Both prints "5"...no difference in representation
Console.ReadLine()
End Sub
End Module
Can someone also please give an example to demonstrate how the representation changes when we convert a char value to a string?
It means the internal presentation of the number (in your case). Try to convert, say, Single to Double:
Sub Main(args As String())
Dim sng As Single = 1.23E+32
Dim dbl As Double = sng
Console.WriteLine($"Single: {sng}")
Console.WriteLine($"Double: {dbl}")
End Sub
' Output:
' Single: 1,23E+32
' Double: 1,2300000079302825E+32

Wrong Number of Characters returned by Len

I am new to VB and have a simple program. I just want the program to display in a message box the number of characters in a long variable. I am using the Len() function. The code is as follows.
Try
Dim num As Long = 1230456985623145
Dim numLength As Long
numLength = Len(num)
MessageBox.Show(numLength.ToString())
Catch ex As Exception
End Try
Simple. However when i run the function, it returns a value of 8 instead of the actual value. Can anyone tell me what i'm doing wrong. Do i need to add anything else to obtain the right value
It should be like this:
Dim num As Long = 1230456985623145
Dim numLength As Long
numLength = Len(num.ToString())
MessageBox.Show(numLength.ToString())
If you forgot to use ToString(), Len function returns the number of bytes required to store the variable, which is 8 because a Long variable requires 8 byte to store.
Definition of Len function in MSDN:
Returns an integer containing either the number of characters in a
string or the nominal number of bytes required to store a variable.
In your original code (before your edit):
You use Name as a parameter in your Len function. Since your code is a WinForm, the Name is a property of the Form. Check the value of the Name using:
MessageBox.Show(Name)
String.Length
Using the Length property of a string is more preferable. Like Adrian Wragg said, it's easier to convert your codes between the languages which are supported by .Net (C#, VB and F#).

Replacing Characters Simultaneously

Hey guys I'm trying to make a program that helps people encrypt messages and decrypt messages using the Caesar shift cipher, I know it's probably already been done, I want to have a go myself though.
The problem I've been having is when it comes to encrypting the text. The user selects a number (between 1-25) and then the application will change the letters corresponding to the number chosen, e.g. if the user inputs "HI" and selects 2, both characters are moved two places down the alphabet outputting "JK". My main problem is the replacing characters though, mostly because I've set up the program to be able to encrypt large blocks of text, because my code is:
If cmbxKey.Text = "1" Then
If txtOutput.Text.Contains("a") Then
sOutput = txtOutput.Text.Replace("a", "b")
txtOutput.Text = sOutput
End If
If txtOutput.Text.Contains("b") Then
sOutput = txtOutput.Text.Replace("b", "c")
txtOutput.Text = sOutput
End If
End If
This means if the user inputs "HAY" it will change it to "HBY" and then because of the second if statement it will change it to "HCY" but I only want it to be changed once. Any suggestions to avoid this???? Thanks guys
Since you want to shift all characters, start out by looping though the characters using something like ToArray:
For each s as string in txtOutput.Text.ToArray
'This will be here for each character in the string, even spaces
Next
Then, rather than having cases for every letter, look at it's ascii number:
ACS(s)
...and shift it by the number you want to. Keep in mind that if the number is greater than (I don't know if you want upper/lower case) 122, you want to subtract 65 to get you back to "A".
Then you can convert it back into a character using:
CHR(?)
So this might look something like this:
Dim sb as new text.StringBuilder()
For each s as string in txtOutput.Text.ToArray
If asc(s) > 122 Then
sb.append(CHR(ASC(s) + ?YourShift? - 65)
Else
sb.append(CHR(ASC(s) + ?YourShift?)
END IF
Next
txtOutput.Text = sb.ToString
A very simple method of changing your application while keeping your strategy is to replace the lower case characters with upper case characters. Then they won't be recognized by the Replace method anymore.
Obviously, the problem is that you want to implement an algorithm. In general, an algorithm should be smart in the sense that you don't have to do the grunt work. That's why a method such as the one presented by Steve is smarter; it doesn't require you to map each character separately, which is tedious, and - as most tedious tasks - error prone.
One big issue arise when you're facing a String that the basic Alphanumeric table can't handle. A String that contains words like :
"Déja vu" -> The "é" is going to be what ?
And also, how about encoding the string "I'm Aaron Mbilébé" if you use .ToUpper().
.ToUpper returns "I'M AARON MBILÉBÉ".
You've lost the casing, and how do you handle the shifting of "É" ?
Of course, a code should be smart as pointed above, and I was used to deal with strings just by using the System.Text.ASCIIEncoding to make things easier. But from the moment I started to use large amount of textual datas, sources from the web, files (...) I was forced to dig deeper, and seriously consider string encoding (and System Endianness by the way, when coding and decoding string to/from array of bytes)
Re-think of what do you really want in the end. If you're the only one to use your code, and you're certain that you'll only use A..Z, 0..9, a..z, space and a fixed amount of allowed characters (like puntuation) then, just build a Table containing each of those chars.
Private _AllowedChars As Char() = { "A"c, "B"c, ... "0"c, "1"c, .. "."c, ","c ... }
or
Private _AllowedChars As Char() = "ABCDEF....012...abcd..xyz.;,?:/".ToCharArray()
Then use
Private Function ShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
Dim AllChars As Char() = CurrentString.ToCharArray()
Dim FinalChars As Char()
Dim i As Integer
FinalChars = New Char(AllChars.Length - 1) {} ' It's VB : UpperBound is n+1 item.
' so n items is UpperBound - 1
For i = 0 To AllChars.Length - 1
FinalChars(i) = _AllowedChars((Array.IndexOf(_AllowedChars, AllChars(i)) + ShiftValue) Mod _AllowedChars.Length)
Next
Return New String(FinalChars)
End Function
And
Private Function UnShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
' ... the same code until :
FinalChars(i) = _AllowedChars((Array.IndexOf(_AllowedChars, AllChars(i)) - ShiftValue + _AllowedChars.Length) Mod _AllowedChars.Length)
' ...
End Function
^^ Assuming ShiftValue is always positive (defined once)
But again, this only works when you have a predefined set of allowed characters. If you want a more flexible tool, you ought to start dealing with encodings, array of byte, BitConverter and have a look at system endianness. That's why I asked if someone else is goind to use your application : let's try this string :
"Xin chào thế giới" ' which is Hello World in vietnamese (Google Trad)
In that case, you may give up..? No ! You ALWAYS have a trick in your cards !
Just create your allowed chars on the fly
Private _AllowedChars As New SortedList(Of Char, Char)
-> get the string to encode (shift)
Private Function ShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
Dim AllChars As Char() = CurrentString.ToCharArray()
Dim FinalChars As Char()
Dim i As Integer
' Build your list of allowed chars...
_AllowedChars.Clear()
For i = 0 To AllChars.Length - 1
If Not _AllowedChars.ContainsKey(AllChars(i)) Then
_AllowedChars.Add(AllChars(i), AllChars(i))
End If
Next
' Then, encode...
FinalChars = New Char(AllChars.Length - 1) {}
For i = 0 To AllChars.Length - 1
FinalChars(i) = _AllowedChars.Keys.Item((_AllowedChars.IndexOfKey(AllChars(i)) + ShiftValue) Mod _AllowedChars.Count)
Next
Return New String(FinalChars)
End Function
The same for Unshift/decode.
Note : in foreing languages, the resulting string is pure garbage and totally unreadable, unless you (un)shift the chars again.
However, the main limitation of this workaround is the same as the fixed chars array above : Once you encode your string, and add a char in your encoded string that doesn't exists in the initial generated allowed chars, then you've nuked your data and you won't be able to decode your string. All you'll have is pure garbage.
So one day... one day maybe, you'll have to dig deeper at the byte level of the thing, in a defined extended encoding (Unicode/UTF8/16) to secure the integrity of your data.

How can a structure with reserved space be marshalled?

Problem:
I have a struct of a fixed size that I'm trying to marshal. This struct contains a number of useful fields for the current version of the struct and a specified amount of unused space at the end that is reserved for future modifications.
How should I design this structure so that the size of the reserved space will be automatically updated when I modify the structure?
While the following would solve my problem
'Variable size structure
<StructLayout(LayoutKind.Sequential, Pack:=1)>
Structure UsefulData
Dim foo As SByte
Dim bar As Integer
Dim foobar As Short
End Structure
Const MAX_SIZE As Integer = 20
'Fixed size structure
<StructLayout(LayoutKind.Sequential, Pack:=1, Size:=MAX_SIZE>
Structure Data
Dim current As UsefulData
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=MAX_SIZE-System.Runtime.InteropServices.Marshal.SizeOf(GetType(UsefulData)))>
Dim reserved As SByte()
End Structure
but doesn't compile as System.Runtime.InteropServices.Marshal.SizeOf(GetType(UsefulData)) is not a constant expression. Any ideas?
Upon further reflection on this problem I've come to question the validity of my desire to expose the reserved space at the end of the struct. In reality nothing should ever reference the reserved section. If something ever needed to, then the correct approach would be to modify the structure itself to expose the relevant parts of the reserved space.
Consequently the structure should look as follows:
<StructLayout(LayoutKind.Sequential, Size:=20, Pack:=1)>
Structure Data
Dim foo As SByte
Dim bar As Integer
Dim foobar As Short
End Structure
While I don't think hard-coding the array length is the ideal solution, I'm placing this answer here in case there are no other alternatives.
Private const TOTAL_SIZE As Integer = 20
Private const RESERVED_SIZE As Integer = 7
<StructLayout(LayoutKind.Sequential, Pack:=1)>
Structure Data
Dim foo As SByte
Dim bar As Integer
Dim foobar As Short
<MarshalAs(UnManagedType.ByValArray, SizeConst:=RESERVED_SIZE)>
Dim reserved As SByte()
End Structure
I could then add either a unit test or custom build action to ensure that System.Runtime.InteropServices.Marshal.Sizeof(GetType(Data)) = TOTAL_SIZE.

VB.net Passing a structure to unmanaged dll

I'm migrating some VB6 code to VB.net,
the code contains a structure that contains 1d arrays, 2d arrays, and few other variables.
The general outline of the Vb.net structure is as under
Public Structure Test
Dim a As Single
Dim b As Single
Dim c As Single
<VBFixedArray(2)> Dim arr1() As Single
<VBFixedArray(2, 2)> Dim mdarr1(,) As Single
<VBFixedArray(4)> Dim arr2() As Byte
<VBFixedArray(4)> Dim arr3() As Short
<VBFixedArray(3, 2)> Dim mdarr2(,) As Integer
Dim d As Integer
Dim e As Decimal
End Structure
The call to the dll is declared as under
Public Declare Sub getState Lib "val.dll" (ByRef state As Test)
Elsewhere on this site I realized that we have to "marshal" the structure to allow it to be compatible with the unmanaged code that is about to accept it.
However I still receiving runtime errors when running the code, I don't have any clue of how to use the System.Runtime.InteropServices.Marshal class.
What would be the correct way to pass this structure to the dll?
EDIT:
The original VB6 data type is
Public Type Test
a As Single
b As Single
c As Single
arr1(0 To 2) As Single
mdarr1(0 To 2, 0 To 2) As Single
arr2(0 To 4) As Byte
arr3(0 To 4) As Integer
mdarr2(0 To 3, 0 To 2) As Long
d As Long
e As Currency
End Type
Do you have the source code for getState in the val.dll? If it's written in C or C++, and you have the source code or even just the headers, you could use the P/Invoke Assistant to automatically generate your VB.Net code.
Alternatively... (and please do post the original VB6 structure!)
You might need to allocate the arrays before calling getState, e.g. state.arr1 = {0.0, 0.0} etc.
The Decimal variable e could cause you a problem. In VB6 this was probably a Currency variable, and Decimal is not an exact equivalent as far as I can remember. There will be a way to tell VB.Net to marshal it like a Currency. Perhaps adding an attribute like this...
Sample code:
Imports System.Runtime
Public Structure Test
''blah blah
<InteropServices.MarshalAs(InteropServices.UnmanagedType.Currency)> _
Dim e As Decimal
''blah blah