首页  编辑  

动态数组API

Tags: /超级猛料/API.Windows应用程序接口/其他相关/   Date Created:
http://www.geocities.com/SiliconValley/4942/arrays.html  
Undocumented
Windows ? 95
 
Dynamic Array Routines
You may be wondering why anyone would want to use a bunch of undocumented array routines, with fairly  limited functionality, when the typical class library these days includes dozens of useful container classes. Well, if you're trying to keep your application as small as possible, it is essential that you make the most of the facilities already implemented by the operating system.
     There are two groups of array functions covered in this article: those that handle Dynamic Pointer Arrays (all pre-fixed with DPA), and those that handle Dynamic Structure 
Arrays (prefixed with DSA). I'm actually just guessing what DPA and DSA stand for, but I think it's a fairly plausible interpretation. The functions are all exported from COMCTL32.DLL, and they are all exported by ordinal.
Dynamic Pointer Arrays
Dynamic pointer arrays are the more versatile of the two array groups. In addition to resizing automatically as items are added and removed, they can also be sorted and searched using a callback comparison function. They even allow you to specify the heap that should be used for memory allocations. That may not sound particularly useful, until you realise that on Windows 95 these functions default to using the undocumented shared heap, which may not always be such a great idea.
     Their only real limitation is that the array items can only be four bytes in size. Typically the items would be pointers(hence the name 'Dynamic Pointer Array'), but they could
just as easily be integers or some other four byte data item.
If you do choose to use pointers, though, bare in mind that it is the application's responsibility to allocate and free those items.
The DPA Handle
Every array that you create is identified by a DPA handle. Each of the create functions return a handle, and all other functions require a handle as their first parameter. A DPA handle is just a pointer to a 20 byte structure containing information about the state of the array. Ideally you would just treat it as a generic HANDLE, but that isn't always possible. As an example, the only efficient way to obtain the array size, is by accessing the structure directly (see Figure 1 for the structure definition).
     nItems specifies the number of items in the array. nCapacity, on the other hand, specifies the maximum number of items that can be placed in the array before new storage will have to be allocated.
typedef struct {
 int     nItems;
 LPVOID *lpItems;
 HANDLE  hHeap;
 int     nCapacity;
 int     nDelta;
} * HDPA;
Figure 1  DPA Handle Structure
nDelta specifies the amount by which the array will grow whenever new storage is required. On NT, though, this delta value is doubled every time the array is resized(unless is it already greater than 255). lpItems is a pointer to the memory where the array items are stored and hHeap is the heap handle used to allocate that memory.    
Creating and Destroying
The functions for createing and destroying arrays can be seen in Figure 2. To create a new array, you would typically use the DPA_Create function (ordinal 328). It takes only one parameter, the delta size, and returns a DPA handle as the result. If you would like the array data to be allocated from a specific heap, you should use the DPA_CreateEx function (ordinal 340). It requires a second parameter, which specifies the handle of the heap to use. If hHeap is NULL, you just get default heap used by DPA_Create (on Windows 95 it's the undocumented shared heap; on Windows NT, though, it's just the process heap).
     The DPA_Clone function (ordinal 331) can be used to create a duplicate of an existing array, by passing the source handle as the first parameter and NULL as the second. If you want the new array to use a different delta size or a different  heap, you should create the array first with DPA_Create or DPA_CreateEx, and then pass that handle as the second parameter to DPA_Clone. Your new array will be 'overwritten' with the data from the source array, but it will keep its delta size and heap handle.
HDPA WINAPI DPA_Create(
 int nDelta);
HDPA WINAPI DPA_CreateEx(
 int nDelta,
 HANDLE hHeap);
HDPA WINAPI DPA_Clone(
 HDPA hSource,
 HDPA hDest);
BOOL WINAPI DPA_Destroy(
 HDPA hArray);
Figure 2  Create and Destroy Functions

     All of these function return the handle of the newly created array. If they fail for any reason, the return value will be NULL. When you are finished using an array, you should destroy it by passing the handle to DPA_Destroy (ordinal 329). The return value is TRUE if the array was destroyed successfully (it even returns TRUE if you pass it a NULL handle). The return value is FALSE if an error occured when freeing the data.    
