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