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