[SHELL32]: CShellLink fixups Part 2:
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Tue, 17 Jan 2017 23:53:55 +0000 (23:53 +0000)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Tue, 17 Jan 2017 23:53:55 +0000 (23:53 +0000)
- Add COM inheritance for interfaces IExtractIconA/W, and add in comment the missing other ones, the ordering of which is given by the apitests/com/shell32.
- Add proper support for the extra data block list (which is added at the end of the .lnk files), by using the API from shlwapi.dll: SH[Read|Write|Free]DataBlockList, SH[Add|Find|Remove]DataBlock.
- Using this support, getting/setting the MSI data block becomes as simple as child's play, and opens the possibility for implementing support for the other types of blocks.
- This in particular enables support for paths with environment variables for the link's target and icon: CORE-9236 #resolve
- Fix all the "shell32_winetest shelllink" tests: CORE-7158 #resolve
  Some of the fixes are inspired from a patch by Katayama Hirofumi MZ.
- Fix all the "shell32_apitest CShellLink" tests *but* those calling IExtractIcon::GetIconLocation().
- Implement a hackish substitute to the shell32!PathResolve API until someone writes a correct one (see the code & the FIXMEs for some ideas), possibly using the SHELL_xxx helpers in Wine's shellpath.c.
- In CFSExtractIcon_CreateInstance: Because IShellLink::GetIconLocation can return no icon location, in case none is specified in the .lnk (proved by apitests), we have to call the shell link's IExtractIcon::GetIconLocation in order to retrieve the icon of its target (yes, some shortcuts are made like that, e.g. Notepad++ 6.9 one...).

- More fixes...
- ... and a lot of documentation added in the code for you!

CORE-12682

svn path=/trunk/; revision=73576

reactos/dll/win32/shell32/CShellLink.cpp
reactos/dll/win32/shell32/CShellLink.h
reactos/dll/win32/shell32/folders.cpp

index c92a4ad..f5c0bcf 100644 (file)
@@ -5,6 +5,7 @@
  *      Copyright 2005  Mike McCormack
  *      Copyright 2009  Andrew Hill
  *      Copyright 2013  Dominik Hornung
  *      Copyright 2005  Mike McCormack
  *      Copyright 2009  Andrew Hill
  *      Copyright 2013  Dominik Hornung
+ *      Copyright 2017  Hermes Belusca-Maito
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  *   created by passing a special string to SetPath, and the information
  *   in that string is parsed an stored.
  */
  *   created by passing a special string to SetPath, and the information
  *   in that string is parsed an stored.
  */
+/*
+ * In the following is listed more documentation about the Shell Link file format,
+ * as well as its interface.
+ *
+ * General introduction about "Shell Links" (MSDN):
+ *   https://msdn.microsoft.com/en-us/library/windows/desktop/bb776891(v=vs.85).aspx
+ *
+ *
+ * Details of the file format:
+ *
+ * - Official MSDN documentation "[MS-SHLLINK]: Shell Link (.LNK) Binary File Format":
+ *   https://msdn.microsoft.com/en-us/library/dd871305.aspx
+ *
+ * - Forensics:
+ *   http://forensicswiki.org/wiki/LNK
+ *   http://computerforensics.parsonage.co.uk/downloads/TheMeaningofLIFE.pdf
+ *   https://ithreats.files.wordpress.com/2009/05/lnk_the_windows_shortcut_file_format.pdf
+ *   https://github.com/libyal/liblnk/blob/master/documentation/Windows%20Shortcut%20File%20(LNK)%20format.asciidoc
+ *
+ * - List of possible shell link header flags (SHELL_LINK_DATA_FLAGS enumeration):
+ *   https://msdn.microsoft.com/en-us/library/windows/desktop/bb762540(v=vs.85).aspx
+ *   https://msdn.microsoft.com/en-us/library/dd891314.aspx
+ *
+ *
+ * In addition to storing its target by using a PIDL, a shell link file also
+ * stores metadata to make the shell able to track the link target, in situations
+ * where the link target is moved amongst local or network directories, or moved
+ * to different volumes. For this, two structures are used:
+ *
+ * - The first and oldest one (from NewShell/WinNT4) is the "LinkInfo" structure,
+ *   stored in a serialized manner at the beginning of the shell link file:
+ *   https://msdn.microsoft.com/en-us/library/dd871404.aspx
+ *   The official API for manipulating this is located in LINKINFO.DLL .
+ *
+ * - The second, more recent one, is an extra binary block appended to the
+ *   extra-data list of the shell link file: this is the "TrackerDataBlock":
+ *   https://msdn.microsoft.com/en-us/library/dd891376.aspx
+ *   Its purpose is for link tracking, and works in coordination with the
+ *   "Distributed Link Tracking" service ('TrkWks' client, 'TrkSvr' server).
+ *   See a detailed explanation at:
+ *   http://www.serverwatch.com/tutorials/article.php/1476701/Searching-for-the-Missing-Link-Distributed-Link-Tracking.htm
+ *
+ *
+ * MSI installations most of the time create so-called "advertised shortcuts".
+ * They provide an icon for a program that may not be installed yet, and invoke
+ * MSI to install the program when the shortcut is opened (resolved).
+ * The philosophy of this approach is explained in detail inside the MSDN article
+ * "Application Resiliency: Unlock the Hidden Features of Windows Installer"
+ * (by Michael Sanford), here:
+ *   https://msdn.microsoft.com/en-us/library/aa302344.aspx
+ *
+ * This functionality is implemented by adding a binary "Darwin" data block
+ * of type "EXP_DARWIN_LINK", signature EXP_DARWIN_ID_SIG == 0xA0000006,
+ * to the shell link file:
+ *   https://msdn.microsoft.com/en-us/library/dd871369.aspx
+ * or, this could be done more simply by specifying a special link target path
+ * with the IShellLink::SetPath() function. Defining the following GUID:
+ *   SHELL32_AdvtShortcutComponent = "::{9db1186e-40df-11d1-aa8c-00c04fb67863}:"
+ * setting a target of the form:
+ *   "::{SHELL32_AdvtShortcutComponent}:<MSI_App_ID>"
+ * would automatically create the necessary binary block.
+ *
+ * With that, the target of the shortcut now becomes the MSI data. The latter
+ * is parsed from MSI and retrieved by the shell that then can run the program.
+ *
+ * This MSI functionality, dubbed "link blessing", actually originates from an
+ * older technology introduced in Internet Explorer 3 (and now obsolete since
+ * Internet Explorer 7), called "MS Internet Component Download (MSICD)", see
+ * this MSDN introductory article:
+ *   https://msdn.microsoft.com/en-us/library/aa741198(v=vs.85).aspx
+ * and leveraged in Internet Explorer 4 with "Software Update Channels", see:
+ *   https://msdn.microsoft.com/en-us/library/aa740931(v=vs.85).aspx
+ * Applications supporting this technology could present shell links having
+ * a special target, see subsection "Modifying the Shortcut" in the article:
+ *   https://msdn.microsoft.com/en-us/library/aa741201(v=vs.85).aspx#pub_shor
+ *
+ * Similarly as for the MSI shortcuts, these MSICD shortcuts are created by
+ * specifying a special link target path with the IShellLink::SetPath() function,
+ * defining the following GUID:
+ *   SHELL32_AdvtShortcutProduct = "::{9db1186f-40df-11d1-aa8c-00c04fb67863}:"
+ * and setting a target of the form:
+ *   "::{SHELL32_AdvtShortcutProduct}:<AppName>::<Path>" .
+ * A tool, called "blesslnk.exe", was also provided for automatizing the process;
+ * its ReadMe can be found in the (now outdated) MS "Internet Client SDK" (INetSDK,
+ * for MS Windows 95 and NT), whose contents can be read at:
+ *   http://www.msfn.org/board/topic/145352-new-windows-lnk-vulnerability/?page=4#comment-944223
+ * The MS INetSDK can be found at:
+ *   https://web.archive.org/web/20100924000013/http://support.microsoft.com/kb/177877
+ *
+ * Internally the shell link target of these MSICD shortcuts is converted into
+ * a binary data block of a type similar to Darwin / "EXP_DARWIN_LINK", but with
+ * a different signature EXP_LOGO3_ID_SIG == 0xA0000007 . Such shell links are
+ * called "Logo3" shortcuts. They were evoked in this user comment in "The Old
+ * New Thing" blog:
+ *   https://blogs.msdn.microsoft.com/oldnewthing/20121210-00/?p=5883#comment-1025083
+ *
+ * The shell exports the API 'SoftwareUpdateMessageBox' (in shdocvw.dll) that
+ * displays a message when an update for an application supporting this
+ * technology is available.
+ *
+ */
 
 #include "precomp.h"
 
 
 #include "precomp.h"
 
@@ -106,14 +208,52 @@ static LPWSTR __inline strdupW(LPCWSTR src)
     return dest;
 }
 
     return dest;
 }
 
+// TODO: Use it for constructor & destructor too
+VOID CShellLink::Reset()
+{
+    ILFree(m_pPidl);
+    m_pPidl = NULL;
+
+    HeapFree(GetProcessHeap(), 0, m_sPath);
+    m_sPath = NULL;
+    ZeroMemory(&volume, sizeof(volume));
+
+    HeapFree(GetProcessHeap(), 0, m_sDescription);
+    m_sDescription = NULL;
+    HeapFree(GetProcessHeap(), 0, m_sPathRel);
+    m_sPathRel = NULL;
+    HeapFree(GetProcessHeap(), 0, m_sWorkDir);
+    m_sWorkDir = NULL;
+    HeapFree(GetProcessHeap(), 0, m_sArgs);
+    m_sArgs = NULL;
+    HeapFree(GetProcessHeap(), 0, m_sIcoPath);
+    m_sIcoPath = NULL;
+
+    m_bRunAs = FALSE;
+    m_bDirty = FALSE;
+
+    if (m_pDBList)
+        SHFreeDataBlockList(m_pDBList);
+    m_pDBList = NULL;
+
+    /**/sProduct = sComponent = NULL;/**/
+}
+
 CShellLink::CShellLink()
 {
 CShellLink::CShellLink()
 {
-    wHotKey = 0;
-    memset(&time1, 0, sizeof(time1));
-    memset(&time2, 0, sizeof(time2));
-    memset(&time3, 0, sizeof(time3));
-    iShowCmd = SW_SHOWNORMAL;
-    iIcoNdx = 0;
+    m_Header.dwSize = sizeof(m_Header);
+    m_Header.clsid = CLSID_ShellLink;
+    m_Header.dwFlags = 0;
+
+    m_Header.dwFileAttributes = 0;
+    ZeroMemory(&m_Header.ftCreationTime, sizeof(m_Header.ftCreationTime));
+    ZeroMemory(&m_Header.ftLastAccessTime, sizeof(m_Header.ftLastAccessTime));
+    ZeroMemory(&m_Header.ftLastWriteTime, sizeof(m_Header.ftLastWriteTime));
+    m_Header.nFileSizeLow = 0;
+
+    m_Header.nIconIndex = 0;
+    m_Header.nShowCommand = SW_SHOWNORMAL;
+    m_Header.wHotKey = 0;
 
     m_pPidl = NULL;
 
 
     m_pPidl = NULL;
 
@@ -127,6 +267,7 @@ CShellLink::CShellLink()
     m_sIcoPath = NULL;
     m_bRunAs = FALSE;
     m_bDirty = FALSE;
     m_sIcoPath = NULL;
     m_bRunAs = FALSE;
     m_bDirty = FALSE;
+    m_pDBList = NULL;
 
     m_sLinkPath = NULL;
     m_iIdOpen = -1;
 
     m_sLinkPath = NULL;
     m_iIdOpen = -1;
@@ -148,6 +289,7 @@ CShellLink::~CShellLink()
     HeapFree(GetProcessHeap(), 0, m_sArgs);
     HeapFree(GetProcessHeap(), 0, m_sIcoPath);
     HeapFree(GetProcessHeap(), 0, m_sLinkPath);
     HeapFree(GetProcessHeap(), 0, m_sArgs);
     HeapFree(GetProcessHeap(), 0, m_sIcoPath);
     HeapFree(GetProcessHeap(), 0, m_sLinkPath);
+    SHFreeDataBlockList(m_pDBList);
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetClassID(CLSID *pclsid)
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetClassID(CLSID *pclsid)
@@ -428,12 +570,12 @@ static HRESULT Stream_LoadLocation(IStream *stm,
     return S_OK;
 }
 
     return S_OK;
 }
 
+
 /*
 /*
- *  The format of the advertised shortcut info seems to be:
+ * The format of the advertised shortcut info is:
  *
  *  Offset     Description
  *  ------     -----------
  *
  *  Offset     Description
  *  ------     -----------
- *
  *    0          Length of the block (4 bytes, usually 0x314)
  *    4          tag (dword)
  *    8          string data in ASCII
  *    0          Length of the block (4 bytes, usually 0x314)
  *    4          tag (dword)
  *    8          string data in ASCII
@@ -442,43 +584,26 @@ static HRESULT Stream_LoadLocation(IStream *stm,
  * In the original Win32 implementation the buffers are not initialized
  * to zero, so data trailing the string is random garbage.
  */
  * In the original Win32 implementation the buffers are not initialized
  * to zero, so data trailing the string is random garbage.
  */
-static HRESULT Stream_LoadAdvertiseInfo(IStream* stm, LPWSTR *str)
+HRESULT CShellLink::GetAdvertiseInfo(LPWSTR *str, DWORD dwSig)
 {
 {
-    TRACE("%p\n", stm);
-
-    ULONG count;
-    EXP_DARWIN_LINK buffer;
-    HRESULT hr = stm->Read(&buffer.dbh.cbSize, sizeof (DWORD), &count);
-    if (FAILED(hr))
-        return hr;
-
-    /* make sure that we read the size of the structure even on error */
-    DWORD size = sizeof buffer - sizeof (DWORD);
-    if (buffer.dbh.cbSize != sizeof buffer)
-    {
-        ERR("Ooops.  This structure is not as expected...\n");
-        return E_FAIL;
-    }
+    LPEXP_DARWIN_LINK pInfo;
 
 
-    hr = stm->Read(&buffer.dbh.dwSignature, size, &count);
-    if (FAILED(hr))
-        return hr;
+    *str = NULL;
 
 
-    if (count != size)
+    pInfo = (LPEXP_DARWIN_LINK)SHFindDataBlock(m_pDBList, dwSig);
+    if (!pInfo)
         return E_FAIL;
 
         return E_FAIL;
 
-    TRACE("magic %08x  string = %s\n", buffer.dbh.dwSignature, debugstr_w(buffer.szwDarwinID));
-
-    if ((buffer.dbh.dwSignature & 0xffff0000) != 0xa0000000)
+    /* Make sure that the size of the structure is valid */
+    if (pInfo->dbh.cbSize != sizeof(*pInfo))
     {
     {
-        ERR("Unknown magic number %08x in advertised shortcut\n", buffer.dbh.dwSignature);
+        ERR("Ooops. This structure is not as expected...\n");
         return E_FAIL;
     }
 
         return E_FAIL;
     }
 
-    *str = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
-                             (wcslen(buffer.szwDarwinID) + 1) * sizeof(WCHAR));
-    wcscpy(*str, buffer.szwDarwinID);
+    TRACE("dwSig %08x  string = '%s'\n", pInfo->dbh.dwSignature, debugstr_w(pInfo->szwDarwinID));
 
 
+    *str = pInfo->szwDarwinID;
     return S_OK;
 }
 
     return S_OK;
 }
 
