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