+/*
+ * Since the real PathResolve (from Wine) is unimplemented at the moment,
+ * we use this local implementation, until a better one is written (using
+ * code parts of the SHELL_xxx helpers in Wine's shellpath.c).
+ */
+static BOOL HACKISH_PathResolve(
+ IN OUT PWSTR pszPath,
+ IN PZPCWSTR dirs OPTIONAL,
+ IN UINT fFlags)
+{
+ // FIXME: This is unimplemented!!!
+ FIXME("PathResolve() is UNIMPLEMENTED, using HACKISH_PathResolve() instead!\n");
+#if 0
+ return PathResolve(pszPath, dirs, fFlags);
+#else
+ BOOL Success = FALSE;
+ USHORT i;
+ LPWSTR fname = NULL;
+ WCHAR szPath[MAX_PATH];
+
+ /* First, search for a valid existing path */
+
+ // NOTE: See also: SHELL_FindExecutable()
+
+ /*
+ * List of extensions searched for, by PathResolve with the flag
+ * PRF_TRYPROGRAMEXTENSIONS == PRF_EXECUTABLE | PRF_VERIFYEXISTS set,
+ * according to MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/bb776478(v=vs.85).aspx
+ */
+ static PCWSTR Extensions[] = {L".pif", L".com", L".bat", L".cmd", L".lnk", L".exe", NULL};
+ #define LNK_EXT_INDEX 4 // ".lnk" has index 4 in the array above
+
+ /*
+ * Start at the beginning of the list if PRF_EXECUTABLE is set, otherwise
+ * just use the last element 'NULL' (no extension checking).
+ */
+ i = ((fFlags & PRF_EXECUTABLE) ? 0 : _countof(Extensions) - 1);
+ for (; i < _countof(Extensions); ++i)
+ {
+ /* Ignore shell links ".lnk" if needed */
+ if ((fFlags & PRF_DONTFINDLNK) && (i == LNK_EXT_INDEX))
+ continue;
+
+ Success = (SearchPathW(NULL, pszPath, Extensions[i],
+ _countof(szPath), szPath, NULL) != 0);
+ if (!Success)
+ {
+ ERR("SearchPathW(pszPath = '%S') failed\n", pszPath);
+ }
+ else
+ {
+ ERR("SearchPathW(pszPath = '%S', szPath = '%S') succeeded\n", pszPath, szPath);
+ break;
+ }
+ }
+
+ if (!Success)
+ {
+ ERR("SearchPathW(pszPath = '%S') failed\n", pszPath);
+
+ /* We failed, try with PathFindOnPath, as explained by MSDN */
+ // Success = PathFindOnPathW(pszPath, dirs);
+ StringCchCopyW(szPath, _countof(szPath), pszPath);
+ Success = PathFindOnPathW(szPath, dirs);
+ if (!Success)
+ {
+ ERR("PathFindOnPathW(pszPath = '%S') failed\n", pszPath);
+
+ /* We failed again, fall back to building a possible non-existing path */
+ if (!GetFullPathNameW(pszPath, _countof(szPath), szPath, &fname))
+ {
+ ERR("GetFullPathNameW(pszPath = '%S') failed\n", pszPath);
+ return FALSE;
+ }
+
+ Success = PathFileExistsW(szPath);
+ if (!Success)
+ ERR("PathFileExistsW(szPath = '%S') failed\n", szPath);
+
+ /******************************************************/
+ /* Question: Why this line is needed only for files?? */
+ if (fname && (_wcsicmp(pszPath, fname) == 0))
+ *szPath = L'\0';
+ /******************************************************/
+ }
+ else
+ {
+ ERR("PathFindOnPathW(pszPath = '%S' ==> '%S') succeeded\n", pszPath, szPath);
+ }
+ }
+
+ /* Copy back the results to the caller */
+ StringCchCopyW(pszPath, MAX_PATH, szPath);
+
+ /*
+ * Since the called functions always checked whether the file path existed,
+ * we do not need to redo a final check: we can use instead the cached
+ * result in 'Success'.
+ */
+ return ((fFlags & PRF_VERIFYEXISTS) ? Success : TRUE);
+#endif
+}
+
+HRESULT CShellLink::SetTargetFromPIDLOrPath(LPCITEMIDLIST pidl, LPCWSTR pszFile)
+{
+ HRESULT hr = S_OK;
+ LPITEMIDLIST pidlNew = NULL;
+ WCHAR szPath[MAX_PATH];
+
+ /*
+ * Not both 'pidl' and 'pszFile' should be set.
+ * But either one or both can be NULL.
+ */
+ if (pidl && pszFile)
+ return E_FAIL;
+
+ if (pidl)
+ {
+ /* Clone the PIDL */
+ pidlNew = ILClone(pidl);
+ if (!pidlNew)
+ return E_FAIL;
+ }
+ else if (pszFile)
+ {
+ /* Build a PIDL for this path target */
+ hr = SHILCreateFromPathW(pszFile, &pidlNew, NULL);
+ if (FAILED(hr))
+ {
+ /* This failed, try to resolve the path, then create a simple PIDL */
+
+ StringCchCopyW(szPath, _countof(szPath), pszFile);
+ // FIXME: Because PathResolve is unimplemented, we use our hackish implementation!
+ HACKISH_PathResolve(szPath, NULL, PRF_TRYPROGRAMEXTENSIONS);
+
+ pidlNew = SHSimpleIDListFromPathW(szPath);
+ /******************************************************/
+ /* Question: Why this line is needed only for files?? */
+ hr = (*szPath ? S_OK : E_INVALIDARG); // S_FALSE
+ /******************************************************/
+ }
+ }
+ // else if (!pidl && !pszFile) { pidlNew = NULL; hr = S_OK; }
+
+ ILFree(m_pPidl);
+ m_pPidl = pidlNew;
+
+ if (!pszFile)
+ {
+ if (SHGetPathFromIDListW(pidlNew, szPath))
+ pszFile = szPath;
+ }
+
+ // TODO: Fully update link info, tracker, file attribs...
+
+ // if (pszFile)
+ if (!pszFile)
+ {
+ *szPath = L'\0';
+ pszFile = szPath;
+ }
+
+ /* Update the cached path (for link info) */
+ ShellLink_GetVolumeInfo(pszFile, &volume);
+ m_sPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
+ (wcslen(pszFile) + 1) * sizeof(WCHAR));
+ if (!m_sPath)
+ return E_OUTOFMEMORY;
+ wcscpy(m_sPath, pszFile);
+
+ m_bDirty = TRUE;
+ return hr;
+}
+