@@ -492,63 +617,47 @@ HRESULT STDMETHODCALLTYPE CShellLink::Load(IStream *stm)
     if (!stm)
         return STG_E_INVALIDPOINTER;
 
     if (!stm)
         return STG_E_INVALIDPOINTER;
 
-    SHELL_LINK_HEADER ShlLnkHeader;
+    /* Free all the old stuff */
+    Reset();
+
     ULONG dwBytesRead = 0;
     ULONG dwBytesRead = 0;
-    HRESULT hr = stm->Read(&ShlLnkHeader, sizeof(ShlLnkHeader), &dwBytesRead);
+    HRESULT hr = stm->Read(&m_Header, sizeof(m_Header), &dwBytesRead);
     if (FAILED(hr))
         return hr;
 
     if (FAILED(hr))
         return hr;
 
-    if (dwBytesRead != sizeof(ShlLnkHeader))
+    if (dwBytesRead != sizeof(m_Header))
         return E_FAIL;
         return E_FAIL;
-    if (ShlLnkHeader.dwSize != sizeof(ShlLnkHeader))
+    if (m_Header.dwSize != sizeof(m_Header))
         return E_FAIL;
         return E_FAIL;
-    if (!IsEqualIID(ShlLnkHeader.clsid, CLSID_ShellLink))
+    if (!IsEqualIID(m_Header.clsid, CLSID_ShellLink))
         return E_FAIL;
 
         return E_FAIL;
 
-    /* free all the old stuff */
-    ILFree(m_pPidl);
-    m_pPidl = NULL;
-    memset(&volume, 0, sizeof volume);
-    HeapFree(GetProcessHeap(), 0, m_sPath);
-    m_sPath = NULL;
-    HeapFree(GetProcessHeap(), 0, m_sDescription);
-    m_sDescription = NULL;
-    HeapFree(GetProcessHeap(), 0, m_sPathRel);
-    m_sPathRel = NULL;
-    HeapFree(GetProcessHeap(), 0, m_sWorkDir);
-    m_sWorkDir = NULL;
-    HeapFree(GetProcessHeap(), 0, m_sArgs);
-    m_sArgs = NULL;
-    HeapFree(GetProcessHeap(), 0, m_sIcoPath);
-    m_sIcoPath = NULL;
-    HeapFree(GetProcessHeap(), 0, sProduct);
-    sProduct = NULL;
-    HeapFree(GetProcessHeap(), 0, sComponent);
-    sComponent = NULL;
-
-    BOOL unicode = FALSE;
-    iShowCmd = ShlLnkHeader.nShowCommand;
-    wHotKey = ShlLnkHeader.wHotKey;
-    iIcoNdx = ShlLnkHeader.nIconIndex;
-    FileTimeToSystemTime (&ShlLnkHeader.ftCreationTime, &time1);
-    FileTimeToSystemTime (&ShlLnkHeader.ftLastAccessTime, &time2);
-    FileTimeToSystemTime (&ShlLnkHeader.ftLastWriteTime, &time3);
+    /* Load the new data in order */
+
     if (TRACE_ON(shell))
     {
     if (TRACE_ON(shell))
     {
+        SYSTEMTIME stCreationTime;
+        SYSTEMTIME stLastAccessTime;
+        SYSTEMTIME stLastWriteTime;
         WCHAR sTemp[MAX_PATH];
         WCHAR sTemp[MAX_PATH];
-        GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time1,
-                       NULL, sTemp, sizeof(sTemp) / sizeof(*sTemp));
-        TRACE("-- time1: %s\n", debugstr_w(sTemp));
-        GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time2,
-                       NULL, sTemp, sizeof(sTemp) / sizeof(*sTemp));
-        TRACE("-- time2: %s\n", debugstr_w(sTemp));
-        GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time3,
-                       NULL, sTemp, sizeof(sTemp) / sizeof(*sTemp));
-        TRACE("-- time3: %s\n", debugstr_w(sTemp));
+
+        FileTimeToSystemTime(&m_Header.ftCreationTime, &stCreationTime);
+        FileTimeToSystemTime(&m_Header.ftLastAccessTime, &stLastAccessTime);
+        FileTimeToSystemTime(&m_Header.ftLastWriteTime, &stLastWriteTime);
+
+        GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &stCreationTime,
+                       NULL, sTemp, _countof(sTemp));
+        TRACE("-- stCreationTime: %s\n", debugstr_w(sTemp));
+        GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &stLastAccessTime,
+                       NULL, sTemp, _countof(sTemp));
+        TRACE("-- stLastAccessTime: %s\n", debugstr_w(sTemp));
+        GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &stLastWriteTime,
+                       NULL, sTemp, _countof(sTemp));
+        TRACE("-- stLastWriteTime: %s\n", debugstr_w(sTemp));
     }
 
     /* load all the new stuff */
     }
 
     /* load all the new stuff */
-    if (ShlLnkHeader.dwFlags & SLDF_HAS_ID_LIST)
+    if (m_Header.dwFlags & SLDF_HAS_ID_LIST)
     {
         hr = ILLoadFromStream(stm, &m_pPidl);
         if (FAILED(hr))
     {
         hr = ILLoadFromStream(stm, &m_pPidl);
         if (FAILED(hr))
@@ -556,82 +665,91 @@ HRESULT STDMETHODCALLTYPE CShellLink::Load(IStream *stm)
     }
     pdump(m_pPidl);
 
     }
     pdump(m_pPidl);
 
-    /* load the location information */
-    if (ShlLnkHeader.dwFlags & SLDF_HAS_LINK_INFO)
+    /* Load the location information... */
+    if (m_Header.dwFlags & SLDF_HAS_LINK_INFO)
+    {
         hr = Stream_LoadLocation(stm, &volume, &m_sPath);
         hr = Stream_LoadLocation(stm, &volume, &m_sPath);
-    if (FAILED(hr))
-        goto end;
+        if (FAILED(hr))
+            return hr;
+    }
+    /* ... but if it is required not to use it, clear it */
+    if (m_Header.dwFlags & SLDF_FORCE_NO_LINKINFO)
+    {
+        HeapFree(GetProcessHeap(), 0, m_sPath);
+        m_sPath = NULL;
+        ZeroMemory(&volume, sizeof(volume));
+    }
 
 
-    if (ShlLnkHeader.dwFlags & SLDF_UNICODE)
-        unicode = TRUE;
+    BOOL unicode = !!(m_Header.dwFlags & SLDF_UNICODE);
 
 
-    if (ShlLnkHeader.dwFlags & SLDF_HAS_NAME)
+    if (m_Header.dwFlags & SLDF_HAS_NAME)
     {
         hr = Stream_LoadString(stm, unicode, &m_sDescription);
     {
         hr = Stream_LoadString(stm, unicode, &m_sDescription);
+        if (FAILED(hr))
+            return hr;
         TRACE("Description  -> %s\n", debugstr_w(m_sDescription));
     }
         TRACE("Description  -> %s\n", debugstr_w(m_sDescription));
     }
-    if (FAILED(hr))
-        goto end;
 
 
-    if (ShlLnkHeader.dwFlags & SLDF_HAS_RELPATH)
+    if (m_Header.dwFlags & SLDF_HAS_RELPATH)
     {
         hr = Stream_LoadString(stm, unicode, &m_sPathRel);
     {
         hr = Stream_LoadString(stm, unicode, &m_sPathRel);
+        if (FAILED(hr))
+            return hr;
         TRACE("Relative Path-> %s\n", debugstr_w(m_sPathRel));
     }
         TRACE("Relative Path-> %s\n", debugstr_w(m_sPathRel));
     }
-    if (FAILED(hr))
-        goto end;
 
 
-    if (ShlLnkHeader.dwFlags & SLDF_HAS_WORKINGDIR)
+    if (m_Header.dwFlags & SLDF_HAS_WORKINGDIR)
     {
         hr = Stream_LoadString(stm, unicode, &m_sWorkDir);
     {
         hr = Stream_LoadString(stm, unicode, &m_sWorkDir);
+        if (FAILED(hr))
+            return hr;
         PathRemoveBackslash(m_sWorkDir);
         TRACE("Working Dir  -> %s\n", debugstr_w(m_sWorkDir));
     }
         PathRemoveBackslash(m_sWorkDir);
         TRACE("Working Dir  -> %s\n", debugstr_w(m_sWorkDir));
     }
-    if (FAILED(hr))
-        goto end;
 
 
-    if (ShlLnkHeader.dwFlags & SLDF_HAS_ARGS)
+    if (m_Header.dwFlags & SLDF_HAS_ARGS)
     {
         hr = Stream_LoadString(stm, unicode, &m_sArgs);
     {
         hr = Stream_LoadString(stm, unicode, &m_sArgs);
+        if (FAILED(hr))
+            return hr;
         TRACE("Arguments    -> %s\n", debugstr_w(m_sArgs));
     }
         TRACE("Arguments    -> %s\n", debugstr_w(m_sArgs));
     }
-    if (FAILED(hr))
-        goto end;
 
 
-    if (ShlLnkHeader.dwFlags & SLDF_HAS_ICONLOCATION)
+    if (m_Header.dwFlags & SLDF_HAS_ICONLOCATION)
     {
         hr = Stream_LoadString(stm, unicode, &m_sIcoPath);
     {
         hr = Stream_LoadString(stm, unicode, &m_sIcoPath);
+        if (FAILED(hr))
+            return hr;
         TRACE("Icon file    -> %s\n", debugstr_w(m_sIcoPath));
     }
         TRACE("Icon file    -> %s\n", debugstr_w(m_sIcoPath));
     }
-    if (FAILED(hr))
-        goto end;
 
 
-#if (NTDDI_VERSION < NTDDI_LONGHORN)
-    if (ShlLnkHeader.dwFlags & SLDF_HAS_LOGO3ID)
-    {
-        hr = Stream_LoadAdvertiseInfo(stm, &sProduct);
-        TRACE("Product      -> %s\n", debugstr_w(sProduct));
-    }
-    if (FAILED(hr))
-        goto end;
-#endif
+    /* Now load the optional data block list */
+    hr = SHReadDataBlockList(stm, &m_pDBList);
+    if (FAILED(hr)) // FIXME: Should we fail?
+        return hr;
 
 
-    if (ShlLnkHeader.dwFlags & SLDF_HAS_DARWINID)
+    if (TRACE_ON(shell))
     {
     {
-        hr = Stream_LoadAdvertiseInfo(stm, &sComponent);
-        TRACE("Component    -> %s\n", debugstr_w(sComponent));
+#if (NTDDI_VERSION < NTDDI_LONGHORN)
+        if (m_Header.dwFlags & SLDF_HAS_LOGO3ID)
+        {
+            hr = GetAdvertiseInfo(&sProduct, EXP_LOGO3_ID_SIG);
+            if (SUCCEEDED(hr))
+                TRACE("Product      -> %s\n", debugstr_w(sProduct));
+        }
+#endif
+        if (m_Header.dwFlags & SLDF_HAS_DARWINID)
+        {
+            hr = GetAdvertiseInfo(&sComponent, EXP_DARWIN_ID_SIG);
+            if (SUCCEEDED(hr))
+                TRACE("Component    -> %s\n", debugstr_w(sComponent));
+        }
     }
     }
