Visual Basic 6 Win32 API Tutorial, Page 6
UDTs are usually not a problem when it comes to API calls. Whenever an API call requires a UDT to be passed in as a parameter, it needs to be passed in by reference. For example, the GlobalMemoryStatus allows you to find out the current status of some OS memory parameters. Here's what the call looks like:
Declare Sub GlobalMemoryStatus Lib "kernel32" _ (lpBuffer As MEMORYSTATUS)
That last parameter is a UDT - here's its definition:
Type MEMORYSTATUS dwLength As Long dwMemoryLoad As Long dwTotalPhys As Long dwAvailPhys As Long dwTotalPageFile As Long dwAvailPageFile As Long dwTotalVirtual As Long dwAvailVirtual As Long End Type
Don't forget that you can get this information from the API Viewer as well, but you have to go to Types instead of Declares.
As we stated before, the MEMORYSTATUS UDT is passed in by reference. Therefore, the DLL may modify the contents of the variable, but in most cases this is exactly what we're looking for.
Note that we saw this same situation with the calls we made in Chapter 1 for the high-resolution timer calls that used the LARGE_INTEGER UDT.
The following code would work just fine in VB:
Private Sub GetMemoryStatus() Dim udtMemory as MEMORYSTATUS GlobalMemoryStatus udtMemory ' Code can be added here to use the memory information ' stored in udtMemory. End Sub
Once the call is made, each value in udtMemory will be changed to reflect some aspect of the current Window memory allocation. If for some reason you won't pass in a UDT to the call, simply add another call (or change the one you already have) that will accept a Long data type ByVal. This situation arises when you need to make a call that needs a SECURITY_ATTRIBUTES UDT as an argument (we'll see this in the next chapter for the CreateFile call). If you're programming in NT, you can use some security features that define how processes can share system objects. However, this has no meaning in the Win9x world. By redefining the argument as a Long data type, you can pass in a null pointer value, or zero in the VB world. This passes in a null pointer to the function call, which will know that you didn't pass in the UDT. You'd have to redefine the argument's data type in these cases. In fact, take a look at our array discussion we just went through. We redefined ReadFile such that we could ignore the OVERLAPPED UDT.
The only issue that should really concern you when you need to use a UDT is memory alignment. The rules that govern UDT memory alignment are as follows:
- A byte can exist anywhere within a structure
- An integer must exist at an address location that is evenly divisible by two
- A long must exist at an address location that is evenly divisible by four
Therefore, if your type has a Byte data type declared along with some other types, VB has to "pad" the structure with some extra memory so the UDT fits the rules. For example, let's take a look at this type:
Private Type WeirdType ByteType As Byte LongType As Long End Type
If you declared udtWeird as a WeirdType UDT and called Len(udtWeird), you would get a 5. However, calling LenB(udtWeird) would return an 8. Since the first type is a Byte, VB has to add three extra bytes to make sure that LongType exists at a proper memory location. LenB returns the actual memory size, including any byte padding that the UDT needs to follow the rules stated above. Len simply returns the length of the UDT "as-is," without the memory padding added in. Here's a diagram to demonstrate what the UDT looks like in code, and how it is actually lined up in memory:
Most APIs are aware of the memory alignment issue and follow the requirements stated above. But what about strings? For example, say we changed WeirdType so that it looks like this:
Private Type WeirdType ByteType As Byte LongType As Long StringType As String * 5 End Type
Now what happens if we call Len and LenB? We get 10 and 20, respectively. But why? We know that VB is adding offsetting memory to get LongType in the correct spot. But it looks like we're only adding 5 more bytes with StringType. Well, guess what - we've run into yet another topic: Unicode