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