-    if (ShlLnkHeader.dwFlags & SLDF_RUNAS_USER)
-    {
+
+    if (m_Header.dwFlags & SLDF_RUNAS_USER)
         m_bRunAs = TRUE;
         m_bRunAs = TRUE;
-    }
     else
     else
-    {
         m_bRunAs = FALSE;
         m_bRunAs = FALSE;
-    }
-
-    if (FAILED(hr))
-        goto end;
 
     DWORD dwZero;
     hr = stm->Read(&dwZero, sizeof(dwZero), &dwBytesRead);
 
     DWORD dwZero;
     hr = stm->Read(&dwZero, sizeof(dwZero), &dwBytesRead);
@@ -643,9 +761,6 @@ HRESULT STDMETHODCALLTYPE CShellLink::Load(IStream *stm)
     pdump(m_pPidl);
 
     return S_OK;
     pdump(m_pPidl);
 
     return S_OK;
-
-end:
-    return hr;
 }
 
 /************************************************************************
 }
 
 /************************************************************************
@@ -656,7 +771,7 @@ end:
  */
 static HRESULT Stream_WriteString(IStream* stm, LPCWSTR str)
 {
  */
 static HRESULT Stream_WriteString(IStream* stm, LPCWSTR str)
 {
-    USHORT len = wcslen(str) + 1;
+    USHORT len = wcslen(str) + 1; // FIXME: Possible overflows?
     DWORD count;
 
     HRESULT hr = stm->Write(&len, sizeof(len), &count);
     DWORD count;
 
     HRESULT hr = stm->Write(&len, sizeof(len), &count);
@@ -734,27 +849,6 @@ static HRESULT Stream_WriteLocationInfo(IStream* stm, LPCWSTR path,
     return hr;
 }
 
     return hr;
 }
 
-static EXP_DARWIN_LINK* shelllink_build_darwinid(LPCWSTR string, DWORD magic)
-{
-    EXP_DARWIN_LINK *buffer = (EXP_DARWIN_LINK *)LocalAlloc(LMEM_ZEROINIT, sizeof * buffer);
-    buffer->dbh.cbSize = sizeof * buffer;
-    buffer->dbh.dwSignature = magic;
-    lstrcpynW(buffer->szwDarwinID, string, MAX_PATH);
-    WideCharToMultiByte(CP_ACP, 0, string, -1, buffer->szDarwinID, MAX_PATH, NULL, NULL);
-
-    return buffer;
-}
-
-static HRESULT Stream_WriteAdvertiseInfo(IStream* stm, LPCWSTR string, DWORD magic)
-{
-    TRACE("%p\n", stm);
-
-    EXP_DARWIN_LINK *buffer = shelllink_build_darwinid(string, magic);
-
-    ULONG count;
-    return stm->Write(buffer, buffer->dbh.cbSize, &count);
-}
-
 /************************************************************************
  * IPersistStream_Save (IPersistStream)
  *
 /************************************************************************
  * IPersistStream_Save (IPersistStream)
  *
@@ -764,52 +858,53 @@ HRESULT STDMETHODCALLTYPE CShellLink::Save(IStream *stm, BOOL fClearDirty)
 {
     TRACE("%p %p %x\n", this, stm, fClearDirty);
 
 {
     TRACE("%p %p %x\n", this, stm, fClearDirty);
 
-    SHELL_LINK_HEADER ShlLnkHeader;
-    memset(&ShlLnkHeader, 0, sizeof(ShlLnkHeader));
-    ShlLnkHeader.dwSize = sizeof(ShlLnkHeader);
-    ShlLnkHeader.nShowCommand = iShowCmd;
-    ShlLnkHeader.clsid = CLSID_ShellLink;
+    m_Header.dwSize = sizeof(m_Header);
+    m_Header.clsid = CLSID_ShellLink;
 
 
-    ShlLnkHeader.wHotKey = wHotKey;
-    ShlLnkHeader.nIconIndex = iIcoNdx;
-    ShlLnkHeader.dwFlags = SLDF_UNICODE;   /* strings are in unicode */
-    if (m_pPidl)
-        ShlLnkHeader.dwFlags |= SLDF_HAS_ID_LIST;
-    if (m_sPath)
-        ShlLnkHeader.dwFlags |= SLDF_HAS_LINK_INFO;
-    if (m_sDescription)
-        ShlLnkHeader.dwFlags |= SLDF_HAS_NAME;
-    if (m_sWorkDir)
-        ShlLnkHeader.dwFlags |= SLDF_HAS_WORKINGDIR;
-    if (m_sArgs)
-        ShlLnkHeader.dwFlags |= SLDF_HAS_ARGS;
-    if (m_sIcoPath)
-        ShlLnkHeader.dwFlags |= SLDF_HAS_ICONLOCATION;
+    /*
+     * Reset the flags: keep only the flags related to data blocks as they were
+     * already set in accordance by the different mutator member functions.
+     * The other flags will be determined now by the presence or absence of data.
+     */
+    m_Header.dwFlags &= (SLDF_RUN_WITH_SHIMLAYER | SLDF_RUNAS_USER |
+                         SLDF_RUN_IN_SEPARATE | SLDF_HAS_DARWINID |
 #if (NTDDI_VERSION < NTDDI_LONGHORN)
 #if (NTDDI_VERSION < NTDDI_LONGHORN)
-    if (sProduct)
-        ShlLnkHeader.dwFlags |= SLDF_HAS_LOGO3ID;
+                         SLDF_HAS_LOGO3ID |
 #endif
 #endif
-    if (sComponent)
-        ShlLnkHeader.dwFlags |= SLDF_HAS_DARWINID;
-    if (m_bRunAs)
-        ShlLnkHeader.dwFlags |= SLDF_RUNAS_USER;
+                         SLDF_HAS_EXP_ICON_SZ | SLDF_HAS_EXP_SZ);
+    // TODO: When we will support Vista+ functionality, add other flags to this list.
 
 
-    SystemTimeToFileTime (&time1, &ShlLnkHeader.ftCreationTime);
-    SystemTimeToFileTime (&time2, &ShlLnkHeader.ftLastAccessTime);
-    SystemTimeToFileTime (&time3, &ShlLnkHeader.ftLastWriteTime);
+    /* The stored strings are in UNICODE */
+    m_Header.dwFlags |= SLDF_UNICODE;
 
 
-    /* write the Shortcut header */
+    if (m_pPidl)
+        m_Header.dwFlags |= SLDF_HAS_ID_LIST;
+    if (m_sPath)
+        m_Header.dwFlags |= SLDF_HAS_LINK_INFO;
+    if (m_sDescription && *m_sDescription)
+        m_Header.dwFlags |= SLDF_HAS_NAME;
+    if (m_sPathRel && *m_sPathRel)
+        m_Header.dwFlags |= SLDF_HAS_RELPATH;
+    if (m_sWorkDir && *m_sWorkDir)
+        m_Header.dwFlags |= SLDF_HAS_WORKINGDIR;
+    if (m_sArgs && *m_sArgs)
+        m_Header.dwFlags |= SLDF_HAS_ARGS;
+    if (m_sIcoPath && *m_sIcoPath)
+        m_Header.dwFlags |= SLDF_HAS_ICONLOCATION;
+    if (m_bRunAs)
+        m_Header.dwFlags |= SLDF_RUNAS_USER;
+
+    /* Write the shortcut header */
     ULONG count;
     ULONG count;
-    HRESULT hr = stm->Write(&ShlLnkHeader, sizeof(ShlLnkHeader), &count);
+    HRESULT hr = stm->Write(&m_Header, sizeof(m_Header), &count);
     if (FAILED(hr))
     {
         ERR("Write failed\n");
         return hr;
     }
 
     if (FAILED(hr))
     {
         ERR("Write failed\n");
         return hr;
     }
 
-    TRACE("Writing pidl\n");
+    /* Save the data in order */
 
 
-    /* write the PIDL to the shortcut */
     if (m_pPidl)
     {
         hr = ILSaveToStream(stm, m_pPidl);
     if (m_pPidl)
     {
         hr = ILSaveToStream(stm, m_pPidl);
@@ -821,34 +916,67 @@ HRESULT STDMETHODCALLTYPE CShellLink::Save(IStream *stm, BOOL fClearDirty)
     }
 
     if (m_sPath)
     }
 
     if (m_sPath)
-        Stream_WriteLocationInfo(stm, m_sPath, &volume);
+    {
+        hr = Stream_WriteLocationInfo(stm, m_sPath, &volume);
+        if (FAILED(hr))
+            return hr;
+    }
 
 
-    if (m_sDescription)
+    if (m_Header.dwFlags & SLDF_HAS_NAME)
+    {
         hr = Stream_WriteString(stm, m_sDescription);
         hr = Stream_WriteString(stm, m_sDescription);
+        if (FAILED(hr))
+            return hr;
+    }
 
 
-    if (m_sPathRel)
+    if (m_Header.dwFlags & SLDF_HAS_RELPATH)
+    {
         hr = Stream_WriteString(stm, m_sPathRel);
         hr = Stream_WriteString(stm, m_sPathRel);
+        if (FAILED(hr))
+            return hr;
+    }
 
 
-    if (m_sWorkDir)
+    if (m_Header.dwFlags & SLDF_HAS_WORKINGDIR)
+    {
         hr = Stream_WriteString(stm, m_sWorkDir);
         hr = Stream_WriteString(stm, m_sWorkDir);
+        if (FAILED(hr))
+            return hr;
+    }
 
 
-    if (m_sArgs)
+    if (m_Header.dwFlags & SLDF_HAS_ARGS)
+    {
         hr = Stream_WriteString(stm, m_sArgs);
         hr = Stream_WriteString(stm, m_sArgs);
+        if (FAILED(hr))
+            return hr;
+    }
 
 
-    if (m_sIcoPath)
+    if (m_Header.dwFlags & SLDF_HAS_ICONLOCATION)
+    {
         hr = Stream_WriteString(stm, m_sIcoPath);
         hr = Stream_WriteString(stm, m_sIcoPath);
+        if (FAILED(hr))
+            return hr;
+    }
 
 
-    if (sProduct)
-        hr = Stream_WriteAdvertiseInfo(stm, sProduct, EXP_SZ_ICON_SIG);
+    /*
+     * Now save the data block list.
+     *
+     * NOTE that both advertised Product and Component are already saved
+     * inside Logo3 and Darwin data blocks in the m_pDBList list, and the
+     * m_Header.dwFlags is suitably initialized.
+     */
+    hr = SHWriteDataBlockList(stm, m_pDBList);
+    if (FAILED(hr))
+        return hr;
 
 
-    if (sComponent)
-        hr = Stream_WriteAdvertiseInfo(stm, sComponent, EXP_DARWIN_ID_SIG);
+    /* Clear the dirty bit if requested */
+    if (fClearDirty)
+        m_bDirty = FALSE;
 
 
-    /* the last field is a single zero dword */
-    DWORD zero = 0;
-    hr = stm->Write(&zero, sizeof zero, &count);
+    /* The last field is a single zero dword */
+    DWORD dwZero = 0;
+    hr = stm->Write(&dwZero, sizeof(dwZero), &count);
 
 
-    return S_OK;
+    return hr;
 }
 
 /************************************************************************
 }
 
 /************************************************************************
@@ -926,22 +1054,47 @@ static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWor
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetPath(LPSTR pszFile, INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
 {
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetPath(LPSTR pszFile, INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
 {
+    HRESULT hr;
+    LPWSTR pszFileW;
+    WIN32_FIND_DATAW wfd;
+
     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
           this, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(m_sPath));
 
     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
           this, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(m_sPath));
 
-    if (sComponent || sProduct)
-        return S_FALSE;
+    /* Allocate a temporary UNICODE buffer */
+    pszFileW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, cchMaxPath * sizeof(WCHAR));
+    if (!pszFileW)
+        return E_OUTOFMEMORY;
 
 
-    if (cchMaxPath)
-        pszFile[0] = 0;
+    /* Call the UNICODE function */
+    hr = GetPath(pszFileW, cchMaxPath, &wfd, fFlags);
 
 
-    if (m_sPath)
-        WideCharToMultiByte(CP_ACP, 0, m_sPath, -1,
-                             pszFile, cchMaxPath, NULL, NULL);
+    /* Convert the file path back to ANSI */
+    WideCharToMultiByte(CP_ACP, 0, pszFileW, -1,
+                        pszFile, cchMaxPath, NULL, NULL);
 
 
-    if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
+    /* Free the temporary buffer */
+    HeapFree(GetProcessHeap(), 0, pszFileW);
 
 
-    return S_OK;
+    if (pfd)
+    {
+        ZeroMemory(pfd, sizeof(*pfd));
+
+        /* Copy the file data if a file path was returned */
+        if (*pszFileW)
+        {
+            /* Copy the fixed part */
+            CopyMemory(pfd, &wfd, FIELD_OFFSET(WIN32_FIND_DATAA, cFileName));
+
+            /* Convert the file names to ANSI */
+            WideCharToMultiByte(CP_ACP, 0, wfd.cFileName, sizeof(wfd.cFileName),
+                                pfd->cFileName, sizeof(pfd->cFileName), NULL, NULL);
+            WideCharToMultiByte(CP_ACP, 0, wfd.cAlternateFileName, sizeof(wfd.cAlternateFileName),
+                                pfd->cAlternateFileName, sizeof(pfd->cAlternateFileName), NULL, NULL);
+        }
+    }
+
+    return hr;
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetIDList(LPITEMIDLIST *ppidl)
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetIDList(LPITEMIDLIST *ppidl)
@@ -961,17 +1114,7 @@ HRESULT STDMETHODCALLTYPE CShellLink::GetIDList(LPITEMIDLIST *ppidl)
 HRESULT STDMETHODCALLTYPE CShellLink::SetIDList(LPCITEMIDLIST pidl)
 {
     TRACE("(%p)->(pidl=%p)\n", this, pidl);
 HRESULT STDMETHODCALLTYPE CShellLink::SetIDList(LPCITEMIDLIST pidl)
 {
     TRACE("(%p)->(pidl=%p)\n", this, pidl);
-
-    if (m_pPidl)
-        ILFree(m_pPidl);
-
-    m_pPidl = ILClone(pidl);
-    if (!m_pPidl)
-        return E_FAIL;
-
-    m_bDirty = TRUE;
-
-    return S_OK;
+    return SetTargetFromPIDLOrPath(pidl, NULL);
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetDescription(LPSTR pszName, INT cchMaxName)
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetDescription(LPSTR pszName, INT cchMaxName)
@@ -1073,8 +1216,8 @@ HRESULT STDMETHODCALLTYPE CShellLink::SetArguments(LPCSTR pszArgs)
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetHotkey(WORD *pwHotkey)
 {
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetHotkey(WORD *pwHotkey)
 {
-    TRACE("(%p)->(%p)(0x%08x)\n", this, pwHotkey, wHotKey);
-    *pwHotkey = wHotKey;
+    TRACE("(%p)->(%p)(0x%08x)\n", this, pwHotkey, m_Header.wHotKey);
+    *pwHotkey = m_Header.wHotKey;
     return S_OK;
 }
 
     return S_OK;
 }
 
@@ -1082,7 +1225,7 @@ HRESULT STDMETHODCALLTYPE CShellLink::SetHotkey(WORD wHotkey)
 {
     TRACE("(%p)->(hotkey=%x)\n", this, wHotkey);
 
 {
     TRACE("(%p)->(hotkey=%x)\n", this, wHotkey);
 
-    wHotKey = wHotkey;
+    m_Header.wHotKey = wHotkey;
     m_bDirty = TRUE;
 
     return S_OK;
     m_bDirty = TRUE;
 
     return S_OK;
@@ -1090,8 +1233,8 @@ HRESULT STDMETHODCALLTYPE CShellLink::SetHotkey(WORD wHotkey)
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetShowCmd(INT *piShowCmd)
 {
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetShowCmd(INT *piShowCmd)
 {
-    TRACE("(%p)->(%p) %d\n", this, piShowCmd, iShowCmd);
-    *piShowCmd = iShowCmd;
+    TRACE("(%p)->(%p) %d\n", this, piShowCmd, m_Header.nShowCommand);
+    *piShowCmd = m_Header.nShowCommand;
     return S_OK;
 }
 
     return S_OK;
 }
 
@@ -1099,7 +1242,7 @@ HRESULT STDMETHODCALLTYPE CShellLink::SetShowCmd(INT iShowCmd)
 {
     TRACE("(%p) %d\n", this, iShowCmd);
 
 {
     TRACE("(%p) %d\n", this, iShowCmd);
 
-    this->iShowCmd = iShowCmd;
+    m_Header.nShowCommand = iShowCmd;
     m_bDirty = TRUE;
 
     return S_OK;
     m_bDirty = TRUE;
 
     return S_OK;
@@ -1130,24 +1273,69 @@ HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(LPSTR pszIconPath, INT cch
     return hr;
 }
 
     return hr;
 }
 
+HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(UINT uFlags, PSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
+{
+    HRESULT hr;
+    LPWSTR pszIconFileW;
+
+    TRACE("(%p)->(%u %p len=%u piIndex=%p pwFlags=%p)\n", this, uFlags, pszIconFile, cchMax, piIndex, pwFlags);
+
+    /* Allocate a temporary UNICODE buffer */
+    pszIconFileW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, cchMax * sizeof(WCHAR));
+    if (!pszIconFileW)
+        return E_OUTOFMEMORY;
+
+    /* Call the UNICODE function */
+    hr = GetIconLocation(uFlags, pszIconFileW, cchMax, piIndex, pwFlags);
+
+    /* Convert the file path back to ANSI */
+    WideCharToMultiByte(CP_ACP, 0, pszIconFileW, -1,
+                        pszIconFile, cchMax, NULL, NULL);
+
+    /* Free the temporary buffer */
+    HeapFree(GetProcessHeap(), 0, pszIconFileW);
+
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE CShellLink::Extract(PCSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize)
+{
+    TRACE("(%p)->(path=%s iicon=%u)\n", this, pszFile, nIconIndex);
+
+    LPWSTR str = NULL;
+    if (pszFile)
+    {
+        str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
+        if (!str)
+            return E_OUTOFMEMORY;
+    }
+
+    HRESULT hr = Extract(str, nIconIndex, phiconLarge, phiconSmall, nIconSize);
+
+    if (str)
+        HeapFree(GetProcessHeap(), 0, str);
+
+    return hr;
+}
+
 HRESULT STDMETHODCALLTYPE CShellLink::SetIconLocation(LPCSTR pszIconPath, INT iIcon)
 {
     TRACE("(%p)->(path=%s iicon=%u)\n", this, pszIconPath, iIcon);
 
 HRESULT STDMETHODCALLTYPE CShellLink::SetIconLocation(LPCSTR pszIconPath, INT iIcon)
 {
     TRACE("(%p)->(path=%s iicon=%u)\n", this, pszIconPath, iIcon);
 
-    HeapFree(GetProcessHeap(), 0, m_sIcoPath);
-    m_sIcoPath = NULL;
-
+    LPWSTR str = NULL;
     if (pszIconPath)
     {
     if (pszIconPath)
     {
-        m_sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
-        if (!m_sIcoPath)
+        str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
+        if (!str)
             return E_OUTOFMEMORY;
     }
 
             return E_OUTOFMEMORY;
     }
 
-    iIcoNdx = iIcon;
-    m_bDirty = TRUE;
+    HRESULT hr = SetIconLocation(str, iIcon);
 
 
-    return S_OK;
+    if (str)
+        HeapFree(GetProcessHeap(), 0, str);
+
+    return hr;
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::SetRelativePath(LPCSTR pszPathRel, DWORD dwReserved)
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::SetRelativePath(LPCSTR pszPathRel, DWORD dwReserved)
@@ -1166,25 +1354,73 @@ HRESULT STDMETHODCALLTYPE CShellLink::SetRelativePath(LPCSTR pszPathRel, DWORD d
     return ShellLink_UpdatePath(m_sPathRel, m_sPath, m_sWorkDir, &m_sPath);
 }
 
     return ShellLink_UpdatePath(m_sPathRel, m_sPath, m_sWorkDir, &m_sPath);
 }
 
-HRESULT STDMETHODCALLTYPE CShellLink::Resolve(HWND hwnd, DWORD fFlags)
+static LPWSTR
+shelllink_get_msi_component_path(LPWSTR component)
+{
+    DWORD Result, sz = 0;
+
+    Result = CommandLineFromMsiDescriptor(component, NULL, &sz);
+    if (Result != ERROR_SUCCESS)
+        return NULL;
+
+    sz++;
+    LPWSTR path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sz * sizeof(WCHAR));
+    Result = CommandLineFromMsiDescriptor(component, path, &sz);
+    if (Result != ERROR_SUCCESS)
+    {
+        HeapFree(GetProcessHeap(), 0, path);
+        path = NULL;
+    }
+
+    TRACE("returning %s\n", debugstr_w(path));
+
+    return path;
+}
+
+HRESULT STDMETHODCALLTYPE CShellLink::Resolve(HWND hwnd, DWORD fFlags)
 {
     HRESULT hr = S_OK;
     BOOL bSuccess;
 
     TRACE("(%p)->(hwnd=%p flags=%x)\n", this, hwnd, fFlags);
 
 {
     HRESULT hr = S_OK;
     BOOL bSuccess;
 
     TRACE("(%p)->(hwnd=%p flags=%x)\n", this, hwnd, fFlags);
 
-    /*FIXME: use IResolveShellLink interface */
+    /* FIXME: use IResolveShellLink interface? */
+
+    // FIXME: See InvokeCommand().
+
+#if (NTDDI_VERSION < NTDDI_LONGHORN)
+    // NOTE: For Logo3 (EXP_LOGO3_ID_SIG), check also for SHRestricted(REST_NOLOGO3CHANNELNOTIFY)
+    if (m_Header.dwFlags & SLDF_HAS_LOGO3ID)
+    {
+        FIXME("Logo3 links are not supported yet!\n");
+        return E_FAIL;
+    }
+#endif
+
+    /* Resolve Darwin (MSI) target */
+    if (m_Header.dwFlags & SLDF_HAS_DARWINID)
+    {
+        LPWSTR component = NULL;
+        hr = GetAdvertiseInfo(&component, EXP_DARWIN_ID_SIG);
+        if (FAILED(hr))
+            return E_FAIL;
+
+        /* Clear the cached path */
+        HeapFree(GetProcessHeap(), 0, m_sPath);
+        m_sPath = NULL;
+        m_sPath = shelllink_get_msi_component_path(component);
+        if (!m_sPath)
+            return E_FAIL;
+    }
 
     if (!m_sPath && m_pPidl)
     {
         WCHAR buffer[MAX_PATH];
 
         bSuccess = SHGetPathFromIDListW(m_pPidl, buffer);
 
     if (!m_sPath && m_pPidl)
     {
         WCHAR buffer[MAX_PATH];
 
         bSuccess = SHGetPathFromIDListW(m_pPidl, buffer);
-
         if (bSuccess && *buffer)
         {
             m_sPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(buffer) + 1) * sizeof(WCHAR));
         if (bSuccess && *buffer)
         {
             m_sPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(buffer) + 1) * sizeof(WCHAR));
-
             if (!m_sPath)
                 return E_OUTOFMEMORY;
 
             if (!m_sPath)
                 return E_OUTOFMEMORY;
 
@@ -1193,18 +1429,20 @@ HRESULT STDMETHODCALLTYPE CShellLink::Resolve(HWND hwnd, DWORD fFlags)
             m_bDirty = TRUE;
         }
         else
             m_bDirty = TRUE;
         }
         else
+        {
             hr = S_OK;    /* don't report an error occurred while just caching information */
             hr = S_OK;    /* don't report an error occurred while just caching information */
+        }
     }
 
     }
 
+    // FIXME: Strange to do that here...
     if (!m_sIcoPath && m_sPath)
     {
         m_sIcoPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(m_sPath) + 1) * sizeof(WCHAR));
     if (!m_sIcoPath && m_sPath)
     {
         m_sIcoPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(m_sPath) + 1) * sizeof(WCHAR));
-
         if (!m_sIcoPath)
             return E_OUTOFMEMORY;
 
         wcscpy(m_sIcoPath, m_sPath);
         if (!m_sIcoPath)
             return E_OUTOFMEMORY;
 
         wcscpy(m_sIcoPath, m_sPath);
-        iIcoNdx = 0;
+        m_Header.nIconIndex = 0;
 
         m_bDirty = TRUE;
     }
 
         m_bDirty = TRUE;
     }
