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