1 /* 2 * PROJECT: ReactOS api tests 3 * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory 4 * PURPOSE: Test for CShellLink 5 * PROGRAMMER: Andreas Maier 6 */ 7 8 #include "shelltest.h" 9 10 #define NDEBUG 11 #include <debug.h> 12 #include <shellutils.h> 13 14 /* Test IShellLink::SetPath with environment-variables, existing, non-existing, ...*/ 15 typedef struct 16 { 17 PCWSTR pathIn; 18 HRESULT hrSetPath; 19 20 /* Test 1 - hrGetPathX = IShellLink::GetPath(pathOutX, ... , flagsX); */ 21 PCWSTR pathOut1; 22 DWORD flags1; 23 HRESULT hrGetPath1; 24 BOOL expandPathOut1; 25 26 /* Test 2 */ 27 PCWSTR pathOut2; 28 DWORD flags2; 29 HRESULT hrGetPath2; 30 BOOL expandPathOut2; 31 } TEST_SHELL_LINK_DEF; 32 33 static TEST_SHELL_LINK_DEF linkTestList[] = 34 { 35 { 36 L"%comspec%", S_OK, 37 L"%comspec%", SLGP_SHORTPATH, S_OK, TRUE, 38 L"%comspec%", SLGP_RAWPATH, S_OK, FALSE 39 }, 40 { 41 L"%anyvar%", E_INVALIDARG, 42 L"", SLGP_SHORTPATH, S_FALSE, FALSE, 43 L"", SLGP_RAWPATH, S_FALSE, FALSE 44 }, 45 { 46 L"%anyvar%%comspec%", S_OK, 47 L"c:\\%anyvar%%comspec%", SLGP_SHORTPATH, S_OK, TRUE, 48 L"%anyvar%%comspec%", SLGP_RAWPATH, S_OK, FALSE 49 }, 50 { 51 L"%temp%", S_OK, 52 L"%temp%", SLGP_SHORTPATH, S_OK, TRUE, 53 L"%temp%", SLGP_RAWPATH, S_OK, FALSE 54 }, 55 { 56 L"%shell%", S_OK, 57 L"%systemroot%\\system32\\%shell%", SLGP_SHORTPATH, S_OK, TRUE, 58 L"%shell%", SLGP_RAWPATH, S_OK, FALSE 59 }, 60 { 61 L"u:\\anypath\\%anyvar%", S_OK, 62 L"u:\\anypath\\%anyvar%", SLGP_SHORTPATH, S_OK, TRUE, 63 L"u:\\anypath\\%anyvar%", SLGP_RAWPATH, S_OK, FALSE 64 }, 65 { 66 L"c:\\temp", S_OK, 67 L"c:\\temp", SLGP_SHORTPATH, S_OK, FALSE, 68 L"c:\\temp", SLGP_RAWPATH, S_OK, FALSE 69 }, 70 { 71 L"cmd.exe", S_OK, 72 L"%comspec%", SLGP_SHORTPATH, S_OK, TRUE, 73 L"%comspec%", SLGP_RAWPATH, S_OK, TRUE 74 }, 75 { 76 L"%systemroot%\\non-existent-file", S_OK, 77 L"%systemroot%\\non-existent-file", SLGP_SHORTPATH, S_OK, TRUE, 78 L"%systemroot%\\non-existent-file", SLGP_RAWPATH, S_OK, FALSE 79 }, 80 { 81 L"c:\\non-existent-path\\non-existent-file", S_OK, 82 L"c:\\non-existent-path\\non-existent-file", SLGP_SHORTPATH, S_OK, FALSE, 83 L"c:\\non-existent-path\\non-existent-file", SLGP_RAWPATH, S_OK, FALSE 84 }, 85 { 86 L"non-existent-file", E_INVALIDARG, 87 L"", SLGP_SHORTPATH, S_FALSE, FALSE, 88 L"", SLGP_RAWPATH, S_FALSE, FALSE 89 }, 90 }; 91 92 static 93 VOID 94 test_checklinkpath(UINT i, TEST_SHELL_LINK_DEF* testDef) 95 { 96 static WCHAR evVar[MAX_PATH]; 97 98 HRESULT hr, expectedHr; 99 WCHAR wPathOut[MAX_PATH]; 100 BOOL expandPathOut; 101 PCWSTR expectedPathOut; 102 CComPtr<IShellLinkW> psl; 103 UINT i1; 104 DWORD flags; 105 106 hr = CoCreateInstance(CLSID_ShellLink, 107 NULL, 108 CLSCTX_INPROC_SERVER, 109 IID_PPV_ARG(IShellLinkW, &psl)); 110 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr); 111 if (FAILED(hr)) 112 { 113 skip("Could not instantiate CShellLink\n"); 114 return; 115 } 116 117 hr = psl->SetPath(testDef->pathIn); 118 ok(hr == testDef->hrSetPath, "IShellLink::SetPath(%d), got hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrSetPath); 119 120 expectedPathOut = NULL; 121 for (i1 = 0; i1 <= 1; i1++) 122 { 123 if (i1 == 0) /* Usually SLGP_SHORTPATH */ 124 { 125 flags = testDef->flags1; 126 expandPathOut = testDef->expandPathOut1; 127 expectedPathOut = testDef->pathOut1; 128 expectedHr = testDef->hrGetPath1; 129 } 130 else // if (i1 == 1) /* Usually SLGP_RAWPATH */ 131 { 132 flags = testDef->flags2; 133 expandPathOut = testDef->expandPathOut2; 134 expectedPathOut = testDef->pathOut2; 135 expectedHr = testDef->hrGetPath2; 136 } 137 138 /* Patch some variables */ 139 if (expandPathOut) 140 { 141 ExpandEnvironmentStringsW(expectedPathOut, evVar, _countof(evVar)); 142 DPRINT("** %S **\n",evVar); 143 expectedPathOut = evVar; 144 } 145 146 hr = psl->GetPath(wPathOut, _countof(wPathOut), NULL, flags); 147 ok(hr == expectedHr, 148 "IShellLink::GetPath(%d), flags 0x%lx, got hr = 0x%lx, expected 0x%lx\n", 149 i, flags, hr, expectedHr); 150 ok(wcsicmp(wPathOut, expectedPathOut) == 0, 151 "IShellLink::GetPath(%d), flags 0x%lx, in %S, got %S, expected %S\n", 152 i, flags, testDef->pathIn, wPathOut, expectedPathOut); 153 } 154 } 155 156 static 157 VOID 158 TestShellLink(void) 159 { 160 UINT i; 161 162 /* Needed for test */ 163 SetEnvironmentVariableW(L"shell", L"cmd.exe"); 164 165 for (i = 0; i < _countof(linkTestList); ++i) 166 { 167 DPRINT("IShellLink-Test(%d): %S\n", i, linkTestList[i].pathIn); 168 test_checklinkpath(i, &linkTestList[i]); 169 } 170 171 SetEnvironmentVariableW(L"shell",NULL); 172 } 173 174 static 175 VOID 176 TestDescription(void) 177 { 178 HRESULT hr; 179 CComPtr<IShellLinkW> psl; 180 WCHAR buffer[64]; 181 PCWSTR testDescription = L"This is a test description"; 182 183 /* Test SetDescription */ 184 hr = CoCreateInstance(CLSID_ShellLink, 185 NULL, 186 CLSCTX_INPROC_SERVER, 187 IID_PPV_ARG(IShellLinkW, &psl)); 188 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr); 189 if (FAILED(hr)) 190 { 191 skip("Could not instantiate CShellLink\n"); 192 return; 193 } 194 195 memset(buffer, 0x55, sizeof(buffer)); 196 hr = psl->GetDescription(buffer, RTL_NUMBER_OF(buffer)); 197 ok(hr == S_OK, "IShellLink::GetDescription returned hr = 0x%lx\n", hr); 198 ok(buffer[0] == 0, "buffer[0] = %x\n", buffer[0]); 199 ok(buffer[1] == 0x5555, "buffer[1] = %x\n", buffer[1]); 200 201 hr = psl->SetDescription(testDescription); 202 ok(hr == S_OK, "IShellLink::SetDescription returned hr = 0x%lx\n", hr); 203 204 memset(buffer, 0x55, sizeof(buffer)); 205 hr = psl->GetDescription(buffer, RTL_NUMBER_OF(buffer)); 206 ok(hr == S_OK, "IShellLink::GetDescription returned hr = 0x%lx\n", hr); 207 ok(buffer[wcslen(testDescription)] == 0, "buffer[n] = %x\n", buffer[wcslen(testDescription)]); 208 ok(buffer[wcslen(testDescription) + 1] == 0x5555, "buffer[n+1] = %x\n", buffer[wcslen(testDescription) + 1]); 209 ok(!wcscmp(buffer, testDescription), "buffer = '%ls'\n", buffer); 210 211 hr = psl->SetDescription(NULL); 212 ok(hr == S_OK, "IShellLink::SetDescription returned hr = 0x%lx\n", hr); 213 214 memset(buffer, 0x55, sizeof(buffer)); 215 hr = psl->GetDescription(buffer, RTL_NUMBER_OF(buffer)); 216 ok(hr == S_OK, "IShellLink::GetDescription returned hr = 0x%lx\n", hr); 217 ok(buffer[0] == 0, "buffer[0] = %x\n", buffer[0]); 218 ok(buffer[1] == 0x5555, "buffer[1] = %x\n", buffer[1]); 219 } 220 221 222 /* Test IShellLink::Get/SetIconLocation and IExtractIcon::GetIconLocation */ 223 typedef struct 224 { 225 PCWSTR FilePath; 226 227 /* Expected results */ 228 HRESULT hrDefIcon; // Return value for GIL_DEFAULTICON 229 HRESULT hrForShrt; // Return value for GIL_FORSHORTCUT 230 /* Return values for GIL_FORSHELL */ 231 HRESULT hrForShell; 232 PCWSTR IconPath; 233 UINT Flags; 234 } TEST_SHELL_ICON; 235 236 static TEST_SHELL_ICON ShIconTests[] = 237 { 238 /* Executable with icons */ 239 {L"%SystemRoot%\\system32\\cmd.exe", S_FALSE, E_INVALIDARG, 240 S_OK, L"%SystemRoot%\\system32\\cmd.exe", GIL_NOTFILENAME | GIL_PERINSTANCE}, 241 242 /* Executable without icon */ 243 {L"%SystemRoot%\\system32\\autochk.exe", S_FALSE, E_INVALIDARG, 244 S_OK, L"%SystemRoot%\\system32\\autochk.exe", GIL_NOTFILENAME | GIL_PERINSTANCE}, 245 246 /* Existing file */ 247 {L"%SystemRoot%\\system32\\shell32.dll", S_FALSE, E_INVALIDARG, 248 S_OK, L"*", GIL_NOTFILENAME | GIL_PERCLASS}, 249 250 /* Non-existing files */ 251 {L"%SystemRoot%\\non-existent-file.sdf", S_FALSE, E_INVALIDARG, 252 S_OK, L"*", GIL_NOTFILENAME | GIL_PERCLASS}, 253 {L"c:\\non-existent-path\\non-existent-file.sdf", S_FALSE, E_INVALIDARG, 254 S_OK, L"*", GIL_NOTFILENAME | GIL_PERCLASS}, 255 }; 256 257 static 258 VOID 259 test_iconlocation(UINT i, TEST_SHELL_ICON* testDef) 260 { 261 HRESULT hr; 262 CComPtr<IShellLinkW> psl; 263 CComPtr<IExtractIconW> pei; 264 INT iIcon; 265 UINT wFlags; 266 PCWSTR pszExplorer = L"%SystemRoot%\\explorer.exe"; 267 WCHAR szPath[MAX_PATH]; 268 WCHAR szPath2[MAX_PATH]; 269 270 hr = CoCreateInstance(CLSID_ShellLink, 271 NULL, 272 CLSCTX_INPROC_SERVER, 273 IID_PPV_ARG(IShellLinkW, &psl)); 274 ok(hr == S_OK, "CoCreateInstance, hr = 0x%lx\n", hr); 275 if (FAILED(hr)) 276 { 277 skip("Could not instantiate CShellLink\n"); 278 return; 279 } 280 281 /* Set the path to a file */ 282 ExpandEnvironmentStringsW(testDef->FilePath, szPath, _countof(szPath)); 283 hr = psl->SetPath(szPath); 284 ok(hr == S_OK, "IShellLink::SetPath failed, hr = 0x%lx\n", hr); 285 286 /* 287 * This test shows that this does not imply that the icon is automatically 288 * set and be retrieved naively by a call to IShellLink::GetIconLocation. 289 */ 290 iIcon = 0xdeadbeef; 291 wcscpy(szPath, L"garbage"); 292 hr = psl->GetIconLocation(szPath, _countof(szPath), &iIcon); 293 ok(hr == S_OK, "IShellLink::GetIconLocation(%d) failed, hr = 0x%lx\n", i, hr); 294 ok(*szPath == L'\0', "IShellLink::GetIconLocation(%d) returned '%S'\n", i, szPath); 295 ok(iIcon == 0, "IShellLink::GetIconLocation(%d) returned %d, expected %d\n", i, iIcon, 0); 296 297 /* Try to grab the IExtractIconW interface */ 298 hr = psl->QueryInterface(IID_PPV_ARG(IExtractIconW, &pei)); 299 ok(hr == S_OK, "IShellLink::QueryInterface(IExtractIconW)(%d) failed, hr = 0x%lx\n", i, hr); 300 if (!pei) 301 { 302 win_skip("No IExtractIconW interface\n"); 303 return; 304 } 305 306 iIcon = wFlags = 0xdeadbeef; 307 wcscpy(szPath, L"garbage"); 308 hr = pei->GetIconLocation(GIL_DEFAULTICON, szPath, _countof(szPath), &iIcon, &wFlags); 309 ok(hr == testDef->hrDefIcon, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrDefIcon); 310 ok(*szPath == L'\0', "IExtractIcon::GetIconLocation(%d) returned '%S'\n", i, szPath); 311 // ok(iIcon == 0, "IExtractIcon::GetIconLocation(%d) returned %d\n", i, iIcon); 312 313 iIcon = wFlags = 0xdeadbeef; 314 wcscpy(szPath, L"garbage"); 315 hr = pei->GetIconLocation(GIL_FORSHORTCUT, szPath, _countof(szPath), &iIcon, &wFlags); 316 ok(hr == testDef->hrForShrt, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShrt); 317 // Here, both szPath and iIcon are untouched... 318 319 iIcon = wFlags = 0xdeadbeef; 320 wcscpy(szPath, L"garbage"); 321 hr = pei->GetIconLocation(GIL_FORSHELL, szPath, _countof(szPath), &iIcon, &wFlags); 322 ok(hr == testDef->hrForShell, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShell); 323 ok(wFlags == testDef->Flags, "IExtractIcon::GetIconLocation(%d) returned wFlags = 0x%x, expected 0x%x\n", i, wFlags, testDef->Flags); 324 /* 325 * Actually, even if wFlags specifies GIL_NOTFILENAME, a correct file name is returned 326 * for executables only (at least...), otherwise we can get an asterix '*'. 327 */ 328 ExpandEnvironmentStringsW(testDef->IconPath, szPath2, _countof(szPath2)); 329 ok(_wcsicmp(szPath2, szPath) == 0, "IExtractIcon::GetIconLocation(%d) returned '%S', expected '%S'\n", i, szPath, szPath2); 330 331 // ok(*szPath == L'\0', "IExtractIcon::GetIconLocation returned '%S'\n", szPath); 332 // ok(iIcon == 0, "IExtractIcon::GetIconLocation returned %d\n", iIcon); 333 // ok(FALSE, "hr = 0x%lx, szPath = '%S', iIcon = %d, wFlags = %d\n", hr, szPath, iIcon, wFlags); 334 335 336 /* 337 * Now we test what happens when we explicitly set an icon to the shortcut. 338 * Note that actually, SetIconLocation() does not verify whether the file 339 * really exists. 340 */ 341 hr = psl->SetIconLocation(pszExplorer, 1); 342 ok(hr == S_OK, "IShellLink::SetIconLocation(%d) failed, hr = 0x%lx\n", i, hr); 343 344 /* 345 * First, we call IShellLink::GetIconLocation. We retrieve 346 * exactly what we specified with SetIconLocation. 347 */ 348 iIcon = 0xdeadbeef; 349 wcscpy(szPath, L"garbage"); 350 hr = psl->GetIconLocation(szPath, _countof(szPath), &iIcon); 351 ok(hr == S_OK, "IShellLink::GetIconLocation(%d) failed, hr = 0x%lx\n", i, hr); 352 ok(wcscmp(szPath, pszExplorer) == 0, "IShellLink::GetIconLocation(%d) returned '%S', expected '%S'\n", i, szPath, pszExplorer); 353 ok(iIcon == 1, "IShellLink::GetIconLocation(%d) returned %d, expected %d\n", i, iIcon, 1); 354 355 /* 356 * Now we test what happens with IExtractIcon::GetIconLocation. 357 * We see that it retrieves the icon of the shortcut's underlying file. 358 */ 359 iIcon = wFlags = 0xdeadbeef; 360 wcscpy(szPath, L"garbage"); 361 hr = pei->GetIconLocation(GIL_DEFAULTICON, szPath, _countof(szPath), &iIcon, &wFlags); 362 ok(hr == testDef->hrDefIcon, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrDefIcon); 363 ok(*szPath == L'\0', "IExtractIcon::GetIconLocation(%d) returned '%S'\n", i, szPath); 364 // ok(iIcon == 0, "IExtractIcon::GetIconLocation(%d) returned %d\n", i, iIcon); 365 366 iIcon = wFlags = 0xdeadbeef; 367 wcscpy(szPath, L"garbage"); 368 hr = pei->GetIconLocation(GIL_FORSHORTCUT, szPath, _countof(szPath), &iIcon, &wFlags); 369 ok(hr == testDef->hrForShrt, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShrt); 370 // Here, both szPath and iIcon are untouched... 371 372 iIcon = wFlags = 0xdeadbeef; 373 wcscpy(szPath, L"garbage"); 374 hr = pei->GetIconLocation(GIL_FORSHELL, szPath, _countof(szPath), &iIcon, &wFlags); 375 ok(hr == testDef->hrForShell, "IExtractIcon::GetIconLocation(%d) returned hr = 0x%lx, expected 0x%lx\n", i, hr, testDef->hrForShell); 376 ok(wFlags == testDef->Flags, "IExtractIcon::GetIconLocation(%d) returned wFlags = 0x%x, expected 0x%x\n", i, wFlags, testDef->Flags); 377 /* 378 * Actually, even if wFlags specifies GIL_NOTFILENAME, a correct file name is returned 379 * for executables only (at least...), otherwise we can get an asterix '*'. 380 */ 381 ExpandEnvironmentStringsW(testDef->IconPath, szPath2, _countof(szPath2)); 382 ok(_wcsicmp(szPath2, szPath) == 0, "IExtractIcon::GetIconLocation(%d) returned '%S', expected '%S'\n", i, szPath, szPath2); 383 384 // ok(*szPath == L'\0', "IExtractIcon::GetIconLocation returned '%S'\n", szPath); 385 // ok(iIcon == 0, "IExtractIcon::GetIconLocation returned %d\n", iIcon); 386 // ok(FALSE, "hr = 0x%lx, szPath = '%S', iIcon = %d, wFlags = %d\n", hr, szPath, iIcon, wFlags); 387 } 388 389 static 390 VOID 391 TestIconLocation(void) 392 { 393 UINT i; 394 395 for (i = 0; i < _countof(ShIconTests); ++i) 396 { 397 test_iconlocation(i, &ShIconTests[i]); 398 } 399 } 400 401 402 START_TEST(CShellLink) 403 { 404 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 405 406 TestShellLink(); 407 TestDescription(); 408 TestIconLocation(); 409 410 CoUninitialize(); 411 } 412