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