FIXME -> TRACE
[reactos.git] / reactos / lib / shell32 / shelllink.c
index 227ced6..4cc02c2 100644 (file)
@@ -19,7 +19,7 @@
  *
  * NOTES
  *   Nearly complete informations about the binary formats 
- *   of .lnk files avaiable at http://www.wotsit.org
+ *   of .lnk files available at http://www.wotsit.org
  *
  */
 
@@ -53,6 +53,7 @@
 #include "pidl.h"
 #include "shell32_main.h"
 #include "shlguid.h"
+#include "shlwapi.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 
@@ -66,8 +67,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell);
 #define SCF_WORKDIR 0x10
 #define SCF_ARGS 0x20
 #define SCF_CUSTOMICON 0x40
-//#define SCF_UNC 0x80
-//#define SCF_UNICODE 0x1000
 #define SCF_UNICODE 0x80
 
 #include "pshpack1.h"
@@ -158,6 +157,7 @@ typedef struct
 #define _ICOM_THIS_From_IPersistStream(class, name) class* This = (class*)(((char*)name)-_IPersistStream_Offset)
 #define _IPersistStream_From_ICOM_THIS(class, name) class* StreamThis = (class*)(((char*)name)+_IPersistStream_Offset)
 
+static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath);
 
 /* strdup on the process heap */
 inline static LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
@@ -219,7 +219,7 @@ static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
 {
        _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
 
-       FIXME("(%p)\n",This);
+       TRACE("(%p)\n",This);
 
        if (This->bDirty)
            return S_OK;
@@ -239,6 +239,7 @@ static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFile
         if( SUCCEEDED( r ) )
         {
             r = IPersistStream_Load(StreamThis, stm);
+            ShellLink_UpdatePath(This->sPathRel, pszFileName, This->sWorkDir, &This->sPath);
             IStream_Release( stm );
         }
 
@@ -633,7 +634,7 @@ static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
     return S_OK;
 }
 
-static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCSTR filename )
+static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR filename )
 {
     LOCATION_INFO loc;
     ULONG count;
@@ -648,9 +649,6 @@ static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCSTR filename )
     return IStream_Write( stm, &loc, loc.dwTotalSize, &count );
 }
 
-extern UINT SHELL_FindExecutable(LPCSTR lpPath, LPCSTR lpFile, LPCSTR lpOperation,
-                                 LPSTR lpResult, LPSTR key, void **env, LPCSTR args);
-
 /************************************************************************
  * IPersistStream_Save (IPersistStream)
  *
@@ -661,9 +659,10 @@ static HRESULT WINAPI IPersistStream_fnSave(
        IStream*         stm,
        BOOL             fClearDirty)
 {
+    static const WCHAR wOpen[] = {'o','p','e','n',0};
+
     LINK_HEADER header;
-    char    buffer[MAX_PATH];
-    char    exePath[MAX_PATH];
+    WCHAR   exePath[MAX_PATH];
     ULONG   count;
     HRESULT r;
 
@@ -673,11 +672,8 @@ static HRESULT WINAPI IPersistStream_fnSave(
 
     *exePath = '\0';
 
-    if (This->sPath) {
-       WideCharToMultiByte(CP_ACP, 0, This->sPath, -1, buffer, MAX_PATH, 0, 0);
-
-       SHELL_FindExecutable(NULL, buffer, "open", exePath, NULL, NULL, NULL);
-    }
+    if (This->sPath)
+       SHELL_FindExecutable(NULL, This->sPath, wOpen, exePath, MAX_PATH, NULL, NULL, NULL, NULL);
 
     /* if there's no PIDL, generate one */
     if( ! This->pPidl )
@@ -685,7 +681,7 @@ static HRESULT WINAPI IPersistStream_fnSave(
         if( !*exePath )
             return E_FAIL;
 
-        This->pPidl = ILCreateFromPathA(exePath);
+        This->pPidl = ILCreateFromPathW(exePath);
     }
 
     memset(&header, 0, sizeof(header));
