[RESOLVED] VB6 - Unsigned data type (Can I trick?)
OK I'm not very hopeful of a resolution but I thought I'd give it a shot here.
I have an OPC Simulator that I can create "in-memory arrays". These arrays can be of various data types. For example they can be VT_I2, VT_I4, VT_UI2, etc...
If these look familiar to you then keep reading... AFAIK I2 = Signed Integer, I4 = Singed Long (both native VB6 data types). Ergo, UI2 = Unsigned Integer, however VB6 cannot declare a variable as an Unsigned Int or Long (a known limitation to VB6).
As you may have guessed, I have successfully Read data from and Written data to the VT_I2 and the VT_I4 arrays, but am unable to Write to the VT_UI2 array (although strangely I can read from it).
I will try to condense all the code as small as possible.
VB Code:
Public Enum CanonicalDataTypes
CanonDtShort = 2 ' VT_I2
CanonDtLong = 3 ' VT_I4
CanonDtFloat = 4
CanonDtDouble = 5
CanonDtString = 8
CanonDtBool = 11
CanonDtChar = 16
CanonDtByte = 17
CanonDtWord = 18 ' VT_UI2
CanonDtDword = 19 ' VT_UI4
CanonDtArray = 8192 ' VT_Array
End Enum
The following code works for Longs (VT_I4):
VB Code:
Sub AddOPCItem
'
Dim Types(1) As Integer
'
' Works:
'
' VT_I4
OPCItemIDs(1) = "Device_1.aLong"
Types(1) = CanonDtLong + CanonDtArray
'
' ...
'
Dim RequestedDataType As Variant
RequestedDataType = CVar(Types)
'
OPCItemCollection.AddItems ItemCount, OPCItemIDs, ClientHandles, ItemServerHandles, ItemServerErrors, RequestedDataType
'
End Sub
Sub WriteOPC
'
Dim Vals(4) As Long ' DHS good for LONGs (VT_I4)
'
For i = 1 To 4
Vals(i) = CLng("SomeNumericValue")
Next
'
' ...
'
ASyncItemValues(1) = CVar(Vals)
ConnectedGroup.AsyncWrite ItemCount, ASyncItemServerHandles, ASyncItemValues, ASyncItemServerErrors, TransactionID, CancelID
'
End Sub
The following code works for Integers (VT_I2):
VB Code:
Sub AddOPCItem
'
Dim Types(1) As Integer
'
' Works:
'
VT_I2
OPCItemIDs(1) = "Device_1.aShort"
Types(1) = CanonDtShort + CanonDtArray
'
' ...
'
Dim RequestedDataType As Variant
RequestedDataType = CVar(Types)
'
OPCItemCollection.AddItems ItemCount, OPCItemIDs, ClientHandles, ItemServerHandles, ItemServerErrors, RequestedDataType
'
End Sub
Sub WriteOPC
'
Dim Vals(4) As Integer ' DHS - good for SHORTs (VT_I2)
'
For i = 1 To 4
Vals(i) = CInt("SomeNumericValue")
Next
'
' ...
'
ASyncItemValues(1) = CVar(Vals)
ConnectedGroup.AsyncWrite ItemCount, ASyncItemServerHandles, ASyncItemValues, ASyncItemServerErrors, TransactionID, CancelID
'
End Sub
As you can see I can define a VB array of Integers to Write data down to VT_I2's and I can define a VB array of Longs to Write data down to VT_I4's.
However I am unable to create a VB array such that I can Write down to VT_UI2's or VT_UI4's.
I have tried using VB arrays of Integers and VB arrays of Longs in an attempt to Write data down to the VT_UI2 and VT_UI4 OPC arrays, but no data gets written down. No error is reported on either end. Its just like nothing happens.
Question: Can I create a VB Array of "Somethings" to trick the transport layer into accepting something as an Unsigned data type?
I have seen some blurbs about SAFEARRAYs but am unsure how that fits into the equation...
Thanks!
Dave
Re: VB6 - Unsigned data type (Can I trick?)
If I understood correctly, you are passing the arrays within a Variant. This means you can do a very simple trick: use PutMem2 (or RtlMoveMemory) to change the type of the Variant so that VB6 can use it.
This sample demonstrates how you can find the type value:
Code:
Option Explicit
Private Declare Sub GetMem2 Lib "msvbvm60" (ByRef Var As Any, ByRef Value As Integer)
Private Declare Sub PutMem2 Lib "msvbvm60" (ByRef Var As Any, ByVal Value As Integer)
Private Sub Form_Load()
Dim intType As Integer, lngTest() As Long, varTest As Variant
ReDim lngTest(0)
varTest = lngTest
GetMem2 varTest, intType
Debug.Print Hex$(intType)
PutMem2 varTest, &H2013
' automation error (because VB does not support unsigned integers except Byte)
Debug.Print varTest(0)
End Sub
I guess this is enough for you to figure out the rest :)
Edit!
Patching brain gas leak.
Re: VB6 - Unsigned data type (Can I trick?)
Have you tried to use a byte array vs long/integer for the unsigned?
Writing the unsigned values to the byte array would be a bit of work, but not too difficult.
As a test, dim Vals(3) As Byte, then change Vals(3)=1 and see if your OPC control accepts the byte array. If it does, then you'd only need some code to copy the unsigned to the byte array.
Re: VB6 - Unsigned data type (Can I trick?)
Hey got another idea if any of the above don't work. I am guessing that your OPC control is checking the variant type of the passed variant and if it isn't UI4 then it is ignoring it. So, how about forcing the issue with CopyMemory API...
Code:
Dim vData As Variant, vType As Integer
vType = &H2000 Or 19 ' array of UI4
vData = CVar(Vals())
' write to the variant structure what type of data it contains
CopyMemory ByVal VarPtr(vData), vType, 2&
' now the variant thinks it is UI4; how now?
ASyncItemValues(1) = vData
If it works, UI2 is &H2000 Or 18
Listing of Variant Type codes
Edited: I see I pretty much repeated what Merri posted. Even though VB can't use the vData variant while it thinks it is UI4, your OPC should be able to. If the vData variant needs to be read by your VB app, then you'd have to change the UI4 to simply I4 which is &H2000 Or 3 for a an array of longs; then you'd be able to read the data back using VB.
Re: VB6 - Unsigned data type (Can I trick?)
Quote:
Originally Posted by
LaVolpe
Have you tried to use a byte array vs long/integer for the unsigned?
Writing the unsigned values to the byte array would be a bit of work, but not too difficult.
As a test, dim Vals(3) As Byte, then change Vals(3)=1 and see if your OPC control accepts the byte array. If it does, then you'd only need some code to copy the unsigned to the byte array.
I tried Dim Vals(4) as Byte - I get the "no reaction", where I can read the values but when I try to do a Write, nothing happens.
I believe the data type Byte is too small.
Can i do something like:
Dim Vals(4) as Byte(2)?
Re: VB6 - Unsigned data type (Can I trick?)
Quote:
Originally Posted by
LaVolpe
Hey got another idea if any of the above don't work. I am guessing that your OPC control is checking the variant type of the passed variant and if it isn't UI4 then it is ignoring it. So, how about forcing the issue with CopyMemory API...
Code:
Dim vData As Variant, vType As Integer
vType = &H2000 Or 19 ' array of UI4
vData = CVar(Vals())
' write to the variant structure what type of data it contains
CopyMemory ByVal VarPtr(vData), vType, 2&
' now the variant thinks it is UI4; how now?
ASyncItemValues(1) = vData
If it works, UI2 is &H2000 Or 18
Listing of Variant Type codes
Edited: I see I pretty much repeated what Merri posted. Even though VB can't use the vData variant while it thinks it is UI4, your OPC should be able to. If the vData variant needs to be read by your VB app, then you'd have to change the UI4 to simply I4 which is &H2000 Or 3 for a an array of longs; then you'd be able to read the data back using VB.
I want to try this code, but CopyMemory is undefined. I tried adding this but I get an error that the entry point is undefined:
VB Code:
Private Declare Sub CopyMemory Lib "kernel32" Alias _
"RTlMoveMemory" (lpDest As Any, lpSource AS Any, _
ByVal cbCopy As Long)
Re: VB6 - Unsigned data type (Can I trick?)
Private Declare Sub PutMem2 Lib "msvbvm60" (ByRef Var As Any, ByVal Value As Integer)
Code:
Sub WriteOPC
'
Dim Vals(4) As Long ' DHS good for LONGs (VT_I4)
'
For i = 1 To 4
Vals(i) = CLng("SomeNumericValue")
Next
'
' ...
'
ASyncItemValues(1) = CVar(Vals)
' hack
PutMem2 AsyncItemValues(1), CanonDtArray Or CanonDtDWord
'
ConnectedGroup.AsyncWrite ItemCount, ASyncItemServerHandles, ASyncItemValues, ASyncItemServerErrors, TransactionID, CancelID
'
End Sub
Re: VB6 - Unsigned data type (Can I trick?)
Quote:
Originally Posted by
Merri
Private Declare Sub PutMem2 Lib "msvbvm60" (ByRef Var As Any, ByVal Value As Integer)
Code:
Sub WriteOPC
'
Dim Vals(4) As Long ' DHS good for LONGs (VT_I4)
'
For i = 1 To 4
Vals(i) = CLng("SomeNumericValue")
Next
'
' ...
'
ASyncItemValues(1) = CVar(Vals)
' hack
PutMem2 AsyncItemValues(1), CanonDtArray Or CanonDtDWord
'
ConnectedGroup.AsyncWrite ItemCount, ASyncItemServerHandles, ASyncItemValues, ASyncItemServerErrors, TransactionID, CancelID
'
End Sub
Wow. This actually works. I am speechless, except a BIG thanks Merri.
Now if only I knew WHY...
Re: [RESOLVED] VB6 - Unsigned data type (Can I trick?)
Variant's first two bytes contain the type (returned by VarType function, and happen to be the same values you defined in your Enum). Thus by changing the type definition of the Variant you pass the required type and there will no longer be a silent error in the background.