@@ -1231,21 +1469,83 @@ HRESULT STDMETHODCALLTYPE CShellLink::SetPath(LPCSTR pszFile)
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetPath(LPWSTR pszFile, INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
 {
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetPath(LPWSTR pszFile, INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
 {
+    WCHAR buffer[MAX_PATH];
+
     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
           this, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(m_sPath));
 
     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
           this, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(m_sPath));
 
-    if (sComponent || sProduct)
-        return S_FALSE;
-
     if (cchMaxPath)
         *pszFile = 0;
     if (cchMaxPath)
         *pszFile = 0;
+    // FIXME: What if cchMaxPath == 0 , or pszFile == NULL ??
 
 
-    if (m_sPath)
-        lstrcpynW(pszFile, m_sPath, cchMaxPath);
+    // FIXME: What about Darwin??
 
 
-    if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
+    /*
+     * Retrieve the path to the target from the PIDL (if we have one).
+     * NOTE: Do NOT use the cached path (m_sPath from link info).
+     */
+    if (m_pPidl && SHGetPathFromIDListW(m_pPidl, buffer))
+    {
+        if (fFlags & SLGP_SHORTPATH)
+            GetShortPathNameW(buffer, buffer, _countof(buffer));
+        // FIXME: Add support for SLGP_UNCPRIORITY
+    }
+    else
+    {
+        *buffer = 0;
+    }
 
 
-    return S_OK;
+    /* If we have a FindData structure, initialize it */
+    if (pfd)
+    {
+        ZeroMemory(pfd, sizeof(*pfd));
+
+        /* Copy the file data if the target is a file path */
+        if (*buffer)
+        {
+            pfd->dwFileAttributes = m_Header.dwFileAttributes;
+            pfd->ftCreationTime   = m_Header.ftCreationTime;
+            pfd->ftLastAccessTime = m_Header.ftLastAccessTime;
+            pfd->ftLastWriteTime  = m_Header.ftLastWriteTime;
+            pfd->nFileSizeHigh    = 0;
+            pfd->nFileSizeLow     = m_Header.nFileSizeLow;
+
+            /*
+             * Build temporarily a short path in pfd->cFileName (of size MAX_PATH),
+             * then extract and store the short file name in pfd->cAlternateFileName.
+             */
+            GetShortPathNameW(buffer, pfd->cFileName, _countof(pfd->cFileName));
+            lstrcpynW(pfd->cAlternateFileName,
+                      PathFindFileNameW(pfd->cFileName),
+                      _countof(pfd->cAlternateFileName));
+
+            /* Now extract and store the long file name in pfd->cFileName */
+            lstrcpynW(pfd->cFileName,
+                      PathFindFileNameW(buffer),
+                      _countof(pfd->cFileName));
+        }
+    }
+
+    /* Finally check if we have a raw path the user actually wants to retrieve */
+    if ((fFlags & SLGP_RAWPATH) && (m_Header.dwFlags & SLDF_HAS_EXP_SZ))
+    {
+        /* Search for a target environment block */
+        LPEXP_SZ_LINK pInfo;
+        pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_LINK_SIG);
+        if (pInfo && (pInfo->cbSize == sizeof(*pInfo)))
+            lstrcpynW(buffer, pInfo->szwTarget, cchMaxPath);
+    }
+
+    /* For diagnostics purposes only... */
+    // NOTE: SLGP_UNCPRIORITY is unsupported
+    fFlags &= ~(SLGP_RAWPATH | SLGP_SHORTPATH);
+    if (fFlags) FIXME("(%p): Unsupported flags %lu\n", this, fFlags);
+
+    /* Copy the data back to the user */
+    if (*buffer)
+        lstrcpynW(pszFile, buffer, cchMaxPath);
+
+    return (*buffer ? S_OK : S_FALSE);
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetDescription(LPWSTR pszName, INT cchMaxName)
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetDescription(LPWSTR pszName, INT cchMaxName)
@@ -1350,11 +1650,55 @@ HRESULT STDMETHODCALLTYPE CShellLink::SetArguments(LPCWSTR pszArgs)
     return S_OK;
 }
 
     return S_OK;
 }
 
+HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(LPWSTR pszIconPath, INT cchIconPath, INT *piIcon)
+{
+    TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath, cchIconPath, piIcon);
+
+    if (cchIconPath)
+        *pszIconPath = 0;
+
+    *piIcon = 0;
+
+    /* Update the original icon path location */
+    if (m_Header.dwFlags & SLDF_HAS_EXP_ICON_SZ)
+    {
+        WCHAR szPath[MAX_PATH];
+
+        /* Search for an icon environment block */
+        LPEXP_SZ_LINK pInfo;
+        pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_ICON_SIG);
+        if (pInfo && (pInfo->cbSize == sizeof(*pInfo)))
+        {
+            m_Header.dwFlags &= ~SLDF_HAS_ICONLOCATION;
+            HeapFree(GetProcessHeap(), 0, m_sIcoPath);
+            m_sIcoPath = NULL;
+
+            SHExpandEnvironmentStringsW(pInfo->szwTarget, szPath, _countof(szPath));
+
+            m_sIcoPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
+                                           (wcslen(szPath) + 1) * sizeof(WCHAR));
+            if (!m_sIcoPath)
+                return E_OUTOFMEMORY;
+
+            wcscpy(m_sIcoPath, szPath);
+            m_Header.dwFlags |= SLDF_HAS_ICONLOCATION;
+
+            m_bDirty = TRUE;
+        }
+    }
+
+    *piIcon = m_Header.nIconIndex;
+
+    if (m_sIcoPath)
+        lstrcpynW(pszIconPath, m_sIcoPath, cchIconPath);
+
+    return S_OK;
+}
+
 static HRESULT SHELL_PidlGetIconLocationW(IShellFolder* psf, LPCITEMIDLIST pidl,
 static HRESULT SHELL_PidlGetIconLocationW(IShellFolder* psf, LPCITEMIDLIST pidl,
-        PWSTR pszIconFile, UINT cchMax, int *piIndex)
+        UINT uFlags, PWSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
 {
     LPCITEMIDLIST pidlLast;
 {
     LPCITEMIDLIST pidlLast;
-    UINT wFlags;
 
     HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
     if (SUCCEEDED(hr))
 
     HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
     if (SUCCEEDED(hr))
@@ -1363,7 +1707,7 @@ static HRESULT SHELL_PidlGetIconLocationW(IShellFolder* psf, LPCITEMIDLIST pidl,
 
         hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_NULL_PPV_ARG(IExtractIconW, &pei));
         if (SUCCEEDED(hr))
 
         hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_NULL_PPV_ARG(IExtractIconW, &pei));
         if (SUCCEEDED(hr))
-            hr = pei->GetIconLocation(0, pszIconFile, cchMax, piIndex, &wFlags);
+            hr = pei->GetIconLocation(uFlags, pszIconFile, cchMax, piIndex, pwFlags);
 
         psf->Release();
     }
 
         psf->Release();
     }
@@ -1371,75 +1715,236 @@ static HRESULT SHELL_PidlGetIconLocationW(IShellFolder* psf, LPCITEMIDLIST pidl,
     return hr;
 }
 
     return hr;
 }
 
-HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(LPWSTR pszIconPath, INT cchIconPath, INT *piIcon)
+HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(UINT uFlags, PWSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
 {
 {
-    TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath, cchIconPath, piIcon);
-
-    if (cchIconPath)
-        *pszIconPath = 0;
-
-    *piIcon = iIcoNdx;
+    /*
+     * It is possible for a shell link to point to another shell link,
+     * and in particular there is the possibility to point to itself.
+     * Now, suppose we ask such a link to retrieve its associated icon.
+     * This function would be called, and due to COM would be called again
+     * recursively. To solve this issue, we forbid calling GetIconLocation()
+     * with GIL_FORSHORTCUT set in uFlags, as done by Windows (shown by tests).
+     */
+    if (uFlags & GIL_FORSHORTCUT)
+        return E_INVALIDARG;
 
 
-    if (m_sIcoPath)
-    {
-        lstrcpynW(pszIconPath, m_sIcoPath, cchIconPath);
-        return S_OK;
-    }
+    /*
+     * Now, we set GIL_FORSHORTCUT so that: i) we allow the icon extractor
+     * of the target to give us a suited icon, and ii) we protect ourselves
+     * against recursive call.
+     */
+    uFlags |= GIL_FORSHORTCUT;
 
     if (m_pPidl || m_sPath)
     {
 
     if (m_pPidl || m_sPath)
     {
-        CComPtr<IShellFolder>        pdsk;
+        CComPtr<IShellFolder> pdsk;
 
         HRESULT hr = SHGetDesktopFolder(&pdsk);
 
         HRESULT hr = SHGetDesktopFolder(&pdsk);
-
         if (SUCCEEDED(hr))
         {
             /* first look for an icon using the PIDL (if present) */
             if (m_pPidl)
         if (SUCCEEDED(hr))
         {
             /* first look for an icon using the PIDL (if present) */
             if (m_pPidl)
-                hr = SHELL_PidlGetIconLocationW(pdsk, m_pPidl, pszIconPath, cchIconPath, piIcon);
+                hr = SHELL_PidlGetIconLocationW(pdsk, m_pPidl, uFlags, pszIconFile, cchMax, piIndex, pwFlags);
             else
                 hr = E_FAIL;
 
             else
                 hr = E_FAIL;
 
+#if 0 // FIXME: Analyse further whether this is needed...
             /* if we couldn't find an icon yet, look for it using the file system path */
             if (FAILED(hr) && m_sPath)
             {
                 LPITEMIDLIST pidl;
 
             /* if we couldn't find an icon yet, look for it using the file system path */
             if (FAILED(hr) && m_sPath)
             {
                 LPITEMIDLIST pidl;
 
+                /* LPITEMIDLIST pidl = ILCreateFromPathW(sPath); */
                 hr = pdsk->ParseDisplayName(0, NULL, m_sPath, NULL, &pidl, NULL);
                 hr = pdsk->ParseDisplayName(0, NULL, m_sPath, NULL, &pidl, NULL);
-
                 if (SUCCEEDED(hr))
                 {
                 if (SUCCEEDED(hr))
                 {
-                    hr = SHELL_PidlGetIconLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
-
+                    hr = SHELL_PidlGetIconLocationW(pdsk, pidl, uFlags, pszIconFile, cchMax, piIndex, pwFlags);
                     SHFree(pidl);
                 }
             }
                     SHFree(pidl);
                 }
             }
+#endif
         }
         return hr;
     }
         }
         return hr;
     }
+
     return S_OK;
 }
 
     return S_OK;
 }
 
+HRESULT STDMETHODCALLTYPE CShellLink::Extract(PCWSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize)
+{
+    UNIMPLEMENTED;
+    return E_FAIL;
+}
+
+#if 0
+/* Extends the functionality of PathUnExpandEnvStringsW */
+BOOL PathFullyUnExpandEnvStringsW(
+    _In_  LPCWSTR pszPath,
+    _Out_ LPWSTR  pszBuf,
+    _In_  UINT    cchBuf)
+{
+    BOOL Ret = FALSE; // Set to TRUE as soon as PathUnExpandEnvStrings starts unexpanding.
+    BOOL res;
+    LPCWSTR p;
+
+    // *pszBuf = L'\0';
+    while (*pszPath && cchBuf > 0)
+    {
+        /* Attempt unexpanding the path */
+        res = PathUnExpandEnvStringsW(pszPath, pszBuf, cchBuf);
+        if (!res)
+        {
+            /* The unexpansion failed. Try to find a path delimiter. */
+            p = wcspbrk(pszPath, L" /\\:*?\"<>|%");
+            if (!p) /* None found, we will copy the remaining path */
+                p = pszPath + wcslen(pszPath);
+            else    /* Found one, we will copy the delimiter and skip it */
+                ++p;
+            /* If we overflow, we cannot unexpand more, so return FALSE */
+            if (p - pszPath >= cchBuf)
+                return FALSE; // *pszBuf = L'\0';
+
+            /* Copy the untouched portion of path up to the delimiter, included */
+            wcsncpy(pszBuf, pszPath, p - pszPath);
+            pszBuf[p - pszPath] = L'\0'; // NULL-terminate
+
+            /* Advance the pointers and decrease the remaining buffer size */
+            cchBuf -= (p - pszPath);
+            pszBuf += (p - pszPath);
+            pszPath += (p - pszPath);
+        }
+        else
+        {
+            /*
+             * The unexpansion succeeded. Skip the unexpanded part by trying
+             * to find where the original path and the unexpanded string
+             * become different.
+             * NOTE: An alternative(?) would be to stop also at the last
+             * path delimiter encountered in the loop (i.e. would be the
+             * first path delimiter in the strings).
+             */
+            LPWSTR q;
+
+            /*
+             * The algorithm starts at the end of the strings and loops back
+             * while the characters are equal, until it finds a discrepancy.
+             */
+            p = pszPath + wcslen(pszPath);
+            q = pszBuf + wcslen(pszBuf); // This wcslen should be < cchBuf
+            while ((*p == *q) && (p > pszPath) && (q > pszBuf))
+            {
+                --p; --q;
+            }
+            /* Skip discrepancy */
+            ++p; ++q;
+
+            /* Advance the pointers and decrease the remaining buffer size */
+            cchBuf -= (q - pszBuf);
+            pszBuf = q;
+            pszPath = p;
+
+            Ret = TRUE;
+        }
+    }
+
+    return Ret;
+}
+#endif
+
 HRESULT STDMETHODCALLTYPE CShellLink::SetIconLocation(LPCWSTR pszIconPath, INT iIcon)
 {
 HRESULT STDMETHODCALLTYPE CShellLink::SetIconLocation(LPCWSTR pszIconPath, INT iIcon)
 {
+    HRESULT hr = E_FAIL;
+    WCHAR szUnExpIconPath[MAX_PATH];
+
     TRACE("(%p)->(path=%s iicon=%u)\n", this, debugstr_w(pszIconPath), iIcon);
 
     TRACE("(%p)->(path=%s iicon=%u)\n", this, debugstr_w(pszIconPath), iIcon);
 
-    HeapFree(GetProcessHeap(), 0, m_sIcoPath);
     if (pszIconPath)
     {
     if (pszIconPath)
     {
+        /* Try to fully unexpand the icon path */
+
+        /*
+         * Check whether the user-given file path contains unexpanded
+         * environment variables. If so, create a target environment block.
+         * Note that in this block we will store the user-given path.
+         * It will contain the unexpanded environment variables, but
+         * it can also contain already expanded path that the user does
+         * not want to see them unexpanded (e.g. so that they always
+         * refer to the same place even if the would-be corresponding
+         * environment variable could change).
+         */
+        // FIXME: http://stackoverflow.com/questions/2976489/ishelllinkseticonlocation-translates-my-icon-path-into-program-files-which-i
+        // if (PathFullyUnExpandEnvStringsW(pszIconPath, szUnExpIconPath, _countof(szUnExpIconPath)))
+        SHExpandEnvironmentStringsW(pszIconPath, szUnExpIconPath, _countof(szUnExpIconPath));
+        if (wcscmp(pszIconPath, szUnExpIconPath) != 0)
+        {
+            /* Unexpansion succeeded, so we need an icon environment block */
+            EXP_SZ_LINK buffer;
+            LPEXP_SZ_LINK pInfo;
+
+            pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_ICON_SIG);
+            if (pInfo)
+            {
+                /* Make sure that the size of the structure is valid */
+                if (pInfo->cbSize != sizeof(*pInfo))
+                {
+                    ERR("Ooops. This structure is not as expected...\n");
+
+                    /* Invalid structure, remove it altogether */
+                    m_Header.dwFlags &= ~SLDF_HAS_EXP_ICON_SZ;
+                    RemoveDataBlock(EXP_SZ_ICON_SIG);
+
+                    /* Reset the pointer and go use the static buffer */
+                    pInfo = NULL;
+                }
+            }
+            if (!pInfo)
+            {
+                /* Use the static buffer */
+                pInfo = &buffer;
+                buffer.cbSize = sizeof(buffer);
+                buffer.dwSignature = EXP_SZ_ICON_SIG;
+            }
+
+            lstrcpynW(pInfo->szwTarget, szUnExpIconPath, _countof(pInfo->szwTarget));
+            WideCharToMultiByte(CP_ACP, 0, szUnExpIconPath, -1,
+                                pInfo->szTarget, _countof(pInfo->szTarget), NULL, NULL);
+
+            hr = S_OK;
+            if (pInfo == &buffer)
+                hr = AddDataBlock(pInfo);
+            if (hr == S_OK)
+                m_Header.dwFlags |= SLDF_HAS_EXP_ICON_SZ;
+        }
+        else
+        {
+            /* Unexpansion failed, so we need to remove any icon environment block */
+            m_Header.dwFlags &= ~SLDF_HAS_EXP_ICON_SZ;
+            RemoveDataBlock(EXP_SZ_ICON_SIG);
+        }
+    }
+
+    /* Store the original icon path location (this one may contain unexpanded environment strings) */
+    if (pszIconPath)
+    {
+        m_Header.dwFlags &= ~SLDF_HAS_ICONLOCATION;
+        HeapFree(GetProcessHeap(), 0, m_sIcoPath);
+        m_sIcoPath = NULL;
+
         m_sIcoPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
         m_sIcoPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
-                                      (wcslen(pszIconPath) + 1) * sizeof (WCHAR));
+                                     (wcslen(pszIconPath) + 1) * sizeof(WCHAR));
         if (!m_sIcoPath)
             return E_OUTOFMEMORY;
         if (!m_sIcoPath)
             return E_OUTOFMEMORY;
+
         wcscpy(m_sIcoPath, pszIconPath);
         wcscpy(m_sIcoPath, pszIconPath);
+        m_Header.dwFlags |= SLDF_HAS_ICONLOCATION;
     }
     }