@@ -822,17 +818,30 @@ HRESULT WINAPI IShellLink_Constructor (
        return S_OK;
 }
 
+static BOOL SHELL_ExistsFileW(LPCWSTR path)
+{
+    HANDLE hfile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+
+    if (hfile != INVALID_HANDLE_VALUE) {
+       CloseHandle(hfile);
+       return TRUE;
+    } else
+        return FALSE;
+}
+
 /**************************************************************************
- *  _IShellLink_UpdatePath
+ *  ShellLink_UpdatePath
  *     update absolute path in sPath using relative path in sPathRel
  */
-static HRESULT _IShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPWSTR* psPath)
+static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
 {
     if (!path || !psPath)
        return E_INVALIDARG;
 
     if (!*psPath && sPathRel) {
        WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
+
+       /* first try if [directory of link file] + [relative path] finds an existing file */
        LPCWSTR src = path;
        LPWSTR last_slash = NULL;
        LPWSTR dest = buffer;
@@ -848,8 +857,26 @@ static HRESULT _IShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPWSTR* psP
 
        lstrcpyW(last_slash? last_slash+1: buffer, sPathRel);
 
-       if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
-           lstrcpyW(abs_path, buffer);
+       *abs_path = '\0';
+
+       if (SHELL_ExistsFileW(buffer)) {
+           if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
+               lstrcpyW(abs_path, buffer);
+       } else {
+           /* try if [working directory] + [relative path] finds an existing file */
+           if (sWorkDir) {
+               lstrcpyW(buffer, sWorkDir);
+               lstrcpyW(PathAddBackslashW(buffer), sPathRel);
+
+               if (SHELL_ExistsFileW(buffer))
+                   if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
+                       lstrcpyW(abs_path, buffer);
+           }
+       }
+
+       /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
+       if (!*abs_path)
+           lstrcpyW(abs_path, sPathRel);
 
        *psPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(abs_path)+1)*sizeof(WCHAR));
        if (!*psPath)