Storing Values
The functions related to storing array items can be seen in Figure 3. The first function, DPA_InsertPtr (ordinal 334), will insert an item at a particular point in the array. If you try to insert an item beyond the end of the array, the item will be appended. Typically you would set the insert position to MAXINT if you wanted to append an item (a negative insert position will fail). The return value is the actual position at which the item was inserted, or -1 if the insertion failed.
     DPA_SetPtr, on the other hand, will store a value at a particular position in the array, overwriting the value that was previously there. It has an ordinal value of 335. If you attempt to store an item beyond the end of the array, the array will  be expanded so that the operation will succeed. The return value is TRUE if the operation was successful. If the operation fails, the function will return FALSE.
int WINAPI DPA_InsertPtr(
 HDPA hArray,
 int nPosition,
 LPVOID lpItem);
BOOL WINAPI DPA_SetPtr(
 HDPA hArray,
 int nPosition,
 LPVOID lpItem);
BOOL WINAPI DPA_Grow(
 HDPA hArray,
 int nCapacity);
Figure 3  Functions for Storing

     As mentioned before, the array will automatically resize itself whenever an insertion operation exceeds the capacity of the array. If you are adding a large number of items to an array, it may need t to resize a number of times, which can be quite a costly operation. To solve this problem, you can use the DPA_Grow function (ordinal 330) to extend the capacity of the array, prior to adding any items. Note that the size is not affected by this operation - only the capacity. Also note that the new capacity will not necessarily be the exact value you specify, but it will be at least that large.  

Retrieving and Deleting
All the functions necessary for retrieving and deleting items can be seen in Figure 4. To retrieve a value from an array, you would use the DPA_GetPtr function (ordinal 332). It will return the pointer that is stored at the specified offset in the array. If you request a value that is out of range, the function will return NULL.
     To remove a value from the array you would use the function DPA_DeletePtr (ordinal 336). It will remove an item from a particular position in the array, and any following items will be shifted down by one. If the difference between the capacity and the actual size is now greater than the delta size, the storage area will be reallocated to  use less memory. The return value is the pointer that was removed from the array. If you attempt to remove a item that is out of range, the function will return NULL.
LPVOID WINAPI DPA_GetPtr(
 HDPA hArray,
 int nPosition);
LPVOID WINAPI DPA_DeletePtr(
 HDPA hArray,
 int nPosition);
BOOL WINAPI DPA_DeleteAllPtrs(
 HDPA hArray);
Figure 4  Functions for Retrieving and Deleting

     To remove all items from the array, you would use the function DPA_DeleteAllPtrs (ordinal 337). Bare in mind that the pointers themselves aren't actually deleted, so you may need to iterate through the array beforehand to free any memory used by the individual itesm. The return value is TRUE if the operation was successful. If the operation fails, the function will return FALSE.    

Sorting and Searching
Sorting and searching can be accomplished with the functions DPA_Sort and DPA_Search (obviously enough). Both require that you specify an application-defined callback function that is used to compare the relative order of any two items (see Figure 5).  You also get to specify a dword value that will be passed as the third parameter to your callback function.
typedef int (CALLBACK *LPFNDPACOMPARE) (
 LPVOID  lpItem1,
 LPVOID  lpItem2,
 LPARAM  lParam);
Figure 5  Comparison Callback Function

     The declarations for the sorting and searching functions can be seen in Figure 6. The DPA_Sort function (ordinal 338) is pretty straightforward. You specify the array handle, your
callback function, and the value to pass to the callback, and it will sort the array using a Merge Sort algorithm. The return value is TRUE if the array was sorted successfully.
If it was unable to allocate the memory required to perform the sort, the function will return FALSE.
     The DPA_Search function (ordinal 339) is a bit more complicated. You specify the pointer that you're searching for (it gets passed as the first parameter to your callback function), and the position in the array at which you would like the search to begin. The callback  function and the callback value are the same as those used  by DPA_Sort. The flags parameter determines the method in which the array is searched (see Figure 7).
BOOL WINAPI DPA_Sort(
 HDPA hArray,
 LPFNDPACOMPARE lpfnCompare,
 LPARAM lParam);
int WINAPI DPA_Search(
 HDPA hArray,
 LPVOID lpItem,
 int nStartPos,
 LPFNDPACOMPARE lpfnCompare,
 LPARAM lParam,
 DWORD dwFlags);
int WINAPI DPA_GetPtrIndex(
 HDPA hArray,
 LPVOID lpItem);
Figure 6  Sorting and Searching Functions
     Specifying the DPAF_LINEAR_SEARCH flag, will cause the array to be searched linearly, starting at the position specified in the nStartPos parameter. To perform a binary search (obviously the array should already be sorted), you would specify the DPAF_BINARY_SEARCH flag. With the binary search, the nStartPos parameter is ignored.
