Visual Basic Data Types

Both PowerBASIC and Visual Basic support the Currency data type - these are internally stored as a Quad-integer (occupying 8 bytes) with an implied decimal point at 4 digits. However, PowerBASIC also supports an Extended-currency data type with only two digits after the decimal point.

This data type is much more suited to dollar values, and uses the IEEE standard for calculations and rounding. The Extended-currency data format is signified by using two "at-signs" (@@) as the type-specifier, or using the CUX keyword in a DIM statement.

In 32-bit Windows, both PowerBASIC and Visual Basic use the OLE string engine for allocating, storing and releasing dynamic strings. The OLE API SysAllocStringLen call is made to allocate a string and the SysFreeString call is made to release a string.

Internally, however, Visual Basic and PowerBASIC store string data differently. Visual Basic stores string data using the Unicode (two bytes per character) format. PowerBASIC stores string data either in ANSI (one byte per character) or Wide Unicode format.

Typically, this will not be a problem, because Visual Basic always converts Unicode strings to ANSI (compatible with ASCII) format when passing them to a DLL or API call. On return from the call, Visual Basic will convert the string back to the Unicode format it uses internally. For example in the following code:

' VB code

MyString$ = "the quick brown fox jumped over the lazy dog."

Call PowerBasicDll(MyString$)

VB will convert the Unicode data to ANSI, call the function PowerBasicDll, and when the function returns, convert the string back to Unicode format. In PowerBASIC we can access the string data directly and modify it without any special API calls:

' PB code

SUB PowerBasicDll ALIAS "PowerBasicDll" (MyString$) EXPORT

REPLACE "the" WITH "The" IN MyString$

END SUB

Many API calls require STRINGZ, or nul-terminated strings. Visual Basic does not natively support this data-type, so it relies on a "kludge" to pass the correct data. To pass a STRINGZ string, you add the BYVAL keyword to the DECLARE statement or CALL:

' VB code

Call PowerBasicDll(ByVal MyString$)

Visual Basic converts the data in MyString$ to ANSI format with a Nul ( CHR$(0) or $NUL) appended to the end of it, and passes a memory address to the location of the string data. PowerBASIC does natively support a STRINGZ string, so the BYVAL keyword is not used, as follows:

' PB Code

SUB PowerBasicDll ALIAS "PowerBasicDll" (MyString AS STRINGZ) EXPORT

REPLACE "the" WITH "The" in MyString

END SUB

If the called API or DLL modifies the data, it cannot extend the length past that of the data it received, or a General Protection Fault (GPF) may occur. If the modified data is shorter than the original string passed, the Visual Basic code must "fix" the string's length, by extracting all characters up to the CHR$(0) embedded in the string data.

It is also important to note that a string BYVAL in PowerBASIC is not the same thing as a string BYVAL in Visual Basic. That is, since PowerBASIC natively supports nul-terminated strings, it does not convert a dynamic string into an STRINGZ string, as does Visual Basic. PowerBASIC does exactly what you tell it: it makes a copy of the string, and passes the new string handle to the API or DLL. For example, if you have the following DECLARE statement in a Visual Basic module:

Declare Sub Hello Lib "HELLO.DLL" Alias "Hello" (Byval Greetings$)

then in PowerBASIC, you would create an almost identical exported Sub, but with the BYVAL clause removed, and the string parameter changed to STRINGZ. For example:

' PB code

#COMPILE DLL

DECLARE SUB Hello LIB "HELLO.DLL" ALIAS "Hello" (Greetings AS STRINGZ)

SUB Hello ALIAS "Hello" (Greetings AS STRINGZ) EXPORT

MSGBOX Greetings

END SUB

Both Visual Basic and PowerBASIC support User-Defined Types (UDT), also known as Structures. However, Visual Basic and PowerBASIC natively store the data for UDTs differently in memory.

Visual Basic uses what is called "natural alignment". The way "natural alignment" works is a combination of BYTE alignment and DWORD alignment. The members are allowed to act as padding as long as they dont cross a DWORD boundary. That is, if the member will fit entirely within the padding that would be used for DWORD alignment, it is BYTE-aligned instead. Lets take a look at how this works.

TYPE MyType

alpha AS STRING * 1

beta AS INTEGER

gamma AS LONG

END TYPE

With DWORD alignment, beta would be aligned on a DWORD, so there would be three bytes of padding between alpha and beta. However, beta only requires two bytes, so theres enough room for it to fit inside the padding. With "natural" alignment, then, beta is stored directly after alpha. Now, the first two elements are taking up three consecutive bytes. With DWORD alignment, gamma would be aligned on a DWORD, so there would be one byte of padding between alpha/beta and gamma. There isnt enough room to store gamma in one byte, so DWORD alignment is used, leaving that one byte of padding empty. With Visual Basic, this MyType structure requires eight bytes (1 byte for the STRING*1 + 2 bytes for the integer + 1 byte of padding + 4 bytes for the long). In order to match this structure in PowerBASIC, you would set up a similar structure using BYTE alignment:

