8af3c978b2a2e0c8e239c4dd661836e0d6854587
[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 "wine/test.h"
31
32 #include "shell32_test.h"
33
34 #ifndef SLDF_HAS_LOGO3ID
35 # define SLDF_HAS_LOGO3ID 0x00000800 /* not available in the Vista SDK */
36 #endif
37
38 static void (WINAPI *pILFree)(LPITEMIDLIST);
39 static BOOL (WINAPI *pILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST);
40 static HRESULT (WINAPI *pSHILCreateFromPath)(LPCWSTR, LPITEMIDLIST *,DWORD*);
41 static HRESULT (WINAPI *pSHDefExtractIconA)(LPCSTR, int, UINT, HICON*, HICON*, UINT);
42 static HRESULT (WINAPI *pSHGetStockIconInfo)(SHSTOCKICONID, UINT, SHSTOCKICONINFO *);
43 static DWORD (WINAPI *pGetLongPathNameA)(LPCSTR, LPSTR, DWORD);
44 static DWORD (WINAPI *pGetShortPathNameA)(LPCSTR, LPSTR, DWORD);
45 static UINT (WINAPI *pSHExtractIconsW)(LPCWSTR, int, int, int, HICON *, UINT *, UINT, UINT);
46
47 static const GUID _IID_IShellLinkDataList = {
48 0x45e2b4ae, 0xb1c3, 0x11d0,
49 { 0xb9, 0x2f, 0x00, 0xa0, 0xc9, 0x03, 0x12, 0xe1 }
50 };
51
52
53 /* For some reason SHILCreateFromPath does not work on Win98 and
54 * SHSimpleIDListFromPathA does not work on NT4. But if we call both we
55 * get what we want on all platforms.
56 */
57 static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathAW)(LPCVOID);
58
59 static LPITEMIDLIST path_to_pidl(const char* path)
60 {
61 LPITEMIDLIST pidl;
62
63 if (!pSHSimpleIDListFromPathAW)
64 {
65 HMODULE hdll=GetModuleHandleA("shell32.dll");
66 pSHSimpleIDListFromPathAW=(void*)GetProcAddress(hdll, (char*)162);
67 if (!pSHSimpleIDListFromPathAW)
68 win_skip("SHSimpleIDListFromPathAW not found in shell32.dll\n");
69 }
70
71 pidl=NULL;
72 /* pSHSimpleIDListFromPathAW maps to A on non NT platforms */
73 if (pSHSimpleIDListFromPathAW && (GetVersion() & 0x80000000))
74 pidl=pSHSimpleIDListFromPathAW(path);
75
76 if (!pidl)
77 {
78 WCHAR* pathW;
79 HRESULT r;
80 int len;
81
82 len=MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
83 pathW=HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
84 MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, len);
85
86 r=pSHILCreateFromPath(pathW, &pidl, NULL);
87 ok(r == S_OK, "SHILCreateFromPath failed (0x%08x)\n", r);
88 HeapFree(GetProcessHeap(), 0, pathW);
89 }
90 return pidl;
91 }
92
93
94 /*
95 * Test manipulation of an IShellLink's properties.
96 */
97
98 static void test_get_set(void)
99 {
100 HRESULT r;
101 IShellLinkA *sl;
102 IShellLinkW *slW = NULL;
103 char mypath[MAX_PATH];
104 char buffer[INFOTIPSIZE];
105 LPITEMIDLIST pidl, tmp_pidl;
106 const char * str;
107 int i;
108 WORD w;
109
110 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
111 &IID_IShellLinkA, (LPVOID*)&sl);
112 ok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
113 if (r != S_OK)
114 return;
115
116 /* Test Getting / Setting the description */
117 strcpy(buffer,"garbage");
118 r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
119 ok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
120 ok(*buffer=='\0', "GetDescription returned '%s'\n", buffer);
121
122 str="Some description";
123 r = IShellLinkA_SetDescription(sl, str);
124 ok(r == S_OK, "SetDescription failed (0x%08x)\n", r);
125
126 strcpy(buffer,"garbage");
127 r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
128 ok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
129 ok(strcmp(buffer,str)==0, "GetDescription returned '%s'\n", buffer);
130
131 r = IShellLinkA_SetDescription(sl, NULL);
132 ok(r == S_OK, "SetDescription failed (0x%08x)\n", r);
133
134 strcpy(buffer,"garbage");
135 r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
136 ok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
137 ok(*buffer=='\0' || broken(strcmp(buffer,str)==0), "GetDescription returned '%s'\n", buffer); /* NT4 */
138
139 /* Test Getting / Setting the work directory */
140 strcpy(buffer,"garbage");
141 r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
142 ok(r == S_OK, "GetWorkingDirectory failed (0x%08x)\n", r);
143 ok(*buffer=='\0', "GetWorkingDirectory returned '%s'\n", buffer);
144
145 str="c:\\nonexistent\\directory";
146 r = IShellLinkA_SetWorkingDirectory(sl, str);
147 ok(r == S_OK, "SetWorkingDirectory failed (0x%08x)\n", r);
148
149 strcpy(buffer,"garbage");
150 r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
151 ok(r == S_OK, "GetWorkingDirectory failed (0x%08x)\n", r);
152 ok(lstrcmpiA(buffer,str)==0, "GetWorkingDirectory returned '%s'\n", buffer);
153
154 /* Test Getting / Setting the path */
155 strcpy(buffer,"garbage");
156 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
157 todo_wine ok(r == S_FALSE || broken(r == S_OK) /* NT4/W2K */, "GetPath failed (0x%08x)\n", r);
158 ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
159
160 CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
161 &IID_IShellLinkW, (LPVOID*)&slW);
162 if (!slW /* Win9x */ || !pGetLongPathNameA /* NT4 */)
163 skip("SetPath with NULL parameter crashes on Win9x and some NT4\n");
164 else
165 {
166 IShellLinkW_Release(slW);
167 r = IShellLinkA_SetPath(sl, NULL);
168 ok(r==E_INVALIDARG ||
169 broken(r==S_OK), /* Some Win95 and NT4 */
170 "SetPath returned wrong error (0x%08x)\n", r);
171 }
172
173 r = IShellLinkA_SetPath(sl, "");
174 ok(r==S_OK, "SetPath failed (0x%08x)\n", r);
175
176 strcpy(buffer,"garbage");
177 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
178 todo_wine ok(r == S_FALSE, "GetPath failed (0x%08x)\n", r);
179 ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
180
181 /* Win98 returns S_FALSE, but WinXP returns S_OK */
182 str="c:\\nonexistent\\file";
183 r = IShellLinkA_SetPath(sl, str);
184 ok(r==S_FALSE || r==S_OK, "SetPath failed (0x%08x)\n", r);
185
186 strcpy(buffer,"garbage");
187 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
188 ok(r == S_OK, "GetPath failed (0x%08x)\n", r);
189 ok(lstrcmpiA(buffer,str)==0, "GetPath returned '%s'\n", buffer);
190
191 /* Get some real path to play with */
192 GetWindowsDirectoryA( mypath, sizeof(mypath)-12 );
193 strcat(mypath, "\\regedit.exe");
194
195 /* Test the interaction of SetPath and SetIDList */
196 tmp_pidl=NULL;
197 r = IShellLinkA_GetIDList(sl, &tmp_pidl);
198 ok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
199 if (r == S_OK)
200 {
201 BOOL ret;
202
203 strcpy(buffer,"garbage");
204 ret = SHGetPathFromIDListA(tmp_pidl, buffer);
205 ok(ret, "SHGetPathFromIDListA failed\n");
206 if (ret)
207 ok(lstrcmpiA(buffer,str)==0, "GetIDList returned '%s'\n", buffer);
208 pILFree(tmp_pidl);
209 }
210
211 pidl=path_to_pidl(mypath);
212 ok(pidl!=NULL, "path_to_pidl returned a NULL pidl\n");
213
214 if (pidl)
215 {
216 LPITEMIDLIST second_pidl;
217
218 r = IShellLinkA_SetIDList(sl, pidl);
219 ok(r == S_OK, "SetIDList failed (0x%08x)\n", r);
220
221 tmp_pidl=NULL;
222 r = IShellLinkA_GetIDList(sl, &tmp_pidl);
223 ok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
224 ok(tmp_pidl && pILIsEqual(pidl, tmp_pidl),
225 "GetIDList returned an incorrect pidl\n");
226
227 r = IShellLinkA_GetIDList(sl, &second_pidl);
228 ok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
229 ok(second_pidl && pILIsEqual(pidl, second_pidl),
230 "GetIDList returned an incorrect pidl\n");
231 ok(second_pidl != tmp_pidl, "pidls are the same\n");
232
233 pILFree(second_pidl);
234 pILFree(tmp_pidl);
235 pILFree(pidl);
236
237 strcpy(buffer,"garbage");
238 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
239 ok(r == S_OK, "GetPath failed (0x%08x)\n", r);
240 todo_wine
241 ok(lstrcmpiA(buffer, mypath)==0, "GetPath returned '%s'\n", buffer);
242 }
243
244 /* test path with quotes (IShellLinkA_SetPath returns S_FALSE on W2K and below and S_OK on XP and above */
245 r = IShellLinkA_SetPath(sl, "\"c:\\nonexistent\\file\"");
246 ok(r==S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
247
248 strcpy(buffer,"garbage");
249 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
250 ok(r==S_OK, "GetPath failed (0x%08x)\n", r);
251 todo_wine ok(!strcmp(buffer, "C:\\nonexistent\\file") ||
252 broken(!strcmp(buffer, "C:\\\"c:\\nonexistent\\file\"")), /* NT4 */
253 "case doesn't match\n");
254
255 r = IShellLinkA_SetPath(sl, "\"c:\\foo");
256 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
257
258 r = IShellLinkA_SetPath(sl, "\"\"c:\\foo");
259 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
260
261 r = IShellLinkA_SetPath(sl, "c:\\foo\"");
262 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
263
264 r = IShellLinkA_SetPath(sl, "\"\"c:\\foo\"");
265 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
266
267 r = IShellLinkA_SetPath(sl, "\"\"c:\\foo\"\"");
268 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08x)\n", r);
269
270 /* Test Getting / Setting the arguments */
271 strcpy(buffer,"garbage");
272 r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
273 ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
274 ok(*buffer=='\0', "GetArguments returned '%s'\n", buffer);
275
276 str="param1 \"spaced param2\"";
277 r = IShellLinkA_SetArguments(sl, str);
278 ok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
279
280 strcpy(buffer,"garbage");
281 r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
282 ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
283 ok(strcmp(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
284
285 strcpy(buffer,"garbage");
286 r = IShellLinkA_SetArguments(sl, NULL);
287 ok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
288 r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
289 ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
290 ok(!buffer[0] || strcmp(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
291
292 strcpy(buffer,"garbage");
293 r = IShellLinkA_SetArguments(sl, "");
294 ok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
295 r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
296 ok(r == S_OK, "GetArguments failed (0x%08x)\n", r);
297 ok(!buffer[0], "GetArguments returned '%s'\n", buffer);
298
299 /* Test Getting / Setting showcmd */
300 i=0xdeadbeef;
301 r = IShellLinkA_GetShowCmd(sl, &i);
302 ok(r == S_OK, "GetShowCmd failed (0x%08x)\n", r);
303 ok(i==SW_SHOWNORMAL, "GetShowCmd returned %d\n", i);
304
305 r = IShellLinkA_SetShowCmd(sl, SW_SHOWMAXIMIZED);
306 ok(r == S_OK, "SetShowCmd failed (0x%08x)\n", r);
307
308 i=0xdeadbeef;
309 r = IShellLinkA_GetShowCmd(sl, &i);
310 ok(r == S_OK, "GetShowCmd failed (0x%08x)\n", r);
311 ok(i==SW_SHOWMAXIMIZED, "GetShowCmd returned %d'\n", i);
312
313 /* Test Getting / Setting the icon */
314 i=0xdeadbeef;
315 strcpy(buffer,"garbage");
316 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
317 ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
318 ok(*buffer=='\0', "GetIconLocation returned '%s'\n", buffer);
319 ok(i==0, "GetIconLocation returned %d\n", i);
320
321 str="c:\\nonexistent\\file";
322 r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
323 ok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
324
325 i=0xdeadbeef;
326 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
327 ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
328 ok(lstrcmpiA(buffer,str)==0, "GetIconLocation returned '%s'\n", buffer);
329 ok(i==0xbabecafe, "GetIconLocation returned %d'\n", i);
330
331 /* Test Getting / Setting the hot key */
332 w=0xbeef;
333 r = IShellLinkA_GetHotkey(sl, &w);
334 ok(r == S_OK, "GetHotkey failed (0x%08x)\n", r);
335 ok(w==0, "GetHotkey returned %d\n", w);
336
337 r = IShellLinkA_SetHotkey(sl, 0x5678);
338 ok(r == S_OK, "SetHotkey failed (0x%08x)\n", r);
339
340 w=0xbeef;
341 r = IShellLinkA_GetHotkey(sl, &w);
342 ok(r == S_OK, "GetHotkey failed (0x%08x)\n", r);
343 ok(w==0x5678, "GetHotkey returned %d'\n", w);
344
345 IShellLinkA_Release(sl);
346 }
347
348
349 /*
350 * Test saving and loading .lnk files
351 */
352
353 #define lok ok_(__FILE__, line)
354 #define check_lnk(a,b,c) check_lnk_(__LINE__, (a), (b), (c))
355
356 void create_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int save_fails)
357 {
358 HRESULT r;
359 IShellLinkA *sl;
360 IPersistFile *pf;
361
362 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
363 &IID_IShellLinkA, (LPVOID*)&sl);
364 lok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
365 if (r != S_OK)
366 return;
367
368 if (desc->description)
369 {
370 r = IShellLinkA_SetDescription(sl, desc->description);
371 lok(r == S_OK, "SetDescription failed (0x%08x)\n", r);
372 }
373 if (desc->workdir)
374 {
375 r = IShellLinkA_SetWorkingDirectory(sl, desc->workdir);
376 lok(r == S_OK, "SetWorkingDirectory failed (0x%08x)\n", r);
377 }
378 if (desc->path)
379 {
380 r = IShellLinkA_SetPath(sl, desc->path);
381 lok(SUCCEEDED(r), "SetPath failed (0x%08x)\n", r);
382 }
383 if (desc->pidl)
384 {
385 r = IShellLinkA_SetIDList(sl, desc->pidl);
386 lok(r == S_OK, "SetIDList failed (0x%08x)\n", r);
387 }
388 if (desc->arguments)
389 {
390 r = IShellLinkA_SetArguments(sl, desc->arguments);
391 lok(r == S_OK, "SetArguments failed (0x%08x)\n", r);
392 }
393 if (desc->showcmd)
394 {
395 r = IShellLinkA_SetShowCmd(sl, desc->showcmd);
396 lok(r == S_OK, "SetShowCmd failed (0x%08x)\n", r);
397 }
398 if (desc->icon)
399 {
400 r = IShellLinkA_SetIconLocation(sl, desc->icon, desc->icon_id);
401 lok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
402 }
403 if (desc->hotkey)
404 {
405 r = IShellLinkA_SetHotkey(sl, desc->hotkey);
406 lok(r == S_OK, "SetHotkey failed (0x%08x)\n", r);
407 }
408
409 r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (void**)&pf);
410 lok(r == S_OK, "no IID_IPersistFile (0x%08x)\n", r);
411 if (r == S_OK)
412 {
413 LPOLESTR str;
414
415 if (0)
416 {
417 /* crashes on XP */
418 IPersistFile_GetCurFile(pf, NULL);
419 }
420
421 /* test GetCurFile before ::Save */
422 str = (LPWSTR)0xdeadbeef;
423 r = IPersistFile_GetCurFile(pf, &str);
424 lok(r == S_FALSE ||
425 broken(r == S_OK), /* shell32 < 5.0 */
426 "got 0x%08x\n", r);
427 lok(str == NULL, "got %p\n", str);
428
429 r = IPersistFile_Save(pf, path, TRUE);
430 todo_wine_if (save_fails)
431 lok(r == S_OK, "save failed (0x%08x)\n", r);
432
433 /* test GetCurFile after ::Save */
434 r = IPersistFile_GetCurFile(pf, &str);
435 lok(r == S_OK, "got 0x%08x\n", r);
436 lok(str != NULL ||
437 broken(str == NULL), /* shell32 < 5.0 */
438 "Didn't expect NULL\n");
439 if (str != NULL)
440 {
441 IMalloc *pmalloc;
442
443 lok(!winetest_strcmpW(path, str), "Expected %s, got %s\n",
444 wine_dbgstr_w(path), wine_dbgstr_w(str));
445
446 SHGetMalloc(&pmalloc);
447 IMalloc_Free(pmalloc, str);
448 }
449 else
450 win_skip("GetCurFile fails on shell32 < 5.0\n");
451
452 IPersistFile_Release(pf);
453 }
454
455 IShellLinkA_Release(sl);
456 }
457
458 static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int todo)
459 {
460 HRESULT r;
461 IShellLinkA *sl;
462 IPersistFile *pf;
463 char buffer[INFOTIPSIZE];
464 LPOLESTR str;
465
466 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
467 &IID_IShellLinkA, (LPVOID*)&sl);
468 lok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
469 if (r != S_OK)
470 return;
471
472 r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
473 lok(r == S_OK, "no IID_IPersistFile (0x%08x)\n", r);
474 if (r != S_OK)
475 {
476 IShellLinkA_Release(sl);
477 return;
478 }
479
480 /* test GetCurFile before ::Load */
481 str = (LPWSTR)0xdeadbeef;
482 r = IPersistFile_GetCurFile(pf, &str);
483 lok(r == S_FALSE ||
484 broken(r == S_OK), /* shell32 < 5.0 */
485 "got 0x%08x\n", r);
486 lok(str == NULL, "got %p\n", str);
487
488 r = IPersistFile_Load(pf, path, STGM_READ);
489 lok(r == S_OK, "load failed (0x%08x)\n", r);
490
491 /* test GetCurFile after ::Save */
492 r = IPersistFile_GetCurFile(pf, &str);
493 lok(r == S_OK, "got 0x%08x\n", r);
494 lok(str != NULL ||
495 broken(str == NULL), /* shell32 < 5.0 */
496 "Didn't expect NULL\n");
497 if (str != NULL)
498 {
499 IMalloc *pmalloc;
500
501 lok(!winetest_strcmpW(path, str), "Expected %s, got %s\n",
502 wine_dbgstr_w(path), wine_dbgstr_w(str));
503
504 SHGetMalloc(&pmalloc);
505 IMalloc_Free(pmalloc, str);
506 }
507 else
508 win_skip("GetCurFile fails on shell32 < 5.0\n");
509
510 IPersistFile_Release(pf);
511 if (r != S_OK)
512 {
513 IShellLinkA_Release(sl);
514 return;
515 }
516
517 if (desc->description)
518 {
519 strcpy(buffer,"garbage");
520 r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
521 lok(r == S_OK, "GetDescription failed (0x%08x)\n", r);
522 todo_wine_if ((todo & 0x1) != 0)
523 lok(strcmp(buffer, desc->description)==0, "GetDescription returned '%s' instead of '%s'\n",
524 buffer, desc->description);
525 }
526 if (desc->workdir)
527 {
528 strcpy(buffer,"garbage");
529 r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
530 lok(r == S_OK, "GetWorkingDirectory failed (0x%08x)\n", r);
531 todo_wine_if ((todo & 0x2) != 0)
532 lok(lstrcmpiA(buffer, desc->workdir)==0, "GetWorkingDirectory returned '%s' instead of '%s'\n",
533 buffer, desc->workdir);
534 }
535 if (desc->path)
536 {
537 strcpy(buffer,"garbage");
538 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
539 lok(SUCCEEDED(r), "GetPath failed (0x%08x)\n", r);
540 todo_wine_if ((todo & 0x4) != 0)
541 lok(lstrcmpiA(buffer, desc->path)==0, "GetPath returned '%s' instead of '%s'\n",
542 buffer, desc->path);
543 }
544 if (desc->pidl)
545 {
546 LPITEMIDLIST pidl=NULL;
547 r = IShellLinkA_GetIDList(sl, &pidl);
548 lok(r == S_OK, "GetIDList failed (0x%08x)\n", r);
549 todo_wine_if ((todo & 0x8) != 0)
550 lok(pILIsEqual(pidl, desc->pidl), "GetIDList returned an incorrect pidl\n");
551 }
552 if (desc->showcmd)
553 {
554 int i=0xdeadbeef;
555 r = IShellLinkA_GetShowCmd(sl, &i);
556 lok(r == S_OK, "GetShowCmd failed (0x%08x)\n", r);
557 todo_wine_if ((todo & 0x10) != 0)
558 lok(i==desc->showcmd, "GetShowCmd returned 0x%0x instead of 0x%0x\n",
559 i, desc->showcmd);
560 }
561 if (desc->icon)
562 {
563 int i=0xdeadbeef;
564 strcpy(buffer,"garbage");
565 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
566 lok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
567 todo_wine_if ((todo & 0x20) != 0) {
568 lok(lstrcmpiA(buffer, desc->icon)==0, "GetIconLocation returned '%s' instead of '%s'\n",
569 buffer, desc->icon);
570 lok(i==desc->icon_id, "GetIconLocation returned 0x%0x instead of 0x%0x\n",
571 i, desc->icon_id);
572 }
573 }
574 if (desc->hotkey)
575 {
576 WORD i=0xbeef;
577 r = IShellLinkA_GetHotkey(sl, &i);
578 lok(r == S_OK, "GetHotkey failed (0x%08x)\n", r);
579 todo_wine_if ((todo & 0x40) != 0)
580 lok(i==desc->hotkey, "GetHotkey returned 0x%04x instead of 0x%04x\n",
581 i, desc->hotkey);
582 }
583
584 IShellLinkA_Release(sl);
585 }
586
587 static void test_load_save(void)
588 {
589 WCHAR lnkfile[MAX_PATH];
590 char lnkfileA[MAX_PATH];
591 static const char lnkfileA_name[] = "\\test.lnk";
592
593 lnk_desc_t desc;
594 char mypath[MAX_PATH];
595 char mydir[MAX_PATH];
596 char realpath[MAX_PATH];
597 char* p;
598 HANDLE hf;
599 DWORD r;
600
601 if (!pGetLongPathNameA)
602 {
603 win_skip("GetLongPathNameA is not available\n");
604 return;
605 }
606
607 /* Don't used a fixed path for the test.lnk file */
608 GetTempPathA(MAX_PATH, lnkfileA);
609 lstrcatA(lnkfileA, lnkfileA_name);
610 MultiByteToWideChar(CP_ACP, 0, lnkfileA, -1, lnkfile, MAX_PATH);
611
612 /* Save an empty .lnk file */
613 memset(&desc, 0, sizeof(desc));
614 create_lnk(lnkfile, &desc, 0);
615
616 /* It should come back as a bunch of empty strings */
617 desc.description="";
618 desc.workdir="";
619 desc.path="";
620 desc.arguments="";
621 desc.icon="";
622 check_lnk(lnkfile, &desc, 0x0);
623
624 /* Point a .lnk file to nonexistent files */
625 desc.description="";
626 desc.workdir="c:\\Nonexitent\\work\\directory";
627 desc.path="c:\\nonexistent\\path";
628 desc.pidl=NULL;
629 desc.arguments="";
630 desc.showcmd=0;
631 desc.icon="c:\\nonexistent\\icon\\file";
632 desc.icon_id=1234;
633 desc.hotkey=0;
634 create_lnk(lnkfile, &desc, 0);
635 check_lnk(lnkfile, &desc, 0x0);
636
637 r=GetModuleFileNameA(NULL, mypath, sizeof(mypath));
638 ok(r<sizeof(mypath), "GetModuleFileName failed (%d)\n", r);
639 strcpy(mydir, mypath);
640 p=strrchr(mydir, '\\');
641 if (p)
642 *p='\0';
643
644 /* IShellLink returns path in long form */
645 if (!pGetLongPathNameA(mypath, realpath, MAX_PATH)) strcpy( realpath, mypath );
646
647 /* Overwrite the existing lnk file and point it to existing files */
648 desc.description="test 2";
649 desc.workdir=mydir;
650 desc.path=realpath;
651 desc.pidl=NULL;
652 desc.arguments="/option1 /option2 \"Some string\"";
653 desc.showcmd=SW_SHOWNORMAL;
654 desc.icon=mypath;
655 desc.icon_id=0;
656 desc.hotkey=0x1234;
657 create_lnk(lnkfile, &desc, 0);
658 check_lnk(lnkfile, &desc, 0x0);
659
660 /* Test omitting .exe from an absolute path */
661 p=strrchr(realpath, '.');
662 if (p)
663 *p='\0';
664
665 desc.description="absolute path without .exe";
666 desc.workdir=mydir;
667 desc.path=realpath;
668 desc.pidl=NULL;
669 desc.arguments="/option1 /option2 \"Some string\"";
670 desc.showcmd=SW_SHOWNORMAL;
671 desc.icon=mypath;
672 desc.icon_id=0;
673 desc.hotkey=0x1234;
674 create_lnk(lnkfile, &desc, 0);
675 strcat(realpath, ".exe");
676 check_lnk(lnkfile, &desc, 0x4);
677
678 /* Overwrite the existing lnk file and test link to a command on the path */
679 desc.description="command on path";
680 desc.workdir=mypath;
681 desc.path="rundll32.exe";
682 desc.pidl=NULL;
683 desc.arguments="/option1 /option2 \"Some string\"";
684 desc.showcmd=SW_SHOWNORMAL;
685 desc.icon=mypath;
686 desc.icon_id=0;
687 desc.hotkey=0x1234;
688 create_lnk(lnkfile, &desc, 0);
689 /* Check that link is created to proper location */
690 SearchPathA( NULL, desc.path, NULL, MAX_PATH, realpath, NULL);
691 desc.path=realpath;
692 check_lnk(lnkfile, &desc, 0x0);
693
694 /* Test omitting .exe from a command on the path */
695 desc.description="command on path without .exe";
696 desc.workdir=mypath;
697 desc.path="rundll32";
698 desc.pidl=NULL;
699 desc.arguments="/option1 /option2 \"Some string\"";
700 desc.showcmd=SW_SHOWNORMAL;
701 desc.icon=mypath;
702 desc.icon_id=0;
703 desc.hotkey=0x1234;
704 create_lnk(lnkfile, &desc, 0);
705 /* Check that link is created to proper location */
706 SearchPathA( NULL, "rundll32", NULL, MAX_PATH, realpath, NULL);
707 desc.path=realpath;
708 check_lnk(lnkfile, &desc, 0x4);
709
710 /* Create a temporary non-executable file */
711 r=GetTempPathA(sizeof(mypath), mypath);
712 ok(r<sizeof(mypath), "GetTempPath failed (%d), err %d\n", r, GetLastError());
713 r=pGetLongPathNameA(mypath, mydir, sizeof(mydir));
714 ok(r<sizeof(mydir), "GetLongPathName failed (%d), err %d\n", r, GetLastError());
715 p=strrchr(mydir, '\\');
716 if (p)
717 *p='\0';
718
719 strcpy(mypath, mydir);
720 strcat(mypath, "\\test.txt");
721 hf = CreateFileA(mypath, GENERIC_WRITE, 0, NULL,
722 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
723 CloseHandle(hf);
724
725 /* Overwrite the existing lnk file and test link to an existing non-executable file */
726 desc.description="non-executable file";
727 desc.workdir=mydir;
728 desc.path=mypath;
729 desc.pidl=NULL;
730 desc.arguments="";
731 desc.showcmd=SW_SHOWNORMAL;
732 desc.icon=mypath;
733 desc.icon_id=0;
734 desc.hotkey=0x1234;
735 create_lnk(lnkfile, &desc, 0);
736 check_lnk(lnkfile, &desc, 0x0);
737
738 r=pGetShortPathNameA(mydir, mypath, sizeof(mypath));
739 ok(r<sizeof(mypath), "GetShortPathName failed (%d), err %d\n", r, GetLastError());
740
741 strcpy(realpath, mypath);
742 strcat(realpath, "\\test.txt");
743 strcat(mypath, "\\\\test.txt");
744
745 /* Overwrite the existing lnk file and test link to a short path with double backslashes */
746 desc.description="non-executable file";
747 desc.workdir=mydir;
748 desc.path=mypath;
749 desc.pidl=NULL;
750 desc.arguments="";
751 desc.showcmd=SW_SHOWNORMAL;
752 desc.icon=mypath;
753 desc.icon_id=0;
754 desc.hotkey=0x1234;
755 create_lnk(lnkfile, &desc, 0);
756 desc.path=realpath;
757 check_lnk(lnkfile, &desc, 0x0);
758
759 r = DeleteFileA(mypath);
760 ok(r, "failed to delete file %s (%d)\n", mypath, GetLastError());
761
762 /* Create a temporary .bat file */
763 strcpy(mypath, mydir);
764 strcat(mypath, "\\test.bat");
765 hf = CreateFileA(mypath, GENERIC_WRITE, 0, NULL,
766 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
767 CloseHandle(hf);
768
769 strcpy(realpath, mypath);
770
771 p=strrchr(mypath, '.');
772 if (p)
773 *p='\0';
774
775 /* Try linking to the .bat file without the extension */
776 desc.description="batch file";
777 desc.workdir=mydir;
778 desc.path=mypath;
779 desc.pidl=NULL;
780 desc.arguments="";
781 desc.showcmd=SW_SHOWNORMAL;
782 desc.icon=mypath;
783 desc.icon_id=0;
784 desc.hotkey=0x1234;
785 create_lnk(lnkfile, &desc, 0);
786 desc.path = realpath;
787 check_lnk(lnkfile, &desc, 0x4);
788
789 r = DeleteFileA(realpath);
790 ok(r, "failed to delete file %s (%d)\n", realpath, GetLastError());
791
792 /* FIXME: Also test saving a .lnk pointing to a pidl that cannot be
793 * represented as a path.
794 */
795
796 /* DeleteFileW is not implemented on Win9x */
797 r=DeleteFileA(lnkfileA);
798 ok(r, "failed to delete link '%s' (%d)\n", lnkfileA, GetLastError());
799 }
800
801 static void test_datalink(void)
802 {
803 static const WCHAR lnk[] = {
804 ':',':','{','9','d','b','1','1','8','6','e','-','4','0','d','f','-','1',
805 '1','d','1','-','a','a','8','c','-','0','0','c','0','4','f','b','6','7',
806 '8','6','3','}',':','2','6',',','!','!','g','x','s','f','(','N','g',']',
807 'q','F','`','H','{','L','s','A','C','C','E','S','S','F','i','l','e','s',
808 '>','p','l','T',']','j','I','{','j','f','(','=','1','&','L','[','-','8',
809 '1','-',']',':',':',0 };
810 static const WCHAR comp[] = {
811 '2','6',',','!','!','g','x','s','f','(','N','g',']','q','F','`','H','{',
812 'L','s','A','C','C','E','S','S','F','i','l','e','s','>','p','l','T',']',
813 'j','I','{','j','f','(','=','1','&','L','[','-','8','1','-',']',0 };
814 IShellLinkDataList *dl = NULL;
815 IShellLinkW *sl = NULL;
816 HRESULT r;
817 DWORD flags = 0;
818 EXP_DARWIN_LINK *dar;
819
820 r = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
821 &IID_IShellLinkW, (LPVOID*)&sl );
822 ok( r == S_OK ||
823 broken(r == E_NOINTERFACE), /* Win9x */
824 "CoCreateInstance failed (0x%08x)\n", r);
825 if (!sl)
826 {
827 win_skip("no shelllink\n");
828 return;
829 }
830
831 r = IShellLinkW_QueryInterface( sl, &_IID_IShellLinkDataList, (LPVOID*) &dl );
832 ok( r == S_OK ||
833 broken(r == E_NOINTERFACE), /* NT4 */
834 "IShellLinkW_QueryInterface failed (0x%08x)\n", r);
835
836 if (!dl)
837 {
838 win_skip("no datalink interface\n");
839 IShellLinkW_Release( sl );
840 return;
841 }
842
843 flags = 0;
844 r = IShellLinkDataList_GetFlags( dl, &flags );
845 ok( r == S_OK, "GetFlags failed\n");
846 ok( flags == 0, "GetFlags returned wrong flags\n");
847
848 dar = (void*)-1;
849 r = IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
850 ok( r == E_FAIL, "CopyDataBlock failed\n");
851 ok( dar == NULL, "should be null\n");
852
853 if (!pGetLongPathNameA /* NT4 */)
854 skip("SetPath with NULL parameter crashes on NT4\n");
855 else
856 {
857 r = IShellLinkW_SetPath(sl, NULL);
858 ok(r == E_INVALIDARG, "SetPath returned wrong error (0x%08x)\n", r);
859 }
860
861 r = IShellLinkW_SetPath(sl, lnk);
862 ok(r == S_OK, "SetPath failed\n");
863
864 if (0)
865 {
866 /* the following crashes */
867 IShellLinkDataList_GetFlags( dl, NULL );
868 }
869
870 flags = 0;
871 r = IShellLinkDataList_GetFlags( dl, &flags );
872 ok( r == S_OK, "GetFlags failed\n");
873 /* SLDF_HAS_LOGO3ID is no longer supported on Vista+, filter it out */
874 ok( (flags & (~ SLDF_HAS_LOGO3ID)) == SLDF_HAS_DARWINID,
875 "GetFlags returned wrong flags\n");
876
877 dar = NULL;
878 r = IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
879 ok( r == S_OK, "CopyDataBlock failed\n");
880
881 ok( dar && ((DATABLOCK_HEADER*)dar)->dwSignature == EXP_DARWIN_ID_SIG, "signature wrong\n");
882 ok( dar && 0==lstrcmpW(dar->szwDarwinID, comp ), "signature wrong\n");
883
884 LocalFree( dar );
885
886 IShellLinkDataList_Release( dl );
887 IShellLinkW_Release( sl );
888 }
889
890 static void test_shdefextracticon(void)
891 {
892 HICON hiconlarge=NULL, hiconsmall=NULL;
893 HRESULT res;
894
895 if (!pSHDefExtractIconA)
896 {
897 win_skip("SHDefExtractIconA is unavailable\n");
898 return;
899 }
900
901 res = pSHDefExtractIconA("shell32.dll", 0, 0, &hiconlarge, &hiconsmall, MAKELONG(16,24));
902 ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%x\n", res);
903 ok(hiconlarge != NULL, "got null hiconlarge\n");
904 ok(hiconsmall != NULL, "got null hiconsmall\n");
905 DestroyIcon(hiconlarge);
906 DestroyIcon(hiconsmall);
907
908 hiconsmall = NULL;
909 res = pSHDefExtractIconA("shell32.dll", 0, 0, NULL, &hiconsmall, MAKELONG(16,24));
910 ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%x\n", res);
911 ok(hiconsmall != NULL, "got null hiconsmall\n");
912 DestroyIcon(hiconsmall);
913
914 res = pSHDefExtractIconA("shell32.dll", 0, 0, NULL, NULL, MAKELONG(16,24));
915 ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%x\n", res);
916 }
917
918 static void test_GetIconLocation(void)
919 {
920 IShellLinkA *sl;
921 const char *str;
922 char buffer[INFOTIPSIZE], mypath[MAX_PATH];
923 int i;
924 HRESULT r;
925 LPITEMIDLIST pidl;
926
927 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
928 &IID_IShellLinkA, (LPVOID*)&sl);
929 ok(r == S_OK, "no IID_IShellLinkA (0x%08x)\n", r);
930 if(r != S_OK)
931 return;
932
933 i = 0xdeadbeef;
934 strcpy(buffer, "garbage");
935 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
936 ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
937 ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
938 ok(i == 0, "GetIconLocation returned %d\n", i);
939
940 str = "c:\\some\\path";
941 r = IShellLinkA_SetPath(sl, str);
942 ok(r == S_FALSE || r == S_OK, "SetPath failed (0x%08x)\n", r);
943
944 i = 0xdeadbeef;
945 strcpy(buffer, "garbage");
946 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
947 ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
948 ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
949 ok(i == 0, "GetIconLocation returned %d\n", i);
950
951 GetWindowsDirectoryA(mypath, sizeof(mypath) - 12);
952 strcat(mypath, "\\regedit.exe");
953 pidl = path_to_pidl(mypath);
954 r = IShellLinkA_SetIDList(sl, pidl);
955 ok(r == S_OK, "SetPath failed (0x%08x)\n", r);
956 pILFree(pidl);
957
958 i = 0xdeadbeef;
959 strcpy(buffer, "garbage");
960 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
961 ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
962 ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
963 ok(i == 0, "GetIconLocation returned %d\n", i);
964
965 str = "c:\\nonexistent\\file";
966 r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
967 ok(r == S_OK, "SetIconLocation failed (0x%08x)\n", r);
968
969 i = 0xdeadbeef;
970 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
971 ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r);
972 ok(lstrcmpiA(buffer,str) == 0, "GetIconLocation returned '%s'\n", buffer);
973 ok(i == 0xbabecafe, "GetIconLocation returned %d'\n", i);
974
975 IShellLinkA_Release(sl);
976 }
977
978 static void test_SHGetStockIconInfo(void)
979 {
980 BYTE buffer[sizeof(SHSTOCKICONINFO) + 16];
981 SHSTOCKICONINFO *sii = (SHSTOCKICONINFO *) buffer;
982 HRESULT hr;
983 INT i;
984
985 /* not present before vista */
986 if (!pSHGetStockIconInfo)
987 {
988 win_skip("SHGetStockIconInfo not available\n");
989 return;
990 }
991
992 /* negative values are handled */
993 memset(buffer, '#', sizeof(buffer));
994 sii->cbSize = sizeof(SHSTOCKICONINFO);
995 hr = pSHGetStockIconInfo(SIID_INVALID, SHGSI_ICONLOCATION, sii);
996 ok(hr == E_INVALIDARG, "-1: got 0x%x (expected E_INVALIDARG)\n", hr);
997
998 /* max. id for vista is 140 (no definition exists for this value) */
999 for (i = SIID_DOCNOASSOC; i <= SIID_CLUSTEREDDRIVE; i++)
1000 {
1001 memset(buffer, '#', sizeof(buffer));
1002 sii->cbSize = sizeof(SHSTOCKICONINFO);
1003 hr = pSHGetStockIconInfo(i, SHGSI_ICONLOCATION, sii);
1004
1005 ok(hr == S_OK,
1006 "%3d: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x (expected S_OK)\n",
1007 i, hr, sii->iSysImageIndex, sii->iIcon);
1008
1009 if ((hr == S_OK) && (winetest_debug > 1))
1010 trace("%3d: got iSysImageIndex %3d, iIcon %3d and %s\n", i, sii->iSysImageIndex,
1011 sii->iIcon, wine_dbgstr_w(sii->szPath));
1012 }
1013
1014 /* test invalid icons indices that are invalid for all platforms */
1015 for (i = SIID_MAX_ICONS; i < (SIID_MAX_ICONS + 25) ; i++)
1016 {
1017 memset(buffer, '#', sizeof(buffer));
1018 sii->cbSize = sizeof(SHSTOCKICONINFO);
1019 hr = pSHGetStockIconInfo(i, SHGSI_ICONLOCATION, sii);
1020 ok(hr == E_INVALIDARG, "%3d: got 0x%x (expected E_INVALIDARG)\n", i, hr);
1021 todo_wine {
1022 ok(sii->iSysImageIndex == -1, "%d: got iSysImageIndex %d\n", i, sii->iSysImageIndex);
1023 ok(sii->iIcon == -1, "%d: got iIcon %d\n", i, sii->iIcon);
1024 }
1025 }
1026
1027 /* test more returned SHSTOCKICONINFO elements without extra flags */
1028 memset(buffer, '#', sizeof(buffer));
1029 sii->cbSize = sizeof(SHSTOCKICONINFO);
1030 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1031 ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr);
1032 ok(!sii->hIcon, "got %p (expected NULL)\n", sii->hIcon);
1033 ok(sii->iSysImageIndex == -1, "got %d (expected -1)\n", sii->iSysImageIndex);
1034
1035 /* the exact size is required of the struct */
1036 memset(buffer, '#', sizeof(buffer));
1037 sii->cbSize = sizeof(SHSTOCKICONINFO) + 2;
1038 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1039 ok(hr == E_INVALIDARG, "+2: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1040
1041 memset(buffer, '#', sizeof(buffer));
1042 sii->cbSize = sizeof(SHSTOCKICONINFO) + 1;
1043 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1044 ok(hr == E_INVALIDARG, "+1: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1045
1046 memset(buffer, '#', sizeof(buffer));
1047 sii->cbSize = sizeof(SHSTOCKICONINFO) - 1;
1048 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1049 ok(hr == E_INVALIDARG, "-1: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1050
1051 memset(buffer, '#', sizeof(buffer));
1052 sii->cbSize = sizeof(SHSTOCKICONINFO) - 2;
1053 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1054 ok(hr == E_INVALIDARG, "-2: got 0x%x, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1055
1056 /* there is a NULL check for the struct */
1057 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, NULL);
1058 ok(hr == E_INVALIDARG, "NULL: got 0x%x\n", hr);
1059 }
1060
1061 static void test_SHExtractIcons(void)
1062 {
1063 static const WCHAR notepadW[] = {'n','o','t','e','p','a','d','.','e','x','e',0};
1064 static const WCHAR shell32W[] = {'s','h','e','l','l','3','2','.','d','l','l',0};
1065 static const WCHAR emptyW[] = {0};
1066 UINT ret, ret2;
1067 HICON icons[256];
1068 UINT ids[256], i;
1069
1070 if (!pSHExtractIconsW)
1071 {
1072 win_skip("SHExtractIconsW not available\n");
1073 return;
1074 }
1075
1076 ret = pSHExtractIconsW(emptyW, 0, 16, 16, icons, ids, 1, 0);
1077 ok(ret == ~0u, "got %u\n", ret);
1078
1079 ret = pSHExtractIconsW(notepadW, 0, 16, 16, NULL, NULL, 1, 0);
1080 ok(ret == 1 || broken(ret == 2) /* win2k */, "got %u\n", ret);
1081
1082 icons[0] = (HICON)0xdeadbeef;
1083 ret = pSHExtractIconsW(notepadW, 0, 16, 16, icons, NULL, 1, 0);
1084 ok(ret == 1, "got %u\n", ret);
1085 ok(icons[0] != (HICON)0xdeadbeef, "icon not set\n");
1086 DestroyIcon(icons[0]);
1087
1088 icons[0] = (HICON)0xdeadbeef;
1089 ids[0] = 0xdeadbeef;
1090 ret = pSHExtractIconsW(notepadW, 0, 16, 16, icons, ids, 1, 0);
1091 ok(ret == 1, "got %u\n", ret);
1092 ok(icons[0] != (HICON)0xdeadbeef, "icon not set\n");
1093 ok(ids[0] != 0xdeadbeef, "id not set\n");
1094 DestroyIcon(icons[0]);
1095
1096 ret = pSHExtractIconsW(shell32W, 0, 16, 16, NULL, NULL, 0, 0);
1097 ret2 = pSHExtractIconsW(shell32W, 4, MAKELONG(32,16), MAKELONG(32,16), NULL, NULL, 256, 0);
1098 ok(ret && ret == ret2,
1099 "icon count should be independent of requested icon sizes and base icon index\n");
1100
1101 ret = pSHExtractIconsW(shell32W, 0, 16, 16, icons, ids, 0, 0);
1102 ok(ret == ~0u || !ret /* < vista */, "got %u\n", ret);
1103
1104 ret = pSHExtractIconsW(shell32W, 0, 16, 16, icons, ids, 3, 0);
1105 ok(ret == 3, "got %u\n", ret);
1106 for (i = 0; i < ret; i++) DestroyIcon(icons[i]);
1107
1108 /* count must be a multiple of two when getting two sizes */
1109 ret = pSHExtractIconsW(shell32W, 0, MAKELONG(16,32), MAKELONG(16,32), icons, ids, 3, 0);
1110 ok(!ret /* vista */ || ret == 4, "got %u\n", ret);
1111 for (i = 0; i < ret; i++) DestroyIcon(icons[i]);
1112
1113 ret = pSHExtractIconsW(shell32W, 0, MAKELONG(16,32), MAKELONG(16,32), icons, ids, 4, 0);
1114 ok(ret == 4, "got %u\n", ret);
1115 for (i = 0; i < ret; i++) DestroyIcon(icons[i]);
1116 }
1117
1118 static void test_propertystore(void)
1119 {
1120 IShellLinkA *linkA;
1121 IShellLinkW *linkW;
1122 IPropertyStore *ps;
1123 HRESULT hr;
1124
1125 hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1126 &IID_IShellLinkA, (void**)&linkA);
1127 ok(hr == S_OK, "got 0x%08x\n", hr);
1128
1129 hr = IShellLinkA_QueryInterface(linkA, &IID_IShellLinkW, (void**)&linkW);
1130 ok(hr == S_OK, "got 0x%08x\n", hr);
1131
1132 hr = IShellLinkA_QueryInterface(linkA, &IID_IPropertyStore, (void**)&ps);
1133 if (hr == S_OK) {
1134 IPropertyStoreCache *pscache;
1135
1136 IPropertyStore_Release(ps);
1137
1138 hr = IShellLinkW_QueryInterface(linkW, &IID_IPropertyStore, (void**)&ps);
1139 ok(hr == S_OK, "got 0x%08x\n", hr);
1140
1141 hr = IPropertyStore_QueryInterface(ps, &IID_IPropertyStoreCache, (void**)&pscache);
1142 ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1143
1144 IPropertyStore_Release(ps);
1145 }
1146 else
1147 win_skip("IShellLink doesn't support IPropertyStore.\n");
1148
1149 IShellLinkA_Release(linkA);
1150 IShellLinkW_Release(linkW);
1151 }
1152
1153 START_TEST(shelllink)
1154 {
1155 HRESULT r;
1156 HMODULE hmod = GetModuleHandleA("shell32.dll");
1157 HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
1158
1159 pILFree = (void *)GetProcAddress(hmod, (LPSTR)155);
1160 pILIsEqual = (void *)GetProcAddress(hmod, (LPSTR)21);
1161 pSHILCreateFromPath = (void *)GetProcAddress(hmod, (LPSTR)28);
1162 pSHDefExtractIconA = (void *)GetProcAddress(hmod, "SHDefExtractIconA");
1163 pSHGetStockIconInfo = (void *)GetProcAddress(hmod, "SHGetStockIconInfo");
1164 pGetLongPathNameA = (void *)GetProcAddress(hkernel32, "GetLongPathNameA");
1165 pGetShortPathNameA = (void *)GetProcAddress(hkernel32, "GetShortPathNameA");
1166 pSHExtractIconsW = (void *)GetProcAddress(hmod, "SHExtractIconsW");
1167
1168 r = CoInitialize(NULL);
1169 ok(r == S_OK, "CoInitialize failed (0x%08x)\n", r);
1170 if (r != S_OK)
1171 return;
1172
1173 test_get_set();
1174 test_load_save();
1175 test_datalink();
1176 test_shdefextracticon();
1177 test_GetIconLocation();
1178 test_SHGetStockIconInfo();
1179 test_SHExtractIcons();
1180 test_propertystore();
1181
1182 CoUninitialize();
1183 }