Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations John Tel on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How do I reset a user-defined data type 1

Status
Not open for further replies.

AndyGroom

Programmer
May 23, 2001
972
GB
Hi,

I have a complicated user-defined data type which includes numerous arrays, different variable types and so on, and I want to reset (erase) it so that all the numbers are back as 0, all the strings back as "" and so on.

I know I can do it like this:

Dim A as MyDataType
Dim B as MyDataType

' (Code here that populates variable A)

A = B

' (This resets A back to empty because B is empty)

However, my application is large and VB is unhappy with holding two of these variables in the same routine, in effect I can't spare the space to have an empty variable for resetting purposes.

VB doesn't allow Erase with a user-defined type, and I can't do A = Nothing, is there any other way of doing what I want?

- Andy.


 
from:-
To avoid undesired effects of optimizing compilers, use the SecureZeroMemory function.

maybe worth a shot!!

good luck!



If somethings hard to do, its not worth doing - Homer Simpson
------------------------------------------------------------------------
A General Guide To Excel in VB FAQ222-3383
 
From
"You can use this approach with any numeric array, as well as Variant arrays that contain only numbers (including the Decimal subtype) and arrays of User Defined Types that contains only numbers and fixed-length strings. If you use the ZeroMemory function with variable-length strings or objects what you get is a GPF."
 
Hi CajunC,

I revisited your initial suggestion of CopyMemory.

The remark I posted about Booleans not being reset was my biggest hang-up, so I created a small UDT as follows:

Type MyUDT
A as Long
B as Integer
C as Boolean
D as String
E as Double
End Type

I set all 5 elements to non-zero values (including C to True), did the CopyMemory and hey-presto, the C was set back to False.

So, puzzled, I returned to my own UDT which is somewhat more complicated, including arrays of other UDTs and several other data types.

Running the same test, I set one of the Boolean values to True, did CopyMemory and it was still True.

So, I substituted the two Len() statements in your code for LenB()'s and it now seems to work. Using Len() my structure was coming out at 1918 bytes, but using LenB it was 2300 - quite a difference, and significant enough to mean that not enough 0's were being written into the structure to reset all the elements - and possibly also explaining the GPFs on some machines?

Anyway, do you think I'm doing the right thing by using LenB? It seems to work in this instance.

- Andy.
 
Hi Andy,

>Dim X As MyUDT
>ZeroMemory X, LenB(X)
>ZeroMemory X, Len(X)

Atleast one of the above statements containing LenB(X) or Len(X) should work correctly and I wonder why are they causing GPF?

Would you please describe the structure of MyUDT that you are using? May be that could give us a clue.
 
Hi Hypetia,

Thanks for the response. Having looked into ZeroMemory on MSDN it does state that it's only suitable for resetting arrays, and not UDTs.

Further to my previous post, using LenB() did work on one UDT but on another it caused my app and VB to close without any message or crash alert! So I guess LenB() isn't right either.

Take a deep breath, here is my structure (scroll all the way down to get the full picture). The UDT I want to reset is called Profile_DataBlock:

Type AddressInf
Used As Boolean
Line0 As String
Line1 As String
Line2 As String
Line3 As String
Line4 As String
Postcode As String
PostcodeX As Long
PostcodeY As Long
Country As String
Context As String
ContextID As Long
PrivateAddress As Boolean
UnusedStr1 As String ' FREE
UnusedLong1 As Long ' FREE
UnusedDbl1 As Double ' FREE
UnusedBool1 As Boolean ' FREE
End Type

Type NumberInf
Used As Boolean
TypeOfNumber As Integer
Comment As String
UserEntered As String
PrivateNumber As Boolean
End Type

Type ForecastInf
Live As Boolean
Status As Long
Type As Integer
Product As String
Quantity As Single
Value As Single
ExpectedDate As Long
Notes As String
ForecastByUserName As String
ForecastByUserID As String
CreatedDate As Double
UnusedStr1 As String ' FREE
UnusedLong1 As Long ' FREE
UnusedDbl1 As Double ' FREE
UnusedBool1 As Boolean ' FREE
End Type

Type LinkInf
Live As Boolean
FolderRef As String
FolderType As Integer
Visual As String
Comment As String
End Type

Type HistoryInf
DateOfChange As Double
FieldID As Integer
FieldName As String
OldNewContents As String
UserAndComputerID As String
End Type

Type CountInf
CountContext As String
CountValue As Long
End Type

Type Profile_DataBlock
FolderID As Integer
Context As Long
Field(1 To 129) As String
Address(0 To 9) As AddressInf
Numbers(0 To 49) As NumberInf

