1 /*
2  * Unit tests for DPA functions
3  *
4  * Copyright 2003 Uwe Bonnes
5  * Copyright 2005 Felix Nawothnig
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include "precomp.h"
23 
24 #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
25 
26 typedef struct _STREAMDATA
27 {
28     DWORD dwSize;
29     DWORD dwData2;
30     DWORD dwItems;
31 } STREAMDATA, *PSTREAMDATA;
32 
33 static HDPA    (WINAPI *pDPA_Clone)(const HDPA,HDPA);
34 static HDPA    (WINAPI *pDPA_Create)(INT);
35 static HDPA    (WINAPI *pDPA_CreateEx)(INT,HANDLE);
36 static PVOID   (WINAPI *pDPA_DeleteAllPtrs)(HDPA);
37 static PVOID   (WINAPI *pDPA_DeletePtr)(HDPA,INT);
38 static BOOL    (WINAPI *pDPA_Destroy)(HDPA);
39 static VOID    (WINAPI *pDPA_DestroyCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID);
40 static VOID    (WINAPI *pDPA_EnumCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID);
41 static INT     (WINAPI *pDPA_GetPtr)(HDPA,INT);
42 static INT     (WINAPI *pDPA_GetPtrIndex)(HDPA,PVOID);
43 static BOOL    (WINAPI *pDPA_Grow)(HDPA,INT);
44 static INT     (WINAPI *pDPA_InsertPtr)(HDPA,INT,PVOID);
45 static HRESULT (WINAPI *pDPA_LoadStream)(HDPA*,PFNDPASTREAM,IStream*,LPVOID);
46 static BOOL    (WINAPI *pDPA_Merge)(HDPA,HDPA,DWORD,PFNDPACOMPARE,PFNDPAMERGE,LPARAM);
47 static HRESULT (WINAPI *pDPA_SaveStream)(HDPA,PFNDPASTREAM,IStream*,LPVOID);
48 static INT     (WINAPI *pDPA_Search)(HDPA,PVOID,INT,PFNDPACOMPARE,LPARAM,UINT);
49 static BOOL    (WINAPI *pDPA_SetPtr)(HDPA,INT,PVOID);
50 static BOOL    (WINAPI *pDPA_Sort)(HDPA,PFNDPACOMPARE,LPARAM);
51 
52 #define COMCTL32_GET_PROC(func, ord) \
53   ((p ## func = (PVOID)GetProcAddress(hcomctl32,(LPCSTR)ord)) ? 1 \
54    : (trace( #func " not exported\n"), 0))
55 
56 static BOOL InitFunctionPtrs(HMODULE hcomctl32)
57 {
58     /* 4.00+ */
59     if(COMCTL32_GET_PROC(DPA_Clone, 331) &&
60        COMCTL32_GET_PROC(DPA_Create, 328) &&
61        COMCTL32_GET_PROC(DPA_CreateEx, 340) &&
62        COMCTL32_GET_PROC(DPA_DeleteAllPtrs, 337) &&
63        COMCTL32_GET_PROC(DPA_DeletePtr, 336) &&
64        COMCTL32_GET_PROC(DPA_Destroy, 329) &&
65        COMCTL32_GET_PROC(DPA_GetPtr, 332) &&
66        COMCTL32_GET_PROC(DPA_GetPtrIndex, 333) &&
67        COMCTL32_GET_PROC(DPA_Grow, 330) &&
68        COMCTL32_GET_PROC(DPA_InsertPtr, 334) &&
69        COMCTL32_GET_PROC(DPA_Search, 339) &&
70        COMCTL32_GET_PROC(DPA_SetPtr, 335) &&
71        COMCTL32_GET_PROC(DPA_Sort, 338))
72     {
73         /* 4.71+ */
74         COMCTL32_GET_PROC(DPA_DestroyCallback, 386) &&
75         COMCTL32_GET_PROC(DPA_EnumCallback, 385) &&
76         COMCTL32_GET_PROC(DPA_LoadStream, 9) &&
77         COMCTL32_GET_PROC(DPA_Merge, 11) &&
78         COMCTL32_GET_PROC(DPA_SaveStream, 10);
79 
80         return TRUE;
81     }
82 
83     return FALSE;
84 }
85 
86 /* Callbacks */
87 static INT CALLBACK CB_CmpLT(PVOID p1, PVOID p2, LPARAM lp)
88 {
89     ok(lp == 0x1abe11ed, "lp=%ld\n", lp);
90     return p1 < p2 ? -1 : p1 > p2 ? 1 : 0;
91 }
92 
93 static INT CALLBACK CB_CmpGT(PVOID p1, PVOID p2, LPARAM lp)
94 {
95     ok(lp == 0x1abe11ed, "lp=%ld\n", lp);
96     return p1 > p2 ? -1 : p1 < p2 ? 1 : 0;
97 }
98 
99 /* merge callback messages counter
100    DPAMM_MERGE     1
101    DPAMM_DELETE    2
102    DPAMM_INSERT    3  */
103 static INT nMessages[4];
104 
105 static PVOID CALLBACK CB_MergeInsertSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
106 {
107     nMessages[op]++;
108     ok(lp == 0x1abe11ed, "lp=%ld\n", lp);
109     return p1;
110 }
111 
112 static PVOID CALLBACK CB_MergeDeleteOddSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
113 {
114     nMessages[op]++;
115     ok(lp == 0x1abe11ed, "lp=%ld\n", lp);
116     return ((PCHAR)p2)+1;
117 }
118 
119 static INT nEnum;
120 
121 static INT CALLBACK CB_EnumFirstThree(PVOID pItem, PVOID lp)
122 {
123     INT i;
124 
125     i = pDPA_GetPtrIndex(lp, pItem);
126     ok(i == nEnum, "i=%d nEnum=%d\n", i, nEnum);
127     nEnum++;
128     pDPA_SetPtr(lp, i, (PVOID)7);
129     return pItem != (PVOID)3;
130 }
131 
132 static HRESULT CALLBACK CB_Save(DPASTREAMINFO *pInfo, IStream *pStm, LPVOID lp)
133 {
134     HRESULT hRes;
135 
136     ok(lp == (LPVOID)0xdeadbeef, "lp=%p\n", lp);
137     hRes = IStream_Write(pStm, &pInfo->iPos, sizeof(INT), NULL);
138     expect(S_OK, hRes);
139     hRes = IStream_Write(pStm, &pInfo->pvItem, sizeof(PVOID), NULL);
140     expect(S_OK, hRes);
141     return S_OK;
142 }
143 
144 static HRESULT CALLBACK CB_Load(DPASTREAMINFO *pInfo, IStream *pStm, LPVOID lp)
145 {
146     HRESULT hRes;
147     INT iOldPos;
148 
149     iOldPos = pInfo->iPos;
150     ok(lp == (LPVOID)0xdeadbeef, "lp=%p\n", lp);
151     hRes = IStream_Read(pStm, &pInfo->iPos, sizeof(INT), NULL);
152     expect(S_OK, hRes);
153     ok(pInfo->iPos == iOldPos, "iPos=%d iOldPos=%d\n", pInfo->iPos, iOldPos);
154     hRes = IStream_Read(pStm, &pInfo->pvItem, sizeof(PVOID), NULL);
155     expect(S_OK, hRes);
156     return S_OK;
157 }
158 
159 static BOOL CheckDPA(HDPA dpa, DWORD dwIn, PDWORD pdwOut)
160 {
161     DWORD dwOut = 0;
162     INT i;
163 
164     for(i = 0; i < 8;)
165     {
166         ULONG_PTR ulItem = (ULONG_PTR)pDPA_GetPtr(dpa, i++);
167         if(!ulItem) break;
168         dwOut = dwOut << 4 | (ulItem & 0xf);
169     }
170 
171     *pdwOut = dwOut;
172 
173     if(dwOut != dwIn)
174     {
175         pDPA_DeleteAllPtrs(dpa);
176 
177         do
178         {
179             pDPA_InsertPtr(dpa, 0, (PVOID)(ULONG_PTR)(dwIn & 0xf));
180             dwIn >>= 4;
181         }
182         while(dwIn);
183 
184         return FALSE;
185     }
186 
187     return TRUE;
188 }
189 
190 static void test_dpa(void)
191 {
192     SYSTEM_INFO si;
193     HANDLE hHeap;
194     HDPA dpa, dpa2, dpa3;
195     INT ret, i;
196     PVOID p;
197     DWORD dw, dw2, dw3;
198     BOOL rc;
199 
200     GetSystemInfo(&si);
201     hHeap = HeapCreate(0, 1, 2);
202     ok(hHeap != NULL, "error=%d\n", GetLastError());
203     dpa3 = pDPA_CreateEx(0, hHeap);
204     ok(dpa3 != NULL, "\n");
205     ret = pDPA_Grow(dpa3, si.dwPageSize + 1);
206     ok(!ret && GetLastError() == ERROR_NOT_ENOUGH_MEMORY,
207        "ret=%d error=%d\n", ret, GetLastError());
208 
209     dpa = pDPA_Create(0);
210     ok(dpa != NULL, "\n");
211 
212     /* Set item with out of bound index */
213     ok(pDPA_SetPtr(dpa, 1, (PVOID)6), "\n");
214     /* Fill the created gap */
215     ok(pDPA_SetPtr(dpa, 0, (PVOID)5), "\n");
216     rc=CheckDPA(dpa, 0x56, &dw);
217     ok(rc, "dw=0x%x\n", dw);
218 
219     /* Prepend item */
220     ret = pDPA_InsertPtr(dpa, 1, (PVOID)1);
221     ok(ret == 1, "ret=%d\n", ret);
222     /* Append item using correct index */
223     ret = pDPA_InsertPtr(dpa, 3, (PVOID)3);
224     ok(ret == 3, "ret=%d\n", ret);
225     /* Append item using out of bound index */
226     ret = pDPA_InsertPtr(dpa, 5, (PVOID)2);
227     ok(ret == 4, "ret=%d\n", ret);
228     /* Append item using DPA_APPEND */
229     ret = pDPA_InsertPtr(dpa, DPA_APPEND, (PVOID)4);
230     ok(ret == 5, "ret=%d\n", ret);
231 
232     rc=CheckDPA(dpa, 0x516324, &dw);
233     ok(rc, "dw=0x%x\n", dw);
234 
235     for(i = 1; i <= 6; i++)
236     {
237         INT j, k;
238         k = pDPA_GetPtrIndex(dpa, (PVOID)(INT_PTR)i);
239         /* Linear searches should work on unsorted DPAs */
240         j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, 0, CB_CmpLT, 0x1abe11ed, 0);
241         ok(j == k, "j=%d k=%d\n", j, k);
242     }
243 
244     /* Sort DPA */
245     ok(pDPA_Sort(dpa, CB_CmpGT, 0x1abe11ed), "\n");
246     rc=CheckDPA(dpa, 0x654321, &dw);
247     ok(rc, "dw=0x%x\n", dw);
248 
249     /* Clone into a new DPA */
250     dpa2 = pDPA_Clone(dpa, NULL);
251     ok(dpa2 != NULL, "\n");
252     /* The old data should have been preserved */
253     rc=CheckDPA(dpa2, 0x654321, &dw2);
254     ok(rc, "dw=0x%x\n", dw2);
255     ok(pDPA_Sort(dpa, CB_CmpLT, 0x1abe11ed), "\n");
256 
257     /* Test if the DPA itself was really copied */
258     rc=CheckDPA(dpa,  0x123456, &dw);
259     ok(rc, "dw=0x%x\n",  dw );
260     rc=CheckDPA(dpa2, 0x654321, &dw2);
261     ok(rc, "dw2=0x%x\n", dw2);
262 
263     /* Clone into an old DPA */
264     SetLastError(ERROR_SUCCESS);
265     p = pDPA_Clone(dpa, dpa3);
266     ok(p == dpa3, "p=%p\n", p);
267     rc=CheckDPA(dpa3, 0x123456, &dw3);
268     ok(rc, "dw3=0x%x\n", dw3);
269 
270     for(i = 1; i <= 6; i++)
271     {
272         INT j;
273 
274         /* The array is in order so ptr == index+1 */
275         j = pDPA_GetPtrIndex(dpa, (PVOID)(INT_PTR)i);
276         ok(j+1 == i, "j=%d i=%d\n", j, i);
277         j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, 0, CB_CmpLT, 0x1abe11ed, DPAS_SORTED);
278         ok(j+1 == i, "j=%d i=%d\n", j, i);
279 
280         /* Linear searches respect iStart ... */
281         j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, i+1, CB_CmpLT, 0x1abe11ed, 0);
282         ok(j == DPA_ERR, "j=%d\n", j);
283         /* ... but for a binary search it's ignored */
284         j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, i+1, CB_CmpLT, 0x1abe11ed, DPAS_SORTED);
285         ok(j+1 == i, "j=%d i=%d\n", j, i);
286     }
287 
288     /* Try to get the index of a nonexistent item */
289     i = pDPA_GetPtrIndex(dpa, (PVOID)7);
290     ok(i == DPA_ERR, "i=%d\n", i);
291 
292     /* Try to delete out of bound indexes */
293     p = pDPA_DeletePtr(dpa, -1);
294     ok(p == NULL, "p=%p\n", p);
295     p = pDPA_DeletePtr(dpa, 6);
296     ok(p == NULL, "p=%p\n", p);
297 
298     /* Delete the third item */
299     p = pDPA_DeletePtr(dpa, 2);
300     ok(p == (PVOID)3, "p=%p\n", p);
301     rc=CheckDPA(dpa, 0x12456, &dw);
302     ok(rc, "dw=0x%x\n", dw);
303 
304     /* Check where to re-insert the deleted item */
305     i = pDPA_Search(dpa, (PVOID)3, 0,
306                     CB_CmpLT, 0x1abe11ed, DPAS_SORTED|DPAS_INSERTAFTER);
307     ok(i == 2, "i=%d\n", i);
308     /* DPAS_INSERTBEFORE works just like DPAS_INSERTAFTER */
309     i = pDPA_Search(dpa, (PVOID)3, 0,
310                     CB_CmpLT, 0x1abe11ed, DPAS_SORTED|DPAS_INSERTBEFORE);
311     ok(i == 2, "i=%d\n", i);
312     /* without DPAS_INSERTBEFORE/AFTER */
313     i = pDPA_Search(dpa, (PVOID)3, 0,
314                     CB_CmpLT, 0x1abe11ed, DPAS_SORTED);
315     ok(i == -1, "i=%d\n", i);
316 
317     /* Re-insert the item */
318     ret = pDPA_InsertPtr(dpa, 2, (PVOID)3);
319     ok(ret == 2, "ret=%d i=%d\n", ret, 2);
320     rc=CheckDPA(dpa, 0x123456, &dw);
321     ok(rc, "dw=0x%x\n", dw);
322 
323     /* When doing a binary search while claiming reverse order all indexes
324      * should be bogus */
325     for(i = 0; i < 6; i++)
326     {
327         INT j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, 0, CB_CmpGT, 0x1abe11ed,
328                             DPAS_SORTED|DPAS_INSERTBEFORE);
329         ok(j != i, "i=%d\n", i);
330     }
331 
332     /* Setting item with huge index should work */
333     ok(pDPA_SetPtr(dpa2, 0x12345, (PVOID)0xdeadbeef), "\n");
334     ret = pDPA_GetPtrIndex(dpa2, (PVOID)0xdeadbeef);
335     ok(ret == 0x12345, "ret=%d\n", ret);
336 
337     pDPA_DeleteAllPtrs(dpa2);
338     rc=CheckDPA(dpa2, 0, &dw2);
339     ok(rc, "dw2=0x%x\n", dw2);
340 
341     pDPA_Destroy(dpa);
342     pDPA_Destroy(dpa2);
343     pDPA_Destroy(dpa3);
344 }
345 
346 static void test_DPA_Merge(void)
347 {
348     HDPA dpa, dpa2, dpa3;
349     INT ret, i;
350     DWORD dw;
351     BOOL rc;
352 
353     if(!pDPA_Merge)
354     {
355         win_skip("DPA_Merge() not available\n");
356         return;
357     }
358 
359     dpa  = pDPA_Create(0);
360     dpa2 = pDPA_Create(0);
361     dpa3 = pDPA_Create(0);
362 
363     ret = pDPA_InsertPtr(dpa, 0, (PVOID)1);
364     ok(ret == 0, "ret=%d\n", ret);
365     ret = pDPA_InsertPtr(dpa, 1, (PVOID)3);
366     ok(ret == 1, "ret=%d\n", ret);
367     ret = pDPA_InsertPtr(dpa, 2, (PVOID)5);
368     ok(ret == 2, "ret=%d\n", ret);
369 
370     rc = CheckDPA(dpa, 0x135, &dw);
371     ok(rc, "dw=0x%x\n", dw);
372 
373     for (i = 0; i < 6; i++)
374     {
375         ret = pDPA_InsertPtr(dpa2, i, (PVOID)(INT_PTR)(6-i));
376         ok(ret == i, "ret=%d\n", ret);
377         ret = pDPA_InsertPtr(dpa3, i, (PVOID)(INT_PTR)(i+1));
378         ok(ret == i, "ret=%d\n", ret);
379     }
380 
381     rc = CheckDPA(dpa2, 0x654321, &dw);
382     ok(rc, "dw=0x%x\n", dw);
383     rc = CheckDPA(dpa3, 0x123456, &dw);
384     ok(rc, "dw=0x%x\n", dw);
385 
386     /* Delete all odd entries from dpa2 */
387     memset(nMessages, 0, sizeof(nMessages));
388     pDPA_Merge(dpa2, dpa, DPAM_INTERSECT,
389                CB_CmpLT, CB_MergeDeleteOddSrc, 0x1abe11ed);
390     rc = CheckDPA(dpa2, 0x246, &dw);
391     ok(rc, "dw=0x%x\n", dw);
392 
393     expect(3, nMessages[DPAMM_MERGE]);
394     expect(3, nMessages[DPAMM_DELETE]);
395     expect(0, nMessages[DPAMM_INSERT]);
396 
397     for (i = 0; i < 6; i++)
398     {
399         ret = pDPA_InsertPtr(dpa2, i, (PVOID)(INT_PTR)(6-i));
400         ok(ret == i, "ret=%d\n", ret);
401     }
402 
403     /* DPAM_INTERSECT - returning source while merging */
404     memset(nMessages, 0, sizeof(nMessages));
405     pDPA_Merge(dpa2, dpa, DPAM_INTERSECT,
406                CB_CmpLT, CB_MergeInsertSrc, 0x1abe11ed);
407     rc = CheckDPA(dpa2, 0x135, &dw);
408     ok(rc, "dw=0x%x\n", dw);
409 
410     expect(3, nMessages[DPAMM_MERGE]);
411     expect(6, nMessages[DPAMM_DELETE]);
412     expect(0, nMessages[DPAMM_INSERT]);
413 
414     /* DPAM_UNION */
415     pDPA_DeleteAllPtrs(dpa);
416     pDPA_InsertPtr(dpa, 0, (PVOID)1);
417     pDPA_InsertPtr(dpa, 1, (PVOID)3);
418     pDPA_InsertPtr(dpa, 2, (PVOID)5);
419     pDPA_DeleteAllPtrs(dpa2);
420     pDPA_InsertPtr(dpa2, 0, (PVOID)2);
421     pDPA_InsertPtr(dpa2, 1, (PVOID)4);
422     pDPA_InsertPtr(dpa2, 2, (PVOID)6);
423 
424     memset(nMessages, 0, sizeof(nMessages));
425     pDPA_Merge(dpa2, dpa, DPAM_UNION,
426                CB_CmpLT, CB_MergeInsertSrc, 0x1abe11ed);
427     rc = CheckDPA(dpa2, 0x123456, &dw);
428     ok(rc ||
429        broken(!rc && dw == 0x23456), /* 4.7x */
430        "dw=0x%x\n", dw);
431 
432     expect(0, nMessages[DPAMM_MERGE]);
433     expect(0, nMessages[DPAMM_DELETE]);
434     ok(nMessages[DPAMM_INSERT] == 3 ||
435        broken(nMessages[DPAMM_INSERT] == 2), /* 4.7x */
436        "Expected 3, got %d\n", nMessages[DPAMM_INSERT]);
437 
438     /* Merge dpa3 into dpa2 and dpa */
439     memset(nMessages, 0, sizeof(nMessages));
440     pDPA_Merge(dpa, dpa3, DPAM_UNION|DPAM_SORTED,
441                CB_CmpLT, CB_MergeInsertSrc, 0x1abe11ed);
442     expect(3, nMessages[DPAMM_MERGE]);
443     expect(0, nMessages[DPAMM_DELETE]);
444     expect(3, nMessages[DPAMM_INSERT]);
445 
446 
447     pDPA_DeleteAllPtrs(dpa2);
448     pDPA_InsertPtr(dpa2, 0, (PVOID)2);
449     pDPA_InsertPtr(dpa2, 1, (PVOID)4);
450     pDPA_InsertPtr(dpa2, 2, (PVOID)6);
451 
452     memset(nMessages, 0, sizeof(nMessages));
453     pDPA_Merge(dpa2, dpa3, DPAM_UNION|DPAM_SORTED,
454                CB_CmpLT, CB_MergeInsertSrc, 0x1abe11ed);
455     expect(3, nMessages[DPAMM_MERGE]);
456     expect(0, nMessages[DPAMM_DELETE]);
457     ok(nMessages[DPAMM_INSERT] == 3 ||
458        broken(nMessages[DPAMM_INSERT] == 2), /* 4.7x */
459        "Expected 3, got %d\n", nMessages[DPAMM_INSERT]);
460 
461     rc = CheckDPA(dpa,  0x123456, &dw);
462     ok(rc, "dw=0x%x\n",  dw);
463     rc = CheckDPA(dpa2, 0x123456, &dw);
464     ok(rc ||
465        broken(!rc), /* win98 */
466        "dw=0x%x\n", dw);
467     rc = CheckDPA(dpa3, 0x123456, &dw);
468     ok(rc, "dw=0x%x\n", dw);
469 
470     pDPA_Destroy(dpa);
471     pDPA_Destroy(dpa2);
472     pDPA_Destroy(dpa3);
473 }
474 
475 static void test_DPA_EnumCallback(void)
476 {
477     HDPA dpa;
478     BOOL rc;
479     DWORD dw;
480     INT i, ret;
481 
482     if(!pDPA_EnumCallback)
483     {
484         win_skip("DPA_EnumCallback() not available\n");
485         return;
486     }
487 
488     dpa = pDPA_Create(0);
489 
490     for (i = 0; i < 6; i++)
491     {
492         ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1));
493         ok(ret == i, "ret=%d\n", ret);
494     }
495 
496     rc = CheckDPA(dpa, 0x123456, &dw);
497     ok(rc, "dw=0x%x\n", dw);
498 
499     nEnum = 0;
500     /* test callback sets first 3 items to 7 */
501     pDPA_EnumCallback(dpa, CB_EnumFirstThree, dpa);
502     rc = CheckDPA(dpa, 0x777456, &dw);
503     ok(rc, "dw=0x%x\n", dw);
504     ok(nEnum == 3, "nEnum=%d\n", nEnum);
505 
506     pDPA_Destroy(dpa);
507 }
508 
509 static void test_DPA_DestroyCallback(void)
510 {
511     HDPA dpa;
512     INT i, ret;
513 
514     if(!pDPA_DestroyCallback)
515     {
516         win_skip("DPA_DestroyCallback() not available\n");
517         return;
518     }
519 
520     dpa = pDPA_Create(0);
521 
522     for (i = 0; i < 3; i++)
523     {
524         ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1));
525         ok(ret == i, "ret=%d\n", ret);
526     }
527 
528     nEnum = 0;
529     pDPA_DestroyCallback(dpa, CB_EnumFirstThree, dpa);
530     ok(nEnum == 3, "nEnum=%d\n", nEnum);
531 }
532 
533 static void test_DPA_LoadStream(void)
534 {
535     static const WCHAR szStg[] = { 'S','t','g',0 };
536     IStorage* pStg = NULL;
537     IStream* pStm = NULL;
538     LARGE_INTEGER li;
539     ULARGE_INTEGER uli;
540     DWORD dwMode;
541     HRESULT hRes;
542     STREAMDATA header;
543     ULONG written, ret;
544     HDPA dpa;
545 
546     if(!pDPA_LoadStream)
547     {
548         win_skip("DPA_LoadStream() not available. Skipping stream tests.\n");
549         return;
550     }
551 
552     hRes = CoInitialize(NULL);
553     if (hRes != S_OK)
554     {
555         ok(0, "hResult: %d\n", hRes);
556         return;
557     }
558 
559     dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
560     hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
561     expect(S_OK, hRes);
562 
563     hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
564     expect(S_OK, hRes);
565 
566     /* write less than header size */
567     li.QuadPart = 0;
568     hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
569     expect(S_OK, hRes);
570 
571     memset(&header, 0, sizeof(header));
572     written = 0;
573     uli.QuadPart = sizeof(header)-1;
574     hRes = IStream_SetSize(pStm, uli);
575     expect(S_OK, hRes);
576     hRes = IStream_Write(pStm, &header, sizeof(header)-1, &written);
577     expect(S_OK, hRes);
578     written -= sizeof(header)-1;
579     expect(0, written);
580 
581     li.QuadPart = 0;
582     hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
583     expect(S_OK, hRes);
584 
585     hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, NULL);
586     expect(E_FAIL, hRes);
587 
588     /* check stream position after header read failed */
589     li.QuadPart = 0;
590     uli.QuadPart = 1;
591     hRes = IStream_Seek(pStm, li, STREAM_SEEK_CUR, &uli);
592     expect(S_OK, hRes);
593     ok(uli.QuadPart == 0, "Expected to position reset\n");
594 
595     /* write valid header for empty DPA */
596     header.dwSize = sizeof(header);
597     header.dwData2 = 1;
598     header.dwItems = 0;
599     written = 0;
600 
601     li.QuadPart = 0;
602     hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
603     expect(S_OK, hRes);
604 
605     uli.QuadPart = sizeof(header);
606     hRes = IStream_SetSize(pStm, uli);
607     expect(S_OK, hRes);
608 
609     hRes = IStream_Write(pStm, &header, sizeof(header), &written);
610     expect(S_OK, hRes);
611     written -= sizeof(header);
612     expect(0, written);
613 
614     li.QuadPart = 0;
615     hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
616     expect(S_OK, hRes);
617 
618     dpa = NULL;
619     hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, NULL);
620     expect(S_OK, hRes);
621     DPA_Destroy(dpa);
622 
623     /* try with altered dwData2 field */
624     header.dwSize = sizeof(header);
625     header.dwData2 = 2;
626     header.dwItems = 0;
627 
628     li.QuadPart = 0;
629     hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
630     expect(S_OK, hRes);
631     hRes = IStream_Write(pStm, &header, sizeof(header), &written);
632     expect(S_OK, hRes);
633     written -= sizeof(header);
634     expect(0, written);
635 
636     li.QuadPart = 0;
637     hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
638     expect(S_OK, hRes);
639 
640     hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, (void*)0xdeadbeef);
641     expect(E_FAIL, hRes);
642 
643     ret = IStream_Release(pStm);
644     ok(!ret, "ret=%d\n", ret);
645 
646     ret = IStorage_Release(pStg);
647     ok(!ret, "ret=%d\n", ret);
648 
649     CoUninitialize();
650 }
651 
652 static void test_DPA_SaveStream(void)
653 {
654     HDPA dpa;
655     static const WCHAR szStg[] = { 'S','t','g',0 };
656     IStorage* pStg = NULL;
657     IStream* pStm = NULL;
658     DWORD dwMode, dw;
659     HRESULT hRes;
660     INT ret;
661     INT i;
662     BOOL rc;
663     LARGE_INTEGER liZero;
664 
665     if(!pDPA_SaveStream)
666     {
667         win_skip("DPA_SaveStream() not available. Skipping stream tests.\n");
668         return;
669     }
670 
671     hRes = CoInitialize(NULL);
672     if (hRes != S_OK)
673     {
674         ok(0, "hResult: %d\n", hRes);
675         return;
676     }
677 
678     dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
679     hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
680     expect(S_OK, hRes);
681 
682     hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
683     expect(S_OK, hRes);
684 
685     dpa = pDPA_Create(0);
686 
687     /* simple parameter check */
688     hRes = pDPA_SaveStream(dpa, NULL, pStm, NULL);
689     ok(hRes == E_INVALIDARG ||
690        broken(hRes == S_OK) /* XP and below */, "Wrong result, %d\n", hRes);
691 if (0) {
692     /* crashes on XP */
693     hRes = pDPA_SaveStream(NULL, CB_Save, pStm, NULL);
694     expect(E_INVALIDARG, hRes);
695 
696     hRes = pDPA_SaveStream(dpa, CB_Save, NULL, NULL);
697     expect(E_INVALIDARG, hRes);
698 }
699 
700     /* saving/loading */
701     for (i = 0; i < 6; i++)
702     {
703         ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1));
704         ok(ret == i, "ret=%d\n", ret);
705     }
706 
707     liZero.QuadPart = 0;
708     hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL);
709     expect(S_OK, hRes);
710 
711     hRes = pDPA_SaveStream(dpa, CB_Save, pStm, (void*)0xdeadbeef);
712     expect(S_OK, hRes);
713     pDPA_Destroy(dpa);
714 
715     liZero.QuadPart = 0;
716     hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL);
717     expect(S_OK, hRes);
718     hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, (void*)0xdeadbeef);
719     expect(S_OK, hRes);
720     rc = CheckDPA(dpa, 0x123456, &dw);
721     ok(rc, "dw=0x%x\n", dw);
722     pDPA_Destroy(dpa);
723 
724     ret = IStream_Release(pStm);
725     ok(!ret, "ret=%d\n", ret);
726 
727     ret = IStorage_Release(pStg);
728     ok(!ret, "ret=%d\n", ret);
729 
730     CoUninitialize();
731 }
732 
733 START_TEST(dpa)
734 {
735     HMODULE hcomctl32;
736 
737     hcomctl32 = GetModuleHandleA("comctl32.dll");
738 
739     if(!InitFunctionPtrs(hcomctl32))
740     {
741         win_skip("Needed functions are not available\n");
742         return;
743     }
744 
745     test_dpa();
746     test_DPA_Merge();
747     test_DPA_EnumCallback();
748     test_DPA_DestroyCallback();
749     test_DPA_LoadStream();
750     test_DPA_SaveStream();
751 }
752