2 * Dynamic pointer array (DPA) implementation
4 * Copyright 1998 Eric Kohl
5 * 1998 Juergen Schmied <j.schmied@metronet.de>
6 * 2000 Eric Kohl for CodeWeavers
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 * These functions were involuntarily documented by Microsoft in 2002 as
24 * the outcome of an anti-trust suit brought by various U.S. governments.
25 * As a result the specifications on MSDN are inaccurate, incomplete
26 * and misleading. A much more complete (unofficial) documentation is
29 * http://members.ozemail.com.au/~geoffch/samples/win32/shell/comctl32
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(dpa);
57 typedef struct _STREAMDATA
62 } STREAMDATA, *PSTREAMDATA;
64 /**************************************************************************
65 * DPA_LoadStream [COMCTL32.9]
67 * Loads a dynamic pointer array from a stream
70 * phDpa [O] pointer to a handle to a dynamic pointer array
71 * loadProc [I] pointer to a callback function
72 * pStream [I] pointer to a stream
73 * pData [I] pointer to callback data
76 * Success: S_OK, S_FALSE - partial success
77 * Failure: HRESULT error code
80 * No more information available yet!
82 HRESULT WINAPI DPA_LoadStream (HDPA *phDpa, PFNDPASTREAM loadProc,
83 IStream *pStream, LPVOID pData)
86 LARGE_INTEGER position;
87 ULARGE_INTEGER initial_pos;
88 STREAMDATA streamData;
89 DPASTREAMINFO streamInfo;
94 TRACE ("phDpa=%p loadProc=%p pStream=%p pData=%p\n",
95 phDpa, loadProc, pStream, pData);
97 if (!phDpa || !loadProc || !pStream)
102 position.QuadPart = 0;
104 errCode = IStream_Seek (pStream, position, STREAM_SEEK_CUR, &initial_pos);
108 memset(&streamData, 0, sizeof(STREAMDATA));
109 errCode = IStream_Read (pStream, &streamData, sizeof(STREAMDATA), &ulRead);
113 TRACE ("dwSize=%u dwData2=%u dwItems=%u\n",
114 streamData.dwSize, streamData.dwData2, streamData.dwItems);
116 if (ulRead < sizeof(STREAMDATA) ||
117 streamData.dwSize < sizeof(STREAMDATA) || streamData.dwData2 != 1) {
118 /* back to initial position */
119 position.QuadPart = initial_pos.QuadPart;
120 IStream_Seek (pStream, position, STREAM_SEEK_SET, NULL);
124 if (streamData.dwItems > (UINT_MAX / 2 / sizeof(VOID*))) /* 536870911 */
125 return E_OUTOFMEMORY;
128 hDpa = DPA_Create (streamData.dwItems);
130 return E_OUTOFMEMORY;
132 if (!DPA_Grow (hDpa, streamData.dwItems))
133 return E_OUTOFMEMORY;
135 /* load data from the stream into the dpa */
137 for (streamInfo.iPos = 0; streamInfo.iPos < streamData.dwItems; streamInfo.iPos++) {
138 errCode = (loadProc)(&streamInfo, pStream, pData);
139 if (errCode != S_OK) {
144 *ptr = streamInfo.pvItem;
148 /* set the number of items */
149 hDpa->nItemCount = streamInfo.iPos;
151 /* store the handle to the dpa */
153 TRACE ("new hDpa=%p, errorcode=%x\n", hDpa, errCode);
159 /**************************************************************************
160 * DPA_SaveStream [COMCTL32.10]
162 * Saves a dynamic pointer array to a stream
165 * hDpa [I] handle to a dynamic pointer array
166 * saveProc [I] pointer to a callback function
167 * pStream [I] pointer to a stream
168 * pData [I] pointer to callback data
171 * Success: S_OK, S_FALSE - partial success
172 * Failure: HRESULT error code
175 * No more information available yet!
177 HRESULT WINAPI DPA_SaveStream (const HDPA hDpa, PFNDPASTREAM saveProc,
178 IStream *pStream, LPVOID pData)
180 LARGE_INTEGER position;
181 ULARGE_INTEGER initial_pos, curr_pos;
182 STREAMDATA streamData;
183 DPASTREAMINFO streamInfo;
187 TRACE ("hDpa=%p saveProc=%p pStream=%p pData=%p\n",
188 hDpa, saveProc, pStream, pData);
190 if (!hDpa || !saveProc || !pStream) return E_INVALIDARG;
192 /* save initial position to write header after completion */
193 position.QuadPart = 0;
194 hr = IStream_Seek (pStream, position, STREAM_SEEK_CUR, &initial_pos);
198 /* write empty header */
199 streamData.dwSize = sizeof(streamData);
200 streamData.dwData2 = 1;
201 streamData.dwItems = 0;
203 hr = IStream_Write (pStream, &streamData, sizeof(streamData), NULL);
205 position.QuadPart = initial_pos.QuadPart;
206 IStream_Seek (pStream, position, STREAM_SEEK_SET, NULL);
210 /* no items - we're done */
211 if (hDpa->nItemCount == 0) return S_OK;
214 for (streamInfo.iPos = 0; streamInfo.iPos < hDpa->nItemCount; streamInfo.iPos++) {
215 streamInfo.pvItem = *ptr;
216 hr = (saveProc)(&streamInfo, pStream, pData);
224 /* write updated header */
225 position.QuadPart = 0;
226 IStream_Seek (pStream, position, STREAM_SEEK_CUR, &curr_pos);
228 streamData.dwSize = curr_pos.QuadPart - initial_pos.QuadPart;
229 streamData.dwData2 = 1;
230 streamData.dwItems = streamInfo.iPos;
232 position.QuadPart = initial_pos.QuadPart;
233 IStream_Seek (pStream, position, STREAM_SEEK_SET, NULL);
234 IStream_Write (pStream, &streamData, sizeof(streamData), NULL);
236 position.QuadPart = curr_pos.QuadPart;
237 IStream_Seek (pStream, position, STREAM_SEEK_SET, NULL);
243 /**************************************************************************
244 * DPA_Merge [COMCTL32.11]
246 * Merge two dynamic pointers arrays.
249 * hdpa1 [I] handle to a dynamic pointer array
250 * hdpa2 [I] handle to a dynamic pointer array
252 * pfnCompare [I] pointer to sort function
253 * pfnMerge [I] pointer to merge function
254 * lParam [I] application specific value
261 * No more information available yet!
263 BOOL WINAPI DPA_Merge (HDPA hdpa1, HDPA hdpa2, DWORD dwFlags,
264 PFNDPACOMPARE pfnCompare, PFNDPAMERGE pfnMerge,
268 LPVOID *pWork1, *pWork2;
272 TRACE("%p %p %08x %p %p %08lx)\n",
273 hdpa1, hdpa2, dwFlags, pfnCompare, pfnMerge, lParam);
275 if (IsBadWritePtr (hdpa1, sizeof(*hdpa1)))
278 if (IsBadWritePtr (hdpa2, sizeof(*hdpa2)))
281 if (IsBadCodePtr ((FARPROC)pfnCompare))
284 if (IsBadCodePtr ((FARPROC)pfnMerge))
287 if (!(dwFlags & DPAM_SORTED)) {
288 TRACE("sorting dpa's!\n");
289 if (hdpa1->nItemCount > 0)
290 DPA_Sort (hdpa1, pfnCompare, lParam);
291 TRACE ("dpa 1 sorted!\n");
292 if (hdpa2->nItemCount > 0)
293 DPA_Sort (hdpa2, pfnCompare, lParam);
294 TRACE ("dpa 2 sorted!\n");
297 if (hdpa2->nItemCount < 1)
300 TRACE("hdpa1->nItemCount=%d hdpa2->nItemCount=%d\n",
301 hdpa1->nItemCount, hdpa2->nItemCount);
304 /* working but untrusted implementation */
306 pWork1 = &(hdpa1->ptrs[hdpa1->nItemCount - 1]);
307 pWork2 = &(hdpa2->ptrs[hdpa2->nItemCount - 1]);
309 nIndex = hdpa1->nItemCount - 1;
310 nCount = hdpa2->nItemCount - 1;
315 if ((nCount >= 0) && (dwFlags & DPAM_UNION)) {
316 /* Now insert the remaining new items into DPA 1 */
317 TRACE("%d items to be inserted at start of DPA 1\n",
319 for (i=nCount; i>=0; i--) {
322 ptr = (pfnMerge)(DPAMM_INSERT, *pWork2, NULL, lParam);
325 DPA_InsertPtr (hdpa1, 0, ptr);
331 nResult = (pfnCompare)(*pWork1, *pWork2, lParam);
332 TRACE("compare result=%d, dpa1.cnt=%d, dpa2.cnt=%d\n",
333 nResult, nIndex, nCount);
339 ptr = (pfnMerge)(DPAMM_MERGE, *pWork1, *pWork2, lParam);
349 else if (nResult > 0)
351 /* item in DPA 1 missing from DPA 2 */
352 if (dwFlags & DPAM_INTERSECT)
354 /* Now delete the extra item in DPA1 */
357 ptr = DPA_DeletePtr (hdpa1, nIndex);
359 (pfnMerge)(DPAMM_DELETE, ptr, NULL, lParam);
366 /* new item in DPA 2 */
367 if (dwFlags & DPAM_UNION)
369 /* Now insert the new item in DPA 1 */
372 ptr = (pfnMerge)(DPAMM_INSERT, *pWork2, NULL, lParam);
375 DPA_InsertPtr (hdpa1, nIndex+1, ptr);
388 /**************************************************************************
389 * DPA_Destroy [COMCTL32.329]
391 * Destroys a dynamic pointer array
394 * hdpa [I] handle (pointer) to the pointer array
400 BOOL WINAPI DPA_Destroy (const HDPA hdpa)
402 TRACE("(%p)\n", hdpa);
407 if (hdpa->ptrs && (!HeapFree (hdpa->hHeap, 0, hdpa->ptrs)))
410 return HeapFree (hdpa->hHeap, 0, hdpa);
414 /**************************************************************************
415 * DPA_Grow [COMCTL32.330]
417 * Sets the growth amount.
420 * hdpa [I] handle (pointer) to the existing (source) pointer array
421 * nGrow [I] number of items by which the array grows when it's too small
427 BOOL WINAPI DPA_Grow (HDPA hdpa, INT nGrow)
429 TRACE("(%p %d)\n", hdpa, nGrow);
434 hdpa->nGrow = max(8, nGrow);
440 /**************************************************************************
441 * DPA_Clone [COMCTL32.331]
443 * Copies a pointer array to an other one or creates a copy
446 * hdpa [I] handle (pointer) to the existing (source) pointer array
447 * hdpaNew [O] handle (pointer) to the destination pointer array
450 * Success: pointer to the destination pointer array.
454 * - If the 'hdpaNew' is a NULL-Pointer, a copy of the source pointer
455 * array will be created and it's handle (pointer) is returned.
456 * - If 'hdpa' is a NULL-Pointer, the original implementation crashes,
457 * this implementation just returns NULL.
459 HDPA WINAPI DPA_Clone (const HDPA hdpa, const HDPA hdpaNew)
461 INT nNewItems, nSize;
467 TRACE("(%p %p)\n", hdpa, hdpaNew);
470 /* create a new DPA */
471 hdpaTemp = HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
473 hdpaTemp->hHeap = hdpa->hHeap;
474 hdpaTemp->nGrow = hdpa->nGrow;
479 if (hdpaTemp->ptrs) {
480 /* remove old pointer array */
481 HeapFree (hdpaTemp->hHeap, 0, hdpaTemp->ptrs);
482 hdpaTemp->ptrs = NULL;
483 hdpaTemp->nItemCount = 0;
484 hdpaTemp->nMaxCount = 0;
487 /* create a new pointer array */
488 nNewItems = hdpaTemp->nGrow *
489 (((hdpa->nItemCount - 1) / hdpaTemp->nGrow) + 1);
490 nSize = nNewItems * sizeof(LPVOID);
491 hdpaTemp->ptrs = HeapAlloc (hdpaTemp->hHeap, HEAP_ZERO_MEMORY, nSize);
492 hdpaTemp->nMaxCount = nNewItems;
494 /* clone the pointer array */
495 hdpaTemp->nItemCount = hdpa->nItemCount;
496 memmove (hdpaTemp->ptrs, hdpa->ptrs,
497 hdpaTemp->nItemCount * sizeof(LPVOID));
503 /**************************************************************************
504 * DPA_GetPtr [COMCTL32.332]
506 * Retrieves a pointer from a dynamic pointer array
509 * hdpa [I] handle (pointer) to the pointer array
510 * nIndex [I] array index of the desired pointer
516 LPVOID WINAPI DPA_GetPtr (const HDPA hdpa, INT nIndex)
518 TRACE("(%p %d)\n", hdpa, nIndex);
523 WARN("no pointer array.\n");
526 if ((nIndex < 0) || (nIndex >= hdpa->nItemCount)) {
527 WARN("not enough pointers in array (%d vs %d).\n",nIndex,hdpa->nItemCount);
531 TRACE("-- %p\n", hdpa->ptrs[nIndex]);
533 return hdpa->ptrs[nIndex];
537 /**************************************************************************
538 * DPA_GetPtrIndex [COMCTL32.333]
540 * Retrieves the index of the specified pointer
543 * hdpa [I] handle (pointer) to the pointer array
547 * Success: index of the specified pointer
550 INT WINAPI DPA_GetPtrIndex (HDPA hdpa, LPCVOID p)
554 if (!hdpa || !hdpa->ptrs)
557 for (i = 0; i < hdpa->nItemCount; i++) {
558 if (hdpa->ptrs[i] == p)
566 /**************************************************************************
567 * DPA_InsertPtr [COMCTL32.334]
569 * Inserts a pointer into a dynamic pointer array
572 * hdpa [I] handle (pointer) to the array
574 * p [I] pointer to insert
577 * Success: index of the inserted pointer
580 INT WINAPI DPA_InsertPtr (const HDPA hdpa, INT i, LPVOID p)
582 TRACE("(%p %d %p)\n", hdpa, i, p);
584 if (!hdpa || i < 0) return -1;
586 /* append item if index is out of bounds */
587 i = min(hdpa->nItemCount, i);
589 /* create empty spot at the end */
590 if (!DPA_SetPtr(hdpa, hdpa->nItemCount, 0)) return -1;
592 if (i != hdpa->nItemCount - 1)
593 memmove (hdpa->ptrs + i + 1, hdpa->ptrs + i,
594 (hdpa->nItemCount - i - 1) * sizeof(LPVOID));
601 /**************************************************************************
602 * DPA_SetPtr [COMCTL32.335]
604 * Sets a pointer in the pointer array
607 * hdpa [I] handle (pointer) to the pointer array
608 * i [I] index of the pointer that will be set
609 * p [I] pointer to be set
615 BOOL WINAPI DPA_SetPtr (const HDPA hdpa, INT i, LPVOID p)
619 TRACE("(%p %d %p)\n", hdpa, i, p);
624 if (hdpa->nItemCount <= i) {
625 /* within the old array */
626 if (hdpa->nMaxCount <= i) {
627 /* resize the block of memory */
629 hdpa->nGrow * ((((i+1) - 1) / hdpa->nGrow) + 1);
630 INT nSize = nNewItems * sizeof(LPVOID);
633 lpTemp = HeapReAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY, hdpa->ptrs, nSize);
635 lpTemp = HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY, nSize);
640 hdpa->nMaxCount = nNewItems;
643 hdpa->nItemCount = i+1;
646 /* put the new entry in */
653 /**************************************************************************
654 * DPA_DeletePtr [COMCTL32.336]
656 * Removes a pointer from the pointer array.
659 * hdpa [I] handle (pointer) to the pointer array
660 * i [I] index of the pointer that will be deleted
663 * Success: deleted pointer
666 LPVOID WINAPI DPA_DeletePtr (const HDPA hdpa, INT i)
668 LPVOID *lpDest, *lpSrc, lpTemp = NULL;
671 TRACE("(%p %d)\n", hdpa, i);
673 if ((!hdpa) || i < 0 || i >= hdpa->nItemCount)
676 lpTemp = hdpa->ptrs[i];
678 /* do we need to move ?*/
679 if (i < hdpa->nItemCount - 1) {
680 lpDest = hdpa->ptrs + i;
682 nSize = (hdpa->nItemCount - i - 1) * sizeof(LPVOID);
683 TRACE("-- move dest=%p src=%p size=%x\n",
684 lpDest, lpSrc, nSize);
685 memmove (lpDest, lpSrc, nSize);
691 if ((hdpa->nMaxCount - hdpa->nItemCount) >= hdpa->nGrow) {
692 INT nNewItems = max(hdpa->nGrow * 2, hdpa->nItemCount);
693 nSize = nNewItems * sizeof(LPVOID);
694 lpDest = HeapReAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
699 hdpa->nMaxCount = nNewItems;
707 /**************************************************************************
708 * DPA_DeleteAllPtrs [COMCTL32.337]
710 * Removes all pointers and reinitializes the array.
713 * hdpa [I] handle (pointer) to the pointer array
719 BOOL WINAPI DPA_DeleteAllPtrs (const HDPA hdpa)
721 TRACE("(%p)\n", hdpa);
726 if (hdpa->ptrs && (!HeapFree (hdpa->hHeap, 0, hdpa->ptrs)))
729 hdpa->nItemCount = 0;
730 hdpa->nMaxCount = hdpa->nGrow * 2;
731 hdpa->ptrs = HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
732 hdpa->nMaxCount * sizeof(LPVOID));
738 /**************************************************************************
739 * DPA_QuickSort [Internal]
741 * Ordinary quicksort (used by DPA_Sort).
744 * lpPtrs [I] pointer to the pointer array
745 * l [I] index of the "left border" of the partition
746 * r [I] index of the "right border" of the partition
747 * pfnCompare [I] pointer to the compare function
748 * lParam [I] user defined value (3rd parameter in compare function)
753 static VOID DPA_QuickSort (LPVOID *lpPtrs, INT l, INT r,
754 PFNDPACOMPARE pfnCompare, LPARAM lParam)
759 TRACE("l=%i r=%i\n", l, r);
761 if (l==r) /* one element is always sorted */
763 if (r<l) /* oops, got it in the wrong order */
765 DPA_QuickSort(lpPtrs, r, l, pfnCompare, lParam);
768 m = (l+r)/2; /* divide by two */
769 DPA_QuickSort(lpPtrs, l, m, pfnCompare, lParam);
770 DPA_QuickSort(lpPtrs, m+1, r, pfnCompare, lParam);
772 /* join the two sides */
773 while( (l<=m) && (m<r) )
775 if(pfnCompare(lpPtrs[l],lpPtrs[m+1],lParam)>0)
778 memmove(&lpPtrs[l+1],&lpPtrs[l],(m-l+1)*sizeof(lpPtrs[l]));
788 /**************************************************************************
789 * DPA_Sort [COMCTL32.338]
791 * Sorts a pointer array using a user defined compare function
794 * hdpa [I] handle (pointer) to the pointer array
795 * pfnCompare [I] pointer to the compare function
796 * lParam [I] user defined value (3rd parameter of compare function)
802 BOOL WINAPI DPA_Sort (const HDPA hdpa, PFNDPACOMPARE pfnCompare, LPARAM lParam)
804 if (!hdpa || !pfnCompare)
807 TRACE("(%p %p 0x%lx)\n", hdpa, pfnCompare, lParam);
809 if ((hdpa->nItemCount > 1) && (hdpa->ptrs))
810 DPA_QuickSort (hdpa->ptrs, 0, hdpa->nItemCount - 1,
817 /**************************************************************************
818 * DPA_Search [COMCTL32.339]
820 * Searches a pointer array for a specified pointer
823 * hdpa [I] handle (pointer) to the pointer array
824 * pFind [I] pointer to search for
825 * nStart [I] start index
826 * pfnCompare [I] pointer to the compare function
827 * lParam [I] user defined value (3rd parameter of compare function)
828 * uOptions [I] search options
831 * Success: index of the pointer in the array.
834 INT WINAPI DPA_Search (const HDPA hdpa, LPVOID pFind, INT nStart,
835 PFNDPACOMPARE pfnCompare, LPARAM lParam, UINT uOptions)
837 if (!hdpa || !pfnCompare || !pFind)
840 TRACE("(%p %p %d %p 0x%08lx 0x%08x)\n",
841 hdpa, pFind, nStart, pfnCompare, lParam, uOptions);
843 if (uOptions & DPAS_SORTED) {
844 /* array is sorted --> use binary search */
848 /* for binary search ignore start index */
850 r = hdpa->nItemCount - 1;
854 n = (pfnCompare)(pFind, lpPtr[x], lParam);
862 if (uOptions & (DPAS_INSERTBEFORE|DPAS_INSERTAFTER)) return l;
865 /* array is not sorted --> use linear search */
869 nIndex = (nStart == -1)? 0 : nStart;
871 for (; nIndex < hdpa->nItemCount; nIndex++) {
872 if ((pfnCompare)(pFind, lpPtr[nIndex], lParam) == 0)
881 /**************************************************************************
882 * DPA_CreateEx [COMCTL32.340]
884 * Creates a dynamic pointer array using the specified size and heap.
887 * nGrow [I] number of items by which the array grows when it is filled
888 * hHeap [I] handle to the heap where the array is stored
891 * Success: handle (pointer) to the pointer array.
895 * The DPA_ functions can be used to create and manipulate arrays of
898 HDPA WINAPI DPA_CreateEx (INT nGrow, HANDLE hHeap)
902 TRACE("(%d %p)\n", nGrow, hHeap);
905 hdpa = HeapAlloc (hHeap, HEAP_ZERO_MEMORY, sizeof(*hdpa));
907 hdpa = Alloc (sizeof(*hdpa));
910 hdpa->nGrow = max(8, nGrow);
911 hdpa->hHeap = hHeap ? hHeap : GetProcessHeap();
912 hdpa->nMaxCount = hdpa->nGrow * 2;
913 hdpa->ptrs = HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
914 hdpa->nMaxCount * sizeof(LPVOID));
917 TRACE("-- %p\n", hdpa);
923 /**************************************************************************
924 * DPA_Create [COMCTL32.328]
926 * Creates a dynamic pointer array.
929 * nGrow [I] number of items by which the array grows when it is filled
932 * Success: handle (pointer) to the pointer array.
936 * The DPA_ functions can be used to create and manipulate arrays of
939 HDPA WINAPI DPA_Create (INT nGrow)
941 return DPA_CreateEx( nGrow, 0 );
945 /**************************************************************************
946 * DPA_EnumCallback [COMCTL32.385]
948 * Enumerates all items in a dynamic pointer array.
951 * hdpa [I] handle to the dynamic pointer array
958 VOID WINAPI DPA_EnumCallback (HDPA hdpa, PFNDPAENUMCALLBACK enumProc,
963 TRACE("(%p %p %p)\n", hdpa, enumProc, lParam);
967 if (hdpa->nItemCount <= 0)
970 for (i = 0; i < hdpa->nItemCount; i++) {
971 if ((enumProc)(hdpa->ptrs[i], lParam) == 0)
979 /**************************************************************************
980 * DPA_DestroyCallback [COMCTL32.386]
982 * Enumerates all items in a dynamic pointer array and destroys it.
985 * hdpa [I] handle to the dynamic pointer array
992 void WINAPI DPA_DestroyCallback (HDPA hdpa, PFNDPAENUMCALLBACK enumProc,
995 TRACE("(%p %p %p)\n", hdpa, enumProc, lParam);
997 DPA_EnumCallback (hdpa, enumProc, lParam);
1001 /**************************************************************************
1002 * DPA_GetSize [COMCTL32.@]
1004 * Returns all array allocated memory size
1007 * hdpa [I] handle to the dynamic pointer array
1012 ULONGLONG WINAPI DPA_GetSize(HDPA hdpa)
1014 TRACE("(%p)\n", hdpa);
1016 if (!hdpa) return 0;
1018 return sizeof(DPA) + hdpa->nMaxCount*sizeof(PVOID);