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