dinput: Clear DIA_APPNOMAP BuildActionMap flag with specific device semantic.
[wine.git] / dlls / shell32 / tests / shelllink.c
blobf13155ade2f5f97cde952c127eb592eac2309774
1 /*
2 * Unit tests for shelllinks
4 * Copyright 2004 Mike McCormack
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define COBJMACROS
24 #include "initguid.h"
25 #include "windows.h"
26 #include "shlguid.h"
27 #include "shobjidl.h"
28 #include "shlobj.h"
29 #include "shlwapi.h"
30 #include "shellapi.h"
31 #include "commoncontrols.h"
33 #include "wine/test.h"
35 #include "shell32_test.h"
37 static HRESULT (WINAPI *pSHILCreateFromPath)(LPCWSTR, LPITEMIDLIST *,DWORD*);
38 static HRESULT (WINAPI *pSHGetFolderLocation)(HWND,INT,HANDLE,DWORD,PIDLIST_ABSOLUTE*);
39 static HRESULT (WINAPI *pSHGetStockIconInfo)(SHSTOCKICONID, UINT, SHSTOCKICONINFO *);
40 static UINT (WINAPI *pSHExtractIconsW)(LPCWSTR, int, int, int, HICON *, UINT *, UINT, UINT);
41 static BOOL (WINAPI *pIsProcessDPIAware)(void);
43 /* For some reason SHILCreateFromPath does not work on Win98 and
44 * SHSimpleIDListFromPathA does not work on NT4. But if we call both we
45 * get what we want on all platforms.
47 static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathAW)(LPCVOID);
49 static LPITEMIDLIST path_to_pidl(const char* path)
51 LPITEMIDLIST pidl;
53 if (!pSHSimpleIDListFromPathAW)
55 HMODULE hdll=GetModuleHandleA("shell32.dll");
56 pSHSimpleIDListFromPathAW=(void*)GetProcAddress(hdll, (char*)162);
57 if (!pSHSimpleIDListFromPathAW)
58 win_skip("SHSimpleIDListFromPathAW not found in shell32.dll\n");
61 pidl=NULL;
62 /* pSHSimpleIDListFromPathAW maps to A on non NT platforms */
63 if (pSHSimpleIDListFromPathAW && (GetVersion() & 0x80000000))
64 pidl=pSHSimpleIDListFromPathAW(path);
66 if (!pidl)
68 WCHAR* pathW;
69 HRESULT r;
70 int len;
72 len=MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
73 pathW = malloc(len * sizeof(WCHAR));
74 MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, len);
76 r=pSHILCreateFromPath(pathW, &pidl, NULL);
77 ok(r == S_OK, "SHILCreateFromPath failed (0x%08lx)\n", r);
78 free(pathW);
80 return pidl;
85 * Test manipulation of an IShellLink's properties.
88 static void test_get_set(void)
90 HRESULT r;
91 IShellLinkA *sl;
92 IShellLinkW *slW = NULL;
93 char mypath[MAX_PATH];
94 char buffer[INFOTIPSIZE];
95 WIN32_FIND_DATAA finddata;
96 LPITEMIDLIST pidl, tmp_pidl;
97 const char * str;
98 int i;
99 WORD w;
101 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
102 &IID_IShellLinkA, (LPVOID*)&sl);
103 ok(r == S_OK, "no IID_IShellLinkA (0x%08lx)\n", r);
104 if (r != S_OK)
105 return;
107 /* Test Getting / Setting the description */
108 strcpy(buffer,"garbage");
109 r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
110 ok(r == S_OK, "GetDescription failed (0x%08lx)\n", r);
111 ok(*buffer=='\0', "GetDescription returned '%s'\n", buffer);
113 str="Some description";
114 r = IShellLinkA_SetDescription(sl, str);
115 ok(r == S_OK, "SetDescription failed (0x%08lx)\n", r);
117 strcpy(buffer,"garbage");
118 r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
119 ok(r == S_OK, "GetDescription failed (0x%08lx)\n", r);
120 ok(strcmp(buffer,str)==0, "GetDescription returned '%s'\n", buffer);
122 r = IShellLinkA_SetDescription(sl, NULL);
123 ok(r == S_OK, "SetDescription failed (0x%08lx)\n", r);
125 strcpy(buffer,"garbage");
126 r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
127 ok(r == S_OK, "GetDescription failed (0x%08lx)\n", r);
128 ok(!*buffer, "GetDescription returned '%s'\n", buffer);
130 /* Test Getting / Setting the work directory */
131 strcpy(buffer,"garbage");
132 r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
133 ok(r == S_OK, "GetWorkingDirectory failed (0x%08lx)\n", r);
134 ok(*buffer=='\0', "GetWorkingDirectory returned '%s'\n", buffer);
136 str="c:\\nonexistent\\directory";
137 r = IShellLinkA_SetWorkingDirectory(sl, str);
138 ok(r == S_OK, "SetWorkingDirectory failed (0x%08lx)\n", r);
140 strcpy(buffer,"garbage");
141 r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
142 ok(r == S_OK, "GetWorkingDirectory failed (0x%08lx)\n", r);
143 ok(lstrcmpiA(buffer,str)==0, "GetWorkingDirectory returned '%s'\n", buffer);
145 /* Test Getting / Setting the path */
146 strcpy(buffer,"garbage");
147 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
148 ok(r == S_FALSE, "GetPath failed (0x%08lx)\n", r);
149 ok(!*buffer, "GetPath returned '%s'\n", buffer);
151 strcpy(buffer,"garbage");
152 memset(&finddata, 0xaa, sizeof(finddata));
153 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), &finddata, SLGP_RAWPATH);
154 ok(r == S_FALSE, "GetPath failed (0x%08lx)\n", r);
155 ok(!*buffer, "GetPath returned '%s'\n", buffer);
156 ok(finddata.dwFileAttributes == 0, "unexpected attributes %lx\n", finddata.dwFileAttributes);
157 ok(finddata.cFileName[0] == 0, "unexpected filename '%s'\n", finddata.cFileName);
159 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (void **)&slW);
160 ok(r == S_OK, "CoCreateInstance failed (0x%08lx)\n", r);
161 IShellLinkW_Release(slW);
163 r = IShellLinkA_SetPath(sl, NULL);
164 ok(r == E_INVALIDARG, "Unexpected hr %#lx.\n", r);
166 r = IShellLinkA_SetPath(sl, "");
167 ok(r==S_OK, "SetPath failed (0x%08lx)\n", r);
169 strcpy(buffer,"garbage");
170 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
171 ok(r == S_FALSE, "GetPath failed (0x%08lx)\n", r);
172 ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
174 /* Win98 returns S_FALSE, but WinXP returns S_OK */
175 str="c:\\nonexistent\\file";
176 r = IShellLinkA_SetPath(sl, str);
177 ok(r==S_FALSE || r==S_OK, "SetPath failed (0x%08lx)\n", r);
179 strcpy(buffer,"garbage");
180 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
181 ok(r == S_OK, "GetPath failed (0x%08lx)\n", r);
182 ok(lstrcmpiA(buffer,str)==0, "GetPath returned '%s'\n", buffer);
184 strcpy(buffer,"garbage");
185 memset(&finddata, 0xaa, sizeof(finddata));
186 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), &finddata, SLGP_RAWPATH);
187 ok(r == S_OK, "GetPath failed (0x%08lx)\n", r);
188 ok(lstrcmpiA(buffer,str)==0, "GetPath returned '%s'\n", buffer);
189 ok(finddata.dwFileAttributes == 0, "unexpected attributes %lx\n", finddata.dwFileAttributes);
190 ok(lstrcmpiA(finddata.cFileName, "file") == 0, "unexpected filename '%s'\n", finddata.cFileName);
192 /* Get some real path to play with */
193 GetWindowsDirectoryA( mypath, sizeof(mypath)-12 );
194 strcat(mypath, "\\regedit.exe");
196 /* Test the interaction of SetPath and SetIDList */
197 tmp_pidl=NULL;
198 r = IShellLinkA_GetIDList(sl, &tmp_pidl);
199 ok(r == S_OK, "GetIDList failed (0x%08lx)\n", r);
200 if (r == S_OK)
202 BOOL ret;
204 strcpy(buffer,"garbage");
205 ret = SHGetPathFromIDListA(tmp_pidl, buffer);
206 ok(ret, "SHGetPathFromIDListA failed\n");
207 if (ret)
208 ok(lstrcmpiA(buffer,str)==0, "GetIDList returned '%s'\n", buffer);
209 ILFree(tmp_pidl);
212 pidl=path_to_pidl(mypath);
213 ok(pidl!=NULL, "path_to_pidl returned a NULL pidl\n");
215 if (pidl)
217 LPITEMIDLIST second_pidl;
219 r = IShellLinkA_SetIDList(sl, pidl);
220 ok(r == S_OK, "SetIDList failed (0x%08lx)\n", r);
222 tmp_pidl=NULL;
223 r = IShellLinkA_GetIDList(sl, &tmp_pidl);
224 ok(r == S_OK, "GetIDList failed (0x%08lx)\n", r);
225 ok(tmp_pidl && ILIsEqual(pidl, tmp_pidl),
226 "GetIDList returned an incorrect pidl\n");
228 r = IShellLinkA_GetIDList(sl, &second_pidl);
229 ok(r == S_OK, "GetIDList failed (0x%08lx)\n", r);
230 ok(second_pidl && ILIsEqual(pidl, second_pidl),
231 "GetIDList returned an incorrect pidl\n");
232 ok(second_pidl != tmp_pidl, "pidls are the same\n");
234 ILFree(second_pidl);
235 ILFree(tmp_pidl);
236 ILFree(pidl);
238 strcpy(buffer,"garbage");
239 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
240 ok(r == S_OK, "GetPath failed (0x%08lx)\n", r);
241 ok(lstrcmpiA(buffer, mypath)==0, "GetPath returned '%s'\n", buffer);
243 strcpy(buffer,"garbage");
244 memset(&finddata, 0xaa, sizeof(finddata));
245 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), &finddata, SLGP_RAWPATH);
246 ok(r == S_OK, "GetPath failed (0x%08lx)\n", r);
247 ok(lstrcmpiA(buffer, mypath)==0, "GetPath returned '%s'\n", buffer);
248 ok(finddata.dwFileAttributes != 0, "unexpected attributes %lx\n", finddata.dwFileAttributes);
249 ok(lstrcmpiA(finddata.cFileName, "regedit.exe") == 0, "unexpected filename '%s'\n", finddata.cFileName);
252 if (pSHGetFolderLocation)
254 LPITEMIDLIST pidl_controls;
256 r = pSHGetFolderLocation(NULL, CSIDL_CONTROLS, NULL, 0, &pidl_controls);
257 ok(r == S_OK, "SHGetFolderLocation failed (0x%08lx)\n", r);
259 r = IShellLinkA_SetIDList(sl, pidl_controls);
260 ok(r == S_OK, "SetIDList failed (0x%08lx)\n", r);
262 strcpy(buffer,"garbage");
263 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
264 ok(r == S_FALSE, "GetPath failed (0x%08lx)\n", r);
265 ok(buffer[0] == 0, "GetPath returned '%s'\n", buffer);
267 strcpy(buffer,"garbage");
268 memset(&finddata, 0xaa, sizeof(finddata));
269 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), &finddata, SLGP_RAWPATH);
270 ok(r == S_FALSE, "GetPath failed (0x%08lx)\n", r);
271 ok(buffer[0] == 0, "GetPath returned '%s'\n", buffer);
272 ok(finddata.dwFileAttributes == 0, "unexpected attributes %lx\n", finddata.dwFileAttributes);
273 ok(finddata.cFileName[0] == 0, "unexpected filename '%s'\n", finddata.cFileName);
275 ILFree(pidl_controls);
278 /* test path with quotes (IShellLinkA_SetPath returns S_FALSE on W2K and below and S_OK on XP and above */
279 r = IShellLinkA_SetPath(sl, "\"c:\\nonexistent\\file\"");
280 ok(r==S_FALSE || r == S_OK, "SetPath failed (0x%08lx)\n", r);
282 strcpy(buffer,"garbage");
283 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
284 ok(r==S_OK, "GetPath failed (0x%08lx)\n", r);
285 todo_wine ok(!strcmp(buffer, "C:\\nonexistent\\file"),
286 "case doesn't match\n");
288 r = IShellLinkA_SetPath(sl, "\"c:\\foo");
289 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08lx)\n", r);
291 r = IShellLinkA_SetPath(sl, "\"\"c:\\foo");
292 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08lx)\n", r);
294 r = IShellLinkA_SetPath(sl, "c:\\foo\"");
295 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08lx)\n", r);
297 r = IShellLinkA_SetPath(sl, "\"\"c:\\foo\"");
298 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08lx)\n", r);
300 r = IShellLinkA_SetPath(sl, "\"\"c:\\foo\"\"");
301 ok(r==S_FALSE || r == S_OK || r == E_INVALIDARG /* Vista */, "SetPath failed (0x%08lx)\n", r);
303 /* Test Getting / Setting the arguments */
304 strcpy(buffer,"garbage");
305 r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
306 ok(r == S_OK, "GetArguments failed (0x%08lx)\n", r);
307 ok(*buffer=='\0', "GetArguments returned '%s'\n", buffer);
309 str="param1 \"spaced param2\"";
310 r = IShellLinkA_SetArguments(sl, str);
311 ok(r == S_OK, "SetArguments failed (0x%08lx)\n", r);
313 strcpy(buffer,"garbage");
314 r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
315 ok(r == S_OK, "GetArguments failed (0x%08lx)\n", r);
316 ok(strcmp(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
318 strcpy(buffer,"garbage");
319 r = IShellLinkA_SetArguments(sl, NULL);
320 ok(r == S_OK, "SetArguments failed (0x%08lx)\n", r);
321 r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
322 ok(r == S_OK, "GetArguments failed (0x%08lx)\n", r);
323 ok(!buffer[0] || strcmp(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
325 strcpy(buffer,"garbage");
326 r = IShellLinkA_SetArguments(sl, "");
327 ok(r == S_OK, "SetArguments failed (0x%08lx)\n", r);
328 r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
329 ok(r == S_OK, "GetArguments failed (0x%08lx)\n", r);
330 ok(!buffer[0], "GetArguments returned '%s'\n", buffer);
332 /* Test Getting / Setting showcmd */
333 i=0xdeadbeef;
334 r = IShellLinkA_GetShowCmd(sl, &i);
335 ok(r == S_OK, "GetShowCmd failed (0x%08lx)\n", r);
336 ok(i==SW_SHOWNORMAL, "GetShowCmd returned %d\n", i);
338 r = IShellLinkA_SetShowCmd(sl, SW_SHOWMAXIMIZED);
339 ok(r == S_OK, "SetShowCmd failed (0x%08lx)\n", r);
341 i=0xdeadbeef;
342 r = IShellLinkA_GetShowCmd(sl, &i);
343 ok(r == S_OK, "GetShowCmd failed (0x%08lx)\n", r);
344 ok(i==SW_SHOWMAXIMIZED, "GetShowCmd returned %d'\n", i);
346 /* Test Getting / Setting the icon */
347 i=0xdeadbeef;
348 strcpy(buffer,"garbage");
349 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
350 ok(r == S_OK, "GetIconLocation failed (0x%08lx)\n", r);
351 ok(*buffer=='\0', "GetIconLocation returned '%s'\n", buffer);
352 ok(i==0, "GetIconLocation returned %d\n", i);
354 str="c:\\nonexistent\\file";
355 r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
356 ok(r == S_OK, "SetIconLocation failed (0x%08lx)\n", r);
358 i=0xdeadbeef;
359 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
360 ok(r == S_OK, "GetIconLocation failed (0x%08lx)\n", r);
361 ok(lstrcmpiA(buffer,str)==0, "GetIconLocation returned '%s'\n", buffer);
362 ok(i==0xbabecafe, "GetIconLocation returned %d'\n", i);
364 /* Test Getting / Setting the hot key */
365 w=0xbeef;
366 r = IShellLinkA_GetHotkey(sl, &w);
367 ok(r == S_OK, "GetHotkey failed (0x%08lx)\n", r);
368 ok(w==0, "GetHotkey returned %d\n", w);
370 r = IShellLinkA_SetHotkey(sl, 0x5678);
371 ok(r == S_OK, "SetHotkey failed (0x%08lx)\n", r);
373 w=0xbeef;
374 r = IShellLinkA_GetHotkey(sl, &w);
375 ok(r == S_OK, "GetHotkey failed (0x%08lx)\n", r);
376 ok(w==0x5678, "GetHotkey returned %d'\n", w);
378 IShellLinkA_Release(sl);
383 * Test saving and loading .lnk files
386 #define lok ok_(__FILE__, line)
387 #define check_lnk(a,b,c) check_lnk_(__LINE__, (a), (b), (c))
389 void create_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
391 HRESULT r, init_dirty;
392 IShellLinkA *sl;
393 IPersistFile *pf;
395 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
396 &IID_IShellLinkA, (LPVOID*)&sl);
397 lok(r == S_OK, "no IID_IShellLinkA (0x%08lx)\n", r);
398 if (r != S_OK)
399 return;
401 if (desc->description)
403 r = IShellLinkA_SetDescription(sl, desc->description);
404 lok(r == S_OK, "SetDescription failed (0x%08lx)\n", r);
406 if (desc->workdir)
408 r = IShellLinkA_SetWorkingDirectory(sl, desc->workdir);
409 lok(r == S_OK, "SetWorkingDirectory failed (0x%08lx)\n", r);
411 if (desc->path)
413 r = IShellLinkA_SetPath(sl, desc->path);
414 lok(SUCCEEDED(r), "SetPath failed (0x%08lx)\n", r);
416 if (desc->pidl)
418 r = IShellLinkA_SetIDList(sl, desc->pidl);
419 lok(r == S_OK, "SetIDList failed (0x%08lx)\n", r);
421 if (desc->arguments)
423 r = IShellLinkA_SetArguments(sl, desc->arguments);
424 lok(r == S_OK, "SetArguments failed (0x%08lx)\n", r);
426 if (desc->showcmd)
428 r = IShellLinkA_SetShowCmd(sl, desc->showcmd);
429 lok(r == S_OK, "SetShowCmd failed (0x%08lx)\n", r);
431 if (desc->icon)
433 r = IShellLinkA_SetIconLocation(sl, desc->icon, desc->icon_id);
434 lok(r == S_OK, "SetIconLocation failed (0x%08lx)\n", r);
436 if (desc->hotkey)
438 r = IShellLinkA_SetHotkey(sl, desc->hotkey);
439 lok(r == S_OK, "SetHotkey failed (0x%08lx)\n", r);
442 r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (void**)&pf);
443 lok(r == S_OK, "no IID_IPersistFile (0x%08lx)\n", r);
444 if (r == S_OK)
446 LPOLESTR str;
448 if (0)
450 /* crashes on XP */
451 IPersistFile_GetCurFile(pf, NULL);
454 init_dirty = IPersistFile_IsDirty(pf); /* empty links start off as clean */
455 r = IPersistFile_Save(pf, NULL, FALSE);
456 lok(r == S_OK || r == E_INVALIDARG /* before Windows 7 */,
457 "save(NULL, FALSE) failed (0x%08lx)\n", r);
459 r = IPersistFile_IsDirty(pf);
460 lok(r == init_dirty, "dirty(NULL, FALSE) (0x%08lx)\n", r);
462 r = IPersistFile_Save(pf, NULL, TRUE);
463 lok(r == S_OK || r == E_INVALIDARG /* before Windows 7 */,
464 "save(NULL, TRUE) failed (0x%08lx)\n", r);
466 r = IPersistFile_IsDirty(pf);
467 lok(r == init_dirty, "dirty(NULL, TRUE) (0x%08lx)\n", r);
469 /* test GetCurFile before ::Save */
470 str = (LPWSTR)0xdeadbeef;
471 r = IPersistFile_GetCurFile(pf, &str);
472 lok(r == S_FALSE, "GetCurFile:before got 0x%08lx\n", r);
473 lok(str == NULL, "GetCurFile:before got %p\n", str);
475 r = IPersistFile_Save(pf, path, TRUE);
476 lok(r == S_OK, "save(path, TRUE) failed (0x%08lx)\n", r);
477 r = IPersistFile_IsDirty(pf);
478 lok(r == S_FALSE, "dirty(path, TRUE) (0x%08lx)\n", r);
480 /* test GetCurFile after ::Save */
481 r = IPersistFile_GetCurFile(pf, &str);
482 lok(r == S_OK, "GetCurFile(path, TRUE) got 0x%08lx\n", r);
483 lok(str != NULL, "GetCurFile(path, TRUE) Didn't expect NULL\n");
484 lok(!wcscmp(path, str), "GetCurFile(path, TRUE) Expected %s, got %s\n", wine_dbgstr_w(path), wine_dbgstr_w(str));
485 CoTaskMemFree(str);
487 r = IPersistFile_Save(pf, NULL, TRUE);
488 lok(r == S_OK, "save(NULL, TRUE) failed (0x%08lx)\n", r);
490 /* test GetCurFile after ::Save */
491 r = IPersistFile_GetCurFile(pf, &str);
492 lok(r == S_OK, "GetCurFile(NULL, TRUE) got 0x%08lx\n", r);
493 lok(str != NULL, "GetCurFile(NULL, TRUE) Didn't expect NULL\n");
494 lok(!wcscmp(path, str), "GetCurFile(NULL, TRUE) Expected %s, got %s\n", wine_dbgstr_w(path), wine_dbgstr_w(str));
495 CoTaskMemFree(str);
497 IPersistFile_Release(pf);
500 IShellLinkA_Release(sl);
503 static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int todo)
505 HRESULT r;
506 IShellLinkA *sl;
507 IPersistFile *pf;
508 char buffer[INFOTIPSIZE];
509 LPOLESTR str;
511 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
512 &IID_IShellLinkA, (LPVOID*)&sl);
513 lok(r == S_OK, "no IID_IShellLinkA (0x%08lx)\n", r);
514 if (r != S_OK)
515 return;
517 r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
518 lok(r == S_OK, "no IID_IPersistFile (0x%08lx)\n", r);
519 if (r != S_OK)
521 IShellLinkA_Release(sl);
522 return;
525 /* test GetCurFile before ::Load */
526 str = (LPWSTR)0xdeadbeef;
527 r = IPersistFile_GetCurFile(pf, &str);
528 lok(r == S_FALSE, "got 0x%08lx\n", r);
529 lok(str == NULL, "got %p\n", str);
531 r = IPersistFile_Load(pf, path, STGM_READ);
532 lok(r == S_OK, "load failed (0x%08lx)\n", r);
534 /* test GetCurFile after ::Save */
535 r = IPersistFile_GetCurFile(pf, &str);
536 lok(r == S_OK, "got 0x%08lx\n", r);
537 lok(str != NULL, "Didn't expect NULL\n");
538 lok(!wcscmp(path, str), "Expected %s, got %s\n", wine_dbgstr_w(path), wine_dbgstr_w(str));
539 CoTaskMemFree(str);
541 IPersistFile_Release(pf);
543 if (desc->description)
545 strcpy(buffer,"garbage");
546 r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
547 lok(r == S_OK, "GetDescription failed (0x%08lx)\n", r);
548 todo_wine_if ((todo & 0x1) != 0)
549 lok(strcmp(buffer, desc->description)==0, "GetDescription returned '%s' instead of '%s'\n",
550 buffer, desc->description);
552 if (desc->workdir)
554 strcpy(buffer,"garbage");
555 r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
556 lok(r == S_OK, "GetWorkingDirectory failed (0x%08lx)\n", r);
557 todo_wine_if ((todo & 0x2) != 0)
558 lok(lstrcmpiA(buffer, desc->workdir)==0, "GetWorkingDirectory returned '%s' instead of '%s'\n",
559 buffer, desc->workdir);
561 if (desc->path)
563 strcpy(buffer,"garbage");
564 r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
565 lok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
566 todo_wine_if ((todo & 0x4) != 0)
567 lok(lstrcmpiA(buffer, desc->path)==0, "GetPath returned '%s' instead of '%s'\n",
568 buffer, desc->path);
570 if (desc->pidl)
572 LPITEMIDLIST pidl=NULL;
573 r = IShellLinkA_GetIDList(sl, &pidl);
574 lok(r == S_OK, "GetIDList failed (0x%08lx)\n", r);
575 todo_wine_if ((todo & 0x8) != 0)
576 lok(ILIsEqual(pidl, desc->pidl), "GetIDList returned an incorrect pidl\n");
578 if (desc->showcmd)
580 int i=0xdeadbeef;
581 r = IShellLinkA_GetShowCmd(sl, &i);
582 lok(r == S_OK, "GetShowCmd failed (0x%08lx)\n", r);
583 todo_wine_if ((todo & 0x10) != 0)
584 lok(i==desc->showcmd, "GetShowCmd returned 0x%0x instead of 0x%0x\n",
585 i, desc->showcmd);
587 if (desc->icon)
589 int i=0xdeadbeef;
590 strcpy(buffer,"garbage");
591 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
592 lok(r == S_OK, "GetIconLocation failed (0x%08lx)\n", r);
593 todo_wine_if ((todo & 0x20) != 0) {
594 lok(lstrcmpiA(buffer, desc->icon)==0, "GetIconLocation returned '%s' instead of '%s'\n",
595 buffer, desc->icon);
596 lok(i==desc->icon_id, "GetIconLocation returned 0x%0x instead of 0x%0x\n",
597 i, desc->icon_id);
600 if (desc->hotkey)
602 WORD i=0xbeef;
603 r = IShellLinkA_GetHotkey(sl, &i);
604 lok(r == S_OK, "GetHotkey failed (0x%08lx)\n", r);
605 todo_wine_if ((todo & 0x40) != 0)
606 lok(i==desc->hotkey, "GetHotkey returned 0x%04x instead of 0x%04x\n",
607 i, desc->hotkey);
610 IShellLinkA_Release(sl);
613 static void test_load_save(void)
615 WCHAR lnkfile[MAX_PATH];
616 char lnkfileA[MAX_PATH];
617 static const char lnkfileA_name[] = "\\test.lnk";
619 lnk_desc_t desc;
620 char mypath[MAX_PATH];
621 char mydir[MAX_PATH];
622 char realpath[MAX_PATH];
623 IPersistFile *pf;
624 IShellLinkA *sl;
625 IStream *stm;
626 char* p;
627 HANDLE hf;
628 DWORD r;
630 /* Don't used a fixed path for the test.lnk file */
631 GetTempPathA(MAX_PATH, lnkfileA);
632 lstrcatA(lnkfileA, lnkfileA_name);
633 MultiByteToWideChar(CP_ACP, 0, lnkfileA, -1, lnkfile, MAX_PATH);
635 /* Save an empty .lnk file */
636 memset(&desc, 0, sizeof(desc));
637 create_lnk(lnkfile, &desc);
639 /* It should come back as a bunch of empty strings */
640 desc.description="";
641 desc.workdir="";
642 desc.path="";
643 desc.arguments="";
644 desc.icon="";
645 check_lnk(lnkfile, &desc, 0x0);
647 /* Point a .lnk file to nonexistent files */
648 desc.description="";
649 desc.workdir="c:\\Nonexitent\\work\\directory";
650 desc.path="c:\\nonexistent\\path";
651 desc.pidl=NULL;
652 desc.arguments="";
653 desc.showcmd=0;
654 desc.icon="c:\\nonexistent\\icon\\file";
655 desc.icon_id=1234;
656 desc.hotkey=0;
657 create_lnk(lnkfile, &desc);
658 check_lnk(lnkfile, &desc, 0x0);
660 r=GetModuleFileNameA(NULL, mypath, sizeof(mypath));
661 ok(r<sizeof(mypath), "GetModuleFileName failed (%ld)\n", r);
662 strcpy(mydir, mypath);
663 p=strrchr(mydir, '\\');
664 if (p)
665 *p='\0';
667 /* IShellLink returns path in long form */
668 if (!GetLongPathNameA(mypath, realpath, MAX_PATH))
669 strcpy( realpath, mypath );
671 /* Overwrite the existing lnk file and point it to existing files */
672 desc.description="test 2";
673 desc.workdir=mydir;
674 desc.path=realpath;
675 desc.pidl=NULL;
676 desc.arguments="/option1 /option2 \"Some string\"";
677 desc.showcmd=SW_SHOWNORMAL;
678 desc.icon=mypath;
679 desc.icon_id=0;
680 desc.hotkey=0x1234;
681 create_lnk(lnkfile, &desc);
682 check_lnk(lnkfile, &desc, 0x0);
684 /* Test omitting .exe from an absolute path */
685 p=strrchr(realpath, '.');
686 if (p)
687 *p='\0';
689 desc.description="absolute path without .exe";
690 desc.workdir=mydir;
691 desc.path=realpath;
692 desc.pidl=NULL;
693 desc.arguments="/option1 /option2 \"Some string\"";
694 desc.showcmd=SW_SHOWNORMAL;
695 desc.icon=mypath;
696 desc.icon_id=0;
697 desc.hotkey=0x1234;
698 create_lnk(lnkfile, &desc);
699 strcat(realpath, ".exe");
700 check_lnk(lnkfile, &desc, 0x4);
702 /* Overwrite the existing lnk file and test link to a command on the path */
703 desc.description="command on path";
704 desc.workdir=mypath;
705 desc.path="rundll32.exe";
706 desc.pidl=NULL;
707 desc.arguments="/option1 /option2 \"Some string\"";
708 desc.showcmd=SW_SHOWNORMAL;
709 desc.icon=mypath;
710 desc.icon_id=0;
711 desc.hotkey=0x1234;
712 create_lnk(lnkfile, &desc);
713 /* Check that link is created to proper location */
714 SearchPathA( NULL, desc.path, NULL, MAX_PATH, realpath, NULL);
715 desc.path=realpath;
716 check_lnk(lnkfile, &desc, 0x0);
718 /* Test omitting .exe from a command on the path */
719 desc.description="command on path without .exe";
720 desc.workdir=mypath;
721 desc.path="rundll32";
722 desc.pidl=NULL;
723 desc.arguments="/option1 /option2 \"Some string\"";
724 desc.showcmd=SW_SHOWNORMAL;
725 desc.icon=mypath;
726 desc.icon_id=0;
727 desc.hotkey=0x1234;
728 create_lnk(lnkfile, &desc);
729 /* Check that link is created to proper location */
730 SearchPathA( NULL, "rundll32", NULL, MAX_PATH, realpath, NULL);
731 desc.path=realpath;
732 check_lnk(lnkfile, &desc, 0x4);
734 /* Create a temporary non-executable file */
735 r=GetTempPathA(sizeof(mypath), mypath);
736 ok(r<sizeof(mypath), "GetTempPath failed (%ld), err %ld\n", r, GetLastError());
737 r = GetLongPathNameA(mypath, mydir, sizeof(mydir));
738 ok(r<sizeof(mydir), "GetLongPathName failed (%ld), err %ld\n", r, GetLastError());
739 p=strrchr(mydir, '\\');
740 if (p)
741 *p='\0';
743 strcpy(mypath, mydir);
744 strcat(mypath, "\\test.txt");
745 hf = CreateFileA(mypath, GENERIC_WRITE, 0, NULL,
746 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
747 CloseHandle(hf);
749 /* Overwrite the existing lnk file and test link to an existing non-executable file */
750 desc.description="non-executable file";
751 desc.workdir=mydir;
752 desc.path=mypath;
753 desc.pidl=NULL;
754 desc.arguments="";
755 desc.showcmd=SW_SHOWNORMAL;
756 desc.icon=mypath;
757 desc.icon_id=0;
758 desc.hotkey=0x1234;
759 create_lnk(lnkfile, &desc);
760 check_lnk(lnkfile, &desc, 0x0);
762 r = GetShortPathNameA(mydir, mypath, sizeof(mypath));
763 ok(r<sizeof(mypath), "GetShortPathName failed (%ld), err %ld\n", r, GetLastError());
765 strcpy(realpath, mypath);
766 strcat(realpath, "\\test.txt");
767 strcat(mypath, "\\\\test.txt");
769 /* Overwrite the existing lnk file and test link to a short path with double backslashes */
770 desc.description="non-executable file";
771 desc.workdir=mydir;
772 desc.path=mypath;
773 desc.pidl=NULL;
774 desc.arguments="";
775 desc.showcmd=SW_SHOWNORMAL;
776 desc.icon=mypath;
777 desc.icon_id=0;
778 desc.hotkey=0x1234;
779 create_lnk(lnkfile, &desc);
780 desc.path=realpath;
781 check_lnk(lnkfile, &desc, 0x0);
783 r = DeleteFileA(mypath);
784 ok(r, "failed to delete file %s (%ld)\n", mypath, GetLastError());
786 /* Create a temporary .bat file */
787 strcpy(mypath, mydir);
788 strcat(mypath, "\\test.bat");
789 hf = CreateFileA(mypath, GENERIC_WRITE, 0, NULL,
790 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
791 CloseHandle(hf);
793 strcpy(realpath, mypath);
795 p=strrchr(mypath, '.');
796 if (p)
797 *p='\0';
799 /* Try linking to the .bat file without the extension */
800 desc.description="batch file";
801 desc.workdir=mydir;
802 desc.path=mypath;
803 desc.pidl=NULL;
804 desc.arguments="";
805 desc.showcmd=SW_SHOWNORMAL;
806 desc.icon=mypath;
807 desc.icon_id=0;
808 desc.hotkey=0x1234;
809 create_lnk(lnkfile, &desc);
810 desc.path = realpath;
811 check_lnk(lnkfile, &desc, 0x4);
813 r = DeleteFileA(realpath);
814 ok(r, "failed to delete file %s (%ld)\n", realpath, GetLastError());
816 /* test sharing modes */
817 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkA, (LPVOID*)&sl);
818 ok( r == S_OK, "no IID_IShellLinkA (0x%08lx)\n", r );
819 r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (void**)&pf);
820 ok( r == S_OK, "no IID_IPersistFile (0x%08lx)\n", r );
822 r = SHCreateStreamOnFileW(lnkfile, STGM_READ, &stm);
823 ok( !r, "SHCreateStreamOnFileW failed %lx\n", r );
824 r = IPersistFile_Save(pf, lnkfile, FALSE);
825 ok(r == S_OK, "IPersistFile_Save failed (0x%08lx)\n", r);
826 r = IPersistFile_Load(pf, lnkfile, 0);
827 ok(r == S_OK, "IPersistFile_Load failed (0x%08lx)\n", r);
828 IStream_Release( stm );
830 r = SHCreateStreamOnFileW(lnkfile, STGM_READ | STGM_SHARE_DENY_WRITE, &stm);
831 ok( r == S_OK, "SHCreateStreamOnFileW failed %lx\n", r );
832 r = IPersistFile_Save(pf, lnkfile, FALSE);
833 ok( r == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION), "IPersistFile_Save failed (0x%08lx)\n", r );
834 r = IPersistFile_Load(pf, lnkfile, 0);
835 ok(r == S_OK, "IPersistFile_Load failed (0x%08lx)\n", r);
836 IStream_Release( stm );
838 r = SHCreateStreamOnFileW(lnkfile, STGM_READWRITE | STGM_SHARE_DENY_WRITE, &stm);
839 ok( r == S_OK, "SHCreateStreamOnFileW failed %lx\n", r );
840 r = IPersistFile_Save(pf, lnkfile, FALSE);
841 ok( r == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION), "IPersistFile_Save failed (0x%08lx)\n", r );
842 r = IPersistFile_Load(pf, lnkfile, 0);
843 ok(r == S_OK, "IPersistFile_Load failed (0x%08lx)\n", r);
844 IStream_Release( stm );
846 r = SHCreateStreamOnFileW(lnkfile, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &stm);
847 ok( r == S_OK, "SHCreateStreamOnFileW failed %lx\n", r );
848 r = IPersistFile_Save(pf, lnkfile, FALSE);
849 ok( r == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION), "IPersistFile_Save failed (0x%08lx)\n", r );
850 r = IPersistFile_Load(pf, lnkfile, 0);
851 ok( r == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION), "IPersistFile_Load failed (0x%08lx)\n", r );
852 IStream_Release( stm );
854 IShellLinkA_Release( sl );
855 IPersistFile_Release( pf );
857 /* FIXME: Also test saving a .lnk pointing to a pidl that cannot be
858 * represented as a path.
861 DeleteFileW(lnkfile);
864 static void test_datalink(void)
866 static const WCHAR lnk[] = {
867 ':',':','{','9','d','b','1','1','8','6','e','-','4','0','d','f','-','1',
868 '1','d','1','-','a','a','8','c','-','0','0','c','0','4','f','b','6','7',
869 '8','6','3','}',':','2','6',',','!','!','g','x','s','f','(','N','g',']',
870 'q','F','`','H','{','L','s','A','C','C','E','S','S','F','i','l','e','s',
871 '>','p','l','T',']','j','I','{','j','f','(','=','1','&','L','[','-','8',
872 '1','-',']',':',':',0 };
873 static const WCHAR comp[] = {
874 '2','6',',','!','!','g','x','s','f','(','N','g',']','q','F','`','H','{',
875 'L','s','A','C','C','E','S','S','F','i','l','e','s','>','p','l','T',']',
876 'j','I','{','j','f','(','=','1','&','L','[','-','8','1','-',']',0 };
877 IShellLinkDataList *dl = NULL;
878 IShellLinkW *sl = NULL;
879 HRESULT r;
880 DWORD flags = 0;
881 EXP_DARWIN_LINK *dar;
883 r = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
884 &IID_IShellLinkW, (LPVOID*)&sl );
885 ok( r == S_OK, "Failed to create shelllink object, hr %#lx.\n", r);
887 r = IShellLinkW_QueryInterface( sl, &IID_IShellLinkDataList, (void **)&dl );
888 ok( r == S_OK, "Failed to get interface, hr %#lx.\n", r);
890 flags = 0;
891 r = IShellLinkDataList_GetFlags( dl, &flags );
892 ok( r == S_OK, "GetFlags failed\n");
893 ok( flags == 0, "GetFlags returned wrong flags\n");
895 dar = (void*)-1;
896 r = IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
897 ok( r == E_FAIL, "CopyDataBlock failed\n");
898 ok( dar == NULL, "should be null\n");
900 r = IShellLinkW_SetPath(sl, NULL);
901 ok(r == E_INVALIDARG, "Unexpected hr %#lx.\n", r);
903 r = IShellLinkW_SetPath(sl, lnk);
904 ok(r == S_OK, "SetPath failed\n");
906 if (0)
908 /* the following crashes */
909 IShellLinkDataList_GetFlags( dl, NULL );
912 flags = 0;
913 r = IShellLinkDataList_GetFlags( dl, &flags );
914 ok( r == S_OK, "GetFlags failed\n");
915 /* SLDF_HAS_LOGO3ID is no longer supported on Vista+, filter it out */
916 ok( (flags & (~ SLDF_HAS_LOGO3ID)) == SLDF_HAS_DARWINID,
917 "GetFlags returned wrong flags\n");
919 dar = NULL;
920 r = IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
921 ok( r == S_OK, "CopyDataBlock failed\n");
923 ok( dar && ((DATABLOCK_HEADER*)dar)->dwSignature == EXP_DARWIN_ID_SIG, "signature wrong\n");
924 ok( dar && 0==lstrcmpW(dar->szwDarwinID, comp ), "signature wrong\n");
926 LocalFree( dar );
928 IShellLinkDataList_Release( dl );
929 IShellLinkW_Release( sl );
932 static void test_shdefextracticon(void)
934 HICON hiconlarge=NULL, hiconsmall=NULL;
935 HRESULT res;
937 res = SHDefExtractIconA("shell32.dll", 0, 0, &hiconlarge, &hiconsmall, MAKELONG(16,24));
938 ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%lx\n", res);
939 ok(hiconlarge != NULL, "got null hiconlarge\n");
940 ok(hiconsmall != NULL, "got null hiconsmall\n");
941 DestroyIcon(hiconlarge);
942 DestroyIcon(hiconsmall);
944 hiconsmall = NULL;
945 res = SHDefExtractIconA("shell32.dll", 0, 0, NULL, &hiconsmall, MAKELONG(16,24));
946 ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%lx\n", res);
947 ok(hiconsmall != NULL, "got null hiconsmall\n");
948 DestroyIcon(hiconsmall);
950 res = SHDefExtractIconA("shell32.dll", 0, 0, NULL, NULL, MAKELONG(16,24));
951 ok(SUCCEEDED(res), "SHDefExtractIconA failed, res=%lx\n", res);
954 static void test_GetIconLocation(void)
956 IShellLinkW *slW;
957 IShellLinkA *sl;
958 const char *str;
959 char buffer[INFOTIPSIZE], mypath[MAX_PATH];
960 int i;
961 HRESULT r;
962 LPITEMIDLIST pidl;
964 r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
965 &IID_IShellLinkA, (LPVOID*)&sl);
966 ok(r == S_OK, "no IID_IShellLinkA (0x%08lx)\n", r);
967 if(r != S_OK)
968 return;
970 i = 0xdeadbeef;
971 strcpy(buffer, "garbage");
972 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
973 ok(r == S_OK, "GetIconLocation failed (0x%08lx)\n", r);
974 ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
975 ok(i == 0, "GetIconLocation returned %d\n", i);
977 str = "c:\\some\\path";
978 r = IShellLinkA_SetPath(sl, str);
979 ok(r == S_FALSE || r == S_OK, "SetPath failed (0x%08lx)\n", r);
981 i = 0xdeadbeef;
982 strcpy(buffer, "garbage");
983 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
984 ok(r == S_OK, "GetIconLocation failed (0x%08lx)\n", r);
985 ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
986 ok(i == 0, "GetIconLocation returned %d\n", i);
988 GetWindowsDirectoryA(mypath, sizeof(mypath) - 12);
989 strcat(mypath, "\\regedit.exe");
990 pidl = path_to_pidl(mypath);
991 r = IShellLinkA_SetIDList(sl, pidl);
992 ok(r == S_OK, "SetPath failed (0x%08lx)\n", r);
993 ILFree(pidl);
995 i = 0xdeadbeef;
996 strcpy(buffer, "garbage");
997 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
998 ok(r == S_OK, "GetIconLocation failed (0x%08lx)\n", r);
999 ok(*buffer == '\0', "GetIconLocation returned '%s'\n", buffer);
1000 ok(i == 0, "GetIconLocation returned %d\n", i);
1002 str = "c:\\nonexistent\\file";
1003 r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
1004 ok(r == S_OK, "SetIconLocation failed (0x%08lx)\n", r);
1006 i = 0xdeadbeef;
1007 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
1008 ok(r == S_OK, "GetIconLocation failed (0x%08lx)\n", r);
1009 ok(lstrcmpiA(buffer,str) == 0, "GetIconLocation returned '%s'\n", buffer);
1010 ok(i == 0xbabecafe, "GetIconLocation returned %#x.\n", i);
1012 r = IShellLinkA_SetIconLocation(sl, NULL, 0xcafefe);
1013 ok(r == S_OK, "SetIconLocation failed (0x%08lx)\n", r);
1015 i = 0xdeadbeef;
1016 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
1017 ok(r == S_OK, "GetIconLocation failed (0x%08lx)\n", r);
1018 ok(!*buffer, "GetIconLocation returned '%s'\n", buffer);
1019 ok(i == 0xcafefe, "GetIconLocation returned %#x.\n", i);
1021 r = IShellLinkA_QueryInterface(sl, &IID_IShellLinkW, (void **)&slW);
1022 ok(SUCCEEDED(r), "Failed to get IShellLinkW, hr %#lx.\n", r);
1024 str = "c:\\nonexistent\\file";
1025 r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
1026 ok(r == S_OK, "SetIconLocation failed (0x%08lx)\n", r);
1028 r = IShellLinkA_SetIconLocation(sl, NULL, 0xcafefe);
1029 ok(r == S_OK, "SetIconLocation failed (0x%08lx)\n", r);
1031 i = 0xdeadbeef;
1032 r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
1033 ok(r == S_OK, "GetIconLocation failed (0x%08lx)\n", r);
1034 ok(!*buffer, "GetIconLocation returned '%s'\n", buffer);
1035 ok(i == 0xcafefe, "GetIconLocation returned %#x.\n", i);
1037 IShellLinkW_Release(slW);
1038 IShellLinkA_Release(sl);
1041 static void test_SHGetStockIconInfo(void)
1043 BYTE buffer[sizeof(SHSTOCKICONINFO) + 16];
1044 SHSTOCKICONINFO *sii = (SHSTOCKICONINFO *) buffer;
1045 HRESULT hr;
1046 INT i;
1048 /* not present before vista */
1049 if (!pSHGetStockIconInfo)
1051 win_skip("SHGetStockIconInfo not available\n");
1052 return;
1055 /* negative values are handled */
1056 memset(buffer, '#', sizeof(buffer));
1057 sii->cbSize = sizeof(SHSTOCKICONINFO);
1058 hr = pSHGetStockIconInfo(SIID_INVALID, SHGSI_ICONLOCATION, sii);
1059 ok(hr == E_INVALIDARG, "-1: got 0x%lx (expected E_INVALIDARG)\n", hr);
1061 /* max. id for vista is 140 (no definition exists for this value) */
1062 for (i = SIID_DOCNOASSOC; i <= SIID_CLUSTEREDDRIVE; i++)
1064 memset(buffer, '#', sizeof(buffer));
1065 sii->cbSize = sizeof(SHSTOCKICONINFO);
1066 hr = pSHGetStockIconInfo(i, SHGSI_ICONLOCATION, sii);
1068 ok(hr == S_OK,
1069 "%3d: got 0x%lx, iSysImageIndex: 0x%x, iIcon: 0x%x (expected S_OK)\n",
1070 i, hr, sii->iSysImageIndex, sii->iIcon);
1072 if ((hr == S_OK) && (winetest_debug > 1))
1073 trace("%3d: got iSysImageIndex %3d, iIcon %3d and %s\n", i, sii->iSysImageIndex,
1074 sii->iIcon, wine_dbgstr_w(sii->szPath));
1077 /* test invalid icons indices that are invalid for all platforms */
1078 for (i = SIID_MAX_ICONS; i < (SIID_MAX_ICONS + 25) ; i++)
1080 memset(buffer, '#', sizeof(buffer));
1081 sii->cbSize = sizeof(SHSTOCKICONINFO);
1082 hr = pSHGetStockIconInfo(i, SHGSI_ICONLOCATION, sii);
1083 ok(hr == E_INVALIDARG, "%3d: got 0x%lx (expected E_INVALIDARG)\n", i, hr);
1084 todo_wine {
1085 ok(sii->iSysImageIndex == -1, "%d: got iSysImageIndex %d\n", i, sii->iSysImageIndex);
1086 ok(sii->iIcon == -1, "%d: got iIcon %d\n", i, sii->iIcon);
1090 /* test more returned SHSTOCKICONINFO elements without extra flags */
1091 memset(buffer, '#', sizeof(buffer));
1092 sii->cbSize = sizeof(SHSTOCKICONINFO);
1093 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1094 ok(hr == S_OK, "got 0x%lx (expected S_OK)\n", hr);
1095 ok(!sii->hIcon, "got %p (expected NULL)\n", sii->hIcon);
1096 ok(sii->iSysImageIndex == -1, "got %d (expected -1)\n", sii->iSysImageIndex);
1098 /* the exact size is required of the struct */
1099 memset(buffer, '#', sizeof(buffer));
1100 sii->cbSize = sizeof(SHSTOCKICONINFO) + 2;
1101 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1102 ok(hr == E_INVALIDARG, "+2: got 0x%lx, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1104 memset(buffer, '#', sizeof(buffer));
1105 sii->cbSize = sizeof(SHSTOCKICONINFO) + 1;
1106 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1107 ok(hr == E_INVALIDARG, "+1: got 0x%lx, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1109 memset(buffer, '#', sizeof(buffer));
1110 sii->cbSize = sizeof(SHSTOCKICONINFO) - 1;
1111 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1112 ok(hr == E_INVALIDARG, "-1: got 0x%lx, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1114 memset(buffer, '#', sizeof(buffer));
1115 sii->cbSize = sizeof(SHSTOCKICONINFO) - 2;
1116 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, sii);
1117 ok(hr == E_INVALIDARG, "-2: got 0x%lx, iSysImageIndex: 0x%x, iIcon: 0x%x\n", hr, sii->iSysImageIndex, sii->iIcon);
1119 /* there is a NULL check for the struct */
1120 hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, NULL);
1121 ok(hr == E_INVALIDARG, "NULL: got 0x%lx\n", hr);
1124 static void test_SHExtractIcons(void)
1126 static const WCHAR notepadW[] = {'n','o','t','e','p','a','d','.','e','x','e',0};
1127 static const WCHAR shell32W[] = {'s','h','e','l','l','3','2','.','d','l','l',0};
1128 static const WCHAR emptyW[] = {0};
1129 UINT ret, ret2;
1130 HICON icons[256];
1131 UINT ids[256], i;
1133 if (!pSHExtractIconsW)
1135 win_skip("SHExtractIconsW not available\n");
1136 return;
1139 ret = pSHExtractIconsW(emptyW, 0, 16, 16, icons, ids, 1, 0);
1140 ok(ret == ~0u, "got %u\n", ret);
1142 ret = pSHExtractIconsW(notepadW, 0, 16, 16, NULL, NULL, 1, 0);
1143 ok(ret == 1 || ret == 4 /* win11 */, "got %u\n", ret);
1145 icons[0] = (HICON)0xdeadbeef;
1146 ret = pSHExtractIconsW(notepadW, 0, 16, 16, icons, NULL, 1, 0);
1147 ok(ret == 1, "got %u\n", ret);
1148 ok(icons[0] != (HICON)0xdeadbeef, "icon not set\n");
1149 DestroyIcon(icons[0]);
1151 icons[0] = (HICON)0xdeadbeef;
1152 ids[0] = 0xdeadbeef;
1153 ret = pSHExtractIconsW(notepadW, 0, 16, 16, icons, ids, 1, 0);
1154 ok(ret == 1, "got %u\n", ret);
1155 ok(icons[0] != (HICON)0xdeadbeef, "icon not set\n");
1156 ok(ids[0] != 0xdeadbeef, "id not set\n");
1157 DestroyIcon(icons[0]);
1159 ret = pSHExtractIconsW(shell32W, 0, 16, 16, NULL, NULL, 0, 0);
1160 ret2 = pSHExtractIconsW(shell32W, 4, MAKELONG(32,16), MAKELONG(32,16), NULL, NULL, 256, 0);
1161 ok(ret && ret == ret2,
1162 "icon count should be independent of requested icon sizes and base icon index\n");
1164 ret = pSHExtractIconsW(shell32W, 0, 16, 16, icons, ids, 0, 0);
1165 ok(ret == ~0u || !ret /* < vista */, "got %u\n", ret);
1167 ret = pSHExtractIconsW(shell32W, 0, 16, 16, icons, ids, 3, 0);
1168 ok(ret == 3, "got %u\n", ret);
1169 for (i = 0; i < ret; i++) DestroyIcon(icons[i]);
1171 /* count must be a multiple of two when getting two sizes */
1172 ret = pSHExtractIconsW(shell32W, 0, MAKELONG(16,32), MAKELONG(16,32), icons, ids, 3, 0);
1173 ok(!ret /* vista */ || ret == 4, "got %u\n", ret);
1174 for (i = 0; i < ret; i++) DestroyIcon(icons[i]);
1176 ret = pSHExtractIconsW(shell32W, 0, MAKELONG(16,32), MAKELONG(16,32), icons, ids, 4, 0);
1177 ok(ret == 4, "got %u\n", ret);
1178 for (i = 0; i < ret; i++) DestroyIcon(icons[i]);
1181 static void test_propertystore(void)
1183 IShellLinkA *linkA;
1184 IShellLinkW *linkW;
1185 IPropertyStore *ps;
1186 HRESULT hr;
1188 hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1189 &IID_IShellLinkA, (void**)&linkA);
1190 ok(hr == S_OK, "got 0x%08lx\n", hr);
1192 hr = IShellLinkA_QueryInterface(linkA, &IID_IShellLinkW, (void**)&linkW);
1193 ok(hr == S_OK, "got 0x%08lx\n", hr);
1195 hr = IShellLinkA_QueryInterface(linkA, &IID_IPropertyStore, (void**)&ps);
1196 if (hr == S_OK) {
1197 IPropertyStoreCache *pscache;
1199 IPropertyStore_Release(ps);
1201 hr = IShellLinkW_QueryInterface(linkW, &IID_IPropertyStore, (void**)&ps);
1202 ok(hr == S_OK, "got 0x%08lx\n", hr);
1204 hr = IPropertyStore_QueryInterface(ps, &IID_IPropertyStoreCache, (void**)&pscache);
1205 ok(hr == E_NOINTERFACE, "got 0x%08lx\n", hr);
1207 IPropertyStore_Release(ps);
1209 else
1210 win_skip("IShellLink doesn't support IPropertyStore.\n");
1212 IShellLinkA_Release(linkA);
1213 IShellLinkW_Release(linkW);
1216 static void test_ExtractIcon(void)
1218 static const WCHAR nameW[] = {'\\','e','x','t','r','a','c','t','i','c','o','n','_','t','e','s','t','.','t','x','t',0};
1219 static const WCHAR shell32W[] = {'s','h','e','l','l','3','2','.','d','l','l',0};
1220 static const WCHAR emptyW[] = {0};
1221 WCHAR pathW[MAX_PATH];
1222 HICON hicon, hicon2;
1223 char path[MAX_PATH];
1224 HANDLE file;
1225 int r;
1226 ICONINFO info;
1227 BITMAP bm;
1229 /* specified instance handle */
1230 hicon = ExtractIconA(GetModuleHandleA("shell32.dll"), NULL, 0);
1231 ok(hicon == NULL, "Got icon %p\n", hicon);
1232 hicon2 = ExtractIconA(GetModuleHandleA("shell32.dll"), "shell32.dll", -1);
1233 ok(hicon2 != NULL, "Got icon %p\n", hicon2);
1235 /* existing index */
1236 hicon = ExtractIconA(NULL, "shell32.dll", 0);
1237 ok(hicon != NULL && HandleToLong(hicon) != -1, "Got icon %p\n", hicon);
1238 DestroyIcon(hicon);
1240 /* returns number of resources */
1241 hicon = ExtractIconA(NULL, "shell32.dll", -1);
1242 ok(HandleToLong(hicon) > 1 && hicon == hicon2, "Got icon %p\n", hicon);
1244 /* invalid index, valid dll name */
1245 hicon = ExtractIconA(NULL, "shell32.dll", 3000);
1246 ok(hicon == NULL, "Got icon %p\n", hicon);
1248 /* Create a temporary non-executable file */
1249 GetTempPathA(sizeof(path), path);
1250 strcat(path, "\\extracticon_test.txt");
1251 file = CreateFileA(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1252 ok(file != INVALID_HANDLE_VALUE, "Failed to create a test file\n");
1253 CloseHandle(file);
1255 hicon = ExtractIconA(NULL, path, 0);
1256 ok(hicon == NULL, "Got icon %p\n", hicon);
1258 hicon = ExtractIconA(NULL, path, -1);
1259 ok(hicon == NULL, "Got icon %p\n", hicon);
1261 hicon = ExtractIconA(NULL, path, 1);
1262 ok(hicon == NULL, "Got icon %p\n", hicon);
1264 r = DeleteFileA(path);
1265 ok(r, "failed to delete file %s (%ld)\n", path, GetLastError());
1267 /* Empty file path */
1268 hicon = ExtractIconA(NULL, "", -1);
1269 ok(hicon == NULL, "Got icon %p\n", hicon);
1271 hicon = ExtractIconA(NULL, "", 0);
1272 ok(hicon == NULL, "Got icon %p\n", hicon);
1274 /* same for W variant */
1275 if (0)
1277 /* specified instance handle, crashes on XP, 2k3 */
1278 hicon = ExtractIconW(GetModuleHandleA("shell32.dll"), NULL, 0);
1279 ok(hicon == NULL, "Got icon %p\n", hicon);
1281 hicon2 = ExtractIconW(GetModuleHandleA("shell32.dll"), shell32W, -1);
1282 ok(hicon2 != NULL, "Got icon %p\n", hicon2);
1284 /* existing index */
1285 hicon = ExtractIconW(NULL, shell32W, 0);
1286 ok(hicon != NULL && HandleToLong(hicon) != -1, "Got icon %p\n", hicon);
1287 GetIconInfo(hicon, &info);
1288 GetObjectW(info.hbmColor, sizeof(bm), &bm);
1289 ok(bm.bmWidth == GetSystemMetrics(SM_CXICON), "got %d\n", bm.bmWidth);
1290 ok(bm.bmHeight == GetSystemMetrics(SM_CYICON), "got %d\n", bm.bmHeight);
1291 DestroyIcon(hicon);
1293 /* returns number of resources */
1294 hicon = ExtractIconW(NULL, shell32W, -1);
1295 ok(HandleToLong(hicon) > 1 && hicon == hicon2, "Got icon %p\n", hicon);
1297 /* invalid index, valid dll name */
1298 hicon = ExtractIconW(NULL, shell32W, 3000);
1299 ok(hicon == NULL, "Got icon %p\n", hicon);
1301 /* Create a temporary non-executable file */
1302 GetTempPathW(ARRAY_SIZE(pathW), pathW);
1303 lstrcatW(pathW, nameW);
1304 file = CreateFileW(pathW, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1305 ok(file != INVALID_HANDLE_VALUE, "Failed to create a test file\n");
1306 CloseHandle(file);
1308 hicon = ExtractIconW(NULL, pathW, 0);
1309 ok(hicon == NULL, "Got icon %p\n", hicon);
1311 hicon = ExtractIconW(NULL, pathW, -1);
1312 ok(hicon == NULL, "Got icon %p\n", hicon);
1314 hicon = ExtractIconW(NULL, pathW, 1);
1315 ok(hicon == NULL, "Got icon %p\n", hicon);
1317 r = DeleteFileW(pathW);
1318 ok(r, "failed to delete file %s (%ld)\n", path, GetLastError());
1320 /* Empty file path */
1321 hicon = ExtractIconW(NULL, emptyW, -1);
1322 ok(hicon == NULL, "Got icon %p\n", hicon);
1324 hicon = ExtractIconW(NULL, emptyW, 0);
1325 ok(hicon == NULL, "Got icon %p\n", hicon);
1328 static void test_ExtractAssociatedIcon(void)
1330 char pathA[MAX_PATH];
1331 HICON hicon;
1332 WORD index;
1334 /* empty path */
1335 index = 0;
1336 *pathA = 0;
1337 hicon = ExtractAssociatedIconA(NULL, pathA, &index);
1338 todo_wine {
1339 ok(hicon != NULL, "Got icon %p\n", hicon);
1340 ok(!*pathA, "Unexpected path %s\n", pathA);
1341 ok(index == 0, "Unexpected index %u\n", index);
1343 DestroyIcon(hicon);
1345 /* by index */
1346 index = 0;
1347 strcpy(pathA, "shell32.dll");
1348 hicon = ExtractAssociatedIconA(NULL, pathA, &index);
1349 ok(hicon != NULL, "Got icon %p\n", hicon);
1350 ok(!strcmp(pathA, "shell32.dll"), "Unexpected path %s\n", pathA);
1351 ok(index == 0, "Unexpected index %u\n", index);
1352 DestroyIcon(hicon);
1354 /* valid dll name, invalid index */
1355 index = 5000;
1356 strcpy(pathA, "user32.dll");
1357 hicon = ExtractAssociatedIconA(NULL, pathA, &index);
1358 CharLowerBuffA(pathA, strlen(pathA));
1359 todo_wine {
1360 ok(hicon != NULL, "Got icon %p\n", hicon);
1361 ok(!!strstr(pathA, "shell32.dll"), "Unexpected path %s\n", pathA);
1363 ok(index != 5000, "Unexpected index %u\n", index);
1364 DestroyIcon(hicon);
1366 /* associated icon */
1367 index = 0xcaca;
1368 strcpy(pathA, "dummy.exe");
1369 hicon = ExtractAssociatedIconA(NULL, pathA, &index);
1370 CharLowerBuffA(pathA, strlen(pathA));
1371 todo_wine {
1372 ok(hicon != NULL, "Got icon %p\n", hicon);
1373 ok(!!strstr(pathA, "shell32.dll"), "Unexpected path %s\n", pathA);
1375 ok(index != 0xcaca, "Unexpected index %u\n", index);
1376 DestroyIcon(hicon);
1379 static int get_shell_icon_size(void)
1381 char buf[10];
1382 DWORD value = 32, size = sizeof(buf), type;
1383 HKEY key;
1385 if (!RegOpenKeyA( HKEY_CURRENT_USER, "Control Panel\\Desktop\\WindowMetrics", &key ))
1387 if (!RegQueryValueExA( key, "Shell Icon Size", NULL, &type, (BYTE *)buf, &size ) && type == REG_SZ)
1388 value = atoi( buf );
1389 RegCloseKey( key );
1391 return value;
1394 static void test_SHGetImageList(void)
1396 HRESULT hr;
1397 IImageList *list, *list2;
1398 BOOL ret;
1399 HIMAGELIST lg, sm;
1400 ULONG start_refs, refs;
1401 int i, width, height, expect;
1402 BOOL dpi_aware = pIsProcessDPIAware && pIsProcessDPIAware();
1404 hr = SHGetImageList( SHIL_LARGE, &IID_IImageList, (void **)&list );
1405 ok( hr == S_OK, "got %08lx\n", hr );
1406 start_refs = IImageList_AddRef( list );
1407 IImageList_Release( list );
1409 hr = SHGetImageList( SHIL_LARGE, &IID_IImageList, (void **)&list2 );
1410 ok( hr == S_OK, "got %08lx\n", hr );
1411 ok( list == list2, "lists differ\n" );
1412 refs = IImageList_AddRef( list );
1413 IImageList_Release( list );
1414 ok( refs == start_refs + 1, "got %ld, start_refs %ld\n", refs, start_refs );
1415 IImageList_Release( list2 );
1417 hr = SHGetImageList( SHIL_SMALL, &IID_IImageList, (void **)&list2 );
1418 ok( hr == S_OK, "got %08lx\n", hr );
1420 ret = Shell_GetImageLists( &lg, &sm );
1421 ok( ret, "got %d\n", ret );
1422 ok( lg == (HIMAGELIST)list, "mismatch\n" );
1423 ok( sm == (HIMAGELIST)list2, "mismatch\n" );
1425 /* Shell_GetImageLists doesn't take a reference */
1426 refs = IImageList_AddRef( list );
1427 IImageList_Release( list );
1428 ok( refs == start_refs, "got %ld, start_refs %ld\n", refs, start_refs );
1430 IImageList_Release( list2 );
1431 IImageList_Release( list );
1433 /* Test the icon sizes */
1434 for (i = 0; i <= SHIL_LAST; i++)
1436 hr = SHGetImageList( i, &IID_IImageList, (void **)&list );
1437 ok( hr == S_OK || broken( i == SHIL_JUMBO && hr == E_INVALIDARG ), /* XP and 2003 */
1438 "%d: got %08lx\n", i, hr );
1439 if (FAILED(hr)) continue;
1440 IImageList_GetIconSize( list, &width, &height );
1441 switch (i)
1443 case SHIL_LARGE:
1444 if (dpi_aware) expect = GetSystemMetrics( SM_CXICON );
1445 else expect = get_shell_icon_size();
1446 break;
1447 case SHIL_SMALL:
1448 if (dpi_aware) expect = GetSystemMetrics( SM_CXICON ) / 2;
1449 else expect = GetSystemMetrics( SM_CXSMICON );
1450 break;
1451 case SHIL_EXTRALARGE:
1452 expect = (GetSystemMetrics( SM_CXICON ) * 3) / 2;
1453 break;
1454 case SHIL_SYSSMALL:
1455 expect = GetSystemMetrics( SM_CXSMICON );
1456 break;
1457 case SHIL_JUMBO:
1458 expect = 256;
1459 break;
1461 todo_wine_if(i == SHIL_SYSSMALL && dpi_aware && expect != GetSystemMetrics( SM_CXICON ) / 2)
1463 ok( width == expect, "%d: got %d expect %d\n", i, width, expect );
1464 ok( height == expect, "%d: got %d expect %d\n", i, height, expect );
1466 IImageList_Release( list );
1470 START_TEST(shelllink)
1472 HRESULT r;
1473 HMODULE hmod = GetModuleHandleA("shell32.dll");
1474 HMODULE huser32 = GetModuleHandleA("user32.dll");
1476 pSHILCreateFromPath = (void *)GetProcAddress(hmod, (LPSTR)28);
1477 pSHGetFolderLocation = (void *)GetProcAddress(hmod, "SHGetFolderLocation");
1478 pSHGetStockIconInfo = (void *)GetProcAddress(hmod, "SHGetStockIconInfo");
1479 pSHExtractIconsW = (void *)GetProcAddress(hmod, "SHExtractIconsW");
1480 pIsProcessDPIAware = (void *)GetProcAddress(huser32, "IsProcessDPIAware");
1482 r = CoInitialize(NULL);
1483 ok(r == S_OK, "CoInitialize failed (0x%08lx)\n", r);
1484 if (r != S_OK)
1485 return;
1487 test_get_set();
1488 test_load_save();
1489 test_datalink();
1490 test_shdefextracticon();
1491 test_GetIconLocation();
1492 test_SHGetStockIconInfo();
1493 test_SHExtractIcons();
1494 test_propertystore();
1495 test_ExtractIcon();
1496 test_ExtractAssociatedIcon();
1497 test_SHGetImageList();
1499 CoUninitialize();