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