shlwapi/tests: Skip some tests if not allowed to modify the registry.
[wine] / dlls / shlwapi / tests / ordinal.c
1 /* Unit test suite for SHLWAPI ordinal functions
2  *
3  * Copyright 2004 Jon Griffiths
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 #include <stdio.h>
21
22 #define COBJMACROS
23 #define CONST_VTABLE
24 #include "wine/test.h"
25 #include "winbase.h"
26 #include "winerror.h"
27 #include "winuser.h"
28 #include "ole2.h"
29 #include "oaidl.h"
30 #include "ocidl.h"
31 #include "mlang.h"
32 #include "shlwapi.h"
33 #include "docobj.h"
34 #include "shobjidl.h"
35 #include "shlobj.h"
36
37 /* Function ptrs for ordinal calls */
38 static HMODULE hShlwapi;
39 static BOOL is_win2k_and_lower;
40 static BOOL is_win9x;
41
42 static int (WINAPI *pSHSearchMapInt)(const int*,const int*,int,int);
43 static HRESULT (WINAPI *pGetAcceptLanguagesA)(LPSTR,LPDWORD);
44
45 static HANDLE (WINAPI *pSHAllocShared)(LPCVOID,DWORD,DWORD);
46 static LPVOID (WINAPI *pSHLockShared)(HANDLE,DWORD);
47 static BOOL   (WINAPI *pSHUnlockShared)(LPVOID);
48 static BOOL   (WINAPI *pSHFreeShared)(HANDLE,DWORD);
49 static HRESULT(WINAPIV *pSHPackDispParams)(DISPPARAMS*,VARIANTARG*,UINT,...);
50 static HRESULT(WINAPI *pIConnectionPoint_SimpleInvoke)(IConnectionPoint*,DISPID,DISPPARAMS*);
51 static HRESULT(WINAPI *pIConnectionPoint_InvokeWithCancel)(IConnectionPoint*,DISPID,DISPPARAMS*,DWORD,DWORD);
52 static HRESULT(WINAPI *pConnectToConnectionPoint)(IUnknown*,REFIID,BOOL,IUnknown*, LPDWORD,IConnectionPoint **);
53 static HRESULT(WINAPI *pSHPropertyBag_ReadLONG)(IPropertyBag *,LPCWSTR,LPLONG);
54 static LONG   (WINAPI *pSHSetWindowBits)(HWND, INT, UINT, UINT);
55 static INT    (WINAPI *pSHFormatDateTimeA)(const FILETIME UNALIGNED*, DWORD*, LPSTR, UINT);
56 static INT    (WINAPI *pSHFormatDateTimeW)(const FILETIME UNALIGNED*, DWORD*, LPWSTR, UINT);
57 static DWORD  (WINAPI *pSHGetObjectCompatFlags)(IUnknown*, const CLSID*);
58 static BOOL   (WINAPI *pGUIDFromStringA)(LPSTR, CLSID *);
59 static HRESULT (WINAPI *pIUnknown_QueryServiceExec)(IUnknown*, REFIID, const GUID*, DWORD, DWORD, VARIANT*, VARIANT*);
60 static HRESULT (WINAPI *pIUnknown_ProfferService)(IUnknown*, REFGUID, IServiceProvider*, DWORD*);
61 static HWND    (WINAPI *pSHCreateWorkerWindowA)(LONG, HWND, DWORD, DWORD, HMENU, LONG_PTR);
62 static HRESULT (WINAPI *pSHIShellFolder_EnumObjects)(LPSHELLFOLDER, HWND, SHCONTF, IEnumIDList**);
63 static DWORD   (WINAPI *pSHGetIniStringW)(LPCWSTR, LPCWSTR, LPWSTR, DWORD, LPCWSTR);
64 static BOOL    (WINAPI *pSHSetIniStringW)(LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR);
65 static HKEY    (WINAPI *pSHGetShellKey)(DWORD, LPCWSTR, BOOL);
66 static HRESULT (WINAPI *pSKGetValueW)(DWORD, LPCWSTR, LPCWSTR, DWORD*, void*, DWORD*);
67 static HRESULT (WINAPI *pSKSetValueW)(DWORD, LPCWSTR, LPCWSTR, DWORD, void*, DWORD);
68 static HRESULT (WINAPI *pSKDeleteValueW)(DWORD, LPCWSTR, LPCWSTR);
69 static HRESULT (WINAPI *pSKAllocValueW)(DWORD, LPCWSTR, LPCWSTR, DWORD*, void**, DWORD*);
70
71 static HMODULE hmlang;
72 static HRESULT (WINAPI *pLcidToRfc1766A)(LCID, LPSTR, INT);
73
74 static HMODULE hshell32;
75 static HRESULT (WINAPI *pSHGetDesktopFolder)(IShellFolder**);
76
77 static const CHAR ie_international[] = {
78     'S','o','f','t','w','a','r','e','\\',
79     'M','i','c','r','o','s','o','f','t','\\',
80     'I','n','t','e','r','n','e','t',' ','E','x','p','l','o','r','e','r','\\',
81     'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
82 static const CHAR acceptlanguage[] = {
83     'A','c','c','e','p','t','L','a','n','g','u','a','g','e',0};
84
85 static int strcmp_wa(LPCWSTR strw, const char *stra)
86 {
87     CHAR buf[512];
88     WideCharToMultiByte(CP_ACP, 0, strw, -1, buf, sizeof(buf), NULL, NULL);
89     return lstrcmpA(stra, buf);
90 }
91
92 typedef struct {
93     int id;
94     const void *args[5];
95 } call_entry_t;
96
97 typedef struct {
98     call_entry_t *calls;
99     int count;
100     int alloc;
101 } call_trace_t;
102
103 static void init_call_trace(call_trace_t *ctrace)
104 {
105     ctrace->alloc = 10;
106     ctrace->count = 0;
107     ctrace->calls = HeapAlloc(GetProcessHeap(), 0, sizeof(call_entry_t) * ctrace->alloc);
108 }
109
110 static void free_call_trace(const call_trace_t *ctrace)
111 {
112     HeapFree(GetProcessHeap(), 0, ctrace->calls);
113 }
114
115 static void add_call(call_trace_t *ctrace, int id, const void *arg0,
116     const void *arg1, const void *arg2, const void *arg3, const void *arg4)
117 {
118     call_entry_t call;
119
120     call.id = id;
121     call.args[0] = arg0;
122     call.args[1] = arg1;
123     call.args[2] = arg2;
124     call.args[3] = arg3;
125     call.args[4] = arg4;
126
127     if (ctrace->count == ctrace->alloc)
128     {
129         ctrace->alloc *= 2;
130         ctrace->calls = HeapReAlloc(GetProcessHeap(),0, ctrace->calls, ctrace->alloc*sizeof(call_entry_t));
131     }
132
133     ctrace->calls[ctrace->count++] = call;
134 }
135
136 static void ok_trace_(call_trace_t *texpected, call_trace_t *tgot, int line)
137 {
138     if (texpected->count == tgot->count)
139     {
140         INT i;
141         /* compare */
142         for (i = 0; i < texpected->count; i++)
143         {
144             call_entry_t *expected = &texpected->calls[i];
145             call_entry_t *got = &tgot->calls[i];
146             INT j;
147
148             ok_(__FILE__, line)(expected->id == got->id, "got different ids %d: %d, %d\n", i+1, expected->id, got->id);
149
150             for (j = 0; j < 5; j++)
151             {
152                 ok_(__FILE__, line)(expected->args[j] == got->args[j], "got different args[%d] for %d: %p, %p\n", j, i+1,
153                    expected->args[j], got->args[j]);
154             }
155         }
156     }
157     else
158         ok_(__FILE__, line)(0, "traces length mismatch\n");
159 }
160
161 #define ok_trace(a, b) ok_trace_(a, b, __LINE__)
162
163 /* trace of actually made calls */
164 static call_trace_t trace_got;
165
166 static void test_GetAcceptLanguagesA(void)
167 {
168     static LPCSTR table[] = {"de,en-gb;q=0.7,en;q=0.3",
169                              "de,en;q=0.3,en-gb;q=0.7", /* sorting is ignored */
170                              "winetest",    /* content is ignored */
171                              "de-de,de;q=0.5",
172                              "de",
173                              NULL};
174
175     DWORD exactsize;
176     char original[512];
177     char language[32];
178     char buffer[64];
179     HKEY hroot = NULL;
180     LONG res_query = ERROR_SUCCESS;
181     LONG lres;
182     HRESULT hr;
183     DWORD maxlen = sizeof(buffer) - 2;
184     DWORD len;
185     LCID lcid;
186     LPCSTR entry;
187     INT i = 0;
188
189     if (!pGetAcceptLanguagesA) {
190         win_skip("GetAcceptLanguagesA is not available\n");
191         return;
192     }
193
194     lcid = GetUserDefaultLCID();
195
196     /* Get the original Value */
197     lres = RegOpenKeyA(HKEY_CURRENT_USER, ie_international, &hroot);
198     if (lres) {
199         skip("RegOpenKey(%s) failed: %d\n", ie_international, lres);
200         return;
201     }
202     len = sizeof(original);
203     original[0] = 0;
204     res_query = RegQueryValueExA(hroot, acceptlanguage, 0, NULL, (PBYTE)original, &len);
205
206     RegDeleteValue(hroot, acceptlanguage);
207
208     /* Some windows versions use "lang-COUNTRY" as default */
209     memset(language, 0, sizeof(language));
210     len = GetLocaleInfoA(lcid, LOCALE_SISO639LANGNAME, language, sizeof(language));
211
212     if (len) {
213         lstrcat(language, "-");
214         memset(buffer, 0, sizeof(buffer));
215         len = GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, buffer, sizeof(buffer) - len - 1);
216         lstrcat(language, buffer);
217     }
218     else
219     {
220         /* LOCALE_SNAME has additional parts in some languages. Try only as last chance */
221         memset(language, 0, sizeof(language));
222         len = GetLocaleInfoA(lcid, LOCALE_SNAME, language, sizeof(language));
223     }
224
225     /* get the default value */
226     len = maxlen;
227     memset(buffer, '#', maxlen);
228     buffer[maxlen] = 0;
229     hr = pGetAcceptLanguagesA( buffer, &len);
230
231     if (hr != S_OK) {
232         win_skip("GetAcceptLanguagesA failed with 0x%x\n", hr);
233         goto restore_original;
234     }
235
236     if (lstrcmpA(buffer, language)) {
237         /* some windows versions use "lang" or "lang-country" as default */
238         language[0] = 0;
239         if (pLcidToRfc1766A) {
240             hr = pLcidToRfc1766A(lcid, language, sizeof(language));
241             ok(hr == S_OK, "LcidToRfc1766A returned 0x%x and %s\n", hr, language);
242         }
243     }
244
245     ok(!lstrcmpA(buffer, language),
246         "have '%s' (searching for '%s')\n", language, buffer);
247
248     if (lstrcmpA(buffer, language)) {
249         win_skip("no more ideas, how to build the default language '%s'\n", buffer);
250         goto restore_original;
251     }
252
253     trace("detected default: %s\n", language);
254     while ((entry = table[i])) {
255
256         exactsize = lstrlenA(entry);
257
258         lres = RegSetValueExA(hroot, acceptlanguage, 0, REG_SZ, (const BYTE *) entry, exactsize + 1);
259         ok(!lres, "got %d for RegSetValueExA: %s\n", lres, entry);
260
261         /* len includes space for the terminating 0 before vista/w2k8 */
262         len = exactsize + 2;
263         memset(buffer, '#', maxlen);
264         buffer[maxlen] = 0;
265         hr = pGetAcceptLanguagesA( buffer, &len);
266         ok(((hr == E_INVALIDARG) && (len == 0)) ||
267             (SUCCEEDED(hr) &&
268             ((len == exactsize) || (len == exactsize+1)) &&
269             !lstrcmpA(buffer, entry)),
270             "+2_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
271
272         len = exactsize + 1;
273         memset(buffer, '#', maxlen);
274         buffer[maxlen] = 0;
275         hr = pGetAcceptLanguagesA( buffer, &len);
276         ok(((hr == E_INVALIDARG) && (len == 0)) ||
277             (SUCCEEDED(hr) &&
278             ((len == exactsize) || (len == exactsize+1)) &&
279             !lstrcmpA(buffer, entry)),
280             "+1_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
281
282         len = exactsize;
283         memset(buffer, '#', maxlen);
284         buffer[maxlen] = 0;
285         hr = pGetAcceptLanguagesA( buffer, &len);
286
287         /* There is no space for the string in the registry.
288            When the buffer is large enough, the default language is returned
289
290            When the buffer is too small for that fallback, win7_32 and w2k8_64
291            and above fail with HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), but
292            recent os succeed and return a partial result while
293            older os succeed and overflow the buffer */
294
295         ok(((hr == E_INVALIDARG) && (len == 0)) ||
296             (((hr == S_OK) && !lstrcmpA(buffer, language)  && (len == lstrlenA(language))) ||
297             ((hr == S_OK) && !memcmp(buffer, language, len)) ||
298             ((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len)),
299             "==_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
300
301         if (exactsize > 1) {
302             len = exactsize - 1;
303             memset(buffer, '#', maxlen);
304             buffer[maxlen] = 0;
305             hr = pGetAcceptLanguagesA( buffer, &len);
306             ok(((hr == E_INVALIDARG) && (len == 0)) ||
307                 (((hr == S_OK) && !lstrcmpA(buffer, language)  && (len == lstrlenA(language))) ||
308                 ((hr == S_OK) && !memcmp(buffer, language, len)) ||
309                 ((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len)),
310                 "-1_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
311         }
312
313         len = 1;
314         memset(buffer, '#', maxlen);
315         buffer[maxlen] = 0;
316         hr = pGetAcceptLanguagesA( buffer, &len);
317         ok(((hr == E_INVALIDARG) && (len == 0)) ||
318             (((hr == S_OK) && !lstrcmpA(buffer, language)  && (len == lstrlenA(language))) ||
319             ((hr == S_OK) && !memcmp(buffer, language, len)) ||
320             ((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len)),
321             "=1_#%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
322
323         len = maxlen;
324         hr = pGetAcceptLanguagesA( NULL, &len);
325
326         /* w2k3 and below: E_FAIL and untouched len,
327            since w2k8: S_OK and needed size (excluding 0) */
328         ok( ((hr == S_OK) && (len == exactsize)) ||
329             ((hr == E_FAIL) && (len == maxlen)),
330             "NULL,max #%d: got 0x%x with %d and %s\n", i, hr, len, buffer);
331
332         i++;
333     }
334
335     /* without a value in the registry, a default language is returned */
336     RegDeleteValue(hroot, acceptlanguage);
337
338     len = maxlen;
339     memset(buffer, '#', maxlen);
340     buffer[maxlen] = 0;
341     hr = pGetAcceptLanguagesA( buffer, &len);
342     ok( ((hr == S_OK) && (len == lstrlenA(language))),
343         "max: got 0x%x with %d and %s (expected S_OK with %d and '%s'\n",
344         hr, len, buffer, lstrlenA(language), language);
345
346     len = 2;
347     memset(buffer, '#', maxlen);
348     buffer[maxlen] = 0;
349     hr = pGetAcceptLanguagesA( buffer, &len);
350     ok( (((hr == S_OK) || (hr == E_INVALIDARG)) && !memcmp(buffer, language, len)) ||
351         ((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len),
352         "=2: got 0x%x with %d and %s\n", hr, len, buffer);
353
354     len = 1;
355     memset(buffer, '#', maxlen);
356     buffer[maxlen] = 0;
357     hr = pGetAcceptLanguagesA( buffer, &len);
358     /* When the buffer is too small, win7_32 and w2k8_64 and above fail with
359        HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), other versions suceed
360        and return a partial 0 terminated result while other versions
361        fail with E_INVALIDARG and return a partial unterminated result */
362     ok( (((hr == S_OK) || (hr == E_INVALIDARG)) && !memcmp(buffer, language, len)) ||
363         ((hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) && !len),
364         "=1: got 0x%x with %d and %s\n", hr, len, buffer);
365
366     len = 0;
367     memset(buffer, '#', maxlen);
368     buffer[maxlen] = 0;
369     hr = pGetAcceptLanguagesA( buffer, &len);
370     /* w2k3 and below: E_FAIL, since w2k8: E_INVALIDARG */
371     ok((hr == E_FAIL) || (hr == E_INVALIDARG),
372         "got 0x%x (expected E_FAIL or E_INVALIDARG)\n", hr);
373
374     memset(buffer, '#', maxlen);
375     buffer[maxlen] = 0;
376     hr = pGetAcceptLanguagesA( buffer, NULL);
377     /* w2k3 and below: E_FAIL, since w2k8: E_INVALIDARG */
378     ok((hr == E_FAIL) || (hr == E_INVALIDARG),
379         "got 0x%x (expected E_FAIL or E_INVALIDARG)\n", hr);
380
381
382     hr = pGetAcceptLanguagesA( NULL, NULL);
383     /* w2k3 and below: E_FAIL, since w2k8: E_INVALIDARG */
384     ok((hr == E_FAIL) || (hr == E_INVALIDARG),
385         "got 0x%x (expected E_FAIL or E_INVALIDARG)\n", hr);
386
387 restore_original:
388     if (!res_query) {
389         len = lstrlenA(original);
390         lres = RegSetValueExA(hroot, acceptlanguage, 0, REG_SZ, (const BYTE *) original, len ? len + 1: 0);
391         ok(!lres, "RegSetValueEx(%s) failed: %d\n", original, lres);
392     }
393     else
394     {
395         RegDeleteValue(hroot, acceptlanguage);
396     }
397     RegCloseKey(hroot);
398 }
399
400 static void test_SHSearchMapInt(void)
401 {
402   int keys[8], values[8];
403   int i = 0;
404
405   if (!pSHSearchMapInt)
406     return;
407
408   memset(keys, 0, sizeof(keys));
409   memset(values, 0, sizeof(values));
410   keys[0] = 99; values[0] = 101;
411
412   /* NULL key/value lists crash native, so skip testing them */
413
414   /* 1 element */
415   i = pSHSearchMapInt(keys, values, 1, keys[0]);
416   ok(i == values[0], "Len 1, expected %d, got %d\n", values[0], i);
417
418   /* Key doesn't exist */
419   i = pSHSearchMapInt(keys, values, 1, 100);
420   ok(i == -1, "Len 1 - bad key, expected -1, got %d\n", i);
421
422   /* Len = 0 => not found */
423   i = pSHSearchMapInt(keys, values, 0, keys[0]);
424   ok(i == -1, "Len 1 - passed len 0, expected -1, got %d\n", i);
425
426   /* 2 elements, len = 1 */
427   keys[1] = 98; values[1] = 102;
428   i = pSHSearchMapInt(keys, values, 1, keys[1]);
429   ok(i == -1, "Len 1 - array len 2, expected -1, got %d\n", i);
430
431   /* 2 elements, len = 2 */
432   i = pSHSearchMapInt(keys, values, 2, keys[1]);
433   ok(i == values[1], "Len 2, expected %d, got %d\n", values[1], i);
434
435   /* Searches forward */
436   keys[2] = 99; values[2] = 103;
437   i = pSHSearchMapInt(keys, values, 3, keys[0]);
438   ok(i == values[0], "Len 3, expected %d, got %d\n", values[0], i);
439 }
440
441 static void test_alloc_shared(void)
442 {
443     DWORD procid;
444     HANDLE hmem;
445     int val;
446     int* p;
447     BOOL ret;
448
449     procid=GetCurrentProcessId();
450     hmem=pSHAllocShared(NULL,10,procid);
451     ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %u\n", GetLastError());
452     ret = pSHFreeShared(hmem, procid);
453     ok( ret, "SHFreeShared failed: %u\n", GetLastError());
454
455     val=0x12345678;
456     hmem=pSHAllocShared(&val,4,procid);
457     ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %u\n", GetLastError());
458
459     p=pSHLockShared(hmem,procid);
460     ok(p!=NULL,"SHLockShared failed: %u\n", GetLastError());
461     if (p!=NULL)
462         ok(*p==val,"Wrong value in shared memory: %d instead of %d\n",*p,val);
463     ret = pSHUnlockShared(p);
464     ok( ret, "SHUnlockShared failed: %u\n", GetLastError());
465
466     ret = pSHFreeShared(hmem, procid);
467     ok( ret, "SHFreeShared failed: %u\n", GetLastError());
468 }
469
470 static void test_fdsa(void)
471 {
472     typedef struct
473     {
474         DWORD num_items;       /* Number of elements inserted */
475         void *mem;             /* Ptr to array */
476         DWORD blocks_alloced;  /* Number of elements allocated */
477         BYTE inc;              /* Number of elements to grow by when we need to expand */
478         BYTE block_size;       /* Size in bytes of an element */
479         BYTE flags;            /* Flags */
480     } FDSA_info;
481
482     BOOL (WINAPI *pFDSA_Initialize)(DWORD block_size, DWORD inc, FDSA_info *info, void *mem,
483                                     DWORD init_blocks);
484     BOOL (WINAPI *pFDSA_Destroy)(FDSA_info *info);
485     DWORD (WINAPI *pFDSA_InsertItem)(FDSA_info *info, DWORD where, const void *block);
486     BOOL (WINAPI *pFDSA_DeleteItem)(FDSA_info *info, DWORD where);
487
488     FDSA_info info;
489     int block_size = 10, init_blocks = 4, inc = 2;
490     DWORD ret;
491     char *mem;
492
493     pFDSA_Initialize = (void *)GetProcAddress(hShlwapi, (LPSTR)208);
494     pFDSA_Destroy    = (void *)GetProcAddress(hShlwapi, (LPSTR)209);
495     pFDSA_InsertItem = (void *)GetProcAddress(hShlwapi, (LPSTR)210);
496     pFDSA_DeleteItem = (void *)GetProcAddress(hShlwapi, (LPSTR)211);
497
498     mem = HeapAlloc(GetProcessHeap(), 0, block_size * init_blocks);
499     memset(&info, 0, sizeof(info));
500
501     ok(pFDSA_Initialize(block_size, inc, &info, mem, init_blocks), "FDSA_Initialize rets FALSE\n");
502     ok(info.num_items == 0, "num_items = %d\n", info.num_items);
503     ok(info.mem == mem, "mem = %p\n", info.mem);
504     ok(info.blocks_alloced == init_blocks, "blocks_alloced = %d\n", info.blocks_alloced);
505     ok(info.inc == inc, "inc = %d\n", info.inc);
506     ok(info.block_size == block_size, "block_size = %d\n", info.block_size);
507     ok(info.flags == 0, "flags = %d\n", info.flags);
508
509     ret = pFDSA_InsertItem(&info, 1234, "1234567890");
510     ok(ret == 0, "ret = %d\n", ret);
511     ok(info.num_items == 1, "num_items = %d\n", info.num_items);
512     ok(info.mem == mem, "mem = %p\n", info.mem);
513     ok(info.blocks_alloced == init_blocks, "blocks_alloced = %d\n", info.blocks_alloced);
514     ok(info.inc == inc, "inc = %d\n", info.inc);
515     ok(info.block_size == block_size, "block_size = %d\n", info.block_size);
516     ok(info.flags == 0, "flags = %d\n", info.flags);
517
518     ret = pFDSA_InsertItem(&info, 1234, "abcdefghij");
519     ok(ret == 1, "ret = %d\n", ret);
520
521     ret = pFDSA_InsertItem(&info, 1, "klmnopqrst");
522     ok(ret == 1, "ret = %d\n", ret);
523
524     ret = pFDSA_InsertItem(&info, 0, "uvwxyzABCD");
525     ok(ret == 0, "ret = %d\n", ret);
526     ok(info.mem == mem, "mem = %p\n", info.mem);
527     ok(info.flags == 0, "flags = %d\n", info.flags);
528
529     /* This next InsertItem will cause shlwapi to allocate its own mem buffer */
530     ret = pFDSA_InsertItem(&info, 0, "EFGHIJKLMN");
531     ok(ret == 0, "ret = %d\n", ret);
532     ok(info.mem != mem, "mem = %p\n", info.mem);
533     ok(info.blocks_alloced == init_blocks + inc, "blocks_alloced = %d\n", info.blocks_alloced);
534     ok(info.flags == 0x1, "flags = %d\n", info.flags);
535
536     ok(!memcmp(info.mem, "EFGHIJKLMNuvwxyzABCD1234567890klmnopqrstabcdefghij", 50), "mem %s\n", (char*)info.mem);
537
538     ok(pFDSA_DeleteItem(&info, 2), "rets FALSE\n");
539     ok(info.mem != mem, "mem = %p\n", info.mem);
540     ok(info.blocks_alloced == init_blocks + inc, "blocks_alloced = %d\n", info.blocks_alloced);
541     ok(info.flags == 0x1, "flags = %d\n", info.flags);
542
543     ok(!memcmp(info.mem, "EFGHIJKLMNuvwxyzABCDklmnopqrstabcdefghij", 40), "mem %s\n", (char*)info.mem);
544
545     ok(pFDSA_DeleteItem(&info, 3), "rets FALSE\n");
546     ok(info.mem != mem, "mem = %p\n", info.mem);
547     ok(info.blocks_alloced == init_blocks + inc, "blocks_alloced = %d\n", info.blocks_alloced);
548     ok(info.flags == 0x1, "flags = %d\n", info.flags);
549
550     ok(!memcmp(info.mem, "EFGHIJKLMNuvwxyzABCDklmnopqrst", 30), "mem %s\n", (char*)info.mem);
551
552     ok(!pFDSA_DeleteItem(&info, 4), "does not ret FALSE\n");
553
554     /* As shlwapi has allocated memory internally, Destroy will ret FALSE */
555     ok(!pFDSA_Destroy(&info), "FDSA_Destroy does not ret FALSE\n");
556
557
558     /* When Initialize is called with inc = 0, set it to 1 */
559     ok(pFDSA_Initialize(block_size, 0, &info, mem, init_blocks), "FDSA_Initialize rets FALSE\n");
560     ok(info.inc == 1, "inc = %d\n", info.inc);
561
562     /* This time, because shlwapi hasn't had to allocate memory
563        internally, Destroy rets non-zero */
564     ok(pFDSA_Destroy(&info), "FDSA_Destroy rets FALSE\n");
565
566
567     HeapFree(GetProcessHeap(), 0, mem);
568 }
569
570
571 typedef struct SHELL_USER_SID {
572     SID_IDENTIFIER_AUTHORITY sidAuthority;
573     DWORD                    dwUserGroupID;
574     DWORD                    dwUserID;
575 } SHELL_USER_SID, *PSHELL_USER_SID;
576 typedef struct SHELL_USER_PERMISSION {
577     SHELL_USER_SID susID;
578     DWORD          dwAccessType;
579     BOOL           fInherit;
580     DWORD          dwAccessMask;
581     DWORD          dwInheritMask;
582     DWORD          dwInheritAccessMask;
583 } SHELL_USER_PERMISSION, *PSHELL_USER_PERMISSION;
584 static void test_GetShellSecurityDescriptor(void)
585 {
586     SHELL_USER_PERMISSION supCurrentUserFull = {
587         { {SECURITY_NULL_SID_AUTHORITY}, 0, 0 },
588         ACCESS_ALLOWED_ACE_TYPE, FALSE,
589         GENERIC_ALL, 0, 0 };
590 #define MY_INHERITANCE 0xBE /* invalid value to proof behavior */
591     SHELL_USER_PERMISSION supEveryoneDenied = {
592         { {SECURITY_WORLD_SID_AUTHORITY}, SECURITY_WORLD_RID, 0 },
593         ACCESS_DENIED_ACE_TYPE, TRUE,
594         GENERIC_WRITE, MY_INHERITANCE | 0xDEADBA00, GENERIC_READ };
595     PSHELL_USER_PERMISSION rgsup[2] = {
596         &supCurrentUserFull, &supEveryoneDenied,
597     };
598     SECURITY_DESCRIPTOR* psd;
599     SECURITY_DESCRIPTOR* (WINAPI*pGetShellSecurityDescriptor)(PSHELL_USER_PERMISSION*,int);
600     void *pChrCmpIW = GetProcAddress(hShlwapi, "ChrCmpIW");
601
602     pGetShellSecurityDescriptor=(void*)GetProcAddress(hShlwapi,(char*)475);
603
604     if(!pGetShellSecurityDescriptor)
605     {
606         win_skip("GetShellSecurityDescriptor not available\n");
607         return;
608     }
609
610     if(pChrCmpIW && pChrCmpIW == pGetShellSecurityDescriptor) /* win2k */
611     {
612         win_skip("Skipping for GetShellSecurityDescriptor, same ordinal used for ChrCmpIW\n");
613         return;
614     }
615
616     psd = pGetShellSecurityDescriptor(NULL, 2);
617     ok(psd==NULL ||
618        broken(psd==INVALID_HANDLE_VALUE), /* IE5 */
619        "GetShellSecurityDescriptor should fail\n");
620     psd = pGetShellSecurityDescriptor(rgsup, 0);
621     ok(psd==NULL, "GetShellSecurityDescriptor should fail, got %p\n", psd);
622
623     SetLastError(0xdeadbeef);
624     psd = pGetShellSecurityDescriptor(rgsup, 2);
625     if (psd == NULL && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
626     {
627         /* The previous calls to GetShellSecurityDescriptor don't set the last error */
628         win_skip("GetShellSecurityDescriptor is not implemented\n");
629         return;
630     }
631     if (psd == INVALID_HANDLE_VALUE)
632     {
633         win_skip("GetShellSecurityDescriptor is broken on IE5\n");
634         return;
635     }
636     ok(psd!=NULL, "GetShellSecurityDescriptor failed\n");
637     if (psd!=NULL)
638     {
639         BOOL bHasDacl = FALSE, bDefaulted, ret;
640         PACL pAcl;
641         DWORD dwRev;
642         SECURITY_DESCRIPTOR_CONTROL control;
643
644         ok(IsValidSecurityDescriptor(psd), "returned value is not valid SD\n");
645
646         ret = GetSecurityDescriptorControl(psd, &control, &dwRev);
647         ok(ret, "GetSecurityDescriptorControl failed with error %u\n", GetLastError());
648         ok(0 == (control & SE_SELF_RELATIVE), "SD should be absolute\n");
649
650         ret = GetSecurityDescriptorDacl(psd, &bHasDacl, &pAcl, &bDefaulted);
651         ok(ret, "GetSecurityDescriptorDacl failed with error %u\n", GetLastError());
652
653         ok(bHasDacl, "SD has no DACL\n");
654         if (bHasDacl)
655         {
656             ok(!bDefaulted, "DACL should not be defaulted\n");
657
658             ok(pAcl != NULL, "NULL DACL!\n");
659             if (pAcl != NULL)
660             {
661                 ACL_SIZE_INFORMATION asiSize;
662
663                 ok(IsValidAcl(pAcl), "DACL is not valid\n");
664
665                 ret = GetAclInformation(pAcl, &asiSize, sizeof(asiSize), AclSizeInformation);
666                 ok(ret, "GetAclInformation failed with error %u\n", GetLastError());
667
668                 ok(asiSize.AceCount == 3, "Incorrect number of ACEs: %d entries\n", asiSize.AceCount);
669                 if (asiSize.AceCount == 3)
670                 {
671                     ACCESS_ALLOWED_ACE *paaa; /* will use for DENIED too */
672
673                     ret = GetAce(pAcl, 0, (LPVOID*)&paaa);
674                     ok(ret, "GetAce failed with error %u\n", GetLastError());
675                     ok(paaa->Header.AceType == ACCESS_ALLOWED_ACE_TYPE, 
676                             "Invalid ACE type %d\n", paaa->Header.AceType); 
677                     ok(paaa->Header.AceFlags == 0, "Invalid ACE flags %x\n", paaa->Header.AceFlags);
678                     ok(paaa->Mask == GENERIC_ALL, "Invalid ACE mask %x\n", paaa->Mask);
679
680                     ret = GetAce(pAcl, 1, (LPVOID*)&paaa);
681                     ok(ret, "GetAce failed with error %u\n", GetLastError());
682                     ok(paaa->Header.AceType == ACCESS_DENIED_ACE_TYPE, 
683                             "Invalid ACE type %d\n", paaa->Header.AceType); 
684                     /* first one of two ACEs generated from inheritable entry - without inheritance */
685                     ok(paaa->Header.AceFlags == 0, "Invalid ACE flags %x\n", paaa->Header.AceFlags);
686                     ok(paaa->Mask == GENERIC_WRITE, "Invalid ACE mask %x\n", paaa->Mask);
687
688                     ret = GetAce(pAcl, 2, (LPVOID*)&paaa);
689                     ok(ret, "GetAce failed with error %u\n", GetLastError());
690                     ok(paaa->Header.AceType == ACCESS_DENIED_ACE_TYPE, 
691                             "Invalid ACE type %d\n", paaa->Header.AceType); 
692                     /* second ACE - with inheritance */
693                     ok(paaa->Header.AceFlags == MY_INHERITANCE,
694                             "Invalid ACE flags %x\n", paaa->Header.AceFlags);
695                     ok(paaa->Mask == GENERIC_READ, "Invalid ACE mask %x\n", paaa->Mask);
696                 }
697             }
698         }
699
700         LocalFree(psd);
701     }
702 }
703
704 static void test_SHPackDispParams(void)
705 {
706     DISPPARAMS params;
707     VARIANT vars[10];
708     HRESULT hres;
709
710     if(!pSHPackDispParams)
711         win_skip("SHPackSidpParams not available\n");
712
713     memset(&params, 0xc0, sizeof(params));
714     memset(vars, 0xc0, sizeof(vars));
715     hres = pSHPackDispParams(&params, vars, 1, VT_I4, 0xdeadbeef);
716     ok(hres == S_OK, "SHPackDispParams failed: %08x\n", hres);
717     ok(params.cArgs == 1, "params.cArgs = %d\n", params.cArgs);
718     ok(params.cNamedArgs == 0, "params.cNamedArgs = %d\n", params.cArgs);
719     ok(params.rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", params.rgdispidNamedArgs);
720     ok(params.rgvarg == vars, "params.rgvarg = %p\n", params.rgvarg);
721     ok(V_VT(vars) == VT_I4, "V_VT(var) = %d\n", V_VT(vars));
722     ok(V_I4(vars) == 0xdeadbeef, "failed %x\n", V_I4(vars));
723
724     memset(&params, 0xc0, sizeof(params));
725     hres = pSHPackDispParams(&params, NULL, 0, 0);
726     ok(hres == S_OK, "SHPackDispParams failed: %08x\n", hres);
727     ok(params.cArgs == 0, "params.cArgs = %d\n", params.cArgs);
728     ok(params.cNamedArgs == 0, "params.cNamedArgs = %d\n", params.cArgs);
729     ok(params.rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", params.rgdispidNamedArgs);
730     ok(params.rgvarg == NULL, "params.rgvarg = %p\n", params.rgvarg);
731
732     memset(vars, 0xc0, sizeof(vars));
733     memset(&params, 0xc0, sizeof(params));
734     hres = pSHPackDispParams(&params, vars, 4, VT_BSTR, (void*)0xdeadbeef, VT_EMPTY, 10,
735             VT_I4, 100, VT_DISPATCH, (void*)0xdeadbeef);
736     ok(hres == S_OK, "SHPackDispParams failed: %08x\n", hres);
737     ok(params.cArgs == 4, "params.cArgs = %d\n", params.cArgs);
738     ok(params.cNamedArgs == 0, "params.cNamedArgs = %d\n", params.cArgs);
739     ok(params.rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", params.rgdispidNamedArgs);
740     ok(params.rgvarg == vars, "params.rgvarg = %p\n", params.rgvarg);
741     ok(V_VT(vars) == VT_DISPATCH, "V_VT(vars[0]) = %x\n", V_VT(vars));
742     ok(V_I4(vars) == 0xdeadbeef, "V_I4(vars[0]) = %x\n", V_I4(vars));
743     ok(V_VT(vars+1) == VT_I4, "V_VT(vars[1]) = %d\n", V_VT(vars+1));
744     ok(V_I4(vars+1) == 100, "V_I4(vars[1]) = %x\n", V_I4(vars+1));
745     ok(V_VT(vars+2) == VT_I4, "V_VT(vars[2]) = %d\n", V_VT(vars+2));
746     ok(V_I4(vars+2) == 10, "V_I4(vars[2]) = %x\n", V_I4(vars+2));
747     ok(V_VT(vars+3) == VT_BSTR, "V_VT(vars[3]) = %d\n", V_VT(vars+3));
748     ok(V_BSTR(vars+3) == (void*)0xdeadbeef, "V_BSTR(vars[3]) = %p\n", V_BSTR(vars+3));
749 }
750
751 typedef struct _disp
752 {
753     IDispatch IDispatch_iface;
754     LONG   refCount;
755 } Disp;
756
757 static inline Disp *impl_from_IDispatch(IDispatch *iface)
758 {
759     return CONTAINING_RECORD(iface, Disp, IDispatch_iface);
760 }
761
762 typedef struct _contain
763 {
764     IConnectionPointContainer IConnectionPointContainer_iface;
765     LONG   refCount;
766
767     UINT  ptCount;
768     IConnectionPoint **pt;
769 } Contain;
770
771 static inline Contain *impl_from_IConnectionPointContainer(IConnectionPointContainer *iface)
772 {
773     return CONTAINING_RECORD(iface, Contain, IConnectionPointContainer_iface);
774 }
775
776 typedef struct _cntptn
777 {
778     IConnectionPoint IConnectionPoint_iface;
779     LONG refCount;
780
781     Contain *container;
782     GUID  id;
783     UINT  sinkCount;
784     IUnknown **sink;
785 } ConPt;
786
787 static inline ConPt *impl_from_IConnectionPoint(IConnectionPoint *iface)
788 {
789     return CONTAINING_RECORD(iface, ConPt, IConnectionPoint_iface);
790 }
791
792 typedef struct _enum
793 {
794     IEnumConnections IEnumConnections_iface;
795     LONG   refCount;
796
797     UINT idx;
798     ConPt *pt;
799 } EnumCon;
800
801 static inline EnumCon *impl_from_IEnumConnections(IEnumConnections *iface)
802 {
803     return CONTAINING_RECORD(iface, EnumCon, IEnumConnections_iface);
804 }
805
806 typedef struct _enumpt
807 {
808     IEnumConnectionPoints IEnumConnectionPoints_iface;
809     LONG   refCount;
810
811     int idx;
812     Contain *container;
813 } EnumPt;
814
815 static inline EnumPt *impl_from_IEnumConnectionPoints(IEnumConnectionPoints *iface)
816 {
817     return CONTAINING_RECORD(iface, EnumPt, IEnumConnectionPoints_iface);
818 }
819
820
821 static HRESULT WINAPI Disp_QueryInterface(
822         IDispatch* This,
823         REFIID riid,
824         void **ppvObject)
825 {
826     *ppvObject = NULL;
827
828     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch))
829     {
830         *ppvObject = This;
831     }
832
833     if (*ppvObject)
834     {
835         IUnknown_AddRef(This);
836         return S_OK;
837     }
838
839     trace("no interface\n");
840     return E_NOINTERFACE;
841 }
842
843 static ULONG WINAPI Disp_AddRef(IDispatch* This)
844 {
845     Disp *iface = impl_from_IDispatch(This);
846     return InterlockedIncrement(&iface->refCount);
847 }
848
849 static ULONG WINAPI Disp_Release(IDispatch* This)
850 {
851     Disp *iface = impl_from_IDispatch(This);
852     ULONG ret;
853
854     ret = InterlockedDecrement(&iface->refCount);
855     if (ret == 0)
856         HeapFree(GetProcessHeap(),0,This);
857     return ret;
858 }
859
860 static HRESULT WINAPI Disp_GetTypeInfoCount(
861         IDispatch* This,
862         UINT *pctinfo)
863 {
864     return ERROR_SUCCESS;
865 }
866
867 static HRESULT WINAPI Disp_GetTypeInfo(
868         IDispatch* This,
869         UINT iTInfo,
870         LCID lcid,
871         ITypeInfo **ppTInfo)
872 {
873     return ERROR_SUCCESS;
874 }
875
876 static HRESULT WINAPI Disp_GetIDsOfNames(
877         IDispatch* This,
878         REFIID riid,
879         LPOLESTR *rgszNames,
880         UINT cNames,
881         LCID lcid,
882         DISPID *rgDispId)
883 {
884     return ERROR_SUCCESS;
885 }
886
887 static HRESULT WINAPI Disp_Invoke(
888         IDispatch* This,
889         DISPID dispIdMember,
890         REFIID riid,
891         LCID lcid,
892         WORD wFlags,
893         DISPPARAMS *pDispParams,
894         VARIANT *pVarResult,
895         EXCEPINFO *pExcepInfo,
896         UINT *puArgErr)
897 {
898     trace("%p %x %p %x %x %p %p %p %p\n",This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
899
900     ok(dispIdMember == 0xa0 || dispIdMember == 0xa1, "Unknown dispIdMember\n");
901     ok(pDispParams != NULL, "Invoked with NULL pDispParams\n");
902     ok(wFlags == DISPATCH_METHOD, "Wrong flags %x\n",wFlags);
903     ok(lcid == 0,"Wrong lcid %x\n",lcid);
904     if (dispIdMember == 0xa0)
905     {
906         ok(pDispParams->cArgs == 0, "params.cArgs = %d\n", pDispParams->cArgs);
907         ok(pDispParams->cNamedArgs == 0, "params.cNamedArgs = %d\n", pDispParams->cArgs);
908         ok(pDispParams->rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", pDispParams->rgdispidNamedArgs);
909         ok(pDispParams->rgvarg == NULL, "params.rgvarg = %p\n", pDispParams->rgvarg);
910     }
911     else if (dispIdMember == 0xa1)
912     {
913         ok(pDispParams->cArgs == 2, "params.cArgs = %d\n", pDispParams->cArgs);
914         ok(pDispParams->cNamedArgs == 0, "params.cNamedArgs = %d\n", pDispParams->cArgs);
915         ok(pDispParams->rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", pDispParams->rgdispidNamedArgs);
916         ok(V_VT(pDispParams->rgvarg) == VT_BSTR, "V_VT(var) = %d\n", V_VT(pDispParams->rgvarg));
917         ok(V_I4(pDispParams->rgvarg) == 0xdeadcafe , "failed %p\n", V_BSTR(pDispParams->rgvarg));
918         ok(V_VT(pDispParams->rgvarg+1) == VT_I4, "V_VT(var) = %d\n", V_VT(pDispParams->rgvarg+1));
919         ok(V_I4(pDispParams->rgvarg+1) == 0xdeadbeef, "failed %x\n", V_I4(pDispParams->rgvarg+1));
920     }
921
922     return ERROR_SUCCESS;
923 }
924
925 static const IDispatchVtbl disp_vtbl = {
926     Disp_QueryInterface,
927     Disp_AddRef,
928     Disp_Release,
929
930     Disp_GetTypeInfoCount,
931     Disp_GetTypeInfo,
932     Disp_GetIDsOfNames,
933     Disp_Invoke
934 };
935
936 static HRESULT WINAPI Enum_QueryInterface(
937         IEnumConnections* This,
938         REFIID riid,
939         void **ppvObject)
940 {
941     *ppvObject = NULL;
942
943     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IEnumConnections))
944     {
945         *ppvObject = This;
946     }
947
948     if (*ppvObject)
949     {
950         IUnknown_AddRef(This);
951         return S_OK;
952     }
953
954     trace("no interface\n");
955     return E_NOINTERFACE;
956 }
957
958 static ULONG WINAPI Enum_AddRef(IEnumConnections* This)
959 {
960     EnumCon *iface = impl_from_IEnumConnections(This);
961     return InterlockedIncrement(&iface->refCount);
962 }
963
964 static ULONG WINAPI Enum_Release(IEnumConnections* This)
965 {
966     EnumCon *iface = impl_from_IEnumConnections(This);
967     ULONG ret;
968
969     ret = InterlockedDecrement(&iface->refCount);
970     if (ret == 0)
971         HeapFree(GetProcessHeap(),0,This);
972     return ret;
973 }
974
975 static HRESULT WINAPI Enum_Next(
976         IEnumConnections* This,
977         ULONG cConnections,
978         LPCONNECTDATA rgcd,
979         ULONG *pcFetched)
980 {
981     EnumCon *iface = impl_from_IEnumConnections(This);
982
983     if (cConnections > 0 && iface->idx < iface->pt->sinkCount)
984     {
985         rgcd->pUnk = iface->pt->sink[iface->idx];
986         IUnknown_AddRef(iface->pt->sink[iface->idx]);
987         rgcd->dwCookie=0xff;
988         if (pcFetched)
989             *pcFetched = 1;
990         iface->idx++;
991         return S_OK;
992     }
993
994     return E_FAIL;
995 }
996
997 static HRESULT WINAPI Enum_Skip(
998         IEnumConnections* This,
999         ULONG cConnections)
1000 {
1001     return E_FAIL;
1002 }
1003
1004 static HRESULT WINAPI Enum_Reset(
1005         IEnumConnections* This)
1006 {
1007     return E_FAIL;
1008 }
1009
1010 static HRESULT WINAPI Enum_Clone(
1011         IEnumConnections* This,
1012         IEnumConnections **ppEnum)
1013 {
1014     return E_FAIL;
1015 }
1016
1017 static const IEnumConnectionsVtbl enum_vtbl = {
1018
1019     Enum_QueryInterface,
1020     Enum_AddRef,
1021     Enum_Release,
1022     Enum_Next,
1023     Enum_Skip,
1024     Enum_Reset,
1025     Enum_Clone
1026 };
1027
1028 static HRESULT WINAPI ConPt_QueryInterface(
1029         IConnectionPoint* This,
1030         REFIID riid,
1031         void **ppvObject)
1032 {
1033     *ppvObject = NULL;
1034
1035     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IConnectionPoint))
1036     {
1037         *ppvObject = This;
1038     }
1039
1040     if (*ppvObject)
1041     {
1042         IUnknown_AddRef(This);
1043         return S_OK;
1044     }
1045
1046     trace("no interface\n");
1047     return E_NOINTERFACE;
1048 }
1049
1050 static ULONG WINAPI ConPt_AddRef(
1051         IConnectionPoint* This)
1052 {
1053     ConPt *iface = impl_from_IConnectionPoint(This);
1054     return InterlockedIncrement(&iface->refCount);
1055 }
1056
1057 static ULONG WINAPI ConPt_Release(
1058         IConnectionPoint* This)
1059 {
1060     ConPt *iface = impl_from_IConnectionPoint(This);
1061     ULONG ret;
1062
1063     ret = InterlockedDecrement(&iface->refCount);
1064     if (ret == 0)
1065     {
1066         if (iface->sinkCount > 0)
1067         {
1068             int i;
1069             for (i = 0; i < iface->sinkCount; i++)
1070             {
1071                 if (iface->sink[i])
1072                     IUnknown_Release(iface->sink[i]);
1073             }
1074             HeapFree(GetProcessHeap(),0,iface->sink);
1075         }
1076         HeapFree(GetProcessHeap(),0,This);
1077     }
1078     return ret;
1079 }
1080
1081 static HRESULT WINAPI ConPt_GetConnectionInterface(
1082         IConnectionPoint* This,
1083         IID *pIID)
1084 {
1085     static int i = 0;
1086     ConPt *iface = impl_from_IConnectionPoint(This);
1087     if (i==0)
1088     {
1089         i++;
1090         return E_FAIL;
1091     }
1092     else
1093         memcpy(pIID,&iface->id,sizeof(GUID));
1094     return S_OK;
1095 }
1096
1097 static HRESULT WINAPI ConPt_GetConnectionPointContainer(
1098         IConnectionPoint* This,
1099         IConnectionPointContainer **ppCPC)
1100 {
1101     ConPt *iface = impl_from_IConnectionPoint(This);
1102
1103     *ppCPC = &iface->container->IConnectionPointContainer_iface;
1104     return S_OK;
1105 }
1106
1107 static HRESULT WINAPI ConPt_Advise(
1108         IConnectionPoint* This,
1109         IUnknown *pUnkSink,
1110         DWORD *pdwCookie)
1111 {
1112     ConPt *iface = impl_from_IConnectionPoint(This);
1113
1114     if (iface->sinkCount == 0)
1115         iface->sink = HeapAlloc(GetProcessHeap(),0,sizeof(IUnknown*));
1116     else
1117         iface->sink = HeapReAlloc(GetProcessHeap(),0,iface->sink,sizeof(IUnknown*)*(iface->sinkCount+1));
1118     iface->sink[iface->sinkCount] = pUnkSink;
1119     IUnknown_AddRef(pUnkSink);
1120     iface->sinkCount++;
1121     *pdwCookie = iface->sinkCount;
1122     return S_OK;
1123 }
1124
1125 static HRESULT WINAPI ConPt_Unadvise(
1126         IConnectionPoint* This,
1127         DWORD dwCookie)
1128 {
1129     ConPt *iface = impl_from_IConnectionPoint(This);
1130
1131     if (dwCookie > iface->sinkCount)
1132         return E_FAIL;
1133     else
1134     {
1135         IUnknown_Release(iface->sink[dwCookie-1]);
1136         iface->sink[dwCookie-1] = NULL;
1137     }
1138     return S_OK;
1139 }
1140
1141 static HRESULT WINAPI ConPt_EnumConnections(
1142         IConnectionPoint* This,
1143         IEnumConnections **ppEnum)
1144 {
1145     EnumCon *ec;
1146
1147     ec = HeapAlloc(GetProcessHeap(),0,sizeof(EnumCon));
1148     ec->IEnumConnections_iface.lpVtbl = &enum_vtbl;
1149     ec->refCount = 1;
1150     ec->pt = impl_from_IConnectionPoint(This);
1151     ec->idx = 0;
1152     *ppEnum = &ec->IEnumConnections_iface;
1153
1154     return S_OK;
1155 }
1156
1157 static const IConnectionPointVtbl point_vtbl = {
1158     ConPt_QueryInterface,
1159     ConPt_AddRef,
1160     ConPt_Release,
1161
1162     ConPt_GetConnectionInterface,
1163     ConPt_GetConnectionPointContainer,
1164     ConPt_Advise,
1165     ConPt_Unadvise,
1166     ConPt_EnumConnections
1167 };
1168
1169 static HRESULT WINAPI EnumPt_QueryInterface(
1170         IEnumConnectionPoints* This,
1171         REFIID riid,
1172         void **ppvObject)
1173 {
1174     *ppvObject = NULL;
1175
1176     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IEnumConnectionPoints))
1177     {
1178         *ppvObject = This;
1179     }
1180
1181     if (*ppvObject)
1182     {
1183         IUnknown_AddRef(This);
1184         return S_OK;
1185     }
1186
1187     trace("no interface\n");
1188     return E_NOINTERFACE;
1189 }
1190
1191 static ULONG WINAPI EnumPt_AddRef(IEnumConnectionPoints* This)
1192 {
1193     EnumPt *iface = impl_from_IEnumConnectionPoints(This);
1194     return InterlockedIncrement(&iface->refCount);
1195 }
1196
1197 static ULONG WINAPI EnumPt_Release(IEnumConnectionPoints* This)
1198 {
1199     EnumPt *iface = impl_from_IEnumConnectionPoints(This);
1200     ULONG ret;
1201
1202     ret = InterlockedDecrement(&iface->refCount);
1203     if (ret == 0)
1204         HeapFree(GetProcessHeap(),0,This);
1205     return ret;
1206 }
1207
1208 static HRESULT WINAPI EnumPt_Next(
1209         IEnumConnectionPoints* This,
1210         ULONG cConnections,
1211         IConnectionPoint **rgcd,
1212         ULONG *pcFetched)
1213 {
1214     EnumPt *iface = impl_from_IEnumConnectionPoints(This);
1215
1216     if (cConnections > 0 && iface->idx < iface->container->ptCount)
1217     {
1218         *rgcd = iface->container->pt[iface->idx];
1219         IUnknown_AddRef(iface->container->pt[iface->idx]);
1220         if (pcFetched)
1221             *pcFetched = 1;
1222         iface->idx++;
1223         return S_OK;
1224     }
1225
1226     return E_FAIL;
1227 }
1228
1229 static HRESULT WINAPI EnumPt_Skip(
1230         IEnumConnectionPoints* This,
1231         ULONG cConnections)
1232 {
1233     return E_FAIL;
1234 }
1235
1236 static HRESULT WINAPI EnumPt_Reset(
1237         IEnumConnectionPoints* This)
1238 {
1239     return E_FAIL;
1240 }
1241
1242 static HRESULT WINAPI EnumPt_Clone(
1243         IEnumConnectionPoints* This,
1244         IEnumConnectionPoints **ppEnumPt)
1245 {
1246     return E_FAIL;
1247 }
1248
1249 static const IEnumConnectionPointsVtbl enumpt_vtbl = {
1250
1251     EnumPt_QueryInterface,
1252     EnumPt_AddRef,
1253     EnumPt_Release,
1254     EnumPt_Next,
1255     EnumPt_Skip,
1256     EnumPt_Reset,
1257     EnumPt_Clone
1258 };
1259
1260 static HRESULT WINAPI Contain_QueryInterface(
1261         IConnectionPointContainer* This,
1262         REFIID riid,
1263         void **ppvObject)
1264 {
1265     *ppvObject = NULL;
1266
1267     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IConnectionPointContainer))
1268     {
1269         *ppvObject = This;
1270     }
1271
1272     if (*ppvObject)
1273     {
1274         IUnknown_AddRef(This);
1275         return S_OK;
1276     }
1277
1278     trace("no interface\n");
1279     return E_NOINTERFACE;
1280 }
1281
1282 static ULONG WINAPI Contain_AddRef(
1283         IConnectionPointContainer* This)
1284 {
1285     Contain *iface = impl_from_IConnectionPointContainer(This);
1286     return InterlockedIncrement(&iface->refCount);
1287 }
1288
1289 static ULONG WINAPI Contain_Release(
1290         IConnectionPointContainer* This)
1291 {
1292     Contain *iface = impl_from_IConnectionPointContainer(This);
1293     ULONG ret;
1294
1295     ret = InterlockedDecrement(&iface->refCount);
1296     if (ret == 0)
1297     {
1298         if (iface->ptCount > 0)
1299         {
1300             int i;
1301             for (i = 0; i < iface->ptCount; i++)
1302                 IUnknown_Release(iface->pt[i]);
1303             HeapFree(GetProcessHeap(),0,iface->pt);
1304         }
1305         HeapFree(GetProcessHeap(),0,This);
1306     }
1307     return ret;
1308 }
1309
1310 static HRESULT WINAPI Contain_EnumConnectionPoints(
1311         IConnectionPointContainer* This,
1312         IEnumConnectionPoints **ppEnum)
1313 {
1314     EnumPt *ec;
1315
1316     ec = HeapAlloc(GetProcessHeap(),0,sizeof(EnumPt));
1317     ec->IEnumConnectionPoints_iface.lpVtbl = &enumpt_vtbl;
1318     ec->refCount = 1;
1319     ec->idx= 0;
1320     ec->container = impl_from_IConnectionPointContainer(This);
1321     *ppEnum = &ec->IEnumConnectionPoints_iface;
1322
1323     return S_OK;
1324 }
1325
1326 static HRESULT WINAPI Contain_FindConnectionPoint(
1327         IConnectionPointContainer* This,
1328         REFIID riid,
1329         IConnectionPoint **ppCP)
1330 {
1331     Contain *iface = impl_from_IConnectionPointContainer(This);
1332     ConPt *pt;
1333
1334     if (!IsEqualIID(riid, &IID_NULL) || iface->ptCount ==0)
1335     {
1336         pt = HeapAlloc(GetProcessHeap(),0,sizeof(ConPt));
1337         pt->IConnectionPoint_iface.lpVtbl = &point_vtbl;
1338         pt->refCount = 1;
1339         pt->sinkCount = 0;
1340         pt->sink = NULL;
1341         pt->container = iface;
1342         pt->id = IID_IDispatch;
1343
1344         if (iface->ptCount == 0)
1345             iface->pt =HeapAlloc(GetProcessHeap(),0,sizeof(IUnknown*));
1346         else
1347             iface->pt = HeapReAlloc(GetProcessHeap(),0,iface->pt,sizeof(IUnknown*)*(iface->ptCount+1));
1348         iface->pt[iface->ptCount] = &pt->IConnectionPoint_iface;
1349         iface->ptCount++;
1350
1351         *ppCP = &pt->IConnectionPoint_iface;
1352     }
1353     else
1354     {
1355         *ppCP = iface->pt[0];
1356         IUnknown_AddRef((IUnknown*)*ppCP);
1357     }
1358
1359     return S_OK;
1360 }
1361
1362 static const IConnectionPointContainerVtbl contain_vtbl = {
1363     Contain_QueryInterface,
1364     Contain_AddRef,
1365     Contain_Release,
1366
1367     Contain_EnumConnectionPoints,
1368     Contain_FindConnectionPoint
1369 };
1370
1371 static void test_IConnectionPoint(void)
1372 {
1373     HRESULT rc;
1374     ULONG ref;
1375     IConnectionPoint *point;
1376     Contain *container;
1377     Disp *dispatch;
1378     DWORD cookie = 0xffffffff;
1379     DISPPARAMS params;
1380     VARIANT vars[10];
1381
1382     if (!pIConnectionPoint_SimpleInvoke || !pConnectToConnectionPoint)
1383     {
1384         win_skip("IConnectionPoint Apis not present\n");
1385         return;
1386     }
1387
1388     container = HeapAlloc(GetProcessHeap(),0,sizeof(Contain));
1389     container->IConnectionPointContainer_iface.lpVtbl = &contain_vtbl;
1390     container->refCount = 1;
1391     container->ptCount = 0;
1392     container->pt = NULL;
1393
1394     dispatch = HeapAlloc(GetProcessHeap(),0,sizeof(Disp));
1395     dispatch->IDispatch_iface.lpVtbl = &disp_vtbl;
1396     dispatch->refCount = 1;
1397
1398     rc = pConnectToConnectionPoint((IUnknown*)dispatch, &IID_NULL, TRUE, (IUnknown*)container, &cookie, &point);
1399     ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
1400     ok(point != NULL, "returned ConnectionPoint is NULL\n");
1401     ok(cookie != 0xffffffff, "invalid cookie returned\n");
1402
1403     rc = pIConnectionPoint_SimpleInvoke(point,0xa0,NULL);
1404     ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
1405
1406     if (pSHPackDispParams)
1407     {
1408         memset(&params, 0xc0, sizeof(params));
1409         memset(vars, 0xc0, sizeof(vars));
1410         rc = pSHPackDispParams(&params, vars, 2, VT_I4, 0xdeadbeef, VT_BSTR, 0xdeadcafe);
1411         ok(rc == S_OK, "SHPackDispParams failed: %08x\n", rc);
1412
1413         rc = pIConnectionPoint_SimpleInvoke(point,0xa1,&params);
1414         ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
1415     }
1416     else
1417         win_skip("pSHPackDispParams not present\n");
1418
1419     rc = pConnectToConnectionPoint(NULL, &IID_NULL, FALSE, (IUnknown*)container, &cookie, NULL);
1420     ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
1421
1422 /* MSDN says this should be required but it crashs on XP
1423     IUnknown_Release(point);
1424 */
1425     ref = IUnknown_Release((IUnknown*)container);
1426     ok(ref == 0, "leftover IConnectionPointContainer reference %i\n",ref);
1427     ref = IUnknown_Release((IUnknown*)dispatch);
1428     ok(ref == 0, "leftover IDispatch reference %i\n",ref);
1429 }
1430
1431 typedef struct _propbag
1432 {
1433     IPropertyBag IPropertyBag_iface;
1434     LONG   refCount;
1435
1436 } PropBag;
1437
1438 static inline PropBag *impl_from_IPropertyBag(IPropertyBag *iface)
1439 {
1440     return CONTAINING_RECORD(iface, PropBag, IPropertyBag_iface);
1441 }
1442
1443
1444 static HRESULT WINAPI Prop_QueryInterface(
1445         IPropertyBag* This,
1446         REFIID riid,
1447         void **ppvObject)
1448 {
1449     *ppvObject = NULL;
1450
1451     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPropertyBag))
1452     {
1453         *ppvObject = This;
1454     }
1455
1456     if (*ppvObject)
1457     {
1458         IUnknown_AddRef(This);
1459         return S_OK;
1460     }
1461
1462     trace("no interface\n");
1463     return E_NOINTERFACE;
1464 }
1465
1466 static ULONG WINAPI Prop_AddRef(
1467         IPropertyBag* This)
1468 {
1469     PropBag *iface = impl_from_IPropertyBag(This);
1470     return InterlockedIncrement(&iface->refCount);
1471 }
1472
1473 static ULONG WINAPI Prop_Release(
1474         IPropertyBag* This)
1475 {
1476     PropBag *iface = impl_from_IPropertyBag(This);
1477     ULONG ret;
1478
1479     ret = InterlockedDecrement(&iface->refCount);
1480     if (ret == 0)
1481         HeapFree(GetProcessHeap(),0,This);
1482     return ret;
1483 }
1484
1485 static HRESULT WINAPI Prop_Read(
1486         IPropertyBag* This,
1487         LPCOLESTR pszPropName,
1488         VARIANT *pVar,
1489         IErrorLog *pErrorLog)
1490 {
1491     V_VT(pVar) = VT_BLOB|VT_BYREF;
1492     V_BYREF(pVar) = (LPVOID)0xdeadcafe;
1493     return S_OK;
1494 }
1495
1496 static HRESULT WINAPI Prop_Write(
1497         IPropertyBag* This,
1498         LPCOLESTR pszPropName,
1499         VARIANT *pVar)
1500 {
1501     return S_OK;
1502 }
1503
1504
1505 static const IPropertyBagVtbl prop_vtbl = {
1506     Prop_QueryInterface,
1507     Prop_AddRef,
1508     Prop_Release,
1509
1510     Prop_Read,
1511     Prop_Write
1512 };
1513
1514 static void test_SHPropertyBag_ReadLONG(void)
1515 {
1516     PropBag *pb;
1517     HRESULT rc;
1518     LONG out;
1519     static const WCHAR szName1[] = {'n','a','m','e','1',0};
1520
1521     if (!pSHPropertyBag_ReadLONG)
1522     {
1523         win_skip("SHPropertyBag_ReadLONG not present\n");
1524         return;
1525     }
1526
1527     pb = HeapAlloc(GetProcessHeap(),0,sizeof(PropBag));
1528     pb->refCount = 1;
1529     pb->IPropertyBag_iface.lpVtbl = &prop_vtbl;
1530
1531     out = 0xfeedface;
1532     rc = pSHPropertyBag_ReadLONG(NULL, szName1, &out);
1533     ok(rc == E_INVALIDARG || broken(rc == 0), "incorrect return %x\n",rc);
1534     ok(out == 0xfeedface, "value should not have changed\n");
1535     rc = pSHPropertyBag_ReadLONG(&pb->IPropertyBag_iface, NULL, &out);
1536     ok(rc == E_INVALIDARG || broken(rc == 0) || broken(rc == 1), "incorrect return %x\n",rc);
1537     ok(out == 0xfeedface, "value should not have changed\n");
1538     rc = pSHPropertyBag_ReadLONG(&pb->IPropertyBag_iface, szName1, NULL);
1539     ok(rc == E_INVALIDARG || broken(rc == 0) || broken(rc == 1), "incorrect return %x\n",rc);
1540     ok(out == 0xfeedface, "value should not have changed\n");
1541     rc = pSHPropertyBag_ReadLONG(&pb->IPropertyBag_iface, szName1, &out);
1542     ok(rc == DISP_E_BADVARTYPE || broken(rc == 0) || broken(rc == 1), "incorrect return %x\n",rc);
1543     ok(out == 0xfeedface  || broken(out == 0xfeedfa00), "value should not have changed %x\n",out);
1544     IUnknown_Release((IUnknown*)pb);
1545 }
1546
1547
1548
1549 static void test_SHSetWindowBits(void)
1550 {
1551     HWND hwnd;
1552     DWORD style, styleold;
1553     WNDCLASSA clsA;
1554
1555     if(!pSHSetWindowBits)
1556     {
1557         win_skip("SHSetWindowBits is not available\n");
1558         return;
1559     }
1560
1561     clsA.style = 0;
1562     clsA.lpfnWndProc = DefWindowProcA;
1563     clsA.cbClsExtra = 0;
1564     clsA.cbWndExtra = 0;
1565     clsA.hInstance = GetModuleHandleA(NULL);
1566     clsA.hIcon = 0;
1567     clsA.hCursor = LoadCursorA(0, IDC_ARROW);
1568     clsA.hbrBackground = NULL;
1569     clsA.lpszMenuName = NULL;
1570     clsA.lpszClassName = "Shlwapi test class";
1571     RegisterClassA(&clsA);
1572
1573     hwnd = CreateWindowA("Shlwapi test class", "Test", WS_VISIBLE, 0, 0, 100, 100,
1574                           NULL, NULL, GetModuleHandle(NULL), 0);
1575     ok(IsWindow(hwnd), "failed to create window\n");
1576
1577     /* null window */
1578     SetLastError(0xdeadbeef);
1579     style = pSHSetWindowBits(NULL, GWL_STYLE, 0, 0);
1580     ok(style == 0, "expected 0 retval, got %d\n", style);
1581     ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE ||
1582         broken(GetLastError() == 0xdeadbeef), /* Win9x/WinMe */
1583         "expected ERROR_INVALID_WINDOW_HANDLE, got %d\n", GetLastError());
1584
1585     /* zero mask, zero flags */
1586     styleold = GetWindowLongA(hwnd, GWL_STYLE);
1587     style = pSHSetWindowBits(hwnd, GWL_STYLE, 0, 0);
1588     ok(styleold == style, "expected old style\n");
1589     ok(styleold == GetWindowLongA(hwnd, GWL_STYLE), "expected to keep old style\n");
1590
1591     /* test mask */
1592     styleold = GetWindowLongA(hwnd, GWL_STYLE);
1593     ok(styleold & WS_VISIBLE, "expected WS_VISIBLE\n");
1594     style = pSHSetWindowBits(hwnd, GWL_STYLE, WS_VISIBLE, 0);
1595
1596     ok(style == styleold, "expected previous style, got %x\n", style);
1597     ok((GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE) == 0, "expected updated style\n");
1598
1599     /* test mask, unset style bit used */
1600     styleold = GetWindowLongA(hwnd, GWL_STYLE);
1601     style = pSHSetWindowBits(hwnd, GWL_STYLE, WS_VISIBLE, 0);
1602     ok(style == styleold, "expected previous style, got %x\n", style);
1603     ok(styleold == GetWindowLongA(hwnd, GWL_STYLE), "expected to keep old style\n");
1604
1605     /* set back with flags */
1606     styleold = GetWindowLongA(hwnd, GWL_STYLE);
1607     style = pSHSetWindowBits(hwnd, GWL_STYLE, WS_VISIBLE, WS_VISIBLE);
1608     ok(style == styleold, "expected previous style, got %x\n", style);
1609     ok(GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE, "expected updated style\n");
1610
1611     /* reset and try to set without a mask */
1612     pSHSetWindowBits(hwnd, GWL_STYLE, WS_VISIBLE, 0);
1613     ok((GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE) == 0, "expected updated style\n");
1614     styleold = GetWindowLongA(hwnd, GWL_STYLE);
1615     style = pSHSetWindowBits(hwnd, GWL_STYLE, 0, WS_VISIBLE);
1616     ok(style == styleold, "expected previous style, got %x\n", style);
1617     ok((GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE) == 0, "expected updated style\n");
1618
1619     DestroyWindow(hwnd);
1620
1621     UnregisterClassA("Shlwapi test class", GetModuleHandleA(NULL));
1622 }
1623
1624 static void test_SHFormatDateTimeA(void)
1625 {
1626     FILETIME UNALIGNED filetime;
1627     CHAR buff[100], buff2[100], buff3[100];
1628     SYSTEMTIME st;
1629     DWORD flags;
1630     INT ret;
1631
1632     if(!pSHFormatDateTimeA)
1633     {
1634         win_skip("pSHFormatDateTimeA isn't available\n");
1635         return;
1636     }
1637
1638 if (0)
1639 {
1640     /* crashes on native */
1641     pSHFormatDateTimeA(NULL, NULL, NULL, 0);
1642 }
1643
1644     GetLocalTime(&st);
1645     SystemTimeToFileTime(&st, &filetime);
1646     /* SHFormatDateTime expects input as utc */
1647     LocalFileTimeToFileTime(&filetime, &filetime);
1648
1649     /* no way to get required buffer length here */
1650     SetLastError(0xdeadbeef);
1651     ret = pSHFormatDateTimeA(&filetime, NULL, NULL, 0);
1652     ok(ret == 0, "got %d\n", ret);
1653     ok(GetLastError() == 0xdeadbeef || broken(GetLastError() == ERROR_SUCCESS /* Win7 */),
1654         "expected 0xdeadbeef, got %d\n", GetLastError());
1655
1656     SetLastError(0xdeadbeef);
1657     buff[0] = 'a'; buff[1] = 0;
1658     ret = pSHFormatDateTimeA(&filetime, NULL, buff, 0);
1659     ok(ret == 0, "got %d\n", ret);
1660     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1661     ok(buff[0] == 'a', "expected same string, got %s\n", buff);
1662
1663     /* flags needs to have FDTF_NOAUTOREADINGORDER for these tests to succeed on Vista+ */
1664
1665     /* all combinations documented as invalid succeeded */
1666     flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTTIME | FDTF_LONGTIME;
1667     SetLastError(0xdeadbeef);
1668     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1669     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1670     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1671
1672     flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTDATE | FDTF_LONGDATE;
1673     SetLastError(0xdeadbeef);
1674     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1675     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1676     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1677
1678     flags =  FDTF_SHORTDATE | FDTF_LTRDATE | FDTF_RTLDATE;
1679     SetLastError(0xdeadbeef);
1680     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1681     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1682     ok(GetLastError() == 0xdeadbeef ||
1683         broken(GetLastError() == ERROR_INVALID_FLAGS), /* Win9x/WinMe */
1684         "expected 0xdeadbeef, got %d\n", GetLastError());
1685
1686     /* now check returned strings */
1687     flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTTIME;
1688     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1689     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1690     ret = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff2, sizeof(buff2));
1691     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1692     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1693
1694     flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGTIME;
1695     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1696     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1697     ret = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buff2, sizeof(buff2));
1698     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1699     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1700
1701     /* both time flags */
1702     flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGTIME | FDTF_SHORTTIME;
1703     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1704     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1705     ret = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buff2, sizeof(buff2));
1706     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1707     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1708
1709     flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTDATE;
1710     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1711     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1712     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2));
1713     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1714     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1715
1716     flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGDATE;
1717     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1718     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1719     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2));
1720     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1721     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1722
1723     /* both date flags */
1724     flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGDATE | FDTF_SHORTDATE;
1725     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1726     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1727     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2));
1728     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1729     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1730
1731     /* various combinations of date/time flags */
1732     flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGDATE | FDTF_SHORTTIME;
1733     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1734     ok(ret == lstrlenA(buff)+1, "got %d, length %d\n", ret, lstrlenA(buff)+1);
1735     ret = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3));
1736     ok(ret == lstrlenA(buff3)+1, "got %d\n", ret);
1737     ok(lstrcmpA(buff3, buff + lstrlenA(buff) - lstrlenA(buff3)) == 0,
1738        "expected (%s), got (%s) for time part\n",
1739        buff3, buff + lstrlenA(buff) - lstrlenA(buff3));
1740     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2));
1741     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1742     buff[lstrlenA(buff2)] = '\0';
1743     ok(lstrcmpA(buff2, buff) == 0, "expected (%s) got (%s) for date part\n",
1744        buff2, buff);
1745
1746     flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGDATE | FDTF_LONGTIME;
1747     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1748     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1749     ret = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3));
1750     ok(ret == lstrlenA(buff3)+1, "got %d\n", ret);
1751     ok(lstrcmpA(buff3, buff + lstrlenA(buff) - lstrlenA(buff3)) == 0,
1752        "expected (%s), got (%s) for time part\n",
1753        buff3, buff + lstrlenA(buff) - lstrlenA(buff3));
1754     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2));
1755     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1756     buff[lstrlenA(buff2)] = '\0';
1757     ok(lstrcmpA(buff2, buff) == 0, "expected (%s) got (%s) for date part\n",
1758        buff2, buff);
1759
1760     flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTDATE | FDTF_SHORTTIME;
1761     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1762     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1763     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2));
1764     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1765     strcat(buff2, " ");
1766     ret = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3));
1767     ok(ret == lstrlenA(buff3)+1, "got %d\n", ret);
1768     strcat(buff2, buff3);
1769     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1770
1771     flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTDATE | FDTF_LONGTIME;
1772     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1773     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1774     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2));
1775     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1776     strcat(buff2, " ");
1777     ret = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3));
1778     ok(ret == lstrlenA(buff3)+1, "got %d\n", ret);
1779     strcat(buff2, buff3);
1780     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1781 }
1782
1783 static void test_SHFormatDateTimeW(void)
1784 {
1785     FILETIME UNALIGNED filetime;
1786     WCHAR buff[100], buff2[100], buff3[100], *p1, *p2;
1787     SYSTEMTIME st;
1788     DWORD flags;
1789     INT ret;
1790     static const WCHAR spaceW[] = {' ',0};
1791 #define UNICODE_LTR_MARK 0x200e
1792
1793     if(!pSHFormatDateTimeW)
1794     {
1795         win_skip("pSHFormatDateTimeW isn't available\n");
1796         return;
1797     }
1798
1799 if (0)
1800 {
1801     /* crashes on native */
1802     pSHFormatDateTimeW(NULL, NULL, NULL, 0);
1803 }
1804
1805     GetLocalTime(&st);
1806     SystemTimeToFileTime(&st, &filetime);
1807     /* SHFormatDateTime expects input as utc */
1808     LocalFileTimeToFileTime(&filetime, &filetime);
1809
1810     /* no way to get required buffer length here */
1811     SetLastError(0xdeadbeef);
1812     ret = pSHFormatDateTimeW(&filetime, NULL, NULL, 0);
1813     ok(ret == 0, "expected 0, got %d\n", ret);
1814     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1815
1816     SetLastError(0xdeadbeef);
1817     buff[0] = 'a'; buff[1] = 0;
1818     ret = pSHFormatDateTimeW(&filetime, NULL, buff, 0);
1819     ok(ret == 0, "expected 0, got %d\n", ret);
1820     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1821     ok(buff[0] == 'a', "expected same string\n");
1822
1823     /* all combinations documented as invalid succeeded */
1824     flags = FDTF_SHORTTIME | FDTF_LONGTIME;
1825     SetLastError(0xdeadbeef);
1826     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1827     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1828        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1829     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1830
1831     flags = FDTF_SHORTDATE | FDTF_LONGDATE;
1832     SetLastError(0xdeadbeef);
1833     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1834     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1835        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1836     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1837
1838     flags = FDTF_SHORTDATE | FDTF_LTRDATE | FDTF_RTLDATE;
1839     SetLastError(0xdeadbeef);
1840     buff[0] = 0; /* NT4 doesn't clear the buffer on failure */
1841     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1842     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1843        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1844     ok(GetLastError() == 0xdeadbeef ||
1845         broken(GetLastError() == ERROR_INVALID_FLAGS), /* Win9x/WinMe/NT4 */
1846         "expected 0xdeadbeef, got %d\n", GetLastError());
1847
1848     /* now check returned strings */
1849     flags = FDTF_SHORTTIME;
1850     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1851     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1852        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1853     SetLastError(0xdeadbeef);
1854     ret = GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1855     if (ret == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1856     {
1857         win_skip("Needed W-functions are not implemented\n");
1858         return;
1859     }
1860     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1861     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1862
1863     flags = FDTF_LONGTIME;
1864     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1865     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1866        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1867     ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1868     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1869     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1870
1871     /* both time flags */
1872     flags = FDTF_LONGTIME | FDTF_SHORTTIME;
1873     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1874     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1875        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1876     ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1877     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1878     ok(lstrcmpW(buff, buff2) == 0, "expected equal string\n");
1879
1880     flags = FDTF_SHORTDATE;
1881     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1882     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1883        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1884     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1885     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1886     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1887
1888     flags = FDTF_LONGDATE;
1889     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1890     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1891        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1892     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1893     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1894     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1895
1896     /* both date flags */
1897     flags = FDTF_LONGDATE | FDTF_SHORTDATE;
1898     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1899     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1900        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1901     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1902     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1903     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1904
1905     /* various combinations of date/time flags */
1906     flags = FDTF_LONGDATE | FDTF_SHORTTIME;
1907     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1908     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1909        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1910     ret = GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR));
1911     ok(ret == lstrlenW(buff3)+1, "expected %d, got %d\n", lstrlenW(buff3)+1, ret);
1912     ok(lstrcmpW(buff3, buff + lstrlenW(buff) - lstrlenW(buff3)) == 0,
1913        "expected (%s), got (%s) for time part\n",
1914        wine_dbgstr_w(buff3), wine_dbgstr_w(buff + lstrlenW(buff) - lstrlenW(buff3)));
1915     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1916     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1917     p1 = buff;
1918     p2 = buff2;
1919     while (*p2 != '\0')
1920     {
1921         while (*p1 == UNICODE_LTR_MARK)
1922             p1++;
1923         while (*p2 == UNICODE_LTR_MARK)
1924             p2++;
1925         p1++;
1926         p2++;
1927     }
1928     *p1 = '\0';
1929     ok(lstrcmpW(buff2, buff) == 0, "expected (%s) got (%s) for date part\n",
1930        wine_dbgstr_w(buff2), wine_dbgstr_w(buff));
1931
1932     flags = FDTF_LONGDATE | FDTF_LONGTIME;
1933     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1934     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1935        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1936     ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR));
1937     ok(ret == lstrlenW(buff3)+1, "expected %d, got %d\n", lstrlenW(buff3)+1, ret);
1938     ok(lstrcmpW(buff3, buff + lstrlenW(buff) - lstrlenW(buff3)) == 0,
1939        "expected (%s), got (%s) for time part\n",
1940        wine_dbgstr_w(buff3), wine_dbgstr_w(buff + lstrlenW(buff) - lstrlenW(buff3)));
1941     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1942     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1943     p1 = buff;
1944     p2 = buff2;
1945     while (*p2 != '\0')
1946     {
1947         while (*p1 == UNICODE_LTR_MARK)
1948             p1++;
1949         while (*p2 == UNICODE_LTR_MARK)
1950             p2++;
1951         p1++;
1952         p2++;
1953     }
1954     *p1 = '\0';
1955     ok(lstrcmpW(buff2, buff) == 0, "expected (%s) got (%s) for date part\n",
1956        wine_dbgstr_w(buff2), wine_dbgstr_w(buff));
1957
1958     flags = FDTF_SHORTDATE | FDTF_SHORTTIME;
1959     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1960     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1961        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1962     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1963     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1964     lstrcatW(buff2, spaceW);
1965     ret = GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR));
1966     ok(ret == lstrlenW(buff3)+1, "expected %d, got %d\n", lstrlenW(buff3)+1, ret);
1967     lstrcatW(buff2, buff3);
1968     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1969
1970     flags = FDTF_SHORTDATE | FDTF_LONGTIME;
1971     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1972     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1973        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1974     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1975     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1976     lstrcatW(buff2, spaceW);
1977     ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR));
1978     ok(ret == lstrlenW(buff3)+1, "expected %d, got %d\n", lstrlenW(buff3)+1, ret);
1979     lstrcatW(buff2, buff3);
1980     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1981 }
1982
1983 static void test_SHGetObjectCompatFlags(void)
1984 {
1985     struct compat_value {
1986         CHAR nameA[30];
1987         DWORD value;
1988     };
1989
1990     struct compat_value values[] = {
1991         { "OTNEEDSSFCACHE", 0x1 },
1992         { "NO_WEBVIEW", 0x2 },
1993         { "UNBINDABLE", 0x4 },
1994         { "PINDLL", 0x8 },
1995         { "NEEDSFILESYSANCESTOR", 0x10 },
1996         { "NOTAFILESYSTEM", 0x20 },
1997         { "CTXMENU_NOVERBS", 0x40 },
1998         { "CTXMENU_LIMITEDQI", 0x80 },
1999         { "COCREATESHELLFOLDERONLY", 0x100 },
2000         { "NEEDSSTORAGEANCESTOR", 0x200 },
2001         { "NOLEGACYWEBVIEW", 0x400 },
2002         { "CTXMENU_XPQCMFLAGS", 0x1000 },
2003         { "NOIPROPERTYSTORE", 0x2000 }
2004     };
2005
2006     static const char compat_path[] = "Software\\Microsoft\\Windows\\CurrentVersion\\ShellCompatibility\\Objects";
2007     void *pColorAdjustLuma = GetProcAddress(hShlwapi, "ColorAdjustLuma");
2008     CHAR keyA[39]; /* {CLSID} */
2009     HKEY root;
2010     DWORD ret;
2011     int i;
2012
2013     if (!pSHGetObjectCompatFlags)
2014     {
2015         win_skip("SHGetObjectCompatFlags isn't available\n");
2016         return;
2017     }
2018
2019     if (pColorAdjustLuma && pColorAdjustLuma == pSHGetObjectCompatFlags) /* win2k */
2020     {
2021         win_skip("Skipping SHGetObjectCompatFlags, same ordinal used for ColorAdjustLuma\n");
2022         return;
2023     }
2024
2025     /* null args */
2026     ret = pSHGetObjectCompatFlags(NULL, NULL);
2027     ok(ret == 0, "got %d\n", ret);
2028
2029     ret = RegOpenKeyA(HKEY_LOCAL_MACHINE, compat_path, &root);
2030     if (ret != ERROR_SUCCESS)
2031     {
2032         skip("No compatibility class data found\n");
2033         return;
2034     }
2035
2036     for (i = 0; RegEnumKeyA(root, i, keyA, sizeof(keyA)) == ERROR_SUCCESS; i++)
2037     {
2038         HKEY clsid_key;
2039
2040         if (RegOpenKeyA(root, keyA, &clsid_key) == ERROR_SUCCESS)
2041         {
2042             CHAR valueA[30];
2043             DWORD expected = 0, got, length = sizeof(valueA);
2044             CLSID clsid;
2045             int v;
2046
2047             for (v = 0; RegEnumValueA(clsid_key, v, valueA, &length, NULL, NULL, NULL, NULL) == ERROR_SUCCESS; v++)
2048             {
2049                 int j;
2050
2051                 for (j = 0; j < sizeof(values)/sizeof(struct compat_value); j++)
2052                     if (lstrcmpA(values[j].nameA, valueA) == 0)
2053                     {
2054                         expected |= values[j].value;
2055                         break;
2056                     }
2057
2058                 length = sizeof(valueA);
2059             }
2060
2061             pGUIDFromStringA(keyA, &clsid);
2062             got = pSHGetObjectCompatFlags(NULL, &clsid);
2063             ok(got == expected, "got 0x%08x, expected 0x%08x. Key %s\n", got, expected, keyA);
2064
2065             RegCloseKey(clsid_key);
2066         }
2067     }
2068
2069     RegCloseKey(root);
2070 }
2071
2072 typedef struct {
2073     IOleCommandTarget IOleCommandTarget_iface;
2074     LONG ref;
2075 } IOleCommandTargetImpl;
2076
2077 static inline IOleCommandTargetImpl *impl_from_IOleCommandTarget(IOleCommandTarget *iface)
2078 {
2079     return CONTAINING_RECORD(iface, IOleCommandTargetImpl, IOleCommandTarget_iface);
2080 }
2081
2082 static const IOleCommandTargetVtbl IOleCommandTargetImpl_Vtbl;
2083
2084 static IOleCommandTarget* IOleCommandTargetImpl_Construct(void)
2085 {
2086     IOleCommandTargetImpl *obj;
2087
2088     obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj));
2089     obj->IOleCommandTarget_iface.lpVtbl = &IOleCommandTargetImpl_Vtbl;
2090     obj->ref = 1;
2091
2092     return &obj->IOleCommandTarget_iface;
2093 }
2094
2095 static HRESULT WINAPI IOleCommandTargetImpl_QueryInterface(IOleCommandTarget *iface, REFIID riid, void **ppvObj)
2096 {
2097     IOleCommandTargetImpl *This = impl_from_IOleCommandTarget(iface);
2098
2099     if (IsEqualIID(riid, &IID_IUnknown) ||
2100         IsEqualIID(riid, &IID_IOleCommandTarget))
2101     {
2102         *ppvObj = This;
2103     }
2104
2105     if(*ppvObj)
2106     {
2107         IUnknown_AddRef(iface);
2108         return S_OK;
2109     }
2110
2111     return E_NOINTERFACE;
2112 }
2113
2114 static ULONG WINAPI IOleCommandTargetImpl_AddRef(IOleCommandTarget *iface)
2115 {
2116     IOleCommandTargetImpl *This = impl_from_IOleCommandTarget(iface);
2117     return InterlockedIncrement(&This->ref);
2118 }
2119
2120 static ULONG WINAPI IOleCommandTargetImpl_Release(IOleCommandTarget *iface)
2121 {
2122     IOleCommandTargetImpl *This = impl_from_IOleCommandTarget(iface);
2123     ULONG ref = InterlockedDecrement(&This->ref);
2124
2125     if (!ref)
2126     {
2127         HeapFree(GetProcessHeap(), 0, This);
2128         return 0;
2129     }
2130     return ref;
2131 }
2132
2133 static HRESULT WINAPI IOleCommandTargetImpl_QueryStatus(
2134     IOleCommandTarget *iface, const GUID *group, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText)
2135 {
2136     return E_NOTIMPL;
2137 }
2138
2139 static HRESULT WINAPI IOleCommandTargetImpl_Exec(
2140     IOleCommandTarget *iface,
2141     const GUID *CmdGroup,
2142     DWORD nCmdID,
2143     DWORD nCmdexecopt,
2144     VARIANT *pvaIn,
2145     VARIANT *pvaOut)
2146 {
2147     add_call(&trace_got, 3, CmdGroup, (void*)(DWORD_PTR)nCmdID, (void*)(DWORD_PTR)nCmdexecopt, pvaIn, pvaOut);
2148     return S_OK;
2149 }
2150
2151 static const IOleCommandTargetVtbl IOleCommandTargetImpl_Vtbl =
2152 {
2153     IOleCommandTargetImpl_QueryInterface,
2154     IOleCommandTargetImpl_AddRef,
2155     IOleCommandTargetImpl_Release,
2156     IOleCommandTargetImpl_QueryStatus,
2157     IOleCommandTargetImpl_Exec
2158 };
2159
2160 typedef struct {
2161     IServiceProvider IServiceProvider_iface;
2162     LONG ref;
2163 } IServiceProviderImpl;
2164
2165 static inline IServiceProviderImpl *impl_from_IServiceProvider(IServiceProvider *iface)
2166 {
2167     return CONTAINING_RECORD(iface, IServiceProviderImpl, IServiceProvider_iface);
2168 }
2169
2170 typedef struct {
2171     IProfferService IProfferService_iface;
2172     LONG ref;
2173 } IProfferServiceImpl;
2174
2175 static inline IProfferServiceImpl *impl_from_IProfferService(IProfferService *iface)
2176 {
2177     return CONTAINING_RECORD(iface, IProfferServiceImpl, IProfferService_iface);
2178 }
2179
2180
2181 static const IServiceProviderVtbl IServiceProviderImpl_Vtbl;
2182 static const IProfferServiceVtbl IProfferServiceImpl_Vtbl;
2183
2184 static IServiceProvider* IServiceProviderImpl_Construct(void)
2185 {
2186     IServiceProviderImpl *obj;
2187
2188     obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj));
2189     obj->IServiceProvider_iface.lpVtbl = &IServiceProviderImpl_Vtbl;
2190     obj->ref = 1;
2191
2192     return &obj->IServiceProvider_iface;
2193 }
2194
2195 static IProfferService* IProfferServiceImpl_Construct(void)
2196 {
2197     IProfferServiceImpl *obj;
2198
2199     obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj));
2200     obj->IProfferService_iface.lpVtbl = &IProfferServiceImpl_Vtbl;
2201     obj->ref = 1;
2202
2203     return &obj->IProfferService_iface;
2204 }
2205
2206 static HRESULT WINAPI IServiceProviderImpl_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppvObj)
2207 {
2208     IServiceProviderImpl *This = impl_from_IServiceProvider(iface);
2209
2210     if (IsEqualIID(riid, &IID_IUnknown) ||
2211         IsEqualIID(riid, &IID_IServiceProvider))
2212     {
2213         *ppvObj = This;
2214     }
2215
2216     if(*ppvObj)
2217     {
2218         IUnknown_AddRef(iface);
2219         /* native uses redefined IID_IServiceProvider symbol, so we can't compare pointers */
2220         if (IsEqualIID(riid, &IID_IServiceProvider))
2221             add_call(&trace_got, 1, iface, &IID_IServiceProvider, 0, 0, 0);
2222         return S_OK;
2223     }
2224
2225     return E_NOINTERFACE;
2226 }
2227
2228 static ULONG WINAPI IServiceProviderImpl_AddRef(IServiceProvider *iface)
2229 {
2230     IServiceProviderImpl *This = impl_from_IServiceProvider(iface);
2231     return InterlockedIncrement(&This->ref);
2232 }
2233
2234 static ULONG WINAPI IServiceProviderImpl_Release(IServiceProvider *iface)
2235 {
2236     IServiceProviderImpl *This = impl_from_IServiceProvider(iface);
2237     ULONG ref = InterlockedDecrement(&This->ref);
2238
2239     if (!ref)
2240     {
2241         HeapFree(GetProcessHeap(), 0, This);
2242         return 0;
2243     }
2244     return ref;
2245 }
2246
2247 static HRESULT WINAPI IServiceProviderImpl_QueryService(
2248     IServiceProvider *iface, REFGUID service, REFIID riid, void **ppv)
2249 {
2250     /* native uses redefined pointer for IID_IOleCommandTarget, not one from uuid.lib */
2251     if (IsEqualIID(riid, &IID_IOleCommandTarget))
2252     {
2253         add_call(&trace_got, 2, iface, service, &IID_IOleCommandTarget, 0, 0);
2254         *ppv = IOleCommandTargetImpl_Construct();
2255     }
2256     if (IsEqualIID(riid, &IID_IProfferService))
2257     {
2258         if (IsEqualIID(service, &IID_IProfferService))
2259             add_call(&trace_got, 2, &IID_IProfferService, &IID_IProfferService, 0, 0, 0);
2260         *ppv = IProfferServiceImpl_Construct();
2261     }
2262     return S_OK;
2263 }
2264
2265 static const IServiceProviderVtbl IServiceProviderImpl_Vtbl =
2266 {
2267     IServiceProviderImpl_QueryInterface,
2268     IServiceProviderImpl_AddRef,
2269     IServiceProviderImpl_Release,
2270     IServiceProviderImpl_QueryService
2271 };
2272
2273 static void test_IUnknown_QueryServiceExec(void)
2274 {
2275     IServiceProvider *provider = IServiceProviderImpl_Construct();
2276     static const GUID dummy_serviceid = { 0xdeadbeef };
2277     static const GUID dummy_groupid = { 0xbeefbeef };
2278     call_trace_t trace_expected;
2279     HRESULT hr;
2280
2281     /* on <=W2K platforms same ordinal used for another export with different
2282        prototype, so skipping using this indirect condition */
2283     if (is_win2k_and_lower)
2284     {
2285         win_skip("IUnknown_QueryServiceExec is not available\n");
2286         return;
2287     }
2288
2289     /* null source pointer */
2290     hr = pIUnknown_QueryServiceExec(NULL, &dummy_serviceid, &dummy_groupid, 0, 0, 0, 0);
2291     ok(hr == E_FAIL, "got 0x%08x\n", hr);
2292
2293     /* expected trace:
2294        IUnknown_QueryServiceExec( ptr1, serviceid, groupid, arg1, arg2, arg3, arg4);
2295          -> IUnknown_QueryInterface( ptr1, &IID_IServiceProvider, &prov );
2296          -> IServiceProvider_QueryService( prov, serviceid, &IID_IOleCommandTarget, &obj );
2297          -> IOleCommandTarget_Exec( obj, groupid, arg1, arg2, arg3, arg4 );
2298     */
2299     init_call_trace(&trace_expected);
2300
2301     add_call(&trace_expected, 1, provider, &IID_IServiceProvider, 0, 0, 0);
2302     add_call(&trace_expected, 2, provider, &dummy_serviceid, &IID_IOleCommandTarget, 0, 0);
2303     add_call(&trace_expected, 3, &dummy_groupid, (void*)0x1, (void*)0x2, (void*)0x3, (void*)0x4);
2304
2305     init_call_trace(&trace_got);
2306     hr = pIUnknown_QueryServiceExec((IUnknown*)provider, &dummy_serviceid, &dummy_groupid, 0x1, 0x2, (void*)0x3, (void*)0x4);
2307     ok(hr == S_OK, "got 0x%08x\n", hr);
2308
2309     ok_trace(&trace_expected, &trace_got);
2310
2311     free_call_trace(&trace_expected);
2312     free_call_trace(&trace_got);
2313
2314     IServiceProvider_Release(provider);
2315 }
2316
2317
2318 static HRESULT WINAPI IProfferServiceImpl_QueryInterface(IProfferService *iface, REFIID riid, void **ppvObj)
2319 {
2320     IProfferServiceImpl *This = impl_from_IProfferService(iface);
2321
2322     if (IsEqualIID(riid, &IID_IUnknown) ||
2323         IsEqualIID(riid, &IID_IProfferService))
2324     {
2325         *ppvObj = This;
2326     }
2327     else if (IsEqualIID(riid, &IID_IServiceProvider))
2328     {
2329         *ppvObj = IServiceProviderImpl_Construct();
2330         add_call(&trace_got, 1, iface, &IID_IServiceProvider, 0, 0, 0);
2331         return S_OK;
2332     }
2333
2334     if(*ppvObj)
2335     {
2336         IUnknown_AddRef(iface);
2337         return S_OK;
2338     }
2339
2340     return E_NOINTERFACE;
2341 }
2342
2343 static ULONG WINAPI IProfferServiceImpl_AddRef(IProfferService *iface)
2344 {
2345     IProfferServiceImpl *This = impl_from_IProfferService(iface);
2346     return InterlockedIncrement(&This->ref);
2347 }
2348
2349 static ULONG WINAPI IProfferServiceImpl_Release(IProfferService *iface)
2350 {
2351     IProfferServiceImpl *This = impl_from_IProfferService(iface);
2352     ULONG ref = InterlockedDecrement(&This->ref);
2353
2354     if (!ref)
2355     {
2356         HeapFree(GetProcessHeap(), 0, This);
2357         return 0;
2358     }
2359     return ref;
2360 }
2361
2362 static HRESULT WINAPI IProfferServiceImpl_ProfferService(IProfferService *iface,
2363     REFGUID service, IServiceProvider *pService, DWORD *pCookie)
2364 {
2365     *pCookie = 0xdeadbeef;
2366     add_call(&trace_got, 3, service, pService, pCookie, 0, 0);
2367     return S_OK;
2368 }
2369
2370 static HRESULT WINAPI IProfferServiceImpl_RevokeService(IProfferService *iface, DWORD cookie)
2371 {
2372     add_call(&trace_got, 4, (void*)(DWORD_PTR)cookie, 0, 0, 0, 0);
2373     return S_OK;
2374 }
2375
2376 static const IProfferServiceVtbl IProfferServiceImpl_Vtbl =
2377 {
2378     IProfferServiceImpl_QueryInterface,
2379     IProfferServiceImpl_AddRef,
2380     IProfferServiceImpl_Release,
2381     IProfferServiceImpl_ProfferService,
2382     IProfferServiceImpl_RevokeService
2383 };
2384
2385 static void test_IUnknown_ProfferService(void)
2386 {
2387     IServiceProvider *provider = IServiceProviderImpl_Construct();
2388     IProfferService *proff = IProfferServiceImpl_Construct();
2389     static const GUID dummy_serviceid = { 0xdeadbeef };
2390     call_trace_t trace_expected;
2391     HRESULT hr;
2392     DWORD cookie;
2393
2394     /* on <=W2K platforms same ordinal used for another export with different
2395        prototype, so skipping using this indirect condition */
2396     if (is_win2k_and_lower)
2397     {
2398         win_skip("IUnknown_ProfferService is not available\n");
2399         return;
2400     }
2401
2402     /* null source pointer */
2403     hr = pIUnknown_ProfferService(NULL, &dummy_serviceid, 0, 0);
2404     ok(hr == E_FAIL, "got 0x%08x\n", hr);
2405
2406     /* expected trace:
2407        IUnknown_ProfferService( ptr1, serviceid, arg1, arg2);
2408          -> IUnknown_QueryInterface( ptr1, &IID_IServiceProvider, &provider );
2409          -> IServiceProvider_QueryService( provider, &IID_IProfferService, &IID_IProfferService, &proffer );
2410
2411          if (service pointer not null):
2412              -> IProfferService_ProfferService( proffer, serviceid, arg1, arg2 );
2413          else
2414              -> IProfferService_RevokeService( proffer, *arg2 );
2415     */
2416     init_call_trace(&trace_expected);
2417
2418     add_call(&trace_expected, 1, proff, &IID_IServiceProvider, 0, 0, 0);
2419     add_call(&trace_expected, 2, &IID_IProfferService, &IID_IProfferService, 0, 0, 0);
2420     add_call(&trace_expected, 3, &dummy_serviceid, provider, &cookie, 0, 0);
2421
2422     init_call_trace(&trace_got);
2423     cookie = 0;
2424     hr = pIUnknown_ProfferService((IUnknown*)proff, &dummy_serviceid, provider, &cookie);
2425     ok(hr == S_OK, "got 0x%08x\n", hr);
2426     ok(cookie == 0xdeadbeef, "got %x\n", cookie);
2427
2428     ok_trace(&trace_expected, &trace_got);
2429     free_call_trace(&trace_got);
2430     free_call_trace(&trace_expected);
2431
2432     /* same with ::Revoke path */
2433     init_call_trace(&trace_expected);
2434
2435     add_call(&trace_expected, 1, proff, &IID_IServiceProvider, 0, 0, 0);
2436     add_call(&trace_expected, 2, &IID_IProfferService, &IID_IProfferService, 0, 0, 0);
2437     add_call(&trace_expected, 4, (void*)(DWORD_PTR)cookie, 0, 0, 0, 0);
2438
2439     init_call_trace(&trace_got);
2440     ok(cookie != 0, "got %x\n", cookie);
2441     hr = pIUnknown_ProfferService((IUnknown*)proff, &dummy_serviceid, 0, &cookie);
2442     ok(hr == S_OK, "got 0x%08x\n", hr);
2443     ok(cookie == 0, "got %x\n", cookie);
2444     ok_trace(&trace_expected, &trace_got);
2445     free_call_trace(&trace_got);
2446     free_call_trace(&trace_expected);
2447
2448     IServiceProvider_Release(provider);
2449     IProfferService_Release(proff);
2450 }
2451
2452 static void test_SHCreateWorkerWindowA(void)
2453 {
2454     WNDCLASSA cliA;
2455     char classA[20];
2456     HWND hwnd;
2457     LONG_PTR ret;
2458     BOOL res;
2459
2460     if (is_win2k_and_lower)
2461     {
2462         win_skip("SHCreateWorkerWindowA not available\n");
2463         return;
2464     }
2465
2466     hwnd = pSHCreateWorkerWindowA(0, NULL, 0, 0, 0, 0);
2467     ok(hwnd != 0, "expected window\n");
2468
2469     GetClassName(hwnd, classA, 20);
2470     ok(lstrcmpA(classA, "WorkerA") == 0, "expected WorkerA class, got %s\n", classA);
2471
2472     ret = GetWindowLongPtrA(hwnd, 0);
2473     ok(ret == 0, "got %ld\n", ret);
2474
2475     /* class info */
2476     memset(&cliA, 0, sizeof(cliA));
2477     res = GetClassInfoA(GetModuleHandle("shlwapi.dll"), "WorkerA", &cliA);
2478     ok(res, "failed to get class info\n");
2479     ok(cliA.style == 0, "got 0x%08x\n", cliA.style);
2480     ok(cliA.cbClsExtra == 0, "got %d\n", cliA.cbClsExtra);
2481     ok(cliA.cbWndExtra == sizeof(LONG_PTR), "got %d\n", cliA.cbWndExtra);
2482     ok(cliA.lpszMenuName == 0, "got %s\n", cliA.lpszMenuName);
2483
2484     DestroyWindow(hwnd);
2485
2486     /* set extra bytes */
2487     hwnd = pSHCreateWorkerWindowA(0, NULL, 0, 0, 0, 0xdeadbeef);
2488     ok(hwnd != 0, "expected window\n");
2489
2490     GetClassName(hwnd, classA, 20);
2491     ok(lstrcmpA(classA, "WorkerA") == 0, "expected WorkerA class, got %s\n", classA);
2492
2493     ret = GetWindowLongPtrA(hwnd, 0);
2494     ok(ret == 0xdeadbeef, "got %ld\n", ret);
2495
2496     /* test exstyle */
2497     ret = GetWindowLongA(hwnd, GWL_EXSTYLE);
2498     ok(ret == WS_EX_WINDOWEDGE ||
2499        ret == (WS_EX_WINDOWEDGE|WS_EX_LAYOUTRTL) /* systems with RTL locale */, "0x%08lx\n", ret);
2500
2501     DestroyWindow(hwnd);
2502
2503     hwnd = pSHCreateWorkerWindowA(0, NULL, WS_EX_TOOLWINDOW, 0, 0, 0);
2504     ret = GetWindowLongA(hwnd, GWL_EXSTYLE);
2505     ok(ret == (WS_EX_WINDOWEDGE|WS_EX_TOOLWINDOW) ||
2506        ret == (WS_EX_WINDOWEDGE|WS_EX_TOOLWINDOW|WS_EX_LAYOUTRTL) /* systems with RTL locale */, "0x%08lx\n", ret);
2507     DestroyWindow(hwnd);
2508 }
2509
2510 static HRESULT WINAPI SF_QueryInterface(IShellFolder *iface,
2511         REFIID riid, void **ppv)
2512 {
2513     /* SHIShellFolder_EnumObjects doesn't QI the object for IShellFolder */
2514     ok(!IsEqualGUID(&IID_IShellFolder, riid),
2515             "Unexpected QI for IShellFolder\n");
2516     return E_NOINTERFACE;
2517 }
2518
2519 static ULONG WINAPI SF_AddRef(IShellFolder *iface)
2520 {
2521     return 2;
2522 }
2523
2524 static ULONG WINAPI SF_Release(IShellFolder *iface)
2525 {
2526     return 1;
2527 }
2528
2529 static HRESULT WINAPI SF_ParseDisplayName(IShellFolder *iface,
2530         HWND owner, LPBC reserved, LPOLESTR displayName, ULONG *eaten,
2531         LPITEMIDLIST *idl, ULONG *attr)
2532 {
2533     ok(0, "Didn't expect ParseDisplayName\n");
2534     return E_NOTIMPL;
2535 }
2536
2537 static HRESULT WINAPI SF_EnumObjects(IShellFolder *iface,
2538         HWND owner, SHCONTF flags, IEnumIDList **enm)
2539 {
2540     *enm = (IEnumIDList*)0xcafebabe;
2541     return S_OK;
2542 }
2543
2544 static HRESULT WINAPI SF_BindToObject(IShellFolder *iface,
2545         LPCITEMIDLIST idl, LPBC reserved, REFIID riid, void **obj)
2546 {
2547     ok(0, "Didn't expect BindToObject\n");
2548     return E_NOTIMPL;
2549 }
2550
2551 static HRESULT WINAPI SF_BindToStorage(IShellFolder *iface,
2552         LPCITEMIDLIST idl, LPBC reserved, REFIID riid, void **obj)
2553 {
2554     ok(0, "Didn't expect BindToStorage\n");
2555     return E_NOTIMPL;
2556 }
2557
2558 static HRESULT WINAPI SF_CompareIDs(IShellFolder *iface,
2559         LPARAM lparam, LPCITEMIDLIST idl1, LPCITEMIDLIST idl2)
2560 {
2561     ok(0, "Didn't expect CompareIDs\n");
2562     return E_NOTIMPL;
2563 }
2564
2565 static HRESULT WINAPI SF_CreateViewObject(IShellFolder *iface,
2566         HWND owner, REFIID riid, void **out)
2567 {
2568     ok(0, "Didn't expect CreateViewObject\n");
2569     return E_NOTIMPL;
2570 }
2571
2572 static HRESULT WINAPI SF_GetAttributesOf(IShellFolder *iface,
2573         UINT cidl, LPCITEMIDLIST *idl, SFGAOF *inOut)
2574 {
2575     ok(0, "Didn't expect GetAttributesOf\n");
2576     return E_NOTIMPL;
2577 }
2578
2579 static HRESULT WINAPI SF_GetUIObjectOf(IShellFolder *iface,
2580         HWND owner, UINT cidl, LPCITEMIDLIST *idls, REFIID riid, UINT *inOut,
2581         void **out)
2582 {
2583     ok(0, "Didn't expect GetUIObjectOf\n");
2584     return E_NOTIMPL;
2585 }
2586
2587 static HRESULT WINAPI SF_GetDisplayNameOf(IShellFolder *iface,
2588         LPCITEMIDLIST idl, SHGDNF flags, STRRET *name)
2589 {
2590     ok(0, "Didn't expect GetDisplayNameOf\n");
2591     return E_NOTIMPL;
2592 }
2593
2594 static HRESULT WINAPI SF_SetNameOf(IShellFolder *iface,
2595         HWND hwnd, LPCITEMIDLIST idl, LPCOLESTR name, SHGDNF flags,
2596         LPITEMIDLIST *idlOut)
2597 {
2598     ok(0, "Didn't expect SetNameOf\n");
2599     return E_NOTIMPL;
2600 }
2601
2602 static IShellFolderVtbl ShellFolderVtbl = {
2603     SF_QueryInterface,
2604     SF_AddRef,
2605     SF_Release,
2606     SF_ParseDisplayName,
2607     SF_EnumObjects,
2608     SF_BindToObject,
2609     SF_BindToStorage,
2610     SF_CompareIDs,
2611     SF_CreateViewObject,
2612     SF_GetAttributesOf,
2613     SF_GetUIObjectOf,
2614     SF_GetDisplayNameOf,
2615     SF_SetNameOf
2616 };
2617
2618 static IShellFolder ShellFolder = { &ShellFolderVtbl };
2619
2620 static void test_SHIShellFolder_EnumObjects(void)
2621 {
2622     IEnumIDList *enm;
2623     HRESULT hres;
2624     IShellFolder *folder;
2625
2626     if(!pSHIShellFolder_EnumObjects || is_win2k_and_lower){
2627         win_skip("SHIShellFolder_EnumObjects not available\n");
2628         return;
2629     }
2630
2631     if(0){
2632         /* NULL object crashes on Windows */
2633         pSHIShellFolder_EnumObjects(NULL, NULL, 0, NULL);
2634     }
2635
2636     /* SHIShellFolder_EnumObjects doesn't QI the object for IShellFolder */
2637     enm = (IEnumIDList*)0xdeadbeef;
2638     hres = pSHIShellFolder_EnumObjects(&ShellFolder, NULL, 0, &enm);
2639     ok(hres == S_OK, "SHIShellFolder_EnumObjects failed: 0x%08x\n", hres);
2640     ok(enm == (IEnumIDList*)0xcafebabe, "Didn't get expected enumerator location, instead: %p\n", enm);
2641
2642     /* SHIShellFolder_EnumObjects isn't strict about the IShellFolder object */
2643     hres = pSHGetDesktopFolder(&folder);
2644     ok(hres == S_OK, "SHGetDesktopFolder failed: 0x%08x\n", hres);
2645
2646     enm = NULL;
2647     hres = pSHIShellFolder_EnumObjects(folder, NULL, 0, &enm);
2648     ok(hres == S_OK, "SHIShellFolder_EnumObjects failed: 0x%08x\n", hres);
2649     ok(enm != NULL, "Didn't get an enumerator\n");
2650     if(enm)
2651         IEnumIDList_Release(enm);
2652
2653     IShellFolder_Release(folder);
2654 }
2655
2656 static void write_inifile(LPCWSTR filename)
2657 {
2658     DWORD written;
2659     HANDLE file;
2660
2661     static const char data[] =
2662         "[TestApp]\r\n"
2663         "AKey=1\r\n"
2664         "AnotherKey=asdf\r\n";
2665
2666     file = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
2667     if(file == INVALID_HANDLE_VALUE)
2668         return;
2669
2670     WriteFile(file, data, sizeof(data), &written, NULL);
2671
2672     CloseHandle(file);
2673 }
2674
2675 #define verify_inifile(f, e) r_verify_inifile(__LINE__, f, e)
2676 static void r_verify_inifile(unsigned l, LPCWSTR filename, LPCSTR exp)
2677 {
2678     HANDLE file;
2679     CHAR buf[1024];
2680     DWORD read;
2681
2682     file = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
2683     if(file == INVALID_HANDLE_VALUE)
2684         return;
2685
2686     ReadFile(file, buf, sizeof(buf) * sizeof(CHAR), &read, NULL);
2687     buf[read] = '\0';
2688
2689     CloseHandle(file);
2690
2691     ok_(__FILE__,l)(!strcmp(buf, exp), "Expected:\n%s\nGot:\n%s\n", exp,
2692             buf);
2693 }
2694
2695 static void test_SHGetIniString(void)
2696 {
2697     DWORD ret;
2698     WCHAR out[64] = {0};
2699
2700     static const WCHAR TestAppW[] = {'T','e','s','t','A','p','p',0};
2701     static const WCHAR TestIniW[] = {'C',':','\\','t','e','s','t','.','i','n','i',0};
2702     static const WCHAR AKeyW[] = {'A','K','e','y',0};
2703     static const WCHAR AnotherKeyW[] = {'A','n','o','t','h','e','r','K','e','y',0};
2704     static const WCHAR JunkKeyW[] = {'J','u','n','k','K','e','y',0};
2705
2706     if(!pSHGetIniStringW || is_win2k_and_lower){
2707         win_skip("SHGetIniStringW is not available\n");
2708         return;
2709     }
2710
2711     write_inifile(TestIniW);
2712
2713     if(0){
2714         /* these crash on Windows */
2715         pSHGetIniStringW(NULL, NULL, NULL, 0, NULL);
2716         pSHGetIniStringW(NULL, AKeyW, out, sizeof(out), TestIniW);
2717         pSHGetIniStringW(TestAppW, AKeyW, NULL, sizeof(out), TestIniW);
2718     }
2719
2720     ret = pSHGetIniStringW(TestAppW, AKeyW, out, 0, TestIniW);
2721     ok(ret == 0, "SHGetIniStringW should have given 0, instead: %d\n", ret);
2722
2723     /* valid arguments */
2724     ret = pSHGetIniStringW(TestAppW, NULL, out, sizeof(out), TestIniW);
2725     ok(broken(ret == 0) || /* win 98 */
2726             ret == 4, "SHGetIniStringW should have given 4, instead: %d\n", ret);
2727     ok(!lstrcmpW(out, AKeyW), "Expected %s, got: %s\n",
2728                 wine_dbgstr_w(AKeyW), wine_dbgstr_w(out));
2729
2730     ret = pSHGetIniStringW(TestAppW, AKeyW, out, sizeof(out), TestIniW);
2731     ok(broken(ret == 0) || /* win 98 */
2732                 ret == 1, "SHGetIniStringW should have given 1, instead: %d\n", ret);
2733     ok(broken(*out == 0) || /*win 98 */
2734         !strcmp_wa(out, "1"), "Expected L\"1\", got: %s\n", wine_dbgstr_w(out));
2735
2736     ret = pSHGetIniStringW(TestAppW, AnotherKeyW, out, sizeof(out), TestIniW);
2737     ok(broken(ret == 0) || /* win 98 */
2738             ret == 4, "SHGetIniStringW should have given 4, instead: %d\n", ret);
2739     ok(broken(*out == 0) || /* win 98 */
2740             !strcmp_wa(out, "asdf"), "Expected L\"asdf\", got: %s\n", wine_dbgstr_w(out));
2741
2742     ret = pSHGetIniStringW(TestAppW, JunkKeyW, out, sizeof(out), TestIniW);
2743     ok(ret == 0, "SHGetIniStringW should have given 0, instead: %d\n", ret);
2744     ok(*out == 0, "Expected L\"\", got: %s\n", wine_dbgstr_w(out));
2745
2746     DeleteFileW(TestIniW);
2747 }
2748
2749 static void test_SHSetIniString(void)
2750 {
2751     BOOL ret;
2752
2753     static const WCHAR TestAppW[] = {'T','e','s','t','A','p','p',0};
2754     static const WCHAR AnotherAppW[] = {'A','n','o','t','h','e','r','A','p','p',0};
2755     static const WCHAR TestIniW[] = {'C',':','\\','t','e','s','t','.','i','n','i',0};
2756     static const WCHAR AKeyW[] = {'A','K','e','y',0};
2757     static const WCHAR NewKeyW[] = {'N','e','w','K','e','y',0};
2758     static const WCHAR AValueW[] = {'A','V','a','l','u','e',0};
2759
2760     if(!pSHSetIniStringW || is_win2k_and_lower){
2761         win_skip("SHSetIniStringW is not available\n");
2762         return;
2763     }
2764
2765     write_inifile(TestIniW);
2766
2767     ret = pSHSetIniStringW(TestAppW, AKeyW, AValueW, TestIniW);
2768     ok(ret == TRUE, "SHSetIniStringW should not have failed\n");
2769     todo_wine /* wine sticks an extra \r\n at the end of the file */
2770         verify_inifile(TestIniW, "[TestApp]\r\nAKey=AValue\r\nAnotherKey=asdf\r\n");
2771
2772     ret = pSHSetIniStringW(TestAppW, AKeyW, NULL, TestIniW);
2773     ok(ret == TRUE, "SHSetIniStringW should not have failed\n");
2774     verify_inifile(TestIniW, "[TestApp]\r\nAnotherKey=asdf\r\n");
2775
2776     ret = pSHSetIniStringW(AnotherAppW, NewKeyW, AValueW, TestIniW);
2777     ok(ret == TRUE, "SHSetIniStringW should not have failed\n");
2778     verify_inifile(TestIniW, "[TestApp]\r\nAnotherKey=asdf\r\n[AnotherApp]\r\nNewKey=AValue\r\n");
2779
2780     ret = pSHSetIniStringW(TestAppW, NULL, AValueW, TestIniW);
2781     ok(ret == TRUE, "SHSetIniStringW should not have failed\n");
2782     verify_inifile(TestIniW, "[AnotherApp]\r\nNewKey=AValue\r\n");
2783
2784     DeleteFileW(TestIniW);
2785 }
2786
2787 enum _shellkey_flags {
2788     SHKEY_Root_HKCU = 0x1,
2789     SHKEY_Root_HKLM = 0x2,
2790     SHKEY_Key_Explorer  = 0x00,
2791     SHKEY_Key_Shell = 0x10,
2792     SHKEY_Key_ShellNoRoam = 0x20,
2793     SHKEY_Key_Classes = 0x30,
2794     SHKEY_Subkey_Default = 0x0000,
2795     SHKEY_Subkey_ResourceName = 0x1000,
2796     SHKEY_Subkey_Handlers = 0x2000,
2797     SHKEY_Subkey_Associations = 0x3000,
2798     SHKEY_Subkey_Volatile = 0x4000,
2799     SHKEY_Subkey_MUICache = 0x5000,
2800     SHKEY_Subkey_FileExts = 0x6000
2801 };
2802
2803 static void test_SHGetShellKey(void)
2804 {
2805     static const WCHAR ShellFoldersW[] = { 'S','h','e','l','l',' ','F','o','l','d','e','r','s',0 };
2806     static const WCHAR WineTestW[] = { 'W','i','n','e','T','e','s','t',0 };
2807
2808     void *pPathBuildRootW = GetProcAddress(hShlwapi, "PathBuildRootW");
2809     DWORD *alloc_data, data, size;
2810     HKEY hkey;
2811     HRESULT hres;
2812
2813     if (!pSHGetShellKey)
2814     {
2815         win_skip("SHGetShellKey(ordinal 491) isn't available\n");
2816         return;
2817     }
2818
2819     /* some win2k */
2820     if (pPathBuildRootW && pPathBuildRootW == pSHGetShellKey)
2821     {
2822         win_skip("SHGetShellKey(ordinal 491) used for PathBuildRootW\n");
2823         return;
2824     }
2825
2826     if (is_win9x || is_win2k_and_lower)
2827     {
2828         win_skip("Ordinal 491 used for another call, skipping SHGetShellKey tests\n");
2829         return;
2830     }
2831
2832     /* Vista+ limits SHKEY enumeration values */
2833     SetLastError(0xdeadbeef);
2834     hkey = pSHGetShellKey(SHKEY_Key_Explorer, ShellFoldersW, FALSE);
2835     if (hkey)
2836     {
2837         /* Tests not working on Vista+ */
2838         RegCloseKey(hkey);
2839
2840         hkey = pSHGetShellKey(SHKEY_Root_HKLM|SHKEY_Key_Classes, NULL, FALSE);
2841         ok(hkey != NULL, "hkey = NULL\n");
2842         RegCloseKey(hkey);
2843     }
2844
2845     hkey = pSHGetShellKey(SHKEY_Root_HKCU|SHKEY_Key_Explorer, ShellFoldersW, FALSE);
2846     ok(hkey != NULL, "hkey = NULL\n");
2847     RegCloseKey(hkey);
2848
2849     hkey = pSHGetShellKey(SHKEY_Root_HKLM|SHKEY_Key_Explorer, ShellFoldersW, FALSE);
2850     ok(hkey != NULL, "hkey = NULL\n");
2851     RegCloseKey(hkey);
2852
2853     hkey = pSHGetShellKey(SHKEY_Root_HKLM, WineTestW, FALSE);
2854     ok(hkey == NULL, "hkey != NULL\n");
2855
2856     hkey = pSHGetShellKey(SHKEY_Root_HKLM, NULL, FALSE);
2857     ok(hkey != NULL, "Can't open key\n");
2858     ok(SUCCEEDED(RegDeleteKeyW(hkey, WineTestW)), "Can't delte key\n");
2859     RegCloseKey(hkey);
2860
2861     hkey = pSHGetShellKey(SHKEY_Root_HKLM, WineTestW, TRUE);
2862     if (!hkey && GetLastError() == ERROR_ACCESS_DENIED)
2863     {
2864         skip("Not authorized to create keys\n");
2865         return;
2866     }
2867     ok(hkey != NULL, "Can't create key\n");
2868     RegCloseKey(hkey);
2869
2870     if (!pSKGetValueW || !pSKSetValueW || !pSKDeleteValueW || !pSKAllocValueW)
2871     {
2872         win_skip("SKGetValueW, SKSetValueW, SKDeleteValueW or SKAllocValueW not available\n");
2873         return;
2874     }
2875
2876     hres = pSKGetValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, &data, &size);
2877     ok(hres == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hres = %x\n", hres);
2878
2879     data = 1234;
2880     hres = pSKSetValueW(SHKEY_Root_HKLM, WineTestW, NULL, REG_DWORD, &data, sizeof(DWORD));
2881     ok(hres == S_OK, "hres = %x\n", hres);
2882
2883     size = 1;
2884     hres = pSKGetValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, NULL, &size);
2885     ok(hres == S_OK, "hres = %x\n", hres);
2886     ok(size == sizeof(DWORD), "size = %d\n", size);
2887
2888     data = 0xdeadbeef;
2889     hres = pSKGetValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, &data, &size);
2890     ok(hres == S_OK, "hres = %x\n", hres);
2891     ok(size == sizeof(DWORD), "size = %d\n", size);
2892     ok(data == 1234, "data = %d\n", data);
2893
2894     hres = pSKAllocValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, (void**)&alloc_data, &size);
2895     ok(hres == S_OK, "hres= %x\n", hres);
2896     ok(size == sizeof(DWORD), "size = %d\n", size);
2897     if (SUCCEEDED(hres))
2898     {
2899         ok(*alloc_data == 1234, "*alloc_data = %d\n", *alloc_data);
2900         LocalFree(alloc_data);
2901     }
2902
2903     hres = pSKDeleteValueW(SHKEY_Root_HKLM, WineTestW, NULL);
2904     ok(hres == S_OK, "hres = %x\n", hres);
2905
2906     hres = pSKDeleteValueW(SHKEY_Root_HKLM, WineTestW, NULL);
2907     ok(hres == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hres = %x\n", hres);
2908
2909     hres = pSKGetValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, &data, &size);
2910     ok(hres == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hres = %x\n", hres);
2911
2912     hkey = pSHGetShellKey(SHKEY_Root_HKLM, NULL, FALSE);
2913     ok(hkey != NULL, "Can't create key\n");
2914     ok(SUCCEEDED(RegDeleteKeyW(hkey, WineTestW)), "Can't delte key\n");
2915     RegCloseKey(hkey);
2916 }
2917
2918 static void init_pointers(void)
2919 {
2920 #define MAKEFUNC(f, ord) (p##f = (void*)GetProcAddress(hShlwapi, (LPSTR)(ord)))
2921     MAKEFUNC(SHAllocShared, 7);
2922     MAKEFUNC(SHLockShared, 8);
2923     MAKEFUNC(SHUnlockShared, 9);
2924     MAKEFUNC(SHFreeShared, 10);
2925     MAKEFUNC(GetAcceptLanguagesA, 14);
2926     MAKEFUNC(SHSetWindowBits, 165);
2927     MAKEFUNC(ConnectToConnectionPoint, 168);
2928     MAKEFUNC(SHSearchMapInt, 198);
2929     MAKEFUNC(SHCreateWorkerWindowA, 257);
2930     MAKEFUNC(GUIDFromStringA, 269);
2931     MAKEFUNC(SHPackDispParams, 282);
2932     MAKEFUNC(IConnectionPoint_InvokeWithCancel, 283);
2933     MAKEFUNC(IConnectionPoint_SimpleInvoke, 284);
2934     MAKEFUNC(SHGetIniStringW, 294);
2935     MAKEFUNC(SHSetIniStringW, 295);
2936     MAKEFUNC(SHFormatDateTimeA, 353);
2937     MAKEFUNC(SHFormatDateTimeW, 354);
2938     MAKEFUNC(SHIShellFolder_EnumObjects, 404);
2939     MAKEFUNC(SHGetObjectCompatFlags, 476);
2940     MAKEFUNC(IUnknown_QueryServiceExec, 484);
2941     MAKEFUNC(SHGetShellKey, 491);
2942     MAKEFUNC(SHPropertyBag_ReadLONG, 496);
2943     MAKEFUNC(IUnknown_ProfferService, 514);
2944     MAKEFUNC(SKGetValueW, 516);
2945     MAKEFUNC(SKSetValueW, 517);
2946     MAKEFUNC(SKDeleteValueW, 518);
2947     MAKEFUNC(SKAllocValueW, 519);
2948 #undef MAKEFUNC
2949 }
2950
2951 START_TEST(ordinal)
2952 {
2953     hShlwapi = GetModuleHandleA("shlwapi.dll");
2954     is_win2k_and_lower = GetProcAddress(hShlwapi, "StrChrNW") == 0;
2955     is_win9x = GetProcAddress(hShlwapi, (LPSTR)99) == 0; /* StrCpyNXA */
2956
2957     /* SHCreateStreamOnFileEx was introduced in shlwapi v6.0 */
2958     if(!GetProcAddress(hShlwapi, "SHCreateStreamOnFileEx")){
2959         win_skip("Too old shlwapi version\n");
2960         return;
2961     }
2962
2963     init_pointers();
2964
2965     hmlang = LoadLibraryA("mlang.dll");
2966     pLcidToRfc1766A = (void *)GetProcAddress(hmlang, "LcidToRfc1766A");
2967
2968     hshell32 = LoadLibraryA("shell32.dll");
2969     pSHGetDesktopFolder = (void *)GetProcAddress(hshell32, "SHGetDesktopFolder");
2970
2971     test_GetAcceptLanguagesA();
2972     test_SHSearchMapInt();
2973     test_alloc_shared();
2974     test_fdsa();
2975     test_GetShellSecurityDescriptor();
2976     test_SHPackDispParams();
2977     test_IConnectionPoint();
2978     test_SHPropertyBag_ReadLONG();
2979     test_SHSetWindowBits();
2980     test_SHFormatDateTimeA();
2981     test_SHFormatDateTimeW();
2982     test_SHGetObjectCompatFlags();
2983     test_IUnknown_QueryServiceExec();
2984     test_IUnknown_ProfferService();
2985     test_SHCreateWorkerWindowA();
2986     test_SHIShellFolder_EnumObjects();
2987     test_SHGetIniString();
2988     test_SHSetIniString();
2989     test_SHGetShellKey();
2990
2991     FreeLibrary(hshell32);
2992     FreeLibrary(hmlang);
2993 }