mapi32/tests: Use win_skip() to skip over unimplemented functionality.
[wine] / dlls / mapi32 / tests / prop.c
1 /*
2  * Unit test suite for MAPI property functions
3  *
4  * Copyright 2004 Jon Griffiths
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "wine/test.h"
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winuser.h"
25 #include "winerror.h"
26 #include "winnt.h"
27 #include "initguid.h"
28 #include "mapiutil.h"
29 #include "mapitags.h"
30
31 static HMODULE hMapi32 = 0;
32
33 static SCODE        (WINAPI *pScInitMapiUtil)(ULONG);
34 static SCODE        (WINAPI *pPropCopyMore)(LPSPropValue,LPSPropValue,ALLOCATEMORE*,LPVOID);
35 static ULONG        (WINAPI *pUlPropSize)(LPSPropValue);
36 static BOOL         (WINAPI *pFPropContainsProp)(LPSPropValue,LPSPropValue,ULONG);
37 static BOOL         (WINAPI *pFPropCompareProp)(LPSPropValue,ULONG,LPSPropValue);
38 static LONG         (WINAPI *pLPropCompareProp)(LPSPropValue,LPSPropValue);
39 static LPSPropValue (WINAPI *pPpropFindProp)(LPSPropValue,ULONG,ULONG);
40 static SCODE        (WINAPI *pScCountProps)(INT,LPSPropValue,ULONG*);
41 static SCODE        (WINAPI *pScCopyProps)(int,LPSPropValue,LPVOID,ULONG*);
42 static SCODE        (WINAPI *pScRelocProps)(int,LPSPropValue,LPVOID,LPVOID,ULONG*);
43 static LPSPropValue (WINAPI *pLpValFindProp)(ULONG,ULONG,LPSPropValue);
44 static BOOL         (WINAPI *pFBadRglpszA)(LPSTR*,ULONG);
45 static BOOL         (WINAPI *pFBadRglpszW)(LPWSTR*,ULONG);
46 static BOOL         (WINAPI *pFBadRowSet)(LPSRowSet);
47 static ULONG        (WINAPI *pFBadPropTag)(ULONG);
48 static ULONG        (WINAPI *pFBadRow)(LPSRow);
49 static ULONG        (WINAPI *pFBadProp)(LPSPropValue);
50 static ULONG        (WINAPI *pFBadColumnSet)(LPSPropTagArray);
51 static SCODE        (WINAPI *pCreateIProp)(LPCIID,ALLOCATEBUFFER*,ALLOCATEMORE*,
52                                            FREEBUFFER*,LPVOID,LPPROPDATA*);
53 static SCODE        (WINAPI *pMAPIAllocateBuffer)(ULONG, LPVOID);
54 static SCODE        (WINAPI *pMAPIAllocateMore)(ULONG, LPVOID, LPVOID);
55 static SCODE        (WINAPI *pMAPIFreeBuffer)(LPVOID);
56
57 static BOOL InitFuncPtrs(void)
58 {
59     hMapi32 = LoadLibraryA("mapi32.dll");
60
61     pScInitMapiUtil = (void*)GetProcAddress(hMapi32, "ScInitMapiUtil@4");
62     pMAPIAllocateBuffer = (void*)GetProcAddress(hMapi32, "MAPIAllocateBuffer");
63     pMAPIAllocateMore = (void*)GetProcAddress(hMapi32, "MAPIAllocateMore");
64     pMAPIFreeBuffer = (void*)GetProcAddress(hMapi32, "MAPIFreeBuffer");
65     if(pScInitMapiUtil && pMAPIAllocateBuffer && pMAPIAllocateMore && pMAPIFreeBuffer)
66         return TRUE;
67     else
68         return FALSE;
69 }
70
71 static ULONG ptTypes[] = {
72     PT_I2, PT_I4, PT_R4, PT_R8, PT_CURRENCY, PT_APPTIME, PT_SYSTIME,
73     PT_ERROR, PT_BOOLEAN, PT_I8, PT_CLSID, PT_STRING8, PT_BINARY,
74     PT_UNICODE
75 };
76
77 static inline int strcmpW(const WCHAR *str1, const WCHAR *str2)
78 {
79     while (*str1 && (*str1 == *str2)) { str1++; str2++; }
80     return *str1 - *str2;
81 }
82
83 static void test_PropCopyMore(void)
84 {
85     static char szHiA[] = "Hi!";
86     static WCHAR szHiW[] = { 'H', 'i', '!', '\0' };
87     SPropValue *lpDest = NULL, *lpSrc = NULL;
88     ULONG i;
89     SCODE scode;
90
91     pPropCopyMore = (void*)GetProcAddress(hMapi32, "PropCopyMore@16");
92
93     if (!pPropCopyMore)
94         return;
95
96     scode = pMAPIAllocateBuffer(sizeof(LPSPropValue), lpDest);
97     if (FAILED(scode))
98         return;
99
100     scode = pMAPIAllocateMore(sizeof(LPSPropValue), lpDest, lpSrc);
101     if (FAILED(scode))
102         return;
103
104     for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
105     {
106         lpSrc->ulPropTag = ptTypes[i];
107
108         switch (ptTypes[i])
109         {
110         case PT_STRING8:
111             lpSrc->Value.lpszA = szHiA;
112             break;
113         case PT_UNICODE:
114             lpSrc->Value.lpszW = szHiW;
115             break;
116         case PT_BINARY:
117             lpSrc->Value.bin.cb = 4;
118             lpSrc->Value.bin.lpb = (LPBYTE)szHiA;
119             break;
120         }
121
122         memset(lpDest, 0xff, sizeof(SPropValue));
123
124         scode = pPropCopyMore(lpDest, lpSrc, (ALLOCATEMORE*)pMAPIAllocateMore, lpDest);
125         ok(!scode && lpDest->ulPropTag == lpSrc->ulPropTag,
126            "PropCopyMore: Expected 0x0,%d, got 0x%08x,%d\n",
127            lpSrc->ulPropTag, scode, lpDest->ulPropTag);
128         if (SUCCEEDED(scode))
129         {
130             switch (ptTypes[i])
131             {
132             case PT_STRING8:
133                 ok(lstrcmpA(lpDest->Value.lpszA, lpSrc->Value.lpszA) == 0,
134                    "PropCopyMore: Ascii string differs\n");
135                 break;
136             case PT_UNICODE:
137                 ok(strcmpW(lpDest->Value.lpszW, lpSrc->Value.lpszW) == 0,
138                    "PropCopyMore: Unicode string differs\n");
139                 break;
140             case PT_BINARY:
141                 ok(lpDest->Value.bin.cb == 4 &&
142                    !memcmp(lpSrc->Value.bin.lpb, lpDest->Value.bin.lpb, 4),
143                    "PropCopyMore: Binary array  differs\n");
144                 break;
145             }
146         }
147     }
148
149     /* Since all allocations are linked, freeing lpDest frees everything */
150     pMAPIFreeBuffer(lpDest);
151 }
152
153 static void test_UlPropSize(void)
154 {
155     static char szHiA[] = "Hi!";
156     static WCHAR szHiW[] = { 'H', 'i', '!', '\0' };
157     LPSTR  buffa[2];
158     LPWSTR buffw[2];
159     SBinary buffbin[2];
160     ULONG pt, exp, res;
161
162     pUlPropSize = (void*)GetProcAddress(hMapi32, "UlPropSize@4");
163
164     if (!pUlPropSize)
165         return;
166
167     for (pt = 0; pt < PROP_ID_INVALID; pt++)
168     {
169         SPropValue pv;
170
171         memset(&pv, 0 ,sizeof(pv));
172         pv.ulPropTag = pt;
173
174         exp = 1u; /* Default to one item for non-MV properties */
175
176         switch (PROP_TYPE(pt))
177         {
178         case PT_MV_I2:       pv.Value.MVi.cValues = exp = 2;
179         case PT_I2:          exp *= sizeof(USHORT); break;
180         case PT_MV_I4:       pv.Value.MVl.cValues = exp = 2;
181         case PT_I4:          exp *= sizeof(LONG); break;
182         case PT_MV_R4:       pv.Value.MVflt.cValues = exp = 2;
183         case PT_R4:          exp *= sizeof(float); break;
184         case PT_MV_DOUBLE:   pv.Value.MVdbl.cValues = exp = 2;
185         case PT_R8:          exp *= sizeof(double); break;
186         case PT_MV_CURRENCY: pv.Value.MVcur.cValues = exp = 2;
187         case PT_CURRENCY:    exp *= sizeof(CY); break;
188         case PT_MV_APPTIME:  pv.Value.MVat.cValues = exp = 2;
189         case PT_APPTIME:     exp *= sizeof(double); break;
190         case PT_MV_SYSTIME:  pv.Value.MVft.cValues = exp = 2;
191         case PT_SYSTIME:     exp *= sizeof(FILETIME); break;
192         case PT_ERROR:       exp = sizeof(SCODE); break;
193         case PT_BOOLEAN:     exp = sizeof(USHORT); break;
194         case PT_OBJECT:      exp = 0; break;
195         case PT_MV_I8:       pv.Value.MVli.cValues = exp = 2;
196         case PT_I8:          exp *= sizeof(LONG64); break;
197 #if 0
198         /* My version of native mapi returns 0 for PT_MV_CLSID even if a valid
199          * array is given. This _has_ to be a bug, so Wine does
200          * the right thing(tm) and we don't test it here.
201          */
202         case PT_MV_CLSID:    pv.Value.MVguid.cValues = exp = 2;
203 #endif
204         case PT_CLSID:       exp *= sizeof(GUID); break;
205         case PT_STRING8:
206             pv.Value.lpszA = szHiA;
207             exp = 4;
208             break;
209         case PT_UNICODE:
210             pv.Value.lpszW = szHiW;
211             exp = 4 * sizeof(WCHAR);
212             break;
213         case PT_BINARY:
214             pv.Value.bin.cb = exp = 19;
215             break;
216         case PT_MV_STRING8:
217             pv.Value.MVszA.cValues = 2;
218             pv.Value.MVszA.lppszA = buffa;
219             buffa[0] = szHiA;
220             buffa[1] = szHiA;
221             exp = 8;
222             break;
223         case PT_MV_UNICODE:
224             pv.Value.MVszW.cValues = 2;
225             pv.Value.MVszW.lppszW = buffw;
226             buffw[0] = szHiW;
227             buffw[1] = szHiW;
228             exp = 8 * sizeof(WCHAR);
229             break;
230         case PT_MV_BINARY:
231             pv.Value.MVbin.cValues = 2;
232             pv.Value.MVbin.lpbin = buffbin;
233             buffbin[0].cb = 19;
234             buffbin[1].cb = 1;
235             exp = 20;
236             break;
237         default:
238             exp = 0;
239         }
240
241         res = pUlPropSize(&pv);
242         ok(res == exp || broken(!res) /* Win9x */,
243            "pt= %d: Expected %d, got %d\n", pt, exp, res);
244     }
245 }
246
247 static void test_FPropContainsProp(void)
248 {
249     static char szFull[] = "Full String";
250     static char szFullLower[] = "full string";
251     static char szPrefix[] = "Full";
252     static char szPrefixLower[] = "full";
253     static char szSubstring[] = "ll St";
254     static char szSubstringLower[] = "ll st";
255     SPropValue pvLeft, pvRight;
256     ULONG pt;
257     BOOL bRet;
258
259     pFPropContainsProp = (void*)GetProcAddress(hMapi32, "FPropContainsProp@12");
260
261     if (!pFPropContainsProp)
262         return;
263
264     /* Ensure that only PT_STRING8 and PT_BINARY are handled */
265     for (pt = 0; pt < PROP_ID_INVALID; pt++)
266     {
267         if (pt == PT_STRING8 || pt == PT_BINARY)
268             continue; /* test these later */
269
270         memset(&pvLeft, 0 ,sizeof(pvLeft));
271         memset(&pvRight, 0 ,sizeof(pvRight));
272         pvLeft.ulPropTag = pvRight.ulPropTag = pt;
273
274         bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
275         ok(bRet == FALSE, "pt= %d: Expected FALSE, got %d\n", pt, bRet);
276     }
277
278     /* test the various flag combinations */
279     pvLeft.ulPropTag = pvRight.ulPropTag = PT_STRING8;
280     pvLeft.Value.lpszA = szFull;
281     pvRight.Value.lpszA = szFull;
282
283     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
284     ok(bRet == TRUE || broken(!bRet) /* Win9x */, "(full,full)[] match failed\n");
285     pvRight.Value.lpszA = szPrefix;
286     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
287     ok(bRet == FALSE, "(full,prefix)[] match failed\n");
288     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
289     ok(bRet == TRUE, "(full,prefix)[PREFIX] match failed\n");
290     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
291     ok(bRet == TRUE || broken(!bRet) /* Win9x */, "(full,prefix)[SUBSTRING] match failed\n");
292     pvRight.Value.lpszA = szPrefixLower;
293     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
294     ok(bRet == FALSE, "(full,prefixlow)[PREFIX] match failed\n");
295     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
296     ok(bRet == FALSE, "(full,prefixlow)[SUBSTRING] match failed\n");
297     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE);
298     ok(bRet == TRUE || broken(!bRet) /* Win9x */, "(full,prefixlow)[PREFIX|IGNORECASE] match failed\n");
299     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE);
300     ok(bRet == TRUE || broken(!bRet) /* Win9x */, "(full,prefixlow)[SUBSTRING|IGNORECASE] match failed\n");
301     pvRight.Value.lpszA = szSubstring;
302     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
303     ok(bRet == FALSE, "(full,substr)[] match failed\n");
304     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
305     ok(bRet == FALSE, "(full,substr)[PREFIX] match failed\n");
306     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
307     ok(bRet == TRUE || broken(!bRet) /* Win9x */, "(full,substr)[SUBSTRING] match failed\n");
308     pvRight.Value.lpszA = szSubstringLower;
309     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
310     ok(bRet == FALSE, "(full,substrlow)[PREFIX] match failed\n");
311     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
312     ok(bRet == FALSE, "(full,substrlow)[SUBSTRING] match failed\n");
313     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE);
314     ok(bRet == FALSE, "(full,substrlow)[PREFIX|IGNORECASE] match failed\n");
315     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE);
316     ok(bRet == TRUE || broken(!bRet) /* Win9x */, "(full,substrlow)[SUBSTRING|IGNORECASE] match failed\n");
317     pvRight.Value.lpszA = szFullLower;
318     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING|FL_IGNORECASE);
319     ok(bRet == TRUE || broken(!bRet) /* Win9x */, "(full,fulllow)[IGNORECASE] match failed\n");
320
321     pvLeft.ulPropTag = pvRight.ulPropTag = PT_BINARY;
322     pvLeft.Value.bin.lpb = (LPBYTE)szFull;
323     pvRight.Value.bin.lpb = (LPBYTE)szFull;
324     pvLeft.Value.bin.cb = pvRight.Value.bin.cb = strlen(szFull);
325
326     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
327     ok(bRet == TRUE || broken(!bRet) /* Win9x */, "bin(full,full)[] match failed\n");
328     pvRight.Value.bin.lpb = (LPBYTE)szPrefix;
329     pvRight.Value.bin.cb = strlen(szPrefix);
330     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
331     ok(bRet == FALSE, "bin(full,prefix)[] match failed\n");
332     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
333     ok(bRet == TRUE || broken(!bRet) /* Win9x */, "bin(full,prefix)[PREFIX] match failed\n");
334     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
335     ok(bRet == TRUE || broken(!bRet) /* Win9x */, "bin(full,prefix)[SUBSTRING] match failed\n");
336     pvRight.Value.bin.lpb = (LPBYTE)szPrefixLower;
337     pvRight.Value.bin.cb = strlen(szPrefixLower);
338     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
339     ok(bRet == FALSE, "bin(full,prefixlow)[PREFIX] match failed\n");
340     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
341     ok(bRet == FALSE, "bin(full,prefixlow)[SUBSTRING] match failed\n");
342     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE);
343     ok(bRet == FALSE, "bin(full,prefixlow)[PREFIX|IGNORECASE] match failed\n");
344     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE);
345     ok(bRet == FALSE, "bin(full,prefixlow)[SUBSTRING|IGNORECASE] match failed\n");
346     pvRight.Value.bin.lpb = (LPBYTE)szSubstring;
347     pvRight.Value.bin.cb = strlen(szSubstring);
348     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
349     ok(bRet == FALSE, "bin(full,substr)[] match failed\n");
350     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
351     ok(bRet == FALSE, "bin(full,substr)[PREFIX] match failed\n");
352     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
353     ok(bRet == TRUE || broken(!bRet) /* Win9x */, "bin(full,substr)[SUBSTRING] match failed\n");
354     pvRight.Value.bin.lpb = (LPBYTE)szSubstringLower;
355     pvRight.Value.bin.cb = strlen(szSubstringLower);
356     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
357     ok(bRet == FALSE, "bin(full,substrlow)[PREFIX] match failed\n");
358     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
359     ok(bRet == FALSE, "bin(full,substrlow)[SUBSTRING] match failed\n");
360     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE);
361     ok(bRet == FALSE, "bin(full,substrlow)[PREFIX|IGNORECASE] match failed\n");
362     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE);
363     ok(bRet == FALSE, "bin(full,substrlow)[SUBSTRING|IGNORECASE] match failed\n");
364     pvRight.Value.bin.lpb = (LPBYTE)szFullLower;
365     pvRight.Value.bin.cb = strlen(szFullLower);
366     bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING|FL_IGNORECASE);
367     ok(bRet == FALSE, "bin(full,fulllow)[IGNORECASE] match failed\n");
368 }
369
370 typedef struct tagFPropCompareProp_Result
371 {
372     SHORT lVal;
373     SHORT rVal;
374     ULONG relOp;
375     BOOL  bRet;
376 } FPropCompareProp_Result;
377
378 static const FPropCompareProp_Result FPCProp_Results[] =
379 {
380     { 1, 2, RELOP_LT, TRUE },
381     { 1, 1, RELOP_LT, FALSE },
382     { 2, 1, RELOP_LT, FALSE },
383     { 1, 2, RELOP_LE, TRUE },
384     { 1, 1, RELOP_LE, TRUE },
385     { 2, 1, RELOP_LE, FALSE },
386     { 1, 2, RELOP_GT, FALSE },
387     { 1, 1, RELOP_GT, FALSE },
388     { 2, 1, RELOP_GT, TRUE },
389     { 1, 2, RELOP_GE, FALSE },
390     { 1, 1, RELOP_GE, TRUE },
391     { 2, 1, RELOP_GE, TRUE },
392     { 1, 2, RELOP_EQ, FALSE },
393     { 1, 1, RELOP_EQ, TRUE },
394     { 2, 1, RELOP_EQ, FALSE }
395 };
396
397 static const char *relops[] = { "RELOP_LT", "RELOP_LE", "RELOP_GT", "RELOP_GE", "RELOP_EQ" };
398
399 static void test_FPropCompareProp(void)
400 {
401     SPropValue pvLeft, pvRight;
402     GUID lguid, rguid;
403     char lbuffa[2], rbuffa[2];
404     WCHAR lbuffw[2], rbuffw[2];
405     ULONG i, j;
406     BOOL bRet, bExp;
407
408     pFPropCompareProp = (void*)GetProcAddress(hMapi32, "FPropCompareProp@12");
409
410     if (!pFPropCompareProp)
411         return;
412
413     lbuffa[1] = '\0';
414     rbuffa[1] = '\0';
415     lbuffw[1] = '\0';
416     rbuffw[1] = '\0';
417
418     for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
419     {
420         pvLeft.ulPropTag = pvRight.ulPropTag = ptTypes[i];
421
422         for (j = 0; j < sizeof(FPCProp_Results)/sizeof(FPCProp_Results[0]); j++)
423         {
424             SHORT lVal = FPCProp_Results[j].lVal;
425             SHORT rVal = FPCProp_Results[j].rVal;
426
427             bExp = FPCProp_Results[j].bRet;
428
429             switch (ptTypes[i])
430             {
431             case PT_BOOLEAN:
432                 /* Boolean values have no concept of less or greater than, only equality */
433                 if ((lVal == 1 && rVal == 2 && FPCProp_Results[j].relOp == RELOP_LT) ||
434                     (lVal == 2 && rVal == 1 && FPCProp_Results[j].relOp == RELOP_LE)||
435                     (lVal == 2 && rVal == 1 && FPCProp_Results[j].relOp == RELOP_GT)||
436                     (lVal == 1 && rVal == 2 && FPCProp_Results[j].relOp == RELOP_GE)||
437                     (lVal == 1 && rVal == 2 && FPCProp_Results[j].relOp == RELOP_EQ)||
438                     (lVal == 2 && rVal == 1 && FPCProp_Results[j].relOp == RELOP_EQ))
439                     bExp = !bExp;
440                     /* Fall through ... */
441             case PT_I2:
442                 pvLeft.Value.i = lVal;
443                 pvRight.Value.i = rVal;
444                 break;
445             case PT_ERROR:
446             case PT_I4:
447                 pvLeft.Value.l = lVal;
448                 pvRight.Value.l = rVal;
449                 break;
450             case PT_R4:
451                 pvLeft.Value.flt = lVal;
452                 pvRight.Value.flt = rVal;
453                 break;
454             case PT_APPTIME:
455             case PT_R8:
456                 pvLeft.Value.dbl = lVal;
457                 pvRight.Value.dbl = rVal;
458                 break;
459             case PT_CURRENCY:
460                 pvLeft.Value.cur.int64 = lVal;
461                 pvRight.Value.cur.int64 = rVal;
462                 break;
463             case PT_SYSTIME:
464                 pvLeft.Value.ft.dwLowDateTime = lVal;
465                 pvLeft.Value.ft.dwHighDateTime = 0;
466                 pvRight.Value.ft.dwLowDateTime = rVal;
467                 pvRight.Value.ft.dwHighDateTime = 0;
468                 break;
469             case PT_I8:
470                 pvLeft.Value.li.u.LowPart = lVal;
471                 pvLeft.Value.li.u.HighPart = 0;
472                 pvRight.Value.li.u.LowPart = rVal;
473                 pvRight.Value.li.u.HighPart = 0;
474                 break;
475             case PT_CLSID:
476                 memset(&lguid, 0, sizeof(GUID));
477                 memset(&rguid, 0, sizeof(GUID));
478                 lguid.Data4[7] = lVal;
479                 rguid.Data4[7] = rVal;
480                 pvLeft.Value.lpguid = &lguid;
481                 pvRight.Value.lpguid = &rguid;
482                 break;
483             case PT_STRING8:
484                 pvLeft.Value.lpszA = lbuffa;
485                 pvRight.Value.lpszA = rbuffa;
486                 lbuffa[0] = '0' + lVal;
487                 rbuffa[0] = '0' + rVal;
488                 break;
489             case PT_UNICODE:
490                 pvLeft.Value.lpszW = lbuffw;
491                 pvRight.Value.lpszW = rbuffw;
492                 lbuffw[0] = '0' + lVal;
493                 rbuffw[0] = '0' + rVal;
494                 break;
495             case PT_BINARY:
496                 pvLeft.Value.bin.cb = 1;
497                 pvRight.Value.bin.cb = 1;
498                 pvLeft.Value.bin.lpb = (LPBYTE)lbuffa;
499                 pvRight.Value.bin.lpb = (LPBYTE)rbuffa;
500                 lbuffa[0] = lVal;
501                 rbuffa[0] = rVal;
502                 break;
503             }
504
505             bRet = pFPropCompareProp(&pvLeft, FPCProp_Results[j].relOp, &pvRight);
506             ok(bRet == bExp || broken(!bRet) /* Win9x */,
507                "pt %d (%d,%d,%s): expected %d, got %d\n", ptTypes[i],
508                FPCProp_Results[j].lVal, FPCProp_Results[j].rVal,
509                relops[FPCProp_Results[j].relOp], bExp, bRet);
510         }
511     }
512 }
513
514 typedef struct tagLPropCompareProp_Result
515 {
516     SHORT lVal;
517     SHORT rVal;
518     INT   iRet;
519 } LPropCompareProp_Result;
520
521 static const LPropCompareProp_Result LPCProp_Results[] =
522 {
523     { 1, 2, -1 },
524     { 1, 1, 0 },
525     { 2, 1, 1 },
526 };
527
528 static void test_LPropCompareProp(void)
529 {
530     SPropValue pvLeft, pvRight;
531     GUID lguid, rguid;
532     char lbuffa[2], rbuffa[2];
533     WCHAR lbuffw[2], rbuffw[2];
534     ULONG i, j;
535     INT iRet, iExp;
536
537     pLPropCompareProp = (void*)GetProcAddress(hMapi32, "LPropCompareProp@8");
538
539     if (!pLPropCompareProp)
540         return;
541
542     lbuffa[1] = '\0';
543     rbuffa[1] = '\0';
544     lbuffw[1] = '\0';
545     rbuffw[1] = '\0';
546
547     for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
548     {
549         pvLeft.ulPropTag = pvRight.ulPropTag = ptTypes[i];
550
551         for (j = 0; j < sizeof(LPCProp_Results)/sizeof(LPCProp_Results[0]); j++)
552         {
553             SHORT lVal = LPCProp_Results[j].lVal;
554             SHORT rVal = LPCProp_Results[j].rVal;
555
556             iExp = LPCProp_Results[j].iRet;
557
558             switch (ptTypes[i])
559             {
560             case PT_BOOLEAN:
561                 /* Boolean values have no concept of less or greater than, only equality */
562                 if (lVal && rVal)
563                     iExp = 0;
564                     /* Fall through ... */
565             case PT_I2:
566                 pvLeft.Value.i = lVal;
567                 pvRight.Value.i = rVal;
568                 break;
569             case PT_ERROR:
570             case PT_I4:
571                 pvLeft.Value.l = lVal;
572                 pvRight.Value.l = rVal;
573                 break;
574             case PT_R4:
575                 pvLeft.Value.flt = lVal;
576                 pvRight.Value.flt = rVal;
577                 break;
578             case PT_APPTIME:
579             case PT_R8:
580                 pvLeft.Value.dbl = lVal;
581                 pvRight.Value.dbl = rVal;
582                 break;
583             case PT_CURRENCY:
584                 pvLeft.Value.cur.int64 = lVal;
585                 pvRight.Value.cur.int64 = rVal;
586                 break;
587             case PT_SYSTIME:
588                 pvLeft.Value.ft.dwLowDateTime = lVal;
589                 pvLeft.Value.ft.dwHighDateTime = 0;
590                 pvRight.Value.ft.dwLowDateTime = rVal;
591                 pvRight.Value.ft.dwHighDateTime = 0;
592                 break;
593             case PT_I8:
594                 pvLeft.Value.li.u.LowPart = lVal;
595                 pvLeft.Value.li.u.HighPart = 0;
596                 pvRight.Value.li.u.LowPart = rVal;
597                 pvRight.Value.li.u.HighPart = 0;
598                 break;
599             case PT_CLSID:
600                 memset(&lguid, 0, sizeof(GUID));
601                 memset(&rguid, 0, sizeof(GUID));
602                 lguid.Data4[7] = lVal;
603                 rguid.Data4[7] = rVal;
604                 pvLeft.Value.lpguid = &lguid;
605                 pvRight.Value.lpguid = &rguid;
606                 break;
607             case PT_STRING8:
608                 pvLeft.Value.lpszA = lbuffa;
609                 pvRight.Value.lpszA = rbuffa;
610                 lbuffa[0] = '0' + lVal;
611                 rbuffa[0] = '0' + rVal;
612                 break;
613             case PT_UNICODE:
614                 pvLeft.Value.lpszW = lbuffw;
615                 pvRight.Value.lpszW = rbuffw;
616                 lbuffw[0] = '0' + lVal;
617                 rbuffw[0] = '0' + rVal;
618                 break;
619             case PT_BINARY:
620                 pvLeft.Value.bin.cb = 1;
621                 pvRight.Value.bin.cb = 1;
622                 pvLeft.Value.bin.lpb = (LPBYTE)lbuffa;
623                 pvRight.Value.bin.lpb = (LPBYTE)rbuffa;
624                 lbuffa[0] = lVal;
625                 rbuffa[0] = rVal;
626                 break;
627             }
628
629             iRet = pLPropCompareProp(&pvLeft, &pvRight);
630             ok(iRet == iExp || broken(iRet == 0) /* Win9x */,
631                "pt %d (%d,%d): expected %d, got %d\n", ptTypes[i],
632                LPCProp_Results[j].lVal, LPCProp_Results[j].rVal, iExp, iRet);
633         }
634     }
635 }
636
637 static void test_PpropFindProp(void)
638 {
639     SPropValue pvProp, *pRet;
640     ULONG i;
641
642     pPpropFindProp = (void*)GetProcAddress(hMapi32, "PpropFindProp@12");
643
644     if (!pPpropFindProp)
645         return;
646
647     for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
648     {
649         pvProp.ulPropTag = ptTypes[i];
650
651         pRet = pPpropFindProp(&pvProp, 1u, ptTypes[i]);
652         ok(pRet == &pvProp || broken(pRet != &pvProp) /* Win9x */,
653            "PpropFindProp[%d]: Didn't find existing propery\n",
654            ptTypes[i]);
655
656         pRet = pPpropFindProp(&pvProp, 1u, i ? ptTypes[i-1] : ptTypes[i+1]);
657         ok(pRet == NULL, "PpropFindProp[%d]: Found nonexistent propery\n",
658            ptTypes[i]);
659     }
660
661     pvProp.ulPropTag = PROP_TAG(PT_I2, 1u);
662     pRet = pPpropFindProp(&pvProp, 1u, PROP_TAG(PT_UNSPECIFIED, 0u));
663     ok(pRet == NULL, "PpropFindProp[UNSPECIFIED]: Matched on different id\n");
664     pRet = pPpropFindProp(&pvProp, 1u, PROP_TAG(PT_UNSPECIFIED, 1u));
665     ok(pRet == &pvProp, "PpropFindProp[UNSPECIFIED]: Didn't match id\n");
666 }
667
668 static void test_ScCountProps(void)
669 {
670     static char szHiA[] = "Hi!";
671     static WCHAR szHiW[] = { 'H', 'i', '!', '\0' };
672     static const ULONG ULHILEN = 4; /* chars in szHiA/W incl. NUL */
673     LPSTR  buffa[3];
674     LPWSTR buffw[3];
675     SBinary buffbin[3];
676     GUID iids[4], *iid = iids;
677     SCODE res;
678     ULONG pt, exp, ulRet;
679     int success = 1;
680
681     pScCountProps = (void*)GetProcAddress(hMapi32, "ScCountProps@12");
682
683     if (!pScCountProps)
684         return;
685
686     for (pt = 0; pt < PROP_ID_INVALID && success; pt++)
687     {
688         SPropValue pv;
689
690         memset(&pv, 0 ,sizeof(pv));
691         pv.ulPropTag = PROP_TAG(pt, 1u);
692
693         switch (PROP_TYPE(pt))
694         {
695         case PT_I2:
696         case PT_I4:
697         case PT_R4:
698         case PT_R8:
699         case PT_CURRENCY:
700         case PT_APPTIME:
701         case PT_SYSTIME:
702         case PT_ERROR:
703         case PT_BOOLEAN:
704         case PT_OBJECT:
705         case PT_I8:
706             exp = sizeof(pv);
707             break;
708         case PT_CLSID:
709             pv.Value.lpguid = iid;
710             exp = sizeof(GUID) + sizeof(pv);
711             break;
712         case PT_STRING8:
713             pv.Value.lpszA = szHiA;
714             exp = 4 + sizeof(pv);
715             break;
716         case PT_UNICODE:
717             pv.Value.lpszW = szHiW;
718             exp = 4 * sizeof(WCHAR) + sizeof(pv);
719             break;
720         case PT_BINARY:
721             pv.Value.bin.cb = 2;
722             pv.Value.bin.lpb = (LPBYTE)iid;
723             exp = 2 + sizeof(pv);
724             break;
725         case PT_MV_I2:
726             pv.Value.MVi.cValues = 3;
727             pv.Value.MVi.lpi = (SHORT*)iid;
728             exp = 3 * sizeof(SHORT) + sizeof(pv);
729             break;
730         case PT_MV_I4:
731             pv.Value.MVl.cValues = 3;
732             pv.Value.MVl.lpl = (LONG*)iid;
733             exp = 3 * sizeof(LONG) + sizeof(pv);
734             break;
735         case PT_MV_I8:
736             pv.Value.MVli.cValues = 3;
737             pv.Value.MVli.lpli = (LARGE_INTEGER*)iid;
738             exp = 3 * sizeof(LARGE_INTEGER) + sizeof(pv);
739             break;
740         case PT_MV_R4:
741             pv.Value.MVflt.cValues = 3;
742             pv.Value.MVflt.lpflt = (float*)iid;
743             exp = 3 * sizeof(float) + sizeof(pv);
744             break;
745         case PT_MV_APPTIME:
746         case PT_MV_R8:
747             pv.Value.MVdbl.cValues = 3;
748             pv.Value.MVdbl.lpdbl = (double*)iid;
749             exp = 3 * sizeof(double) + sizeof(pv);
750             break;
751         case PT_MV_CURRENCY:
752             pv.Value.MVcur.cValues = 3;
753             pv.Value.MVcur.lpcur = (CY*)iid;
754             exp = 3 * sizeof(CY) + sizeof(pv);
755             break;
756         case PT_MV_SYSTIME:
757             pv.Value.MVft.cValues = 3;
758             pv.Value.MVft.lpft = (FILETIME*)iid;
759             exp = 3 * sizeof(CY) + sizeof(pv);
760             break;
761         case PT_MV_STRING8:
762             pv.Value.MVszA.cValues = 3;
763             pv.Value.MVszA.lppszA = buffa;
764             buffa[0] = szHiA;
765             buffa[1] = szHiA;
766             buffa[2] = szHiA;
767             exp = ULHILEN * 3 + 3 * sizeof(char*) + sizeof(pv);
768             break;
769         case PT_MV_UNICODE:
770             pv.Value.MVszW.cValues = 3;
771             pv.Value.MVszW.lppszW = buffw;
772             buffw[0] = szHiW;
773             buffw[1] = szHiW;
774             buffw[2] = szHiW;
775             exp = ULHILEN * 3 * sizeof(WCHAR) + 3 * sizeof(WCHAR*) + sizeof(pv);
776             break;
777         case PT_MV_BINARY:
778             pv.Value.MVbin.cValues = 3;
779             pv.Value.MVbin.lpbin = buffbin;
780             buffbin[0].cb = 17;
781             buffbin[0].lpb = (LPBYTE)&iid;
782             buffbin[1].cb = 2;
783             buffbin[1].lpb = (LPBYTE)&iid;
784             buffbin[2].cb = 1;
785             buffbin[2].lpb = (LPBYTE)&iid;
786             exp = 20 + sizeof(pv) + sizeof(SBinary) * 3;
787             break;
788         default:
789             exp = 0;
790         }
791
792         ulRet = 0xffffffff;
793         res = pScCountProps(1, &pv, &ulRet);
794         if (!exp) {
795             success = res == MAPI_E_INVALID_PARAMETER && ulRet == 0xffffffff;
796             ok(success, "pt= %d: Expected failure, got %d, ret=0x%08X\n",
797                pt, ulRet, res);
798         }
799         else {
800             success = res == S_OK && ulRet == exp;
801             ok(success, "pt= %d: Expected %d, got %d, ret=0x%08X\n",
802                pt, exp, ulRet, res);
803         }
804     }
805
806 }
807
808 static void test_ScCopyRelocProps(void)
809 {
810     static char szTestA[] = "Test";
811     char buffer[512], buffer2[512], *lppszA[1];
812     SPropValue pvProp, *lpResProp = (LPSPropValue)buffer;
813     ULONG ulCount;
814     SCODE sc;
815
816     pScCopyProps = (void*)GetProcAddress(hMapi32, "ScCopyProps@16");
817     pScRelocProps = (void*)GetProcAddress(hMapi32, "ScRelocProps@20");
818
819     if (!pScCopyProps || !pScRelocProps)
820         return;
821
822     pvProp.ulPropTag = PROP_TAG(PT_MV_STRING8, 1u);
823
824     lppszA[0] = szTestA;
825     pvProp.Value.MVszA.cValues = 1;
826     pvProp.Value.MVszA.lppszA = lppszA;
827     ulCount = 0;
828
829     sc = pScCopyProps(1, &pvProp, buffer, &ulCount);
830     ok(sc == S_OK, "wrong ret %d\n", sc);
831     if(sc == S_OK)
832     {
833         ok(lpResProp->ulPropTag == pvProp.ulPropTag, "wrong tag %x\n",lpResProp->ulPropTag);
834         ok(lpResProp->Value.MVszA.cValues == 1, "wrong cValues %d\n", lpResProp->Value.MVszA.cValues);
835         ok(lpResProp->Value.MVszA.lppszA[0] == buffer + sizeof(SPropValue) + sizeof(char*),
836            "wrong lppszA[0] %p\n",lpResProp->Value.MVszA.lppszA[0]);
837         ok(ulCount == sizeof(SPropValue) + sizeof(char*) + 5, "wrong count %d\n", ulCount);
838         ok(!strcmp(lpResProp->Value.MVszA.lppszA[0], szTestA),
839            "wrong string '%s'\n", lpResProp->Value.MVszA.lppszA[0]);
840     }
841
842     memcpy(buffer2, buffer, sizeof(buffer));
843
844     /* Clear the data in the source buffer. Since pointers in the copied buffer
845      * refer to the source buffer, this proves that native always assumes that
846      * the copied buffers pointers are bad (needing to be relocated first).
847      */
848     memset(buffer, 0, sizeof(buffer));
849     ulCount = 0;
850
851     sc = pScRelocProps(1, (LPSPropValue)buffer2, buffer, buffer2, &ulCount);
852     lpResProp = (LPSPropValue)buffer2;
853
854     ok(sc == S_OK, "wrong ret %d\n", sc);
855     if(sc == S_OK)
856     {
857         ok(lpResProp->ulPropTag == pvProp.ulPropTag, "wrong tag %x\n",lpResProp->ulPropTag);
858         ok(lpResProp->Value.MVszA.cValues == 1, "wrong cValues %d\n", lpResProp->Value.MVszA.cValues);
859         ok(lpResProp->Value.MVszA.lppszA[0] == buffer2 + sizeof(SPropValue) + sizeof(char*),
860            "wrong lppszA[0] %p\n",lpResProp->Value.MVszA.lppszA[0]);
861         /* Native has a bug whereby it calculates the size correctly when copying
862          * but when relocating does not (presumably it uses UlPropSize() which
863          * ignores multivalue pointers). Wine returns the correct value.
864          */
865         ok(ulCount == sizeof(SPropValue) + sizeof(char*) + 5 || ulCount == sizeof(SPropValue) + 5,
866            "wrong count %d\n", ulCount);
867         ok(!strcmp(lpResProp->Value.MVszA.lppszA[0], szTestA),
868            "wrong string '%s'\n", lpResProp->Value.MVszA.lppszA[0]);
869     }
870
871     /* Native crashes with lpNew or lpOld set to NULL so skip testing this */
872 }
873
874 static void test_LpValFindProp(void)
875 {
876     SPropValue pvProp, *pRet;
877     ULONG i;
878
879     pLpValFindProp = (void*)GetProcAddress(hMapi32, "LpValFindProp@12");
880
881     if (!pLpValFindProp)
882         return;
883
884     for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
885     {
886         pvProp.ulPropTag = PROP_TAG(ptTypes[i], 1u);
887
888         pRet = pLpValFindProp(PROP_TAG(ptTypes[i], 1u), 1u, &pvProp);
889         ok(pRet == &pvProp || broken(pRet != &pvProp) /* Win9x */,
890            "LpValFindProp[%d]: Didn't find existing propery id/type\n",
891            ptTypes[i]);
892
893         pRet = pLpValFindProp(PROP_TAG(ptTypes[i], 0u), 1u, &pvProp);
894         ok(pRet == NULL, "LpValFindProp[%d]: Found nonexistent propery id\n",
895            ptTypes[i]);
896
897         pRet = pLpValFindProp(PROP_TAG(PT_NULL, 0u), 1u, &pvProp);
898         ok(pRet == NULL, "LpValFindProp[%d]: Found nonexistent propery id/type\n",
899            ptTypes[i]);
900
901         pRet = pLpValFindProp(PROP_TAG(PT_NULL, 1u), 1u, &pvProp);
902         ok(pRet == &pvProp || broken(pRet != &pvProp) /* Win9x */,
903            "LpValFindProp[%d]: Didn't find existing propery id\n",
904            ptTypes[i]);
905     }
906 }
907
908 static void test_FBadRglpszA(void)
909 {
910     LPSTR lpStrs[4];
911     static CHAR szString[] = "A String";
912     BOOL bRet;
913
914     pFBadRglpszA = (void*)GetProcAddress(hMapi32, "FBadRglpszA@8");
915     if (!pFBadRglpszA)
916         return;
917
918     bRet = pFBadRglpszA(NULL, 10);
919     ok(bRet == TRUE, "FBadRglpszA(Null): expected TRUE, got FALSE\n");
920
921     lpStrs[0] = lpStrs[1] = lpStrs[2] = lpStrs[3] = NULL;
922     bRet = pFBadRglpszA(lpStrs, 4);
923     ok(bRet == TRUE, "FBadRglpszA(Nulls): expected TRUE, got FALSE\n");
924
925     lpStrs[0] = lpStrs[1] = lpStrs[2] = szString;
926     bRet = pFBadRglpszA(lpStrs, 3);
927     ok(bRet == FALSE, "FBadRglpszA(valid): expected FALSE, got TRUE\n");
928
929     bRet = pFBadRglpszA(lpStrs, 4);
930     ok(bRet == TRUE, "FBadRglpszA(1 invalid): expected TRUE, got FALSE\n");
931 }
932
933 static void test_FBadRglpszW(void)
934 {
935     LPWSTR lpStrs[4];
936     static WCHAR szString[] = { 'A',' ','S','t','r','i','n','g','\0' };
937     BOOL bRet;
938
939     pFBadRglpszW = (void*)GetProcAddress(hMapi32, "FBadRglpszW@8");
940     if (!pFBadRglpszW)
941         return;
942
943     bRet = pFBadRglpszW(NULL, 10);
944     ok(bRet == TRUE, "FBadRglpszW(Null): expected TRUE, got FALSE\n");
945
946     lpStrs[0] = lpStrs[1] = lpStrs[2] = lpStrs[3] = NULL;
947     bRet = pFBadRglpszW(lpStrs, 4);
948     ok(bRet == TRUE, "FBadRglpszW(Nulls): expected TRUE, got FALSE\n");
949
950     lpStrs[0] = lpStrs[1] = lpStrs[2] = szString;
951     bRet = pFBadRglpszW(lpStrs, 3);
952     ok(bRet == FALSE, "FBadRglpszW(valid): expected FALSE, got TRUE\n");
953
954     bRet = pFBadRglpszW(lpStrs, 4);
955     ok(bRet == TRUE, "FBadRglpszW(1 invalid): expected TRUE, got FALSE\n");
956 }
957
958 static void test_FBadRowSet(void)
959 {
960     ULONG ulRet;
961
962     pFBadRowSet = (void*)GetProcAddress(hMapi32, "FBadRowSet@4");
963     if (!pFBadRowSet)
964         return;
965
966     ulRet = pFBadRowSet(NULL);
967     ok(ulRet != 0, "FBadRow(null): Expected non-zero, got 0\n");
968
969     /* FIXME */
970 }
971
972 static void test_FBadPropTag(void)
973 {
974     ULONG pt, res;
975
976     pFBadPropTag = (void*)GetProcAddress(hMapi32, "FBadPropTag@4");
977     if (!pFBadPropTag)
978         return;
979
980     for (pt = 0; pt < PROP_ID_INVALID; pt++)
981     {
982         BOOL bBad = TRUE;
983
984         switch (pt & (~MV_FLAG & PROP_TYPE_MASK))
985         {
986         case PT_UNSPECIFIED:
987         case PT_NULL: case PT_I2: case PT_I4: case PT_R4:
988         case PT_R8: case PT_CURRENCY: case PT_APPTIME:
989         case PT_ERROR: case PT_BOOLEAN: case PT_OBJECT:
990         case PT_I8: case PT_STRING8: case PT_UNICODE:
991         case PT_SYSTIME: case PT_CLSID: case PT_BINARY:
992             bBad = FALSE;
993         }
994
995         res = pFBadPropTag(pt);
996         if (bBad)
997             ok(res != 0, "pt= %d: Expected non-zero, got 0\n", pt);
998         else
999             ok(res == 0 || broken(res) /* Win9x */,
1000                "pt= %d: Expected zero, got %d\n", pt, res);
1001     }
1002 }
1003
1004 static void test_FBadRow(void)
1005 {
1006     ULONG ulRet;
1007
1008     pFBadRow = (void*)GetProcAddress(hMapi32, "FBadRow@4");
1009     if (!pFBadRow)
1010         return;
1011
1012     ulRet = pFBadRow(NULL);
1013     ok(ulRet != 0, "FBadRow(null): Expected non-zero, got 0\n");
1014
1015     /* FIXME */
1016 }
1017
1018 static void test_FBadProp(void)
1019 {
1020     static WCHAR szEmpty[] = { '\0' };
1021     GUID iid;
1022     ULONG pt, res;
1023     SPropValue pv;
1024
1025     pFBadProp = (void*)GetProcAddress(hMapi32, "FBadProp@4");
1026     if (!pFBadProp)
1027         return;
1028
1029     for (pt = 0; pt < PROP_ID_INVALID; pt++)
1030     {
1031         BOOL bBad = TRUE;
1032
1033         memset(&pv, 0, sizeof(pv));
1034         pv.ulPropTag = pt;
1035
1036         /* Note that MV values are valid below because their array count is 0,
1037          * so no pointers are validated.
1038          */
1039         switch (PROP_TYPE(pt))
1040         {
1041         case (MV_FLAG|PT_UNSPECIFIED):
1042         case PT_UNSPECIFIED:
1043         case (MV_FLAG|PT_NULL):
1044         case PT_NULL:
1045         case PT_MV_I2:
1046         case PT_I2:
1047         case PT_MV_I4:
1048         case PT_I4:
1049         case PT_MV_I8:
1050         case PT_I8:
1051         case PT_MV_R4:
1052         case PT_R4:
1053         case PT_MV_R8:
1054         case PT_R8:
1055         case PT_MV_CURRENCY:
1056         case PT_CURRENCY:
1057         case PT_MV_APPTIME:
1058         case PT_APPTIME:
1059         case (MV_FLAG|PT_ERROR):
1060         case PT_ERROR:
1061         case (MV_FLAG|PT_BOOLEAN):
1062         case PT_BOOLEAN:
1063         case (MV_FLAG|PT_OBJECT):
1064         case PT_OBJECT:
1065         case PT_MV_STRING8:
1066         case PT_MV_UNICODE:
1067         case PT_MV_SYSTIME:
1068         case PT_SYSTIME:
1069         case PT_MV_BINARY:
1070         case PT_BINARY:
1071         case PT_MV_CLSID:
1072             bBad = FALSE;
1073             break;
1074         case PT_STRING8:
1075         case PT_UNICODE:
1076             pv.Value.lpszW = szEmpty;
1077             bBad = FALSE;
1078             break;
1079         case PT_CLSID:
1080             pv.Value.lpguid = &iid;
1081             bBad = FALSE;
1082             break;
1083         }
1084
1085         res = pFBadProp(&pv);
1086         if (bBad)
1087             ok(res != 0, "pt= %d: Expected non-zero, got 0\n", pt);
1088         else
1089             ok(res == 0 || broken(res) /* Win9x */,
1090                "pt= %d: Expected zero, got %d\n", pt, res);
1091     }
1092 }
1093
1094 static void test_FBadColumnSet(void)
1095 {
1096     SPropTagArray pta;
1097     ULONG pt, res;
1098
1099     pFBadColumnSet = (void*)GetProcAddress(hMapi32, "FBadColumnSet@4");
1100     if (!pFBadColumnSet)
1101         return;
1102
1103     res = pFBadColumnSet(NULL);
1104     ok(res != 0, "(null): Expected non-zero, got 0\n");
1105
1106     pta.cValues = 1;
1107
1108     for (pt = 0; pt < PROP_ID_INVALID; pt++)
1109     {
1110         BOOL bBad = TRUE;
1111
1112         pta.aulPropTag[0] = pt;
1113
1114         switch (pt & (~MV_FLAG & PROP_TYPE_MASK))
1115         {
1116         case PT_UNSPECIFIED:
1117         case PT_NULL:
1118         case PT_I2:
1119         case PT_I4:
1120         case PT_R4:
1121         case PT_R8:
1122         case PT_CURRENCY:
1123         case PT_APPTIME:
1124         case PT_BOOLEAN:
1125         case PT_OBJECT:
1126         case PT_I8:
1127         case PT_STRING8:
1128         case PT_UNICODE:
1129         case PT_SYSTIME:
1130         case PT_CLSID:
1131         case PT_BINARY:
1132             bBad = FALSE;
1133         }
1134         if (pt == (MV_FLAG|PT_ERROR))
1135             bBad = FALSE;
1136
1137         res = pFBadColumnSet(&pta);
1138         if (bBad)
1139             ok(res != 0, "pt= %d: Expected non-zero, got 0\n", pt);
1140         else
1141             ok(res == 0 || broken(res) /* Win9x */,
1142                "pt= %d: Expected zero, got %d\n", pt, res);
1143     }
1144 }
1145
1146
1147 static void test_IProp(void)
1148 {
1149     IPropData *lpIProp;
1150     LPMAPIERROR lpError;
1151     LPSPropProblemArray lpProbs;
1152     LPSPropValue lpProps;
1153     LPSPropTagArray lpTags;
1154     SPropValue pvs[2];
1155     SizedSPropTagArray(2,tags);
1156     ULONG access[2], count;
1157     SCODE sc;
1158
1159     pCreateIProp = (void*)GetProcAddress(hMapi32, "CreateIProp@24");
1160
1161     if (!pCreateIProp)
1162         return;
1163
1164     memset(&tags, 0 , sizeof(tags));
1165
1166     /* Create the object */
1167     lpIProp = NULL;
1168     sc = pCreateIProp(&IID_IMAPIPropData, (ALLOCATEBUFFER *)pMAPIAllocateBuffer, (ALLOCATEMORE*)pMAPIAllocateMore,
1169                       (FREEBUFFER *)pMAPIFreeBuffer, NULL, &lpIProp);
1170     ok(sc == S_OK && lpIProp,
1171        "CreateIProp: expected S_OK, non-null, got 0x%08X,%p\n", sc, lpIProp);
1172
1173     if (sc != S_OK || !lpIProp)
1174         return;
1175
1176     /* GetLastError - No errors set */
1177     lpError = NULL;
1178     IPropData_GetLastError(lpIProp, E_INVALIDARG, 0, &lpError);
1179     ok(sc == S_OK && !lpError,
1180        "GetLastError: Expected S_OK, null, got 0x%08X,%p\n", sc, lpError);
1181
1182     /* Get prop tags - succeeds returning 0 items */
1183     lpTags = NULL;
1184     sc = IPropData_GetPropList(lpIProp, 0, &lpTags);
1185     ok(sc == S_OK && lpTags && lpTags->cValues == 0,
1186        "GetPropList(empty): Expected S_OK, non-null, 0, got 0x%08X,%p,%d\n",
1187         sc, lpTags, lpTags ? lpTags->cValues : 0);
1188     if (lpTags)
1189         pMAPIFreeBuffer(lpTags);
1190
1191     /* Get props - succeeds returning 0 items */
1192     lpProps = NULL;
1193     count = 0;
1194     tags.cValues = 1;
1195     tags.aulPropTag[0] = PR_IMPORTANCE;
1196     sc = IPropData_GetProps(lpIProp, (LPSPropTagArray)&tags, 0, &count, &lpProps);
1197     ok(sc == MAPI_W_ERRORS_RETURNED && lpProps && count == 1,
1198        "GetProps(empty): Expected ERRORS_RETURNED, non-null, 1, got 0x%08X,%p,%d\n",
1199        sc, lpProps, count);
1200     if (lpProps && count > 0)
1201     {
1202         ok(lpProps[0].ulPropTag == CHANGE_PROP_TYPE(PR_IMPORTANCE,PT_ERROR),
1203            "GetProps(empty): Expected %x, got %x\n",
1204            CHANGE_PROP_TYPE(PR_IMPORTANCE,PT_ERROR), lpProps[0].ulPropTag);
1205
1206         pMAPIFreeBuffer(lpProps);
1207     }
1208
1209     /* Add (NULL) - Can't add NULLs */
1210     lpProbs = NULL;
1211     pvs[0].ulPropTag = PROP_TAG(PT_NULL,0x01);
1212     sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs);
1213     ok(sc == MAPI_E_INVALID_PARAMETER && !lpProbs,
1214        "SetProps(): Expected INVALID_PARAMETER, null, got 0x%08X,%p\n",
1215        sc, lpProbs);
1216
1217     /* Add (OBJECT) - Can't add OBJECTs */
1218     lpProbs = NULL;
1219     pvs[0].ulPropTag = PROP_TAG(PT_OBJECT,0x01);
1220     sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs);
1221     ok(sc == MAPI_E_INVALID_PARAMETER && !lpProbs,
1222        "SetProps(OBJECT): Expected INVALID_PARAMETER, null, got 0x%08X,%p\n",
1223        sc, lpProbs);
1224
1225     /* Add - Adds value */
1226     lpProbs = NULL;
1227     pvs[0].ulPropTag = PR_IMPORTANCE;
1228     sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs);
1229     ok(sc == S_OK && !lpProbs,
1230        "SetProps(ERROR): Expected S_OK, null, got 0x%08X,%p\n", sc, lpProbs);
1231
1232     /* Get prop list - returns 1 item */
1233     lpTags = NULL;
1234     IPropData_GetPropList(lpIProp, 0, &lpTags);
1235     ok(sc == S_OK && lpTags && lpTags->cValues == 1,
1236        "GetPropList: Expected S_OK, non-null, 1, got 0x%08X,%p,%d\n",
1237         sc, lpTags, lpTags ? lpTags->cValues : 0);
1238     if (lpTags && lpTags->cValues > 0)
1239     {
1240         ok(lpTags->aulPropTag[0] == PR_IMPORTANCE,
1241            "GetPropList: Expected %x, got %x\n",
1242            PR_IMPORTANCE, lpTags->aulPropTag[0]);
1243         pMAPIFreeBuffer(lpTags);
1244     }
1245
1246     /* Set access to read and write */
1247     sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READWRITE);
1248     ok(sc == S_OK, "SetObjAcess(WRITE): Expected S_OK got 0x%08X\n", sc);
1249
1250     tags.cValues = 1;
1251     tags.aulPropTag[0] = PR_IMPORTANCE;
1252
1253     /* Set item access (bad access) - Fails */
1254     access[0] = 0;
1255     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1256     ok(sc == MAPI_E_INVALID_PARAMETER,
1257        "SetPropAcess(0): Expected INVALID_PARAMETER got 0x%08X\n",sc);
1258     access[0] = IPROP_READWRITE;
1259     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1260     ok(sc == MAPI_E_INVALID_PARAMETER,
1261        "SetPropAcess(RW): Expected INVALID_PARAMETER got 0x%08X\n",sc);
1262     access[0] = IPROP_CLEAN;
1263     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1264     ok(sc == MAPI_E_INVALID_PARAMETER,
1265        "SetPropAcess(C): Expected INVALID_PARAMETER got 0x%08X\n",sc);
1266
1267     /* Set item access to read/write/clean */
1268     tags.cValues = 1;
1269     tags.aulPropTag[0] = PR_IMPORTANCE;
1270     access[0] = IPROP_READWRITE|IPROP_CLEAN;
1271     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1272     ok(sc == S_OK, "SetPropAcess(RW/C): Expected S_OK got 0x%08X\n",sc);
1273
1274     /* Set object access to read only */
1275     sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READONLY);
1276     ok(sc == S_OK, "SetObjAcess(READ): Expected S_OK got 0x%08X\n", sc);
1277
1278     /* Set item access to read/write/dirty - doesn't care about RO object */
1279     access[0] = IPROP_READONLY|IPROP_DIRTY;
1280     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1281     ok(sc == S_OK, "SetPropAcess(WRITE): Expected S_OK got 0x%08X\n", sc);
1282
1283     /* Delete any item when set to read only - Error */
1284     lpProbs = NULL;
1285     tags.aulPropTag[0] = PR_RESPONSE_REQUESTED;
1286     sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1287     ok(sc == E_ACCESSDENIED && !lpProbs,
1288        "DeleteProps(nonexistent): Expected E_ACCESSDENIED null got 0x%08X %p\n",
1289        sc, lpProbs);
1290
1291     /* Set access to read and write */
1292     sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READWRITE);
1293     ok(sc == S_OK, "SetObjAcess(WRITE): Expected S_OK got 0x%08X\n", sc);
1294
1295     /* Delete nonexistent item - No error */
1296     lpProbs = NULL;
1297     tags.aulPropTag[0] = PR_RESPONSE_REQUESTED;
1298     sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1299     ok(sc == S_OK && !lpProbs,
1300        "DeleteProps(nonexistent): Expected S_OK null got 0x%08X %p\n",
1301        sc, lpProbs);
1302
1303     /* Delete existing item (r/o) - No error, but lpProbs populated */
1304     lpProbs = NULL;
1305     tags.aulPropTag[0] = PR_IMPORTANCE;
1306     sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1307     ok(sc == S_OK && lpProbs,
1308        "DeleteProps(RO): Expected S_OK non-null got 0x%08X %p\n", sc, lpProbs);
1309
1310     if (lpProbs && lpProbs->cProblem > 0)
1311     {
1312         ok(lpProbs->cProblem == 1 &&
1313            lpProbs->aProblem[0].ulIndex == 0 &&
1314            lpProbs->aProblem[0].ulPropTag == PR_IMPORTANCE &&
1315            lpProbs->aProblem[0].scode == E_ACCESSDENIED,
1316            "DeleteProps(RO): Expected (1,0,%x,%x) got (%d,%x,%x)\n",
1317             PR_IMPORTANCE, E_ACCESSDENIED,
1318             lpProbs->aProblem[0].ulIndex, lpProbs->aProblem[0].ulPropTag,
1319             lpProbs->aProblem[0].scode);
1320         pMAPIFreeBuffer(lpProbs);
1321     }
1322
1323     lpProbs = NULL;
1324     tags.cValues = 1;
1325     tags.aulPropTag[0] = PR_RESPONSE_REQUESTED;
1326     IPropData_HrAddObjProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1327     ok(sc == S_OK && !lpProbs,
1328        "AddObjProps(RO): Expected S_OK null got 0x%08X %p\n", sc, lpProbs);
1329
1330     /* Get prop list - returns 1 item */
1331     lpTags = NULL;
1332     IPropData_GetPropList(lpIProp, 0, &lpTags);
1333     ok(sc == S_OK && lpTags && lpTags->cValues == 1,
1334        "GetPropList: Expected S_OK, non-null, 1, got 0x%08X,%p,%d\n",
1335         sc, lpTags, lpTags ? lpTags->cValues : 0);
1336     if (lpTags && lpTags->cValues > 0)
1337     {
1338         ok(lpTags->aulPropTag[0] == PR_IMPORTANCE,
1339            "GetPropList: Expected %x, got %x\n",
1340            PR_IMPORTANCE, lpTags->aulPropTag[0]);
1341         pMAPIFreeBuffer(lpTags);
1342     }
1343
1344     /* Set item to r/w again */
1345     access[0] = IPROP_READWRITE|IPROP_DIRTY;
1346     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1347     ok(sc == S_OK, "SetPropAcess(WRITE): Expected S_OK got 0x%08X\n", sc);
1348
1349     /* Delete existing item (r/w) - No error, no problems */
1350     lpProbs = NULL;
1351     sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1352     ok(sc == S_OK && !lpProbs,
1353        "DeleteProps(RO): Expected S_OK null got 0x%08X %p\n", sc, lpProbs);
1354
1355     /* Free the list */
1356     IPropData_Release(lpIProp);
1357 }
1358
1359 START_TEST(prop)
1360 {
1361     SCODE ret;
1362
1363     if(!InitFuncPtrs())
1364     {
1365         win_skip("Needed functions are not available\n");
1366         return;
1367     }
1368
1369     SetLastError(0xdeadbeef);
1370     ret = pScInitMapiUtil(0);
1371     if ((ret != S_OK) && (GetLastError() == ERROR_PROC_NOT_FOUND))
1372     {
1373         win_skip("ScInitMapiUtil is not implemented\n");
1374         FreeLibrary(hMapi32);
1375         return;
1376     }
1377
1378     test_PropCopyMore();
1379     test_UlPropSize();
1380     test_FPropContainsProp();
1381     test_FPropCompareProp();
1382     test_LPropCompareProp();
1383     test_PpropFindProp();
1384     test_ScCountProps();
1385     test_ScCopyRelocProps();
1386     test_LpValFindProp();
1387     test_FBadRglpszA();
1388     test_FBadRglpszW();
1389     test_FBadRowSet();
1390     test_FBadPropTag();
1391     test_FBadRow();
1392     test_FBadProp();
1393     test_FBadColumnSet();
1394
1395     test_IProp();
1396     FreeLibrary(hMapi32);
1397 }