-    else
-        m_sIcoPath = NULL;
 
 
-    iIcoNdx = iIcon;
+    hr = S_OK;
+
+    m_Header.nIconIndex = iIcon;
     m_bDirty = TRUE;
 
     m_bDirty = TRUE;
 
-    return S_OK;
+    return hr;
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::SetRelativePath(LPCWSTR pszPathRel, DWORD dwReserved)
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::SetRelativePath(LPCWSTR pszPathRel, DWORD dwReserved)
@@ -1482,9 +1987,81 @@ static LPWSTR GetAdvertisedArg(LPCWSTR str)
     return ret;
 }
 
     return ret;
 }
 
+HRESULT CShellLink::WriteAdvertiseInfo(LPCWSTR string, DWORD dwSig)
+{
+    EXP_DARWIN_LINK buffer;
+    LPEXP_DARWIN_LINK pInfo;
+
+    if (   (dwSig != EXP_DARWIN_ID_SIG)
+#if (NTDDI_VERSION < NTDDI_LONGHORN)
+        && (dwSig != EXP_LOGO3_ID_SIG)
+#endif
+        )
+    {
+        return E_INVALIDARG;
+    }
+
+    if (!string)
+        return S_FALSE;
+
+    pInfo = (LPEXP_DARWIN_LINK)SHFindDataBlock(m_pDBList, dwSig);
+    if (pInfo)
+    {
+        /* Make sure that the size of the structure is valid */
+        if (pInfo->dbh.cbSize != sizeof(*pInfo))
+        {
+            ERR("Ooops. This structure is not as expected...\n");
+
+            /* Invalid structure, remove it altogether */
+            if (dwSig == EXP_DARWIN_ID_SIG)
+                m_Header.dwFlags &= ~SLDF_HAS_DARWINID;
+#if (NTDDI_VERSION < NTDDI_LONGHORN)
+            else if (dwSig == EXP_LOGO3_ID_SIG)
+                m_Header.dwFlags &= ~SLDF_HAS_LOGO3ID;
+#endif
+            RemoveDataBlock(dwSig);
+
+            /* Reset the pointer and go use the static buffer */
+            pInfo = NULL;
+        }
+    }
+    if (!pInfo)
+    {
+        /* Use the static buffer */
+        pInfo = &buffer;
+        buffer.dbh.cbSize = sizeof(buffer);
+        buffer.dbh.dwSignature = dwSig;
+    }
+
+    lstrcpynW(pInfo->szwDarwinID, string, _countof(pInfo->szwDarwinID));
+    WideCharToMultiByte(CP_ACP, 0, string, -1,
+                        pInfo->szDarwinID, _countof(pInfo->szDarwinID), NULL, NULL);
+
+    HRESULT hr = S_OK;
+    if (pInfo == &buffer)
+        hr = AddDataBlock(pInfo);
+    if (hr == S_OK)
+    {
+        if (dwSig == EXP_DARWIN_ID_SIG)
+            m_Header.dwFlags |= SLDF_HAS_DARWINID;
+#if (NTDDI_VERSION < NTDDI_LONGHORN)
+        else if (dwSig == EXP_LOGO3_ID_SIG)
+            m_Header.dwFlags |= SLDF_HAS_LOGO3ID;
+#endif
+    }
+
+    return hr;
+}
+
 HRESULT CShellLink::SetAdvertiseInfo(LPCWSTR str)
 {
 HRESULT CShellLink::SetAdvertiseInfo(LPCWSTR str)
 {
-    LPCWSTR szComponent = NULL, szProduct = NULL;
+    HRESULT hr;
+    LPCWSTR szComponent = NULL, szProduct = NULL, p;
+    INT len;
+    GUID guid;
+    WCHAR szGuid[38+1];
+
+    /**/sProduct = sComponent = NULL;/**/
 
     while (str[0])
     {
 
     while (str[0])
     {
@@ -1498,29 +2075,27 @@ HRESULT CShellLink::SetAdvertiseInfo(LPCWSTR str)
         str += 2;
 
         /* there must be a colon straight after a guid */
         str += 2;
 
         /* there must be a colon straight after a guid */
-        LPCWSTR p = wcschr(str, L':');
+        p = wcschr(str, L':');
         if (!p)
             return E_FAIL;
         if (!p)
             return E_FAIL;
-        INT len = p - str;
+        len = p - str;
         if (len != 38)
             return E_FAIL;
 
         /* get the guid, and check if it's validly formatted */
         if (len != 38)
             return E_FAIL;
 
         /* get the guid, and check if it's validly formatted */
-        WCHAR szGuid[39];
         memcpy(szGuid, str, sizeof(WCHAR)*len);
         szGuid[len] = 0;
 
         memcpy(szGuid, str, sizeof(WCHAR)*len);
         szGuid[len] = 0;
 
-        GUID guid;
-        HRESULT hr = CLSIDFromString(szGuid, &guid);
+        hr = CLSIDFromString(szGuid, &guid);
         if (hr != S_OK)
             return hr;
         str = p + 1;
 
         /* match it up to a guid that we care about */
         if (IsEqualGUID(guid, SHELL32_AdvtShortcutComponent) && !szComponent)
         if (hr != S_OK)
             return hr;
         str = p + 1;
 
         /* match it up to a guid that we care about */
         if (IsEqualGUID(guid, SHELL32_AdvtShortcutComponent) && !szComponent)
-            szComponent = str;
+            szComponent = str; /* Darwin */
         else if (IsEqualGUID(guid, SHELL32_AdvtShortcutProduct) && !szProduct)
         else if (IsEqualGUID(guid, SHELL32_AdvtShortcutProduct) && !szProduct)
-            szProduct = str;
+            szProduct = str;   /* Logo3  */
         else
             return E_FAIL;
 
         else
             return E_FAIL;
 
@@ -1534,15 +2109,208 @@ HRESULT CShellLink::SetAdvertiseInfo(LPCWSTR str)
     if (!szComponent)
         return E_FAIL;
 
     if (!szComponent)
         return E_FAIL;
 
-    sComponent = GetAdvertisedArg(szComponent);
-    sProduct = GetAdvertisedArg(szProduct);
+    szComponent = GetAdvertisedArg(szComponent);
+    szProduct = GetAdvertisedArg(szProduct);
 
 
-    TRACE("Component = %s\n", debugstr_w(sComponent));
-    TRACE("Product = %s\n", debugstr_w(sProduct));
+    hr = WriteAdvertiseInfo(szComponent, EXP_DARWIN_ID_SIG);
+    // if (FAILED(hr))
+        // return hr;
+#if (NTDDI_VERSION < NTDDI_LONGHORN)
+    hr = WriteAdvertiseInfo(szProduct, EXP_LOGO3_ID_SIG);
+    // if (FAILED(hr))
+        // return hr;
+#endif
+
+    HeapFree(GetProcessHeap(), 0, (PVOID)szComponent);
+    HeapFree(GetProcessHeap(), 0, (PVOID)szProduct);
+
+    if (TRACE_ON(shell))
+    {
+        GetAdvertiseInfo(&sComponent, EXP_DARWIN_ID_SIG);
+        TRACE("Component = %s\n", debugstr_w(sComponent));
+#if (NTDDI_VERSION < NTDDI_LONGHORN)
+        GetAdvertiseInfo(&sProduct, EXP_LOGO3_ID_SIG);
+        TRACE("Product = %s\n", debugstr_w(sProduct));
+#endif
+    }
 
     return S_OK;
 }
 
 
     return S_OK;
 }
 
+/*
+ * 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;
+}
+
 HRESULT STDMETHODCALLTYPE CShellLink::SetPath(LPCWSTR pszFile)
 {
     LPWSTR unquoted = NULL;
 HRESULT STDMETHODCALLTYPE CShellLink::SetPath(LPCWSTR pszFile)
 {
     LPWSTR unquoted = NULL;
@@ -1553,6 +2321,13 @@ HRESULT STDMETHODCALLTYPE CShellLink::SetPath(LPCWSTR pszFile)
     if (!pszFile)
         return E_INVALIDARG;
 
     if (!pszFile)
         return E_INVALIDARG;
 
+    /*
+     * Allow upgrading Logo3 shortcuts (m_Header.dwFlags & SLDF_HAS_LOGO3ID),
+     * but forbid upgrading Darwin ones.
+     */
+    if (m_Header.dwFlags & SLDF_HAS_DARWINID)
+        return S_FALSE;
+
     /* quotes at the ends of the string are stripped */
     SIZE_T len = wcslen(pszFile);
     if (pszFile[0] == L'"' && pszFile[len-1] == L'"')
     /* quotes at the ends of the string are stripped */
     SIZE_T len = wcslen(pszFile);
     if (pszFile[0] == L'"' && pszFile[len-1] == L'"')
@@ -1565,120 +2340,167 @@ HRESULT STDMETHODCALLTYPE CShellLink::SetPath(LPCWSTR pszFile)
     /* any other quote marks are invalid */
     if (wcschr(pszFile, L'"'))
     {
     /* any other quote marks are invalid */
     if (wcschr(pszFile, L'"'))
     {
-        HeapFree(GetProcessHeap(), 0, unquoted);
-        return S_FALSE;
+        hr = S_FALSE;
+        goto end;
     }
 
     }
 
+    /* Clear the cached path */
     HeapFree(GetProcessHeap(), 0, m_sPath);
     m_sPath = NULL;
 
     HeapFree(GetProcessHeap(), 0, m_sPath);
     m_sPath = NULL;
 
-    HeapFree(GetProcessHeap(), 0, sComponent);
-    sComponent = NULL;
+    /* Check for an advertised target (Logo3 or Darwin) */
+    if (SetAdvertiseInfo(pszFile) != S_OK)
+    {
+        /* This is not an advertised target, but a regular path */
+        WCHAR szPath[MAX_PATH];
+
+        /*
+         * Check whether the user-given file path contains unexpanded
+         * environment variables. If so, create a target environment block.
+         * Note that in this block we will store the user-given path.
+         * It will contain the unexpanded environment variables, but
+         * it can also contain already expanded path that the user does
+         * not want to see them unexpanded (e.g. so that they always
+         * refer to the same place even if the would-be corresponding
+         * environment variable could change).
+         */
+        if (*pszFile)
+            SHExpandEnvironmentStringsW(pszFile, szPath, _countof(szPath));
+        else
+            *szPath = L'\0';
 
 
-    if (m_pPidl)
-        ILFree(m_pPidl);
-    m_pPidl = NULL;
+        if (*pszFile && (wcscmp(pszFile, szPath) != 0))
+        {
+            /*
+             * The user-given file path contains unexpanded environment
+             * variables, so we need a target environment block.
+             */
+            EXP_SZ_LINK buffer;
+            LPEXP_SZ_LINK pInfo;
+
+            pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_LINK_SIG);
+            if (pInfo)
+            {
+                /* Make sure that the size of the structure is valid */
+                if (pInfo->cbSize != sizeof(*pInfo))
+                {
+                    ERR("Ooops. This structure is not as expected...\n");
 
 
-    if (S_OK != SetAdvertiseInfo(pszFile))
-    {
-        WCHAR buffer[MAX_PATH];
-        LPWSTR fname;
+                    /* Invalid structure, remove it altogether */
+                    m_Header.dwFlags &= ~SLDF_HAS_EXP_SZ;
+                    RemoveDataBlock(EXP_SZ_LINK_SIG);
 
 
-        if (*pszFile == '\0')
-            *buffer = '\0';
-        else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
-            return E_FAIL;
-        else if(!PathFileExistsW(buffer) &&
-                !SearchPathW(NULL, pszFile, NULL, MAX_PATH, buffer, NULL))
-            hr = S_FALSE;
+                    /* Reset the pointer and go use the static buffer */
+                    pInfo = NULL;
+                }
+            }
+            if (!pInfo)
+            {
+                /* Use the static buffer */
+                pInfo = &buffer;
+                buffer.cbSize = sizeof(buffer);
+                buffer.dwSignature = EXP_SZ_LINK_SIG;
+            }
 
 
-        m_pPidl = SHSimpleIDListFromPathW(pszFile);
-        ShellLink_GetVolumeInfo(buffer, &volume);
+            lstrcpynW(pInfo->szwTarget, pszFile, _countof(pInfo->szwTarget));
+            WideCharToMultiByte(CP_ACP, 0, pszFile, -1,
+                                pInfo->szTarget, _countof(pInfo->szTarget), NULL, NULL);
 
 
-        m_sPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
-                                  (wcslen(buffer) + 1) * sizeof (WCHAR));
-        if (!m_sPath)
-            return E_OUTOFMEMORY;
+            hr = S_OK;
+            if (pInfo == &buffer)
+                hr = AddDataBlock(pInfo);
+            if (hr == S_OK)
+                m_Header.dwFlags |= SLDF_HAS_EXP_SZ;
 
 
-        wcscpy(m_sPath, buffer);
+            /* Now, make pszFile point to the expanded buffer */
+            pszFile = szPath;
+        }
+        else
+        {
+            /*
+             * The user-given file path does not contain unexpanded environment
+             * variables, so we need to remove any target environment block.
+             */
+            m_Header.dwFlags &= ~SLDF_HAS_EXP_SZ;
+            RemoveDataBlock(EXP_SZ_LINK_SIG);
+
+            /* pszFile points to the user path */
+        }
+
+        /* Set the target */
+        hr = SetTargetFromPIDLOrPath(NULL, pszFile);
     }
 
     m_bDirty = TRUE;
     }
 
     m_bDirty = TRUE;