TYPE MyType

alpha AS STRING * 1

beta AS INTEGER

padding AS BYTE

gamma AS LONG

END TYPE

Its important to understand that Visual Basic only inserts padding if the next member item is too large to fit in the current DWORD.

Finally, it should be noted that PowerBASIC does not support dynamic strings as members of UDT structures, since the size of a UDT structure must be fixed at compile-time.

PowerBASIC now features native support for Variants for use with Visual Basic and when creating COM client/controller applications. In order to determine the type of data being passed in the Variant, the VARIANTVT function can be used. Strings and numeric data can be extracted with the VARIANT#, VARIANT$, and VARIANT$$ functions. See the Variant Data Type topic for more information on using Variants in PowerBASIC.

Like Visual Basic, PowerBASIC supports arrays of all supported data types. However, there are some differences on how each language deals with arrays internally.

Like Visual Basic, each element of an array follows the previous one consecutively in memory. So if element A&(0) is at offset zero, then A&(1) is at offset 4, and so on (since Long-integers are four bytes long).

Internally, however, Visual Basic and PowerBASIC reference arrays differently. Visual Basic uses a SafeArray handle created using the OLE API. This four-byte handle is then passed to the OLE API to find the first element location in memory, get the size of the array, etc. PowerBASIC, on the other hand, uses a 64-byte array descriptor, which holds all of the information about the array. The overhead of calling the OLE API to get information about the array is not necessary.

This means that you cannot simply pass a Visual Basic array to PowerBASIC and access it. There are several different methods for accessing a Visual Basic array, each with their own advantages.

If you need to access a Visual Basic array, and not change its size, the easiest way is to pass the first element of the array as one parameter, and pass the total number of elements as another parameter.

' VB code

Declare Sub SortInt Lib "MYDLL.DLL" Alias "SortInt" (Byref Arr As Integer, Byval iCount As Long)

...

Dim A%(1 TO 50)

A%(1) = 123

A%(2) = 456

...

Call SortLong(A%(1), 50)

In PowerBASIC, you would dimension a PowerBASIC array at the same memory location used by the passed Visual Basic array. To accomplish this, the DLL code expects to receive the address of the array element, and the array can be created at that address:

' PB code

#COMPILE DLL "MYDLL.DLL"

SUB SortInt ALIAS "SortInt" (BYVAL FirstElem AS DWORD, BYVAL Total AS LONG) EXPORT

DIM A(1 TO Total) AS INTEGER AT FirstElem

ARRAY SORT A%()

END SUB

This allows you to read values from the array, and write new values to the array, as well as using the special ARRAY functions built-in to the PowerBASIC language ( ARRAY SORT, ARRAY SCAN, ARRAY INSERT, ARRAY DELETE, etc.). The only thing you cannot do with this technique is resize the array.

The problem with the above method is when you need to access a string or UDT array passed from Visual Basic. As noted in the previous sections, Visual Basic stores string data in Unicode format. When you pass a string from Visual Basic to PowerBASIC, VB will convert the string to ANSI, so if you pass only the first element of the string array to PowerBASIC, only that particular element will be converted. All of the remaining elements in the VB array are still in Unicode format.

To access a string array or UDT array that has strings, you need to have Visual Basic pass the SafeArray handle to the array. By passing the array handle rather than the array element(s), you force Visual Basic to convert all of the strings from Unicode to ANSI format before making the call to the DLL.

' VB code

Declare Sub SortString Lib "MYDLL.DLL" Alias "SortString" (A$())

' more code here

Call SortString(A$())

Since PowerBASIC cannot access SafeArrays directly, you will need to call the OLE API to get the location of the first element in memory, as well as the size of the array. The VBAPI32.INC file in the Samples directory contains the necessary code to help you do this. Also included in the VBAPI32.INC file is a routine that lets you resize a Visual Basic array.

' PB code

#COMPILE DLL "MYDLL.DLL"

#INCLUDE "VBAPI32.INC"

SUB SortString ALIAS "SortString" (pSA AS DWORD) EXPORT

LOCAL l AS LONG

LOCAL u AS LONG

LOCAL vb AS DWORD

l = vbArrayLBound(psa, 1)

u = vbArrayUBound(psa, 1)

vb = vbArrayFirstElem(psa)

DIM A(l TO u) AS STRING AT vb

ARRAY SORT A$()

END SUB

Also included in the VBAPI32.INC file is a routine that lets you resize a Visual Basic array from a PowerBASIC DLL. See the ReDim VB Array example project in the PB\SAMPLES\VB32\REDIM VB ARRAY folder.

Both Visual Basic and PowerBASIC support the following data types: Byte, Integer, Long-integer, Single-precision float, Double-precision float, Currency, User-Defined Type, Fixed-length string, Dynamic string, and Variant. Both products also support arrays of all data types.

PowerBASIC also supports the following data types, which Visual Basic does not: Word, Double-word, Quad-integer, Extended-currency, STRINGZ string, Unions, and Pointers.

See Also

Comparative Data Types To Visual Basic 6