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