Option Explicit
Private Declare Sub CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function SysAllocString Lib "oleaut32" (ByVal pUnicodeString As Long) As String
Private Declare Sub ZeroMemory Lib "KERNEL32" Alias "RtlMoveMemory" (dest As Any, ByVal numBytes As Long)
Sub main()
Dim Sr() As Byte
Dim u As TEST_TYPE
Dim u2 As TEST_TYPE
Dim lp As Long
With u
.a = "a"
.b = "b"
.c = 12345
End With
Sr = Serialise(VarPtr(u), "TEST_TYPE", "c:\test.tlb")
DeSerialise VarPtr(u2), "TEST_TYPE", "c:\test.tlb", Sr
End Sub
Public Function Serialise(pUDT As Long, UDTName As String, TLBFile As String) As Byte()
On Error GoTo ERR_Serialise
Dim TypeLib As TypeLibInfo
Dim Record As RecordInfo
Dim Mmbers As Members
Dim Member As MemberInfo
Dim Stream() As Byte
Dim pMemPos As Long
ReDim Stream(1024)
'**************************
'* Open type library . . .
'**************************
Set TypeLib = New TypeLibInfo
TypeLib.ContainingFile = TLBFile
'*********************************
'* Get the UDT definition . . .
'*********************************
For Each Record In TypeLib.Records
If Record.Name = UDTName Then
Set Mmbers = Record.Members
Exit For
End If
Next
'****************************************
'* Serialise Each and every member . . .
'****************************************
pMemPos = VarPtr(Stream(0))
SerialiseUDT Mmbers, Stream, pUDT, pMemPos
'ReDim Preserve Stream(pMemPos - VarPtr(Stream(0)))
Serialise = Stream
Exit Function
ERR_Serialise:
Set TypeLib = Nothing
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End Function
Public Sub DeSerialise(pUDT As Long, UDTName As String, TLBFile, Stream() As Byte)
On Error GoTo ERR_DeSerialise
Dim TypeLib As TypeLibInfo
Dim Record As RecordInfo
Dim Mmbers As Members
Dim Member As MemberInfo
Dim pBufPos As Long
Dim pMemPos As Long
'**************************
'* Open type library . . .
'**************************
Set TypeLib = New TypeLibInfo
TypeLib.ContainingFile = TLBFile
'*********************************
'* Get the UDT definition . . .
'*********************************
For Each Record In TypeLib.Records
If Record.Name = UDTName Then
Set Mmbers = Record.Members
Exit For
End If
Next
'****************************************
'* Serialise Each and every member . . .
'****************************************
DeSerialiseUDT Mmbers, VarPtr(Stream(0)), pUDT
Exit Sub
ERR_DeSerialise:
Set TypeLib = Nothing
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End Sub
Private Sub SerialiseUDT(Mmbers As Members, Buffer() As Byte, pUDT As Long, pStream As Long)
On Error GoTo ERR_SerialiseUDT
Dim Member As MemberInfo
For Each Member In Mmbers
Select Case Member.ReturnType.VarType
Case vbString:
SerialiseString pUDT, pStream
Case vbInteger
SerialiseData pUDT, pStream, 2, 2
End Select
Next
Exit Sub
ERR_SerialiseUDT:
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End Sub
Private Sub DeSerialiseUDT(Mmbers As Members, pStream As Long, pUDT As Long)
On Error GoTo ERR_DeSerialise
Dim Member As MemberInfo
For Each Member In Mmbers
Select Case Member.ReturnType.VarType
Case vbString:
DeSerialiseString pUDT, pStream
Case vbInteger
DeSerialiseData pUDT, pStream, 2, 2
End Select
Next
Exit Sub
ERR_DeSerialise:
Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End Sub
Private Sub SerialiseData(pUDT As Long, pStream As Long, Alignment As Long, Size As Long)
Dim lPadding As Long
'Increment forward (if we have too) to make sure that we
'only pick up the data and not the preceding padding bytes
If Not (pUDT Mod Alignment = 0) Then
lPadding = (Alignment - (pUDT Mod Alignment))
pUDT = pUDT + lPadding
End If
'Serialise the data
CopyMemory ByVal pStream, ByVal pUDT, Size
'Increment pointers accordingly
pUDT = pUDT + Size
pStream = pStream + Size
End Sub
Private Sub DeSerialiseData(pUDT As Long, pStream As Long, Alignment As Long, Size As Long)
Dim lPadding As Long
'Pad data, if necessary
If Not (pUDT Mod Alignment = 0) Then
lPadding = (Alignment - (pUDT Mod Alignment))
pUDT = pUDT + lPadding
End If
CopyMemory ByVal pUDT, ByVal pStream, Size
pUDT = pUDT + Size
pStream = pStream + Size
End Sub
Private Sub SerialiseString(pUDT As Long, pStream As Long)
Dim Temp As String
Dim pStr As Long
Dim lPadding As Long
'Ensure the alignment's good
If Not (pUDT Mod 4 = 0) Then
lPadding = (4 - (pUDT Mod 4))
pUDT = pUDT + lPadding
End If
'deref pUDT, and get the actual string data
CopyMemory pStr, ByVal pUDT, 4
Temp = StrConv(SysAllocString(pStr), vbFromUnicode) & Chr(0)
'Copy over the string data
CopyMemory ByVal pStream, ByVal StrPtr(Temp), LenB(Temp)
'Increment pointers
pUDT = pUDT + 4
pStream = pStream + LenB(Temp)
End Sub
Private Sub DeSerialiseString(pUDT As Long, pStream As Long)
Dim Temp As String
Dim lPadding As Long
'Ensure the alignment's good
If Not (pUDT Mod 4 = 0) Then
lPadding = (4 - (pUDT Mod 4))
pUDT = pUDT + lPadding
End If
'Get the string from the stream
Temp = StrConv(SysAllocString(pStream), vbFromUnicode) & Chr(0)
'Copy the BSTR descriptor to the UDT
CopyMemory ByVal pUDT, ByVal VarPtr(Temp), 4
'Increment pointers
pStream = pStream + LenB(Temp)
pUDT = pUDT + 4
'Clear temp string descriptor to avoid VB's garbage collection
CopyMemory ByVal VarPtr(Temp), 0&, 4
End Sub