[SHELL32_WINETEST] Sync with Wine Staging 2.9. CORE-13362
[reactos.git] / rostests / winetests / shell32 / shelllink.c
1 /*
2 * Unit tests for shelllinks
3 *
4 * Copyright 2004 Mike McCormack
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 *
20 */
21
22 #define COBJMACROS
23
24 #include "initguid.h"
25 #include "windows.h"
26 #include "shlguid.h"
27 #include "shobjidl.h"
28 #include "shlobj.h"
29 #include "shellapi.h"
30 #include "commoncontrols.h"
31 #include "wine/test.h"
32
33 #include "shell32_test.h"
34
35 #include <reactos/undocshell.h>
36
37 #ifndef SLDF_HAS_LOGO3ID
38 # define SLDF_HAS_LOGO3ID 0x00000800 /* not available in the Vista SDK */
39 #endif
40
41 static void (WINAPI *pILFree)(LPITEMIDLIST);
42 static BOOL (WINAPI *pILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST);
43 static HRESULT (WINAPI *pSHILCreateFromPath)(LPCWSTR, LPITEMIDLIST *,DWORD*);
44 static HRESULT (WINAPI *pSHDefExtractIconA)(LPCSTR, int, UINT, HICON*, HICON*, UINT);
45 static HRESULT (WINAPI *pSHGetStockIconInfo)(SHSTOCKICONID, UINT, SHSTOCKICONINFO *);
46 static DWORD (WINAPI *pGetLongPathNameA)(LPCSTR, LPSTR, DWORD);
47 static DWORD (WINAPI *pGetShortPathNameA)(LPCSTR, LPSTR, DWORD);
48 static UINT (WINAPI *pSHExtractIconsW)(LPCWSTR, int, int, int, HICON *, UINT *, UINT, UINT);
49 static BOOL (WINAPI *pIsProcessDPIAware)(void);
50
51 static const GUID _IID_IShellLinkDataList = {
52 0x45e2b4ae, 0xb1c3, 0x11d0,
53 { 0xb9, 0x2f, 0x00, 0xa0, 0xc9, 0x03, 0x12, 0xe1 }
54 };
55
56
57 /* For some reason SHILCreateFromPath does not work on Win98 and
58 * SHSimpleIDListFromPathA does not work on NT4. But if we call both we
59 * get what we want on all platforms.
60 */
61 static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathAW)(LPCVOID);
62
63 static LPITEMIDLIST path_to_pidl(const char* path)
64 {
65 LPITEMIDLIST pidl;
66
67 if (!pSHSimpleIDListFromPathAW)
68 {
69 HMODULE hdll=GetModuleHandleA("shell32.dll");
70 pSHSimpleIDListFromPathAW=(void*)GetProcAddress(hdll, (char*)162);
71 if (!pSHSimpleIDListFromPathAW)
72 win_skip("SHSimpleIDListFromPathAW not found in shell32.dll\n");
73 }
74
75 pidl=NULL;
76 /* pSHSimpleIDListFromPathAW maps to A on non NT platforms */
77 if (pSHSimpleIDListFromPathAW && (GetVersion() & 0x80000000))
78 pidl=pSHSimpleIDListFromPathAW(path);
79
80 if (!pidl)
81 {
82 WCHAR* pathW;
83 HRESULT r;
84 int len;
85
86 len=MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
87 pathW=HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
88 MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, len);
89
90 r=pSHILCreateFromPath(pathW, &pidl, NULL);
91 ok(r == S_OK, "SHILCreateFromPath failed (0x%08x)\n", r);
92 HeapFree(GetProcessHeap(), 0, pathW);
93 }
94 return pidl;
95 }
96
97
98 /*
99 * Test manipulation of an IShellLink's properties.
100 */
101
102 static void test_get_set(void)
103 {
104 HRESULT r;
105 IShellLinkA *sl;
106 IShellLinkW *slW = NULL;
107 char mypath[MAX_PATH];
108 char buffer[INFOTIPSIZE];
109 LPITEMIDLIST pidl, tmp_pidl;
110 const char * str;
111 int i;
112 WORD w;
113
114 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
115 &IID_IShellLinkA, (LPVOID*)&sl);
116 ok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
117 if (r != S_OK)
118 return;
119
120 /* Test Getting / Setting the description */
121 strcpy(buffer,"garbage");
122 r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
123 ok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
124 ok(*buffer=='\0', "GetDescription returned '%s'\n", buffer);
125
126 str="Some description";
127 r = IShellLinkA_SetDescription(sl, str);
128 ok(r == S_OK, "SetDescription failed (0x%08x)\n", r);
129
130 strcpy(buffer,"garbage");
131 r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
132 ok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
133 ok(strcmp(buffer,str)==0, "GetDescription returned '%s'\n", buffer);
134
135 r = IShellLinkA_SetDescription(sl, NULL);
136 ok(r == S_OK, "SetDescription failed (0x%08x)\n", r);
137
138 strcpy(buffer,"garbage");
139 r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
140 ok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
141 ok(*buffer=='\0' || broken(strcmp(buffer,str)==0), "GetDescription returned '%s'\n", buffer); /* NT4 */
142
143 /* Test Getting / Setting the work directory */
144 strcpy(buffer,"garbage");
145 r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
146 ok(r == S_OK, "GetWorkingDirectory failed (0x%08x)\n", r);
147 ok(*buffer=='\0', "GetWorkingDirectory returned '%s'\n", buffer);
148
149 str="c:\\nonexistent\\directory";
150 r = IShellLinkA_SetWorkingDirectory(sl, str);
151 ok(r == S_OK, "SetWorkingDirectory failed (0x%08x)\n", r);
152
153 strcpy(buffer,"garbage");
154 r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
155 ok(r == S_OK, "GetWorkingDirectory failed (0x%08x)\n", r);
156 ok(lstrcmpiA(buffer,str)==0, "GetWorkingDirectory returned '%s'\n", buffer);
157
158 /* Test Getting / Setting the path */
159 strcpy(buffer,"garbage");
160 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
161 todo_wine ok(r == S_FALSE || broken(r == S_OK) /* NT4/W2K */, "GetPath failed (0x%08x)\n", r);
162 ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
163
164 CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
165 &IID_IShellLinkW, (LPVOID*)&slW);
166 if (!slW /* Win9x */ || !pGetLongPathNameA /* NT4 */)
167 skip("SetPath with NULL parameter crashes on Win9x and some NT4\n");
168 else
169 {
170 IShellLinkW_Release(slW);
171 r = IShellLinkA_SetPath(sl, NULL);
172 ok(r==E_INVALIDARG ||
173 broken(r==S_OK), /* Some Win95 and NT4 */
174 "SetPath returned wrong error (0x%08x)\n", r);
175 }
176
177 r = IShellLinkA_SetPath(sl, "");
178 ok(r==S_OK, "SetPath failed (0x%08x)\n", r);
179
180 strcpy(buffer,"garbage");
181 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
182 todo_wine ok(r == S_FALSE, "GetPath failed (0x%08x)\n", r);
183 ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
184
185 /* Win98 returns S_FALSE, but WinXP returns S_OK */
186 str="c:\\nonexistent\\file";
187 r = IShellLinkA_SetPath(sl, str);
188 ok(r==S_FALSE || r==S_OK, "SetPath failed (0x%08x)\n", r);
189
190 strcpy(buffer,"garbage");
191 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
192 ok(r == S_OK, "GetPath failed (0x%08x)\n", r);
193 ok(lstrcmpiA(buffer,str)==0, "GetPath returned '%s'\n", buffer);
194
195 /* Get some real path to play with */
196 GetWindowsDirectoryA( mypath, sizeof(mypath)-12 );
197 strcat(mypath, "\\regedit.exe");
198
199 /* Test the interaction of SetPath and SetIDList */
200 tmp_pidl=NULL;
201 r = IShellLinkA_GetIDList(sl, &tmp_pidl);
202 ok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
203 if (r == S_OK)
204 {
205 BOOL ret;
206
207 strcpy(buffer,"garbage");
208 ret = SHGetPathFromIDListA(tmp_pidl, buffer);
209 ok(ret, "SHGetPathFromIDListA failed\n");
210 if (ret)
211 ok(lstrcmpiA(buffer,str)==0, "GetIDList returned '%s'\n", buffer);
212 pILFree(tmp_pidl);
213 }
214
215 pidl=path_to_pidl(mypath);
216 ok(pidl!=NULL, "path_to_pidl returned a NULL pidl\n");
217
218 if (pidl)
219 {
220 LPITEMIDLIST second_pidl;
221
222 r = IShellLinkA_SetIDList(sl, pidl);
223 ok(r == S_OK, "SetIDList failed (0x%08x)\n", r);
224
225 tmp_pidl=NULL;
226 r = IShellLinkA_GetIDList(sl, &tmp_pidl);
227 ok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
228 ok(tmp_pidl && pILIsEqual(pidl, tmp_pidl),
229 "GetIDList returned an incorrect pidl\n");
230
231 r = IShellLinkA_GetIDList(sl, &second_pidl);
232 ok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
233 ok(second_pidl && pILIsEqual(pidl, second_pidl),
234 "GetIDList returned an incorrect pidl\n");
235 ok(second_pidl != tmp_pidl, "pidls are the same\n");
236
237 pILFree(second_pidl);
238 pILFree(tmp_pidl);
239 pILFree(pidl);
240
241 strcpy(buffer,"garbage");
242 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
243 ok(r == S_OK, "GetPath failed (0x%08x)\n", r);
244 todo_wine
245 ok(lstrcmpiA(buffer, mypath)==0, "GetPath returned '%s'\n", buffer);
246 }
247
248 /* test path with quotes (IShellLinkA_SetPath returns S_FALSE on W2K and below and S_OK on XP and above */
249 r = IShellLinkA_SetPath(sl, "\"c:\\nonexistent\\file\"");
250 ok(r==S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
251
252 strcpy(buffer,"garbage");
253 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
254 ok(r==S_OK, "GetPath failed (0x%08x)\n", r);
255 todo_wine ok(!strcmp(buffer, "C:\\nonexistent\\file") ||
256 broken(!strcmp(buffer, "C:\\\"c:\\nonexistent\\file\"")), /* NT4 */
257 "case doesn't match\n");
258
259 r = IShellLinkA_SetPath(sl, "\"c:\\foo");
260 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
261
262 r = IShellLinkA_SetPath(sl, "\"\"c:\\foo");
263 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
264
265 r = IShellLinkA_SetPath(sl, "c:\\foo\"");
266 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
267
268 r = IShellLinkA_SetPath(sl, "\"\"c:\\foo\"");
269 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
270
271 r = IShellLinkA_SetPath(sl, "\"\"c:\\foo\"\"");
272 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
273
274 /* Test Getting / Setting the arguments */
275 strcpy(buffer,"garbage");
276 r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
277 ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
278 ok(*buffer=='\0', "GetArguments returned '%s'\n", buffer);
279
280 str="param1 \"spaced param2\"";
281 r = IShellLinkA_SetArguments(sl, str);
282 ok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
283
284 strcpy(buffer,"garbage");
285 r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
286 ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
287 ok(strcmp(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
288
289 strcpy(buffer,"garbage");
290 r = IShellLinkA_SetArguments(sl, NULL);
291 ok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
292 r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
293 ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
294 ok(!buffer[0] || strcmp(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
295
296 strcpy(buffer,"garbage");
297 r = IShellLinkA_SetArguments(sl, "");
298 ok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
299 r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
300 ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
301 ok(!buffer[0], "GetArguments returned '%s'\n", buffer);
302
303 /* Test Getting / Setting showcmd */
304 i=0xdeadbeef;
305 r = IShellLinkA_GetShowCmd(sl, &i);
306 ok(r == S_OK, "GetShowCmd failed (0x%08x)\n", r);
307 ok(i==SW_SHOWNORMAL, "GetShowCmd returned %d\n", i);
308
309 r = IShellLinkA_SetShowCmd(sl, SW_SHOWMAXIMIZED);
310 ok(r == S_OK, "SetShowCmd failed (0x%08x)\n", r);
311
312 i=0xdeadbeef;
313 r = IShellLinkA_GetShowCmd(sl, &i);
314 ok(r == S_OK, "GetShowCmd failed (0x%08x)\n", r);
315 ok(i==SW_SHOWMAXIMIZED, "GetShowCmd returned %d'\n", i);
316
317 /* Test Getting / Setting the icon */
318 i=0xdeadbeef;
319 strcpy(buffer,"garbage");
320 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
321 ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
322 ok(*buffer=='\0', "GetIconLocation returned '%s'\n", buffer);
323 ok(i==0, "GetIconLocation returned %d\n", i);
324
325 str="c:\\nonexistent\\file";
326 r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
327 ok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
328
329 i=0xdeadbeef;
330 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
331 ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
332 ok(lstrcmpiA(buffer,str)==0, "GetIconLocation returned '%s'\n", buffer);
333 ok(i==0xbabecafe, "GetIconLocation returned %d'\n", i);
334
335 /* Test Getting / Setting the hot key */
336 w=0xbeef;
337 r = IShellLinkA_GetHotkey(sl, &w);
338 ok(r == S_OK, "GetHotkey failed (0x%08x)\n", r);
339 ok(w==0, "GetHotkey returned %d\n", w);
340
341 r = IShellLinkA_SetHotkey(sl, 0x5678);
342 ok(r == S_OK, "SetHotkey failed (0x%08x)\n", r);
343
344 w=0xbeef;
345 r = IShellLinkA_GetHotkey(sl, &w);
346 ok(r == S_OK, "GetHotkey failed (0x%08x)\n", r);
347 ok(w==0x5678, "GetHotkey returned %d'\n", w);
348
349 IShellLinkA_Release(sl);
350 }
351
352
353 /*
354 * Test saving and loading .lnk files
355 */
356
357 #define lok ok_(__FILE__, line)
358 #define check_lnk(a,b,c) check_lnk_(__LINE__, (a), (b), (c))
359
360 void create_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int save_fails)
361 {
362 HRESULT r;
363 IShellLinkA *sl;
364 IPersistFile *pf;
365
366 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
367 &IID_IShellLinkA, (LPVOID*)&sl);
368 lok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
369 if (r != S_OK)
370 return;
371
372 if (desc->description)
373 {
374 r = IShellLinkA_SetDescription(sl, desc->description);
375 lok(r == S_OK, "SetDescription failed (0x%08x)\n", r);
376 }
377 if (desc->workdir)
378 {
379 r = IShellLinkA_SetWorkingDirectory(sl, desc->workdir);
380 lok(r == S_OK, "SetWorkingDirectory failed (0x%08x)\n", r);
381 }
382 if (desc->path)
383 {
384 r = IShellLinkA_SetPath(sl, desc->path);
385 lok(SUCCEEDED(r), "SetPath failed (0x%08x)\n", r);
386 }
387 if (desc->pidl)
388 {
389 r = IShellLinkA_SetIDList(sl, desc->pidl);
390 lok(r == S_OK, "SetIDList failed (0x%08x)\n", r);
391 }
392 if (desc->arguments)
393 {
394 r = IShellLinkA_SetArguments(sl, desc->arguments);
395 lok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
396 }
397 if (desc->showcmd)
398 {
399 r = IShellLinkA_SetShowCmd(sl, desc->showcmd);
400 lok(r == S_OK, "SetShowCmd failed (0x%08x)\n", r);
401 }
402 if (desc->icon)
403 {
404 r = IShellLinkA_SetIconLocation(sl, desc->icon, desc->icon_id);
405 lok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
406 }
407 if (desc->hotkey)
408 {
409 r = IShellLinkA_SetHotkey(sl, desc->hotkey);
410 lok(r == S_OK, "SetHotkey failed (0x%08x)\n", r);
411 }
412
413 r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (void**)&pf);
414 lok(r == S_OK, "no IID_IPersistFile (0x%08x)\n", r);
415 if (r == S_OK)
416 {
417 LPOLESTR str;
418
419 if (0)
420 {
421 /* crashes on XP */
422 IPersistFile_GetCurFile(pf, NULL);
423 }
424
425 /* test GetCurFile before ::Save */
426 str = (LPWSTR)0xdeadbeef;
427 r = IPersistFile_GetCurFile(pf, &str);
428 lok(r == S_FALSE ||
429 broken(r == S_OK), /* shell32 < 5.0 */
430 "got 0x%08x\n", r);
431 lok(str == NULL, "got %p\n", str);
432
433 r = IPersistFile_Save(pf, path, TRUE);
434 todo_wine_if (save_fails)
435 lok(r == S_OK, "save failed (0x%08x)\n", r);
436
437 /* test GetCurFile after ::Save */
438 r = IPersistFile_GetCurFile(pf, &str);
439 lok(r == S_OK, "got 0x%08x\n", r);
440 lok(str != NULL ||
441 broken(str == NULL), /* shell32 < 5.0 */
442 "Didn't expect NULL\n");
443 if (str != NULL)
444 {
445 IMalloc *pmalloc;
446
447 lok(!winetest_strcmpW(path, str), "Expected %s, got %s\n",
448 wine_dbgstr_w(path), wine_dbgstr_w(str));
449
450 SHGetMalloc(&pmalloc);
451 IMalloc_Free(pmalloc, str);
452 }
453 else
454 win_skip("GetCurFile fails on shell32 < 5.0\n");
455
456 IPersistFile_Release(pf);
457 }
458
459 IShellLinkA_Release(sl);
460 }
461
462 static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int todo)
463 {
464 HRESULT r;
465 IShellLinkA *sl;
466 IPersistFile *pf;
467 char buffer[INFOTIPSIZE];
468 LPOLESTR str;
469
470 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
471 &IID_IShellLinkA, (LPVOID*)&sl);
472 lok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
473 if (r != S_OK)
474 return;
475
476 r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
477 lok(r == S_OK, "no IID_IPersistFile (0x%08x)\n", r);
478 if (r != S_OK)
479 {
480 IShellLinkA_Release(sl);
481 return;
482 }
483
484 /* test GetCurFile before ::Load */
485 str = (LPWSTR)0xdeadbeef;
486 r = IPersistFile_GetCurFile(pf, &str);
487 lok(r == S_FALSE ||
488 broken(r == S_OK), /* shell32 < 5.0 */
489 "got 0x%08x\n", r);
490 lok(str == NULL, "got %p\n", str);
491
492 r = IPersistFile_Load(pf, path, STGM_READ);
493 lok(r == S_OK, "load failed (0x%08x)\n", r);
494
495 /* test GetCurFile after ::Save */
496 r = IPersistFile_GetCurFile(pf, &str);
497 lok(r == S_OK, "got 0x%08x\n", r);
498 lok(str != NULL ||
499 broken(str == NULL), /* shell32 < 5.0 */
500 "Didn't expect NULL\n");
501 if (str != NULL)
502 {
503 IMalloc *pmalloc;
504
505 lok(!winetest_strcmpW(path, str), "Expected %s, got %s\n",
506 wine_dbgstr_w(path), wine_dbgstr_w(str));
507
508 SHGetMalloc(&pmalloc);
509 IMalloc_Free(pmalloc, str);
510 }
511 else
512 win_skip("GetCurFile fails on shell32 < 5.0\n");
513
514 IPersistFile_Release(pf);
515 if (r != S_OK)
516 {
517 IShellLinkA_Release(sl);
518 return;
519 }
520
521 if (desc->description)
522 {
523 strcpy(buffer,"garbage");
524 r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
525 lok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
526 todo_wine_if ((todo & 0x1) != 0)
527 lok(strcmp(buffer, desc->description)==0, "GetDescription returned '%s' instead of '%s'\n",
528 buffer, desc->description);
529 }
530 if (desc->workdir)
531 {
532 strcpy(buffer,"garbage");
533 r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
534 lok(r == S_OK, "GetWorkingDirectory failed (0x%08x)\n", r);
535 todo_wine_if ((todo & 0x2) != 0)
536 lok(lstrcmpiA(buffer, desc->workdir)==0, "GetWorkingDirectory returned '%s' instead of '%s'\n",
537 buffer, desc->workdir);
538 }
539 if (desc->path)
540 {
541 strcpy(buffer,"garbage");
542 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
543 lok(SUCCEEDED(r), "GetPath failed (0x%08x)\n", r);
544 todo_wine_if ((todo & 0x4) != 0)
545 lok(lstrcmpiA(buffer, desc->path)==0, "GetPath returned '%s' instead of '%s'\n",
546 buffer, desc->path);
547 }
548 if (desc->pidl)
549 {
550 LPITEMIDLIST pidl=NULL;
551 r = IShellLinkA_GetIDList(sl, &pidl);
552 lok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
553 todo_wine_if ((todo & 0x8) != 0)
554 lok(pILIsEqual(pidl, desc->pidl), "GetIDList returned an incorrect pidl\n");
555 }
556 if (desc->showcmd)
557 {
558 int i=0xdeadbeef;
559 r = IShellLinkA_GetShowCmd(sl, &i);
560 lok(r == S_OK, "GetShowCmd failed (0x%08x)\n", r);
561 todo_wine_if ((todo & 0x10) != 0)
562 lok(i==desc->showcmd, "GetShowCmd returned 0x%0x instead of 0x%0x\n",
563 i, desc->showcmd);
564 }
565 if (desc->icon)
566 {
567 int i=0xdeadbeef;
568 strcpy(buffer,"garbage");
569 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
570 lok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
571 todo_wine_if ((todo & 0x20) != 0) {
572 lok(lstrcmpiA(buffer, desc->icon)==0, "GetIconLocation returned '%s' instead of '%s'\n",
573 buffer, desc->icon);
574 lok(i==desc->icon_id, "GetIconLocation returned 0x%0x instead of 0x%0x\n",
575 i, desc->icon_id);
576 }
577 }
578 if (desc->hotkey)
579 {
580 WORD i=0xbeef;
581 r = IShellLinkA_GetHotkey(sl, &i);
582 lok(r == S_OK, "GetHotkey failed (0x%08x)\n", r);
583 todo_wine_if ((todo & 0x40) != 0)
584 lok(i==desc->hotkey, "GetHotkey returned 0x%04x instead of 0x%04x\n",
585 i, desc->hotkey);
586 }
587
588 IShellLinkA_Release(sl);
589 }
590
591 static void test_load_save(void)
592 {
593 WCHAR lnkfile[MAX_PATH];
594 char lnkfileA[MAX_PATH];
595 static const char lnkfileA_name[] = "\\test.lnk";
596
597 lnk_desc_t desc;
598 char mypath[MAX_PATH];
599 char mydir[MAX_PATH];
600 char realpath[MAX_PATH];
601 char* p;
602 HANDLE hf;
603 DWORD r;
604
605 if (!pGetLongPathNameA)
606 {
607 win_skip("GetLongPathNameA is not available\n");
608 return;
609 }
610
611 /* Don't used a fixed path for the test.lnk file */
612 GetTempPathA(MAX_PATH, lnkfileA);
613 lstrcatA(lnkfileA, lnkfileA_name);
614 MultiByteToWideChar(CP_ACP, 0, lnkfileA, -1, lnkfile, MAX_PATH);
615
616 /* Save an empty .lnk file */
617 memset(&desc, 0, sizeof(desc));
618 create_lnk(lnkfile, &desc, 0);
619
620 /* It should come back as a bunch of empty strings */
621 desc.description="";
622 desc.workdir="";
623 desc.path="";
624 desc.arguments="";
625 desc.icon="";
626 check_lnk(lnkfile, &desc, 0x0);
627
628 /* Point a .lnk file to nonexistent files */
629 desc.description="";
630 desc.workdir="c:\\Nonexitent\\work\\directory";
631 desc.path="c:\\nonexistent\\path";
632 desc.pidl=NULL;
633 desc.arguments="";
634 desc.showcmd=0;
635 desc.icon="c:\\nonexistent\\icon\\file";
636 desc.icon_id=1234;
637 desc.hotkey=0;
638 create_lnk(lnkfile, &desc, 0);
639 check_lnk(lnkfile, &desc, 0x0);
640
641 r=GetModuleFileNameA(NULL, mypath, sizeof(mypath));
642 ok(r<sizeof(mypath), "GetModuleFileName failed (%d)\n", r);
643 strcpy(mydir, mypath);
644 p=strrchr(mydir, '\\');
645 if (p)
646 *p='\0';
647
648 /* IShellLink returns path in long form */
649 if (!pGetLongPathNameA(mypath, realpath, MAX_PATH)) strcpy( realpath, mypath );
650
651 /* Overwrite the existing lnk file and point it to existing files */
652 desc.description="test 2";
653 desc.workdir=mydir;
654 desc.path=realpath;
655 desc.pidl=NULL;
656 desc.arguments="/option1 /option2 \"Some string\"";
657 desc.showcmd=SW_SHOWNORMAL;
658 desc.icon=mypath;
659 desc.icon_id=0;
660 desc.hotkey=0x1234;
661 create_lnk(lnkfile, &desc, 0);
662 check_lnk(lnkfile, &desc, 0x0);
663
664 /* Test omitting .exe from an absolute path */
665 p=strrchr(realpath, '.');
666 if (p)
667 *p='\0';
668
669 desc.description="absolute path without .exe";
670 desc.workdir=mydir;
671 desc.path=realpath;
672 desc.pidl=NULL;
673 desc.arguments="/option1 /option2 \"Some string\"";
674 desc.showcmd=SW_SHOWNORMAL;
675 desc.icon=mypath;
676 desc.icon_id=0;
677 desc.hotkey=0x1234;
678 create_lnk(lnkfile, &desc, 0);
679 strcat(realpath, ".exe");
680 check_lnk(lnkfile, &desc, 0x4);
681
682 /* Overwrite the existing lnk file and test link to a command on the path */
683 desc.description="command on path";
684 desc.workdir=mypath;
685 desc.path="rundll32.exe";
686 desc.pidl=NULL;
687 desc.arguments="/option1 /option2 \"Some string\"";
688 desc.showcmd=SW_SHOWNORMAL;
689 desc.icon=mypath;
690 desc.icon_id=0;
691 desc.hotkey=0x1234;
692 create_lnk(lnkfile, &desc, 0);
693 /* Check that link is created to proper location */
694 SearchPathA( NULL, desc.path, NULL, MAX_PATH, realpath, NULL);
695 desc.path=realpath;
696 check_lnk(lnkfile, &desc, 0x0);
697
698 /* Test omitting .exe from a command on the path */
699 desc.description="command on path without .exe";
700 desc.workdir=mypath;
701 desc.path="rundll32";
702 desc.pidl=NULL;
703 desc.arguments="/option1 /option2 \"Some string\"";
704 desc.showcmd=SW_SHOWNORMAL;
705 desc.icon=mypath;
706 desc.icon_id=0;
707 desc.hotkey=0x1234;
708 create_lnk(lnkfile, &desc, 0);
709 /* Check that link is created to proper location */
710 SearchPathA( NULL, "rundll32", NULL, MAX_PATH, realpath, NULL);
711 desc.path=realpath;
712 check_lnk(lnkfile, &desc, 0x4);
713
714 /* Create a temporary non-executable file */
715 r=GetTempPathA(sizeof(mypath), mypath);
716 ok(r<sizeof(mypath), "GetTempPath failed (%d), err %d\n", r, GetLastError());
717 r=pGetLongPathNameA(mypath, mydir, sizeof(mydir));
718 ok(r<sizeof(mydir), "GetLongPathName failed (%d), err %d\n", r, GetLastError());
719 p=strrchr(mydir, '\\');
720 if (p)
721 *p='\0';
722
723 strcpy(mypath, mydir);
724 strcat(mypath, "\\test.txt");
725 hf = CreateFileA(mypath, GENERIC_WRITE, 0, NULL,
726 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
727 CloseHandle(hf);
728
729 /* Overwrite the existing lnk file and test link to an existing non-executable file */
730 desc.description="non-executable file";
731 desc.workdir=mydir;
732 desc.path=mypath;
733 desc.pidl=NULL;
734 desc.arguments="";
735 desc.showcmd=SW_SHOWNORMAL;
736 desc.icon=mypath;
737 desc.icon_id=0;
738 desc.hotkey=0x1234;
739 create_lnk(lnkfile, &desc, 0);
740 check_lnk(lnkfile, &desc, 0x0);
741
742 r=pGetShortPathNameA(mydir, mypath, sizeof(mypath));
743 ok(r<sizeof(mypath), "GetShortPathName failed (%d), err %d\n", r, GetLastError());
744
745 strcpy(realpath, mypath);
746 strcat(realpath, "\\test.txt");
747 strcat(mypath, "\\\\test.txt");
748
749 /* Overwrite the existing lnk file and test link to a short path with double backslashes */
750 desc.description="non-executable file";
751 desc.workdir=mydir;
752 desc.path=mypath;
753 desc.pidl=NULL;
754 desc.arguments="";
755 desc.showcmd=SW_SHOWNORMAL;
756 desc.icon=mypath;
757 desc.icon_id=0;
758 desc.hotkey=0x1234;
759 create_lnk(lnkfile, &desc, 0);
760 desc.path=realpath;
761 check_lnk(lnkfile, &desc, 0x0);
762
763 r = DeleteFileA(mypath);
764 ok(r, "failed to delete file %s (%d)\n", mypath, GetLastError());
765
766 /* Create a temporary .bat file */
767 strcpy(mypath, mydir);
768 strcat(mypath, "\\test.bat");
769 hf = CreateFileA(mypath, GENERIC_WRITE, 0, NULL,
770 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
771 CloseHandle(hf);
772
773 strcpy(realpath, mypath);
774
775 p=strrchr(mypath, '.');
776 if (p)
777 *p='\0';
778
779 /* Try linking to the .bat file without the extension */
780 desc.description="batch file";
781 desc.workdir=mydir;
782 desc.path=mypath;
783 desc.pidl=NULL;
784 desc.arguments="";
785 desc.showcmd=SW_SHOWNORMAL;
786 desc.icon=mypath;
787 desc.icon_id=0;
788 desc.hotkey=0x1234;
789 create_lnk(lnkfile, &desc, 0);
790 desc.path = realpath;
791 check_lnk(lnkfile, &desc, 0x4);
792
793 r = DeleteFileA(realpath);
794 ok(r, "failed to delete file %s (%d)\n", realpath, GetLastError());
795
796 /* FIXME: Also test saving a .lnk pointing to a pidl that cannot be
797 * represented as a path.
798 */
799
800 /* DeleteFileW is not implemented on Win9x */
801 r=DeleteFileA(lnkfileA);
802 ok(r, "failed to delete link '%s' (%d)\n", lnkfileA, GetLastError());
803 }
804
805 static void test_datalink(void)
806 {
807 static const WCHAR lnk[] = {
808 ':',':','{','9','d','b','1','1','8','6','e','-','4','0','d','f','-','1',
809 '1','d','1','-','a','a','8','c','-','0','0','c','0','4','f','b','6','7',
810 '8','6','3','}',':','2','6',',','!','!','g','x','s','f','(','N','g',']',
811 'q','F','`','H','{','L','s','A','C','C','E','S','S','F','i','l','e','s',
812 '>','p','l','T',']','j','I','{','j','f','(','=','1','&','L','[','-','8',
813 '1','-',']',':',':',0 };
814 static const WCHAR comp[] = {
815 '2','6',',','!','!','g','x','s','f','(','N','g',']','q','F','`','H','{',
816 'L','s','A','C','C','E','S','S','F','i','l','e','s','>','p','l','T',']',
817 'j','I','{','j','f','(','=','1','&','L','[','-','8','1','-',']',0 };
818 IShellLinkDataList *dl = NULL;
819 IShellLinkW *sl = NULL;
820 HRESULT r;
821 DWORD flags = 0;
822 EXP_DARWIN_LINK *dar;
823
824 r = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
825 &IID_IShellLinkW, (LPVOID*)&sl );
826 ok( r == S_OK ||
827 broken(r == E_NOINTERFACE), /* Win9x */
828 "CoCreateInstance failed (0x%08x)\n", r);
829 if (!sl)
830 {
831 win_skip("no shelllink\n");
832 return;
833 }
834
835 r = IShellLinkW_QueryInterface( sl, &_IID_IShellLinkDataList, (LPVOID*) &dl );
836 ok( r == S_OK ||
837 broken(r == E_NOINTERFACE), /* NT4 */
838 "IShellLinkW_QueryInterface failed (0x%08x)\n", r);
839
840 if (!dl)
841 {
842 win_skip("no datalink interface\n");
843 IShellLinkW_Release( sl );
844 return;
845 }
846
847 flags = 0;
848 r = IShellLinkDataList_GetFlags( dl, &flags );
849 ok( r == S_OK, "GetFlags failed\n");
850 ok( flags == 0, "GetFlags returned wrong flags\n");
851
852 dar = (void*)-1;
853 r = IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
854 ok( r == E_FAIL, "CopyDataBlock failed\n");
855 ok( dar == NULL, "should be null\n");
856
857 if (!pGetLongPathNameA /* NT4 */)
858 skip("SetPath with NULL parameter crashes on NT4\n");
859 else
860 {
861 r = IShellLinkW_SetPath(sl, NULL);
862 ok(r == E_INVALIDARG, "SetPath returned wrong error (0x%08x)\n", r);
863 }
864
865 r = IShellLinkW_SetPath(sl, lnk);
866 ok(r == S_OK, "SetPath failed\n");
867
868 if (0)
869 {
870 /* the following crashes */
871 IShellLinkDataList_GetFlags( dl, NULL );
872 }
873
874 flags = 0;
875 r = IShellLinkDataList_GetFlags( dl, &flags );
876 ok( r == S_OK, "GetFlags failed\n");
877 /* SLDF_HAS_LOGO3ID is no longer supported on Vista+, filter it out */
878 ok( (flags & (~ SLDF_HAS_LOGO3ID)) == SLDF_HAS_DARWINID,
879 "GetFlags returned wrong flags\n");
880
881 dar = NULL;
882 r = IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
883 ok( r == S_OK, "CopyDataBlock failed\n");
884
885 ok( dar && ((DATABLOCK_HEADER*)dar)->dwSignature == EXP_DARWIN_ID_SIG, "signature wrong\n");
886 ok( dar && 0==lstrcmpW(dar->szwDarwinID, comp ), "signature wrong\n");
887
888 LocalFree( dar );
889
890 IShellLinkDataList_Release( dl );
891 IShellLinkW_Release( sl );
892 }
893
894 static void test_shdefextracticon(void)
895 {
896 HICON hiconlarge=NULL, hiconsmall=NULL;
897 HRESULT res;
898
899 if (!pSHDefExtractIconA)
900 {
901 win_skip("SHDefExtractIconA is unavailable\n");
902 return;
903 }
904
905 res = pSHDefExtractIconA("shell32.dll", 0, 0, &hiconlarge, &hiconsmall, MAKELONG(16,24));
906 ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%x\n", res);
907 ok(hiconlarge != NULL, "got null hiconlarge\n");
908 ok(hiconsmall != NULL, "got null hiconsmall\n");
909 DestroyIcon(hiconlarge);
910 DestroyIcon(hiconsmall);
911
912 hiconsmall = NULL;
913 res = pSHDefExtractIconA("shell32.dll", 0, 0, NULL, &hiconsmall, MAKELONG(16,24));
914 ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%x\n", res);
915 ok(hiconsmall != NULL, "got null hiconsmall\n");
916 DestroyIcon(hiconsmall);
917
918 res = pSHDefExtractIconA("shell32.dll", 0, 0, NULL, NULL, MAKELONG(16,24));
919 ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%x\n", res);
920 }
921
922 static void test_GetIconLocation(void)
923 {
924 IShellLinkA *sl;
925 const char *str;
926 char buffer[INFOTIPSIZE], mypath[MAX_PATH];
927 int i;
928 HRESULT r;
929 LPITEMIDLIST pidl;
930
931 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
932 &IID_IShellLinkA, (LPVOID*)&sl);
933 ok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
934 if(r != S_OK)
935 return;
936
937 i = 0xdeadbeef;
938 strcpy(buffer, "garbage");
939 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
940 ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
941 ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
942 ok(i == 0, "GetIconLocation returned %d\n", i);
943
944 str = "c:\\some\\path";
945 r = IShellLinkA_SetPath(sl, str);
946 ok(r == S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
947
948 i = 0xdeadbeef;
949 strcpy(buffer, "garbage");
950 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
951 ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
952 ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
953 ok(i == 0, "GetIconLocation returned %d\n", i);
954
955 GetWindowsDirectoryA(mypath, sizeof(mypath) - 12);
956 strcat(mypath, "\\regedit.exe");
957 pidl = path_to_pidl(mypath);
958 r = IShellLinkA_SetIDList(sl, pidl);
959 ok(r == S_OK, "SetPath failed (0x%08x)\n", r);
960 pILFree(pidl);
961
962 i = 0xdeadbeef;
963 strcpy(buffer, "garbage");
964 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
965 ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
966 ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
967 ok(i == 0, "GetIconLocation returned %d\n", i);
968
969 str = "c:\\nonexistent\\file";
970 r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
971 ok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
972
973 i = 0xdeadbeef;
974 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
975 ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
976 ok(lstrcmpiA(buffer,str) == 0, "GetIconLocation returned '%s'\n", buffer);
977 ok(i == 0xbabecafe, "GetIconLocation returned %d'\n", i);
978
979 IShellLinkA_Release(sl);
980 }
981
982 static void test_SHGetStockIconInfo(void)
983 {
984 BYTE buffer[sizeof(SHSTOCKICONINFO) + 16];
985 SHSTOCKICONINFO *sii = (SHSTOCKICONINFO *) buffer;
986 HRESULT hr;
987 INT i;
988
989 /* not present before vista */
990 if (!pSHGetStockIconInfo)
991 {
992 win_skip("SHGetStockIconInfo not available\n");
993 return;
994 }
995
996 /* negative values are handled */
997 memset(buffer, '#', sizeof(buffer));
998 sii->cbSize = sizeof(SHSTOCKICONINFO);
999 hr = pSHGetStockIconInfo(SIID_INVALID, SHGSI_ICONLOCATION, sii);
1000 ok(hr == E_INVALIDARG, "-1: got 0x%x (expected E_INVALIDARG)\n", hr);
1001
1002 /* max. id for vista is 140 (no definition exists for this value) */
1003 for (i = SIID_DOCNOASSOC; i <= SIID_CLUSTEREDDRIVE; i++)
1004 {
1005 memset(buffer, '#', sizeof(buffer));
1006 sii->cbSize = sizeof(SHSTOCKICONINFO);
1007 hr = pSHGetStockIconInfo(i, SHGSI_ICONLOCATION, sii);
1008
1009 ok(hr == S_OK,
1010 "%3d: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x (expected S_OK)\n",
1011 i, hr, sii->iSysImageIndex, sii->iIcon);
1012
1013 if ((hr == S_OK) && (winetest_debug > 1))
1014 trace("%3d: got iSysImageIndex %3d, iIcon %3d and %s\n", i, sii->iSysImageIndex,
1015 sii->iIcon, wine_dbgstr_w(sii->szPath));
1016 }
1017
1018 /* test invalid icons indices that are invalid for all platforms */
1019 for (i = SIID_MAX_ICONS; i < (SIID_MAX_ICONS + 25) ; i++)
1020 {
1021 memset(buffer, '#', sizeof(buffer));
1022 sii->cbSize = sizeof(SHSTOCKICONINFO);
1023 hr = pSHGetStockIconInfo(i, SHGSI_ICONLOCATION, sii);
1024 ok(hr == E_INVALIDARG, "%3d: got 0x%x (expected E_INVALIDARG)\n", i, hr);
1025 todo_wine {
1026 ok(sii->iSysImageIndex == -1, "%d: got iSysImageIndex %d\n", i, sii->iSysImageIndex);
1027 ok(sii->iIcon == -1, "%d: got iIcon %d\n", i, sii->iIcon);
1028 }
1029 }
1030
1031 /* test more returned SHSTOCKICONINFO elements without extra flags */
1032 memset(buffer, '#', sizeof(buffer));
1033 sii->cbSize = sizeof(SHSTOCKICONINFO);
1034 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1035 ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr);
1036 ok(!sii->hIcon, "got %p (expected NULL)\n", sii->hIcon);
1037 ok(sii->iSysImageIndex == -1, "got %d (expected -1)\n", sii->iSysImageIndex);
1038
1039 /* the exact size is required of the struct */
1040 memset(buffer, '#', sizeof(buffer));
1041 sii->cbSize = sizeof(SHSTOCKICONINFO) + 2;
1042 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1043 ok(hr == E_INVALIDARG, "+2: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1044
1045 memset(buffer, '#', sizeof(buffer));
1046 sii->cbSize = sizeof(SHSTOCKICONINFO) + 1;
1047 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1048 ok(hr == E_INVALIDARG, "+1: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1049
1050 memset(buffer, '#', sizeof(buffer));
1051 sii->cbSize = sizeof(SHSTOCKICONINFO) - 1;
1052 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1053 ok(hr == E_INVALIDARG, "-1: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1054
1055 memset(buffer, '#', sizeof(buffer));
1056 sii->cbSize = sizeof(SHSTOCKICONINFO) - 2;
1057 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1058 ok(hr == E_INVALIDARG, "-2: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1059
1060 /* there is a NULL check for the struct */
1061 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, NULL);
1062 ok(hr == E_INVALIDARG, "NULL: got 0x%x\n", hr);
1063 }
1064
1065 static void test_SHExtractIcons(void)
1066 {
1067 static const WCHAR notepadW[] = {'n','o','t','e','p','a','d','.','e','x','e',0};
1068 static const WCHAR shell32W[] = {'s','h','e','l','l','3','2','.','d','l','l',0};
1069 static const WCHAR emptyW[] = {0};
1070 UINT ret, ret2;
1071 HICON icons[256];
1072 UINT ids[256], i;
1073
1074 if (!pSHExtractIconsW)
1075 {
1076 win_skip("SHExtractIconsW not available\n");
1077 return;
1078 }
1079
1080 ret = pSHExtractIconsW(emptyW, 0, 16, 16, icons, ids, 1, 0);
1081 ok(ret == ~0u, "got %u\n", ret);
1082
1083 ret = pSHExtractIconsW(notepadW, 0, 16, 16, NULL, NULL, 1, 0);
1084 ok(ret == 1 || broken(ret == 2) /* win2k */, "got %u\n", ret);
1085
1086 icons[0] = (HICON)0xdeadbeef;
1087 ret = pSHExtractIconsW(notepadW, 0, 16, 16, icons, NULL, 1, 0);
1088 ok(ret == 1, "got %u\n", ret);
1089 ok(icons[0] != (HICON)0xdeadbeef, "icon not set\n");
1090 DestroyIcon(icons[0]);
1091
1092 icons[0] = (HICON)0xdeadbeef;
1093 ids[0] = 0xdeadbeef;
1094 ret = pSHExtractIconsW(notepadW, 0, 16, 16, icons, ids, 1, 0);
1095 ok(ret == 1, "got %u\n", ret);
1096 ok(icons[0] != (HICON)0xdeadbeef, "icon not set\n");
1097 ok(ids[0] != 0xdeadbeef, "id not set\n");
1098 DestroyIcon(icons[0]);
1099
1100 ret = pSHExtractIconsW(shell32W, 0, 16, 16, NULL, NULL, 0, 0);
1101 ret2 = pSHExtractIconsW(shell32W, 4, MAKELONG(32,16), MAKELONG(32,16), NULL, NULL, 256, 0);
1102 ok(ret && ret == ret2,
1103 "icon count should be independent of requested icon sizes and base icon index\n");
1104
1105 ret = pSHExtractIconsW(shell32W, 0, 16, 16, icons, ids, 0, 0);
1106 ok(ret == ~0u || !ret /* < vista */, "got %u\n", ret);
1107
1108 ret = pSHExtractIconsW(shell32W, 0, 16, 16, icons, ids, 3, 0);
1109 ok(ret == 3, "got %u\n", ret);
1110 for (i = 0; i < ret; i++) DestroyIcon(icons[i]);
1111
1112 /* count must be a multiple of two when getting two sizes */
1113 ret = pSHExtractIconsW(shell32W, 0, MAKELONG(16,32), MAKELONG(16,32), icons, ids, 3, 0);
1114 ok(!ret /* vista */ || ret == 4, "got %u\n", ret);
1115 for (i = 0; i < ret; i++) DestroyIcon(icons[i]);
1116
1117 ret = pSHExtractIconsW(shell32W, 0, MAKELONG(16,32), MAKELONG(16,32), icons, ids, 4, 0);
1118 ok(ret == 4, "got %u\n", ret);
1119 for (i = 0; i < ret; i++) DestroyIcon(icons[i]);
1120 }
1121
1122 static void test_propertystore(void)
1123 {
1124 IShellLinkA *linkA;
1125 IShellLinkW *linkW;
1126 IPropertyStore *ps;
1127 HRESULT hr;
1128
1129 hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1130 &IID_IShellLinkA, (void**)&linkA);
1131 ok(hr == S_OK, "got 0x%08x\n", hr);
1132
1133 hr = IShellLinkA_QueryInterface(linkA, &IID_IShellLinkW, (void**)&linkW);
1134 ok(hr == S_OK, "got 0x%08x\n", hr);
1135
1136 hr = IShellLinkA_QueryInterface(linkA, &IID_IPropertyStore, (void**)&ps);
1137 if (hr == S_OK) {
1138 IPropertyStoreCache *pscache;
1139
1140 IPropertyStore_Release(ps);
1141
1142 hr = IShellLinkW_QueryInterface(linkW, &IID_IPropertyStore, (void**)&ps);
1143 ok(hr == S_OK, "got 0x%08x\n", hr);
1144
1145 hr = IPropertyStore_QueryInterface(ps, &IID_IPropertyStoreCache, (void**)&pscache);
1146 ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1147
1148 IPropertyStore_Release(ps);
1149 }
1150 else
1151 win_skip("IShellLink doesn't support IPropertyStore.\n");
1152
1153 IShellLinkA_Release(linkA);
1154 IShellLinkW_Release(linkW);
1155 }
1156
1157 static void test_ExtractIcon(void)
1158 {
1159 static const WCHAR nameW[] = {'\\','e','x','t','r','a','c','t','i','c','o','n','_','t','e','s','t','.','t','x','t',0};
1160 static const WCHAR shell32W[] = {'s','h','e','l','l','3','2','.','d','l','l',0};
1161 WCHAR pathW[MAX_PATH];
1162 HICON hicon, hicon2;
1163 char path[MAX_PATH];
1164 HANDLE file;
1165 int r;
1166 ICONINFO info;
1167 BITMAP bm;
1168
1169 /* specified instance handle */
1170 hicon = ExtractIconA(GetModuleHandleA("shell32.dll"), NULL, 0);
1171 todo_wine
1172 ok(hicon == NULL, "Got icon %p\n", hicon);
1173 hicon2 = ExtractIconA(GetModuleHandleA("shell32.dll"), "shell32.dll", -1);
1174 ok(hicon2 != NULL, "Got icon %p\n", hicon2);
1175
1176 /* existing index */
1177 hicon = ExtractIconA(NULL, "shell32.dll", 0);
1178 ok(hicon != NULL && HandleToLong(hicon) != -1, "Got icon %p\n", hicon);
1179 DestroyIcon(hicon);
1180
1181 /* returns number of resources */
1182 hicon = ExtractIconA(NULL, "shell32.dll", -1);
1183 ok(HandleToLong(hicon) > 1 && hicon == hicon2, "Got icon %p\n", hicon);
1184
1185 /* invalid index, valid dll name */
1186 hicon = ExtractIconA(NULL, "shell32.dll", 3000);
1187 ok(hicon == NULL, "Got icon %p\n", hicon);
1188
1189 /* Create a temporary non-executable file */
1190 GetTempPathA(sizeof(path), path);
1191 strcat(path, "\\extracticon_test.txt");
1192 file = CreateFileA(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1193 ok(file != INVALID_HANDLE_VALUE, "Failed to create a test file\n");
1194 CloseHandle(file);
1195
1196 hicon = ExtractIconA(NULL, path, 0);
1197 todo_wine
1198 ok(hicon == NULL, "Got icon %p\n", hicon);
1199
1200 hicon = ExtractIconA(NULL, path, -1);
1201 ok(hicon == NULL, "Got icon %p\n", hicon);
1202
1203 hicon = ExtractIconA(NULL, path, 1);
1204 todo_wine
1205 ok(hicon == NULL, "Got icon %p\n", hicon);
1206
1207 r = DeleteFileA(path);
1208 ok(r, "failed to delete file %s (%d)\n", path, GetLastError());
1209
1210 /* same for W variant */
1211 if (0)
1212 {
1213 /* specified instance handle, crashes on XP, 2k3 */
1214 hicon = ExtractIconW(GetModuleHandleA("shell32.dll"), NULL, 0);
1215 ok(hicon == NULL, "Got icon %p\n", hicon);
1216 }
1217 hicon2 = ExtractIconW(GetModuleHandleA("shell32.dll"), shell32W, -1);
1218 ok(hicon2 != NULL, "Got icon %p\n", hicon2);
1219
1220 /* existing index */
1221 hicon = ExtractIconW(NULL, shell32W, 0);
1222 ok(hicon != NULL && HandleToLong(hicon) != -1, "Got icon %p\n", hicon);
1223 GetIconInfo(hicon, &info);
1224 GetObjectW(info.hbmColor, sizeof(bm), &bm);
1225 ok(bm.bmWidth == GetSystemMetrics(SM_CXICON), "got %d\n", bm.bmWidth);
1226 ok(bm.bmHeight == GetSystemMetrics(SM_CYICON), "got %d\n", bm.bmHeight);
1227 DestroyIcon(hicon);
1228
1229 /* returns number of resources */
1230 hicon = ExtractIconW(NULL, shell32W, -1);
1231 ok(HandleToLong(hicon) > 1 && hicon == hicon2, "Got icon %p\n", hicon);
1232
1233 /* invalid index, valid dll name */
1234 hicon = ExtractIconW(NULL, shell32W, 3000);
1235 ok(hicon == NULL, "Got icon %p\n", hicon);
1236
1237 /* Create a temporary non-executable file */
1238 GetTempPathW(sizeof(pathW)/sizeof(pathW[0]), pathW);
1239 lstrcatW(pathW, nameW);
1240 file = CreateFileW(pathW, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1241 ok(file != INVALID_HANDLE_VALUE, "Failed to create a test file\n");
1242 CloseHandle(file);
1243
1244 hicon = ExtractIconW(NULL, pathW, 0);
1245 todo_wine
1246 ok(hicon == NULL, "Got icon %p\n", hicon);
1247
1248 hicon = ExtractIconW(NULL, pathW, -1);
1249 ok(hicon == NULL, "Got icon %p\n", hicon);
1250
1251 hicon = ExtractIconW(NULL, pathW, 1);
1252 todo_wine
1253 ok(hicon == NULL, "Got icon %p\n", hicon);
1254
1255 r = DeleteFileW(pathW);
1256 ok(r, "failed to delete file %s (%d)\n", path, GetLastError());
1257 }
1258
1259 static void test_ExtractAssociatedIcon(void)
1260 {
1261 char pathA[MAX_PATH];
1262 HICON hicon;
1263 WORD index;
1264
1265 /* empty path */
1266 index = 0;
1267 *pathA = 0;
1268 hicon = ExtractAssociatedIconA(NULL, pathA, &index);
1269 todo_wine {
1270 ok(hicon != NULL, "Got icon %p\n", hicon);
1271 ok(!*pathA, "Unexpected path %s\n", pathA);
1272 ok(index == 0, "Unexpected index %u\n", index);
1273 }
1274 DestroyIcon(hicon);
1275
1276 /* by index */
1277 index = 0;
1278 strcpy(pathA, "shell32.dll");
1279 hicon = ExtractAssociatedIconA(NULL, pathA, &index);
1280 ok(hicon != NULL, "Got icon %p\n", hicon);
1281 ok(!strcmp(pathA, "shell32.dll"), "Unexpected path %s\n", pathA);
1282 ok(index == 0, "Unexpected index %u\n", index);
1283 DestroyIcon(hicon);
1284
1285 /* valid dll name, invalid index */
1286 index = 5000;
1287 strcpy(pathA, "user32.dll");
1288 hicon = ExtractAssociatedIconA(NULL, pathA, &index);
1289 CharLowerBuffA(pathA, strlen(pathA));
1290 todo_wine {
1291 ok(hicon != NULL, "Got icon %p\n", hicon);
1292 ok(!!strstr(pathA, "shell32.dll"), "Unexpected path %s\n", pathA);
1293 }
1294 ok(index != 5000, "Unexpected index %u\n", index);
1295 DestroyIcon(hicon);
1296
1297 /* associated icon */
1298 index = 0xcaca;
1299 strcpy(pathA, "dummy.exe");
1300 hicon = ExtractAssociatedIconA(NULL, pathA, &index);
1301 CharLowerBuffA(pathA, strlen(pathA));
1302 todo_wine {
1303 ok(hicon != NULL, "Got icon %p\n", hicon);
1304 ok(!!strstr(pathA, "shell32.dll"), "Unexpected path %s\n", pathA);
1305 }
1306 ok(index != 0xcaca, "Unexpected index %u\n", index);
1307 DestroyIcon(hicon);
1308 }
1309
1310 static int get_shell_icon_size(void)
1311 {
1312 char buf[10];
1313 DWORD value = 32, size = sizeof(buf), type;
1314 HKEY key;
1315
1316 if (!RegOpenKeyA( HKEY_CURRENT_USER, "Control Panel\\Desktop\\WindowMetrics", &key ))
1317 {
1318 if (!RegQueryValueExA( key, "Shell Icon Size", NULL, &type, (BYTE *)buf, &size ) && type == REG_SZ)
1319 value = atoi( buf );
1320 RegCloseKey( key );
1321 }
1322 return value;
1323 }
1324
1325 static void test_SHGetImageList(void)
1326 {
1327 HRESULT hr;
1328 IImageList *list, *list2;
1329 BOOL ret;
1330 HIMAGELIST lg, sm;
1331 ULONG start_refs, refs;
1332 int i, width, height, expect;
1333 BOOL dpi_aware = pIsProcessDPIAware && pIsProcessDPIAware();
1334
1335 hr = SHGetImageList( SHIL_LARGE, &IID_IImageList, (void **)&list );
1336 ok( hr == S_OK, "got %08x\n", hr );
1337 start_refs = IImageList_AddRef( list );
1338 IImageList_Release( list );
1339
1340 hr = SHGetImageList( SHIL_LARGE, &IID_IImageList, (void **)&list2 );
1341 ok( hr == S_OK, "got %08x\n", hr );
1342 ok( list == list2, "lists differ\n" );
1343 refs = IImageList_AddRef( list );
1344 IImageList_Release( list );
1345 ok( refs == start_refs + 1, "got %d, start_refs %d\n", refs, start_refs );
1346 IImageList_Release( list2 );
1347
1348 hr = SHGetImageList( SHIL_SMALL, &IID_IImageList, (void **)&list2 );
1349 ok( hr == S_OK, "got %08x\n", hr );
1350
1351 ret = Shell_GetImageLists( &lg, &sm );
1352 ok( ret, "got %d\n", ret );
1353 ok( lg == (HIMAGELIST)list, "mismatch\n" );
1354 ok( sm == (HIMAGELIST)list2, "mismatch\n" );
1355
1356 /* Shell_GetImageLists doesn't take a reference */
1357 refs = IImageList_AddRef( list );
1358 IImageList_Release( list );
1359 ok( refs == start_refs, "got %d, start_refs %d\n", refs, start_refs );
1360
1361 IImageList_Release( list2 );
1362 IImageList_Release( list );
1363
1364 /* Test the icon sizes */
1365 for (i = 0; i <= SHIL_LAST; i++)
1366 {
1367 hr = SHGetImageList( i, &IID_IImageList, (void **)&list );
1368 ok( hr == S_OK ||
1369 broken( i == SHIL_JUMBO && hr == E_INVALIDARG ), /* XP and 2003 */
1370 "%d: got %08x\n", i, hr );
1371 if (FAILED(hr)) continue;
1372 IImageList_GetIconSize( list, &width, &height );
1373 switch (i)
1374 {
1375 case SHIL_LARGE:
1376 if (dpi_aware) expect = GetSystemMetrics( SM_CXICON );
1377 else expect = get_shell_icon_size();
1378 break;
1379 case SHIL_SMALL:
1380 if (dpi_aware) expect = GetSystemMetrics( SM_CXICON ) / 2;
1381 else expect = GetSystemMetrics( SM_CXSMICON );
1382 break;
1383 case SHIL_EXTRALARGE:
1384 expect = (GetSystemMetrics( SM_CXICON ) * 3) / 2;
1385 break;
1386 case SHIL_SYSSMALL:
1387 expect = GetSystemMetrics( SM_CXSMICON );
1388 break;
1389 case SHIL_JUMBO:
1390 expect = 256;
1391 break;
1392 }
1393 todo_wine_if(i == SHIL_SYSSMALL && dpi_aware && expect != GetSystemMetrics( SM_CXICON ) / 2)
1394 {
1395 ok( width == expect, "%d: got %d expect %d\n", i, width, expect );
1396 ok( height == expect, "%d: got %d expect %d\n", i, height, expect );
1397 }
1398 IImageList_Release( list );
1399 }
1400 }
1401
1402 START_TEST(shelllink)
1403 {
1404 HRESULT r;
1405 HMODULE hmod = GetModuleHandleA("shell32.dll");
1406 HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
1407 HMODULE huser32 = GetModuleHandleA("user32.dll");
1408
1409 pILFree = (void *)GetProcAddress(hmod, (LPSTR)155);
1410 pILIsEqual = (void *)GetProcAddress(hmod, (LPSTR)21);
1411 pSHILCreateFromPath = (void *)GetProcAddress(hmod, (LPSTR)28);
1412 pSHDefExtractIconA = (void *)GetProcAddress(hmod, "SHDefExtractIconA");
1413 pSHGetStockIconInfo = (void *)GetProcAddress(hmod, "SHGetStockIconInfo");
1414 pGetLongPathNameA = (void *)GetProcAddress(hkernel32, "GetLongPathNameA");
1415 pGetShortPathNameA = (void *)GetProcAddress(hkernel32, "GetShortPathNameA");
1416 pSHExtractIconsW = (void *)GetProcAddress(hmod, "SHExtractIconsW");
1417 pIsProcessDPIAware = (void *)GetProcAddress(huser32, "IsProcessDPIAware");
1418
1419 r = CoInitialize(NULL);
1420 ok(r == S_OK, "CoInitialize failed (0x%08x)\n", r);
1421 if (r != S_OK)
1422 return;
1423
1424 test_get_set();
1425 test_load_save();
1426 test_datalink();
1427 test_shdefextracticon();
1428 test_GetIconLocation();
1429 test_SHGetStockIconInfo();
1430 test_SHExtractIcons();
1431 test_propertystore();
1432 test_ExtractIcon();
1433 test_ExtractAssociatedIcon();
1434 test_SHGetImageList();
1435
1436 CoUninitialize();
1437 }