@@ -885,27 +912,13 @@ HRESULT WINAPI IShellLink_ConstructFromFile (
        if (SUCCEEDED(hr)) {
            WCHAR path[MAX_PATH];
 
-           if (SHGetPathFromIDListW(pidl, path)) {
+           hr = SHELL_GetPathFromIDListW(pidl, path, MAX_PATH);
+
+           if (SUCCEEDED(hr)) {
                hr = IPersistFile_Load(ppf, path, 0);
 
-               if (SUCCEEDED(hr)) {
+               if (SUCCEEDED(hr))
                    *ppv = (IUnknown*) psl;
-
-                   /*
-                       The following code is here, not in IPersistStream_fnLoad() because
-                       we need to know the path of the shell link file to convert the
-                       relative path into the absolute path.
-                   */
-                   if (IsEqualIID(riid, &IID_IShellLinkW)) {
-                       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, psl);
-
-                       hr = _IShellLink_UpdatePath(This->sPathRel, path, &This->sPath);
-                   } else {
-                       ICOM_THIS(IShellLinkImpl, psl);
-
-                       hr = _IShellLink_UpdatePath(This->sPathRel, path, &This->sPath);
-                   }
-               }
            }
 
            IPersistFile_Release(ppf);
@@ -1194,7 +1207,7 @@ static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd
     return NOERROR;
 }
 
-static HRESULT _PidlGeticonLocationA(IShellFolder* psf, LPITEMIDLIST pidl, LPSTR pszIconPath, int cchIconPath, int* piIcon)
+static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPITEMIDLIST pidl, LPSTR pszIconPath, int cchIconPath, int* piIcon)
 {
     LPCITEMIDLIST pidlLast;
 
@@ -1240,7 +1253,7 @@ static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR p
        if (SUCCEEDED(hr)) {
            /* first look for an icon using the PIDL (if present) */
            if (This->pPidl)
-               hr = _PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
+               hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
            else
                hr = E_FAIL;
 
@@ -1251,7 +1264,7 @@ static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR p
                hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
 
                if (SUCCEEDED(hr)) {
-                   hr = _PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
+                   hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
 
                    SHFree(pidl);
                }
@@ -1295,7 +1308,7 @@ static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR
     This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
     This->bDirty = TRUE;
 
-    return _IShellLink_UpdatePath(This->sPathRel, This->sPath, &This->sPath);
+    return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
 }
 
 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
@@ -1306,10 +1319,12 @@ static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWOR
 
     FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
 
+    /*FIXME: use IResolveShellLink interface */
+
     if (!This->sPath && This->pPidl) {
        WCHAR buffer[MAX_PATH];
 
-       hr = _SHGetPathFromIDListW(This->pPidl, buffer, MAX_PATH);
+       hr = SHELL_GetPathFromIDListW(This->pPidl, buffer, MAX_PATH);
 
        if (SUCCEEDED(hr) && *buffer) {
            This->sPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
@@ -1340,12 +1355,18 @@ static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWOR
 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
 {
     ICOM_THIS(IShellLinkImpl, iface);
+    char buffer[MAX_PATH];
+    LPSTR fname;
 
     TRACE("(%p)->(path=%s)\n",This, pszFile);
 
+    if (!GetFullPathNameA(pszFile, MAX_PATH, buffer, &fname))
+       return E_FAIL;
+
     if (This->sPath)
         HeapFree(GetProcessHeap(), 0, This->sPath);
-    This->sPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
+
+    This->sPath = HEAP_strdupAtoW(GetProcessHeap(), 0, buffer);
     if( !This->sPath )
         return E_OUTOFMEMORY;
 
@@ -1615,7 +1636,7 @@ static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd
     return S_OK;
 }
 
-static HRESULT _PidlGeticonLocationW(IShellFolder* psf, LPITEMIDLIST pidl, LPWSTR pszIconPath, int cchIconPath, int* piIcon)
+static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPITEMIDLIST pidl, LPWSTR pszIconPath, int cchIconPath, int* piIcon)
 {
     LPCITEMIDLIST pidlLast;
 
@@ -1661,7 +1682,7 @@ static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR
        if (SUCCEEDED(hr)) {
            /* first look for an icon using the PIDL (if present) */
            if (This->pPidl)
-               hr = _PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
+               hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
            else
                hr = E_FAIL;
 
@@ -1672,7 +1693,7 @@ static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR
                hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
 
                if (SUCCEEDED(hr)) {
-                   hr = _PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
+                   hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
 
                    SHFree(pidl);
                }
@@ -1722,7 +1743,7 @@ static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR
     lstrcpyW( This->sPathRel, pszPathRel );
     This->bDirty = TRUE;
 
-    return _IShellLink_UpdatePath(This->sPathRel, This->sPath, &This->sPath);
+    return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
 }
 
 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
@@ -1733,10 +1754,12 @@ static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWOR
 
     FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
 
+    /*FIXME: use IResolveShellLink interface */
+
     if (!This->sPath && This->pPidl) {
        WCHAR buffer[MAX_PATH];
 
-       hr = _SHGetPathFromIDListW(This->pPidl, buffer, MAX_PATH);
+       hr = SHELL_GetPathFromIDListW(This->pPidl, buffer, MAX_PATH);
 
        if (SUCCEEDED(hr) && *buffer) {
            This->sPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
@@ -1767,17 +1790,23 @@ static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWOR
 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
 {
     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    WCHAR buffer[MAX_PATH];
+    LPWSTR fname;
 
     TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
 
+    if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
+       return E_FAIL;
+
     if (This->sPath)
         HeapFree(GetProcessHeap(), 0, This->sPath);
+
     This->sPath = HeapAlloc( GetProcessHeap(), 0,
-                             (lstrlenW( pszFile )+1) * sizeof (WCHAR) );
+                             (lstrlenW( buffer )+1) * sizeof (WCHAR) );
     if ( !This->sPath )
         return E_OUTOFMEMORY;
 
-    lstrcpyW( This->sPath, pszFile );
+    lstrcpyW( This->sPath, buffer );
     This->bDirty = TRUE;
 
     return S_OK;