Set remove to TRUE in accept_hardware_message for messages that should
[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        ulCount == sizeof(SPropValue) + sizeof(char*) + 5 &&
816        !strcmp(lpResProp->Value.MVszA.lppszA[0], szTestA),
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        sc==S_OK?lpResProp->Value.MVszA.lppszA[0]:NULL, 
822        sc==S_OK?lpResProp->Value.MVszA.lppszA[0]:NULL, ulCount);
823
824     memcpy(buffer2, buffer, sizeof(buffer));
825
826     /* Clear the data in the source buffer. Since pointers in the copied buffer
827      * refer to the source buffer, this proves that native always assumes that
828      * the copied buffers pointers are bad (needing to be relocated first).
829      */
830     memset(buffer, 0, sizeof(buffer));
831     ulCount = 0;
832
833     sc = pScRelocProps(1, (LPSPropValue)buffer2, buffer, buffer2, &ulCount);
834     lpResProp = (LPSPropValue)buffer2;
835     ok(sc == S_OK && lpResProp->ulPropTag == pvProp.ulPropTag &&
836        lpResProp->Value.MVszA.cValues == 1 &&
837        lpResProp->Value.MVszA.lppszA[0] == buffer2 + sizeof(SPropValue) + sizeof(char*) &&
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        !strcmp(lpResProp->Value.MVszA.lppszA[0], szTestA),
844        "RelocProps(str): Expected 0 {1,%lx,%p,%s} %d got 0x%08lx {%ld,%lx,%p,%s} %ld\n",
845        pvProp.ulPropTag, buffer2 + sizeof(SPropValue) + sizeof(char*),
846        szTestA, sizeof(SPropValue) + sizeof(char*) + 5, sc,
847        lpResProp->Value.MVszA.cValues, lpResProp->ulPropTag,
848        sc==S_OK?lpResProp->Value.MVszA.lppszA[0]:NULL, 
849        sc==S_OK?lpResProp->Value.MVszA.lppszA[0]:NULL, ulCount);
850
851     /* Native crashes with lpNew or lpOld set to NULL so skip testing this */
852 }
853
854 static void test_LpValFindProp(void)
855 {
856     SPropValue pvProp, *pRet;
857     ULONG i;
858
859     pLpValFindProp = (void*)GetProcAddress(hMapi32, "LpValFindProp@12");
860
861     if (!pLpValFindProp)
862         return;
863
864     for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
865     {
866         pvProp.ulPropTag = PROP_TAG(ptTypes[i], 1u);
867
868         pRet = pLpValFindProp(PROP_TAG(ptTypes[i], 1u), 1u, &pvProp);
869         ok(pRet == &pvProp, "LpValFindProp[%ld]: Didn't find existing propery id/type\n",
870            ptTypes[i]);
871
872         pRet = pLpValFindProp(PROP_TAG(ptTypes[i], 0u), 1u, &pvProp);
873         ok(pRet == NULL, "LpValFindProp[%ld]: Found nonexistent propery id\n",
874            ptTypes[i]);
875
876         pRet = pLpValFindProp(PROP_TAG(PT_NULL, 0u), 1u, &pvProp);
877         ok(pRet == NULL, "LpValFindProp[%ld]: Found nonexistent propery id/type\n",
878            ptTypes[i]);
879
880         pRet = pLpValFindProp(PROP_TAG(PT_NULL, 1u), 1u, &pvProp);
881         ok(pRet == &pvProp, "LpValFindProp[%ld]: Didn't find existing propery id\n",
882            ptTypes[i]);
883     }
884 }
885
886 static void test_FBadRglpszA(void)
887 {
888     LPSTR lpStrs[4];
889     char *szString = "A String";
890     BOOL bRet;
891
892     pFBadRglpszA = (void*)GetProcAddress(hMapi32, "FBadRglpszA@8");
893     if (!pFBadRglpszA)
894         return;
895
896     bRet = pFBadRglpszA(NULL, 10);
897     ok(bRet == TRUE, "FBadRglpszA(Null): expected TRUE, got FALSE\n");
898
899     lpStrs[0] = lpStrs[1] = lpStrs[2] = lpStrs[3] = NULL;
900     bRet = pFBadRglpszA(lpStrs, 4);
901     ok(bRet == TRUE, "FBadRglpszA(Nulls): expected TRUE, got FALSE\n");
902
903     lpStrs[0] = lpStrs[1] = lpStrs[2] = szString;
904     bRet = pFBadRglpszA(lpStrs, 3);
905     ok(bRet == FALSE, "FBadRglpszA(valid): expected FALSE, got TRUE\n");
906
907     bRet = pFBadRglpszA(lpStrs, 4);
908     ok(bRet == TRUE, "FBadRglpszA(1 invalid): expected TRUE, got FALSE\n");
909 }
910
911 static void test_FBadRglpszW(void)
912 {
913     LPWSTR lpStrs[4];
914     WCHAR szString[] = { 'A',' ','S','t','r','i','n','g','\0' };
915     BOOL bRet;
916
917     pFBadRglpszW = (void*)GetProcAddress(hMapi32, "FBadRglpszW@8");
918     if (!pFBadRglpszW)
919         return;
920
921     bRet = pFBadRglpszW(NULL, 10);
922     ok(bRet == TRUE, "FBadRglpszW(Null): expected TRUE, got FALSE\n");
923
924     lpStrs[0] = lpStrs[1] = lpStrs[2] = lpStrs[3] = NULL;
925     bRet = pFBadRglpszW(lpStrs, 4);
926     ok(bRet == TRUE, "FBadRglpszW(Nulls): expected TRUE, got FALSE\n");
927
928     lpStrs[0] = lpStrs[1] = lpStrs[2] = szString;
929     bRet = pFBadRglpszW(lpStrs, 3);
930     ok(bRet == FALSE, "FBadRglpszW(valid): expected FALSE, got TRUE\n");
931
932     bRet = pFBadRglpszW(lpStrs, 4);
933     ok(bRet == TRUE, "FBadRglpszW(1 invalid): expected TRUE, got FALSE\n");
934 }
935
936 static void test_FBadRowSet(void)
937 {
938     ULONG ulRet;
939
940     pFBadRowSet = (void*)GetProcAddress(hMapi32, "FBadRowSet@4");
941     if (!pFBadRowSet)
942         return;
943
944     ulRet = pFBadRowSet(NULL);
945     ok(ulRet != 0, "FBadRow(null): Expected non-zero, got 0\n");
946
947     /* FIXME */
948 }
949
950 static void test_FBadPropTag(void)
951 {
952     ULONG pt, res;
953
954     pFBadPropTag = (void*)GetProcAddress(hMapi32, "FBadPropTag@4");
955     if (!pFBadPropTag)
956         return;
957
958     for (pt = 0; pt < PROP_ID_INVALID; pt++)
959     {
960         BOOL bBad = TRUE;
961
962         switch (pt & (~MV_FLAG & PROP_TYPE_MASK))
963         {
964         case PT_UNSPECIFIED:
965         case PT_NULL: case PT_I2: case PT_I4: case PT_R4:
966         case PT_R8: case PT_CURRENCY: case PT_APPTIME:
967         case PT_ERROR: case PT_BOOLEAN: case PT_OBJECT:
968         case PT_I8: case PT_STRING8: case PT_UNICODE:
969         case PT_SYSTIME: case PT_CLSID: case PT_BINARY:
970             bBad = FALSE;
971         }
972
973         res = pFBadPropTag(pt);
974         if (bBad)
975             ok(res != 0, "pt= %ld: Expected non-zero, got 0\n", pt);
976         else
977             ok(res == 0, "pt= %ld: Expected zero, got %ld\n", pt, res);
978     }
979 }
980
981 static void test_FBadRow(void)
982 {
983     ULONG ulRet;
984
985     pFBadRow = (void*)GetProcAddress(hMapi32, "FBadRow@4");
986     if (!pFBadRow)
987         return;
988
989     ulRet = pFBadRow(NULL);
990     ok(ulRet != 0, "FBadRow(null): Expected non-zero, got 0\n");
991
992     /* FIXME */
993 }
994
995 static void test_FBadProp(void)
996 {
997     WCHAR szEmpty[] = { '\0' };
998     GUID iid;
999     ULONG pt, res;
1000     SPropValue pv;
1001
1002     pFBadProp = (void*)GetProcAddress(hMapi32, "FBadProp@4");
1003     if (!pFBadProp)
1004         return;
1005
1006     for (pt = 0; pt < PROP_ID_INVALID; pt++)
1007     {
1008         BOOL bBad = TRUE;
1009
1010         memset(&pv, 0, sizeof(pv));
1011         pv.ulPropTag = pt;
1012
1013         /* Note that MV values are valid below because their array count is 0,
1014          * so no pointers are validated.
1015          */
1016         switch (PROP_TYPE(pt))
1017         {
1018         case (MV_FLAG|PT_UNSPECIFIED):
1019         case PT_UNSPECIFIED:
1020         case (MV_FLAG|PT_NULL):
1021         case PT_NULL:
1022         case PT_MV_I2:
1023         case PT_I2:
1024         case PT_MV_I4:
1025         case PT_I4:
1026         case PT_MV_I8:
1027         case PT_I8:
1028         case PT_MV_R4:
1029         case PT_R4:
1030         case PT_MV_R8:
1031         case PT_R8:
1032         case PT_MV_CURRENCY:
1033         case PT_CURRENCY:
1034         case PT_MV_APPTIME:
1035         case PT_APPTIME:
1036         case (MV_FLAG|PT_ERROR):
1037         case PT_ERROR:
1038         case (MV_FLAG|PT_BOOLEAN):
1039         case PT_BOOLEAN:
1040         case (MV_FLAG|PT_OBJECT):
1041         case PT_OBJECT:
1042         case PT_MV_STRING8:
1043         case PT_MV_UNICODE:
1044         case PT_MV_SYSTIME:
1045         case PT_SYSTIME:
1046         case PT_MV_BINARY:
1047         case PT_BINARY:
1048         case PT_MV_CLSID:
1049             bBad = FALSE;
1050             break;
1051         case PT_STRING8:
1052         case PT_UNICODE:
1053             pv.Value.lpszW = szEmpty;
1054             bBad = FALSE;
1055             break;
1056         case PT_CLSID:
1057             pv.Value.lpguid = &iid;
1058             bBad = FALSE;
1059             break;
1060         }
1061
1062         res = pFBadProp(&pv);
1063         if (bBad)
1064             ok(res != 0, "pt= %ld: Expected non-zero, got 0\n", pt);
1065         else
1066             ok(res == 0, "pt= %ld: Expected zero, got %ld\n", pt, res);
1067     }
1068 }
1069
1070 static void test_FBadColumnSet(void)
1071 {
1072     SPropTagArray pta;
1073     ULONG pt, res;
1074
1075     pFBadColumnSet = (void*)GetProcAddress(hMapi32, "FBadColumnSet@4");
1076     if (!pFBadColumnSet)
1077         return;
1078
1079     res = pFBadColumnSet(NULL);
1080     ok(res != 0, "(null): Expected non-zero, got 0\n");
1081
1082     pta.cValues = 1;
1083
1084     for (pt = 0; pt < PROP_ID_INVALID; pt++)
1085     {
1086         BOOL bBad = TRUE;
1087
1088         pta.aulPropTag[0] = pt;
1089
1090         switch (pt & (~MV_FLAG & PROP_TYPE_MASK))
1091         {
1092         case PT_UNSPECIFIED:
1093         case PT_NULL:
1094         case PT_I2:
1095         case PT_I4:
1096         case PT_R4:
1097         case PT_R8:
1098         case PT_CURRENCY:
1099         case PT_APPTIME:
1100         case PT_BOOLEAN:
1101         case PT_OBJECT:
1102         case PT_I8:
1103         case PT_STRING8:
1104         case PT_UNICODE:
1105         case PT_SYSTIME:
1106         case PT_CLSID:
1107         case PT_BINARY:
1108             bBad = FALSE;
1109         }
1110         if (pt == (MV_FLAG|PT_ERROR))
1111             bBad = FALSE;
1112
1113         res = pFBadColumnSet(&pta);
1114         if (bBad)
1115             ok(res != 0, "pt= %ld: Expected non-zero, got 0\n", pt);
1116         else
1117             ok(res == 0, "pt= %ld: Expected zero, got %ld\n", pt, res);
1118     }
1119 }
1120
1121
1122 static void test_IProp(void)
1123 {
1124     IPropData *lpIProp;
1125     LPMAPIERROR lpError;
1126     LPSPropProblemArray lpProbs;
1127     LPSPropValue lpProps;
1128     LPSPropTagArray lpTags;
1129     SPropValue pvs[2];
1130     SizedSPropTagArray(2,tags);
1131     ULONG access[2], count;
1132     SCODE sc;
1133
1134     pCreateIProp = (void*)GetProcAddress(hMapi32, "CreateIProp@24");
1135     if (!pCreateIProp)
1136         return;
1137
1138     memset(&tags, 0 , sizeof(tags));
1139
1140     /* Create the object */
1141     lpIProp = NULL;
1142     sc = pCreateIProp(&IID_IMAPIPropData, MAPIAllocateBuffer, MAPIAllocateMore,
1143                       MAPIFreeBuffer, NULL, &lpIProp);
1144     ok(sc == S_OK && lpIProp,
1145        "CreateIProp: expected S_OK, non-null, got 0x%08lX,%p\n", sc, lpIProp);
1146
1147     if (sc != S_OK || !lpIProp)
1148         return;
1149
1150     /* GetLastError - No errors set */
1151     lpError = NULL;
1152     IPropData_GetLastError(lpIProp, E_INVALIDARG, 0, &lpError);
1153     ok(sc == S_OK && !lpError,
1154        "GetLastError: Expected S_OK, null, got 0x%08lX,%p\n", sc, lpError);
1155
1156     /* Get prop tags - succeeds returning 0 items */
1157     lpTags = NULL;
1158     sc = IPropData_GetPropList(lpIProp, 0, &lpTags);
1159     ok(sc == S_OK && lpTags && lpTags->cValues == 0,
1160        "GetPropList(empty): Expected S_OK, non-null, 0, got 0x%08lX,%p,%ld\n",
1161         sc, lpTags, lpTags ? lpTags->cValues : 0);
1162     if (lpTags)
1163         MAPIFreeBuffer(lpTags);
1164
1165     /* Get props - succeeds returning 0 items */
1166     lpProps = NULL;
1167     count = 0;
1168     tags.cValues = 1;
1169     tags.aulPropTag[0] = PR_IMPORTANCE;
1170     sc = IPropData_GetProps(lpIProp, (LPSPropTagArray)&tags, 0, &count, &lpProps);
1171     ok(sc == MAPI_W_ERRORS_RETURNED && lpProps && count == 1,
1172        "GetProps(empty): Expected ERRORS_RETURNED, non-null, 1, got 0x%08lX,%p,%ld\n",
1173        sc, lpProps, count);
1174     if (lpProps && count > 0)
1175     {
1176         ok(lpProps[0].ulPropTag == CHANGE_PROP_TYPE(PR_IMPORTANCE,PT_ERROR),
1177            "GetProps(empty): Expected %x, got %lx\n",
1178            CHANGE_PROP_TYPE(PR_IMPORTANCE,PT_ERROR), lpProps[0].ulPropTag);
1179
1180         MAPIFreeBuffer(lpProps);
1181     }
1182
1183     /* Add (NULL) - Can't add NULL's */
1184     lpProbs = NULL;
1185     pvs[0].ulPropTag = PROP_TAG(PT_NULL,0x01);
1186     sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs);
1187     ok(sc == MAPI_E_INVALID_PARAMETER && !lpProbs,
1188        "SetProps(): Expected INVALID_PARAMETER, null, got 0x%08lX,%p\n",
1189        sc, lpProbs);
1190
1191     /* Add (OBJECT) - Can't add OBJECTS's */
1192     lpProbs = NULL;
1193     pvs[0].ulPropTag = PROP_TAG(PT_OBJECT,0x01);
1194     sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs);
1195     ok(sc == MAPI_E_INVALID_PARAMETER && !lpProbs,
1196        "SetProps(OBJECT): Expected INVALID_PARAMETER, null, got 0x%08lX,%p\n",
1197        sc, lpProbs);
1198
1199     /* Add - Adds value */
1200     lpProbs = NULL;
1201     pvs[0].ulPropTag = PR_IMPORTANCE;
1202     sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs);
1203     ok(sc == S_OK && !lpProbs,
1204        "SetProps(ERROR): Expected S_OK, null, got 0x%08lX,%p\n", sc, lpProbs);
1205
1206     /* Get prop list - returns 1 item */
1207     lpTags = NULL;
1208     IPropData_GetPropList(lpIProp, 0, &lpTags);
1209     ok(sc == S_OK && lpTags && lpTags->cValues == 1,
1210        "GetPropList: Expected S_OK, non-null, 1, got 0x%08lX,%p,%ld\n",
1211         sc, lpTags, lpTags ? lpTags->cValues : 0);
1212     if (lpTags && lpTags->cValues > 0)
1213     {
1214         ok(lpTags->aulPropTag[0] == PR_IMPORTANCE,
1215            "GetPropList: Expected %x, got %lx\n",
1216            PR_IMPORTANCE, lpTags->aulPropTag[0]);
1217         MAPIFreeBuffer(lpTags);
1218     }
1219
1220     /* Set access to read and write */
1221     sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READWRITE);
1222     ok(sc == S_OK, "SetObjAcess(WRITE): Expected S_OK got 0x%08lX\n", sc);
1223
1224     tags.cValues = 1;
1225     tags.aulPropTag[0] = PR_IMPORTANCE;
1226
1227     /* Set item access (bad access) - Fails */
1228     access[0] = 0;
1229     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1230     ok(sc == MAPI_E_INVALID_PARAMETER,
1231        "SetPropAcess(0): Expected INVALID_PARAMETER got 0x%08lX\n",sc);
1232     access[0] = IPROP_READWRITE;
1233     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1234     ok(sc == MAPI_E_INVALID_PARAMETER,
1235        "SetPropAcess(RW): Expected INVALID_PARAMETER got 0x%08lX\n",sc);
1236     access[0] = IPROP_CLEAN;
1237     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1238     ok(sc == MAPI_E_INVALID_PARAMETER,
1239        "SetPropAcess(C): Expected INVALID_PARAMETER got 0x%08lX\n",sc);
1240
1241     /* Set item access to read/write/clean */
1242     tags.cValues = 1;
1243     tags.aulPropTag[0] = PR_IMPORTANCE;
1244     access[0] = IPROP_READWRITE|IPROP_CLEAN;
1245     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1246     ok(sc == S_OK, "SetPropAcess(RW/C): Expected S_OK got 0x%08lX\n",sc);
1247
1248     /* Set object access to read only */
1249     sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READONLY);
1250     ok(sc == S_OK, "SetObjAcess(READ): Expected S_OK got 0x%08lX\n", sc);
1251
1252     /* Set item access to read/write/dirty - doesn't care about RO object */
1253     access[0] = IPROP_READONLY|IPROP_DIRTY;
1254     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1255     ok(sc == S_OK, "SetPropAcess(WRITE): Expected S_OK got 0x%08lX\n", sc);
1256
1257     /* Delete any item when set to read only - Error */
1258     lpProbs = NULL;
1259     tags.aulPropTag[0] = PR_RESPONSE_REQUESTED;
1260     sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1261     ok(sc == E_ACCESSDENIED && !lpProbs,
1262        "DeleteProps(nonexistent): Expected E_ACCESSDENIED null got 0x%08lX %p\n",
1263        sc, lpProbs);
1264
1265     /* Set access to read and write */
1266     sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READWRITE);
1267     ok(sc == S_OK, "SetObjAcess(WRITE): Expected S_OK got 0x%08lX\n", sc);
1268
1269     /* Delete nonexistent item - No error */
1270     lpProbs = NULL;
1271     tags.aulPropTag[0] = PR_RESPONSE_REQUESTED;
1272     sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1273     ok(sc == S_OK && !lpProbs,
1274        "DeleteProps(nonexistent): Expected S_OK null got 0x%08lX %p\n",
1275        sc, lpProbs);
1276
1277     /* Delete existing item (r/o) - No error, but lpProbs populated */
1278     lpProbs = NULL;
1279     tags.aulPropTag[0] = PR_IMPORTANCE;
1280     sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1281     ok(sc == S_OK && lpProbs,
1282        "DeleteProps(RO): Expected S_OK non-null got 0x%08lX %p\n", sc, lpProbs);
1283
1284     if (lpProbs && lpProbs->cProblem > 0)
1285     {
1286         ok(lpProbs->cProblem == 1 &&
1287            lpProbs->aProblem[0].ulIndex == 0 &&
1288            lpProbs->aProblem[0].ulPropTag == PR_IMPORTANCE &&
1289            lpProbs->aProblem[0].scode == E_ACCESSDENIED,
1290            "DeleteProps(RO): Expected (1,0,%x,%lx) got (%ld,%lx,%lx)\n",
1291             PR_IMPORTANCE, E_ACCESSDENIED,
1292             lpProbs->aProblem[0].ulIndex, lpProbs->aProblem[0].ulPropTag,
1293             lpProbs->aProblem[0].scode);
1294         MAPIFreeBuffer(lpProbs);
1295     }
1296
1297     lpProbs = NULL;
1298     tags.cValues = 1;
1299     tags.aulPropTag[0] = PR_RESPONSE_REQUESTED;
1300     IPropData_HrAddObjProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1301     ok(sc == S_OK && !lpProbs,
1302        "AddObjProps(RO): Expected S_OK null got 0x%08lX %p\n", sc, lpProbs);
1303
1304     /* Get prop list - returns 1 item */
1305     lpTags = NULL;
1306     IPropData_GetPropList(lpIProp, 0, &lpTags);
1307     ok(sc == S_OK && lpTags && lpTags->cValues == 1,
1308        "GetPropList: Expected S_OK, non-null, 1, got 0x%08lX,%p,%ld\n",
1309         sc, lpTags, lpTags ? lpTags->cValues : 0);
1310     if (lpTags && lpTags->cValues > 0)
1311     {
1312         ok(lpTags->aulPropTag[0] == PR_IMPORTANCE,
1313            "GetPropList: Expected %x, got %lx\n",
1314            PR_IMPORTANCE, lpTags->aulPropTag[0]);
1315         MAPIFreeBuffer(lpTags);
1316     }
1317
1318     /* Set item to r/w again */
1319     access[0] = IPROP_READWRITE|IPROP_DIRTY;
1320     sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
1321     ok(sc == S_OK, "SetPropAcess(WRITE): Expected S_OK got 0x%08lX\n", sc);
1322
1323     /* Delete existing item (r/w) - No error, no problems */
1324     lpProbs = NULL;
1325     sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
1326     ok(sc == S_OK && !lpProbs,
1327        "DeleteProps(RO): Expected S_OK null got 0x%08lX %p\n", sc, lpProbs);
1328
1329     /* Free the list */
1330     IPropData_Release(lpIProp);
1331 }
1332
1333 START_TEST(prop)
1334 {
1335     hMapi32 = LoadLibraryA("mapi32.dll");
1336
1337     pScInitMapiUtil = (void*)GetProcAddress(hMapi32, "ScInitMapiUtil@4");
1338     if (!pScInitMapiUtil)
1339         return;
1340     pScInitMapiUtil(0);
1341
1342     test_PropCopyMore();
1343     test_UlPropSize();
1344     test_FPropContainsProp();
1345     test_FPropCompareProp();
1346     test_LPropCompareProp();
1347     test_PpropFindProp();
1348     test_ScCountProps();
1349     test_ScCopyRelocProps();
1350     test_LpValFindProp();
1351     test_FBadRglpszA();
1352     test_FBadRglpszW();
1353     test_FBadRowSet();
1354     test_FBadPropTag();
1355     test_FBadRow();
1356     test_FBadProp();
1357     test_FBadColumnSet();
1358
1359     test_IProp();
1360 }