diff --git a/dlls/shell32/shell32_main.h b/dlls/shell32/shell32_main.h index 0aaa42c2c88..27f08404354 100644 --- a/dlls/shell32/shell32_main.h +++ b/dlls/shell32/shell32_main.h @@ -91,6 +91,7 @@ LPSHELLVIEW IShellView_Constructor(LPSHELLFOLDER); HRESULT WINAPI IFSFolder_Constructor(IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv); HRESULT WINAPI IShellLink_Constructor(IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv); +HRESULT WINAPI IShellLink_ConstructFromFile(IUnknown * pUnkOuter, REFIID riid, LPCITEMIDLIST pidl, LPVOID * ppv); HRESULT WINAPI ISF_Desktop_Constructor(IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv); HRESULT WINAPI ISF_MyComputer_Constructor(IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv); HRESULT WINAPI IDropTargetHelper_Constructor (IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv); diff --git a/dlls/shell32/shelllink.c b/dlls/shell32/shelllink.c index d665425fdb2..b44343fc284 100644 --- a/dlls/shell32/shelllink.c +++ b/dlls/shell32/shelllink.c @@ -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,7 @@ 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" @@ -128,7 +128,7 @@ typedef struct ICOM_VTABLE(IPersistFile)* lpvtblPersistFile; ICOM_VTABLE(IPersistStream)* lpvtblPersistStream; - /* data structures according to the informations in the lnk */ + /* data structures according to the informations in the link */ LPITEMIDLIST pPidl; WORD wHotKey; SYSTEMTIME time1; @@ -802,6 +802,134 @@ 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; +} + +/************************************************************************** + * SHELL_ShellLink_UpdatePath + * update absolute path in sPath using relative path in sPathRel + */ +static HRESULT SHELL_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; + LPWSTR final; + + /* copy path without file name to buffer */ + while(*src) { + if (*src=='/' || *src=='\\') + last_slash = dest; + + *dest++ = *src++; + } + + lstrcpyW(last_slash? last_slash+1: buffer, sPathRel); + + *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) + return E_OUTOFMEMORY; + + lstrcpyW(*psPath, abs_path); + } + + return S_OK; +} + +/************************************************************************** + * IShellLink_ConstructFromFile + */ +HRESULT WINAPI IShellLink_ConstructFromFile ( + IUnknown* pUnkOuter, + REFIID riid, + LPCITEMIDLIST pidl, + LPVOID* ppv +) +{ + IShellLinkW* psl; + + HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl); + + if (SUCCEEDED(hr)) { + IPersistFile* ppf; + + *ppv = NULL; + + hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf); + + if (SUCCEEDED(hr)) { + WCHAR path[MAX_PATH]; + + if (SHGetPathFromIDListW(pidl, path)) { + hr = IPersistFile_Load(ppf, path, 0); + + if (SUCCEEDED(hr)) { + *ppv = (IUnknown*) psl; + + /* + The following code is here, not in IPersistStream_fnLoad() because + to be able to convert the relative path into the absolute path, + we need to know the path of the shell link file. + */ + if (IsEqualIID(riid, &IID_IShellLinkW)) { + _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, psl); + + hr = SHELL_ShellLink_UpdatePath(This->sPathRel, path, This->sWorkDir, &This->sPath); + } else { + ICOM_THIS(IShellLinkImpl, psl); + + hr = SHELL_ShellLink_UpdatePath(This->sPathRel, path, This->sWorkDir, &This->sPath); + } + } + } + + IPersistFile_Release(ppf); + } + + if (!*ppv) + IShellLinkW_Release(psl); + } + + return hr; +} + /************************************************************************** * IShellLinkA_QueryInterface */ diff --git a/dlls/shell32/shfldr_desktop.c b/dlls/shell32/shfldr_desktop.c index bf70f19e288..664cd5d08ca 100644 --- a/dlls/shell32/shfldr_desktop.c +++ b/dlls/shell32/shfldr_desktop.c @@ -430,6 +430,11 @@ static HRESULT WINAPI ISF_Desktop_fnGetUIObjectOf (IShellFolder2 * iface, hr = S_OK; } else if (IsEqualIID (riid, &IID_IDropTarget) && (cidl >= 1)) { hr = IShellFolder_QueryInterface (iface, &IID_IDropTarget, (LPVOID *) & pObj); + } else if ((IsEqualIID(riid,&IID_IShellLinkW) || IsEqualIID(riid,&IID_IShellLinkA)) + && (cidl == 1)) { + pidl = ILCombine (This->pidlRoot, apidl[0]); + hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj); + SHFree (pidl); } else { hr = E_NOINTERFACE; } diff --git a/dlls/shell32/shfldr_fs.c b/dlls/shell32/shfldr_fs.c index 00d3162e98e..b25c3b23f4f 100644 --- a/dlls/shell32/shfldr_fs.c +++ b/dlls/shell32/shfldr_fs.c @@ -574,6 +574,11 @@ IShellFolder_fnGetUIObjectOf (IShellFolder2 * iface, hr = S_OK; } else if (IsEqualIID (riid, &IID_IDropTarget) && (cidl >= 1)) { hr = IShellFolder_QueryInterface (iface, &IID_IDropTarget, (LPVOID *) & pObj); + } else if ((IsEqualIID(riid,&IID_IShellLinkW) || IsEqualIID(riid,&IID_IShellLinkA)) + && (cidl == 1)) { + pidl = ILCombine (This->pidlRoot, apidl[0]); + hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj); + SHFree (pidl); } else { hr = E_NOINTERFACE; } diff --git a/dlls/shell32/shfldr_mycomp.c b/dlls/shell32/shfldr_mycomp.c index a4371fd5df4..1b7b944c2c2 100644 --- a/dlls/shell32/shfldr_mycomp.c +++ b/dlls/shell32/shfldr_mycomp.c @@ -409,6 +409,11 @@ ISF_MyComputer_fnGetUIObjectOf (IShellFolder2 * iface, hr = S_OK; } else if (IsEqualIID (riid, &IID_IDropTarget) && (cidl >= 1)) { hr = IShellFolder_QueryInterface (iface, &IID_IDropTarget, (LPVOID *) & pObj); + } else if ((IsEqualIID(riid,&IID_IShellLinkW) || IsEqualIID(riid,&IID_IShellLinkA)) + && (cidl == 1)) { + pidl = ILCombine (This->pidlRoot, apidl[0]); + hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj); + SHFree (pidl); } else { hr = E_NOINTERFACE; }