Home > Archive > Visual Basic Syntax > February 2005 > UDT bug
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
|
|
|
| W98 & VB6
Public Type TestType
Long1 As Long
Long2 As Long
Long3 As Long
Long4 As Long
Word1 As Integer
Long5 As Long
End Type
?Len(TestType)
22
So far, so good, but:
?VarPtr(TestType.Word1)
8451328
?VarPtr(TestType.Long5)
8451332
Huh?
Since Word1 is an integer, Long5 should live at 8451330 not 8451332.
Even if I define Word1 as a Byte VB6 still sticks Long5 in the same
place as above (4 bytes over).
Therefore, any assignment to TestType.Long5 will go into a wrong
address - as I found out. The hard way... bringing VB6 down.
Is this a known problem?
Danny
(You guessed it! Remove NOSPAMFOR before emailing.)
| |
| Mike D Sutton 2005-02-07, 9:04 pm |
| > W98 & VB6
<code snipped>
> Since Word1 is an integer, Long5 should live at 8451330 not 8451332.
> Even if I define Word1 as a Byte VB6 still sticks Long5 in the same
> place as above (4 bytes over).
>
> Therefore, any assignment to TestType.Long5 will go into a wrong
> address - as I found out. The hard way... bringing VB6 down.
>
> Is this a known problem?
It's not a problem, it's down to the storage mechanism for UDT's in VB since UDT members are stored with efficient
offsets.
Have a look at this for example:
'***
Private Declare Sub RtlMoveMemory Lib "Kernel32.dll" ( _
ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Private Type TestType
Long1 As Long
Long2 As Long
Long3 As Long
Long4 As Long
Word1 As Integer
Long5 As Long
End Type
Private Sub Form_Load()
Dim MyType As TestType
Dim UDTBuf() As Byte
Dim LoopBuf As Long
With MyType ' Enter some test data
.Long1 = &H11111111
.Long2 = &H22222222
.Long3 = &H33333333
.Long4 = &H44444444
.Word1 = &H5555
.Long5 = &H66666666
End With
' Buffer UDT data locally
ReDim UDTBuf(0 To LenB(MyType) - 1) As Byte
Call RtlMoveMemory(UDTBuf(0), MyType, LenB(MyType))
' Write hex dump of UDT data
For LoopBuf = 0 To LenB(MyType) - 1
Debug.Print Hex(UDTBuf(LoopBuf)) & ", ";
Next LoopBuf
Debug.Print
End Sub
'***
The members Word1 and Long5 are contiguous in the structure definition, however in the hex dump you'll see that two
additional padding bytes are inserted between them to ensure that Long5 starts on a DWord offset.
If you change Long4 to an Integer you'll see that these padding bytes disappear since Long5 is now DWord aligned and
Integers only need to be located on Word boundaries. Changing Long4 to a byte will insert an additional padding byte
between that and Word5 to align Word5 on a Word boundary.
Hope this helps,
Mike
- Microsoft Visual Basic MVP -
E-Mail: EDais@mvps.org
WWW: Http://EDais.mvps.org/
| |
| Jim Mack 2005-02-07, 9:04 pm |
| Danny wrote:
> W98 & VB6
>
> Public Type TestType
> Long1 As Long
> Long2 As Long
> Long3 As Long
> Long4 As Long
> Word1 As Integer
> Long5 As Long
> End Type
>
> ?Len(TestType)
> 22
>
> So far, so good, but:
>
> ?VarPtr(TestType.Word1)
> 8451328
> ?VarPtr(TestType.Long5)
> 8451332
>
> Huh?
>
> Since Word1 is an integer, Long5 should live at 8451330 not 8451332.
> Even if I define Word1 as a Byte VB6 still sticks Long5 in the same
> place as above (4 bytes over).
>
> Therefore, any assignment to TestType.Long5 will go into a wrong
> address - as I found out. The hard way... bringing VB6 down.
>
> Is this a known problem?
As Mike says, it's well-known, it just isn't a problem :-) Padding is
used so that elements align at addresses compatible with their size, up
to Dword (32 bits). In MS C the default for structure packing is 8-byte
alignment.
If padding concerns you, a good general rule is to place elements in
order of descending size of the underlying types: first all 8-byte types
(Currency, Double), then all 4-byte types (Long, Single etc), then
2-byte types (Integer, Boolean), then fixed-length strings, and finally
bytes.
Done this way there will be little or no need for padding, and
structures will be compatible between VB and C.
--
Jim Mack
MicroDexterity Inc
www.microdexterity.com
| |
| Michael C 2005-02-07, 9:04 pm |
| "Danny" <NOSPAMFORdaniel_ahorn@yahoo.com> wrote in message
news:4207e6de.1709232@News.Individual.NET...
> W98 & VB6
>
> Public Type TestType
> Long1 As Long
> Long2 As Long
> Long3 As Long
> Long4 As Long
> Word1 As Integer
> Long5 As Long
> End Type
>
> ?Len(TestType)
> 22
Try using LenB instead to get the correct value.
Michael
| |
| Michael C 2005-02-07, 9:04 pm |
| "Jim Mack" <jmack@mdxi.nospam.com> wrote in message
news:u5q7VpWDFHA.624@TK2MSFTNGP15.phx.gbl...
> If padding concerns you, a good general rule is to place elements in
> order of descending size of the underlying types: first all 8-byte types
> (Currency, Double), then all 4-byte types (Long, Single etc), then
> 2-byte types (Integer, Boolean), then fixed-length strings, and finally
> bytes.
>
> Done this way there will be little or no need for padding, and
> structures will be compatible between VB and C.
Provided you get to define the order. If you don't you can pass a byte array
into the API call and then manually copy the data to the UDT.
Michael
| |
|
| Date: Tue, 8 Feb 2005 10:20:09 +1100
Name: "Michael C" <mculley@NOSPAMoptushome.com.au>
>"Danny" <NOSPAMFORdaniel_ahorn@yahoo.com> wrote in message
>news:4207e6de.1709232@News.Individual.NET...
>
>Try using LenB instead to get the correct value.
Actually, Len() gives me the correct value (all variables added up)
;o)
The problem is that's not what's really in memory. :-(
Danny
(You guessed it! Remove NOSPAMFOR before emailing.)
| |
|
| Date: Mon, 7 Feb 2005 18:11:18 -0500
Name: "Jim Mack" <jmack@mdxi.nospam.com>
>
>As Mike says, it's well-known, it just isn't a problem :-)
As they say, it's not a bug it's a feature... ;o)
Unfortunately, the external routine I'm interfacing with thought
different! Kaboom!
>If padding concerns you, a good general rule is to place elements in
>order of descending size of the underlying types: first all 8-byte types
>(Currency, Double), then all 4-byte types (Long, Single etc), then
>2-byte types (Integer, Boolean), then fixed-length strings, and finally
>bytes.
>
>Done this way there will be little or no need for padding, and
>structures will be compatible between VB and C.
Since I'm sending this structure to an external routine the order is
not up to me.
Anyway, I already "fixed" it by poking the long manually.
VarPtr(Word1)+2 gives me the address of the following Long5.
However, that defeats the whole point of defining the structure in the
first place.
Anyway, thank you both Jim and Mike! Mystery solved. As always, it's
MS' fault!
Danny
(You guessed it! Remove NOSPAMFOR before emailing.)
| |
|
| Date: Tue, 8 Feb 2005 11:09:20 +1100
Name: "Michael C" <mculley@NOSPAMoptushome.com.au>
>Provided you get to define the order. If you don't you can pass a byte array
>into the API call and then manually copy the data to the UDT.
The catch, is that defies the purpose a UDT i.e. variables no longer
have meaningful names plus you have to handle them manually. But, as I
explained to Jim, I did something similar by calculating the address
of Long5 and poking it in manually.
That way I can at least refer to the preceding variables by meaningful
names Of course, Long1-Long5 is not what I really call them, it was
just to present the case more clearly.
Danny
(You guessed it! Remove NOSPAMFOR before emailing.)
| |
| Mike D Sutton 2005-02-08, 9:02 pm |
| > Since I'm sending this structure to an external routine the order is
> not up to me.
>
> Anyway, I already "fixed" it by poking the long manually.
> VarPtr(Word1)+2 gives me the address of the following Long5.
Be very careful using this technique if you're using the structure elsewhere within your application, since the padding
data is effectively undefined there is no responsibility by VB to ensure that this data gets copied when the rest of the
structures members do. I would imagine it should work fine but it's tempting fate and IMO I wouldn't risk it.
> However, that defeats the whole point of defining the structure in the
> first place.
>
> Anyway, thank you both Jim and Mike! Mystery solved. As always, it's
> MS' fault!
It's not really Microsoft's 'fault' as you're so quick to conclude, structure packing is common in most (all?) other
high level languages, however since VB attempts to hide developers from what's going on behind the scenes it was assumed
that this would only complicate things for the majority of developers who simply don't need to know or deal with it and
thus hidden (at least in terms of the IDE.) Since structure packing is user definable within the IDE in other languages
it's more visible to the developer than VB's fixed structure packing, but it's still there.
Their view is if you're poking around in memory then this is way outside VB's responsibility and it's therefore your
responsibility to know about the quirks in how the memory is defined, I'm sure there's some mention of this in the MSDN
if you dig deep enough and it comes up on here reasonably often enough that a quick Google search would likely turn
something useful up.
In terms of fixing your problem then by far the easiest way is to simply allocate a user defined buffer, for example a
byte array and translate to and/or from this and a VB structure if that's required - I've outlined a similar technique
in the DC article on my page, Chapter 4 demonstrates converting a variable sized EXTLOGPEN into a VB compatible
structure which is a similar problem to your alignment problem, and again in Chapter 6, a similar technique is used for
region data.
Hope this helps,
Mike
- Microsoft Visual Basic MVP -
E-Mail: EDais@mvps.org
WWW: Http://EDais.mvps.org/
| |
| Michael C 2005-02-08, 9:02 pm |
| "Danny" <NOSPAMFORdaniel_ahorn@yahoo.com> wrote in message
news:4208e850.1723139@News.Individual.NET...
> The catch, is that defies the purpose a UDT i.e. variables no longer
> have meaningful names plus you have to handle them manually. But, as I
> explained to Jim, I did something similar by calculating the address
> of Long5 and poking it in manually.
>
> That way I can at least refer to the preceding variables by meaningful
> names Of course, Long1-Long5 is not what I really call them, it was
> just to present the case more clearly.
But the byte array is only used for a short time and the data is immediately
copied to a UDT once the API has been called. Not perfect I know but only a
little bit extra work.
Michael
| |
|
| Date: Wed, 9 Feb 2005 08:19:16 +1100
Name: "Michael C" <mculley@NOSPAMoptushome.com.au>
>"Danny" <NOSPAMFORdaniel_ahorn@yahoo.com> wrote in message
>news:4208e850.1723139@News.Individual.NET...
>
>But the byte array is only used for a short time and the data is immediately
>copied to a UDT once the API has been called. Not perfect I know but only a
>little bit extra work.
Yes, that's a good alternative although one has to be very careful
when copying. A simple memcopy of the whole structure would not do.
Anything after the Word (in my example) will have to be shifted over
by 2 bytes so it would have to be a "smart" copy.
Danny
(You guessed it! Remove NOSPAMFOR before emailing.)
| |
|
| Date: Tue, 8 Feb 2005 21:06:35 -0000
Name: "Mike D Sutton" <EDais@mvps.org>
>
>Be very careful using this technique if you're using the structure elsewhere within your application, since the padding
>data is effectively undefined there is no responsibility by VB to ensure that this data gets copied when the rest of the
>structures members do. I would imagine it should work fine but it's tempting fate and IMO I wouldn't risk it.
Yes, I'm aware of that, but it's only a temporary structure so I
should be OK. The key thing is I now know what's going and I have
mentally "red-flagged" it - for future reference as well. The problem
only arose because I didn't know what was going on under the hood.
>
>It's not really Microsoft's 'fault' as you're so quick to conclude, structure packing is common in most (all?) other
>high level languages, however since VB attempts to hide developers from what's going on behind the scenes it was assumed
>that this would only complicate things for the majority of developers
I'm familiar with padding but only between structures, not within a
single structure, exactly for the reasons my case illustrates. In the
past padding was useful to align the subsequent structure to an "even"
address (whatever "even" meant for any particular architecture). That
used to be the formal definition of padding in the IT context.
However, most processors today have no performance penalty for
odd-address access.
The reason I was so quick to blame MS is because they are notorious
for redefining established terminology and, due to their size, they
often succeed. We need go no farther than VB itself. I believe up to
and including VB4 MS referred to conversion to p-code as "compiling".
The conventional definition of "compile" is (was?) "translate source
code into object code". And pseudo code is definitely not object code.
At best, what VB did back then could be described as a step in the
pre-compile stage to facilitate formal syntax analysis (be it for
subsequent compilation or interpretation).
But such behavior is nothing new. I clearly remember the days when
everyone used "file" while IBM insisted on "data set"! Monopolies have
always tried to invent their own terminology and the reasons range
from devious marketing plots to simple arrogance.
My apologies if I sound over-excited. ;o) I don't mean anything by all
this, just trying to explain my context.
Be that as it may, doing the sort things I do is beyond the scope of
VB, as you later state quite correctly. I'm very aware (and accept)
that "I'm on my own" although it would be nice to be told of this
padding in more explicit terms up front. It may have been outlined
somewhere in small print but I just haven't come across it.
>In terms of fixing your problem then by far the easiest way is to simply allocate a user defined buffer, for example a
>byte array and translate to and/or from this and a VB structure if that's required - I've outlined a similar technique
>in the DC article on my page, Chapter 4 demonstrates converting a variable sized EXTLOGPEN into a VB compatible
>structure which is a similar problem to your alignment problem, and again in Chapter 6, a similar technique is used for
>region data.
>Hope this helps,
Thanks Mike! It does help, as always. In this case, however, the
implementation was not the problem once I knew what was happening.
Danny
(You guessed it! Remove NOSPAMFOR before emailing.)
| |
| Michael C 2005-02-09, 9:00 pm |
| "Danny" <NOSPAMFORdaniel_ahorn@yahoo.com> wrote in message
news:420a37c5.1298370@News.Individual.NET...
> Yes, that's a good alternative although one has to be very careful
> when copying. A simple memcopy of the whole structure would not do.
> Anything after the Word (in my example) will have to be shifted over
> by 2 bytes so it would have to be a "smart" copy.
Yeh, it has to be a specific copy for each UDT. I've had to do it before, it
is necessary occasionally.
Michael
| |
| Michael C 2005-02-09, 9:00 pm |
| "Danny" <NOSPAMFORdaniel_ahorn@yahoo.com> wrote in message
news:420a3bf3.2367920@News.Individual.NET...
> Be that as it may, doing the sort things I do is beyond the scope of
> VB, as you later state quite correctly. I'm very aware (and accept)
> that "I'm on my own" although it would be nice to be told of this
> padding in more explicit terms up front. It may have been outlined
> somewhere in small print but I just haven't come across it.
It should be in small print. Users of VB should not be bothered by such
small details.
Michael
| |
| Bo Gusman 2005-02-10, 4:01 pm |
| >
> Be that as it may, doing the sort things I do is beyond the scope of
> VB, as you later state quite correctly. I'm very aware (and accept)
> that "I'm on my own" although it would be nice to be told of this
> padding in more explicit terms up front. It may have been outlined
> somewhere in small print but I just haven't come across it.
Check out Matt Curland's book, Advanced Visual Basic if you're
interested in some of these details, and more.
Bo
| |
|
| Date: Thu, 10 Feb 2005 09:51:38 -0800
Name: Bo Gusman <bo@bogusville.us>
>
>Check out Matt Curland's book, Advanced Visual Basic if you're
>interested in some of these details, and more.
Thanks for the tip! I'll look into it.
Danny
(You guessed it! Remove NOSPAMFOR before emailing.)
|
|
|
|
|