HasForecasts As Boolean
Forecasts() As ForecastInf
HasAssetOwnership As Boolean
OwnsAssets() As LinkInf
HasAssociates As Boolean
Associates() As LinkInf
HasHistory As Boolean
History() As HistoryInf
CountOf() As CountInf

vb_Success As Boolean
vb_Dirty As Boolean

vb_UnusedBool1 As Boolean ' FREE
vb_UnusedBool2 As Boolean ' FREE
vb_UnusedBool3 As Boolean ' FREE

InstallDate As Long

vb_UnusedLong2 As Long ' FREE
End Type

- Andy.
 
Apart from the ongoing discussion about ZeroMemory, I got a very fairly idea for resetting UDTs.

Suppose that you have a variable SomeVar of type MyUDT with its members containing valid data and you want to have it reset.

To do so, create another variable of type MyUDT and assign it to SomeVar. i.e.

Dim SomeVar As MyUDT
'...
'Code that fills in members of SomeVar
'...

'Now you want to reset SomeVar
Dim X As MyUDT
SomeVar = X

At this step, all the members of SomeVar will be initialized to defaults.
 
Hi Hypetia,

Thanks for the suggestion, however I did mention in my original post that I knew this was a solution but since VB has a 32k limit on contiguous data structures during compilation it wouldn't compile if I declared two of these large UDTs and just kept one for resetting purposes.

I'm still doing some testing with CopyMemory, it's very weird, it seems to work perfectly with small UDTs but as they get bigger the Len() function seems to get more and more inaccurate regarding how much memory needs to be cleared.

I'll post my results when I've come to a conclusion, or when I hit a brick wall!

- Andy.
 
i may of missed the point here (and its a little similar to zemps earlier suggestion) but can you not just do something like:-

Dim PD() As Profile_DataBlock

Private Sub Form_Load()

ReDim PD(0)

PD(0).FolderID = 12
PD(0).Address(0).Used = True
PD(0).Address(1).PrivateAddress = True
PD(0).HasAssociates = True
PD(0).HasHistory = True

ReDim PD(0)

End Sub

i tried filling in a sorts of info and it always reset back to original values!!

just a thought

good luck!

If somethings hard to do, its not worth doing - Homer Simpson
------------------------------------------------------------------------
A General Guide To Excel in VB FAQ222-3383
 
Just for info...

One of the problem we are facing is how to get the actual size of the UDT which is Profile_DataBlock in our case. Len and LenB are reporting different sizes and we don't know which one is correct and should be passed to ZeroMemroy or CopyMemory.

I got another idea for calculting the size of UDT. Here it is...[tt]
___
Dim X(1) As Profile_DataBlock
Debug.Print Len(X(0)), LenB(X(0)), VarPtr(X(1)) - VarPtr(X(0))
___
[/tt]
I tried this method for all the above UDTs and found that the value of LenB function always matches with the one returned by VarPtr method, whereas the value of Len function does not necessarily match the other two particularly for large UDTs.
 
Hi Hypetia,

After much faffing around here is some simple sample code that we can all try:

Type MyUDT
A As Integer
B As Integer
C As Boolean
D As Double
E As String * 1
End Type

Private Sub Form_Load()

Dim ATest As MyUDT
ATest.E = "X"

A& = Len(ATest)
Do
CopyMemory ATest, ByVal StrPtr(String(A&, Chr$(0))), A&
If (ATest.LastByte = Chr$(0)) Then Stop
A& = A& + 1
Loop

End Sub

What this basically does is set the last byte in the UDT (E) to the letter "X". It then uses Len to measure the size of the UDT and writes Chr$(0)'s into the UDT.

Crucially, it then checks to see if enough 0's were written to overwrite the X, and if not it increases A& by one and tries again.

My results, with this simple structure, are that:

Len(MyUDT) = 15

but A& must be 17 in order to reset the "X" character. So, even with a simple structure it's already 2 bytes astray.

LenB(MyUDT) = 20, which is far too many and causes a page fault.

Any ideas?

- Andy.
 
Didn't realize this thread had so many twists and turns. It seems based on what's been posted that the critical element is to determine the proper size of the UDT, and that the Len and LenB functions may not be the answer. Will run some sizing test and see what turns up.

Good Luck
--------------
As a circle of light increases so does the circumference of darkness around it. - Albert Einstein
 
Not sure of what this means, but test result data yield the following hypotheses:

In an UDT, each Boolean and Integer will require 2 bytes, Longs and Singles need 4, and Doubles need 8. Both Len and LenB return the sum of these numerical data types.

