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