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