Things get interesting when Strings are introduced into the UDT. If you use a variable length string, then 4 bytes will be required for the string pointer,

<Begin Conjecture>
but it appears that the string pointer must be word aligned (on a 4 byte boundary) and whereas the Len function will dutifully add the proper 4 pointer bytes, the LenB may add an additional two bytes for each string pointer that is shifted by two bytes to insure word alignment. It also appears that if a string type is used in the structure, then the structure ends on a word boundary.
<End Conjecture>

For example:
Type UDTOne
Number1 as Integer
String1 as String
String2 as String
Number2 as Integer
End Type

Len(UDTOne) = 12 : LenB(UDTOne) = 16

Type UDTTwo
Number1 as Integer
String1 as String
String2 as String
Number2 as Integer
Number3 as Integer
End Type

Len(UDTTwo) = 14 : LenB(UDTTwo) = 16

Type UDTThr
Number1 as Integer
Number2 as Integer
String1 as String
String2 as String
End Type

Len(UDTThr) = 12 : LenB(UDTThr) = 12

The difference is that in the first structure, the first string begins 2 bytes into the structure, and since that is not on a word boundary, 2 bytes are added before the 4 byte string pointer. That puts the end of the structure 14 bytes into the structrue, require 2 more bytes to word align the end of the structure. Since 4 bytes have added into the structure to word align the string pointers and structure end, LenB reports 16 rather than the actual 12 being used.

In the second case, the end of the structure is already at a word boundary, so only two bytes have been added, those being to word align the string pointers.

In the third structure, the strings are already word aligned, as is the end of the structure, so no padding is required.

Fixed length strings do not seem to require word alignment, probably because we're dealing with actual values, rather than memory addresses, but there is a quirk here too. The Len function will return the length of the string, but the LenB returns the bytes required to store the string, and since the default string representation in VB is Unicode format, each character requires two storage bytes, so

Type UDTThr
String1 as String * 11
End Type

Len(UDTThr) = 11 : LenB(UDTThr) = 22

Now, as to how we use this information to properly determine the value to use in the size parameter of the API call, I'm not quite sure just yet.

Good Luck
--------------
As a circle of light increases so does the circumference of darkness around it. - Albert Einstein
 
Hi,

Thanks for the feedback.

Strongm, I *believe* that while VB will happily run code that includes large UDTs, it will not necessarily happily compile them. Check out MSDN ID: Q179140. My mistake though, it's 64k not 32k.

CajunC, yes I think your hypothesis regarding word boundaries is extremely probable since it's possible to have the same variables within a UDT and yet they take up different amounts of memory depending on which order they are in! I guess that's been my whole problem throughout this thread.

I suppose there are a couple of solutions but none are elegant, such as storing the size of the UDT in a separate variable (based on working it out by trial and error), or the alternative is basically the same method I've used in the sample code, ie to end your UDT with a 1-byte string, stick an &quot;X&quot; in it and carry on wiping ever increasing amounts of memory until the X is reset!

Yuck!

Surely there must be a better way?

- Andy.
 
Hi CajunC,

The aforementioned MSDN article (Q179140) contains some interesting pointers:

&quot;For more information regarding byte-alignment, refer to Section 6 of the VB5DLL.DOC document located in the Tools\Docs folder of the Visual Basic CD-ROM.

Because the Visual Basic run-time library converts the strings from UNICODE to ANSI when using the CopyMemory function, you should only allocate 1 byte for each character in your fixed-length strings&quot;

- Andy.
 
Without knowing the details of your app, one approach might be to reorder the entires in the UDT to insure that word alignment is already in place.
Such as

Type AddressInf
Used As Boolean
PrivateAddress As Boolean ' Word Align
Line0 As String
Line1 As String
Line2 As String
Line3 As String
Line4 As String
Postcode As String
PostcodeX As Long
PostcodeY As Long
Country As String
Context As String
ContextID As Long
UnusedStr1 As String ' FREE
UnusedLong1 As Long ' FREE
UnusedDbl1 As Double ' FREE
UnusedBool1 As Boolean ' FREE
End Type

You now should not need the trailing Unused Bool1 at the end

Good Luck
--------------
As a circle of light increases so does the circumference of darkness around it. - Albert Einstein
 
Thanks for the idea. Unfortunately I write most of these structures to various files, so without doing an upgrader for all the files I couldn't easily change the structure.

I guess VB must know exactly the size of these structures when it's running, the trick is to figure out how it knows...

- Andy.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top