mshtml: COM cleanup for the IHTMLDOMNode iface.
[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;
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         ok(GetSecurityDescriptorControl(psd, &control, &dwRev),
647                 "GetSecurityDescriptorControl failed with error %u\n", GetLastError());
648         ok(0 == (control & SE_SELF_RELATIVE), "SD should be absolute\n");
649
650         ok(GetSecurityDescriptorDacl(psd, &bHasDacl, &pAcl, &bDefaulted), 
651             "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                 ok(GetAclInformation(pAcl, &asiSize, sizeof(asiSize), AclSizeInformation),
666                         "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                     ok(GetAce(pAcl, 0, (LPVOID*)&paaa), "GetAce failed with error %u\n", GetLastError());
674                     ok(paaa->Header.AceType == ACCESS_ALLOWED_ACE_TYPE, 
675                             "Invalid ACE type %d\n", paaa->Header.AceType); 
676                     ok(paaa->Header.AceFlags == 0, "Invalid ACE flags %x\n", paaa->Header.AceFlags);
677                     ok(paaa->Mask == GENERIC_ALL, "Invalid ACE mask %x\n", paaa->Mask);
678
679                     ok(GetAce(pAcl, 1, (LPVOID*)&paaa), "GetAce failed with error %u\n", GetLastError());
680                     ok(paaa->Header.AceType == ACCESS_DENIED_ACE_TYPE, 
681                             "Invalid ACE type %d\n", paaa->Header.AceType); 
682                     /* first one of two ACEs generated from inheritable entry - without inheritance */
683                     ok(paaa->Header.AceFlags == 0, "Invalid ACE flags %x\n", paaa->Header.AceFlags);
684                     ok(paaa->Mask == GENERIC_WRITE, "Invalid ACE mask %x\n", paaa->Mask);
685
686                     ok(GetAce(pAcl, 2, (LPVOID*)&paaa), "GetAce failed with error %u\n", GetLastError());
687                     ok(paaa->Header.AceType == ACCESS_DENIED_ACE_TYPE, 
688                             "Invalid ACE type %d\n", paaa->Header.AceType); 
689                     /* second ACE - with inheritance */
690                     ok(paaa->Header.AceFlags == MY_INHERITANCE,
691                             "Invalid ACE flags %x\n", paaa->Header.AceFlags);
692                     ok(paaa->Mask == GENERIC_READ, "Invalid ACE mask %x\n", paaa->Mask);
693                 }
694             }
695         }
696
697         LocalFree(psd);
698     }
699 }
700
701 static void test_SHPackDispParams(void)
702 {
703     DISPPARAMS params;
704     VARIANT vars[10];
705     HRESULT hres;
706
707     if(!pSHPackDispParams)
708         win_skip("SHPackSidpParams not available\n");
709
710     memset(&params, 0xc0, sizeof(params));
711     memset(vars, 0xc0, sizeof(vars));
712     hres = pSHPackDispParams(&params, vars, 1, VT_I4, 0xdeadbeef);
713     ok(hres == S_OK, "SHPackDispParams failed: %08x\n", hres);
714     ok(params.cArgs == 1, "params.cArgs = %d\n", params.cArgs);
715     ok(params.cNamedArgs == 0, "params.cNamedArgs = %d\n", params.cArgs);
716     ok(params.rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", params.rgdispidNamedArgs);
717     ok(params.rgvarg == vars, "params.rgvarg = %p\n", params.rgvarg);
718     ok(V_VT(vars) == VT_I4, "V_VT(var) = %d\n", V_VT(vars));
719     ok(V_I4(vars) == 0xdeadbeef, "failed %x\n", V_I4(vars));
720
721     memset(&params, 0xc0, sizeof(params));
722     hres = pSHPackDispParams(&params, NULL, 0, 0);
723     ok(hres == S_OK, "SHPackDispParams failed: %08x\n", hres);
724     ok(params.cArgs == 0, "params.cArgs = %d\n", params.cArgs);
725     ok(params.cNamedArgs == 0, "params.cNamedArgs = %d\n", params.cArgs);
726     ok(params.rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", params.rgdispidNamedArgs);
727     ok(params.rgvarg == NULL, "params.rgvarg = %p\n", params.rgvarg);
728
729     memset(vars, 0xc0, sizeof(vars));
730     memset(&params, 0xc0, sizeof(params));
731     hres = pSHPackDispParams(&params, vars, 4, VT_BSTR, (void*)0xdeadbeef, VT_EMPTY, 10,
732             VT_I4, 100, VT_DISPATCH, (void*)0xdeadbeef);
733     ok(hres == S_OK, "SHPackDispParams failed: %08x\n", hres);
734     ok(params.cArgs == 4, "params.cArgs = %d\n", params.cArgs);
735     ok(params.cNamedArgs == 0, "params.cNamedArgs = %d\n", params.cArgs);
736     ok(params.rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", params.rgdispidNamedArgs);
737     ok(params.rgvarg == vars, "params.rgvarg = %p\n", params.rgvarg);
738     ok(V_VT(vars) == VT_DISPATCH, "V_VT(vars[0]) = %x\n", V_VT(vars));
739     ok(V_I4(vars) == 0xdeadbeef, "V_I4(vars[0]) = %x\n", V_I4(vars));
740     ok(V_VT(vars+1) == VT_I4, "V_VT(vars[1]) = %d\n", V_VT(vars+1));
741     ok(V_I4(vars+1) == 100, "V_I4(vars[1]) = %x\n", V_I4(vars+1));
742     ok(V_VT(vars+2) == VT_I4, "V_VT(vars[2]) = %d\n", V_VT(vars+2));
743     ok(V_I4(vars+2) == 10, "V_I4(vars[2]) = %x\n", V_I4(vars+2));
744     ok(V_VT(vars+3) == VT_BSTR, "V_VT(vars[3]) = %d\n", V_VT(vars+3));
745     ok(V_BSTR(vars+3) == (void*)0xdeadbeef, "V_BSTR(vars[3]) = %p\n", V_BSTR(vars+3));
746 }
747
748 typedef struct _disp
749 {
750     IDispatch IDispatch_iface;
751     LONG   refCount;
752 } Disp;
753
754 static inline Disp *impl_from_IDispatch(IDispatch *iface)
755 {
756     return CONTAINING_RECORD(iface, Disp, IDispatch_iface);
757 }
758
759 typedef struct _contain
760 {
761     IConnectionPointContainer IConnectionPointContainer_iface;
762     LONG   refCount;
763
764     UINT  ptCount;
765     IConnectionPoint **pt;
766 } Contain;
767
768 static inline Contain *impl_from_IConnectionPointContainer(IConnectionPointContainer *iface)
769 {
770     return CONTAINING_RECORD(iface, Contain, IConnectionPointContainer_iface);
771 }
772
773 typedef struct _cntptn
774 {
775     IConnectionPoint IConnectionPoint_iface;
776     LONG refCount;
777
778     Contain *container;
779     GUID  id;
780     UINT  sinkCount;
781     IUnknown **sink;
782 } ConPt;
783
784 static inline ConPt *impl_from_IConnectionPoint(IConnectionPoint *iface)
785 {
786     return CONTAINING_RECORD(iface, ConPt, IConnectionPoint_iface);
787 }
788
789 typedef struct _enum
790 {
791     IEnumConnections IEnumConnections_iface;
792     LONG   refCount;
793
794     UINT idx;
795     ConPt *pt;
796 } EnumCon;
797
798 static inline EnumCon *impl_from_IEnumConnections(IEnumConnections *iface)
799 {
800     return CONTAINING_RECORD(iface, EnumCon, IEnumConnections_iface);
801 }
802
803 typedef struct _enumpt
804 {
805     IEnumConnectionPoints IEnumConnectionPoints_iface;
806     LONG   refCount;
807
808     int idx;
809     Contain *container;
810 } EnumPt;
811
812 static inline EnumPt *impl_from_IEnumConnectionPoints(IEnumConnectionPoints *iface)
813 {
814     return CONTAINING_RECORD(iface, EnumPt, IEnumConnectionPoints_iface);
815 }
816
817
818 static HRESULT WINAPI Disp_QueryInterface(
819         IDispatch* This,
820         REFIID riid,
821         void **ppvObject)
822 {
823     *ppvObject = NULL;
824
825     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch))
826     {
827         *ppvObject = This;
828     }
829
830     if (*ppvObject)
831     {
832         IUnknown_AddRef(This);
833         return S_OK;
834     }
835
836     trace("no interface\n");
837     return E_NOINTERFACE;
838 }
839
840 static ULONG WINAPI Disp_AddRef(IDispatch* This)
841 {
842     Disp *iface = impl_from_IDispatch(This);
843     return InterlockedIncrement(&iface->refCount);
844 }
845
846 static ULONG WINAPI Disp_Release(IDispatch* This)
847 {
848     Disp *iface = impl_from_IDispatch(This);
849     ULONG ret;
850
851     ret = InterlockedDecrement(&iface->refCount);
852     if (ret == 0)
853         HeapFree(GetProcessHeap(),0,This);
854     return ret;
855 }
856
857 static HRESULT WINAPI Disp_GetTypeInfoCount(
858         IDispatch* This,
859         UINT *pctinfo)
860 {
861     return ERROR_SUCCESS;
862 }
863
864 static HRESULT WINAPI Disp_GetTypeInfo(
865         IDispatch* This,
866         UINT iTInfo,
867         LCID lcid,
868         ITypeInfo **ppTInfo)
869 {
870     return ERROR_SUCCESS;
871 }
872
873 static HRESULT WINAPI Disp_GetIDsOfNames(
874         IDispatch* This,
875         REFIID riid,
876         LPOLESTR *rgszNames,
877         UINT cNames,
878         LCID lcid,
879         DISPID *rgDispId)
880 {
881     return ERROR_SUCCESS;
882 }
883
884 static HRESULT WINAPI Disp_Invoke(
885         IDispatch* This,
886         DISPID dispIdMember,
887         REFIID riid,
888         LCID lcid,
889         WORD wFlags,
890         DISPPARAMS *pDispParams,
891         VARIANT *pVarResult,
892         EXCEPINFO *pExcepInfo,
893         UINT *puArgErr)
894 {
895     trace("%p %x %p %x %x %p %p %p %p\n",This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
896
897     ok(dispIdMember == 0xa0 || dispIdMember == 0xa1, "Unknown dispIdMember\n");
898     ok(pDispParams != NULL, "Invoked with NULL pDispParams\n");
899     ok(wFlags == DISPATCH_METHOD, "Wrong flags %x\n",wFlags);
900     ok(lcid == 0,"Wrong lcid %x\n",lcid);
901     if (dispIdMember == 0xa0)
902     {
903         ok(pDispParams->cArgs == 0, "params.cArgs = %d\n", pDispParams->cArgs);
904         ok(pDispParams->cNamedArgs == 0, "params.cNamedArgs = %d\n", pDispParams->cArgs);
905         ok(pDispParams->rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", pDispParams->rgdispidNamedArgs);
906         ok(pDispParams->rgvarg == NULL, "params.rgvarg = %p\n", pDispParams->rgvarg);
907     }
908     else if (dispIdMember == 0xa1)
909     {
910         ok(pDispParams->cArgs == 2, "params.cArgs = %d\n", pDispParams->cArgs);
911         ok(pDispParams->cNamedArgs == 0, "params.cNamedArgs = %d\n", pDispParams->cArgs);
912         ok(pDispParams->rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", pDispParams->rgdispidNamedArgs);
913         ok(V_VT(pDispParams->rgvarg) == VT_BSTR, "V_VT(var) = %d\n", V_VT(pDispParams->rgvarg));
914         ok(V_I4(pDispParams->rgvarg) == 0xdeadcafe , "failed %p\n", V_BSTR(pDispParams->rgvarg));
915         ok(V_VT(pDispParams->rgvarg+1) == VT_I4, "V_VT(var) = %d\n", V_VT(pDispParams->rgvarg+1));
916         ok(V_I4(pDispParams->rgvarg+1) == 0xdeadbeef, "failed %x\n", V_I4(pDispParams->rgvarg+1));
917     }
918
919     return ERROR_SUCCESS;
920 }
921
922 static const IDispatchVtbl disp_vtbl = {
923     Disp_QueryInterface,
924     Disp_AddRef,
925     Disp_Release,
926
927     Disp_GetTypeInfoCount,
928     Disp_GetTypeInfo,
929     Disp_GetIDsOfNames,
930     Disp_Invoke
931 };
932
933 static HRESULT WINAPI Enum_QueryInterface(
934         IEnumConnections* This,
935         REFIID riid,
936         void **ppvObject)
937 {
938     *ppvObject = NULL;
939
940     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IEnumConnections))
941     {
942         *ppvObject = This;
943     }
944
945     if (*ppvObject)
946     {
947         IUnknown_AddRef(This);
948         return S_OK;
949     }
950
951     trace("no interface\n");
952     return E_NOINTERFACE;
953 }
954
955 static ULONG WINAPI Enum_AddRef(IEnumConnections* This)
956 {
957     EnumCon *iface = impl_from_IEnumConnections(This);
958     return InterlockedIncrement(&iface->refCount);
959 }
960
961 static ULONG WINAPI Enum_Release(IEnumConnections* This)
962 {
963     EnumCon *iface = impl_from_IEnumConnections(This);
964     ULONG ret;
965
966     ret = InterlockedDecrement(&iface->refCount);
967     if (ret == 0)
968         HeapFree(GetProcessHeap(),0,This);
969     return ret;
970 }
971
972 static HRESULT WINAPI Enum_Next(
973         IEnumConnections* This,
974         ULONG cConnections,
975         LPCONNECTDATA rgcd,
976         ULONG *pcFetched)
977 {
978     EnumCon *iface = impl_from_IEnumConnections(This);
979
980     if (cConnections > 0 && iface->idx < iface->pt->sinkCount)
981     {
982         rgcd->pUnk = iface->pt->sink[iface->idx];
983         IUnknown_AddRef(iface->pt->sink[iface->idx]);
984         rgcd->dwCookie=0xff;
985         if (pcFetched)
986             *pcFetched = 1;
987         iface->idx++;
988         return S_OK;
989     }
990
991     return E_FAIL;
992 }
993
994 static HRESULT WINAPI Enum_Skip(
995         IEnumConnections* This,
996         ULONG cConnections)
997 {
998     return E_FAIL;
999 }
1000
1001 static HRESULT WINAPI Enum_Reset(
1002         IEnumConnections* This)
1003 {
1004     return E_FAIL;
1005 }
1006
1007 static HRESULT WINAPI Enum_Clone(
1008         IEnumConnections* This,
1009         IEnumConnections **ppEnum)
1010 {
1011     return E_FAIL;
1012 }
1013
1014 static const IEnumConnectionsVtbl enum_vtbl = {
1015
1016     Enum_QueryInterface,
1017     Enum_AddRef,
1018     Enum_Release,
1019     Enum_Next,
1020     Enum_Skip,
1021     Enum_Reset,
1022     Enum_Clone
1023 };
1024
1025 static HRESULT WINAPI ConPt_QueryInterface(
1026         IConnectionPoint* This,
1027         REFIID riid,
1028         void **ppvObject)
1029 {
1030     *ppvObject = NULL;
1031
1032     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IConnectionPoint))
1033     {
1034         *ppvObject = This;
1035     }
1036
1037     if (*ppvObject)
1038     {
1039         IUnknown_AddRef(This);
1040         return S_OK;
1041     }
1042
1043     trace("no interface\n");
1044     return E_NOINTERFACE;
1045 }
1046
1047 static ULONG WINAPI ConPt_AddRef(
1048         IConnectionPoint* This)
1049 {
1050     ConPt *iface = impl_from_IConnectionPoint(This);
1051     return InterlockedIncrement(&iface->refCount);
1052 }
1053
1054 static ULONG WINAPI ConPt_Release(
1055         IConnectionPoint* This)
1056 {
1057     ConPt *iface = impl_from_IConnectionPoint(This);
1058     ULONG ret;
1059
1060     ret = InterlockedDecrement(&iface->refCount);
1061     if (ret == 0)
1062     {
1063         if (iface->sinkCount > 0)
1064         {
1065             int i;
1066             for (i = 0; i < iface->sinkCount; i++)
1067             {
1068                 if (iface->sink[i])
1069                     IUnknown_Release(iface->sink[i]);
1070             }
1071             HeapFree(GetProcessHeap(),0,iface->sink);
1072         }
1073         HeapFree(GetProcessHeap(),0,This);
1074     }
1075     return ret;
1076 }
1077
1078 static HRESULT WINAPI ConPt_GetConnectionInterface(
1079         IConnectionPoint* This,
1080         IID *pIID)
1081 {
1082     static int i = 0;
1083     ConPt *iface = impl_from_IConnectionPoint(This);
1084     if (i==0)
1085     {
1086         i++;
1087         return E_FAIL;
1088     }
1089     else
1090         memcpy(pIID,&iface->id,sizeof(GUID));
1091     return S_OK;
1092 }
1093
1094 static HRESULT WINAPI ConPt_GetConnectionPointContainer(
1095         IConnectionPoint* This,
1096         IConnectionPointContainer **ppCPC)
1097 {
1098     ConPt *iface = impl_from_IConnectionPoint(This);
1099
1100     *ppCPC = &iface->container->IConnectionPointContainer_iface;
1101     return S_OK;
1102 }
1103
1104 static HRESULT WINAPI ConPt_Advise(
1105         IConnectionPoint* This,
1106         IUnknown *pUnkSink,
1107         DWORD *pdwCookie)
1108 {
1109     ConPt *iface = impl_from_IConnectionPoint(This);
1110
1111     if (iface->sinkCount == 0)
1112         iface->sink = HeapAlloc(GetProcessHeap(),0,sizeof(IUnknown*));
1113     else
1114         iface->sink = HeapReAlloc(GetProcessHeap(),0,iface->sink,sizeof(IUnknown*)*(iface->sinkCount+1));
1115     iface->sink[iface->sinkCount] = pUnkSink;
1116     IUnknown_AddRef(pUnkSink);
1117     iface->sinkCount++;
1118     *pdwCookie = iface->sinkCount;
1119     return S_OK;
1120 }
1121
1122 static HRESULT WINAPI ConPt_Unadvise(
1123         IConnectionPoint* This,
1124         DWORD dwCookie)
1125 {
1126     ConPt *iface = impl_from_IConnectionPoint(This);
1127
1128     if (dwCookie > iface->sinkCount)
1129         return E_FAIL;
1130     else
1131     {
1132         IUnknown_Release(iface->sink[dwCookie-1]);
1133         iface->sink[dwCookie-1] = NULL;
1134     }
1135     return S_OK;
1136 }
1137
1138 static HRESULT WINAPI ConPt_EnumConnections(
1139         IConnectionPoint* This,
1140         IEnumConnections **ppEnum)
1141 {
1142     EnumCon *ec;
1143
1144     ec = HeapAlloc(GetProcessHeap(),0,sizeof(EnumCon));
1145     ec->IEnumConnections_iface.lpVtbl = &enum_vtbl;
1146     ec->refCount = 1;
1147     ec->pt = impl_from_IConnectionPoint(This);
1148     ec->idx = 0;
1149     *ppEnum = &ec->IEnumConnections_iface;
1150
1151     return S_OK;
1152 }
1153
1154 static const IConnectionPointVtbl point_vtbl = {
1155     ConPt_QueryInterface,
1156     ConPt_AddRef,
1157     ConPt_Release,
1158
1159     ConPt_GetConnectionInterface,
1160     ConPt_GetConnectionPointContainer,
1161     ConPt_Advise,
1162     ConPt_Unadvise,
1163     ConPt_EnumConnections
1164 };
1165
1166 static HRESULT WINAPI EnumPt_QueryInterface(
1167         IEnumConnectionPoints* This,
1168         REFIID riid,
1169         void **ppvObject)
1170 {
1171     *ppvObject = NULL;
1172
1173     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IEnumConnectionPoints))
1174     {
1175         *ppvObject = This;
1176     }
1177
1178     if (*ppvObject)
1179     {
1180         IUnknown_AddRef(This);
1181         return S_OK;
1182     }
1183
1184     trace("no interface\n");
1185     return E_NOINTERFACE;
1186 }
1187
1188 static ULONG WINAPI EnumPt_AddRef(IEnumConnectionPoints* This)
1189 {
1190     EnumPt *iface = impl_from_IEnumConnectionPoints(This);
1191     return InterlockedIncrement(&iface->refCount);
1192 }
1193
1194 static ULONG WINAPI EnumPt_Release(IEnumConnectionPoints* This)
1195 {
1196     EnumPt *iface = impl_from_IEnumConnectionPoints(This);
1197     ULONG ret;
1198
1199     ret = InterlockedDecrement(&iface->refCount);
1200     if (ret == 0)
1201         HeapFree(GetProcessHeap(),0,This);
1202     return ret;
1203 }
1204
1205 static HRESULT WINAPI EnumPt_Next(
1206         IEnumConnectionPoints* This,
1207         ULONG cConnections,
1208         IConnectionPoint **rgcd,
1209         ULONG *pcFetched)
1210 {
1211     EnumPt *iface = impl_from_IEnumConnectionPoints(This);
1212
1213     if (cConnections > 0 && iface->idx < iface->container->ptCount)
1214     {
1215         *rgcd = iface->container->pt[iface->idx];
1216         IUnknown_AddRef(iface->container->pt[iface->idx]);
1217         if (pcFetched)
1218             *pcFetched = 1;
1219         iface->idx++;
1220         return S_OK;
1221     }
1222
1223     return E_FAIL;
1224 }
1225
1226 static HRESULT WINAPI EnumPt_Skip(
1227         IEnumConnectionPoints* This,
1228         ULONG cConnections)
1229 {
1230     return E_FAIL;
1231 }
1232
1233 static HRESULT WINAPI EnumPt_Reset(
1234         IEnumConnectionPoints* This)
1235 {
1236     return E_FAIL;
1237 }
1238
1239 static HRESULT WINAPI EnumPt_Clone(
1240         IEnumConnectionPoints* This,
1241         IEnumConnectionPoints **ppEnumPt)
1242 {
1243     return E_FAIL;
1244 }
1245
1246 static const IEnumConnectionPointsVtbl enumpt_vtbl = {
1247
1248     EnumPt_QueryInterface,
1249     EnumPt_AddRef,
1250     EnumPt_Release,
1251     EnumPt_Next,
1252     EnumPt_Skip,
1253     EnumPt_Reset,
1254     EnumPt_Clone
1255 };
1256
1257 static HRESULT WINAPI Contain_QueryInterface(
1258         IConnectionPointContainer* This,
1259         REFIID riid,
1260         void **ppvObject)
1261 {
1262     *ppvObject = NULL;
1263
1264     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IConnectionPointContainer))
1265     {
1266         *ppvObject = This;
1267     }
1268
1269     if (*ppvObject)
1270     {
1271         IUnknown_AddRef(This);
1272         return S_OK;
1273     }
1274
1275     trace("no interface\n");
1276     return E_NOINTERFACE;
1277 }
1278
1279 static ULONG WINAPI Contain_AddRef(
1280         IConnectionPointContainer* This)
1281 {
1282     Contain *iface = impl_from_IConnectionPointContainer(This);
1283     return InterlockedIncrement(&iface->refCount);
1284 }
1285
1286 static ULONG WINAPI Contain_Release(
1287         IConnectionPointContainer* This)
1288 {
1289     Contain *iface = impl_from_IConnectionPointContainer(This);
1290     ULONG ret;
1291
1292     ret = InterlockedDecrement(&iface->refCount);
1293     if (ret == 0)
1294     {
1295         if (iface->ptCount > 0)
1296         {
1297             int i;
1298             for (i = 0; i < iface->ptCount; i++)
1299                 IUnknown_Release(iface->pt[i]);
1300             HeapFree(GetProcessHeap(),0,iface->pt);
1301         }
1302         HeapFree(GetProcessHeap(),0,This);
1303     }
1304     return ret;
1305 }
1306
1307 static HRESULT WINAPI Contain_EnumConnectionPoints(
1308         IConnectionPointContainer* This,
1309         IEnumConnectionPoints **ppEnum)
1310 {
1311     EnumPt *ec;
1312
1313     ec = HeapAlloc(GetProcessHeap(),0,sizeof(EnumPt));
1314     ec->IEnumConnectionPoints_iface.lpVtbl = &enumpt_vtbl;
1315     ec->refCount = 1;
1316     ec->idx= 0;
1317     ec->container = impl_from_IConnectionPointContainer(This);
1318     *ppEnum = &ec->IEnumConnectionPoints_iface;
1319
1320     return S_OK;
1321 }
1322
1323 static HRESULT WINAPI Contain_FindConnectionPoint(
1324         IConnectionPointContainer* This,
1325         REFIID riid,
1326         IConnectionPoint **ppCP)
1327 {
1328     Contain *iface = impl_from_IConnectionPointContainer(This);
1329     ConPt *pt;
1330
1331     if (!IsEqualIID(riid, &IID_NULL) || iface->ptCount ==0)
1332     {
1333         pt = HeapAlloc(GetProcessHeap(),0,sizeof(ConPt));
1334         pt->IConnectionPoint_iface.lpVtbl = &point_vtbl;
1335         pt->refCount = 1;
1336         pt->sinkCount = 0;
1337         pt->sink = NULL;
1338         pt->container = iface;
1339         pt->id = IID_IDispatch;
1340
1341         if (iface->ptCount == 0)
1342             iface->pt =HeapAlloc(GetProcessHeap(),0,sizeof(IUnknown*));
1343         else
1344             iface->pt = HeapReAlloc(GetProcessHeap(),0,iface->pt,sizeof(IUnknown*)*(iface->ptCount+1));
1345         iface->pt[iface->ptCount] = &pt->IConnectionPoint_iface;
1346         iface->ptCount++;
1347
1348         *ppCP = &pt->IConnectionPoint_iface;
1349     }
1350     else
1351     {
1352         *ppCP = iface->pt[0];
1353         IUnknown_AddRef((IUnknown*)*ppCP);
1354     }
1355
1356     return S_OK;
1357 }
1358
1359 static const IConnectionPointContainerVtbl contain_vtbl = {
1360     Contain_QueryInterface,
1361     Contain_AddRef,
1362     Contain_Release,
1363
1364     Contain_EnumConnectionPoints,
1365     Contain_FindConnectionPoint
1366 };
1367
1368 static void test_IConnectionPoint(void)
1369 {
1370     HRESULT rc;
1371     ULONG ref;
1372     IConnectionPoint *point;
1373     Contain *container;
1374     Disp *dispatch;
1375     DWORD cookie = 0xffffffff;
1376     DISPPARAMS params;
1377     VARIANT vars[10];
1378
1379     if (!pIConnectionPoint_SimpleInvoke || !pConnectToConnectionPoint)
1380     {
1381         win_skip("IConnectionPoint Apis not present\n");
1382         return;
1383     }
1384
1385     container = HeapAlloc(GetProcessHeap(),0,sizeof(Contain));
1386     container->IConnectionPointContainer_iface.lpVtbl = &contain_vtbl;
1387     container->refCount = 1;
1388     container->ptCount = 0;
1389     container->pt = NULL;
1390
1391     dispatch = HeapAlloc(GetProcessHeap(),0,sizeof(Disp));
1392     dispatch->IDispatch_iface.lpVtbl = &disp_vtbl;
1393     dispatch->refCount = 1;
1394
1395     rc = pConnectToConnectionPoint((IUnknown*)dispatch, &IID_NULL, TRUE, (IUnknown*)container, &cookie, &point);
1396     ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
1397     ok(point != NULL, "returned ConnectionPoint is NULL\n");
1398     ok(cookie != 0xffffffff, "invalid cookie returned\n");
1399
1400     rc = pIConnectionPoint_SimpleInvoke(point,0xa0,NULL);
1401     ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
1402
1403     if (pSHPackDispParams)
1404     {
1405         memset(&params, 0xc0, sizeof(params));
1406         memset(vars, 0xc0, sizeof(vars));
1407         rc = pSHPackDispParams(&params, vars, 2, VT_I4, 0xdeadbeef, VT_BSTR, 0xdeadcafe);
1408         ok(rc == S_OK, "SHPackDispParams failed: %08x\n", rc);
1409
1410         rc = pIConnectionPoint_SimpleInvoke(point,0xa1,&params);
1411         ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
1412     }
1413     else
1414         win_skip("pSHPackDispParams not present\n");
1415
1416     rc = pConnectToConnectionPoint(NULL, &IID_NULL, FALSE, (IUnknown*)container, &cookie, NULL);
1417     ok(rc == S_OK, "pConnectToConnectionPoint failed with %x\n",rc);
1418
1419 /* MSDN says this should be required but it crashs on XP
1420     IUnknown_Release(point);
1421 */
1422     ref = IUnknown_Release((IUnknown*)container);
1423     ok(ref == 0, "leftover IConnectionPointContainer reference %i\n",ref);
1424     ref = IUnknown_Release((IUnknown*)dispatch);
1425     ok(ref == 0, "leftover IDispatch reference %i\n",ref);
1426 }
1427
1428 typedef struct _propbag
1429 {
1430     IPropertyBag IPropertyBag_iface;
1431     LONG   refCount;
1432
1433 } PropBag;
1434
1435 static inline PropBag *impl_from_IPropertyBag(IPropertyBag *iface)
1436 {
1437     return CONTAINING_RECORD(iface, PropBag, IPropertyBag_iface);
1438 }
1439
1440
1441 static HRESULT WINAPI Prop_QueryInterface(
1442         IPropertyBag* This,
1443         REFIID riid,
1444         void **ppvObject)
1445 {
1446     *ppvObject = NULL;
1447
1448     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IPropertyBag))
1449     {
1450         *ppvObject = This;
1451     }
1452
1453     if (*ppvObject)
1454     {
1455         IUnknown_AddRef(This);
1456         return S_OK;
1457     }
1458
1459     trace("no interface\n");
1460     return E_NOINTERFACE;
1461 }
1462
1463 static ULONG WINAPI Prop_AddRef(
1464         IPropertyBag* This)
1465 {
1466     PropBag *iface = impl_from_IPropertyBag(This);
1467     return InterlockedIncrement(&iface->refCount);
1468 }
1469
1470 static ULONG WINAPI Prop_Release(
1471         IPropertyBag* This)
1472 {
1473     PropBag *iface = impl_from_IPropertyBag(This);
1474     ULONG ret;
1475
1476     ret = InterlockedDecrement(&iface->refCount);
1477     if (ret == 0)
1478         HeapFree(GetProcessHeap(),0,This);
1479     return ret;
1480 }
1481
1482 static HRESULT WINAPI Prop_Read(
1483         IPropertyBag* This,
1484         LPCOLESTR pszPropName,
1485         VARIANT *pVar,
1486         IErrorLog *pErrorLog)
1487 {
1488     V_VT(pVar) = VT_BLOB|VT_BYREF;
1489     V_BYREF(pVar) = (LPVOID)0xdeadcafe;
1490     return S_OK;
1491 }
1492
1493 static HRESULT WINAPI Prop_Write(
1494         IPropertyBag* This,
1495         LPCOLESTR pszPropName,
1496         VARIANT *pVar)
1497 {
1498     return S_OK;
1499 }
1500
1501
1502 static const IPropertyBagVtbl prop_vtbl = {
1503     Prop_QueryInterface,
1504     Prop_AddRef,
1505     Prop_Release,
1506
1507     Prop_Read,
1508     Prop_Write
1509 };
1510
1511 static void test_SHPropertyBag_ReadLONG(void)
1512 {
1513     PropBag *pb;
1514     HRESULT rc;
1515     LONG out;
1516     static const WCHAR szName1[] = {'n','a','m','e','1',0};
1517
1518     if (!pSHPropertyBag_ReadLONG)
1519     {
1520         win_skip("SHPropertyBag_ReadLONG not present\n");
1521         return;
1522     }
1523
1524     pb = HeapAlloc(GetProcessHeap(),0,sizeof(PropBag));
1525     pb->refCount = 1;
1526     pb->IPropertyBag_iface.lpVtbl = &prop_vtbl;
1527
1528     out = 0xfeedface;
1529     rc = pSHPropertyBag_ReadLONG(NULL, szName1, &out);
1530     ok(rc == E_INVALIDARG || broken(rc == 0), "incorrect return %x\n",rc);
1531     ok(out == 0xfeedface, "value should not have changed\n");
1532     rc = pSHPropertyBag_ReadLONG(&pb->IPropertyBag_iface, NULL, &out);
1533     ok(rc == E_INVALIDARG || broken(rc == 0) || broken(rc == 1), "incorrect return %x\n",rc);
1534     ok(out == 0xfeedface, "value should not have changed\n");
1535     rc = pSHPropertyBag_ReadLONG(&pb->IPropertyBag_iface, szName1, NULL);
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, &out);
1539     ok(rc == DISP_E_BADVARTYPE || broken(rc == 0) || broken(rc == 1), "incorrect return %x\n",rc);
1540     ok(out == 0xfeedface  || broken(out == 0xfeedfa00), "value should not have changed %x\n",out);
1541     IUnknown_Release((IUnknown*)pb);
1542 }
1543
1544
1545
1546 static void test_SHSetWindowBits(void)
1547 {
1548     HWND hwnd;
1549     DWORD style, styleold;
1550     WNDCLASSA clsA;
1551
1552     if(!pSHSetWindowBits)
1553     {
1554         win_skip("SHSetWindowBits is not available\n");
1555         return;
1556     }
1557
1558     clsA.style = 0;
1559     clsA.lpfnWndProc = DefWindowProcA;
1560     clsA.cbClsExtra = 0;
1561     clsA.cbWndExtra = 0;
1562     clsA.hInstance = GetModuleHandleA(NULL);
1563     clsA.hIcon = 0;
1564     clsA.hCursor = LoadCursorA(0, IDC_ARROW);
1565     clsA.hbrBackground = NULL;
1566     clsA.lpszMenuName = NULL;
1567     clsA.lpszClassName = "Shlwapi test class";
1568     RegisterClassA(&clsA);
1569
1570     hwnd = CreateWindowA("Shlwapi test class", "Test", WS_VISIBLE, 0, 0, 100, 100,
1571                           NULL, NULL, GetModuleHandle(NULL), 0);
1572     ok(IsWindow(hwnd), "failed to create window\n");
1573
1574     /* null window */
1575     SetLastError(0xdeadbeef);
1576     style = pSHSetWindowBits(NULL, GWL_STYLE, 0, 0);
1577     ok(style == 0, "expected 0 retval, got %d\n", style);
1578     ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE ||
1579         broken(GetLastError() == 0xdeadbeef), /* Win9x/WinMe */
1580         "expected ERROR_INVALID_WINDOW_HANDLE, got %d\n", GetLastError());
1581
1582     /* zero mask, zero flags */
1583     styleold = GetWindowLongA(hwnd, GWL_STYLE);
1584     style = pSHSetWindowBits(hwnd, GWL_STYLE, 0, 0);
1585     ok(styleold == style, "expected old style\n");
1586     ok(styleold == GetWindowLongA(hwnd, GWL_STYLE), "expected to keep old style\n");
1587
1588     /* test mask */
1589     styleold = GetWindowLongA(hwnd, GWL_STYLE);
1590     ok(styleold & WS_VISIBLE, "expected WS_VISIBLE\n");
1591     style = pSHSetWindowBits(hwnd, GWL_STYLE, WS_VISIBLE, 0);
1592
1593     ok(style == styleold, "expected previous style, got %x\n", style);
1594     ok((GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE) == 0, "expected updated style\n");
1595
1596     /* test mask, unset style bit used */
1597     styleold = GetWindowLongA(hwnd, GWL_STYLE);
1598     style = pSHSetWindowBits(hwnd, GWL_STYLE, WS_VISIBLE, 0);
1599     ok(style == styleold, "expected previous style, got %x\n", style);
1600     ok(styleold == GetWindowLongA(hwnd, GWL_STYLE), "expected to keep old style\n");
1601
1602     /* set back with flags */
1603     styleold = GetWindowLongA(hwnd, GWL_STYLE);
1604     style = pSHSetWindowBits(hwnd, GWL_STYLE, WS_VISIBLE, WS_VISIBLE);
1605     ok(style == styleold, "expected previous style, got %x\n", style);
1606     ok(GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE, "expected updated style\n");
1607
1608     /* reset and try to set without a mask */
1609     pSHSetWindowBits(hwnd, GWL_STYLE, WS_VISIBLE, 0);
1610     ok((GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE) == 0, "expected updated style\n");
1611     styleold = GetWindowLongA(hwnd, GWL_STYLE);
1612     style = pSHSetWindowBits(hwnd, GWL_STYLE, 0, WS_VISIBLE);
1613     ok(style == styleold, "expected previous style, got %x\n", style);
1614     ok((GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE) == 0, "expected updated style\n");
1615
1616     DestroyWindow(hwnd);
1617
1618     UnregisterClassA("Shlwapi test class", GetModuleHandleA(NULL));
1619 }
1620
1621 static void test_SHFormatDateTimeA(void)
1622 {
1623     FILETIME UNALIGNED filetime;
1624     CHAR buff[100], buff2[100], buff3[100];
1625     SYSTEMTIME st;
1626     DWORD flags;
1627     INT ret;
1628
1629     if(!pSHFormatDateTimeA)
1630     {
1631         win_skip("pSHFormatDateTimeA isn't available\n");
1632         return;
1633     }
1634
1635 if (0)
1636 {
1637     /* crashes on native */
1638     ret = pSHFormatDateTimeA(NULL, NULL, NULL, 0);
1639 }
1640
1641     GetLocalTime(&st);
1642     SystemTimeToFileTime(&st, &filetime);
1643     /* SHFormatDateTime expects input as utc */
1644     LocalFileTimeToFileTime(&filetime, &filetime);
1645
1646     /* no way to get required buffer length here */
1647     SetLastError(0xdeadbeef);
1648     ret = pSHFormatDateTimeA(&filetime, NULL, NULL, 0);
1649     ok(ret == 0, "got %d\n", ret);
1650     ok(GetLastError() == 0xdeadbeef || broken(GetLastError() == ERROR_SUCCESS /* Win7 */),
1651         "expected 0xdeadbeef, got %d\n", GetLastError());
1652
1653     SetLastError(0xdeadbeef);
1654     buff[0] = 'a'; buff[1] = 0;
1655     ret = pSHFormatDateTimeA(&filetime, NULL, buff, 0);
1656     ok(ret == 0, "got %d\n", ret);
1657     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1658     ok(buff[0] == 'a', "expected same string, got %s\n", buff);
1659
1660     /* flags needs to have FDTF_NOAUTOREADINGORDER for these tests to succeed on Vista+ */
1661
1662     /* all combinations documented as invalid succeeded */
1663     flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTTIME | FDTF_LONGTIME;
1664     SetLastError(0xdeadbeef);
1665     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1666     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1667     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1668
1669     flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTDATE | FDTF_LONGDATE;
1670     SetLastError(0xdeadbeef);
1671     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1672     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1673     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1674
1675     flags =  FDTF_SHORTDATE | FDTF_LTRDATE | FDTF_RTLDATE;
1676     SetLastError(0xdeadbeef);
1677     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1678     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1679     ok(GetLastError() == 0xdeadbeef ||
1680         broken(GetLastError() == ERROR_INVALID_FLAGS), /* Win9x/WinMe */
1681         "expected 0xdeadbeef, got %d\n", GetLastError());
1682
1683     /* now check returned strings */
1684     flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTTIME;
1685     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1686     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1687     ret = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff2, sizeof(buff2));
1688     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1689     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1690
1691     flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGTIME;
1692     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1693     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1694     ret = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buff2, sizeof(buff2));
1695     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1696     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1697
1698     /* both time flags */
1699     flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGTIME | FDTF_SHORTTIME;
1700     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1701     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1702     ret = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buff2, sizeof(buff2));
1703     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1704     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1705
1706     flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTDATE;
1707     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1708     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1709     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2));
1710     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1711     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1712
1713     flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGDATE;
1714     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1715     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1716     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2));
1717     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1718     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1719
1720     /* both date flags */
1721     flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGDATE | FDTF_SHORTDATE;
1722     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1723     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1724     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2));
1725     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1726     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1727
1728     /* various combinations of date/time flags */
1729     flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGDATE | FDTF_SHORTTIME;
1730     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1731     ok(ret == lstrlenA(buff)+1, "got %d, length %d\n", ret, lstrlenA(buff)+1);
1732     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2));
1733     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1734     strcat(buff2, ", ");
1735     ret = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3));
1736     ok(ret == lstrlenA(buff3)+1, "got %d\n", ret);
1737     strcat(buff2, buff3);
1738     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1739
1740     flags = FDTF_NOAUTOREADINGORDER | FDTF_LONGDATE | FDTF_LONGTIME;
1741     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1742     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1743     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2));
1744     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1745     strcat(buff2, ", ");
1746     ret = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3));
1747     ok(ret == lstrlenA(buff3)+1, "got %d\n", ret);
1748     strcat(buff2, buff3);
1749     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1750
1751     flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTDATE | FDTF_SHORTTIME;
1752     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1753     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1754     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2));
1755     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1756     strcat(buff2, " ");
1757     ret = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3));
1758     ok(ret == lstrlenA(buff3)+1, "got %d\n", ret);
1759     strcat(buff2, buff3);
1760     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1761
1762     flags = FDTF_NOAUTOREADINGORDER | FDTF_SHORTDATE | FDTF_LONGTIME;
1763     ret = pSHFormatDateTimeA(&filetime, &flags, buff, sizeof(buff));
1764     ok(ret == lstrlenA(buff)+1, "got %d\n", ret);
1765     ret = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2));
1766     ok(ret == lstrlenA(buff2)+1, "got %d\n", ret);
1767     strcat(buff2, " ");
1768     ret = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3));
1769     ok(ret == lstrlenA(buff3)+1, "got %d\n", ret);
1770     strcat(buff2, buff3);
1771     ok(lstrcmpA(buff, buff2) == 0, "expected (%s), got (%s)\n", buff2, buff);
1772 }
1773
1774 static void test_SHFormatDateTimeW(void)
1775 {
1776     FILETIME UNALIGNED filetime;
1777     WCHAR buff[100], buff2[100], buff3[100];
1778     SYSTEMTIME st;
1779     DWORD flags;
1780     INT ret;
1781     static const WCHAR spaceW[] = {' ',0};
1782     static const WCHAR commaW[] = {',',' ',0};
1783
1784     if(!pSHFormatDateTimeW)
1785     {
1786         win_skip("pSHFormatDateTimeW isn't available\n");
1787         return;
1788     }
1789
1790 if (0)
1791 {
1792     /* crashes on native */
1793     ret = pSHFormatDateTimeW(NULL, NULL, NULL, 0);
1794 }
1795
1796     GetLocalTime(&st);
1797     SystemTimeToFileTime(&st, &filetime);
1798     /* SHFormatDateTime expects input as utc */
1799     LocalFileTimeToFileTime(&filetime, &filetime);
1800
1801     /* no way to get required buffer length here */
1802     SetLastError(0xdeadbeef);
1803     ret = pSHFormatDateTimeW(&filetime, NULL, NULL, 0);
1804     ok(ret == 0, "expected 0, got %d\n", ret);
1805     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1806
1807     SetLastError(0xdeadbeef);
1808     buff[0] = 'a'; buff[1] = 0;
1809     ret = pSHFormatDateTimeW(&filetime, NULL, buff, 0);
1810     ok(ret == 0, "expected 0, got %d\n", ret);
1811     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1812     ok(buff[0] == 'a', "expected same string\n");
1813
1814     /* all combinations documented as invalid succeeded */
1815     flags = FDTF_SHORTTIME | FDTF_LONGTIME;
1816     SetLastError(0xdeadbeef);
1817     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1818     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1819        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1820     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1821
1822     flags = FDTF_SHORTDATE | FDTF_LONGDATE;
1823     SetLastError(0xdeadbeef);
1824     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1825     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1826        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1827     ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError());
1828
1829     flags = FDTF_SHORTDATE | FDTF_LTRDATE | FDTF_RTLDATE;
1830     SetLastError(0xdeadbeef);
1831     buff[0] = 0; /* NT4 doesn't clear the buffer on failure */
1832     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1833     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1834        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1835     ok(GetLastError() == 0xdeadbeef ||
1836         broken(GetLastError() == ERROR_INVALID_FLAGS), /* Win9x/WinMe/NT4 */
1837         "expected 0xdeadbeef, got %d\n", GetLastError());
1838
1839     /* now check returned strings */
1840     flags = FDTF_SHORTTIME;
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     SetLastError(0xdeadbeef);
1845     ret = GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1846     if (ret == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1847     {
1848         win_skip("Needed W-functions are not implemented\n");
1849         return;
1850     }
1851     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1852     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1853
1854     flags = FDTF_LONGTIME;
1855     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1856     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1857        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1858     ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1859     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1860     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1861
1862     /* both time flags */
1863     flags = FDTF_LONGTIME | FDTF_SHORTTIME;
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 string\n");
1870
1871     flags = FDTF_SHORTDATE;
1872     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1873     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1874        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1875     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1876     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1877     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1878
1879     flags = FDTF_LONGDATE;
1880     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1881     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1882        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1883     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1884     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1885     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1886
1887     /* both date flags */
1888     flags = FDTF_LONGDATE | FDTF_SHORTDATE;
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     /* various combinations of date/time flags */
1897     flags = FDTF_LONGDATE | FDTF_SHORTTIME;
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     lstrcatW(buff2, commaW);
1904     ret = GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR));
1905     ok(ret == lstrlenW(buff3)+1, "expected %d, got %d\n", lstrlenW(buff3)+1, ret);
1906     lstrcatW(buff2, buff3);
1907     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1908
1909     flags = FDTF_LONGDATE | FDTF_LONGTIME;
1910     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1911     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1912        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1913     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1914     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1915     lstrcatW(buff2, commaW);
1916     ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR));
1917     ok(ret == lstrlenW(buff3)+1, "expected %d, got %d\n", lstrlenW(buff3)+1, ret);
1918     lstrcatW(buff2, buff3);
1919     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1920
1921     flags = FDTF_SHORTDATE | FDTF_SHORTTIME;
1922     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1923     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1924        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1925     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1926     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1927     lstrcatW(buff2, spaceW);
1928     ret = GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR));
1929     ok(ret == lstrlenW(buff3)+1, "expected %d, got %d\n", lstrlenW(buff3)+1, ret);
1930     lstrcatW(buff2, buff3);
1931     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1932
1933     flags = FDTF_SHORTDATE | FDTF_LONGTIME;
1934     ret = pSHFormatDateTimeW(&filetime, &flags, buff, sizeof(buff)/sizeof(WCHAR));
1935     ok(ret == lstrlenW(buff)+1 || ret == lstrlenW(buff),
1936        "expected %d or %d, got %d\n", lstrlenW(buff)+1, lstrlenW(buff), ret);
1937     ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buff2, sizeof(buff2)/sizeof(WCHAR));
1938     ok(ret == lstrlenW(buff2)+1, "expected %d, got %d\n", lstrlenW(buff2)+1, ret);
1939     lstrcatW(buff2, spaceW);
1940     ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buff3, sizeof(buff3)/sizeof(WCHAR));
1941     ok(ret == lstrlenW(buff3)+1, "expected %d, got %d\n", lstrlenW(buff3)+1, ret);
1942     lstrcatW(buff2, buff3);
1943     ok(lstrcmpW(buff, buff2) == 0, "expected equal strings\n");
1944 }
1945
1946 static void test_SHGetObjectCompatFlags(void)
1947 {
1948     struct compat_value {
1949         CHAR nameA[30];
1950         DWORD value;
1951     };
1952
1953     struct compat_value values[] = {
1954         { "OTNEEDSSFCACHE", 0x1 },
1955         { "NO_WEBVIEW", 0x2 },
1956         { "UNBINDABLE", 0x4 },
1957         { "PINDLL", 0x8 },
1958         { "NEEDSFILESYSANCESTOR", 0x10 },
1959         { "NOTAFILESYSTEM", 0x20 },
1960         { "CTXMENU_NOVERBS", 0x40 },
1961         { "CTXMENU_LIMITEDQI", 0x80 },
1962         { "COCREATESHELLFOLDERONLY", 0x100 },
1963         { "NEEDSSTORAGEANCESTOR", 0x200 },
1964         { "NOLEGACYWEBVIEW", 0x400 },
1965         { "CTXMENU_XPQCMFLAGS", 0x1000 },
1966         { "NOIPROPERTYSTORE", 0x2000 }
1967     };
1968
1969     static const char compat_path[] = "Software\\Microsoft\\Windows\\CurrentVersion\\ShellCompatibility\\Objects";
1970     void *pColorAdjustLuma = GetProcAddress(hShlwapi, "ColorAdjustLuma");
1971     CHAR keyA[39]; /* {CLSID} */
1972     HKEY root;
1973     DWORD ret;
1974     int i;
1975
1976     if (!pSHGetObjectCompatFlags)
1977     {
1978         win_skip("SHGetObjectCompatFlags isn't available\n");
1979         return;
1980     }
1981
1982     if (pColorAdjustLuma && pColorAdjustLuma == pSHGetObjectCompatFlags) /* win2k */
1983     {
1984         win_skip("Skipping SHGetObjectCompatFlags, same ordinal used for ColorAdjustLuma\n");
1985         return;
1986     }
1987
1988     /* null args */
1989     ret = pSHGetObjectCompatFlags(NULL, NULL);
1990     ok(ret == 0, "got %d\n", ret);
1991
1992     ret = RegOpenKeyA(HKEY_LOCAL_MACHINE, compat_path, &root);
1993     if (ret != ERROR_SUCCESS)
1994     {
1995         skip("No compatibility class data found\n");
1996         return;
1997     }
1998
1999     for (i = 0; RegEnumKeyA(root, i, keyA, sizeof(keyA)) == ERROR_SUCCESS; i++)
2000     {
2001         HKEY clsid_key;
2002
2003         if (RegOpenKeyA(root, keyA, &clsid_key) == ERROR_SUCCESS)
2004         {
2005             CHAR valueA[30];
2006             DWORD expected = 0, got, length = sizeof(valueA);
2007             CLSID clsid;
2008             int v;
2009
2010             for (v = 0; RegEnumValueA(clsid_key, v, valueA, &length, NULL, NULL, NULL, NULL) == ERROR_SUCCESS; v++)
2011             {
2012                 int j;
2013
2014                 for (j = 0; j < sizeof(values)/sizeof(struct compat_value); j++)
2015                     if (lstrcmpA(values[j].nameA, valueA) == 0)
2016                     {
2017                         expected |= values[j].value;
2018                         break;
2019                     }
2020
2021                 length = sizeof(valueA);
2022             }
2023
2024             pGUIDFromStringA(keyA, &clsid);
2025             got = pSHGetObjectCompatFlags(NULL, &clsid);
2026             ok(got == expected, "got 0x%08x, expected 0x%08x. Key %s\n", got, expected, keyA);
2027
2028             RegCloseKey(clsid_key);
2029         }
2030     }
2031
2032     RegCloseKey(root);
2033 }
2034
2035 typedef struct {
2036     const IOleCommandTargetVtbl *lpVtbl;
2037     LONG ref;
2038 } IOleCommandTargetImpl;
2039
2040 static const IOleCommandTargetVtbl IOleCommandTargetImpl_Vtbl;
2041
2042 static IOleCommandTarget* IOleCommandTargetImpl_Construct(void)
2043 {
2044     IOleCommandTargetImpl *obj;
2045
2046     obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj));
2047     obj->lpVtbl = &IOleCommandTargetImpl_Vtbl;
2048     obj->ref = 1;
2049
2050     return (IOleCommandTarget*)obj;
2051 }
2052
2053 static HRESULT WINAPI IOleCommandTargetImpl_QueryInterface(IOleCommandTarget *iface, REFIID riid, void **ppvObj)
2054 {
2055     IOleCommandTargetImpl *This = (IOleCommandTargetImpl *)iface;
2056
2057     if (IsEqualIID(riid, &IID_IUnknown) ||
2058         IsEqualIID(riid, &IID_IOleCommandTarget))
2059     {
2060         *ppvObj = This;
2061     }
2062
2063     if(*ppvObj)
2064     {
2065         IUnknown_AddRef(iface);
2066         return S_OK;
2067     }
2068
2069     return E_NOINTERFACE;
2070 }
2071
2072 static ULONG WINAPI IOleCommandTargetImpl_AddRef(IOleCommandTarget *iface)
2073 {
2074     IOleCommandTargetImpl *This = (IOleCommandTargetImpl *)iface;
2075     return InterlockedIncrement(&This->ref);
2076 }
2077
2078 static ULONG WINAPI IOleCommandTargetImpl_Release(IOleCommandTarget *iface)
2079 {
2080     IOleCommandTargetImpl *This = (IOleCommandTargetImpl *)iface;
2081     ULONG ref = InterlockedDecrement(&This->ref);
2082
2083     if (!ref)
2084     {
2085         HeapFree(GetProcessHeap(), 0, This);
2086         return 0;
2087     }
2088     return ref;
2089 }
2090
2091 static HRESULT WINAPI IOleCommandTargetImpl_QueryStatus(
2092     IOleCommandTarget *iface, const GUID *group, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText)
2093 {
2094     return E_NOTIMPL;
2095 }
2096
2097 static HRESULT WINAPI IOleCommandTargetImpl_Exec(
2098     IOleCommandTarget *iface,
2099     const GUID *CmdGroup,
2100     DWORD nCmdID,
2101     DWORD nCmdexecopt,
2102     VARIANT *pvaIn,
2103     VARIANT *pvaOut)
2104 {
2105     add_call(&trace_got, 3, CmdGroup, (void*)(DWORD_PTR)nCmdID, (void*)(DWORD_PTR)nCmdexecopt, pvaIn, pvaOut);
2106     return S_OK;
2107 }
2108
2109 static const IOleCommandTargetVtbl IOleCommandTargetImpl_Vtbl =
2110 {
2111     IOleCommandTargetImpl_QueryInterface,
2112     IOleCommandTargetImpl_AddRef,
2113     IOleCommandTargetImpl_Release,
2114     IOleCommandTargetImpl_QueryStatus,
2115     IOleCommandTargetImpl_Exec
2116 };
2117
2118 typedef struct {
2119     const IServiceProviderVtbl *lpVtbl;
2120     LONG ref;
2121 } IServiceProviderImpl;
2122
2123 typedef struct {
2124     const IProfferServiceVtbl *lpVtbl;
2125     LONG ref;
2126 } IProfferServiceImpl;
2127
2128
2129 static const IServiceProviderVtbl IServiceProviderImpl_Vtbl;
2130 static const IProfferServiceVtbl IProfferServiceImpl_Vtbl;
2131
2132 static IServiceProvider* IServiceProviderImpl_Construct(void)
2133 {
2134     IServiceProviderImpl *obj;
2135
2136     obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj));
2137     obj->lpVtbl = &IServiceProviderImpl_Vtbl;
2138     obj->ref = 1;
2139
2140     return (IServiceProvider*)obj;
2141 }
2142
2143 static IProfferService* IProfferServiceImpl_Construct(void)
2144 {
2145     IProfferServiceImpl *obj;
2146
2147     obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj));
2148     obj->lpVtbl = &IProfferServiceImpl_Vtbl;
2149     obj->ref = 1;
2150
2151     return (IProfferService*)obj;
2152 }
2153
2154 static HRESULT WINAPI IServiceProviderImpl_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppvObj)
2155 {
2156     IServiceProviderImpl *This = (IServiceProviderImpl *)iface;
2157
2158     if (IsEqualIID(riid, &IID_IUnknown) ||
2159         IsEqualIID(riid, &IID_IServiceProvider))
2160     {
2161         *ppvObj = This;
2162     }
2163
2164     if(*ppvObj)
2165     {
2166         IUnknown_AddRef(iface);
2167         /* native uses redefined IID_IServiceProvider symbol, so we can't compare pointers */
2168         if (IsEqualIID(riid, &IID_IServiceProvider))
2169             add_call(&trace_got, 1, iface, &IID_IServiceProvider, 0, 0, 0);
2170         return S_OK;
2171     }
2172
2173     return E_NOINTERFACE;
2174 }
2175
2176 static ULONG WINAPI IServiceProviderImpl_AddRef(IServiceProvider *iface)
2177 {
2178     IServiceProviderImpl *This = (IServiceProviderImpl *)iface;
2179     return InterlockedIncrement(&This->ref);
2180 }
2181
2182 static ULONG WINAPI IServiceProviderImpl_Release(IServiceProvider *iface)
2183 {
2184     IServiceProviderImpl *This = (IServiceProviderImpl *)iface;
2185     ULONG ref = InterlockedDecrement(&This->ref);
2186
2187     if (!ref)
2188     {
2189         HeapFree(GetProcessHeap(), 0, This);
2190         return 0;
2191     }
2192     return ref;
2193 }
2194
2195 static HRESULT WINAPI IServiceProviderImpl_QueryService(
2196     IServiceProvider *iface, REFGUID service, REFIID riid, void **ppv)
2197 {
2198     /* native uses redefined pointer for IID_IOleCommandTarget, not one from uuid.lib */
2199     if (IsEqualIID(riid, &IID_IOleCommandTarget))
2200     {
2201         add_call(&trace_got, 2, iface, service, &IID_IOleCommandTarget, 0, 0);
2202         *ppv = IOleCommandTargetImpl_Construct();
2203     }
2204     if (IsEqualIID(riid, &IID_IProfferService))
2205     {
2206         if (IsEqualIID(service, &IID_IProfferService))
2207             add_call(&trace_got, 2, &IID_IProfferService, &IID_IProfferService, 0, 0, 0);
2208         *ppv = IProfferServiceImpl_Construct();
2209     }
2210     return S_OK;
2211 }
2212
2213 static const IServiceProviderVtbl IServiceProviderImpl_Vtbl =
2214 {
2215     IServiceProviderImpl_QueryInterface,
2216     IServiceProviderImpl_AddRef,
2217     IServiceProviderImpl_Release,
2218     IServiceProviderImpl_QueryService
2219 };
2220
2221 static void test_IUnknown_QueryServiceExec(void)
2222 {
2223     IServiceProvider *provider = IServiceProviderImpl_Construct();
2224     static const GUID dummy_serviceid = { 0xdeadbeef };
2225     static const GUID dummy_groupid = { 0xbeefbeef };
2226     call_trace_t trace_expected;
2227     HRESULT hr;
2228
2229     /* on <=W2K platforms same ordinal used for another export with different
2230        prototype, so skipping using this indirect condition */
2231     if (is_win2k_and_lower)
2232     {
2233         win_skip("IUnknown_QueryServiceExec is not available\n");
2234         return;
2235     }
2236
2237     /* null source pointer */
2238     hr = pIUnknown_QueryServiceExec(NULL, &dummy_serviceid, &dummy_groupid, 0, 0, 0, 0);
2239     ok(hr == E_FAIL, "got 0x%08x\n", hr);
2240
2241     /* expected trace:
2242        IUnknown_QueryServiceExec( ptr1, serviceid, groupid, arg1, arg2, arg3, arg4);
2243          -> IUnknown_QueryInterface( ptr1, &IID_IServiceProvider, &prov );
2244          -> IServiceProvider_QueryService( prov, serviceid, &IID_IOleCommandTarget, &obj );
2245          -> IOleCommandTarget_Exec( obj, groupid, arg1, arg2, arg3, arg4 );
2246     */
2247     init_call_trace(&trace_expected);
2248
2249     add_call(&trace_expected, 1, provider, &IID_IServiceProvider, 0, 0, 0);
2250     add_call(&trace_expected, 2, provider, &dummy_serviceid, &IID_IOleCommandTarget, 0, 0);
2251     add_call(&trace_expected, 3, &dummy_groupid, (void*)0x1, (void*)0x2, (void*)0x3, (void*)0x4);
2252
2253     init_call_trace(&trace_got);
2254     hr = pIUnknown_QueryServiceExec((IUnknown*)provider, &dummy_serviceid, &dummy_groupid, 0x1, 0x2, (void*)0x3, (void*)0x4);
2255     ok(hr == S_OK, "got 0x%08x\n", hr);
2256
2257     ok_trace(&trace_expected, &trace_got);
2258
2259     free_call_trace(&trace_expected);
2260     free_call_trace(&trace_got);
2261
2262     IServiceProvider_Release(provider);
2263 }
2264
2265
2266 static HRESULT WINAPI IProfferServiceImpl_QueryInterface(IProfferService *iface, REFIID riid, void **ppvObj)
2267 {
2268     IProfferServiceImpl *This = (IProfferServiceImpl *)iface;
2269
2270     if (IsEqualIID(riid, &IID_IUnknown) ||
2271         IsEqualIID(riid, &IID_IProfferService))
2272     {
2273         *ppvObj = This;
2274     }
2275     else if (IsEqualIID(riid, &IID_IServiceProvider))
2276     {
2277         *ppvObj = IServiceProviderImpl_Construct();
2278         add_call(&trace_got, 1, iface, &IID_IServiceProvider, 0, 0, 0);
2279         return S_OK;
2280     }
2281
2282     if(*ppvObj)
2283     {
2284         IUnknown_AddRef(iface);
2285         return S_OK;
2286     }
2287
2288     return E_NOINTERFACE;
2289 }
2290
2291 static ULONG WINAPI IProfferServiceImpl_AddRef(IProfferService *iface)
2292 {
2293     IProfferServiceImpl *This = (IProfferServiceImpl *)iface;
2294     return InterlockedIncrement(&This->ref);
2295 }
2296
2297 static ULONG WINAPI IProfferServiceImpl_Release(IProfferService *iface)
2298 {
2299     IProfferServiceImpl *This = (IProfferServiceImpl *)iface;
2300     ULONG ref = InterlockedDecrement(&This->ref);
2301
2302     if (!ref)
2303     {
2304         HeapFree(GetProcessHeap(), 0, This);
2305         return 0;
2306     }
2307     return ref;
2308 }
2309
2310 static HRESULT WINAPI IProfferServiceImpl_ProfferService(IProfferService *iface,
2311     REFGUID service, IServiceProvider *pService, DWORD *pCookie)
2312 {
2313     add_call(&trace_got, 3, service, pService, pCookie, 0, 0);
2314     return S_OK;
2315 }
2316
2317 static HRESULT WINAPI IProfferServiceImpl_RevokeService(IProfferService *iface, DWORD cookie)
2318 {
2319     add_call(&trace_got, 4, (void*)(DWORD_PTR)cookie, 0, 0, 0, 0);
2320     return S_OK;
2321 }
2322
2323 static const IProfferServiceVtbl IProfferServiceImpl_Vtbl =
2324 {
2325     IProfferServiceImpl_QueryInterface,
2326     IProfferServiceImpl_AddRef,
2327     IProfferServiceImpl_Release,
2328     IProfferServiceImpl_ProfferService,
2329     IProfferServiceImpl_RevokeService
2330 };
2331
2332 static void test_IUnknown_ProfferService(void)
2333 {
2334     IServiceProvider *provider = IServiceProviderImpl_Construct();
2335     IProfferService *proff = IProfferServiceImpl_Construct();
2336     static const GUID dummy_serviceid = { 0xdeadbeef };
2337     call_trace_t trace_expected;
2338     HRESULT hr;
2339     DWORD cookie;
2340
2341     /* on <=W2K platforms same ordinal used for another export with different
2342        prototype, so skipping using this indirect condition */
2343     if (is_win2k_and_lower)
2344     {
2345         win_skip("IUnknown_ProfferService is not available\n");
2346         return;
2347     }
2348
2349     /* null source pointer */
2350     hr = pIUnknown_ProfferService(NULL, &dummy_serviceid, 0, 0);
2351     ok(hr == E_FAIL, "got 0x%08x\n", hr);
2352
2353     /* expected trace:
2354        IUnknown_ProfferService( ptr1, serviceid, arg1, arg2);
2355          -> IUnknown_QueryInterface( ptr1, &IID_IServiceProvider, &provider );
2356          -> IServiceProvider_QueryService( provider, &IID_IProfferService, &IID_IProfferService, &proffer );
2357
2358          if (service pointer not null):
2359              -> IProfferService_ProfferService( proffer, serviceid, arg1, arg2 );
2360          else
2361              -> IProfferService_RevokeService( proffer, *arg2 );
2362     */
2363     init_call_trace(&trace_expected);
2364
2365     add_call(&trace_expected, 1, proff, &IID_IServiceProvider, 0, 0, 0);
2366     add_call(&trace_expected, 2, &IID_IProfferService, &IID_IProfferService, 0, 0, 0);
2367     add_call(&trace_expected, 3, &dummy_serviceid, provider, &cookie, 0, 0);
2368
2369     init_call_trace(&trace_got);
2370     hr = pIUnknown_ProfferService((IUnknown*)proff, &dummy_serviceid, provider, &cookie);
2371     ok(hr == S_OK, "got 0x%08x\n", hr);
2372
2373     ok_trace(&trace_expected, &trace_got);
2374     free_call_trace(&trace_got);
2375     free_call_trace(&trace_expected);
2376
2377     /* same with ::Revoke path */
2378     init_call_trace(&trace_expected);
2379
2380     add_call(&trace_expected, 1, proff, &IID_IServiceProvider, 0, 0, 0);
2381     add_call(&trace_expected, 2, &IID_IProfferService, &IID_IProfferService, 0, 0, 0);
2382     add_call(&trace_expected, 4, (void*)(DWORD_PTR)cookie, 0, 0, 0, 0);
2383
2384     init_call_trace(&trace_got);
2385     hr = pIUnknown_ProfferService((IUnknown*)proff, &dummy_serviceid, 0, &cookie);
2386     ok(hr == S_OK, "got 0x%08x\n", hr);
2387     ok_trace(&trace_expected, &trace_got);
2388     free_call_trace(&trace_got);
2389     free_call_trace(&trace_expected);
2390
2391     IServiceProvider_Release(provider);
2392     IProfferService_Release(proff);
2393 }
2394
2395 static void test_SHCreateWorkerWindowA(void)
2396 {
2397     WNDCLASSA cliA;
2398     char classA[20];
2399     HWND hwnd;
2400     LONG_PTR ret;
2401     BOOL res;
2402
2403     if (is_win2k_and_lower)
2404     {
2405         win_skip("SHCreateWorkerWindowA not available\n");
2406         return;
2407     }
2408
2409     hwnd = pSHCreateWorkerWindowA(0, NULL, 0, 0, 0, 0);
2410     ok(hwnd != 0, "expected window\n");
2411
2412     GetClassName(hwnd, classA, 20);
2413     ok(lstrcmpA(classA, "WorkerA") == 0, "expected WorkerA class, got %s\n", classA);
2414
2415     ret = GetWindowLongPtrA(hwnd, 0);
2416     ok(ret == 0, "got %ld\n", ret);
2417
2418     /* class info */
2419     memset(&cliA, 0, sizeof(cliA));
2420     res = GetClassInfoA(GetModuleHandle("shlwapi.dll"), "WorkerA", &cliA);
2421     ok(res, "failed to get class info\n");
2422     ok(cliA.style == 0, "got 0x%08x\n", cliA.style);
2423     ok(cliA.cbClsExtra == 0, "got %d\n", cliA.cbClsExtra);
2424     ok(cliA.cbWndExtra == sizeof(LONG_PTR), "got %d\n", cliA.cbWndExtra);
2425     ok(cliA.lpszMenuName == 0, "got %s\n", cliA.lpszMenuName);
2426
2427     DestroyWindow(hwnd);
2428
2429     /* set extra bytes */
2430     hwnd = pSHCreateWorkerWindowA(0, NULL, 0, 0, 0, 0xdeadbeef);
2431     ok(hwnd != 0, "expected window\n");
2432
2433     GetClassName(hwnd, classA, 20);
2434     ok(lstrcmpA(classA, "WorkerA") == 0, "expected WorkerA class, got %s\n", classA);
2435
2436     ret = GetWindowLongPtrA(hwnd, 0);
2437     ok(ret == 0xdeadbeef, "got %ld\n", ret);
2438
2439     /* test exstyle */
2440     ret = GetWindowLongA(hwnd, GWL_EXSTYLE);
2441     ok(ret == WS_EX_WINDOWEDGE ||
2442        ret == (WS_EX_WINDOWEDGE|WS_EX_LAYOUTRTL) /* systems with RTL locale */, "0x%08lx\n", ret);
2443
2444     DestroyWindow(hwnd);
2445
2446     hwnd = pSHCreateWorkerWindowA(0, NULL, WS_EX_TOOLWINDOW, 0, 0, 0);
2447     ret = GetWindowLongA(hwnd, GWL_EXSTYLE);
2448     ok(ret == (WS_EX_WINDOWEDGE|WS_EX_TOOLWINDOW) ||
2449        ret == (WS_EX_WINDOWEDGE|WS_EX_TOOLWINDOW|WS_EX_LAYOUTRTL) /* systems with RTL locale */, "0x%08lx\n", ret);
2450     DestroyWindow(hwnd);
2451 }
2452
2453 static HRESULT WINAPI SF_QueryInterface(IShellFolder *iface,
2454         REFIID riid, void **ppv)
2455 {
2456     /* SHIShellFolder_EnumObjects doesn't QI the object for IShellFolder */
2457     ok(!IsEqualGUID(&IID_IShellFolder, riid),
2458             "Unexpected QI for IShellFolder\n");
2459     return E_NOINTERFACE;
2460 }
2461
2462 static ULONG WINAPI SF_AddRef(IShellFolder *iface)
2463 {
2464     return 2;
2465 }
2466
2467 static ULONG WINAPI SF_Release(IShellFolder *iface)
2468 {
2469     return 1;
2470 }
2471
2472 static HRESULT WINAPI SF_ParseDisplayName(IShellFolder *iface,
2473         HWND owner, LPBC reserved, LPOLESTR displayName, ULONG *eaten,
2474         LPITEMIDLIST *idl, ULONG *attr)
2475 {
2476     ok(0, "Didn't expect ParseDisplayName\n");
2477     return E_NOTIMPL;
2478 }
2479
2480 static HRESULT WINAPI SF_EnumObjects(IShellFolder *iface,
2481         HWND owner, SHCONTF flags, IEnumIDList **enm)
2482 {
2483     *enm = (IEnumIDList*)0xcafebabe;
2484     return S_OK;
2485 }
2486
2487 static HRESULT WINAPI SF_BindToObject(IShellFolder *iface,
2488         LPCITEMIDLIST idl, LPBC reserved, REFIID riid, void **obj)
2489 {
2490     ok(0, "Didn't expect BindToObject\n");
2491     return E_NOTIMPL;
2492 }
2493
2494 static HRESULT WINAPI SF_BindToStorage(IShellFolder *iface,
2495         LPCITEMIDLIST idl, LPBC reserved, REFIID riid, void **obj)
2496 {
2497     ok(0, "Didn't expect BindToStorage\n");
2498     return E_NOTIMPL;
2499 }
2500
2501 static HRESULT WINAPI SF_CompareIDs(IShellFolder *iface,
2502         LPARAM lparam, LPCITEMIDLIST idl1, LPCITEMIDLIST idl2)
2503 {
2504     ok(0, "Didn't expect CompareIDs\n");
2505     return E_NOTIMPL;
2506 }
2507
2508 static HRESULT WINAPI SF_CreateViewObject(IShellFolder *iface,
2509         HWND owner, REFIID riid, void **out)
2510 {
2511     ok(0, "Didn't expect CreateViewObject\n");
2512     return E_NOTIMPL;
2513 }
2514
2515 static HRESULT WINAPI SF_GetAttributesOf(IShellFolder *iface,
2516         UINT cidl, LPCITEMIDLIST *idl, SFGAOF *inOut)
2517 {
2518     ok(0, "Didn't expect GetAttributesOf\n");
2519     return E_NOTIMPL;
2520 }
2521
2522 static HRESULT WINAPI SF_GetUIObjectOf(IShellFolder *iface,
2523         HWND owner, UINT cidl, LPCITEMIDLIST *idls, REFIID riid, UINT *inOut,
2524         void **out)
2525 {
2526     ok(0, "Didn't expect GetUIObjectOf\n");
2527     return E_NOTIMPL;
2528 }
2529
2530 static HRESULT WINAPI SF_GetDisplayNameOf(IShellFolder *iface,
2531         LPCITEMIDLIST idl, SHGDNF flags, STRRET *name)
2532 {
2533     ok(0, "Didn't expect GetDisplayNameOf\n");
2534     return E_NOTIMPL;
2535 }
2536
2537 static HRESULT WINAPI SF_SetNameOf(IShellFolder *iface,
2538         HWND hwnd, LPCITEMIDLIST idl, LPCOLESTR name, SHGDNF flags,
2539         LPITEMIDLIST *idlOut)
2540 {
2541     ok(0, "Didn't expect SetNameOf\n");
2542     return E_NOTIMPL;
2543 }
2544
2545 static IShellFolderVtbl ShellFolderVtbl = {
2546     SF_QueryInterface,
2547     SF_AddRef,
2548     SF_Release,
2549     SF_ParseDisplayName,
2550     SF_EnumObjects,
2551     SF_BindToObject,
2552     SF_BindToStorage,
2553     SF_CompareIDs,
2554     SF_CreateViewObject,
2555     SF_GetAttributesOf,
2556     SF_GetUIObjectOf,
2557     SF_GetDisplayNameOf,
2558     SF_SetNameOf
2559 };
2560
2561 static IShellFolder ShellFolder = { &ShellFolderVtbl };
2562
2563 static void test_SHIShellFolder_EnumObjects(void)
2564 {
2565     IEnumIDList *enm;
2566     HRESULT hres;
2567     IShellFolder *folder;
2568
2569     if(!pSHIShellFolder_EnumObjects || is_win2k_and_lower){
2570         win_skip("SHIShellFolder_EnumObjects not available\n");
2571         return;
2572     }
2573
2574     if(0){
2575         /* NULL object crashes on Windows */
2576         hres = pSHIShellFolder_EnumObjects(NULL, NULL, 0, NULL);
2577     }
2578
2579     /* SHIShellFolder_EnumObjects doesn't QI the object for IShellFolder */
2580     enm = (IEnumIDList*)0xdeadbeef;
2581     hres = pSHIShellFolder_EnumObjects(&ShellFolder, NULL, 0, &enm);
2582     ok(hres == S_OK, "SHIShellFolder_EnumObjects failed: 0x%08x\n", hres);
2583     ok(enm == (IEnumIDList*)0xcafebabe, "Didn't get expected enumerator location, instead: %p\n", enm);
2584
2585     /* SHIShellFolder_EnumObjects isn't strict about the IShellFolder object */
2586     hres = pSHGetDesktopFolder(&folder);
2587     ok(hres == S_OK, "SHGetDesktopFolder failed: 0x%08x\n", hres);
2588
2589     enm = NULL;
2590     hres = pSHIShellFolder_EnumObjects(folder, NULL, 0, &enm);
2591     ok(hres == S_OK, "SHIShellFolder_EnumObjects failed: 0x%08x\n", hres);
2592     ok(enm != NULL, "Didn't get an enumerator\n");
2593     if(enm)
2594         IEnumIDList_Release(enm);
2595
2596     IShellFolder_Release(folder);
2597 }
2598
2599 static void write_inifile(LPCWSTR filename)
2600 {
2601     DWORD written;
2602     HANDLE file;
2603
2604     static const char data[] =
2605         "[TestApp]\r\n"
2606         "AKey=1\r\n"
2607         "AnotherKey=asdf\r\n";
2608
2609     file = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
2610     if(file == INVALID_HANDLE_VALUE)
2611         return;
2612
2613     WriteFile(file, data, sizeof(data), &written, NULL);
2614
2615     CloseHandle(file);
2616 }
2617
2618 #define verify_inifile(f, e) r_verify_inifile(__LINE__, f, e)
2619 static void r_verify_inifile(unsigned l, LPCWSTR filename, LPCSTR exp)
2620 {
2621     HANDLE file;
2622     CHAR buf[1024];
2623     DWORD read;
2624
2625     file = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
2626     if(file == INVALID_HANDLE_VALUE)
2627         return;
2628
2629     ReadFile(file, buf, sizeof(buf) * sizeof(CHAR), &read, NULL);
2630     buf[read] = '\0';
2631
2632     CloseHandle(file);
2633
2634     ok_(__FILE__,l)(!strcmp(buf, exp), "Expected:\n%s\nGot:\n%s\n", exp,
2635             buf);
2636 }
2637
2638 static void test_SHGetIniString(void)
2639 {
2640     DWORD ret;
2641     WCHAR out[64] = {0};
2642
2643     static const WCHAR TestAppW[] = {'T','e','s','t','A','p','p',0};
2644     static const WCHAR TestIniW[] = {'C',':','\\','t','e','s','t','.','i','n','i',0};
2645     static const WCHAR AKeyW[] = {'A','K','e','y',0};
2646     static const WCHAR AnotherKeyW[] = {'A','n','o','t','h','e','r','K','e','y',0};
2647     static const WCHAR JunkKeyW[] = {'J','u','n','k','K','e','y',0};
2648
2649     if(!pSHGetIniStringW || is_win2k_and_lower){
2650         win_skip("SHGetIniStringW is not available\n");
2651         return;
2652     }
2653
2654     write_inifile(TestIniW);
2655
2656     if(0){
2657         /* these crash on Windows */
2658         ret = pSHGetIniStringW(NULL, NULL, NULL, 0, NULL);
2659         ret = pSHGetIniStringW(NULL, AKeyW, out, sizeof(out), TestIniW);
2660         ret = pSHGetIniStringW(TestAppW, AKeyW, NULL, sizeof(out), TestIniW);
2661     }
2662
2663     ret = pSHGetIniStringW(TestAppW, AKeyW, out, 0, TestIniW);
2664     ok(ret == 0, "SHGetIniStringW should have given 0, instead: %d\n", ret);
2665
2666     /* valid arguments */
2667     ret = pSHGetIniStringW(TestAppW, NULL, out, sizeof(out), TestIniW);
2668     ok(broken(ret == 0) || /* win 98 */
2669             ret == 4, "SHGetIniStringW should have given 4, instead: %d\n", ret);
2670     ok(!lstrcmpW(out, AKeyW), "Expected %s, got: %s\n",
2671                 wine_dbgstr_w(AKeyW), wine_dbgstr_w(out));
2672
2673     ret = pSHGetIniStringW(TestAppW, AKeyW, out, sizeof(out), TestIniW);
2674     ok(broken(ret == 0) || /* win 98 */
2675                 ret == 1, "SHGetIniStringW should have given 1, instead: %d\n", ret);
2676     ok(broken(*out == 0) || /*win 98 */
2677         !strcmp_wa(out, "1"), "Expected L\"1\", got: %s\n", wine_dbgstr_w(out));
2678
2679     ret = pSHGetIniStringW(TestAppW, AnotherKeyW, out, sizeof(out), TestIniW);
2680     ok(broken(ret == 0) || /* win 98 */
2681             ret == 4, "SHGetIniStringW should have given 4, instead: %d\n", ret);
2682     ok(broken(*out == 0) || /* win 98 */
2683             !strcmp_wa(out, "asdf"), "Expected L\"asdf\", got: %s\n", wine_dbgstr_w(out));
2684
2685     ret = pSHGetIniStringW(TestAppW, JunkKeyW, out, sizeof(out), TestIniW);
2686     ok(ret == 0, "SHGetIniStringW should have given 0, instead: %d\n", ret);
2687     ok(*out == 0, "Expected L\"\", got: %s\n", wine_dbgstr_w(out));
2688
2689     DeleteFileW(TestIniW);
2690 }
2691
2692 static void test_SHSetIniString(void)
2693 {
2694     BOOL ret;
2695
2696     static const WCHAR TestAppW[] = {'T','e','s','t','A','p','p',0};
2697     static const WCHAR AnotherAppW[] = {'A','n','o','t','h','e','r','A','p','p',0};
2698     static const WCHAR TestIniW[] = {'C',':','\\','t','e','s','t','.','i','n','i',0};
2699     static const WCHAR AKeyW[] = {'A','K','e','y',0};
2700     static const WCHAR NewKeyW[] = {'N','e','w','K','e','y',0};
2701     static const WCHAR AValueW[] = {'A','V','a','l','u','e',0};
2702
2703     if(!pSHSetIniStringW || is_win2k_and_lower){
2704         win_skip("SHSetIniStringW is not available\n");
2705         return;
2706     }
2707
2708     write_inifile(TestIniW);
2709
2710     ret = pSHSetIniStringW(TestAppW, AKeyW, AValueW, TestIniW);
2711     ok(ret == TRUE, "SHSetIniStringW should not have failed\n");
2712     todo_wine /* wine sticks an extra \r\n at the end of the file */
2713         verify_inifile(TestIniW, "[TestApp]\r\nAKey=AValue\r\nAnotherKey=asdf\r\n");
2714
2715     ret = pSHSetIniStringW(TestAppW, AKeyW, NULL, TestIniW);
2716     ok(ret == TRUE, "SHSetIniStringW should not have failed\n");
2717     verify_inifile(TestIniW, "[TestApp]\r\nAnotherKey=asdf\r\n");
2718
2719     ret = pSHSetIniStringW(AnotherAppW, NewKeyW, AValueW, TestIniW);
2720     ok(ret == TRUE, "SHSetIniStringW should not have failed\n");
2721     verify_inifile(TestIniW, "[TestApp]\r\nAnotherKey=asdf\r\n[AnotherApp]\r\nNewKey=AValue\r\n");
2722
2723     ret = pSHSetIniStringW(TestAppW, NULL, AValueW, TestIniW);
2724     ok(ret == TRUE, "SHSetIniStringW should not have failed\n");
2725     verify_inifile(TestIniW, "[AnotherApp]\r\nNewKey=AValue\r\n");
2726
2727     DeleteFileW(TestIniW);
2728 }
2729
2730 enum _shellkey_flags {
2731     SHKEY_Root_HKCU = 0x1,
2732     SHKEY_Root_HKLM = 0x2,
2733     SHKEY_Key_Explorer  = 0x00,
2734     SHKEY_Key_Shell = 0x10,
2735     SHKEY_Key_ShellNoRoam = 0x20,
2736     SHKEY_Key_Classes = 0x30,
2737     SHKEY_Subkey_Default = 0x0000,
2738     SHKEY_Subkey_ResourceName = 0x1000,
2739     SHKEY_Subkey_Handlers = 0x2000,
2740     SHKEY_Subkey_Associations = 0x3000,
2741     SHKEY_Subkey_Volatile = 0x4000,
2742     SHKEY_Subkey_MUICache = 0x5000,
2743     SHKEY_Subkey_FileExts = 0x6000
2744 };
2745
2746 static void test_SHGetShellKey(void)
2747 {
2748     static const WCHAR ShellFoldersW[] = { 'S','h','e','l','l',' ','F','o','l','d','e','r','s',0 };
2749     static const WCHAR WineTestW[] = { 'W','i','n','e','T','e','s','t',0 };
2750
2751     void *pPathBuildRootW = GetProcAddress(hShlwapi, "PathBuildRootW");
2752     DWORD *alloc_data, data, size;
2753     HKEY hkey;
2754     HRESULT hres;
2755
2756     if (!pSHGetShellKey)
2757     {
2758         win_skip("SHGetShellKey(ordinal 491) isn't available\n");
2759         return;
2760     }
2761
2762     /* some win2k */
2763     if (pPathBuildRootW && pPathBuildRootW == pSHGetShellKey)
2764     {
2765         win_skip("SHGetShellKey(ordinal 491) used for PathBuildRootW\n");
2766         return;
2767     }
2768
2769     if (is_win9x || is_win2k_and_lower)
2770     {
2771         win_skip("Ordinal 491 used for another call, skipping SHGetShellKey tests\n");
2772         return;
2773     }
2774
2775     /* Vista+ limits SHKEY enumeration values */
2776     SetLastError(0xdeadbeef);
2777     hkey = pSHGetShellKey(SHKEY_Key_Explorer, ShellFoldersW, FALSE);
2778     if (hkey)
2779     {
2780         /* Tests not working on Vista+ */
2781         RegCloseKey(hkey);
2782
2783         hkey = pSHGetShellKey(SHKEY_Root_HKLM|SHKEY_Key_Classes, NULL, FALSE);
2784         ok(hkey != NULL, "hkey = NULL\n");
2785         RegCloseKey(hkey);
2786     }
2787
2788     hkey = pSHGetShellKey(SHKEY_Root_HKCU|SHKEY_Key_Explorer, ShellFoldersW, FALSE);
2789     ok(hkey != NULL, "hkey = NULL\n");
2790     RegCloseKey(hkey);
2791
2792     hkey = pSHGetShellKey(SHKEY_Root_HKLM|SHKEY_Key_Explorer, ShellFoldersW, FALSE);
2793     ok(hkey != NULL, "hkey = NULL\n");
2794     RegCloseKey(hkey);
2795
2796     hkey = pSHGetShellKey(SHKEY_Root_HKLM, WineTestW, FALSE);
2797     ok(hkey == NULL, "hkey != NULL\n");
2798
2799     hkey = pSHGetShellKey(SHKEY_Root_HKLM, WineTestW, TRUE);
2800     ok(hkey != NULL, "Can't create key\n");
2801     RegCloseKey(hkey);
2802
2803     hkey = pSHGetShellKey(SHKEY_Root_HKLM, NULL, FALSE);
2804     ok(hkey != NULL, "Can't create key\n");
2805     ok(SUCCEEDED(RegDeleteKeyW(hkey, WineTestW)), "Can't delte key\n");
2806     RegCloseKey(hkey);
2807
2808     if (!pSKGetValueW || !pSKSetValueW || !pSKDeleteValueW || !pSKAllocValueW)
2809     {
2810         win_skip("SKGetValueW, SKSetValueW, SKDeleteValueW or SKAllocValueW not available\n");
2811         return;
2812     }
2813
2814     hres = pSKGetValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, &data, &size);
2815     ok(hres == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hres = %x\n", hres);
2816
2817     data = 1234;
2818     hres = pSKSetValueW(SHKEY_Root_HKLM, WineTestW, NULL, REG_DWORD, &data, sizeof(DWORD));
2819     ok(hres == S_OK, "hres = %x\n", hres);
2820
2821     size = 1;
2822     hres = pSKGetValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, NULL, &size);
2823     ok(hres == S_OK, "hres = %x\n", hres);
2824     ok(size == sizeof(DWORD), "size = %d\n", size);
2825
2826     data = 0xdeadbeef;
2827     hres = pSKGetValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, &data, &size);
2828     ok(hres == S_OK, "hres = %x\n", hres);
2829     ok(size == sizeof(DWORD), "size = %d\n", size);
2830     ok(data == 1234, "data = %d\n", data);
2831
2832     hres = pSKAllocValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, (void**)&alloc_data, &size);
2833     ok(hres == S_OK, "hres= %x\n", hres);
2834     ok(size == sizeof(DWORD), "size = %d\n", size);
2835     ok(*alloc_data == 1234, "*alloc_data = %d\n", *alloc_data);
2836     LocalFree(alloc_data);
2837
2838     hres = pSKDeleteValueW(SHKEY_Root_HKLM, WineTestW, NULL);
2839     ok(hres == S_OK, "hres = %x\n", hres);
2840
2841     hres = pSKDeleteValueW(SHKEY_Root_HKLM, WineTestW, NULL);
2842     ok(hres == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hres = %x\n", hres);
2843
2844     hres = pSKGetValueW(SHKEY_Root_HKLM, WineTestW, NULL, NULL, &data, &size);
2845     ok(hres == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hres = %x\n", hres);
2846
2847     hkey = pSHGetShellKey(SHKEY_Root_HKLM, NULL, FALSE);
2848     ok(hkey != NULL, "Can't create key\n");
2849     ok(SUCCEEDED(RegDeleteKeyW(hkey, WineTestW)), "Can't delte key\n");
2850     RegCloseKey(hkey);
2851 }
2852
2853 static void init_pointers(void)
2854 {
2855 #define MAKEFUNC(f, ord) (p##f = (void*)GetProcAddress(hShlwapi, (LPSTR)(ord)))
2856     MAKEFUNC(SHAllocShared, 7);
2857     MAKEFUNC(SHLockShared, 8);
2858     MAKEFUNC(SHUnlockShared, 9);
2859     MAKEFUNC(SHFreeShared, 10);
2860     MAKEFUNC(GetAcceptLanguagesA, 14);
2861     MAKEFUNC(SHSetWindowBits, 165);
2862     MAKEFUNC(ConnectToConnectionPoint, 168);
2863     MAKEFUNC(SHSearchMapInt, 198);
2864     MAKEFUNC(SHCreateWorkerWindowA, 257);
2865     MAKEFUNC(GUIDFromStringA, 269);
2866     MAKEFUNC(SHPackDispParams, 282);
2867     MAKEFUNC(IConnectionPoint_InvokeWithCancel, 283);
2868     MAKEFUNC(IConnectionPoint_SimpleInvoke, 284);
2869     MAKEFUNC(SHGetIniStringW, 294);
2870     MAKEFUNC(SHSetIniStringW, 295);
2871     MAKEFUNC(SHFormatDateTimeA, 353);
2872     MAKEFUNC(SHFormatDateTimeW, 354);
2873     MAKEFUNC(SHIShellFolder_EnumObjects, 404);
2874     MAKEFUNC(SHGetObjectCompatFlags, 476);
2875     MAKEFUNC(IUnknown_QueryServiceExec, 484);
2876     MAKEFUNC(SHGetShellKey, 491);
2877     MAKEFUNC(SHPropertyBag_ReadLONG, 496);
2878     MAKEFUNC(IUnknown_ProfferService, 514);
2879     MAKEFUNC(SKGetValueW, 516);
2880     MAKEFUNC(SKSetValueW, 517);
2881     MAKEFUNC(SKDeleteValueW, 518);
2882     MAKEFUNC(SKAllocValueW, 519);
2883 #undef MAKEFUNC
2884 }
2885
2886 START_TEST(ordinal)
2887 {
2888     hShlwapi = GetModuleHandleA("shlwapi.dll");
2889     is_win2k_and_lower = GetProcAddress(hShlwapi, "StrChrNW") == 0;
2890     is_win9x = GetProcAddress(hShlwapi, (LPSTR)99) == 0; /* StrCpyNXA */
2891
2892     /* SHCreateStreamOnFileEx was introduced in shlwapi v6.0 */
2893     if(!GetProcAddress(hShlwapi, "SHCreateStreamOnFileEx")){
2894         win_skip("Too old shlwapi version\n");
2895         return;
2896     }
2897
2898     init_pointers();
2899
2900     hmlang = LoadLibraryA("mlang.dll");
2901     pLcidToRfc1766A = (void *)GetProcAddress(hmlang, "LcidToRfc1766A");
2902
2903     hshell32 = LoadLibraryA("shell32.dll");
2904     pSHGetDesktopFolder = (void *)GetProcAddress(hshell32, "SHGetDesktopFolder");
2905
2906     test_GetAcceptLanguagesA();
2907     test_SHSearchMapInt();
2908     test_alloc_shared();
2909     test_fdsa();
2910     test_GetShellSecurityDescriptor();
2911     test_SHPackDispParams();
2912     test_IConnectionPoint();
2913     test_SHPropertyBag_ReadLONG();
2914     test_SHSetWindowBits();
2915     test_SHFormatDateTimeA();
2916     test_SHFormatDateTimeW();
2917     test_SHGetObjectCompatFlags();
2918     test_IUnknown_QueryServiceExec();
2919     test_IUnknown_ProfferService();
2920     test_SHCreateWorkerWindowA();
2921     test_SHIShellFolder_EnumObjects();
2922     test_SHGetIniString();
2923     test_SHSetIniString();
2924     test_SHGetShellKey();
2925
2926     FreeLibrary(hshell32);
2927     FreeLibrary(hmlang);
2928 }