DPAF_LINEAR_SEARCH 0x00
DPAF_BINARY_SEARCH 0x01
DPAF_BINARY_INSERT 0x07
Figure 7  Search Flags

     In both cases, the function returns the position of the first matching item, or -1 if the item could not be found. When doing a binary search, though, you may wish to find the position at which the item should be inserted if didn't already exist in the array. In that case you should use the DPAF_BINARY_INSERT flag.
     There is another function, DPA_GetPtrIndex (ordinal 333), which has also been included in this section, although it may seem a bit out of place. It doesn't use a comparison
function like DPA_Sort and DPA_Search - it just searches linearly from the start of the array, looking for an exact match with a pointer stored in the array. If there were no exact matches, it will return -1. The function delcaration is also listed in Figure 6.    

Dynamic Structure Arrays
Dynamic Structure Arrays provide the same basic functionality as Dynamic Pointer Arrays. All necessary resizing of the array is handled automatically as items are added and removed. Unfortunately you don't have the equivalent sorting and searching functionality and you don't have any control of the heap that is used for the memory allocation (that means you're going to be stuck with the shared heap on Windows 95).
     The advantage of the Structure Arrays, though, is that the array elements can be of any size. You can now handle arrays of structures, without the complication of storing them as pointers and having to handle all the memory allocation yourself.

The DSA Handle
The DSA handle has the same basic elements as the DPA handle, but not always at the same offsets. Each handle also has one unique element. The DPA handle requires a heap handle that Dynamic Structure Arrays don't need, and the DSA handle as an attribute for storing the size of the array items which Dynamic Pointer Arrays obviously don't need (see Figure 8 for the structure definition).
     As with the DPA handle, nItems specifies the number of items in the array, and nCapacity specifies the maximum number of items that can be placed in the array before new storage will have to be allocated. nDelta specifies the amount by which the array will grow whenever new storage  is required. lpItems is a pointer to the memory where the array items are stored and nItemSize specifies the byte size of each item.
typedef struct {
 int     nItems;
 LPVOID  lpItems;
 int     nCapacity;
 int     nItemSize;
 int     nDelta;
} * HDSA;
Figure 8  DSA Handle Structure

The DSA Functions  
HDSA WINAPI DSA_Create(
 int nItemSize,
 int nDelta);
BOOL WINAPI DSA_Destroy(
 HDSA hArray);
Figure 9  Creation and Destroy Functions

Most of the DSA functions are pretty much identical to their DPA counterparts (the function declarations are listed in Figures 9, 10, 11 & 12). You create and destroy Dynamic Structure Arrays with DSA_Create and DSA_Destroy (ordinals 320 and 321). The only difference between the DSA_Create function and the DPA_Create function is that the former now requires an extra parameter specifying the size of the array items.
     Storing items in a structure array is accomplished with the DSA_InsertItem function and the DSA_SetItem function (ordinals 324 and 325). In each case, the data pointed to by the lpItem parameter is actually copied into the array (the corresponding DPA functions would only store a pointer to the data). As with the DPA_SetPtr function, trying to store an
item with DSA_SetItem beyond the end of the array will cause the array to be resized.
int WINAPI DSA_InsertItem(
 HDSA hArray,
 int nPosition,
 LPVOID lpItem);
BOOL WINAPI DSA_SetItem(
 HDSA hArray,
 int nPosition,
 LPVOID lpItem);
Figure 10  Functions for Storing

     Retrieving items is completely different though. Your one option is the DSA_GetItem function (ordinal 322), which will copy an item from the array into a specified buffer. It returns TRUE if successful, or FALSE if the requested item was out of range. The other option, DSA_GetItemPtr (ordinal 323), will return a pointer to the item in the actual array buffer. The resulting pointer can  even be used to modify the item directly. Bare in mind,  though, that any subsequent set or insert operation could invalidate that pointer.
BOOL WINAPI DSA_GetItem(
 HDSA hArray,
 int nPosition,
 LPVOID lpBuffer);
LPVOID WINAPI DSA_GetItemPtr(
 HDSA hArray,
 int nPosition);
Figure 11  Functions for Retrieving

     The functions for deleting items, DSA_DeleteItem and DSA_DeleteAllItems (ordinals 326 and 327) are exactly the same their DPA equivalents. This time, though, you don't have to worry  about pointers needing to released.
BOOL WINAPI DSA_DeleteItem(
 HDSA hArray,
 int nPosition);
BOOL WINAPI DSA_DeleteAllItems(
 HDSA hArray);
Figure 12  Functions for Deleting