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