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
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
)
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");
62 /* pSHSimpleIDListFromPathAW maps to A on non NT platforms */
63 if (pSHSimpleIDListFromPathAW
&& (GetVersion() & 0x80000000))
64 pidl
=pSHSimpleIDListFromPathAW(path
);
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
);
85 * Test manipulation of an IShellLink's properties.
88 static void test_get_set(void)
92 IShellLinkW
*slW
= NULL
;
93 char mypath
[MAX_PATH
];
94 char buffer
[INFOTIPSIZE
];
95 WIN32_FIND_DATAA finddata
;
96 LPITEMIDLIST pidl
, tmp_pidl
;
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
);
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 */
198 r
= IShellLinkA_GetIDList(sl
, &tmp_pidl
);
199 ok(r
== S_OK
, "GetIDList failed (0x%08lx)\n", r
);
204 strcpy(buffer
,"garbage");
205 ret
= SHGetPathFromIDListA(tmp_pidl
, buffer
);
206 ok(ret
, "SHGetPathFromIDListA failed\n");
208 ok(lstrcmpiA(buffer
,str
)==0, "GetIDList returned '%s'\n", buffer
);
212 pidl
=path_to_pidl(mypath
);
213 ok(pidl
!=NULL
, "path_to_pidl returned a NULL pidl\n");
217 LPITEMIDLIST second_pidl
;
219 r
= IShellLinkA_SetIDList(sl
, pidl
);
220 ok(r
== S_OK
, "SetIDList failed (0x%08lx)\n", r
);
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");
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 */
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
);
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 */
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
);
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 */
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
);
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
;
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
);
401 if (desc
->description
)
403 r
= IShellLinkA_SetDescription(sl
, desc
->description
);
404 lok(r
== S_OK
, "SetDescription failed (0x%08lx)\n", r
);
408 r
= IShellLinkA_SetWorkingDirectory(sl
, desc
->workdir
);
409 lok(r
== S_OK
, "SetWorkingDirectory failed (0x%08lx)\n", r
);
413 r
= IShellLinkA_SetPath(sl
, desc
->path
);
414 lok(SUCCEEDED(r
), "SetPath failed (0x%08lx)\n", r
);
418 r
= IShellLinkA_SetIDList(sl
, desc
->pidl
);
419 lok(r
== S_OK
, "SetIDList failed (0x%08lx)\n", r
);
423 r
= IShellLinkA_SetArguments(sl
, desc
->arguments
);
424 lok(r
== S_OK
, "SetArguments failed (0x%08lx)\n", r
);
428 r
= IShellLinkA_SetShowCmd(sl
, desc
->showcmd
);
429 lok(r
== S_OK
, "SetShowCmd failed (0x%08lx)\n", r
);
433 r
= IShellLinkA_SetIconLocation(sl
, desc
->icon
, desc
->icon_id
);
434 lok(r
== S_OK
, "SetIconLocation failed (0x%08lx)\n", r
);
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
);
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
));
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
));
497 IPersistFile_Release(pf
);
500 IShellLinkA_Release(sl
);
503 static void check_lnk_(int line
, const WCHAR
* path
, lnk_desc_t
* desc
, int todo
)
508 char buffer
[INFOTIPSIZE
];
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
);
517 r
= IShellLinkA_QueryInterface(sl
, &IID_IPersistFile
, (LPVOID
*)&pf
);
518 lok(r
== S_OK
, "no IID_IPersistFile (0x%08lx)\n", r
);
521 IShellLinkA_Release(sl
);
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
));
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
);
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
);
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",
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");
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",
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",
596 lok(i
==desc
->icon_id
, "GetIconLocation returned 0x%0x instead of 0x%0x\n",
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",
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";
620 char mypath
[MAX_PATH
];
621 char mydir
[MAX_PATH
];
622 char realpath
[MAX_PATH
];
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 */
645 check_lnk(lnkfile
, &desc
, 0x0);
647 /* Point a .lnk file to nonexistent files */
649 desc
.workdir
="c:\\Nonexitent\\work\\directory";
650 desc
.path
="c:\\nonexistent\\path";
654 desc
.icon
="c:\\nonexistent\\icon\\file";
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
, '\\');
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";
676 desc
.arguments
="/option1 /option2 \"Some string\"";
677 desc
.showcmd
=SW_SHOWNORMAL
;
681 create_lnk(lnkfile
, &desc
);
682 check_lnk(lnkfile
, &desc
, 0x0);
684 /* Test omitting .exe from an absolute path */
685 p
=strrchr(realpath
, '.');
689 desc
.description
="absolute path without .exe";
693 desc
.arguments
="/option1 /option2 \"Some string\"";
694 desc
.showcmd
=SW_SHOWNORMAL
;
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";
705 desc
.path
="rundll32.exe";
707 desc
.arguments
="/option1 /option2 \"Some string\"";
708 desc
.showcmd
=SW_SHOWNORMAL
;
712 create_lnk(lnkfile
, &desc
);
713 /* Check that link is created to proper location */
714 SearchPathA( NULL
, desc
.path
, NULL
, MAX_PATH
, realpath
, NULL
);
716 check_lnk(lnkfile
, &desc
, 0x0);
718 /* Test omitting .exe from a command on the path */
719 desc
.description
="command on path without .exe";
721 desc
.path
="rundll32";
723 desc
.arguments
="/option1 /option2 \"Some string\"";
724 desc
.showcmd
=SW_SHOWNORMAL
;
728 create_lnk(lnkfile
, &desc
);
729 /* Check that link is created to proper location */
730 SearchPathA( NULL
, "rundll32", NULL
, MAX_PATH
, realpath
, NULL
);
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
, '\\');
743 strcpy(mypath
, mydir
);
744 strcat(mypath
, "\\test.txt");
745 hf
= CreateFileA(mypath
, GENERIC_WRITE
, 0, NULL
,
746 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
749 /* Overwrite the existing lnk file and test link to an existing non-executable file */
750 desc
.description
="non-executable file";
755 desc
.showcmd
=SW_SHOWNORMAL
;
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";
775 desc
.showcmd
=SW_SHOWNORMAL
;
779 create_lnk(lnkfile
, &desc
);
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
);
793 strcpy(realpath
, mypath
);
795 p
=strrchr(mypath
, '.');
799 /* Try linking to the .bat file without the extension */
800 desc
.description
="batch file";
805 desc
.showcmd
=SW_SHOWNORMAL
;
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
;
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
);
891 r
= IShellLinkDataList_GetFlags( dl
, &flags
);
892 ok( r
== S_OK
, "GetFlags failed\n");
893 ok( flags
== 0, "GetFlags returned wrong flags\n");
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");
908 /* the following crashes */
909 IShellLinkDataList_GetFlags( dl
, NULL
);
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");
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");
928 IShellLinkDataList_Release( dl
);
929 IShellLinkW_Release( sl
);
932 static void test_shdefextracticon(void)
934 HICON hiconlarge
=NULL
, hiconsmall
=NULL
;
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
);
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)
959 char buffer
[INFOTIPSIZE
], mypath
[MAX_PATH
];
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
);
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
);
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
);
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
);
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
);
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
);
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
;
1048 /* not present before vista */
1049 if (!pSHGetStockIconInfo
)
1051 win_skip("SHGetStockIconInfo not available\n");
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
);
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
);
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};
1133 if (!pSHExtractIconsW
)
1135 win_skip("SHExtractIconsW not available\n");
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)
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
);
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
);
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
];
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
);
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");
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 */
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
);
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");
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
];
1337 hicon
= ExtractAssociatedIconA(NULL
, pathA
, &index
);
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
);
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
);
1354 /* valid dll name, invalid index */
1356 strcpy(pathA
, "user32.dll");
1357 hicon
= ExtractAssociatedIconA(NULL
, pathA
, &index
);
1358 CharLowerBuffA(pathA
, strlen(pathA
));
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
);
1366 /* associated icon */
1368 strcpy(pathA
, "dummy.exe");
1369 hicon
= ExtractAssociatedIconA(NULL
, pathA
, &index
);
1370 CharLowerBuffA(pathA
, strlen(pathA
));
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
);
1379 static int get_shell_icon_size(void)
1382 DWORD value
= 32, size
= sizeof(buf
), type
;
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
);
1394 static void test_SHGetImageList(void)
1397 IImageList
*list
, *list2
;
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
);
1444 if (dpi_aware
) expect
= GetSystemMetrics( SM_CXICON
);
1445 else expect
= get_shell_icon_size();
1448 if (dpi_aware
) expect
= GetSystemMetrics( SM_CXICON
) / 2;
1449 else expect
= GetSystemMetrics( SM_CXSMICON
);
1451 case SHIL_EXTRALARGE
:
1452 expect
= (GetSystemMetrics( SM_CXICON
) * 3) / 2;
1455 expect
= GetSystemMetrics( SM_CXSMICON
);
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
)
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
);
1490 test_shdefextracticon();
1491 test_GetIconLocation();
1492 test_SHGetStockIconInfo();
1493 test_SHExtractIcons();
1494 test_propertystore();
1496 test_ExtractAssociatedIcon();
1497 test_SHGetImageList();