Using Data Structures with Sockets in Visual Basic
In some cases, it would be useful to be able to send and receive user-defined types (UDTs) through the SocketWrench control. However, since the control only supports the sending and receiving data using the String data type, an error is generated if you attempt to use UDTs directly.
The solution to this problem would be to somehow copy the data into a string, and then use that string with the control. The problem is that you can't simply assign a UDT to a string variable. You need some method of copying the data into the string, and is where the Windows API comes in.
Under Visual Basic 3.0, and the 16-bit version of Visual Basic 4.0 you can use the 16-bit Windows API function hmemcpy(). The function can be declared like this:
Declare Sub CopyMemory Lib "KERNEL" Alias "hmemcpy" _
(hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)
As the declaration shows, the first argument is a destination buffer, the variable that the UDT will be copied into. The second argument is the UDT that you wish to copy. The third argument is the size of the UDT in bytes, which can be obtained using the Len() function. You simply must allocate a string buffer large enough to contain the data, either by statically declaring it, or by using a function such as String().
In the 32-bit version of Visual Basic 4.0, and Visual Basic 5.0, things get a bit more complicated. The reason for this is that the 32-bit versions of Visual Basic use Unicode to represent strings. With standard ANSI strings, each character is represented by a single byte, with a maximum of 256 characters. However, with Unicode, each character is represented by 2 bytes, which can accommodate the various languages that require more than 256 characters, such as Japanese.
To handle the Unicode strings, you first need to allocate a byte array to store the data structure, copy the UDT data into the array, and then convert that array into a string. To copy the memory, you can use the 32-bit RtlMoveMemory Windows function, which can be declared like this:
Private Declare Sub CopyMemory Lib "Kernel32" _
Alias "RtlMoveMemory" _
(lpvDest As Any, lpvSrc As Any, ByVal dwSize&)
Once the data has been copied into the string, you then need to use the Write method (not the SendData and SendLen properties) to write the data out on the socket. This is important because only the Write method can correctly send data which contains embedded null (0) byte characters. Using SendData to write the data on the socket will most likely result in it being corrupted.
To read a data structure, you need to do the inverse. Use the Read method to read the data into a string, and then copy the string data back into your UDT using RtlMoveMemory. Here's an example of how that code would look:
Private Declare Sub CopyMemory Lib "Kernel32" _
Alias "RtlMoveMemory" _
(lpvDest As Any, lpvSrc As Any, ByVal dwSize&)
Private Declare Sub CopyString Lib "Kernel32" _
Alias "RtlMoveMemory" _
(lpvDest As Any, ByVal strSrc As String, ByVal dwSize&)
Private Type MyDataType
nValue As Integer
lValue As Long
strValue As String * 32
End Type
Private Sub Form_Load()
Dim myData As MyDataType
Dim byBuffer(Len(myData)) As Byte, strBuffer As String
Dim nBytes As Integer
'
' Create a socket and connect to the echo port (7);
' the server will simply echo back anything that we
' send to it
'
Socket1.AutoResolve = False
Socket1.Blocking = True
Socket1.HostAddress = "127.0.0.1"
Socket1.RemotePort = 7
Socket1.Connect
'
' Initialize the myData structure to some values
'
myData.nValue = 23
myData.lValue = 91923
myData.strValue = "Testing"
'
' Copy the contents of the myData structure into the byte
' array and convert it to a Unicode string
'
CopyMemory byBuffer(0), myData, Len(myData)
strBuffer = Left$(StrConv(byBuffer, 64), Len(myData))
'
' Reset the values of the myData structure so that
' we can be sure we're getting good data back
'
myData.nValue = 0
myData.lValue = 0
myData.strValue = ""
'
' Send the string to the server; it will send it right
' back to us
'
nBytes = Socket1.Write(strBuffer, Len(myData))
nBytes = Socket1.Read(strBuffer, Len(myData))
Socket1.Disconnect
'
' Use this variation of RtlMoveMemory to copy the string
' back into the myData structure; this is needed to
' convert the Unicode string data
'
CopyString myData, strBuffer, Len(myData)
Debug.Print myData.nValue, myData.lValue, myData.strValue
End Sub
There are a couple of important things to keep in mind when reading and writing user-defined types:
1. Strings should always be pre-allocated in the user-defined type; this is important because the size of the type needs to be fixed.
2. Numeric values in user-defined types are stored in a machine specific (little endian) byte order; attempting to read or write these values on a different machine, such as a Sun workstation, will result in wildly different values. If you are communicating strictly between two Intel-based PCs, then you can safely pass numeric values. However, it is recommended that you pass numeric values as strings in order to preserve the greatest degree of interoperability between different platforms.
3. If you are passing a UDT to an application that is expecting a C structure, make sure that you account for the extra bytes that are usually allocated to the structure so that member variables fall on a word or double-word boundary.
Lastly, always be sure to save your program before testing it when you're making calls to move blocks of memory. Because you are calling directly to the Windows API, many of the safe-guards that Visual Basic provides will not be available. If you inadvertently specify a wrong variable name, or length of the data structure, you can easy cause the program to terminate with a GP fault and loose your work. As the saying goes, it's better to be safe than sorry!
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.