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