1 /* 2 * 3 * Copyright 1997 Marcus Meissner 4 * Copyright 1998 Juergen Schmied 5 * Copyright 2005 Mike McCormack 6 * Copyright 2009 Andrew Hill 7 * Copyright 2013 Dominik Hornung 8 * Copyright 2017 Hermes Belusca-Maito 9 * Copyright 2018 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 10 * 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Lesser General Public 13 * License as published by the Free Software Foundation; either 14 * version 2.1 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public 22 * License along with this library; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 24 * 25 * NOTES 26 * Nearly complete information about the binary formats 27 * of .lnk files available at http://www.wotsit.org 28 * 29 * You can use winedump to examine the contents of a link file: 30 * winedump lnk sc.lnk 31 * 32 * MSI advertised shortcuts are totally undocumented. They provide an 33 * icon for a program that is not yet installed, and invoke MSI to 34 * install the program when the shortcut is clicked on. They are 35 * created by passing a special string to SetPath, and the information 36 * in that string is parsed an stored. 37 */ 38 /* 39 * In the following is listed more documentation about the Shell Link file format, 40 * as well as its interface. 41 * 42 * General introduction about "Shell Links" (MSDN): 43 * https://msdn.microsoft.com/en-us/library/windows/desktop/bb776891(v=vs.85).aspx 44 * 45 * 46 * Details of the file format: 47 * 48 * - Official MSDN documentation "[MS-SHLLINK]: Shell Link (.LNK) Binary File Format": 49 * https://msdn.microsoft.com/en-us/library/dd871305.aspx 50 * 51 * - Forensics: 52 * http://forensicswiki.org/wiki/LNK 53 * http://computerforensics.parsonage.co.uk/downloads/TheMeaningofLIFE.pdf 54 * https://ithreats.files.wordpress.com/2009/05/lnk_the_windows_shortcut_file_format.pdf 55 * https://github.com/libyal/liblnk/blob/master/documentation/Windows%20Shortcut%20File%20(LNK)%20format.asciidoc 56 * 57 * - List of possible shell link header flags (SHELL_LINK_DATA_FLAGS enumeration): 58 * https://msdn.microsoft.com/en-us/library/windows/desktop/bb762540(v=vs.85).aspx 59 * https://msdn.microsoft.com/en-us/library/dd891314.aspx 60 * 61 * 62 * In addition to storing its target by using a PIDL, a shell link file also 63 * stores metadata to make the shell able to track the link target, in situations 64 * where the link target is moved amongst local or network directories, or moved 65 * to different volumes. For this, two structures are used: 66 * 67 * - The first and oldest one (from NewShell/WinNT4) is the "LinkInfo" structure, 68 * stored in a serialized manner at the beginning of the shell link file: 69 * https://msdn.microsoft.com/en-us/library/dd871404.aspx 70 * The official API for manipulating this is located in LINKINFO.DLL . 71 * 72 * - The second, more recent one, is an extra binary block appended to the 73 * extra-data list of the shell link file: this is the "TrackerDataBlock": 74 * https://msdn.microsoft.com/en-us/library/dd891376.aspx 75 * Its purpose is for link tracking, and works in coordination with the 76 * "Distributed Link Tracking" service ('TrkWks' client, 'TrkSvr' server). 77 * See a detailed explanation at: 78 * http://www.serverwatch.com/tutorials/article.php/1476701/Searching-for-the-Missing-Link-Distributed-Link-Tracking.htm 79 * 80 * 81 * MSI installations most of the time create so-called "advertised shortcuts". 82 * They provide an icon for a program that may not be installed yet, and invoke 83 * MSI to install the program when the shortcut is opened (resolved). 84 * The philosophy of this approach is explained in detail inside the MSDN article 85 * "Application Resiliency: Unlock the Hidden Features of Windows Installer" 86 * (by Michael Sanford), here: 87 * https://msdn.microsoft.com/en-us/library/aa302344.aspx 88 * 89 * This functionality is implemented by adding a binary "Darwin" data block 90 * of type "EXP_DARWIN_LINK", signature EXP_DARWIN_ID_SIG == 0xA0000006, 91 * to the shell link file: 92 * https://msdn.microsoft.com/en-us/library/dd871369.aspx 93 * or, this could be done more simply by specifying a special link target path 94 * with the IShellLink::SetPath() function. Defining the following GUID: 95 * SHELL32_AdvtShortcutComponent = "::{9db1186e-40df-11d1-aa8c-00c04fb67863}:" 96 * setting a target of the form: 97 * "::{SHELL32_AdvtShortcutComponent}:<MSI_App_ID>" 98 * would automatically create the necessary binary block. 99 * 100 * With that, the target of the shortcut now becomes the MSI data. The latter 101 * is parsed from MSI and retrieved by the shell that then can run the program. 102 * 103 * This MSI functionality, dubbed "link blessing", actually originates from an 104 * older technology introduced in Internet Explorer 3 (and now obsolete since 105 * Internet Explorer 7), called "MS Internet Component Download (MSICD)", see 106 * this MSDN introductory article: 107 * https://msdn.microsoft.com/en-us/library/aa741198(v=vs.85).aspx 108 * and leveraged in Internet Explorer 4 with "Software Update Channels", see: 109 * https://msdn.microsoft.com/en-us/library/aa740931(v=vs.85).aspx 110 * Applications supporting this technology could present shell links having 111 * a special target, see subsection "Modifying the Shortcut" in the article: 112 * https://msdn.microsoft.com/en-us/library/aa741201(v=vs.85).aspx#pub_shor 113 * 114 * Similarly as for the MSI shortcuts, these MSICD shortcuts are created by 115 * specifying a special link target path with the IShellLink::SetPath() function, 116 * defining the following GUID: 117 * SHELL32_AdvtShortcutProduct = "::{9db1186f-40df-11d1-aa8c-00c04fb67863}:" 118 * and setting a target of the form: 119 * "::{SHELL32_AdvtShortcutProduct}:<AppName>::<Path>" . 120 * A tool, called "blesslnk.exe", was also provided for automatizing the process; 121 * its ReadMe can be found in the (now outdated) MS "Internet Client SDK" (INetSDK, 122 * for MS Windows 95 and NT), whose contents can be read at: 123 * http://www.msfn.org/board/topic/145352-new-windows-lnk-vulnerability/?page=4#comment-944223 124 * The MS INetSDK can be found at: 125 * https://web.archive.org/web/20100924000013/http://support.microsoft.com/kb/177877 126 * 127 * Internally the shell link target of these MSICD shortcuts is converted into 128 * a binary data block of a type similar to Darwin / "EXP_DARWIN_LINK", but with 129 * a different signature EXP_LOGO3_ID_SIG == 0xA0000007 . Such shell links are 130 * called "Logo3" shortcuts. They were evoked in this user comment in "The Old 131 * New Thing" blog: 132 * https://blogs.msdn.microsoft.com/oldnewthing/20121210-00/?p=5883#comment-1025083 133 * 134 * The shell exports the API 'SoftwareUpdateMessageBox' (in shdocvw.dll) that 135 * displays a message when an update for an application supporting this 136 * technology is available. 137 * 138 */ 139 140 #include "precomp.h" 141 142 #include <appmgmt.h> 143 144 WINE_DEFAULT_DEBUG_CHANNEL(shell); 145 146 #define SHLINK_LOCAL 0 147 #define SHLINK_REMOTE 1 148 149 /* link file formats */ 150 151 #include "pshpack1.h" 152 153 struct LOCATION_INFO 154 { 155 DWORD dwTotalSize; 156 DWORD dwHeaderSize; 157 DWORD dwFlags; 158 DWORD dwVolTableOfs; 159 DWORD dwLocalPathOfs; 160 DWORD dwNetworkVolTableOfs; 161 DWORD dwFinalPathOfs; 162 }; 163 164 struct LOCAL_VOLUME_INFO 165 { 166 DWORD dwSize; 167 DWORD dwType; 168 DWORD dwVolSerial; 169 DWORD dwVolLabelOfs; 170 }; 171 172 struct volume_info 173 { 174 DWORD type; 175 DWORD serial; 176 WCHAR label[12]; /* assume 8.3 */ 177 }; 178 179 #include "poppack.h" 180 181 /* IShellLink Implementation */ 182 183 static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath); 184 185 /* strdup on the process heap */ 186 static LPWSTR __inline HEAP_strdupAtoW(HANDLE heap, DWORD flags, LPCSTR str) 187 { 188 INT len; 189 LPWSTR p; 190 191 assert(str); 192 193 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); 194 p = (LPWSTR)HeapAlloc(heap, flags, len * sizeof(WCHAR)); 195 if (!p) 196 return p; 197 MultiByteToWideChar(CP_ACP, 0, str, -1, p, len); 198 return p; 199 } 200 201 static LPWSTR __inline strdupW(LPCWSTR src) 202 { 203 LPWSTR dest; 204 if (!src) return NULL; 205 dest = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(src) + 1) * sizeof(WCHAR)); 206 if (dest) 207 wcscpy(dest, src); 208 return dest; 209 } 210 211 // TODO: Use it for constructor & destructor too 212 VOID CShellLink::Reset() 213 { 214 ILFree(m_pPidl); 215 m_pPidl = NULL; 216 217 HeapFree(GetProcessHeap(), 0, m_sPath); 218 m_sPath = NULL; 219 ZeroMemory(&volume, sizeof(volume)); 220 221 HeapFree(GetProcessHeap(), 0, m_sDescription); 222 m_sDescription = NULL; 223 HeapFree(GetProcessHeap(), 0, m_sPathRel); 224 m_sPathRel = NULL; 225 HeapFree(GetProcessHeap(), 0, m_sWorkDir); 226 m_sWorkDir = NULL; 227 HeapFree(GetProcessHeap(), 0, m_sArgs); 228 m_sArgs = NULL; 229 HeapFree(GetProcessHeap(), 0, m_sIcoPath); 230 m_sIcoPath = NULL; 231 232 m_bRunAs = FALSE; 233 m_bDirty = FALSE; 234 235 if (m_pDBList) 236 SHFreeDataBlockList(m_pDBList); 237 m_pDBList = NULL; 238 239 /**/sProduct = sComponent = NULL;/**/ 240 } 241 242 CShellLink::CShellLink() 243 { 244 m_Header.dwSize = sizeof(m_Header); 245 m_Header.clsid = CLSID_ShellLink; 246 m_Header.dwFlags = 0; 247 248 m_Header.dwFileAttributes = 0; 249 ZeroMemory(&m_Header.ftCreationTime, sizeof(m_Header.ftCreationTime)); 250 ZeroMemory(&m_Header.ftLastAccessTime, sizeof(m_Header.ftLastAccessTime)); 251 ZeroMemory(&m_Header.ftLastWriteTime, sizeof(m_Header.ftLastWriteTime)); 252 m_Header.nFileSizeLow = 0; 253 254 m_Header.nIconIndex = 0; 255 m_Header.nShowCommand = SW_SHOWNORMAL; 256 m_Header.wHotKey = 0; 257 258 m_pPidl = NULL; 259 260 m_sPath = NULL; 261 ZeroMemory(&volume, sizeof(volume)); 262 263 m_sDescription = NULL; 264 m_sPathRel = NULL; 265 m_sWorkDir = NULL; 266 m_sArgs = NULL; 267 m_sIcoPath = NULL; 268 m_bRunAs = FALSE; 269 m_bDirty = FALSE; 270 m_pDBList = NULL; 271 272 m_sLinkPath = NULL; 273 m_iIdOpen = -1; 274 275 /**/sProduct = sComponent = NULL;/**/ 276 } 277 278 CShellLink::~CShellLink() 279 { 280 TRACE("-- destroying IShellLink(%p)\n", this); 281 282 ILFree(m_pPidl); 283 284 HeapFree(GetProcessHeap(), 0, m_sPath); 285 286 HeapFree(GetProcessHeap(), 0, m_sDescription); 287 HeapFree(GetProcessHeap(), 0, m_sPathRel); 288 HeapFree(GetProcessHeap(), 0, m_sWorkDir); 289 HeapFree(GetProcessHeap(), 0, m_sArgs); 290 HeapFree(GetProcessHeap(), 0, m_sIcoPath); 291 HeapFree(GetProcessHeap(), 0, m_sLinkPath); 292 SHFreeDataBlockList(m_pDBList); 293 } 294 295 HRESULT STDMETHODCALLTYPE CShellLink::GetClassID(CLSID *pclsid) 296 { 297 TRACE("%p %p\n", this, pclsid); 298 299 if (pclsid == NULL) 300 return E_POINTER; 301 *pclsid = CLSID_ShellLink; 302 return S_OK; 303 } 304 305 /************************************************************************ 306 * IPersistStream_IsDirty (IPersistStream) 307 */ 308 HRESULT STDMETHODCALLTYPE CShellLink::IsDirty() 309 { 310 TRACE("(%p)\n", this); 311 return (m_bDirty ? S_OK : S_FALSE); 312 } 313 314 HRESULT STDMETHODCALLTYPE CShellLink::Load(LPCOLESTR pszFileName, DWORD dwMode) 315 { 316 TRACE("(%p, %s, %x)\n", this, debugstr_w(pszFileName), dwMode); 317 318 if (dwMode == 0) 319 dwMode = STGM_READ | STGM_SHARE_DENY_WRITE; 320 321 CComPtr<IStream> stm; 322 HRESULT hr = SHCreateStreamOnFileW(pszFileName, dwMode, &stm); 323 if (SUCCEEDED(hr)) 324 { 325 HeapFree(GetProcessHeap(), 0, m_sLinkPath); 326 m_sLinkPath = strdupW(pszFileName); 327 hr = Load(stm); 328 ShellLink_UpdatePath(m_sPathRel, pszFileName, m_sWorkDir, &m_sPath); 329 m_bDirty = FALSE; 330 } 331 TRACE("-- returning hr %08x\n", hr); 332 return hr; 333 } 334 335 HRESULT STDMETHODCALLTYPE CShellLink::Save(LPCOLESTR pszFileName, BOOL fRemember) 336 { 337 TRACE("(%p)->(%s)\n", this, debugstr_w(pszFileName)); 338 339 if (!pszFileName) 340 return E_FAIL; 341 342 CComPtr<IStream> stm; 343 HRESULT hr = SHCreateStreamOnFileW(pszFileName, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &stm); 344 if (SUCCEEDED(hr)) 345 { 346 hr = Save(stm, FALSE); 347 348 if (SUCCEEDED(hr)) 349 { 350 if (m_sLinkPath) 351 HeapFree(GetProcessHeap(), 0, m_sLinkPath); 352 353 m_sLinkPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(pszFileName) + 1) * sizeof(WCHAR)); 354 if (m_sLinkPath) 355 wcscpy(m_sLinkPath, pszFileName); 356 357 m_bDirty = FALSE; 358 } 359 else 360 { 361 DeleteFileW(pszFileName); 362 WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName)); 363 } 364 } 365 366 return hr; 367 } 368 369 HRESULT STDMETHODCALLTYPE CShellLink::SaveCompleted(LPCOLESTR pszFileName) 370 { 371 FIXME("(%p)->(%s)\n", this, debugstr_w(pszFileName)); 372 return S_OK; 373 } 374 375 HRESULT STDMETHODCALLTYPE CShellLink::GetCurFile(LPOLESTR *ppszFileName) 376 { 377 *ppszFileName = NULL; 378 379 if (!m_sLinkPath) 380 { 381 /* IPersistFile::GetCurFile called before IPersistFile::Save */ 382 return S_FALSE; 383 } 384 385 *ppszFileName = (LPOLESTR)CoTaskMemAlloc((wcslen(m_sLinkPath) + 1) * sizeof(WCHAR)); 386 if (!*ppszFileName) 387 { 388 /* out of memory */ 389 return E_OUTOFMEMORY; 390 } 391 392 /* copy last saved filename */ 393 wcscpy(*ppszFileName, m_sLinkPath); 394 395 return S_OK; 396 } 397 398 static HRESULT Stream_LoadString(IStream* stm, BOOL unicode, LPWSTR *pstr) 399 { 400 TRACE("%p\n", stm); 401 402 USHORT len; 403 DWORD count = 0; 404 HRESULT hr = stm->Read(&len, sizeof(len), &count); 405 if (FAILED(hr) || count != sizeof(len)) 406 return E_FAIL; 407 408 if (unicode) 409 len *= sizeof(WCHAR); 410 411 TRACE("reading %d\n", len); 412 LPSTR temp = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len + sizeof(WCHAR)); 413 if (!temp) 414 return E_OUTOFMEMORY; 415 count = 0; 416 hr = stm->Read(temp, len, &count); 417 if (FAILED(hr) || count != len) 418 { 419 HeapFree(GetProcessHeap(), 0, temp); 420 return E_FAIL; 421 } 422 423 TRACE("read %s\n", debugstr_an(temp, len)); 424 425 /* convert to unicode if necessary */ 426 LPWSTR str; 427 if (!unicode) 428 { 429 count = MultiByteToWideChar(CP_ACP, 0, temp, len, NULL, 0); 430 str = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (count + 1) * sizeof(WCHAR)); 431 if (!str) 432 { 433 HeapFree(GetProcessHeap(), 0, temp); 434 return E_OUTOFMEMORY; 435 } 436 MultiByteToWideChar(CP_ACP, 0, temp, len, str, count); 437 HeapFree(GetProcessHeap(), 0, temp); 438 } 439 else 440 { 441 count /= sizeof(WCHAR); 442 str = (LPWSTR)temp; 443 } 444 str[count] = 0; 445 446 *pstr = str; 447 448 return S_OK; 449 } 450 451 452 /* 453 * NOTE: The following 5 functions are part of LINKINFO.DLL 454 */ 455 static BOOL ShellLink_GetVolumeInfo(LPCWSTR path, CShellLink::volume_info *volume) 456 { 457 WCHAR drive[4] = { path[0], ':', '\\', 0 }; 458 459 volume->type = GetDriveTypeW(drive); 460 BOOL bRet = GetVolumeInformationW(drive, volume->label, _countof(volume->label), &volume->serial, NULL, NULL, NULL, 0); 461 TRACE("ret = %d type %d serial %08x name %s\n", bRet, 462 volume->type, volume->serial, debugstr_w(volume->label)); 463 return bRet; 464 } 465 466 static HRESULT Stream_ReadChunk(IStream* stm, LPVOID *data) 467 { 468 struct sized_chunk 469 { 470 DWORD size; 471 unsigned char data[1]; 472 } *chunk; 473 474 TRACE("%p\n", stm); 475 476 DWORD size; 477 ULONG count; 478 HRESULT hr = stm->Read(&size, sizeof(size), &count); 479 if (FAILED(hr) || count != sizeof(size)) 480 return E_FAIL; 481 482 chunk = static_cast<sized_chunk *>(HeapAlloc(GetProcessHeap(), 0, size)); 483 if (!chunk) 484 return E_OUTOFMEMORY; 485 486 chunk->size = size; 487 hr = stm->Read(chunk->data, size - sizeof(size), &count); 488 if (FAILED(hr) || count != (size - sizeof(size))) 489 { 490 HeapFree(GetProcessHeap(), 0, chunk); 491 return E_FAIL; 492 } 493 494 TRACE("Read %d bytes\n", chunk->size); 495 496 *data = chunk; 497 498 return S_OK; 499 } 500 501 static BOOL Stream_LoadVolume(LOCAL_VOLUME_INFO *vol, CShellLink::volume_info *volume) 502 { 503 volume->serial = vol->dwVolSerial; 504 volume->type = vol->dwType; 505 506 if (!vol->dwVolLabelOfs) 507 return FALSE; 508 if (vol->dwSize <= vol->dwVolLabelOfs) 509 return FALSE; 510 INT len = vol->dwSize - vol->dwVolLabelOfs; 511 512 LPSTR label = (LPSTR)vol; 513 label += vol->dwVolLabelOfs; 514 MultiByteToWideChar(CP_ACP, 0, label, len, volume->label, _countof(volume->label)); 515 516 return TRUE; 517 } 518 519 static LPWSTR Stream_LoadPath(LPCSTR p, DWORD maxlen) 520 { 521 UINT len = 0; 522 523 while (p[len] && len < maxlen) 524 len++; 525 526 UINT wlen = MultiByteToWideChar(CP_ACP, 0, p, len, NULL, 0); 527 LPWSTR path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wlen + 1) * sizeof(WCHAR)); 528 if (!path) 529 return NULL; 530 MultiByteToWideChar(CP_ACP, 0, p, len, path, wlen); 531 path[wlen] = 0; 532 533 return path; 534 } 535 536 static HRESULT Stream_LoadLocation(IStream *stm, 537 CShellLink::volume_info *volume, LPWSTR *path) 538 { 539 char *p = NULL; 540 HRESULT hr = Stream_ReadChunk(stm, (LPVOID*) &p); 541 if (FAILED(hr)) 542 return hr; 543 544 LOCATION_INFO *loc = reinterpret_cast<LOCATION_INFO *>(p); 545 if (loc->dwTotalSize < sizeof(LOCATION_INFO)) 546 { 547 HeapFree(GetProcessHeap(), 0, p); 548 return E_FAIL; 549 } 550 551 /* if there's valid local volume information, load it */ 552 if (loc->dwVolTableOfs && 553 ((loc->dwVolTableOfs + sizeof(LOCAL_VOLUME_INFO)) <= loc->dwTotalSize)) 554 { 555 LOCAL_VOLUME_INFO *volume_info; 556 557 volume_info = (LOCAL_VOLUME_INFO*) &p[loc->dwVolTableOfs]; 558 Stream_LoadVolume(volume_info, volume); 559 } 560 561 /* if there's a local path, load it */ 562 DWORD n = loc->dwLocalPathOfs; 563 if (n && n < loc->dwTotalSize) 564 *path = Stream_LoadPath(&p[n], loc->dwTotalSize - n); 565 566 TRACE("type %d serial %08x name %s path %s\n", volume->type, 567 volume->serial, debugstr_w(volume->label), debugstr_w(*path)); 568 569 HeapFree(GetProcessHeap(), 0, p); 570 return S_OK; 571 } 572 573 574 /* 575 * The format of the advertised shortcut info is: 576 * 577 * Offset Description 578 * ------ ----------- 579 * 0 Length of the block (4 bytes, usually 0x314) 580 * 4 tag (dword) 581 * 8 string data in ASCII 582 * 8+0x104 string data in UNICODE 583 * 584 * In the original Win32 implementation the buffers are not initialized 585 * to zero, so data trailing the string is random garbage. 586 */ 587 HRESULT CShellLink::GetAdvertiseInfo(LPWSTR *str, DWORD dwSig) 588 { 589 LPEXP_DARWIN_LINK pInfo; 590 591 *str = NULL; 592 593 pInfo = (LPEXP_DARWIN_LINK)SHFindDataBlock(m_pDBList, dwSig); 594 if (!pInfo) 595 return E_FAIL; 596 597 /* Make sure that the size of the structure is valid */ 598 if (pInfo->dbh.cbSize != sizeof(*pInfo)) 599 { 600 ERR("Ooops. This structure is not as expected...\n"); 601 return E_FAIL; 602 } 603 604 TRACE("dwSig %08x string = '%s'\n", pInfo->dbh.dwSignature, debugstr_w(pInfo->szwDarwinID)); 605 606 *str = pInfo->szwDarwinID; 607 return S_OK; 608 } 609 610 /************************************************************************ 611 * IPersistStream_Load (IPersistStream) 612 */ 613 HRESULT STDMETHODCALLTYPE CShellLink::Load(IStream *stm) 614 { 615 TRACE("%p %p\n", this, stm); 616 617 if (!stm) 618 return STG_E_INVALIDPOINTER; 619 620 /* Free all the old stuff */ 621 Reset(); 622 623 ULONG dwBytesRead = 0; 624 HRESULT hr = stm->Read(&m_Header, sizeof(m_Header), &dwBytesRead); 625 if (FAILED(hr)) 626 return hr; 627 628 if (dwBytesRead != sizeof(m_Header)) 629 return E_FAIL; 630 if (m_Header.dwSize != sizeof(m_Header)) 631 return E_FAIL; 632 if (!IsEqualIID(m_Header.clsid, CLSID_ShellLink)) 633 return E_FAIL; 634 635 /* Load the new data in order */ 636 637 if (TRACE_ON(shell)) 638 { 639 SYSTEMTIME stCreationTime; 640 SYSTEMTIME stLastAccessTime; 641 SYSTEMTIME stLastWriteTime; 642 WCHAR sTemp[MAX_PATH]; 643 644 FileTimeToSystemTime(&m_Header.ftCreationTime, &stCreationTime); 645 FileTimeToSystemTime(&m_Header.ftLastAccessTime, &stLastAccessTime); 646 FileTimeToSystemTime(&m_Header.ftLastWriteTime, &stLastWriteTime); 647 648 GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &stCreationTime, 649 NULL, sTemp, _countof(sTemp)); 650 TRACE("-- stCreationTime: %s\n", debugstr_w(sTemp)); 651 GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &stLastAccessTime, 652 NULL, sTemp, _countof(sTemp)); 653 TRACE("-- stLastAccessTime: %s\n", debugstr_w(sTemp)); 654 GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &stLastWriteTime, 655 NULL, sTemp, _countof(sTemp)); 656 TRACE("-- stLastWriteTime: %s\n", debugstr_w(sTemp)); 657 } 658 659 /* load all the new stuff */ 660 if (m_Header.dwFlags & SLDF_HAS_ID_LIST) 661 { 662 hr = ILLoadFromStream(stm, &m_pPidl); 663 if (FAILED(hr)) 664 return hr; 665 } 666 pdump(m_pPidl); 667 668 /* Load the location information... */ 669 if (m_Header.dwFlags & SLDF_HAS_LINK_INFO) 670 { 671 hr = Stream_LoadLocation(stm, &volume, &m_sPath); 672 if (FAILED(hr)) 673 return hr; 674 } 675 /* ... but if it is required not to use it, clear it */ 676 if (m_Header.dwFlags & SLDF_FORCE_NO_LINKINFO) 677 { 678 HeapFree(GetProcessHeap(), 0, m_sPath); 679 m_sPath = NULL; 680 ZeroMemory(&volume, sizeof(volume)); 681 } 682 683 BOOL unicode = !!(m_Header.dwFlags & SLDF_UNICODE); 684 685 if (m_Header.dwFlags & SLDF_HAS_NAME) 686 { 687 hr = Stream_LoadString(stm, unicode, &m_sDescription); 688 if (FAILED(hr)) 689 return hr; 690 TRACE("Description -> %s\n", debugstr_w(m_sDescription)); 691 } 692 693 if (m_Header.dwFlags & SLDF_HAS_RELPATH) 694 { 695 hr = Stream_LoadString(stm, unicode, &m_sPathRel); 696 if (FAILED(hr)) 697 return hr; 698 TRACE("Relative Path-> %s\n", debugstr_w(m_sPathRel)); 699 } 700 701 if (m_Header.dwFlags & SLDF_HAS_WORKINGDIR) 702 { 703 hr = Stream_LoadString(stm, unicode, &m_sWorkDir); 704 if (FAILED(hr)) 705 return hr; 706 PathRemoveBackslash(m_sWorkDir); 707 TRACE("Working Dir -> %s\n", debugstr_w(m_sWorkDir)); 708 } 709 710 if (m_Header.dwFlags & SLDF_HAS_ARGS) 711 { 712 hr = Stream_LoadString(stm, unicode, &m_sArgs); 713 if (FAILED(hr)) 714 return hr; 715 TRACE("Arguments -> %s\n", debugstr_w(m_sArgs)); 716 } 717 718 if (m_Header.dwFlags & SLDF_HAS_ICONLOCATION) 719 { 720 hr = Stream_LoadString(stm, unicode, &m_sIcoPath); 721 if (FAILED(hr)) 722 return hr; 723 TRACE("Icon file -> %s\n", debugstr_w(m_sIcoPath)); 724 } 725 726 /* Now load the optional data block list */ 727 hr = SHReadDataBlockList(stm, &m_pDBList); 728 if (FAILED(hr)) // FIXME: Should we fail? 729 return hr; 730 731 if (TRACE_ON(shell)) 732 { 733 #if (NTDDI_VERSION < NTDDI_LONGHORN) 734 if (m_Header.dwFlags & SLDF_HAS_LOGO3ID) 735 { 736 hr = GetAdvertiseInfo(&sProduct, EXP_LOGO3_ID_SIG); 737 if (SUCCEEDED(hr)) 738 TRACE("Product -> %s\n", debugstr_w(sProduct)); 739 } 740 #endif 741 if (m_Header.dwFlags & SLDF_HAS_DARWINID) 742 { 743 hr = GetAdvertiseInfo(&sComponent, EXP_DARWIN_ID_SIG); 744 if (SUCCEEDED(hr)) 745 TRACE("Component -> %s\n", debugstr_w(sComponent)); 746 } 747 } 748 749 if (m_Header.dwFlags & SLDF_RUNAS_USER) 750 m_bRunAs = TRUE; 751 else 752 m_bRunAs = FALSE; 753 754 TRACE("OK\n"); 755 756 pdump(m_pPidl); 757 758 return S_OK; 759 } 760 761 /************************************************************************ 762 * Stream_WriteString 763 * 764 * Helper function for IPersistStream_Save. Writes a unicode string 765 * with terminating nul byte to a stream, preceded by the its length. 766 */ 767 static HRESULT Stream_WriteString(IStream* stm, LPCWSTR str) 768 { 769 USHORT len = wcslen(str) + 1; // FIXME: Possible overflows? 770 DWORD count; 771 772 HRESULT hr = stm->Write(&len, sizeof(len), &count); 773 if (FAILED(hr)) 774 return hr; 775 776 len *= sizeof(WCHAR); 777 778 hr = stm->Write(str, len, &count); 779 if (FAILED(hr)) 780 return hr; 781 782 return S_OK; 783 } 784 785 /************************************************************************ 786 * Stream_WriteLocationInfo 787 * 788 * Writes the location info to a stream 789 * 790 * FIXME: One day we might want to write the network volume information 791 * and the final path. 792 * Figure out how Windows deals with unicode paths here. 793 */ 794 static HRESULT Stream_WriteLocationInfo(IStream* stm, LPCWSTR path, 795 CShellLink::volume_info *volume) 796 { 797 LOCAL_VOLUME_INFO *vol; 798 LOCATION_INFO *loc; 799 800 TRACE("%p %s %p\n", stm, debugstr_w(path), volume); 801 802 /* figure out the size of everything */ 803 DWORD label_size = WideCharToMultiByte(CP_ACP, 0, volume->label, -1, 804 NULL, 0, NULL, NULL); 805 DWORD path_size = WideCharToMultiByte(CP_ACP, 0, path, -1, 806 NULL, 0, NULL, NULL); 807 DWORD volume_info_size = sizeof(*vol) + label_size; 808 DWORD final_path_size = 1; 809 DWORD total_size = sizeof(*loc) + volume_info_size + path_size + final_path_size; 810 811 /* create pointers to everything */ 812 loc = static_cast<LOCATION_INFO *>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total_size)); 813 vol = (LOCAL_VOLUME_INFO*) &loc[1]; 814 LPSTR szLabel = (LPSTR) &vol[1]; 815 LPSTR szPath = &szLabel[label_size]; 816 LPSTR szFinalPath = &szPath[path_size]; 817 818 /* fill in the location information header */ 819 loc->dwTotalSize = total_size; 820 loc->dwHeaderSize = sizeof(*loc); 821 loc->dwFlags = 1; 822 loc->dwVolTableOfs = sizeof(*loc); 823 loc->dwLocalPathOfs = sizeof(*loc) + volume_info_size; 824 loc->dwNetworkVolTableOfs = 0; 825 loc->dwFinalPathOfs = sizeof(*loc) + volume_info_size + path_size; 826 827 /* fill in the volume information */ 828 vol->dwSize = volume_info_size; 829 vol->dwType = volume->type; 830 vol->dwVolSerial = volume->serial; 831 vol->dwVolLabelOfs = sizeof(*vol); 832 833 /* copy in the strings */ 834 WideCharToMultiByte(CP_ACP, 0, volume->label, -1, 835 szLabel, label_size, NULL, NULL); 836 WideCharToMultiByte(CP_ACP, 0, path, -1, 837 szPath, path_size, NULL, NULL); 838 *szFinalPath = 0; 839 840 ULONG count = 0; 841 HRESULT hr = stm->Write(loc, total_size, &count); 842 HeapFree(GetProcessHeap(), 0, loc); 843 844 return hr; 845 } 846 847 /************************************************************************ 848 * IPersistStream_Save (IPersistStream) 849 * 850 * FIXME: makes assumptions about byte order 851 */ 852 HRESULT STDMETHODCALLTYPE CShellLink::Save(IStream *stm, BOOL fClearDirty) 853 { 854 TRACE("%p %p %x\n", this, stm, fClearDirty); 855 856 m_Header.dwSize = sizeof(m_Header); 857 m_Header.clsid = CLSID_ShellLink; 858 859 /* 860 * Reset the flags: keep only the flags related to data blocks as they were 861 * already set in accordance by the different mutator member functions. 862 * The other flags will be determined now by the presence or absence of data. 863 */ 864 m_Header.dwFlags &= (SLDF_RUN_WITH_SHIMLAYER | SLDF_RUNAS_USER | 865 SLDF_RUN_IN_SEPARATE | SLDF_HAS_DARWINID | 866 #if (NTDDI_VERSION < NTDDI_LONGHORN) 867 SLDF_HAS_LOGO3ID | 868 #endif 869 SLDF_HAS_EXP_ICON_SZ | SLDF_HAS_EXP_SZ); 870 // TODO: When we will support Vista+ functionality, add other flags to this list. 871 872 /* The stored strings are in UNICODE */ 873 m_Header.dwFlags |= SLDF_UNICODE; 874 875 if (m_pPidl) 876 m_Header.dwFlags |= SLDF_HAS_ID_LIST; 877 if (m_sPath) 878 m_Header.dwFlags |= SLDF_HAS_LINK_INFO; 879 if (m_sDescription && *m_sDescription) 880 m_Header.dwFlags |= SLDF_HAS_NAME; 881 if (m_sPathRel && *m_sPathRel) 882 m_Header.dwFlags |= SLDF_HAS_RELPATH; 883 if (m_sWorkDir && *m_sWorkDir) 884 m_Header.dwFlags |= SLDF_HAS_WORKINGDIR; 885 if (m_sArgs && *m_sArgs) 886 m_Header.dwFlags |= SLDF_HAS_ARGS; 887 if (m_sIcoPath && *m_sIcoPath) 888 m_Header.dwFlags |= SLDF_HAS_ICONLOCATION; 889 if (m_bRunAs) 890 m_Header.dwFlags |= SLDF_RUNAS_USER; 891 892 /* Write the shortcut header */ 893 ULONG count; 894 HRESULT hr = stm->Write(&m_Header, sizeof(m_Header), &count); 895 if (FAILED(hr)) 896 { 897 ERR("Write failed\n"); 898 return hr; 899 } 900 901 /* Save the data in order */ 902 903 if (m_pPidl) 904 { 905 hr = ILSaveToStream(stm, m_pPidl); 906 if (FAILED(hr)) 907 { 908 ERR("Failed to write PIDL\n"); 909 return hr; 910 } 911 } 912 913 if (m_sPath) 914 { 915 hr = Stream_WriteLocationInfo(stm, m_sPath, &volume); 916 if (FAILED(hr)) 917 return hr; 918 } 919 920 if (m_Header.dwFlags & SLDF_HAS_NAME) 921 { 922 hr = Stream_WriteString(stm, m_sDescription); 923 if (FAILED(hr)) 924 return hr; 925 } 926 927 if (m_Header.dwFlags & SLDF_HAS_RELPATH) 928 { 929 hr = Stream_WriteString(stm, m_sPathRel); 930 if (FAILED(hr)) 931 return hr; 932 } 933 934 if (m_Header.dwFlags & SLDF_HAS_WORKINGDIR) 935 { 936 hr = Stream_WriteString(stm, m_sWorkDir); 937 if (FAILED(hr)) 938 return hr; 939 } 940 941 if (m_Header.dwFlags & SLDF_HAS_ARGS) 942 { 943 hr = Stream_WriteString(stm, m_sArgs); 944 if (FAILED(hr)) 945 return hr; 946 } 947 948 if (m_Header.dwFlags & SLDF_HAS_ICONLOCATION) 949 { 950 hr = Stream_WriteString(stm, m_sIcoPath); 951 if (FAILED(hr)) 952 return hr; 953 } 954 955 /* 956 * Now save the data block list. 957 * 958 * NOTE that both advertised Product and Component are already saved 959 * inside Logo3 and Darwin data blocks in the m_pDBList list, and the 960 * m_Header.dwFlags is suitably initialized. 961 */ 962 hr = SHWriteDataBlockList(stm, m_pDBList); 963 if (FAILED(hr)) 964 return hr; 965 966 /* Clear the dirty bit if requested */ 967 if (fClearDirty) 968 m_bDirty = FALSE; 969 970 return hr; 971 } 972 973 /************************************************************************ 974 * IPersistStream_GetSizeMax (IPersistStream) 975 */ 976 HRESULT STDMETHODCALLTYPE CShellLink::GetSizeMax(ULARGE_INTEGER *pcbSize) 977 { 978 TRACE("(%p)\n", this); 979 return E_NOTIMPL; 980 } 981 982 static BOOL SHELL_ExistsFileW(LPCWSTR path) 983 { 984 if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path)) 985 return FALSE; 986 987 return TRUE; 988 } 989 990 /************************************************************************** 991 * ShellLink_UpdatePath 992 * update absolute path in sPath using relative path in sPathRel 993 */ 994 static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath) 995 { 996 if (!path || !psPath) 997 return E_INVALIDARG; 998 999 if (!*psPath && sPathRel) 1000 { 1001 WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH]; 1002 LPWSTR final = NULL; 1003 1004 /* first try if [directory of link file] + [relative path] finds an existing file */ 1005 1006 GetFullPathNameW(path, MAX_PATH * 2, buffer, &final); 1007 if (!final) 1008 final = buffer; 1009 wcscpy(final, sPathRel); 1010 1011 *abs_path = '\0'; 1012 1013 if (SHELL_ExistsFileW(buffer)) 1014 { 1015 if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final)) 1016 wcscpy(abs_path, buffer); 1017 } 1018 else 1019 { 1020 /* try if [working directory] + [relative path] finds an existing file */ 1021 if (sWorkDir) 1022 { 1023 wcscpy(buffer, sWorkDir); 1024 wcscpy(PathAddBackslashW(buffer), sPathRel); 1025 1026 if (SHELL_ExistsFileW(buffer)) 1027 if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final)) 1028 wcscpy(abs_path, buffer); 1029 } 1030 } 1031 1032 /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */ 1033 if (!*abs_path) 1034 wcscpy(abs_path, sPathRel); 1035 1036 *psPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(abs_path) + 1) * sizeof(WCHAR)); 1037 if (!*psPath) 1038 return E_OUTOFMEMORY; 1039 1040 wcscpy(*psPath, abs_path); 1041 } 1042 1043 return S_OK; 1044 } 1045 1046 HRESULT STDMETHODCALLTYPE CShellLink::GetPath(LPSTR pszFile, INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags) 1047 { 1048 HRESULT hr; 1049 LPWSTR pszFileW; 1050 WIN32_FIND_DATAW wfd; 1051 1052 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n", 1053 this, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(m_sPath)); 1054 1055 /* Allocate a temporary UNICODE buffer */ 1056 pszFileW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, cchMaxPath * sizeof(WCHAR)); 1057 if (!pszFileW) 1058 return E_OUTOFMEMORY; 1059 1060 /* Call the UNICODE function */ 1061 hr = GetPath(pszFileW, cchMaxPath, &wfd, fFlags); 1062 1063 /* Convert the file path back to ANSI */ 1064 WideCharToMultiByte(CP_ACP, 0, pszFileW, -1, 1065 pszFile, cchMaxPath, NULL, NULL); 1066 1067 /* Free the temporary buffer */ 1068 HeapFree(GetProcessHeap(), 0, pszFileW); 1069 1070 if (pfd) 1071 { 1072 ZeroMemory(pfd, sizeof(*pfd)); 1073 1074 /* Copy the file data if a file path was returned */ 1075 if (*pszFile) 1076 { 1077 DWORD len; 1078 1079 /* Copy the fixed part */ 1080 CopyMemory(pfd, &wfd, FIELD_OFFSET(WIN32_FIND_DATAA, cFileName)); 1081 1082 /* Convert the file names to ANSI */ 1083 len = lstrlenW(wfd.cFileName); 1084 WideCharToMultiByte(CP_ACP, 0, wfd.cFileName, len + 1, 1085 pfd->cFileName, sizeof(pfd->cFileName), NULL, NULL); 1086 len = lstrlenW(wfd.cAlternateFileName); 1087 WideCharToMultiByte(CP_ACP, 0, wfd.cAlternateFileName, len + 1, 1088 pfd->cAlternateFileName, sizeof(pfd->cAlternateFileName), NULL, NULL); 1089 } 1090 } 1091 1092 return hr; 1093 } 1094 1095 HRESULT STDMETHODCALLTYPE CShellLink::GetIDList(LPITEMIDLIST *ppidl) 1096 { 1097 TRACE("(%p)->(ppidl=%p)\n", this, ppidl); 1098 1099 if (!m_pPidl) 1100 { 1101 *ppidl = NULL; 1102 return S_FALSE; 1103 } 1104 1105 *ppidl = ILClone(m_pPidl); 1106 return S_OK; 1107 } 1108 1109 HRESULT STDMETHODCALLTYPE CShellLink::SetIDList(LPCITEMIDLIST pidl) 1110 { 1111 TRACE("(%p)->(pidl=%p)\n", this, pidl); 1112 return SetTargetFromPIDLOrPath(pidl, NULL); 1113 } 1114 1115 HRESULT STDMETHODCALLTYPE CShellLink::GetDescription(LPSTR pszName, INT cchMaxName) 1116 { 1117 TRACE("(%p)->(%p len=%u)\n", this, pszName, cchMaxName); 1118 1119 if (cchMaxName) 1120 *pszName = 0; 1121 1122 if (m_sDescription) 1123 WideCharToMultiByte(CP_ACP, 0, m_sDescription, -1, 1124 pszName, cchMaxName, NULL, NULL); 1125 1126 return S_OK; 1127 } 1128 1129 HRESULT STDMETHODCALLTYPE CShellLink::SetDescription(LPCSTR pszName) 1130 { 1131 TRACE("(%p)->(pName=%s)\n", this, pszName); 1132 1133 HeapFree(GetProcessHeap(), 0, m_sDescription); 1134 m_sDescription = NULL; 1135 1136 if (pszName) 1137 { 1138 m_sDescription = HEAP_strdupAtoW(GetProcessHeap(), 0, pszName); 1139 if (!m_sDescription) 1140 return E_OUTOFMEMORY; 1141 } 1142 m_bDirty = TRUE; 1143 1144 return S_OK; 1145 } 1146 1147 HRESULT STDMETHODCALLTYPE CShellLink::GetWorkingDirectory(LPSTR pszDir, INT cchMaxPath) 1148 { 1149 TRACE("(%p)->(%p len=%u)\n", this, pszDir, cchMaxPath); 1150 1151 if (cchMaxPath) 1152 *pszDir = 0; 1153 1154 if (m_sWorkDir) 1155 WideCharToMultiByte(CP_ACP, 0, m_sWorkDir, -1, 1156 pszDir, cchMaxPath, NULL, NULL); 1157 1158 return S_OK; 1159 } 1160 1161 HRESULT STDMETHODCALLTYPE CShellLink::SetWorkingDirectory(LPCSTR pszDir) 1162 { 1163 TRACE("(%p)->(dir=%s)\n", this, pszDir); 1164 1165 HeapFree(GetProcessHeap(), 0, m_sWorkDir); 1166 m_sWorkDir = NULL; 1167 1168 if (pszDir) 1169 { 1170 m_sWorkDir = HEAP_strdupAtoW(GetProcessHeap(), 0, pszDir); 1171 if (!m_sWorkDir) 1172 return E_OUTOFMEMORY; 1173 } 1174 m_bDirty = TRUE; 1175 1176 return S_OK; 1177 } 1178 1179 HRESULT STDMETHODCALLTYPE CShellLink::GetArguments(LPSTR pszArgs, INT cchMaxPath) 1180 { 1181 TRACE("(%p)->(%p len=%u)\n", this, pszArgs, cchMaxPath); 1182 1183 if (cchMaxPath) 1184 *pszArgs = 0; 1185 1186 if (m_sArgs) 1187 WideCharToMultiByte(CP_ACP, 0, m_sArgs, -1, 1188 pszArgs, cchMaxPath, NULL, NULL); 1189 1190 return S_OK; 1191 } 1192 1193 HRESULT STDMETHODCALLTYPE CShellLink::SetArguments(LPCSTR pszArgs) 1194 { 1195 TRACE("(%p)->(args=%s)\n", this, pszArgs); 1196 1197 HeapFree(GetProcessHeap(), 0, m_sArgs); 1198 m_sArgs = NULL; 1199 1200 if (pszArgs) 1201 { 1202 m_sArgs = HEAP_strdupAtoW(GetProcessHeap(), 0, pszArgs); 1203 if (!m_sArgs) 1204 return E_OUTOFMEMORY; 1205 } 1206 1207 m_bDirty = TRUE; 1208 1209 return S_OK; 1210 } 1211 1212 HRESULT STDMETHODCALLTYPE CShellLink::GetHotkey(WORD *pwHotkey) 1213 { 1214 TRACE("(%p)->(%p)(0x%08x)\n", this, pwHotkey, m_Header.wHotKey); 1215 *pwHotkey = m_Header.wHotKey; 1216 return S_OK; 1217 } 1218 1219 HRESULT STDMETHODCALLTYPE CShellLink::SetHotkey(WORD wHotkey) 1220 { 1221 TRACE("(%p)->(hotkey=%x)\n", this, wHotkey); 1222 1223 m_Header.wHotKey = wHotkey; 1224 m_bDirty = TRUE; 1225 1226 return S_OK; 1227 } 1228 1229 HRESULT STDMETHODCALLTYPE CShellLink::GetShowCmd(INT *piShowCmd) 1230 { 1231 TRACE("(%p)->(%p) %d\n", this, piShowCmd, m_Header.nShowCommand); 1232 *piShowCmd = m_Header.nShowCommand; 1233 return S_OK; 1234 } 1235 1236 HRESULT STDMETHODCALLTYPE CShellLink::SetShowCmd(INT iShowCmd) 1237 { 1238 TRACE("(%p) %d\n", this, iShowCmd); 1239 1240 m_Header.nShowCommand = iShowCmd; 1241 m_bDirty = TRUE; 1242 1243 return S_OK; 1244 } 1245 1246 HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(LPSTR pszIconPath, INT cchIconPath, INT *piIcon) 1247 { 1248 HRESULT hr; 1249 LPWSTR pszIconPathW; 1250 1251 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath, cchIconPath, piIcon); 1252 1253 /* Allocate a temporary UNICODE buffer */ 1254 pszIconPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, cchIconPath * sizeof(WCHAR)); 1255 if (!pszIconPathW) 1256 return E_OUTOFMEMORY; 1257 1258 /* Call the UNICODE function */ 1259 hr = GetIconLocation(pszIconPathW, cchIconPath, piIcon); 1260 1261 /* Convert the file path back to ANSI */ 1262 WideCharToMultiByte(CP_ACP, 0, pszIconPathW, -1, 1263 pszIconPath, cchIconPath, NULL, NULL); 1264 1265 /* Free the temporary buffer */ 1266 HeapFree(GetProcessHeap(), 0, pszIconPathW); 1267 1268 return hr; 1269 } 1270 1271 HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(UINT uFlags, PSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags) 1272 { 1273 HRESULT hr; 1274 LPWSTR pszIconFileW; 1275 1276 TRACE("(%p)->(%u %p len=%u piIndex=%p pwFlags=%p)\n", this, uFlags, pszIconFile, cchMax, piIndex, pwFlags); 1277 1278 /* Allocate a temporary UNICODE buffer */ 1279 pszIconFileW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, cchMax * sizeof(WCHAR)); 1280 if (!pszIconFileW) 1281 return E_OUTOFMEMORY; 1282 1283 /* Call the UNICODE function */ 1284 hr = GetIconLocation(uFlags, pszIconFileW, cchMax, piIndex, pwFlags); 1285 1286 /* Convert the file path back to ANSI */ 1287 WideCharToMultiByte(CP_ACP, 0, pszIconFileW, -1, 1288 pszIconFile, cchMax, NULL, NULL); 1289 1290 /* Free the temporary buffer */ 1291 HeapFree(GetProcessHeap(), 0, pszIconFileW); 1292 1293 return hr; 1294 } 1295 1296 HRESULT STDMETHODCALLTYPE CShellLink::Extract(PCSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) 1297 { 1298 TRACE("(%p)->(path=%s iicon=%u)\n", this, pszFile, nIconIndex); 1299 1300 LPWSTR str = NULL; 1301 if (pszFile) 1302 { 1303 str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile); 1304 if (!str) 1305 return E_OUTOFMEMORY; 1306 } 1307 1308 HRESULT hr = Extract(str, nIconIndex, phiconLarge, phiconSmall, nIconSize); 1309 1310 if (str) 1311 HeapFree(GetProcessHeap(), 0, str); 1312 1313 return hr; 1314 } 1315 1316 HRESULT STDMETHODCALLTYPE CShellLink::SetIconLocation(LPCSTR pszIconPath, INT iIcon) 1317 { 1318 TRACE("(%p)->(path=%s iicon=%u)\n", this, pszIconPath, iIcon); 1319 1320 LPWSTR str = NULL; 1321 if (pszIconPath) 1322 { 1323 str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath); 1324 if (!str) 1325 return E_OUTOFMEMORY; 1326 } 1327 1328 HRESULT hr = SetIconLocation(str, iIcon); 1329 1330 if (str) 1331 HeapFree(GetProcessHeap(), 0, str); 1332 1333 return hr; 1334 } 1335 1336 HRESULT STDMETHODCALLTYPE CShellLink::SetRelativePath(LPCSTR pszPathRel, DWORD dwReserved) 1337 { 1338 TRACE("(%p)->(path=%s %x)\n", this, pszPathRel, dwReserved); 1339 1340 HeapFree(GetProcessHeap(), 0, m_sPathRel); 1341 m_sPathRel = NULL; 1342 1343 if (pszPathRel) 1344 { 1345 m_sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel); 1346 m_bDirty = TRUE; 1347 } 1348 1349 return ShellLink_UpdatePath(m_sPathRel, m_sPath, m_sWorkDir, &m_sPath); 1350 } 1351 1352 static LPWSTR 1353 shelllink_get_msi_component_path(LPWSTR component) 1354 { 1355 DWORD Result, sz = 0; 1356 1357 Result = CommandLineFromMsiDescriptor(component, NULL, &sz); 1358 if (Result != ERROR_SUCCESS) 1359 return NULL; 1360 1361 sz++; 1362 LPWSTR path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sz * sizeof(WCHAR)); 1363 Result = CommandLineFromMsiDescriptor(component, path, &sz); 1364 if (Result != ERROR_SUCCESS) 1365 { 1366 HeapFree(GetProcessHeap(), 0, path); 1367 path = NULL; 1368 } 1369 1370 TRACE("returning %s\n", debugstr_w(path)); 1371 1372 return path; 1373 } 1374 1375 HRESULT STDMETHODCALLTYPE CShellLink::Resolve(HWND hwnd, DWORD fFlags) 1376 { 1377 HRESULT hr = S_OK; 1378 BOOL bSuccess; 1379 1380 TRACE("(%p)->(hwnd=%p flags=%x)\n", this, hwnd, fFlags); 1381 1382 /* FIXME: use IResolveShellLink interface? */ 1383 1384 // FIXME: See InvokeCommand(). 1385 1386 #if (NTDDI_VERSION < NTDDI_LONGHORN) 1387 // NOTE: For Logo3 (EXP_LOGO3_ID_SIG), check also for SHRestricted(REST_NOLOGO3CHANNELNOTIFY) 1388 if (m_Header.dwFlags & SLDF_HAS_LOGO3ID) 1389 { 1390 FIXME("Logo3 links are not supported yet!\n"); 1391 return E_FAIL; 1392 } 1393 #endif 1394 1395 /* Resolve Darwin (MSI) target */ 1396 if (m_Header.dwFlags & SLDF_HAS_DARWINID) 1397 { 1398 LPWSTR component = NULL; 1399 hr = GetAdvertiseInfo(&component, EXP_DARWIN_ID_SIG); 1400 if (FAILED(hr)) 1401 return E_FAIL; 1402 1403 /* Clear the cached path */ 1404 HeapFree(GetProcessHeap(), 0, m_sPath); 1405 m_sPath = NULL; 1406 m_sPath = shelllink_get_msi_component_path(component); 1407 if (!m_sPath) 1408 return E_FAIL; 1409 } 1410 1411 if (!m_sPath && m_pPidl) 1412 { 1413 WCHAR buffer[MAX_PATH]; 1414 1415 bSuccess = SHGetPathFromIDListW(m_pPidl, buffer); 1416 if (bSuccess && *buffer) 1417 { 1418 m_sPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(buffer) + 1) * sizeof(WCHAR)); 1419 if (!m_sPath) 1420 return E_OUTOFMEMORY; 1421 1422 wcscpy(m_sPath, buffer); 1423 1424 m_bDirty = TRUE; 1425 } 1426 else 1427 { 1428 hr = S_OK; /* don't report an error occurred while just caching information */ 1429 } 1430 } 1431 1432 // FIXME: Strange to do that here... 1433 if (!m_sIcoPath && m_sPath) 1434 { 1435 m_sIcoPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(m_sPath) + 1) * sizeof(WCHAR)); 1436 if (!m_sIcoPath) 1437 return E_OUTOFMEMORY; 1438 1439 wcscpy(m_sIcoPath, m_sPath); 1440 m_Header.nIconIndex = 0; 1441 1442 m_bDirty = TRUE; 1443 } 1444 1445 return hr; 1446 } 1447 1448 HRESULT STDMETHODCALLTYPE CShellLink::SetPath(LPCSTR pszFile) 1449 { 1450 TRACE("(%p)->(path=%s)\n", this, pszFile); 1451 1452 if (!pszFile) 1453 return E_INVALIDARG; 1454 1455 LPWSTR str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile); 1456 if (!str) 1457 return E_OUTOFMEMORY; 1458 1459 HRESULT hr = SetPath(str); 1460 HeapFree(GetProcessHeap(), 0, str); 1461 1462 return hr; 1463 } 1464 1465 HRESULT STDMETHODCALLTYPE CShellLink::GetPath(LPWSTR pszFile, INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags) 1466 { 1467 WCHAR buffer[MAX_PATH]; 1468 1469 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n", 1470 this, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(m_sPath)); 1471 1472 if (cchMaxPath) 1473 *pszFile = 0; 1474 // FIXME: What if cchMaxPath == 0 , or pszFile == NULL ?? 1475 1476 // FIXME: What about Darwin?? 1477 1478 /* 1479 * Retrieve the path to the target from the PIDL (if we have one). 1480 * NOTE: Do NOT use the cached path (m_sPath from link info). 1481 */ 1482 if (m_pPidl && SHGetPathFromIDListW(m_pPidl, buffer)) 1483 { 1484 if (fFlags & SLGP_SHORTPATH) 1485 GetShortPathNameW(buffer, buffer, _countof(buffer)); 1486 // FIXME: Add support for SLGP_UNCPRIORITY 1487 } 1488 else 1489 { 1490 *buffer = 0; 1491 } 1492 1493 /* If we have a FindData structure, initialize it */ 1494 if (pfd) 1495 { 1496 ZeroMemory(pfd, sizeof(*pfd)); 1497 1498 /* Copy the file data if the target is a file path */ 1499 if (*buffer) 1500 { 1501 pfd->dwFileAttributes = m_Header.dwFileAttributes; 1502 pfd->ftCreationTime = m_Header.ftCreationTime; 1503 pfd->ftLastAccessTime = m_Header.ftLastAccessTime; 1504 pfd->ftLastWriteTime = m_Header.ftLastWriteTime; 1505 pfd->nFileSizeHigh = 0; 1506 pfd->nFileSizeLow = m_Header.nFileSizeLow; 1507 1508 /* 1509 * Build temporarily a short path in pfd->cFileName (of size MAX_PATH), 1510 * then extract and store the short file name in pfd->cAlternateFileName. 1511 */ 1512 GetShortPathNameW(buffer, pfd->cFileName, _countof(pfd->cFileName)); 1513 lstrcpynW(pfd->cAlternateFileName, 1514 PathFindFileNameW(pfd->cFileName), 1515 _countof(pfd->cAlternateFileName)); 1516 1517 /* Now extract and store the long file name in pfd->cFileName */ 1518 lstrcpynW(pfd->cFileName, 1519 PathFindFileNameW(buffer), 1520 _countof(pfd->cFileName)); 1521 } 1522 } 1523 1524 /* Finally check if we have a raw path the user actually wants to retrieve */ 1525 if ((fFlags & SLGP_RAWPATH) && (m_Header.dwFlags & SLDF_HAS_EXP_SZ)) 1526 { 1527 /* Search for a target environment block */ 1528 LPEXP_SZ_LINK pInfo; 1529 pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_LINK_SIG); 1530 if (pInfo && (pInfo->cbSize == sizeof(*pInfo))) 1531 lstrcpynW(buffer, pInfo->szwTarget, cchMaxPath); 1532 } 1533 1534 /* For diagnostics purposes only... */ 1535 // NOTE: SLGP_UNCPRIORITY is unsupported 1536 fFlags &= ~(SLGP_RAWPATH | SLGP_SHORTPATH); 1537 if (fFlags) FIXME("(%p): Unsupported flags %lu\n", this, fFlags); 1538 1539 /* Copy the data back to the user */ 1540 if (*buffer) 1541 lstrcpynW(pszFile, buffer, cchMaxPath); 1542 1543 return (*buffer ? S_OK : S_FALSE); 1544 } 1545 1546 HRESULT STDMETHODCALLTYPE CShellLink::GetDescription(LPWSTR pszName, INT cchMaxName) 1547 { 1548 TRACE("(%p)->(%p len=%u)\n", this, pszName, cchMaxName); 1549 1550 *pszName = 0; 1551 if (m_sDescription) 1552 lstrcpynW(pszName, m_sDescription, cchMaxName); 1553 1554 return S_OK; 1555 } 1556 1557 HRESULT STDMETHODCALLTYPE CShellLink::SetDescription(LPCWSTR pszName) 1558 { 1559 TRACE("(%p)->(desc=%s)\n", this, debugstr_w(pszName)); 1560 1561 HeapFree(GetProcessHeap(), 0, m_sDescription); 1562 if (pszName) 1563 { 1564 m_sDescription = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, 1565 (wcslen(pszName) + 1) * sizeof(WCHAR)); 1566 if (!m_sDescription) 1567 return E_OUTOFMEMORY; 1568 1569 wcscpy(m_sDescription, pszName); 1570 } 1571 else 1572 m_sDescription = NULL; 1573 1574 m_bDirty = TRUE; 1575 1576 return S_OK; 1577 } 1578 1579 HRESULT STDMETHODCALLTYPE CShellLink::GetWorkingDirectory(LPWSTR pszDir, INT cchMaxPath) 1580 { 1581 TRACE("(%p)->(%p len %u)\n", this, pszDir, cchMaxPath); 1582 1583 if (cchMaxPath) 1584 *pszDir = 0; 1585 1586 if (m_sWorkDir) 1587 lstrcpynW(pszDir, m_sWorkDir, cchMaxPath); 1588 1589 return S_OK; 1590 } 1591 1592 HRESULT STDMETHODCALLTYPE CShellLink::SetWorkingDirectory(LPCWSTR pszDir) 1593 { 1594 TRACE("(%p)->(dir=%s)\n", this, debugstr_w(pszDir)); 1595 1596 HeapFree(GetProcessHeap(), 0, m_sWorkDir); 1597 if (pszDir) 1598 { 1599 m_sWorkDir = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, 1600 (wcslen(pszDir) + 1) * sizeof(WCHAR)); 1601 if (!m_sWorkDir) 1602 return E_OUTOFMEMORY; 1603 wcscpy(m_sWorkDir, pszDir); 1604 } 1605 else 1606 m_sWorkDir = NULL; 1607 1608 m_bDirty = TRUE; 1609 1610 return S_OK; 1611 } 1612 1613 HRESULT STDMETHODCALLTYPE CShellLink::GetArguments(LPWSTR pszArgs, INT cchMaxPath) 1614 { 1615 TRACE("(%p)->(%p len=%u)\n", this, pszArgs, cchMaxPath); 1616 1617 if (cchMaxPath) 1618 *pszArgs = 0; 1619 1620 if (m_sArgs) 1621 lstrcpynW(pszArgs, m_sArgs, cchMaxPath); 1622 1623 return S_OK; 1624 } 1625 1626 HRESULT STDMETHODCALLTYPE CShellLink::SetArguments(LPCWSTR pszArgs) 1627 { 1628 TRACE("(%p)->(args=%s)\n", this, debugstr_w(pszArgs)); 1629 1630 HeapFree(GetProcessHeap(), 0, m_sArgs); 1631 if (pszArgs) 1632 { 1633 m_sArgs = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, 1634 (wcslen(pszArgs) + 1) * sizeof(WCHAR)); 1635 if (!m_sArgs) 1636 return E_OUTOFMEMORY; 1637 1638 wcscpy(m_sArgs, pszArgs); 1639 } 1640 else 1641 m_sArgs = NULL; 1642 1643 m_bDirty = TRUE; 1644 1645 return S_OK; 1646 } 1647 1648 HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(LPWSTR pszIconPath, INT cchIconPath, INT *piIcon) 1649 { 1650 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath, cchIconPath, piIcon); 1651 1652 if (cchIconPath) 1653 *pszIconPath = 0; 1654 1655 *piIcon = 0; 1656 1657 /* Update the original icon path location */ 1658 if (m_Header.dwFlags & SLDF_HAS_EXP_ICON_SZ) 1659 { 1660 WCHAR szPath[MAX_PATH]; 1661 1662 /* Search for an icon environment block */ 1663 LPEXP_SZ_LINK pInfo; 1664 pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_ICON_SIG); 1665 if (pInfo && (pInfo->cbSize == sizeof(*pInfo))) 1666 { 1667 m_Header.dwFlags &= ~SLDF_HAS_ICONLOCATION; 1668 HeapFree(GetProcessHeap(), 0, m_sIcoPath); 1669 m_sIcoPath = NULL; 1670 1671 SHExpandEnvironmentStringsW(pInfo->szwTarget, szPath, _countof(szPath)); 1672 1673 m_sIcoPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, 1674 (wcslen(szPath) + 1) * sizeof(WCHAR)); 1675 if (!m_sIcoPath) 1676 return E_OUTOFMEMORY; 1677 1678 wcscpy(m_sIcoPath, szPath); 1679 m_Header.dwFlags |= SLDF_HAS_ICONLOCATION; 1680 1681 m_bDirty = TRUE; 1682 } 1683 } 1684 1685 *piIcon = m_Header.nIconIndex; 1686 1687 if (m_sIcoPath) 1688 lstrcpynW(pszIconPath, m_sIcoPath, cchIconPath); 1689 1690 return S_OK; 1691 } 1692 1693 static HRESULT SHELL_PidlGetIconLocationW(PCIDLIST_ABSOLUTE pidl, 1694 UINT uFlags, PWSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags) 1695 { 1696 LPCITEMIDLIST pidlLast; 1697 CComPtr<IShellFolder> psf; 1698 1699 HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); 1700 if (FAILED_UNEXPECTEDLY(hr)) 1701 return hr; 1702 1703 CComPtr<IExtractIconW> pei; 1704 hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_NULL_PPV_ARG(IExtractIconW, &pei)); 1705 if (FAILED_UNEXPECTEDLY(hr)) 1706 return hr; 1707 1708 hr = pei->GetIconLocation(uFlags, pszIconFile, cchMax, piIndex, pwFlags); 1709 if (FAILED_UNEXPECTEDLY(hr)) 1710 return hr; 1711 1712 return S_OK; 1713 } 1714 1715 HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(UINT uFlags, PWSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags) 1716 { 1717 HRESULT hr; 1718 /* 1719 * It is possible for a shell link to point to another shell link, 1720 * and in particular there is the possibility to point to itself. 1721 * Now, suppose we ask such a link to retrieve its associated icon. 1722 * This function would be called, and due to COM would be called again 1723 * recursively. To solve this issue, we forbid calling GetIconLocation() 1724 * with GIL_FORSHORTCUT set in uFlags, as done by Windows (shown by tests). 1725 */ 1726 if (uFlags & GIL_FORSHORTCUT) 1727 return E_INVALIDARG; 1728 1729 /* 1730 * Now, we set GIL_FORSHORTCUT so that: i) we allow the icon extractor 1731 * of the target to give us a suited icon, and ii) we protect ourselves 1732 * against recursive call. 1733 */ 1734 uFlags |= GIL_FORSHORTCUT; 1735 1736 if (m_pPidl || m_sPath) 1737 { 1738 /* first look for an icon using the PIDL (if present) */ 1739 if (m_pPidl) 1740 hr = SHELL_PidlGetIconLocationW(m_pPidl, uFlags, pszIconFile, cchMax, piIndex, pwFlags); 1741 else 1742 hr = E_FAIL; 1743 1744 #if 0 // FIXME: Analyse further whether this is needed... 1745 /* if we couldn't find an icon yet, look for it using the file system path */ 1746 if (FAILED(hr) && m_sPath) 1747 { 1748 LPITEMIDLIST pidl; 1749 CComPtr<IShellFolder> pdsk; 1750 1751 hr = SHGetDesktopFolder(&pdsk); 1752 1753 /* LPITEMIDLIST pidl = ILCreateFromPathW(sPath); */ 1754 hr = pdsk->ParseDisplayName(0, NULL, m_sPath, NULL, &pidl, NULL); 1755 if (SUCCEEDED(hr)) 1756 { 1757 hr = SHELL_PidlGetIconLocationW(pidl, uFlags, pszIconFile, cchMax, piIndex, pwFlags); 1758 SHFree(pidl); 1759 } 1760 } 1761 #endif 1762 return hr; 1763 } 1764 1765 return S_OK; 1766 } 1767 1768 HRESULT STDMETHODCALLTYPE 1769 CShellLink::Extract(PCWSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) 1770 { 1771 SHFILEINFOW info; 1772 1773 if (phiconLarge) 1774 { 1775 SHGetFileInfoW(pszFile, 0, &info, sizeof(info), 1776 SHGFI_ICON | SHGFI_LARGEICON | SHGFI_LINKOVERLAY); 1777 *phiconLarge = info.hIcon; 1778 if (!info.hIcon) 1779 return E_FAIL; 1780 } 1781 1782 if (phiconSmall) 1783 { 1784 SHGetFileInfoW(pszFile, 0, &info, sizeof(info), 1785 SHGFI_ICON | SHGFI_SMALLICON | SHGFI_LINKOVERLAY); 1786 *phiconSmall = info.hIcon; 1787 if (!info.hIcon) 1788 return E_FAIL; 1789 } 1790 1791 return S_OK; 1792 } 1793 1794 #if 0 1795 /* Extends the functionality of PathUnExpandEnvStringsW */ 1796 BOOL PathFullyUnExpandEnvStringsW( 1797 _In_ LPCWSTR pszPath, 1798 _Out_ LPWSTR pszBuf, 1799 _In_ UINT cchBuf) 1800 { 1801 BOOL Ret = FALSE; // Set to TRUE as soon as PathUnExpandEnvStrings starts unexpanding. 1802 BOOL res; 1803 LPCWSTR p; 1804 1805 // *pszBuf = L'\0'; 1806 while (*pszPath && cchBuf > 0) 1807 { 1808 /* Attempt unexpanding the path */ 1809 res = PathUnExpandEnvStringsW(pszPath, pszBuf, cchBuf); 1810 if (!res) 1811 { 1812 /* The unexpansion failed. Try to find a path delimiter. */ 1813 p = wcspbrk(pszPath, L" /\\:*?\"<>|%"); 1814 if (!p) /* None found, we will copy the remaining path */ 1815 p = pszPath + wcslen(pszPath); 1816 else /* Found one, we will copy the delimiter and skip it */ 1817 ++p; 1818 /* If we overflow, we cannot unexpand more, so return FALSE */ 1819 if (p - pszPath >= cchBuf) 1820 return FALSE; // *pszBuf = L'\0'; 1821 1822 /* Copy the untouched portion of path up to the delimiter, included */ 1823 wcsncpy(pszBuf, pszPath, p - pszPath); 1824 pszBuf[p - pszPath] = L'\0'; // NULL-terminate 1825 1826 /* Advance the pointers and decrease the remaining buffer size */ 1827 cchBuf -= (p - pszPath); 1828 pszBuf += (p - pszPath); 1829 pszPath += (p - pszPath); 1830 } 1831 else 1832 { 1833 /* 1834 * The unexpansion succeeded. Skip the unexpanded part by trying 1835 * to find where the original path and the unexpanded string 1836 * become different. 1837 * NOTE: An alternative(?) would be to stop also at the last 1838 * path delimiter encountered in the loop (i.e. would be the 1839 * first path delimiter in the strings). 1840 */ 1841 LPWSTR q; 1842 1843 /* 1844 * The algorithm starts at the end of the strings and loops back 1845 * while the characters are equal, until it finds a discrepancy. 1846 */ 1847 p = pszPath + wcslen(pszPath); 1848 q = pszBuf + wcslen(pszBuf); // This wcslen should be < cchBuf 1849 while ((*p == *q) && (p > pszPath) && (q > pszBuf)) 1850 { 1851 --p; --q; 1852 } 1853 /* Skip discrepancy */ 1854 ++p; ++q; 1855 1856 /* Advance the pointers and decrease the remaining buffer size */ 1857 cchBuf -= (q - pszBuf); 1858 pszBuf = q; 1859 pszPath = p; 1860 1861 Ret = TRUE; 1862 } 1863 } 1864 1865 return Ret; 1866 } 1867 #endif 1868 1869 HRESULT STDMETHODCALLTYPE CShellLink::SetIconLocation(LPCWSTR pszIconPath, INT iIcon) 1870 { 1871 HRESULT hr = E_FAIL; 1872 WCHAR szUnExpIconPath[MAX_PATH]; 1873 1874 TRACE("(%p)->(path=%s iicon=%u)\n", this, debugstr_w(pszIconPath), iIcon); 1875 1876 if (pszIconPath) 1877 { 1878 /* Try to fully unexpand the icon path */ 1879 1880 /* 1881 * Check whether the user-given file path contains unexpanded 1882 * environment variables. If so, create a target environment block. 1883 * Note that in this block we will store the user-given path. 1884 * It will contain the unexpanded environment variables, but 1885 * it can also contain already expanded path that the user does 1886 * not want to see them unexpanded (e.g. so that they always 1887 * refer to the same place even if the would-be corresponding 1888 * environment variable could change). 1889 */ 1890 // FIXME: http://stackoverflow.com/questions/2976489/ishelllinkseticonlocation-translates-my-icon-path-into-program-files-which-i 1891 // if (PathFullyUnExpandEnvStringsW(pszIconPath, szUnExpIconPath, _countof(szUnExpIconPath))) 1892 SHExpandEnvironmentStringsW(pszIconPath, szUnExpIconPath, _countof(szUnExpIconPath)); 1893 if (wcscmp(pszIconPath, szUnExpIconPath) != 0) 1894 { 1895 /* Unexpansion succeeded, so we need an icon environment block */ 1896 EXP_SZ_LINK buffer; 1897 LPEXP_SZ_LINK pInfo; 1898 1899 pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_ICON_SIG); 1900 if (pInfo) 1901 { 1902 /* Make sure that the size of the structure is valid */ 1903 if (pInfo->cbSize != sizeof(*pInfo)) 1904 { 1905 ERR("Ooops. This structure is not as expected...\n"); 1906 1907 /* Invalid structure, remove it altogether */ 1908 m_Header.dwFlags &= ~SLDF_HAS_EXP_ICON_SZ; 1909 RemoveDataBlock(EXP_SZ_ICON_SIG); 1910 1911 /* Reset the pointer and go use the static buffer */ 1912 pInfo = NULL; 1913 } 1914 } 1915 if (!pInfo) 1916 { 1917 /* Use the static buffer */ 1918 pInfo = &buffer; 1919 buffer.cbSize = sizeof(buffer); 1920 buffer.dwSignature = EXP_SZ_ICON_SIG; 1921 } 1922 1923 lstrcpynW(pInfo->szwTarget, szUnExpIconPath, _countof(pInfo->szwTarget)); 1924 WideCharToMultiByte(CP_ACP, 0, szUnExpIconPath, -1, 1925 pInfo->szTarget, _countof(pInfo->szTarget), NULL, NULL); 1926 1927 hr = S_OK; 1928 if (pInfo == &buffer) 1929 hr = AddDataBlock(pInfo); 1930 if (hr == S_OK) 1931 m_Header.dwFlags |= SLDF_HAS_EXP_ICON_SZ; 1932 } 1933 else 1934 { 1935 /* Unexpansion failed, so we need to remove any icon environment block */ 1936 m_Header.dwFlags &= ~SLDF_HAS_EXP_ICON_SZ; 1937 RemoveDataBlock(EXP_SZ_ICON_SIG); 1938 } 1939 } 1940 1941 /* Store the original icon path location (this one may contain unexpanded environment strings) */ 1942 if (pszIconPath) 1943 { 1944 m_Header.dwFlags &= ~SLDF_HAS_ICONLOCATION; 1945 HeapFree(GetProcessHeap(), 0, m_sIcoPath); 1946 m_sIcoPath = NULL; 1947 1948 m_sIcoPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, 1949 (wcslen(pszIconPath) + 1) * sizeof(WCHAR)); 1950 if (!m_sIcoPath) 1951 return E_OUTOFMEMORY; 1952 1953 wcscpy(m_sIcoPath, pszIconPath); 1954 m_Header.dwFlags |= SLDF_HAS_ICONLOCATION; 1955 } 1956 1957 hr = S_OK; 1958 1959 m_Header.nIconIndex = iIcon; 1960 m_bDirty = TRUE; 1961 1962 return hr; 1963 } 1964 1965 HRESULT STDMETHODCALLTYPE CShellLink::SetRelativePath(LPCWSTR pszPathRel, DWORD dwReserved) 1966 { 1967 TRACE("(%p)->(path=%s %x)\n", this, debugstr_w(pszPathRel), dwReserved); 1968 1969 HeapFree(GetProcessHeap(), 0, m_sPathRel); 1970 if (pszPathRel) 1971 { 1972 m_sPathRel = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, 1973 (wcslen(pszPathRel) + 1) * sizeof(WCHAR)); 1974 if (!m_sPathRel) 1975 return E_OUTOFMEMORY; 1976 wcscpy(m_sPathRel, pszPathRel); 1977 } 1978 else 1979 m_sPathRel = NULL; 1980 1981 m_bDirty = TRUE; 1982 1983 return ShellLink_UpdatePath(m_sPathRel, m_sPath, m_sWorkDir, &m_sPath); 1984 } 1985 1986 static LPWSTR GetAdvertisedArg(LPCWSTR str) 1987 { 1988 if (!str) 1989 return NULL; 1990 1991 LPCWSTR p = wcschr(str, L':'); 1992 if (!p) 1993 return NULL; 1994 1995 DWORD len = p - str; 1996 LPWSTR ret = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (len + 1)); 1997 if (!ret) 1998 return ret; 1999 2000 memcpy(ret, str, sizeof(WCHAR)*len); 2001 ret[len] = 0; 2002 return ret; 2003 } 2004 2005 HRESULT CShellLink::WriteAdvertiseInfo(LPCWSTR string, DWORD dwSig) 2006 { 2007 EXP_DARWIN_LINK buffer; 2008 LPEXP_DARWIN_LINK pInfo; 2009 2010 if ( (dwSig != EXP_DARWIN_ID_SIG) 2011 #if (NTDDI_VERSION < NTDDI_LONGHORN) 2012 && (dwSig != EXP_LOGO3_ID_SIG) 2013 #endif 2014 ) 2015 { 2016 return E_INVALIDARG; 2017 } 2018 2019 if (!string) 2020 return S_FALSE; 2021 2022 pInfo = (LPEXP_DARWIN_LINK)SHFindDataBlock(m_pDBList, dwSig); 2023 if (pInfo) 2024 { 2025 /* Make sure that the size of the structure is valid */ 2026 if (pInfo->dbh.cbSize != sizeof(*pInfo)) 2027 { 2028 ERR("Ooops. This structure is not as expected...\n"); 2029 2030 /* Invalid structure, remove it altogether */ 2031 if (dwSig == EXP_DARWIN_ID_SIG) 2032 m_Header.dwFlags &= ~SLDF_HAS_DARWINID; 2033 #if (NTDDI_VERSION < NTDDI_LONGHORN) 2034 else if (dwSig == EXP_LOGO3_ID_SIG) 2035 m_Header.dwFlags &= ~SLDF_HAS_LOGO3ID; 2036 #endif 2037 RemoveDataBlock(dwSig); 2038 2039 /* Reset the pointer and go use the static buffer */ 2040 pInfo = NULL; 2041 } 2042 } 2043 if (!pInfo) 2044 { 2045 /* Use the static buffer */ 2046 pInfo = &buffer; 2047 buffer.dbh.cbSize = sizeof(buffer); 2048 buffer.dbh.dwSignature = dwSig; 2049 } 2050 2051 lstrcpynW(pInfo->szwDarwinID, string, _countof(pInfo->szwDarwinID)); 2052 WideCharToMultiByte(CP_ACP, 0, string, -1, 2053 pInfo->szDarwinID, _countof(pInfo->szDarwinID), NULL, NULL); 2054 2055 HRESULT hr = S_OK; 2056 if (pInfo == &buffer) 2057 hr = AddDataBlock(pInfo); 2058 if (hr == S_OK) 2059 { 2060 if (dwSig == EXP_DARWIN_ID_SIG) 2061 m_Header.dwFlags |= SLDF_HAS_DARWINID; 2062 #if (NTDDI_VERSION < NTDDI_LONGHORN) 2063 else if (dwSig == EXP_LOGO3_ID_SIG) 2064 m_Header.dwFlags |= SLDF_HAS_LOGO3ID; 2065 #endif 2066 } 2067 2068 return hr; 2069 } 2070 2071 HRESULT CShellLink::SetAdvertiseInfo(LPCWSTR str) 2072 { 2073 HRESULT hr; 2074 LPCWSTR szComponent = NULL, szProduct = NULL, p; 2075 INT len; 2076 GUID guid; 2077 WCHAR szGuid[38+1]; 2078 2079 /**/sProduct = sComponent = NULL;/**/ 2080 2081 while (str[0]) 2082 { 2083 /* each segment must start with two colons */ 2084 if (str[0] != ':' || str[1] != ':') 2085 return E_FAIL; 2086 2087 /* the last segment is just two colons */ 2088 if (!str[2]) 2089 break; 2090 str += 2; 2091 2092 /* there must be a colon straight after a guid */ 2093 p = wcschr(str, L':'); 2094 if (!p) 2095 return E_FAIL; 2096 len = p - str; 2097 if (len != 38) 2098 return E_FAIL; 2099 2100 /* get the guid, and check if it's validly formatted */ 2101 memcpy(szGuid, str, sizeof(WCHAR)*len); 2102 szGuid[len] = 0; 2103 2104 hr = CLSIDFromString(szGuid, &guid); 2105 if (hr != S_OK) 2106 return hr; 2107 str = p + 1; 2108 2109 /* match it up to a guid that we care about */ 2110 if (IsEqualGUID(guid, SHELL32_AdvtShortcutComponent) && !szComponent) 2111 szComponent = str; /* Darwin */ 2112 else if (IsEqualGUID(guid, SHELL32_AdvtShortcutProduct) && !szProduct) 2113 szProduct = str; /* Logo3 */ 2114 else 2115 return E_FAIL; 2116 2117 /* skip to the next field */ 2118 str = wcschr(str, L':'); 2119 if (!str) 2120 return E_FAIL; 2121 } 2122 2123 /* we have to have a component for an advertised shortcut */ 2124 if (!szComponent) 2125 return E_FAIL; 2126 2127 szComponent = GetAdvertisedArg(szComponent); 2128 szProduct = GetAdvertisedArg(szProduct); 2129 2130 hr = WriteAdvertiseInfo(szComponent, EXP_DARWIN_ID_SIG); 2131 // if (FAILED(hr)) 2132 // return hr; 2133 #if (NTDDI_VERSION < NTDDI_LONGHORN) 2134 hr = WriteAdvertiseInfo(szProduct, EXP_LOGO3_ID_SIG); 2135 // if (FAILED(hr)) 2136 // return hr; 2137 #endif 2138 2139 HeapFree(GetProcessHeap(), 0, (PVOID)szComponent); 2140 HeapFree(GetProcessHeap(), 0, (PVOID)szProduct); 2141 2142 if (TRACE_ON(shell)) 2143 { 2144 GetAdvertiseInfo(&sComponent, EXP_DARWIN_ID_SIG); 2145 TRACE("Component = %s\n", debugstr_w(sComponent)); 2146 #if (NTDDI_VERSION < NTDDI_LONGHORN) 2147 GetAdvertiseInfo(&sProduct, EXP_LOGO3_ID_SIG); 2148 TRACE("Product = %s\n", debugstr_w(sProduct)); 2149 #endif 2150 } 2151 2152 return S_OK; 2153 } 2154 2155 /* 2156 * Since the real PathResolve (from Wine) is unimplemented at the moment, 2157 * we use this local implementation, until a better one is written (using 2158 * code parts of the SHELL_xxx helpers in Wine's shellpath.c). 2159 */ 2160 static BOOL HACKISH_PathResolve( 2161 IN OUT PWSTR pszPath, 2162 IN PZPCWSTR dirs OPTIONAL, 2163 IN UINT fFlags) 2164 { 2165 // FIXME: This is unimplemented!!! 2166 #if 0 2167 return PathResolve(pszPath, dirs, fFlags); 2168 #else 2169 BOOL Success = FALSE; 2170 USHORT i; 2171 LPWSTR fname = NULL; 2172 WCHAR szPath[MAX_PATH]; 2173 2174 /* First, search for a valid existing path */ 2175 2176 // NOTE: See also: SHELL_FindExecutable() 2177 2178 /* 2179 * List of extensions searched for, by PathResolve with the flag 2180 * PRF_TRYPROGRAMEXTENSIONS == PRF_EXECUTABLE | PRF_VERIFYEXISTS set, 2181 * according to MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/bb776478(v=vs.85).aspx 2182 */ 2183 static PCWSTR Extensions[] = {L".pif", L".com", L".bat", L".cmd", L".lnk", L".exe", NULL}; 2184 #define LNK_EXT_INDEX 4 // ".lnk" has index 4 in the array above 2185 2186 /* 2187 * Start at the beginning of the list if PRF_EXECUTABLE is set, otherwise 2188 * just use the last element 'NULL' (no extension checking). 2189 */ 2190 i = ((fFlags & PRF_EXECUTABLE) ? 0 : _countof(Extensions) - 1); 2191 for (; i < _countof(Extensions); ++i) 2192 { 2193 /* Ignore shell links ".lnk" if needed */ 2194 if ((fFlags & PRF_DONTFINDLNK) && (i == LNK_EXT_INDEX)) 2195 continue; 2196 2197 Success = (SearchPathW(NULL, pszPath, Extensions[i], 2198 _countof(szPath), szPath, NULL) != 0); 2199 if (!Success) 2200 { 2201 ERR("SearchPathW(pszPath = '%S') failed\n", pszPath); 2202 } 2203 else 2204 { 2205 ERR("SearchPathW(pszPath = '%S', szPath = '%S') succeeded\n", pszPath, szPath); 2206 break; 2207 } 2208 } 2209 2210 if (!Success) 2211 { 2212 ERR("SearchPathW(pszPath = '%S') failed\n", pszPath); 2213 2214 /* We failed, try with PathFindOnPath, as explained by MSDN */ 2215 // Success = PathFindOnPathW(pszPath, dirs); 2216 StringCchCopyW(szPath, _countof(szPath), pszPath); 2217 Success = PathFindOnPathW(szPath, dirs); 2218 if (!Success) 2219 { 2220 ERR("PathFindOnPathW(pszPath = '%S') failed\n", pszPath); 2221 2222 /* We failed again, fall back to building a possible non-existing path */ 2223 if (!GetFullPathNameW(pszPath, _countof(szPath), szPath, &fname)) 2224 { 2225 ERR("GetFullPathNameW(pszPath = '%S') failed\n", pszPath); 2226 return FALSE; 2227 } 2228 2229 Success = PathFileExistsW(szPath); 2230 if (!Success) 2231 ERR("PathFileExistsW(szPath = '%S') failed\n", szPath); 2232 2233 /******************************************************/ 2234 /* Question: Why this line is needed only for files?? */ 2235 if (fname && (_wcsicmp(pszPath, fname) == 0)) 2236 *szPath = L'\0'; 2237 /******************************************************/ 2238 } 2239 else 2240 { 2241 ERR("PathFindOnPathW(pszPath = '%S' ==> '%S') succeeded\n", pszPath, szPath); 2242 } 2243 } 2244 2245 /* Copy back the results to the caller */ 2246 StringCchCopyW(pszPath, MAX_PATH, szPath); 2247 2248 /* 2249 * Since the called functions always checked whether the file path existed, 2250 * we do not need to redo a final check: we can use instead the cached 2251 * result in 'Success'. 2252 */ 2253 return ((fFlags & PRF_VERIFYEXISTS) ? Success : TRUE); 2254 #endif 2255 } 2256 2257 HRESULT CShellLink::SetTargetFromPIDLOrPath(LPCITEMIDLIST pidl, LPCWSTR pszFile) 2258 { 2259 HRESULT hr = S_OK; 2260 LPITEMIDLIST pidlNew = NULL; 2261 WCHAR szPath[MAX_PATH]; 2262 2263 /* 2264 * Not both 'pidl' and 'pszFile' should be set. 2265 * But either one or both can be NULL. 2266 */ 2267 if (pidl && pszFile) 2268 return E_FAIL; 2269 2270 if (pidl) 2271 { 2272 /* Clone the PIDL */ 2273 pidlNew = ILClone(pidl); 2274 if (!pidlNew) 2275 return E_FAIL; 2276 } 2277 else if (pszFile) 2278 { 2279 /* Build a PIDL for this path target */ 2280 hr = SHILCreateFromPathW(pszFile, &pidlNew, NULL); 2281 if (FAILED(hr)) 2282 { 2283 /* This failed, try to resolve the path, then create a simple PIDL */ 2284 2285 StringCchCopyW(szPath, _countof(szPath), pszFile); 2286 // FIXME: Because PathResolve is unimplemented, we use our hackish implementation! 2287 HACKISH_PathResolve(szPath, NULL, PRF_TRYPROGRAMEXTENSIONS); 2288 2289 pidlNew = SHSimpleIDListFromPathW(szPath); 2290 /******************************************************/ 2291 /* Question: Why this line is needed only for files?? */ 2292 hr = (*szPath ? S_OK : E_INVALIDARG); // S_FALSE 2293 /******************************************************/ 2294 } 2295 } 2296 // else if (!pidl && !pszFile) { pidlNew = NULL; hr = S_OK; } 2297 2298 ILFree(m_pPidl); 2299 m_pPidl = pidlNew; 2300 2301 if (!pszFile) 2302 { 2303 if (SHGetPathFromIDListW(pidlNew, szPath)) 2304 pszFile = szPath; 2305 } 2306 2307 // TODO: Fully update link info, tracker, file attribs... 2308 2309 // if (pszFile) 2310 if (!pszFile) 2311 { 2312 *szPath = L'\0'; 2313 pszFile = szPath; 2314 } 2315 2316 /* Update the cached path (for link info) */ 2317 ShellLink_GetVolumeInfo(pszFile, &volume); 2318 m_sPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, 2319 (wcslen(pszFile) + 1) * sizeof(WCHAR)); 2320 if (!m_sPath) 2321 return E_OUTOFMEMORY; 2322 wcscpy(m_sPath, pszFile); 2323 2324 m_bDirty = TRUE; 2325 return hr; 2326 } 2327 2328 HRESULT STDMETHODCALLTYPE CShellLink::SetPath(LPCWSTR pszFile) 2329 { 2330 LPWSTR unquoted = NULL; 2331 HRESULT hr = S_OK; 2332 2333 TRACE("(%p)->(path=%s)\n", this, debugstr_w(pszFile)); 2334 2335 if (!pszFile) 2336 return E_INVALIDARG; 2337 2338 /* 2339 * Allow upgrading Logo3 shortcuts (m_Header.dwFlags & SLDF_HAS_LOGO3ID), 2340 * but forbid upgrading Darwin ones. 2341 */ 2342 if (m_Header.dwFlags & SLDF_HAS_DARWINID) 2343 return S_FALSE; 2344 2345 /* quotes at the ends of the string are stripped */ 2346 SIZE_T len = wcslen(pszFile); 2347 if (pszFile[0] == L'"' && pszFile[len-1] == L'"') 2348 { 2349 unquoted = strdupW(pszFile); 2350 PathUnquoteSpacesW(unquoted); 2351 pszFile = unquoted; 2352 } 2353 2354 /* any other quote marks are invalid */ 2355 if (wcschr(pszFile, L'"')) 2356 { 2357 hr = S_FALSE; 2358 goto end; 2359 } 2360 2361 /* Clear the cached path */ 2362 HeapFree(GetProcessHeap(), 0, m_sPath); 2363 m_sPath = NULL; 2364 2365 /* Check for an advertised target (Logo3 or Darwin) */ 2366 if (SetAdvertiseInfo(pszFile) != S_OK) 2367 { 2368 /* This is not an advertised target, but a regular path */ 2369 WCHAR szPath[MAX_PATH]; 2370 2371 /* 2372 * Check whether the user-given file path contains unexpanded 2373 * environment variables. If so, create a target environment block. 2374 * Note that in this block we will store the user-given path. 2375 * It will contain the unexpanded environment variables, but 2376 * it can also contain already expanded path that the user does 2377 * not want to see them unexpanded (e.g. so that they always 2378 * refer to the same place even if the would-be corresponding 2379 * environment variable could change). 2380 */ 2381 if (*pszFile) 2382 SHExpandEnvironmentStringsW(pszFile, szPath, _countof(szPath)); 2383 else 2384 *szPath = L'\0'; 2385 2386 if (*pszFile && (wcscmp(pszFile, szPath) != 0)) 2387 { 2388 /* 2389 * The user-given file path contains unexpanded environment 2390 * variables, so we need a target environment block. 2391 */ 2392 EXP_SZ_LINK buffer; 2393 LPEXP_SZ_LINK pInfo; 2394 2395 pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_LINK_SIG); 2396 if (pInfo) 2397 { 2398 /* Make sure that the size of the structure is valid */ 2399 if (pInfo->cbSize != sizeof(*pInfo)) 2400 { 2401 ERR("Ooops. This structure is not as expected...\n"); 2402 2403 /* Invalid structure, remove it altogether */ 2404 m_Header.dwFlags &= ~SLDF_HAS_EXP_SZ; 2405 RemoveDataBlock(EXP_SZ_LINK_SIG); 2406 2407 /* Reset the pointer and go use the static buffer */ 2408 pInfo = NULL; 2409 } 2410 } 2411 if (!pInfo) 2412 { 2413 /* Use the static buffer */ 2414 pInfo = &buffer; 2415 buffer.cbSize = sizeof(buffer); 2416 buffer.dwSignature = EXP_SZ_LINK_SIG; 2417 } 2418 2419 lstrcpynW(pInfo->szwTarget, pszFile, _countof(pInfo->szwTarget)); 2420 WideCharToMultiByte(CP_ACP, 0, pszFile, -1, 2421 pInfo->szTarget, _countof(pInfo->szTarget), NULL, NULL); 2422 2423 hr = S_OK; 2424 if (pInfo == &buffer) 2425 hr = AddDataBlock(pInfo); 2426 if (hr == S_OK) 2427 m_Header.dwFlags |= SLDF_HAS_EXP_SZ; 2428 2429 /* Now, make pszFile point to the expanded buffer */ 2430 pszFile = szPath; 2431 } 2432 else 2433 { 2434 /* 2435 * The user-given file path does not contain unexpanded environment 2436 * variables, so we need to remove any target environment block. 2437 */ 2438 m_Header.dwFlags &= ~SLDF_HAS_EXP_SZ; 2439 RemoveDataBlock(EXP_SZ_LINK_SIG); 2440 2441 /* pszFile points to the user path */ 2442 } 2443 2444 /* Set the target */ 2445 hr = SetTargetFromPIDLOrPath(NULL, pszFile); 2446 } 2447 2448 m_bDirty = TRUE; 2449 2450 end: 2451 HeapFree(GetProcessHeap(), 0, unquoted); 2452 return hr; 2453 } 2454 2455 HRESULT STDMETHODCALLTYPE CShellLink::AddDataBlock(void* pDataBlock) 2456 { 2457 if (SHAddDataBlock(&m_pDBList, (DATABLOCK_HEADER*)pDataBlock)) 2458 { 2459 m_bDirty = TRUE; 2460 return S_OK; 2461 } 2462 return S_FALSE; 2463 } 2464 2465 HRESULT STDMETHODCALLTYPE CShellLink::CopyDataBlock(DWORD dwSig, void** ppDataBlock) 2466 { 2467 DATABLOCK_HEADER* pBlock; 2468 PVOID pDataBlock; 2469 2470 TRACE("%p %08x %p\n", this, dwSig, ppDataBlock); 2471 2472 *ppDataBlock = NULL; 2473 2474 pBlock = SHFindDataBlock(m_pDBList, dwSig); 2475 if (!pBlock) 2476 { 2477 ERR("unknown datablock %08x (not found)\n", dwSig); 2478 return E_FAIL; 2479 } 2480 2481 pDataBlock = LocalAlloc(LMEM_ZEROINIT, pBlock->cbSize); 2482 if (!pDataBlock) 2483 return E_OUTOFMEMORY; 2484 2485 CopyMemory(pDataBlock, pBlock, pBlock->cbSize); 2486 2487 *ppDataBlock = pDataBlock; 2488 return S_OK; 2489 } 2490 2491 HRESULT STDMETHODCALLTYPE CShellLink::RemoveDataBlock(DWORD dwSig) 2492 { 2493 if (SHRemoveDataBlock(&m_pDBList, dwSig)) 2494 { 2495 m_bDirty = TRUE; 2496 return S_OK; 2497 } 2498 return S_FALSE; 2499 } 2500 2501 HRESULT STDMETHODCALLTYPE CShellLink::GetFlags(DWORD *pdwFlags) 2502 { 2503 TRACE("%p %p\n", this, pdwFlags); 2504 *pdwFlags = m_Header.dwFlags; 2505 return S_OK; 2506 } 2507 2508 HRESULT STDMETHODCALLTYPE CShellLink::SetFlags(DWORD dwFlags) 2509 { 2510 #if 0 // FIXME! 2511 m_Header.dwFlags = dwFlags; 2512 m_bDirty = TRUE; 2513 return S_OK; 2514 #else 2515 FIXME("\n"); 2516 return E_NOTIMPL; 2517 #endif 2518 } 2519 2520 /************************************************************************** 2521 * CShellLink implementation of IShellExtInit::Initialize() 2522 * 2523 * Loads the shelllink from the dataobject the shell is pointing to. 2524 */ 2525 HRESULT STDMETHODCALLTYPE CShellLink::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID) 2526 { 2527 TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID); 2528 2529 if (!pdtobj) 2530 return E_FAIL; 2531 2532 FORMATETC format; 2533 format.cfFormat = CF_HDROP; 2534 format.ptd = NULL; 2535 format.dwAspect = DVASPECT_CONTENT; 2536 format.lindex = -1; 2537 format.tymed = TYMED_HGLOBAL; 2538 2539 STGMEDIUM stgm; 2540 HRESULT hr = pdtobj->GetData(&format, &stgm); 2541 if (FAILED(hr)) 2542 return hr; 2543 2544 UINT count = DragQueryFileW((HDROP)stgm.hGlobal, -1, NULL, 0); 2545 if (count == 1) 2546 { 2547 count = DragQueryFileW((HDROP)stgm.hGlobal, 0, NULL, 0); 2548 count++; 2549 LPWSTR path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR)); 2550 if (path) 2551 { 2552 count = DragQueryFileW((HDROP)stgm.hGlobal, 0, path, count); 2553 hr = Load(path, 0); 2554 HeapFree(GetProcessHeap(), 0, path); 2555 } 2556 } 2557 ReleaseStgMedium(&stgm); 2558 2559 return S_OK; 2560 } 2561 2562 HRESULT STDMETHODCALLTYPE CShellLink::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 2563 { 2564 int id = 1; 2565 2566 TRACE("%p %p %u %u %u %u\n", this, 2567 hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); 2568 2569 if (!hMenu) 2570 return E_INVALIDARG; 2571 2572 WCHAR wszOpen[20]; 2573 if (!LoadStringW(shell32_hInstance, IDS_OPEN_VERB, wszOpen, _countof(wszOpen))) 2574 *wszOpen = L'\0'; 2575 2576 MENUITEMINFOW mii; 2577 ZeroMemory(&mii, sizeof(mii)); 2578 mii.cbSize = sizeof(mii); 2579 mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE; 2580 mii.dwTypeData = wszOpen; 2581 mii.cch = wcslen(mii.dwTypeData); 2582 mii.wID = idCmdFirst + id++; 2583 mii.fState = MFS_DEFAULT | MFS_ENABLED; 2584 mii.fType = MFT_STRING; 2585 if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii)) 2586 return E_FAIL; 2587 m_iIdOpen = 1; 2588 2589 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id); 2590 } 2591 2592 HRESULT STDMETHODCALLTYPE CShellLink::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) 2593 { 2594 LPWSTR args = NULL; 2595 LPWSTR path = NULL; 2596 2597 TRACE("%p %p\n", this, lpici); 2598 2599 if (lpici->cbSize < sizeof(CMINVOKECOMMANDINFO)) 2600 return E_INVALIDARG; 2601 2602 // NOTE: We could use lpici->hwnd (certainly in case lpici->fMask doesn't contain CMIC_MASK_FLAG_NO_UI) 2603 // as the parent window handle... ? 2604 /* FIXME: get using interface set from IObjectWithSite?? */ 2605 // NOTE: We might need an extended version of Resolve that provides us with paths... 2606 HRESULT hr = Resolve(lpici->hwnd, 0); 2607 if (FAILED(hr)) 2608 { 2609 TRACE("failed to resolve component with error 0x%08x", hr); 2610 return hr; 2611 } 2612 2613 path = strdupW(m_sPath); 2614 2615 if ( lpici->cbSize == sizeof(CMINVOKECOMMANDINFOEX) && 2616 (lpici->fMask & CMIC_MASK_UNICODE) ) 2617 { 2618 LPCMINVOKECOMMANDINFOEX iciex = (LPCMINVOKECOMMANDINFOEX)lpici; 2619 SIZE_T len = 2; 2620 2621 if (m_sArgs) 2622 len += wcslen(m_sArgs); 2623 if (iciex->lpParametersW) 2624 len += wcslen(iciex->lpParametersW); 2625 2626 args = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 2627 *args = 0; 2628 if (m_sArgs) 2629 wcscat(args, m_sArgs); 2630 if (iciex->lpParametersW) 2631 { 2632 wcscat(args, L" "); 2633 wcscat(args, iciex->lpParametersW); 2634 } 2635 } 2636 else if (m_sArgs != NULL) 2637 { 2638 args = strdupW(m_sArgs); 2639 } 2640 2641 SHELLEXECUTEINFOW sei; 2642 ZeroMemory(&sei, sizeof(sei)); 2643 sei.cbSize = sizeof(sei); 2644 sei.fMask = SEE_MASK_HASLINKNAME | SEE_MASK_UNICODE | 2645 (lpici->fMask & (SEE_MASK_NOASYNC | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI)); 2646 sei.lpFile = path; 2647 sei.lpClass = m_sLinkPath; 2648 sei.nShow = m_Header.nShowCommand; 2649 sei.lpDirectory = m_sWorkDir; 2650 sei.lpParameters = args; 2651 sei.lpVerb = L"open"; 2652 2653 // HACK for ShellExecuteExW 2654 if (m_sPath && wcsstr(m_sPath, L".cpl")) 2655 sei.lpVerb = L"cplopen"; 2656 2657 if (ShellExecuteExW(&sei)) 2658 hr = S_OK; 2659 else 2660 hr = E_FAIL; 2661 2662 HeapFree(GetProcessHeap(), 0, args); 2663 HeapFree(GetProcessHeap(), 0, path); 2664 2665 return hr; 2666 } 2667 2668 HRESULT STDMETHODCALLTYPE CShellLink::GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax) 2669 { 2670 FIXME("%p %lu %u %p %p %u\n", this, idCmd, uType, pwReserved, pszName, cchMax); 2671 return E_NOTIMPL; 2672 } 2673 2674 INT_PTR CALLBACK ExtendedShortcutProc(HWND hwndDlg, UINT uMsg, 2675 WPARAM wParam, LPARAM lParam) 2676 { 2677 switch(uMsg) 2678 { 2679 case WM_INITDIALOG: 2680 if (lParam) 2681 { 2682 HWND hDlgCtrl = GetDlgItem(hwndDlg, IDC_SHORTEX_RUN_DIFFERENT); 2683 SendMessage(hDlgCtrl, BM_SETCHECK, BST_CHECKED, 0); 2684 } 2685 return TRUE; 2686 case WM_COMMAND: 2687 { 2688 HWND hDlgCtrl = GetDlgItem(hwndDlg, IDC_SHORTEX_RUN_DIFFERENT); 2689 if (LOWORD(wParam) == IDOK) 2690 { 2691 if (SendMessage(hDlgCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED) 2692 EndDialog(hwndDlg, 1); 2693 else 2694 EndDialog(hwndDlg, 0); 2695 } 2696 else if (LOWORD(wParam) == IDCANCEL) 2697 { 2698 EndDialog(hwndDlg, -1); 2699 } 2700 else if (LOWORD(wParam) == IDC_SHORTEX_RUN_DIFFERENT) 2701 { 2702 if (SendMessage(hDlgCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED) 2703 SendMessage(hDlgCtrl, BM_SETCHECK, BST_UNCHECKED, 0); 2704 else 2705 SendMessage(hDlgCtrl, BM_SETCHECK, BST_CHECKED, 0); 2706 } 2707 } 2708 } 2709 return FALSE; 2710 } 2711 2712 EXTERN_C HRESULT 2713 WINAPI 2714 SHOpenFolderAndSelectItems(LPITEMIDLIST pidlFolder, 2715 UINT cidl, 2716 PCUITEMID_CHILD_ARRAY apidl, 2717 DWORD dwFlags); 2718 2719 /************************************************************************** 2720 * SH_GetTargetTypeByPath 2721 * 2722 * Function to get target type by passing full path to it 2723 */ 2724 LPWSTR SH_GetTargetTypeByPath(LPCWSTR lpcwFullPath) 2725 { 2726 LPCWSTR pwszExt; 2727 static WCHAR wszBuf[MAX_PATH]; 2728 2729 /* Get file information */ 2730 SHFILEINFOW fi; 2731 if (!SHGetFileInfoW(lpcwFullPath, 0, &fi, sizeof(fi), SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES)) 2732 { 2733 ERR("SHGetFileInfoW failed for %ls (%lu)\n", lpcwFullPath, GetLastError()); 2734 fi.szTypeName[0] = L'\0'; 2735 fi.hIcon = NULL; 2736 } 2737 2738 pwszExt = PathFindExtensionW(lpcwFullPath); 2739 if (pwszExt[0]) 2740 { 2741 if (!fi.szTypeName[0]) 2742 { 2743 /* The file type is unknown, so default to string "FileExtension File" */ 2744 size_t cchRemaining = 0; 2745 LPWSTR pwszEnd = NULL; 2746 2747 StringCchPrintfExW(wszBuf, _countof(wszBuf), &pwszEnd, &cchRemaining, 0, L"%s ", pwszExt + 1); 2748 } 2749 else 2750 { 2751 /* Update file type */ 2752 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s (%s)", fi.szTypeName, pwszExt); 2753 } 2754 } 2755 2756 return wszBuf; 2757 } 2758 2759 /************************************************************************** 2760 * SH_ShellLinkDlgProc 2761 * 2762 * dialog proc of the shortcut property dialog 2763 */ 2764 2765 INT_PTR CALLBACK CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 2766 { 2767 CShellLink *pThis = reinterpret_cast<CShellLink *>(GetWindowLongPtr(hwndDlg, DWLP_USER)); 2768 2769 switch(uMsg) 2770 { 2771 case WM_INITDIALOG: 2772 { 2773 LPPROPSHEETPAGEW ppsp = (LPPROPSHEETPAGEW)lParam; 2774 if (ppsp == NULL) 2775 break; 2776 2777 TRACE("ShellLink_DlgProc (WM_INITDIALOG hwnd %p lParam %p ppsplParam %x)\n", hwndDlg, lParam, ppsp->lParam); 2778 2779 pThis = reinterpret_cast<CShellLink *>(ppsp->lParam); 2780 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pThis); 2781 2782 TRACE("m_sArgs: %S sComponent: %S m_sDescription: %S m_sIcoPath: %S m_sPath: %S m_sPathRel: %S sProduct: %S m_sWorkDir: %S\n", pThis->m_sArgs, pThis->sComponent, pThis->m_sDescription, 2783 pThis->m_sIcoPath, pThis->m_sPath, pThis->m_sPathRel, pThis->sProduct, pThis->m_sWorkDir); 2784 2785 /* Get file information */ 2786 // FIXME! FIXME! Shouldn't we use pThis->m_sIcoPath, pThis->m_Header.nIconIndex instead??? 2787 SHFILEINFOW fi; 2788 if (!SHGetFileInfoW(pThis->m_sLinkPath, 0, &fi, sizeof(fi), SHGFI_TYPENAME | SHGFI_ICON)) 2789 { 2790 ERR("SHGetFileInfoW failed for %ls (%lu)\n", pThis->m_sLinkPath, GetLastError()); 2791 fi.szTypeName[0] = L'\0'; 2792 fi.hIcon = NULL; 2793 } 2794 2795 if (fi.hIcon) // TODO: destroy icon 2796 SendDlgItemMessageW(hwndDlg, IDC_SHORTCUT_ICON, STM_SETICON, (WPARAM)fi.hIcon, 0); 2797 else 2798 ERR("ExtractIconW failed %ls %u\n", pThis->m_sIcoPath, pThis->m_Header.nIconIndex); 2799 2800 /* Target type */ 2801 if (pThis->m_sPath) 2802 SetDlgItemTextW(hwndDlg, IDC_SHORTCUT_TYPE_EDIT, SH_GetTargetTypeByPath(pThis->m_sPath)); 2803 2804 /* Target location */ 2805 if (pThis->m_sPath) 2806 { 2807 WCHAR target[MAX_PATH]; 2808 StringCchCopyW(target, _countof(target), pThis->m_sPath); 2809 PathRemoveFileSpecW(target); 2810 SetDlgItemTextW(hwndDlg, IDC_SHORTCUT_LOCATION_EDIT, PathFindFileNameW(target)); 2811 } 2812 2813 /* Target path */ 2814 if (pThis->m_sPath) 2815 { 2816 WCHAR newpath[2*MAX_PATH] = L"\0"; 2817 if (wcschr(pThis->m_sPath, ' ')) 2818 StringCchPrintfExW(newpath, _countof(newpath), NULL, NULL, 0, L"\"%ls\"", pThis->m_sPath); 2819 else 2820 StringCchCopyExW(newpath, _countof(newpath), pThis->m_sPath, NULL, NULL, 0); 2821 2822 if (pThis->m_sArgs && pThis->m_sArgs[0]) 2823 { 2824 StringCchCatW(newpath, _countof(newpath), L" "); 2825 StringCchCatW(newpath, _countof(newpath), pThis->m_sArgs); 2826 } 2827 SetDlgItemTextW(hwndDlg, IDC_SHORTCUT_TARGET_TEXT, newpath); 2828 } 2829 2830 /* Working dir */ 2831 if (pThis->m_sWorkDir) 2832 SetDlgItemTextW(hwndDlg, IDC_SHORTCUT_START_IN_EDIT, pThis->m_sWorkDir); 2833 2834 /* Description */ 2835 if (pThis->m_sDescription) 2836 SetDlgItemTextW(hwndDlg, IDC_SHORTCUT_COMMENT_EDIT, pThis->m_sDescription); 2837 2838 return TRUE; 2839 } 2840 2841 case WM_NOTIFY: 2842 { 2843 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam; 2844 if (lppsn->hdr.code == PSN_APPLY) 2845 { 2846 WCHAR wszBuf[MAX_PATH]; 2847 /* set working directory */ 2848 GetDlgItemTextW(hwndDlg, IDC_SHORTCUT_START_IN_EDIT, wszBuf, _countof(wszBuf)); 2849 pThis->SetWorkingDirectory(wszBuf); 2850 /* set link destination */ 2851 GetDlgItemTextW(hwndDlg, IDC_SHORTCUT_TARGET_TEXT, wszBuf, _countof(wszBuf)); 2852 LPWSTR lpszArgs = NULL; 2853 LPWSTR unquoted = strdupW(wszBuf); 2854 StrTrimW(unquoted, L" "); 2855 if (!PathFileExistsW(unquoted)) 2856 { 2857 lpszArgs = PathGetArgsW(unquoted); 2858 PathRemoveArgsW(unquoted); 2859 StrTrimW(lpszArgs, L" "); 2860 } 2861 if (unquoted[0] == '"' && unquoted[wcslen(unquoted)-1] == '"') 2862 PathUnquoteSpacesW(unquoted); 2863 2864 2865 WCHAR *pwszExt = PathFindExtensionW(unquoted); 2866 if (!wcsicmp(pwszExt, L".lnk")) 2867 { 2868 // FIXME load localized error msg 2869 MessageBoxW(hwndDlg, L"You cannot create a link to a shortcut", L"Error", MB_ICONERROR); 2870 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); 2871 return TRUE; 2872 } 2873 2874 if (!PathFileExistsW(unquoted)) 2875 { 2876 // FIXME load localized error msg 2877 MessageBoxW(hwndDlg, L"The specified file name in the target box is invalid", L"Error", MB_ICONERROR); 2878 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); 2879 return TRUE; 2880 } 2881 2882 pThis->SetPath(unquoted); 2883 if (lpszArgs) 2884 pThis->SetArguments(lpszArgs); 2885 else 2886 pThis->SetArguments(L"\0"); 2887 2888 HeapFree(GetProcessHeap(), 0, unquoted); 2889 2890 TRACE("This %p m_sLinkPath %S\n", pThis, pThis->m_sLinkPath); 2891 pThis->Save(pThis->m_sLinkPath, TRUE); 2892 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); 2893 return TRUE; 2894 } 2895 break; 2896 } 2897 2898 case WM_COMMAND: 2899 switch(LOWORD(wParam)) 2900 { 2901 case IDC_SHORTCUT_FIND: 2902 SHOpenFolderAndSelectItems(pThis->m_pPidl, 0, NULL, 0); 2903 /// 2904 /// FIXME 2905 /// open target directory 2906 /// 2907 return TRUE; 2908 2909 case IDC_SHORTCUT_CHANGE_ICON: 2910 { 2911 WCHAR wszPath[MAX_PATH] = L""; 2912 2913 if (pThis->m_sIcoPath) 2914 wcscpy(wszPath, pThis->m_sIcoPath); 2915 else 2916 FindExecutableW(pThis->m_sPath, NULL, wszPath); 2917 2918 INT IconIndex = pThis->m_Header.nIconIndex; 2919 if (PickIconDlg(hwndDlg, wszPath, _countof(wszPath), &IconIndex)) 2920 { 2921 pThis->SetIconLocation(wszPath, IconIndex); 2922 2923 HICON hIconLarge = NULL; 2924 if (S_OK == pThis->Extract(wszPath, IconIndex, &hIconLarge, NULL, 0)) 2925 { 2926 HICON hIconOld = (HICON)SendDlgItemMessageW(hwndDlg, IDC_SHORTCUT_ICON, STM_GETICON, 0, 0); 2927 SendDlgItemMessageW(hwndDlg, IDC_SHORTCUT_ICON, STM_SETICON, (WPARAM)hIconLarge, 0); 2928 DestroyIcon(hIconOld); 2929 } 2930 } 2931 return TRUE; 2932 } 2933 2934 case IDC_SHORTCUT_ADVANCED: 2935 { 2936 INT_PTR result = DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_SHORTCUT_EXTENDED_PROPERTIES), hwndDlg, ExtendedShortcutProc, (LPARAM)pThis->m_bRunAs); 2937 if (result == 1 || result == 0) 2938 { 2939 if (pThis->m_bRunAs != result) 2940 { 2941 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 2942 } 2943 2944 pThis->m_bRunAs = result; 2945 } 2946 return TRUE; 2947 } 2948 } 2949 if (HIWORD(wParam) == EN_CHANGE) 2950 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 2951 break; 2952 2953 default: 2954 break; 2955 } 2956 return FALSE; 2957 } 2958 2959 /************************************************************************** 2960 * ShellLink_IShellPropSheetExt interface 2961 */ 2962 2963 HRESULT STDMETHODCALLTYPE CShellLink::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) 2964 { 2965 HPROPSHEETPAGE hPage = SH_CreatePropertySheetPage(IDD_SHORTCUT_PROPERTIES, SH_ShellLinkDlgProc, (LPARAM)this, NULL); 2966 if (hPage == NULL) 2967 { 2968 ERR("failed to create property sheet page\n"); 2969 return E_FAIL; 2970 } 2971 2972 if (!pfnAddPage(hPage, lParam)) 2973 return E_FAIL; 2974 2975 return S_OK; 2976 } 2977 2978 HRESULT STDMETHODCALLTYPE CShellLink::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam) 2979 { 2980 TRACE("(%p) (uPageID %u, pfnReplacePage %p lParam %p\n", this, uPageID, pfnReplacePage, lParam); 2981 return E_NOTIMPL; 2982 } 2983 2984 HRESULT STDMETHODCALLTYPE CShellLink::SetSite(IUnknown *punk) 2985 { 2986 TRACE("%p %p\n", this, punk); 2987 2988 m_site = punk; 2989 2990 return S_OK; 2991 } 2992 2993 HRESULT STDMETHODCALLTYPE CShellLink::GetSite(REFIID iid, void ** ppvSite) 2994 { 2995 TRACE("%p %s %p\n", this, debugstr_guid(&iid), ppvSite); 2996 2997 if (m_site == NULL) 2998 return E_FAIL; 2999 3000 return m_site->QueryInterface(iid, ppvSite); 3001 } 3002 3003 HRESULT STDMETHODCALLTYPE CShellLink::DragEnter(IDataObject *pDataObject, 3004 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) 3005 { 3006 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject); 3007 LPCITEMIDLIST pidlLast; 3008 CComPtr<IShellFolder> psf; 3009 3010 HRESULT hr = SHBindToParent(m_pPidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast); 3011 3012 if (SUCCEEDED(hr)) 3013 { 3014 hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_NULL_PPV_ARG(IDropTarget, &m_DropTarget)); 3015 3016 if (SUCCEEDED(hr)) 3017 hr = m_DropTarget->DragEnter(pDataObject, dwKeyState, pt, pdwEffect); 3018 else 3019 *pdwEffect = DROPEFFECT_NONE; 3020 } 3021 else 3022 *pdwEffect = DROPEFFECT_NONE; 3023 3024 return S_OK; 3025 } 3026 3027 HRESULT STDMETHODCALLTYPE CShellLink::DragOver(DWORD dwKeyState, POINTL pt, 3028 DWORD *pdwEffect) 3029 { 3030 TRACE("(%p)\n", this); 3031 HRESULT hr = S_OK; 3032 if (m_DropTarget) 3033 hr = m_DropTarget->DragOver(dwKeyState, pt, pdwEffect); 3034 return hr; 3035 } 3036 3037 HRESULT STDMETHODCALLTYPE CShellLink::DragLeave() 3038 { 3039 TRACE("(%p)\n", this); 3040 HRESULT hr = S_OK; 3041 if (m_DropTarget) 3042 { 3043 hr = m_DropTarget->DragLeave(); 3044 m_DropTarget.Release(); 3045 } 3046 3047 return hr; 3048 } 3049 3050 HRESULT STDMETHODCALLTYPE CShellLink::Drop(IDataObject *pDataObject, 3051 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) 3052 { 3053 TRACE("(%p)\n", this); 3054 HRESULT hr = S_OK; 3055 if (m_DropTarget) 3056 hr = m_DropTarget->Drop(pDataObject, dwKeyState, pt, pdwEffect); 3057 3058 return hr; 3059 } 3060 3061 /************************************************************************** 3062 * IShellLink_ConstructFromFile 3063 */ 3064 HRESULT WINAPI IShellLink_ConstructFromPath(WCHAR *path, REFIID riid, LPVOID *ppv) 3065 { 3066 CComPtr<IPersistFile> ppf; 3067 HRESULT hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IPersistFile, &ppf)); 3068 if (FAILED(hr)) 3069 return hr; 3070 3071 hr = ppf->Load(path, 0); 3072 if (FAILED(hr)) 3073 return hr; 3074 3075 return ppf->QueryInterface(riid, ppv); 3076 } 3077 3078 HRESULT WINAPI IShellLink_ConstructFromFile(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppv) 3079 { 3080 WCHAR path[MAX_PATH]; 3081 if (!ILGetDisplayNameExW(psf, pidl, path, 0)) 3082 return E_FAIL; 3083 3084 return IShellLink_ConstructFromPath(path, riid, ppv); 3085 } 3086