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