include: Move some definitions to public header, use PSDK names.
[wine] / dlls / comctl32 / tests / dpa.c
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 #define COBJMACROS
23
24 #include <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "commctrl.h"
30 #include "objidl.h"
31
32 #include "wine/test.h"
33
34 #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
35
36 static HDPA    (WINAPI *pDPA_Clone)(const HDPA,const HDPA);
37 static HDPA    (WINAPI *pDPA_Create)(INT);
38 static HDPA    (WINAPI *pDPA_CreateEx)(INT,HANDLE);
39 static PVOID   (WINAPI *pDPA_DeleteAllPtrs)(const HDPA);
40 static PVOID   (WINAPI *pDPA_DeletePtr)(const HDPA,INT);
41 static BOOL    (WINAPI *pDPA_Destroy)(const HDPA);
42 static VOID    (WINAPI *pDPA_DestroyCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID);
43 static VOID    (WINAPI *pDPA_EnumCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID); 
44 static INT     (WINAPI *pDPA_GetPtr)(const HDPA,INT);
45 static INT     (WINAPI *pDPA_GetPtrIndex)(const HDPA,PVOID);
46 static BOOL    (WINAPI *pDPA_Grow)(HDPA,INT);
47 static INT     (WINAPI *pDPA_InsertPtr)(const HDPA,INT,PVOID);
48 static HRESULT (WINAPI *pDPA_LoadStream)(HDPA*,PFNDPASTREAM,IStream*,LPARAM);
49 static BOOL    (WINAPI *pDPA_Merge)(const HDPA,const HDPA,DWORD,PFNDPACOMPARE,PFNDPAMERGE,LPARAM);
50 static HRESULT (WINAPI *pDPA_SaveStream)(HDPA,PFNDPASTREAM,IStream*,LPARAM);
51 static INT     (WINAPI *pDPA_Search)(HDPA,PVOID,INT,PFNDPACOMPARE,LPARAM,UINT);
52 static BOOL    (WINAPI *pDPA_SetPtr)(const HDPA,INT,PVOID);
53 static BOOL    (WINAPI *pDPA_Sort)(const HDPA,PFNDPACOMPARE,LPARAM);
54
55 #define COMCTL32_GET_PROC(func, ord) \
56   ((p ## func = (PVOID)GetProcAddress(hcomctl32,(LPCSTR)ord)) ? 1 \
57    : (trace( #func " not exported\n"), 0)) 
58
59 static BOOL InitFunctionPtrs(HMODULE hcomctl32)
60 {
61     /* 4.00+ */
62     if(COMCTL32_GET_PROC(DPA_Clone, 331) &&
63        COMCTL32_GET_PROC(DPA_Create, 328) &&
64        COMCTL32_GET_PROC(DPA_CreateEx, 340) &&
65        COMCTL32_GET_PROC(DPA_DeleteAllPtrs, 337) &&
66        COMCTL32_GET_PROC(DPA_DeletePtr, 336) &&
67        COMCTL32_GET_PROC(DPA_Destroy, 329) &&
68        COMCTL32_GET_PROC(DPA_GetPtr, 332) &&
69        COMCTL32_GET_PROC(DPA_GetPtrIndex, 333) &&
70        COMCTL32_GET_PROC(DPA_Grow, 330) &&
71        COMCTL32_GET_PROC(DPA_InsertPtr, 334) &&
72        COMCTL32_GET_PROC(DPA_Search, 339) &&
73        COMCTL32_GET_PROC(DPA_SetPtr, 335) &&
74        COMCTL32_GET_PROC(DPA_Sort, 338))
75     {
76         /* 4.71+ */
77         COMCTL32_GET_PROC(DPA_DestroyCallback, 386) &&
78         COMCTL32_GET_PROC(DPA_EnumCallback, 385) &&
79         COMCTL32_GET_PROC(DPA_LoadStream, 9) &&
80         COMCTL32_GET_PROC(DPA_Merge, 11) &&
81         COMCTL32_GET_PROC(DPA_SaveStream, 10);
82
83         return TRUE;
84     }
85
86     return FALSE;
87 }
88
89 /* Callbacks */
90 static INT CALLBACK CB_CmpLT(PVOID p1, PVOID p2, LPARAM lp)
91 {
92     ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
93     return p1 < p2 ? -1 : p1 > p2 ? 1 : 0;
94 }
95
96 static INT CALLBACK CB_CmpGT(PVOID p1, PVOID p2, LPARAM lp)
97 {
98     ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
99     return p1 > p2 ? -1 : p1 < p2 ? 1 : 0;
100 }
101
102 /* merge callback messages counter
103    DPAMM_MERGE     1
104    DPAMM_DELETE    2
105    DPAMM_INSERT    3  */
106 static INT nMessages[4];
107
108 static PVOID CALLBACK CB_MergeInsertSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
109 {
110     nMessages[op]++;
111     ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
112     return p1;
113 }        
114
115 static PVOID CALLBACK CB_MergeDeleteOddSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
116 {
117     nMessages[op]++;
118     ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
119     return ((PCHAR)p2)+1;
120 }
121
122 static INT nEnum;
123
124 static INT CALLBACK CB_EnumFirstThree(PVOID pItem, PVOID lp)
125 {   
126     INT i;
127
128     i = pDPA_GetPtrIndex(lp, pItem);
129     ok(i == nEnum, "i=%d nEnum=%d\n", i, nEnum);
130     nEnum++;
131     pDPA_SetPtr(lp, i, (PVOID)7);
132     return pItem != (PVOID)3;
133 }
134
135 static HRESULT CALLBACK CB_Save(DPASTREAMINFO *pInfo, IStream *pStm, LPVOID lp)
136 {
137     HRESULT hRes;
138     
139     ok(lp == (LPVOID)0xdeadbeef, "lp=%p\n", lp);
140     hRes = IStream_Write(pStm, &pInfo->iPos, sizeof(INT), NULL);
141     ok(hRes == S_OK, "hRes=0x%x\n", hRes);
142     hRes = IStream_Write(pStm, &pInfo->pvItem, sizeof(PVOID), NULL);
143     ok(hRes == S_OK, "hRes=0x%x\n", hRes);
144     return S_OK;
145 }
146
147 static HRESULT CALLBACK CB_Load(DPASTREAMINFO *pInfo, IStream *pStm, LPVOID lp)
148 {
149     HRESULT hRes;
150     INT iOldPos;
151     
152     iOldPos = pInfo->iPos;
153     ok(lp == (LPVOID)0xdeadbeef, "lp=%p\n", lp);
154     hRes = IStream_Read(pStm, &pInfo->iPos, sizeof(INT), NULL);
155     ok(hRes == S_OK, "hRes=0x%x\n", hRes);
156     ok(pInfo->iPos == iOldPos, "iPos=%d iOldPos=%d\n", pInfo->iPos, iOldPos);
157     hRes = IStream_Read(pStm, &pInfo->pvItem, sizeof(PVOID), NULL);
158     ok(hRes == S_OK, "hRes=0x%x\n", hRes);
159     return S_OK;
160 }
161
162 static BOOL CheckDPA(HDPA dpa, DWORD dwIn, PDWORD pdwOut)
163 {
164     DWORD dwOut = 0;
165     INT i;
166
167     for(i = 0; i < 8;)
168     {
169         ULONG_PTR ulItem = (ULONG_PTR)pDPA_GetPtr(dpa, i++);
170         if(!ulItem) break;
171         dwOut = dwOut << 4 | (ulItem & 0xf);
172     }
173     
174     *pdwOut = dwOut;
175
176     if(dwOut != dwIn)
177     {
178         pDPA_DeleteAllPtrs(dpa);
179         
180         do
181         {
182             pDPA_InsertPtr(dpa, 0, (PVOID)(ULONG_PTR)(dwIn & 0xf));
183             dwIn >>= 4;
184         }
185         while(dwIn);
186         
187         return FALSE;
188     }
189     
190     return TRUE;
191 }
192
193 static void test_dpa(void)
194 {
195     SYSTEM_INFO si;
196     HANDLE hHeap;
197     HDPA dpa, dpa2, dpa3;
198     INT ret, i;
199     PVOID p;
200     DWORD dw, dw2, dw3;
201     BOOL rc;
202     
203     GetSystemInfo(&si);
204     hHeap = HeapCreate(0, 1, 2);
205     ok(hHeap != NULL, "error=%d\n", GetLastError());
206     dpa3 = pDPA_CreateEx(0, hHeap);
207     ok(dpa3 != NULL, "\n");
208     ret = pDPA_Grow(dpa3, si.dwPageSize + 1);
209     todo_wine ok(!ret && GetLastError() == ERROR_NOT_ENOUGH_MEMORY, 
210        "ret=%d error=%d\n", ret, GetLastError());
211         
212     dpa = pDPA_Create(0);
213     ok(dpa != NULL, "\n");
214
215     /* Set item with out of bound index */
216     ok(pDPA_SetPtr(dpa, 1, (PVOID)6), "\n");
217     /* Fill the created gap */
218     ok(pDPA_SetPtr(dpa, 0, (PVOID)5), "\n");
219     rc=CheckDPA(dpa, 0x56, &dw);
220     ok(rc, "dw=0x%x\n", dw);
221     
222     /* Prepend item */
223     ret = pDPA_InsertPtr(dpa, 1, (PVOID)1);
224     ok(ret == 1, "ret=%d\n", ret);
225     /* Append item using correct index */
226     ret = pDPA_InsertPtr(dpa, 3, (PVOID)3);
227     ok(ret == 3, "ret=%d\n", ret);
228     /* Append item using out of bound index */
229     ret = pDPA_InsertPtr(dpa, 5, (PVOID)2);
230     ok(ret == 4, "ret=%d\n", ret);
231     /* Append item using DPA_APPEND */ 
232     ret = pDPA_InsertPtr(dpa, DPA_APPEND, (PVOID)4);
233     ok(ret == 5, "ret=%d\n", ret);
234
235     rc=CheckDPA(dpa, 0x516324, &dw);
236     ok(rc, "dw=0x%x\n", dw);
237
238     for(i = 1; i <= 6; i++)
239     {
240         INT j, k;
241         k = pDPA_GetPtrIndex(dpa, (PVOID)(INT_PTR)i);
242         /* Linear searches should work on unsorted DPAs */
243         j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, 0, CB_CmpLT, 0xdeadbeef, 0);
244         ok(j == k, "j=%d k=%d\n", j, k);
245     }
246
247     /* Sort DPA */
248     ok(pDPA_Sort(dpa, CB_CmpGT, 0xdeadbeef), "\n");
249     rc=CheckDPA(dpa, 0x654321, &dw);
250     ok(rc, "dw=0x%x\n", dw);
251     
252     /* Clone into a new DPA */
253     dpa2 = pDPA_Clone(dpa, NULL);
254     ok(dpa2 != NULL, "\n");
255     /* The old data should have been preserved */
256     rc=CheckDPA(dpa2, 0x654321, &dw2);
257     ok(rc, "dw=0x%x\n", dw2);
258     ok(pDPA_Sort(dpa, CB_CmpLT, 0xdeadbeef), "\n");
259     
260     /* Test if the DPA itself was really copied */
261     rc=CheckDPA(dpa,  0x123456, &dw);
262     ok(rc, "dw=0x%x\n",  dw );
263     rc=CheckDPA(dpa2, 0x654321, &dw2);
264     ok(rc, "dw2=0x%x\n", dw2);
265
266     /* Clone into an old DPA */
267     p = NULL; SetLastError(ERROR_SUCCESS);
268     p = pDPA_Clone(dpa, dpa3);
269     ok(p == dpa3, "p=%p\n", p);
270     rc=CheckDPA(dpa3, 0x123456, &dw3);
271     ok(rc, "dw3=0x%x\n", dw3);
272
273     for(i = 1; i <= 6; i++)
274     {
275         INT j;
276
277         /* The array is in order so ptr == index+1 */
278         j = pDPA_GetPtrIndex(dpa, (PVOID)(INT_PTR)i);
279         ok(j+1 == i, "j=%d i=%d\n", j, i);
280         j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, 0, CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
281         ok(j+1 == i, "j=%d i=%d\n", j, i);
282
283         /* Linear searches respect iStart ... */
284         j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, i+1, CB_CmpLT, 0xdeadbeef, 0);
285         ok(j == DPA_ERR, "j=%d\n", j);
286         /* ... but for a binary search it's ignored */
287         j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, i+1, CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
288         ok(j+1 == i, "j=%d i=%d\n", j, i);
289     }
290
291     /* Try to get the index of a nonexistent item */
292     i = pDPA_GetPtrIndex(dpa, (PVOID)7);
293     ok(i == DPA_ERR, "i=%d\n", i);
294     
295     /* Try to delete out of bound indexes */
296     p = pDPA_DeletePtr(dpa, -1);
297     ok(p == NULL, "p=%p\n", p);
298     p = pDPA_DeletePtr(dpa, 6);
299     ok(p == NULL, "p=%p\n", p);
300
301     /* Delete the third item */
302     p = pDPA_DeletePtr(dpa, 2);
303     ok(p == (PVOID)3, "p=%p\n", p);
304     rc=CheckDPA(dpa, 0x12456, &dw);
305     ok(rc, "dw=0x%x\n", dw);
306
307     /* Check where to re-insert the deleted item */
308     i = pDPA_Search(dpa, (PVOID)3, 0, 
309                     CB_CmpLT, 0xdeadbeef, DPAS_SORTED|DPAS_INSERTAFTER);
310     ok(i == 2, "i=%d\n", i);
311     /* DPAS_INSERTBEFORE works just like DPAS_INSERTAFTER */
312     i = pDPA_Search(dpa, (PVOID)3, 0,
313                     CB_CmpLT, 0xdeadbeef, DPAS_SORTED|DPAS_INSERTBEFORE);
314     ok(i == 2, "i=%d\n", i);
315     /* without DPAS_INSERTBEFORE/AFTER */
316     i = pDPA_Search(dpa, (PVOID)3, 0,
317                     CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
318     ok(i == -1, "i=%d\n", i);
319
320     /* Re-insert the item */
321     ret = pDPA_InsertPtr(dpa, 2, (PVOID)3);
322     ok(ret == 2, "ret=%d i=%d\n", ret, 2);
323     rc=CheckDPA(dpa, 0x123456, &dw);
324     ok(rc, "dw=0x%x\n", dw);
325     
326     /* When doing a binary search while claiming reverse order all indexes
327      * should be bogus */
328     for(i = 0; i < 6; i++)
329     {
330         INT j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, 0, CB_CmpGT, 0xdeadbeef,
331                             DPAS_SORTED|DPAS_INSERTBEFORE);
332         ok(j != i, "i=%d\n", i);
333     }
334
335     /* Setting item with huge index should work */
336     ok(pDPA_SetPtr(dpa2, 0x12345, (PVOID)0xdeadbeef), "\n");
337     ret = pDPA_GetPtrIndex(dpa2, (PVOID)0xdeadbeef);
338     ok(ret == 0x12345, "ret=%d\n", ret);
339           
340     pDPA_DeleteAllPtrs(dpa2);
341     rc=CheckDPA(dpa2, 0, &dw2);
342     ok(rc, "dw2=0x%x\n", dw2);
343     pDPA_Destroy(dpa2);
344     pDPA_Destroy(dpa3);
345 }
346
347 static void test_DPA_Merge(void)
348 {
349     HDPA dpa, dpa2, dpa3;
350     INT ret, i;
351     DWORD dw;
352     BOOL rc;
353
354     if(!pDPA_Merge)
355     {
356         win_skip("DPA_Merge() not available\n");
357         return;
358     }
359
360     dpa  = pDPA_Create(0);
361     dpa2 = pDPA_Create(0);
362     dpa3 = pDPA_Create(0);
363
364     ret = pDPA_InsertPtr(dpa, 0, (PVOID)1);
365     ok(ret == 0, "ret=%d\n", ret);
366     ret = pDPA_InsertPtr(dpa, 1, (PVOID)3);
367     ok(ret == 1, "ret=%d\n", ret);
368     ret = pDPA_InsertPtr(dpa, 2, (PVOID)5);
369     ok(ret == 2, "ret=%d\n", ret);
370
371     rc = CheckDPA(dpa, 0x135, &dw);
372     ok(rc, "dw=0x%x\n", dw);
373
374     for (i = 0; i < 6; i++)
375     {
376         ret = pDPA_InsertPtr(dpa2, i, (PVOID)(6-i));
377         ok(ret == i, "ret=%d\n", ret);
378         ret = pDPA_InsertPtr(dpa3, i, (PVOID)(i+1));
379         ok(ret == i, "ret=%d\n", ret);
380     }
381
382     rc = CheckDPA(dpa2, 0x654321, &dw);
383     ok(rc, "dw=0x%x\n", dw);
384     rc = CheckDPA(dpa3, 0x123456, &dw);
385     ok(rc, "dw=0x%x\n", dw);
386
387     /* Delete all odd entries from dpa2 */
388     memset(nMessages, 0, sizeof(nMessages));
389     pDPA_Merge(dpa2, dpa, DPAM_INTERSECT,
390                CB_CmpLT, CB_MergeDeleteOddSrc, 0xdeadbeef);
391     rc = CheckDPA(dpa2, 0x246, &dw);
392     ok(rc, "dw=0x%x\n", dw);
393
394     expect(3, nMessages[DPAMM_MERGE]);
395     expect(3, nMessages[DPAMM_DELETE]);
396     expect(0, nMessages[DPAMM_INSERT]);
397
398     for (i = 0; i < 6; i++)
399     {
400         ret = pDPA_InsertPtr(dpa2, i, (PVOID)(6-i));
401         ok(ret == i, "ret=%d\n", ret);
402     }
403
404     /* DPAM_INTERSECT - returning source while merging */
405     memset(nMessages, 0, sizeof(nMessages));
406     pDPA_Merge(dpa2, dpa, DPAM_INTERSECT,
407                CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
408     rc = CheckDPA(dpa2, 0x135, &dw);
409     ok(rc, "dw=0x%x\n", dw);
410
411     expect(3, nMessages[DPAMM_MERGE]);
412     expect(6, nMessages[DPAMM_DELETE]);
413     expect(0, nMessages[DPAMM_INSERT]);
414
415     /* DPAM_UNION */
416     pDPA_DeleteAllPtrs(dpa);
417     pDPA_InsertPtr(dpa, 0, (PVOID)1);
418     pDPA_InsertPtr(dpa, 1, (PVOID)3);
419     pDPA_InsertPtr(dpa, 2, (PVOID)5);
420     pDPA_DeleteAllPtrs(dpa2);
421     pDPA_InsertPtr(dpa2, 0, (PVOID)2);
422     pDPA_InsertPtr(dpa2, 1, (PVOID)4);
423     pDPA_InsertPtr(dpa2, 2, (PVOID)6);
424
425     memset(nMessages, 0, sizeof(nMessages));
426     pDPA_Merge(dpa2, dpa, DPAM_UNION,
427                CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
428     rc = CheckDPA(dpa2, 0x123456, &dw);
429     ok(rc, "dw=0x%x\n", dw);
430
431     expect(0, nMessages[DPAMM_MERGE]);
432     expect(0, nMessages[DPAMM_DELETE]);
433     expect(3, nMessages[DPAMM_INSERT]);
434
435     /* Merge dpa3 into dpa2 and dpa */
436     memset(nMessages, 0, sizeof(nMessages));
437     pDPA_Merge(dpa, dpa3, DPAM_UNION|DPAM_SORTED,
438                CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
439     expect(3, nMessages[DPAMM_MERGE]);
440     expect(0, nMessages[DPAMM_DELETE]);
441     expect(3, nMessages[DPAMM_INSERT]);
442
443
444     pDPA_DeleteAllPtrs(dpa2);
445     pDPA_InsertPtr(dpa2, 0, (PVOID)2);
446     pDPA_InsertPtr(dpa2, 1, (PVOID)4);
447     pDPA_InsertPtr(dpa2, 2, (PVOID)6);
448
449     memset(nMessages, 0, sizeof(nMessages));
450     pDPA_Merge(dpa2, dpa3, DPAM_UNION|DPAM_SORTED,
451                CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
452     expect(3, nMessages[DPAMM_MERGE]);
453     expect(0, nMessages[DPAMM_DELETE]);
454     expect(3, nMessages[DPAMM_INSERT]);
455
456     rc = CheckDPA(dpa,  0x123456, &dw);
457     ok(rc, "dw=0x%x\n",  dw);
458     rc = CheckDPA(dpa2, 0x123456, &dw);
459     ok(rc ||
460        broken(!rc), /* win98 */
461        "dw=0x%x\n", dw);
462     rc = CheckDPA(dpa3, 0x123456, &dw);
463     ok(rc, "dw=0x%x\n", dw);
464
465     pDPA_Destroy(dpa);
466     pDPA_Destroy(dpa2);
467     pDPA_Destroy(dpa3);
468 }
469
470 static void test_DPA_EnumCallback(void)
471 {
472     HDPA dpa;
473     BOOL rc;
474     DWORD dw;
475     INT i, ret;
476
477     if(!pDPA_EnumCallback)
478     {
479         win_skip("DPA_EnumCallback() not available\n");
480         return;
481     }
482
483     dpa = pDPA_Create(0);
484
485     for (i = 0; i < 6; i++)
486     {
487         ret = pDPA_InsertPtr(dpa, i, (PVOID)(i+1));
488         ok(ret == i, "ret=%d\n", ret);
489     }
490
491     rc = CheckDPA(dpa, 0x123456, &dw);
492     ok(rc, "dw=0x%x\n", dw);
493
494     nEnum = 0;
495     /* test callback sets first 3 items to 7 */
496     pDPA_EnumCallback(dpa, CB_EnumFirstThree, dpa);
497     rc = CheckDPA(dpa, 0x777456, &dw);
498     ok(rc, "dw=0x%x\n", dw);
499     ok(nEnum == 3, "nEnum=%d\n", nEnum);
500
501     pDPA_Destroy(dpa);
502 }
503
504 static void test_DPA_DestroyCallback(void)
505 {
506     HDPA dpa;
507     INT i, ret;
508
509     if(!pDPA_DestroyCallback)
510     {
511         win_skip("DPA_DestroyCallback() not available\n");
512         return;
513     }
514
515     dpa = pDPA_Create(0);
516
517     for (i = 0; i < 3; i++)
518     {
519         ret = pDPA_InsertPtr(dpa, i, (PVOID)(i+1));
520         ok(ret == i, "ret=%d\n", ret);
521     }
522
523     nEnum = 0;
524     pDPA_DestroyCallback(dpa, CB_EnumFirstThree, dpa);
525     ok(nEnum == 3, "nEnum=%d\n", nEnum);
526 }
527
528 static void test_dpa_stream(void)
529 {
530     HDPA dpa;
531     HRESULT hRes;
532     INT ret, i;
533     BOOL rc;
534
535     static const WCHAR szStg[] = { 'S','t','g',0 };
536     IStorage* pStg = NULL;
537     IStream* pStm = NULL;
538     LARGE_INTEGER liZero;
539     DWORD dwMode, dw;
540
541     if(!pDPA_SaveStream)
542     {
543         win_skip("DPA_SaveStream() not available. Skipping stream tests.\n");
544         return;
545     }
546
547     hRes = CoInitialize(NULL);
548     if (hRes != S_OK)
549     {
550         ok(0, "hResult: %d\n", hRes);
551         return;
552     }
553
554     dpa = pDPA_Create(0);
555
556     for (i = 0; i < 6; i++)
557     {
558         ret = pDPA_InsertPtr(dpa, i, (PVOID)(i+1));
559         ok(ret == i, "ret=%d\n", ret);
560     }
561
562     dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
563     hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
564     ok(hRes == S_OK, "hRes=0x%x\n", hRes);
565
566     hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
567     ok(hRes == S_OK, "hRes=0x%x\n", hRes);
568
569     hRes = pDPA_SaveStream(dpa, CB_Save, pStm, 0xdeadbeef);
570     todo_wine ok(hRes == S_OK, "hRes=0x%x\n", hRes);
571     pDPA_Destroy(dpa);
572
573     liZero.QuadPart = 0;
574     hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL);
575     ok(hRes == S_OK, "hRes=0x%x\n", hRes);
576     hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, 0xdeadbeef);
577     todo_wine
578     {
579         ok(hRes == S_OK, "hRes=0x%x\n", hRes);
580         rc = CheckDPA(dpa, 0x123456, &dw);
581         ok(rc, "dw=0x%x\n", dw);
582     }
583     pDPA_Destroy(dpa);
584
585     ret = IStream_Release(pStm);
586     ok(!ret, "ret=%d\n", ret);
587
588     ret = IStorage_Release(pStg);
589     ok(!ret, "ret=%d\n", ret);
590
591     CoUninitialize();
592 }
593
594 START_TEST(dpa)
595 {
596     HMODULE hcomctl32;
597
598     hcomctl32 = GetModuleHandleA("comctl32.dll");
599
600     if(!InitFunctionPtrs(hcomctl32))
601     {
602         win_skip("Needed functions are not available\n");
603         return;
604     }
605
606     test_dpa();
607     test_DPA_Merge();
608     test_DPA_EnumCallback();
609     test_DPA_DestroyCallback();
610     test_dpa_stream();
611 }