-    HeapFree(GetProcessHeap(), 0, unquoted);
 
 
+end:
+    HeapFree(GetProcessHeap(), 0, unquoted);
     return hr;
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::AddDataBlock(void* pDataBlock)
 {
     return hr;
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::AddDataBlock(void* pDataBlock)
 {
-    FIXME("\n");
-    return E_NOTIMPL;
+    if (SHAddDataBlock(&m_pDBList, (DATABLOCK_HEADER*)pDataBlock))
+    {
+        m_bDirty = TRUE;
+        return S_OK;
+    }
+    return S_FALSE;
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::CopyDataBlock(DWORD dwSig, void** ppDataBlock)
 {
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::CopyDataBlock(DWORD dwSig, void** ppDataBlock)
 {
-    LPVOID block = NULL;
-    HRESULT hr = E_FAIL;
+    DATABLOCK_HEADER* pBlock;
+    PVOID pDataBlock;
 
     TRACE("%p %08x %p\n", this, dwSig, ppDataBlock);
 
 
     TRACE("%p %08x %p\n", this, dwSig, ppDataBlock);
 
-    switch (dwSig)
+    *ppDataBlock = NULL;
+
+    pBlock = SHFindDataBlock(m_pDBList, dwSig);
+    if (!pBlock)
     {
     {
-        case EXP_DARWIN_ID_SIG:
-            if (!sComponent)
-                break;
-            block = shelllink_build_darwinid(sComponent, dwSig);
-            hr = S_OK;
-            break;
-        case EXP_SZ_LINK_SIG:
-        case NT_CONSOLE_PROPS_SIG:
-        case NT_FE_CONSOLE_PROPS_SIG:
-        case EXP_SPECIAL_FOLDER_SIG:
-        case EXP_SZ_ICON_SIG:
-            FIXME("valid but unhandled datablock %08x\n", dwSig);
-            break;
-        default:
-            ERR("unknown datablock %08x\n", dwSig);
+        ERR("unknown datablock %08x (not found)\n", dwSig);
+        return E_FAIL;
     }
     }
-    *ppDataBlock = block;
-    return hr;
+
+    pDataBlock = LocalAlloc(LMEM_ZEROINIT, pBlock->cbSize);
+    if (!pDataBlock)
+        return E_OUTOFMEMORY;
+
+    CopyMemory(pDataBlock, pBlock, pBlock->cbSize);
+
+    *ppDataBlock = pDataBlock;
+    return S_OK;
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::RemoveDataBlock(DWORD dwSig)
 {
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::RemoveDataBlock(DWORD dwSig)
 {
-    FIXME("\n");
-    return E_NOTIMPL;
+    if (SHRemoveDataBlock(&m_pDBList, dwSig))
+    {
+        m_bDirty = TRUE;
+        return S_OK;
+    }
+    return S_FALSE;
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetFlags(DWORD *pdwFlags)
 {
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::GetFlags(DWORD *pdwFlags)
 {
-    DWORD flags = 0;
-
-    FIXME("%p %p\n", this, pdwFlags);
-
-    /* FIXME: add more */
-    if (m_sArgs)
-        flags |= SLDF_HAS_ARGS;
-    if (sComponent)
-        flags |= SLDF_HAS_DARWINID;
-    if (m_sIcoPath)
-        flags |= SLDF_HAS_ICONLOCATION;
-#if (NTDDI_VERSION < NTDDI_LONGHORN)
-    if (sProduct)
-        flags |= SLDF_HAS_LOGO3ID;
-#endif
-    if (m_pPidl)
-        flags |= SLDF_HAS_ID_LIST;
-
-    *pdwFlags = flags;
-
+    TRACE("%p %p\n", this, pdwFlags);
+    *pdwFlags = m_Header.dwFlags;
     return S_OK;
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::SetFlags(DWORD dwFlags)
 {
     return S_OK;
 }
 
 HRESULT STDMETHODCALLTYPE CShellLink::SetFlags(DWORD dwFlags)
 {
+#if 0 // FIXME!
+    m_Header.dwFlags = dwFlags;
+    m_bDirty = TRUE;
+    return S_OK;
+#else
     FIXME("\n");
     return E_NOTIMPL;
     FIXME("\n");
     return E_NOTIMPL;
+#endif
 }
 
 /**************************************************************************
 }
 
 /**************************************************************************
@@ -1753,32 +2575,8 @@ HRESULT STDMETHODCALLTYPE CShellLink::QueryContextMenu(HMENU hMenu, UINT indexMe
     return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id);
 }
 
     return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id);
 }
 
-static LPWSTR
-shelllink_get_msi_component_path(LPWSTR component)
-{
-    DWORD Result, sz = 0;
-
-    Result = CommandLineFromMsiDescriptor(component, NULL, &sz);
-    if (Result != ERROR_SUCCESS)
-        return NULL;
-
-    sz++;
-    LPWSTR path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sz * sizeof(WCHAR));
-    Result = CommandLineFromMsiDescriptor(component, path, &sz);
-    if (Result != ERROR_SUCCESS)
-    {
-        HeapFree(GetProcessHeap(), 0, path);
-        path = NULL;
-    }
-
-    TRACE("returning %s\n", debugstr_w(path));
-
-    return path;
-}
-
 HRESULT STDMETHODCALLTYPE CShellLink::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
 {
 HRESULT STDMETHODCALLTYPE CShellLink::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
 {
-    HWND hwnd = NULL; /* FIXME: get using interface set from IObjectWithSite */
     LPWSTR args = NULL;
     LPWSTR path = NULL;
 
     LPWSTR args = NULL;
     LPWSTR path = NULL;
 
@@ -1787,20 +2585,18 @@ HRESULT STDMETHODCALLTYPE CShellLink::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
     if (lpici->cbSize < sizeof(CMINVOKECOMMANDINFO))
         return E_INVALIDARG;
 
     if (lpici->cbSize < sizeof(CMINVOKECOMMANDINFO))
         return E_INVALIDARG;
 
-    HRESULT hr = Resolve(hwnd, 0);
+    // NOTE: We could use lpici->hwnd (certainly in case lpici->fMask doesn't contain CMIC_MASK_FLAG_NO_UI)
+    // as the parent window handle... ?
+    /* FIXME: get using interface set from IObjectWithSite?? */
+    // NOTE: We might need an extended version of Resolve that provides us with paths...
+    HRESULT hr = Resolve(lpici->hwnd, 0);
     if (FAILED(hr))
     {
         TRACE("failed to resolve component with error 0x%08x", hr);
         return hr;
     }
     if (FAILED(hr))
     {
         TRACE("failed to resolve component with error 0x%08x", hr);
         return hr;
     }
-    if (sComponent)
-    {
-        path = shelllink_get_msi_component_path(sComponent);
-        if (!path)
-            return E_FAIL;
-    }
-    else
-        path = strdupW(m_sPath);
+
+    path = strdupW(m_sPath);
 
     if ( lpici->cbSize == sizeof(CMINVOKECOMMANDINFOEX) &&
         (lpici->fMask & CMIC_MASK_UNICODE) )
 
     if ( lpici->cbSize == sizeof(CMINVOKECOMMANDINFOEX) &&
         (lpici->fMask & CMIC_MASK_UNICODE) )
@@ -1835,7 +2631,7 @@ HRESULT STDMETHODCALLTYPE CShellLink::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
                (lpici->fMask & (SEE_MASK_NOASYNC | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI));
     sei.lpFile = path;
     sei.lpClass = m_sLinkPath;
                (lpici->fMask & (SEE_MASK_NOASYNC | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI));
     sei.lpFile = path;
     sei.lpClass = m_sLinkPath;
-    sei.nShow = iShowCmd;
+    sei.nShow = m_Header.nShowCommand;
     sei.lpDirectory = m_sWorkDir;
     sei.lpParameters = args;
     sei.lpVerb = L"open";
     sei.lpDirectory = m_sWorkDir;
     sei.lpParameters = args;
     sei.lpVerb = L"open";
@@ -1970,6 +2766,7 @@ INT_PTR CALLBACK CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg, UINT uMsg, WPARAM
                   pThis->m_sIcoPath, pThis->m_sPath, pThis->m_sPathRel, pThis->sProduct, pThis->m_sWorkDir);
 
             /* Get file information */
                   pThis->m_sIcoPath, pThis->m_sPath, pThis->m_sPathRel, pThis->sProduct, pThis->m_sWorkDir);
 
             /* Get file information */
+            // FIXME! FIXME! Shouldn't we use pThis->m_sIcoPath, pThis->m_Header.nIconIndex instead???
             SHFILEINFOW fi;
             if (!SHGetFileInfoW(pThis->m_sLinkPath, 0, &fi, sizeof(fi), SHGFI_TYPENAME|SHGFI_ICON))
             {
             SHFILEINFOW fi;
             if (!SHGetFileInfoW(pThis->m_sLinkPath, 0, &fi, sizeof(fi), SHGFI_TYPENAME|SHGFI_ICON))
             {
@@ -1981,7 +2778,7 @@ INT_PTR CALLBACK CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg, UINT uMsg, WPARAM
             if (fi.hIcon) // TODO: destroy icon
                 SendDlgItemMessageW(hwndDlg, 14000, STM_SETICON, (WPARAM)fi.hIcon, 0);
             else
             if (fi.hIcon) // TODO: destroy icon
                 SendDlgItemMessageW(hwndDlg, 14000, STM_SETICON, (WPARAM)fi.hIcon, 0);
             else
-                ERR("ExtractIconW failed %ls %u\n", pThis->m_sIcoPath, pThis->iIcoNdx);
+                ERR("ExtractIconW failed %ls %u\n", pThis->m_sIcoPath, pThis->m_Header.nIconIndex);
 
             /* target type */
             if (pThis->m_sPath)
 
             /* target type */
             if (pThis->m_sPath)
@@ -1997,9 +2794,9 @@ INT_PTR CALLBACK CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg, UINT uMsg, WPARAM
                 WCHAR newpath[2*MAX_PATH] = L"\0";
                 if (wcschr(pThis->m_sPath, ' '))
                     StringCchPrintfExW(newpath, _countof(newpath), NULL, NULL, 0, L"\"%ls\"", pThis->m_sPath);
                 WCHAR newpath[2*MAX_PATH] = L"\0";
                 if (wcschr(pThis->m_sPath, ' '))
                     StringCchPrintfExW(newpath, _countof(newpath), NULL, NULL, 0, L"\"%ls\"", pThis->m_sPath);
-                else 
+                else
                     StringCchCopyExW(newpath, _countof(newpath), pThis->m_sPath, NULL, NULL, 0);
                     StringCchCopyExW(newpath, _countof(newpath), pThis->m_sPath, NULL, NULL, 0);
-                
+
                 if (pThis->m_sArgs && pThis->m_sArgs[0])
                 {
                     StringCchCatW(newpath, _countof(newpath), L" ");
                 if (pThis->m_sArgs && pThis->m_sArgs[0])
                 {
                     StringCchCatW(newpath, _countof(newpath), L" ");
@@ -2031,7 +2828,7 @@ INT_PTR CALLBACK CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg, UINT uMsg, WPARAM
                 LPWSTR lpszArgs = NULL;
                 LPWSTR unquoted = strdupW(wszBuf);
                 StrTrimW(unquoted, L" ");
                 LPWSTR lpszArgs = NULL;
                 LPWSTR unquoted = strdupW(wszBuf);
                 StrTrimW(unquoted, L" ");
-                if (!PathFileExistsW(unquoted)) 
+                if (!PathFileExistsW(unquoted))
                 {
                     lpszArgs = PathGetArgsW(unquoted);
                     PathRemoveArgsW(unquoted);
                 {
                     lpszArgs = PathGetArgsW(unquoted);
                     PathRemoveArgsW(unquoted);
@@ -2057,11 +2854,11 @@ INT_PTR CALLBACK CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg, UINT uMsg, WPARAM
                     SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
                     return TRUE;
                 }
                     SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
                     return TRUE;
                 }
-                
+
                 pThis->SetPath(unquoted);
                 if (lpszArgs)
                     pThis->SetArguments(lpszArgs);
                 pThis->SetPath(unquoted);
                 if (lpszArgs)
                     pThis->SetArguments(lpszArgs);
-                else 
+                else
                     pThis->SetArguments(L"\0");
 
                 HeapFree(GetProcessHeap(), 0, unquoted);
                     pThis->SetArguments(L"\0");
 
                 HeapFree(GetProcessHeap(), 0, unquoted);
@@ -2089,7 +2886,7 @@ INT_PTR CALLBACK CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg, UINT uMsg, WPARAM
 
                     if (pThis->m_sIcoPath)
                         wcscpy(wszPath, pThis->m_sIcoPath);
 
                     if (pThis->m_sIcoPath)
                         wcscpy(wszPath, pThis->m_sIcoPath);
-                    INT IconIndex = pThis->iIcoNdx;
+                    INT IconIndex = pThis->m_Header.nIconIndex;
                     if (PickIconDlg(hwndDlg, wszPath, _countof(wszPath), &IconIndex))
                     {
                         pThis->SetIconLocation(wszPath, IconIndex);
                     if (PickIconDlg(hwndDlg, wszPath, _countof(wszPath), &IconIndex))
                     {
                         pThis->SetIconLocation(wszPath, IconIndex);
@@ -2182,7 +2979,7 @@ HRESULT STDMETHODCALLTYPE CShellLink::DragEnter(IDataObject *pDataObject,
 
         if (SUCCEEDED(hr))
             hr = m_DropTarget->DragEnter(pDataObject, dwKeyState, pt, pdwEffect);
 
         if (SUCCEEDED(hr))
             hr = m_DropTarget->DragEnter(pDataObject, dwKeyState, pt, pdwEffect);
-        else 
+        else
             *pdwEffect = DROPEFFECT_NONE;
     }
     else
             *pdwEffect = DROPEFFECT_NONE;
     }
     else
index 045e34e..c8ce27e 100644 (file)
@@ -4,6 +4,7 @@
  *      Copyright 1998  Juergen Schmied
  *      Copyright 2005  Mike McCormack
  *      Copyright 2009  Andrew Hill
  *      Copyright 1998  Juergen Schmied
  *      Copyright 2005  Mike McCormack
  *      Copyright 2009  Andrew Hill
+ *      Copyright 2017  Hermes Belusca-Maito
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -29,13 +30,21 @@ class CShellLink :
     public CComObjectRootEx<CComMultiThreadModelNoCS>,
     public IShellLinkA,
     public IShellLinkW,
     public CComObjectRootEx<CComMultiThreadModelNoCS>,
     public IShellLinkA,
     public IShellLinkW,
-    public IPersistFile,
     public IPersistStream,
     public IPersistStream,
-    public IShellLinkDataList,
+    public IPersistFile,
     public IShellExtInit,
     public IShellExtInit,
-    public IContextMenu,
+    public IContextMenu, // Technically it should be IContextMenu3 (inherits from IContextMenu2 and IContextMenu)
     public IDropTarget,
     public IDropTarget,
+//  public IQueryInfo,
+    public IShellLinkDataList,
+    public IExtractIconA,
+    public IExtractIconW,
+//  public IExtractImage2, // Inherits from IExtractImage
+//  public IPersistPropertyBag,
+//  public IServiceProvider,
+//  public IFilter,
     public IObjectWithSite,
     public IObjectWithSite,
+//  public ICustomizeInfoTip,
     public IShellPropSheetExt
 {
 public:
     public IShellPropSheetExt
 {
 public:
@@ -53,14 +62,8 @@ public:
     #include "poppack.h"
 
 private:
     #include "poppack.h"
 
 private:
-    /* data structures according to the information in the link */
-    WORD        wHotKey;
-    SYSTEMTIME    time1;
-    SYSTEMTIME    time2;
-    SYSTEMTIME    time3;
-
-    DWORD         iShowCmd;
-    INT           iIcoNdx;
+    /* Cached link header */
+    SHELL_LINK_HEADER m_Header;
 
     /* Cached data set according to m_Header.dwFlags (SHELL_LINK_DATA_FLAGS) */
 
 
     /* Cached data set according to m_Header.dwFlags (SHELL_LINK_DATA_FLAGS) */
 
@@ -77,7 +80,9 @@ private:
     LPWSTR        m_sIcoPath;
     BOOL          m_bRunAs;
     BOOL          m_bDirty;
     LPWSTR        m_sIcoPath;
     BOOL          m_bRunAs;
     BOOL          m_bDirty;
+    LPDBLIST      m_pDBList; /* Optional data block list (in the extra data section) */
 
 
+    /* Pointers to strings inside Logo3/Darwin info blocks, cached for debug info purposes only */
     LPWSTR sProduct;
     LPWSTR sComponent;
 
     LPWSTR sProduct;
     LPWSTR sComponent;
 
@@ -87,10 +92,16 @@ private:
     CComPtr<IUnknown>    m_site;
     CComPtr<IDropTarget> m_DropTarget;
 
     CComPtr<IUnknown>    m_site;
     CComPtr<IDropTarget> m_DropTarget;
 
+    VOID Reset();
+
+    HRESULT GetAdvertiseInfo(LPWSTR *str, DWORD dwSig);
+    HRESULT SetAdvertiseInfo(LPCWSTR str);
+    HRESULT WriteAdvertiseInfo(LPCWSTR string, DWORD dwSig);
+    HRESULT SetTargetFromPIDLOrPath(LPCITEMIDLIST pidl, LPCWSTR pszFile);
+
 public:
     CShellLink();
     ~CShellLink();
 public:
     CShellLink();
     ~CShellLink();
-    HRESULT SetAdvertiseInfo(LPCWSTR str);
     static INT_PTR CALLBACK SH_ShellLinkDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
 
     // IPersistFile
     static INT_PTR CALLBACK SH_ShellLinkDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
 
     // IPersistFile
@@ -155,6 +166,14 @@ public:
     virtual HRESULT STDMETHODCALLTYPE GetFlags(DWORD *pdwFlags);
     virtual HRESULT STDMETHODCALLTYPE SetFlags(DWORD dwFlags);
 
     virtual HRESULT STDMETHODCALLTYPE GetFlags(DWORD *pdwFlags);
     virtual HRESULT STDMETHODCALLTYPE SetFlags(DWORD dwFlags);
 
+    // IExtractIconA
+    virtual HRESULT STDMETHODCALLTYPE Extract(PCSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize);
+    virtual HRESULT STDMETHODCALLTYPE GetIconLocation(UINT uFlags, PSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags);
+
+    // IExtractIconW
+    virtual HRESULT STDMETHODCALLTYPE Extract(PCWSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize);
+    virtual HRESULT STDMETHODCALLTYPE GetIconLocation(UINT uFlags, PWSTR pszIconFile, UINT cchMax, int *piIndex, UINT *pwFlags);
+
     // IShellExtInit
     virtual HRESULT STDMETHODCALLTYPE Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
 
     // IShellExtInit
     virtual HRESULT STDMETHODCALLTYPE Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
 
@@ -183,17 +202,25 @@ DECLARE_NOT_AGGREGATABLE(CShellLink)
 DECLARE_PROTECT_FINAL_CONSTRUCT()
 
 BEGIN_COM_MAP(CShellLink)
 DECLARE_PROTECT_FINAL_CONSTRUCT()
 
 BEGIN_COM_MAP(CShellLink)
+    COM_INTERFACE_ENTRY_IID(IID_IShellLinkA, IShellLinkA)
+    COM_INTERFACE_ENTRY_IID(IID_IShellLinkW, IShellLinkW)
     COM_INTERFACE_ENTRY2_IID(IID_IPersist, IPersist, IPersistFile)
     COM_INTERFACE_ENTRY_IID(IID_IPersistFile, IPersistFile)
     COM_INTERFACE_ENTRY_IID(IID_IPersistStream, IPersistStream)
     COM_INTERFACE_ENTRY2_IID(IID_IPersist, IPersist, IPersistFile)
     COM_INTERFACE_ENTRY_IID(IID_IPersistFile, IPersistFile)
     COM_INTERFACE_ENTRY_IID(IID_IPersistStream, IPersistStream)
-    COM_INTERFACE_ENTRY_IID(IID_IShellLinkA, IShellLinkA)
-    COM_INTERFACE_ENTRY_IID(IID_IShellLinkW, IShellLinkW)
-    COM_INTERFACE_ENTRY_IID(IID_IShellLinkDataList, IShellLinkDataList)
     COM_INTERFACE_ENTRY_IID(IID_IShellExtInit, IShellExtInit)
     COM_INTERFACE_ENTRY_IID(IID_IShellExtInit, IShellExtInit)
-    COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
+    COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu) // Technically it should be IContextMenu3
     COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget)
     COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget)
-    COM_INTERFACE_ENTRY_IID(IID_IShellPropSheetExt, IShellPropSheetExt)
+//  COM_INTERFACE_ENTRY_IID(IID_IQueryInfo, IQueryInfo)
+    COM_INTERFACE_ENTRY_IID(IID_IShellLinkDataList, IShellLinkDataList)
+    COM_INTERFACE_ENTRY_IID(IID_IExtractIconA, IExtractIconA)
+    COM_INTERFACE_ENTRY_IID(IID_IExtractIconW, IExtractIconW)
+//  COM_INTERFACE_ENTRY_IID(IID_IExtractImage2, IExtractImage2)
+//  COM_INTERFACE_ENTRY_IID(IID_IPersistPropertyBag, IPersistPropertyBag)
+//  COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider)
+//  COM_INTERFACE_ENTRY_IID(IID_IFilter, IFilter)
     COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite)
     COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite)
+//  COM_INTERFACE_ENTRY_IID(IID_ICustomizeInfoTip, ICustomizeInfoTip)
+    COM_INTERFACE_ENTRY_IID(IID_IShellPropSheetExt, IShellPropSheetExt)
 END_COM_MAP()
 };
 
 END_COM_MAP()
 };
 
index 12c1d71..c94ef10 100644 (file)
@@ -19,6 +19,8 @@
 
 #include "precomp.h"
 
 
 #include "precomp.h"
 
+WINE_DEFAULT_DEBUG_CHANNEL(shell);
+
 WCHAR swShell32Name[MAX_PATH];
 
 DWORD NumIconOverlayHandlers = 0;
 WCHAR swShell32Name[MAX_PATH];
 
 DWORD NumIconOverlayHandlers = 0;
@@ -267,11 +269,21 @@ HRESULT CFSExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, RE
             {
                 /* extract icon from shell shortcut */
                 CComPtr<IShellLinkW> psl;
             {
                 /* extract icon from shell shortcut */
                 CComPtr<IShellLinkW> psl;
+                CComPtr<IExtractIconW> pei;
 
                 HRESULT hr = psf->GetUIObjectOf(NULL, 1, &pidl, IID_NULL_PPV_ARG(IShellLinkW, &psl));
                 if (SUCCEEDED(hr))
                 {
                     hr = psl->GetIconLocation(wTemp, _countof(wTemp), &icon_idx);
 
                 HRESULT hr = psf->GetUIObjectOf(NULL, 1, &pidl, IID_NULL_PPV_ARG(IShellLinkW, &psl));
                 if (SUCCEEDED(hr))
                 {
                     hr = psl->GetIconLocation(wTemp, _countof(wTemp), &icon_idx);
+                    if (FAILED(hr) || !*wTemp)
+                    {
+                        /* The icon was not found directly, try to retrieve it from the shell link target */
+                        hr = psl->QueryInterface(IID_PPV_ARG(IExtractIconW, &pei));
+                        if (FAILED(hr) || !pei)
+                            TRACE("No IExtractIconW interface!\n");
+                        else
+                            hr = pei->GetIconLocation(GIL_FORSHELL, wTemp, _countof(wTemp), &icon_idx, &flags);
+                    }
 
                     if (SUCCEEDED(hr) && *wTemp)
                         found = TRUE;
 
                     if (SUCCEEDED(hr) && *wTemp)
                         found = TRUE;