user32/tests: Avoid strlen in trace.
[wine] / dlls / shell32 / shelllink.c
index 55d4b04..aa21a4a 100644 (file)
@@ -1,7 +1,8 @@
 /*
  *
- *     Copyright 1997  Marcus Meissner
- *     Copyright 1998  Juergen Schmied
+ *      Copyright 1997  Marcus Meissner
+ *      Copyright 1998  Juergen Schmied
+ *      Copyright 2005  Mike McCormack
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * NOTES
+ *   Nearly complete information about the binary formats
+ *   of .lnk files available at http://www.wotsit.org
+ *
+ *  You can use winedump to examine the contents of a link file:
+ *   winedump lnk sc.lnk
+ *
+ *  MSI advertised shortcuts are totally undocumented.  They provide an
+ *   icon for a program that is not yet installed, and invoke MSI to
+ *   install the program when the shortcut is clicked on.  They are
+ *   created by passing a special string to SetPath, and the information
+ *   in that string is parsed an stored.
  */
 
-#include "config.h"
-
-#include <string.h>
-#include <stdio.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#include <errno.h>
-#ifdef HAVE_SYS_WAIT_H
-# include <sys/wait.h>
-#endif
+#define COBJMACROS
+#define NONAMELESSUNION
+
 #include "wine/debug.h"
-#include "wine/port.h"
 #include "winerror.h"
+#include "windef.h"
 #include "winbase.h"
 #include "winnls.h"
 #include "winreg.h"
 
+#include "winuser.h"
+#include "wingdi.h"
 #include "shlobj.h"
 #include "undocshell.h"
-#include "bitmaps/wine.xpm"
 
-#include "heap.h"
 #include "pidl.h"
 #include "shell32_main.h"
 #include "shlguid.h"
+#include "shlwapi.h"
+#include "msi.h"
+#include "appmgmt.h"
+
+#include "initguid.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 
+DEFINE_GUID( SHELL32_AdvtShortcutProduct,
+       0x9db1186f,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
+DEFINE_GUID( SHELL32_AdvtShortcutComponent,
+       0x9db1186e,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
+
 /* link file formats */
 
 #include "pshpack1.h"
 
-/* flag1: lnk elements: simple link has 0x0B */
-#define        WORKDIR         0x10
-#define        ARGUMENT        0x20
-#define        ICON            0x40
-#define UNC            0x80
-
-/* fStartup */
-#define        NORMAL          0x01
-#define        MAXIMIZED       0x03
-#define        MINIMIZED       0x07
-
 typedef struct _LINK_HEADER
-{      DWORD   MagicStr;       /* 0x00 'L','\0','\0','\0' */
-       GUID    MagicGuid;      /* 0x04 is CLSID_ShellLink */
-       DWORD   Flag1;          /* 0x14 describes elements following */
-       DWORD   Flag2;          /* 0x18 */
+{
+       DWORD    dwSize;        /* 0x00 size of the header - 0x4c */
+       GUID     MagicGuid;     /* 0x04 is CLSID_ShellLink */
+       DWORD    dwFlags;       /* 0x14 describes elements following */
+       DWORD    dwFileAttr;    /* 0x18 attributes of the target file */
        FILETIME Time1;         /* 0x1c */
        FILETIME Time2;         /* 0x24 */
        FILETIME Time3;         /* 0x2c */
-       DWORD   Unknown1;       /* 0x34 */
-       DWORD   Unknown2;       /* 0x38 icon number */
+       DWORD    dwFileLength;  /* 0x34 File length */
+       DWORD    nIcon;         /* 0x38 icon number */
        DWORD   fStartup;       /* 0x3c startup type */
        DWORD   wHotKey;        /* 0x40 hotkey */
        DWORD   Unknown5;       /* 0x44 */
        DWORD   Unknown6;       /* 0x48 */
-       USHORT  PidlSize;       /* 0x4c */
-       ITEMIDLIST Pidl;        /* 0x4e */
 } LINK_HEADER, * PLINK_HEADER;
 
-#define LINK_HEADER_SIZE (sizeof(LINK_HEADER)-sizeof(ITEMIDLIST))
-
-typedef struct
-{
-       BYTE bWidth;
-       BYTE bHeight;
-       BYTE bColorCount;
-       BYTE bReserved;
-       WORD wPlanes;
-       WORD wBitCount;
-       DWORD dwBytesInRes;
-       WORD nID;
-} GRPICONDIRENTRY;
+#define SHLINK_LOCAL  0
+#define SHLINK_REMOTE 1
 
-typedef struct
+typedef struct _LOCATION_INFO
 {
-       WORD idReserved;
-       WORD idType;
-       WORD idCount;
-       GRPICONDIRENTRY idEntries[1];
-} GRPICONDIR;
+    DWORD  dwTotalSize;
+    DWORD  dwHeaderSize;
+    DWORD  dwFlags;
+    DWORD  dwVolTableOfs;
+    DWORD  dwLocalPathOfs;
+    DWORD  dwNetworkVolTableOfs;
+    DWORD  dwFinalPathOfs;
+} LOCATION_INFO;
 
-typedef struct
+typedef struct _LOCAL_VOLUME_INFO
 {
-       BYTE bWidth;
-       BYTE bHeight;
-       BYTE bColorCount;
-       BYTE bReserved;
-       WORD wPlanes;
-       WORD wBitCount;
-       DWORD dwBytesInRes;
-       DWORD dwImageOffset;
-} ICONDIRENTRY;
+    DWORD dwSize;
+    DWORD dwType;
+    DWORD dwVolSerial;
+    DWORD dwVolLabelOfs;
+} LOCAL_VOLUME_INFO;
 
-typedef struct
+typedef struct volume_info_t
 {
-       WORD idReserved;
-       WORD idType;
-       WORD idCount;
-} ICONDIR;
-
+    DWORD type;
+    DWORD serial;
+    WCHAR label[12];  /* assume 8.3 */
+} volume_info;
 
 #include "poppack.h"
 
-typedef struct
-{
-        HRSRC *pResInfo;
-        int   nIndex;
-} ENUMRESSTRUCT;
-
-static ICOM_VTABLE(IShellLinkA)                slvt;
-static ICOM_VTABLE(IShellLinkW)                slvtw;
-static ICOM_VTABLE(IPersistFile)       pfvt;
-static ICOM_VTABLE(IPersistStream)     psvt;
+static const IShellLinkAVtbl slvt;
+static const IShellLinkWVtbl slvtw;
+static const IPersistFileVtbl pfvt;
+static const IPersistStreamVtbl psvt;
+static const IShellLinkDataListVtbl dlvt;
+static const IShellExtInitVtbl eivt;
+static const IContextMenuVtbl cmvt;
+static const IObjectWithSiteVtbl owsvt;
 
 /* IShellLink Implementation */
 
 typedef struct
 {
-       ICOM_VFIELD(IShellLinkA);
-       DWORD                           ref;
-
-       ICOM_VTABLE(IShellLinkW)*       lpvtblw;
-       ICOM_VTABLE(IPersistFile)*      lpvtblPersistFile;
-       ICOM_VTABLE(IPersistStream)*    lpvtblPersistStream;
+       const IShellLinkAVtbl *lpVtbl;
+       const IShellLinkWVtbl *lpvtblw;
+       const IPersistFileVtbl *lpvtblPersistFile;
+       const IPersistStreamVtbl *lpvtblPersistStream;
+       const IShellLinkDataListVtbl *lpvtblShellLinkDataList;
+       const IShellExtInitVtbl *lpvtblShellExtInit;
+       const IContextMenuVtbl *lpvtblContextMenu;
+       const IObjectWithSiteVtbl *lpvtblObjectWithSite;
 
-       /* internal stream of the IPersistFile interface */
-       IStream*                        lpFileStream;
+       LONG            ref;
 
-       /* data structures according to the informations in the lnk */
-       LPSTR           sPath;
+       /* data structures according to the information in the link */
        LPITEMIDLIST    pPidl;
        WORD            wHotKey;
        SYSTEMTIME      time1;
        SYSTEMTIME      time2;
        SYSTEMTIME      time3;
 
-       LPSTR           sIcoPath;
-       INT             iIcoNdx;
-       LPSTR           sArgs;
-       LPSTR           sWorkDir;
-       LPSTR           sDescription;
+       DWORD         iShowCmd;
+       LPWSTR        sIcoPath;
+       INT           iIcoNdx;
+       LPWSTR        sPath;
+       LPWSTR        sArgs;
+       LPWSTR        sWorkDir;
+       LPWSTR        sDescription;
+       LPWSTR        sPathRel;
+       LPWSTR        sProduct;
+       LPWSTR        sComponent;
+       volume_info   volume;
+
+       BOOL          bDirty;
+        INT           iIdOpen;  /* id of the "Open" entry in the context menu */
+       IUnknown      *site;
 } IShellLinkImpl;
 
-#define _IShellLinkW_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblw)))
-#define _ICOM_THIS_From_IShellLinkW(class, name) class* This = (class*)(((char*)name)-_IShellLinkW_Offset);
-
-#define _IPersistFile_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblPersistFile)))
-#define _ICOM_THIS_From_IPersistFile(class, name) class* This = (class*)(((char*)name)-_IPersistFile_Offset);
-
-#define _IPersistStream_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblPersistStream)))
-#define _ICOM_THIS_From_IPersistStream(class, name) class* This = (class*)(((char*)name)-_IPersistStream_Offset);
-#define _IPersistStream_From_ICOM_THIS(class, name) class* StreamThis = (class*)(((char*)name)+_IPersistStream_Offset);
-
-
-/* strdup on the process heap */
-inline static LPSTR heap_strdup( LPCSTR str )
+static inline IShellLinkImpl *impl_from_IShellLinkW( IShellLinkW *iface )
 {
-    INT len = strlen(str) + 1;
-    LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
-    if (p) memcpy( p, str, len );
-    return p;
+    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblw));
 }
 
-
-/**************************************************************************
- *  IPersistFile_QueryInterface
- */
-static HRESULT WINAPI IPersistFile_fnQueryInterface(
-       IPersistFile* iface,
-       REFIID riid,
-       LPVOID *ppvObj)
+static inline IShellLinkImpl *impl_from_IPersistFile( IPersistFile *iface )
 {
-       _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface)
-
-       TRACE("(%p)\n",This);
-
-       return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
+    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistFile));
 }
 
-/******************************************************************************
- * IPersistFile_AddRef
- */
-static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
+static inline IShellLinkImpl *impl_from_IPersistStream( IPersistStream *iface )
 {
-       _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface)
-
-       TRACE("(%p)->(count=%lu)\n",This,This->ref);
-
-       return IShellLinkA_AddRef((IShellLinkA*)This);
+    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistStream));
 }
-/******************************************************************************
- * IPersistFile_Release
- */
-static ULONG WINAPI IPersistFile_fnRelease(IPersistFile* iface)
-{
-       _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface)
 
-       TRACE("(%p)->(count=%lu)\n",This,This->ref);
-
-       return IShellLinkA_Release((IShellLinkA*)This);
+static inline IShellLinkImpl *impl_from_IShellLinkDataList( IShellLinkDataList *iface )
+{
+    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellLinkDataList));
 }
 
-static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
+static inline IShellLinkImpl *impl_from_IShellExtInit( IShellExtInit *iface )
 {
-       _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface)
-       FIXME("(%p)\n",This);
-       return NOERROR;
+    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellExtInit));
 }
-static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
+
+static inline IShellLinkImpl *impl_from_IContextMenu( IContextMenu *iface )
 {
-       _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface)
-       FIXME("(%p)\n",This);
-       return NOERROR;
+    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblContextMenu));
 }
-static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
-{
-       _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface)
-       _IPersistStream_From_ICOM_THIS(IPersistStream, This)
-
-       LPSTR           sFile = HEAP_strdupWtoA ( GetProcessHeap(), 0, pszFileName);
-       HRESULT         hRet = E_FAIL;
-
-       TRACE("(%p, %s)\n",This, sFile);
-
-
-       if (This->lpFileStream)
-         IStream_Release(This->lpFileStream);
-
-       if SUCCEEDED(CreateStreamOnFile(sFile, &(This->lpFileStream)))
-       {
-         if SUCCEEDED (IPersistStream_Load(StreamThis, This->lpFileStream))
-         {
-           return NOERROR;
-         }
-       }
 
-       return hRet;
+static inline IShellLinkImpl *impl_from_IObjectWithSite( IObjectWithSite *iface )
+{
+    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblObjectWithSite));
 }
 
+static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath);
 
-/* Icon extraction routines
- *
- * FIXME: should use PrivateExtractIcons and friends
- * FIXME: should not use stdio
- */
-
-static BOOL SaveIconResAsXPM(const BITMAPINFO *pIcon, const char *szXPMFileName)
-{
-    FILE *fXPMFile;
-    int nHeight;
-    int nXORWidthBytes;
-    int nANDWidthBytes;
-    BOOL b8BitColors;
-    int nColors;
-    BYTE *pXOR;
-    BYTE *pAND;
-    BOOL aColorUsed[256] = {0};
-    int nColorsUsed = 0;
-    int i,j;
-
-    if (!((pIcon->bmiHeader.biBitCount == 4) || (pIcon->bmiHeader.biBitCount == 8)))
-        return 0;
-
-    if (!(fXPMFile = fopen(szXPMFileName, "w")))
-        return 0;
-
-    nHeight = pIcon->bmiHeader.biHeight / 2;
-    nXORWidthBytes = 4 * ((pIcon->bmiHeader.biWidth * pIcon->bmiHeader.biBitCount / 32)
-                          + ((pIcon->bmiHeader.biWidth * pIcon->bmiHeader.biBitCount % 32) > 0));
-    nANDWidthBytes = 4 * ((pIcon->bmiHeader.biWidth / 32)
-                          + ((pIcon->bmiHeader.biWidth % 32) > 0));
-    b8BitColors = pIcon->bmiHeader.biBitCount == 8;
-    nColors = pIcon->bmiHeader.biClrUsed ? pIcon->bmiHeader.biClrUsed
-        : 1 << pIcon->bmiHeader.biBitCount;
-    pXOR = (BYTE*) pIcon + sizeof (BITMAPINFOHEADER) + (nColors * sizeof (RGBQUAD));
-    pAND = pXOR + nHeight * nXORWidthBytes;
-
-#define MASK(x,y) (pAND[(x) / 8 + (nHeight - (y) - 1) * nANDWidthBytes] & (1 << (7 - (x) % 8)))
-#define COLOR(x,y) (b8BitColors ? pXOR[(x) + (nHeight - (y) - 1) * nXORWidthBytes] : (x) % 2 ? pXOR[(x) / 2 + (nHeight - (y) - 1) * nXORWidthBytes] & 0xF : (pXOR[(x) / 2 + (nHeight - (y) - 1) * nXORWidthBytes] & 0xF0) >> 4)
-
-    for (i = 0; i < nHeight; i++)
-        for (j = 0; j < pIcon->bmiHeader.biWidth; j++)
-            if (!aColorUsed[COLOR(j,i)] && !MASK(j,i))
-            {
-                aColorUsed[COLOR(j,i)] = TRUE;
-                nColorsUsed++;
-            }
-
-    if (fprintf(fXPMFile, "/* XPM */\nstatic char *icon[] = {\n") <= 0)
-        goto error;
-    if (fprintf(fXPMFile, "\"%d %d %d %d\",\n",
-                (int) pIcon->bmiHeader.biWidth, nHeight, nColorsUsed + 1, 2) <=0)
-        goto error;
-
-    for (i = 0; i < nColors; i++)
-        if (aColorUsed[i])
-            if (fprintf(fXPMFile, "\"%.2X c #%.2X%.2X%.2X\",\n", i, pIcon->bmiColors[i].rgbRed,
-                        pIcon->bmiColors[i].rgbGreen, pIcon->bmiColors[i].rgbBlue) <= 0)
-                goto error;
-    if (fprintf(fXPMFile, "\"   c None\"") <= 0)
-        goto error;
-
-    for (i = 0; i < nHeight; i++)
-    {
-        if (fprintf(fXPMFile, ",\n\"") <= 0)
-            goto error;
-        for (j = 0; j < pIcon->bmiHeader.biWidth; j++)
-        {
-            if MASK(j,i)
-                {
-                    if (fprintf(fXPMFile, "  ") <= 0)
-                        goto error;
-                }
-            else
-                if (fprintf(fXPMFile, "%.2X", COLOR(j,i)) <= 0)
-                    goto error;
-        }
-        if (fprintf(fXPMFile, "\"") <= 0)
-            goto error;
-    }
-    if (fprintf(fXPMFile, "};\n") <= 0)
-        goto error;
-
-#undef MASK
-#undef COLOR
-
-    fclose(fXPMFile);
-    return 1;
-
- error:
-    fclose(fXPMFile);
-    unlink( szXPMFileName );
-    return 0;
+/* strdup on the process heap */
+static inline LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
+{
+    INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
+    LPWSTR p = HeapAlloc( heap, flags, len*sizeof (WCHAR) );
+    if( !p )
+        return p;
+    MultiByteToWideChar( CP_ACP, 0, str, -1, p, len );
+    return p;
 }
 
-static BOOL CALLBACK EnumResNameProc(HANDLE hModule, const char *lpszType, char *lpszName, LONG lParam)
+static inline LPWSTR strdupW( LPCWSTR src )
 {
-    ENUMRESSTRUCT *sEnumRes = (ENUMRESSTRUCT *) lParam;
-
-    if (!sEnumRes->nIndex--)
-    {
-      *sEnumRes->pResInfo = FindResourceA(hModule, lpszName, RT_GROUP_ICONA);
-      return FALSE;
-    }
-    else
-      return TRUE;
+    LPWSTR dest;
+    if (!src) return NULL;
+    dest = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(src)+1)*sizeof(WCHAR) );
+    if (dest)
+        lstrcpyW(dest, src);
+    return dest;
 }
 
-static int ExtractFromEXEDLL(const char *szFileName, int nIndex, const char *szXPMFileName)
+/**************************************************************************
+ *  ShellLink::QueryInterface implementation
+ */
+static HRESULT ShellLink_QueryInterface( IShellLinkImpl *This, REFIID riid,  LPVOID *ppvObj)
 {
-    HMODULE hModule;
-    HRSRC hResInfo;
-    char *lpName = NULL;
-    HGLOBAL hResData;
-    GRPICONDIR *pIconDir;
-    BITMAPINFO *pIcon;
-    ENUMRESSTRUCT sEnumRes;
-    int nMax = 0;
-    int nMaxBits = 0;
-    int i;
+    TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
 
-    if (!(hModule = LoadLibraryExA(szFileName, 0, LOAD_LIBRARY_AS_DATAFILE)))
-    {
-        TRACE("LoadLibraryExA (%s) failed, error %ld\n", szFileName, GetLastError());
-        goto error1;
-    }
+    *ppvObj = NULL;
 
-    if (nIndex < 0)
+    if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IShellLinkA))
     {
-        hResInfo = FindResourceA(hModule, MAKEINTRESOURCEA(-nIndex), RT_GROUP_ICONA);
-        TRACE("FindResourceA (%s) called, return 0x%x, error %ld\n", szFileName, hResInfo, GetLastError());
+        *ppvObj = This;
     }
-    else
+    else if(IsEqualIID(riid, &IID_IShellLinkW))
     {
-        sEnumRes.pResInfo = &hResInfo;
-        sEnumRes.nIndex = nIndex;
-        if (EnumResourceNamesA(hModule, RT_GROUP_ICONA, &EnumResNameProc, (LONG) &sEnumRes))
-        {
-            TRACE("EnumResourceNamesA failed, error %ld\n", GetLastError());
-            goto error2;
-        }
+        *ppvObj = &(This->lpvtblw);
     }
-
-    if (!hResInfo)
+    else if(IsEqualIID(riid, &IID_IPersistFile))
     {
-        TRACE("ExtractFromEXEDLL failed, error %ld\n", GetLastError());
-        goto error2;
+        *ppvObj = &(This->lpvtblPersistFile);
     }
-
-    if (!(hResData = LoadResource(hModule, hResInfo)))
+    else if(IsEqualIID(riid, &IID_IPersistStream))
     {
-        TRACE("LoadResource failed, error %ld\n", GetLastError());
-        goto error2;
+        *ppvObj = &(This->lpvtblPersistStream);
     }
-    if (!(pIconDir = LockResource(hResData)))
+    else if(IsEqualIID(riid, &IID_IShellLinkDataList))
     {
-        TRACE("LockResource failed, error %ld\n", GetLastError());
-        goto error3;
+        *ppvObj = &(This->lpvtblShellLinkDataList);
     }
-
-    for (i = 0; i < pIconDir->idCount; i++)
-        if ((pIconDir->idEntries[i].wBitCount >= nMaxBits) && (pIconDir->idEntries[i].wBitCount <= 8))
-        {
-          if (pIconDir->idEntries[i].wBitCount > nMaxBits)
-          {
-              nMaxBits = pIconDir->idEntries[i].wBitCount;
-              nMax = 0;
-          }
-          if ((pIconDir->idEntries[i].bHeight * pIconDir->idEntries[i].bWidth) > nMax)
-          {
-              lpName = MAKEINTRESOURCEA(pIconDir->idEntries[i].nID);
-              nMax = pIconDir->idEntries[i].bHeight * pIconDir->idEntries[i].bWidth;
-          }
-        }
-
-    FreeResource(hResData);
-
-    if (!(hResInfo = FindResourceA(hModule, lpName, RT_ICONA)))
+    else if(IsEqualIID(riid, &IID_IShellExtInit))
     {
-        TRACE("Second FindResourceA failed, error %ld\n", GetLastError());
-        goto error2;
+        *ppvObj = &(This->lpvtblShellExtInit);
     }
-    if (!(hResData = LoadResource(hModule, hResInfo)))
+    else if(IsEqualIID(riid, &IID_IContextMenu))
     {
-        TRACE("Second LoadResource failed, error %ld\n", GetLastError());
-        goto error2;
+        *ppvObj = &(This->lpvtblContextMenu);
     }
-    if (!(pIcon = LockResource(hResData)))
+    else if(IsEqualIID(riid, &IID_IObjectWithSite))
     {
-        TRACE("Second LockResource failed, error %ld\n", GetLastError());
-        goto error3;
+        *ppvObj = &(This->lpvtblObjectWithSite);
     }
 
-    if(!SaveIconResAsXPM(pIcon, szXPMFileName))
+    if(*ppvObj)
     {
-        TRACE("Failed saving icon as XPM, error %ld\n", GetLastError());
-        goto error3;
+        IUnknown_AddRef((IUnknown*)(*ppvObj));
+        TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
+        return S_OK;
     }
+    ERR("-- Interface: E_NOINTERFACE\n");
+    return E_NOINTERFACE;
+}
 
-    FreeResource(hResData);
-    FreeLibrary(hModule);
+/**************************************************************************
+ *  ShellLink::AddRef implementation
+ */
+static ULONG ShellLink_AddRef( IShellLinkImpl *This )
+{
+    ULONG refCount = InterlockedIncrement(&This->ref);
 
-    return 1;
+    TRACE("(%p)->(count=%u)\n", This, refCount - 1);
 
- error3:
-    FreeResource(hResData);
- error2:
-    FreeLibrary(hModule);
- error1:
-    return 0;
+    return refCount;
 }
 
-static int ExtractFromICO(const char *szFileName, const char *szXPMFileName)
+/**************************************************************************
+ *  ShellLink::Release implementation
+ */
+static ULONG ShellLink_Release( IShellLinkImpl *This )
 {
-    FILE *fICOFile;
-    ICONDIR iconDir;
-    ICONDIRENTRY *pIconDirEntry;
-    int nMax = 0;
-    int nIndex = 0;
-    void *pIcon;
-    int i;
+    ULONG refCount = InterlockedDecrement(&This->ref);
 
-    if (!(fICOFile = fopen(szFileName, "r")))
-        goto error1;
+    TRACE("(%p)->(count=%u)\n", This, refCount + 1);
 
-    if (fread(&iconDir, sizeof (ICONDIR), 1, fICOFile) != 1)
-        goto error2;
-    if ((iconDir.idReserved != 0) || (iconDir.idType != 1))
-        goto error2;
+    if (refCount)
+        return refCount;
 
-    if ((pIconDirEntry = malloc(iconDir.idCount * sizeof (ICONDIRENTRY))) == NULL)
-        goto error2;
-    if (fread(pIconDirEntry, sizeof (ICONDIRENTRY), iconDir.idCount, fICOFile) != iconDir.idCount)
-        goto error3;
+    TRACE("-- destroying IShellLink(%p)\n",This);
+
+    HeapFree(GetProcessHeap(), 0, This->sIcoPath);
+    HeapFree(GetProcessHeap(), 0, This->sArgs);
+    HeapFree(GetProcessHeap(), 0, This->sWorkDir);
+    HeapFree(GetProcessHeap(), 0, This->sDescription);
+    HeapFree(GetProcessHeap(),0,This->sPath);
+
+    if (This->site)
+        IUnknown_Release( This->site );
+
+    if (This->pPidl)
+        ILFree(This->pPidl);
+
+    LocalFree((HANDLE)This);
 
-    for (i = 0; i < iconDir.idCount; i++)
-        if ((pIconDirEntry[i].bHeight * pIconDirEntry[i].bWidth) > nMax)
-        {
-            nIndex = i;
-            nMax = pIconDirEntry[i].bHeight * pIconDirEntry[i].bWidth;
-        }
-    if ((pIcon = malloc(pIconDirEntry[nIndex].dwBytesInRes)) == NULL)
-        goto error3;
-    if (fseek(fICOFile, pIconDirEntry[nIndex].dwImageOffset, SEEK_SET))
-        goto error4;
-    if (fread(pIcon, pIconDirEntry[nIndex].dwBytesInRes, 1, fICOFile) != 1)
-        goto error4;
-
-    if(!SaveIconResAsXPM(pIcon, szXPMFileName))
-        goto error4;
-
-    free(pIcon);
-    free(pIconDirEntry);
-    fclose(fICOFile);
-
-    return 1;
-
- error4:
-    free(pIcon);
- error3:
-    free(pIconDirEntry);
- error2:
-    fclose(fICOFile);
- error1:
     return 0;
 }
 
-/* get the Unix file name for a given path, allocating the string */
-inline static char *get_unix_file_name( const char *dos )
+static HRESULT ShellLink_GetClassID( IShellLinkImpl *This, CLSID *pclsid )
 {
-    char buffer[MAX_PATH];
+    TRACE("%p %p\n", This, pclsid);
 
-    if (!wine_get_unix_file_name( dos, buffer, sizeof(buffer) )) return NULL;
-    return heap_strdup( buffer );
+    memcpy( pclsid, &CLSID_ShellLink, sizeof (CLSID) );
+    return S_OK;
 }
 
-static BOOL create_default_icon( const char *filename )
+/**************************************************************************
+ *  IPersistFile_QueryInterface
+ */
+static HRESULT WINAPI IPersistFile_fnQueryInterface(
+       IPersistFile* iface,
+       REFIID riid,
+       LPVOID *ppvObj)
 {
-    FILE *fXPM;
-    int i;
+    IShellLinkImpl *This = impl_from_IPersistFile(iface);
+    return ShellLink_QueryInterface( This, riid, ppvObj );
+}
 
-    if (!(fXPM = fopen(filename, "w"))) return FALSE;
-    fprintf(fXPM, "/* XPM */\nstatic char * icon[] = {");
-    for (i = 0; i < sizeof(wine_xpm)/sizeof(wine_xpm[0]); i++)
-        fprintf( fXPM, "\n\"%s\",", wine_xpm[i]);
-    fprintf( fXPM, "};\n" );
-    fclose( fXPM );
-    return TRUE;
+/******************************************************************************
+ * IPersistFile_AddRef
+ */
+static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
+{
+    IShellLinkImpl *This = impl_from_IPersistFile(iface);
+    return ShellLink_AddRef( This );
 }
 
-/* extract an icon from an exe or icon file; helper for IPersistFile_fnSave */
-static char *extract_icon( const char *path, int index)
+/******************************************************************************
+ * IPersistFile_Release
+ */
+static ULONG WINAPI IPersistFile_fnRelease(IPersistFile* iface)
 {
-    int fd, nodefault = 1;
-    char *filename, tmpfn[25];
+    IShellLinkImpl *This = impl_from_IPersistFile(iface);
+    return IShellLinkA_Release((IShellLinkA*)This);
+}
 
-    strcpy(tmpfn,"/tmp/icon.XXXXXX");
-    fd = mkstemp( tmpfn );
-    if (fd == -1)
-        return NULL;
-    filename = heap_strdup( tmpfn );
-    close(fd); /* not needed */
+static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
+{
+    IShellLinkImpl *This = impl_from_IPersistFile(iface);
+    return ShellLink_GetClassID( This, pClassID );
+}
 
-    /* If icon path begins with a '*' then this is a deferred call */
-    if (path[0] == '*')
-    {
-        path++;
-        nodefault = 0;
-    }
-    if (ExtractFromEXEDLL( path, index, filename )) return filename;
-    if (ExtractFromICO( path, filename )) return filename;
-    if (!nodefault)
-        if (create_default_icon( filename )) return filename;
-    HeapFree( GetProcessHeap(), 0, filename );
-    return NULL;
+static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
+{
+       IShellLinkImpl *This = impl_from_IPersistFile(iface);
+
+       TRACE("(%p)\n",This);
+
+       if (This->bDirty)
+           return S_OK;
+
+       return S_FALSE;
 }
 
+static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
+{
+       IShellLinkImpl *This = impl_from_IPersistFile(iface);
+       IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
+        HRESULT r;
+        IStream *stm;
+
+        TRACE("(%p, %s, %x)\n",This, debugstr_w(pszFileName), dwMode);
 
-static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
+        if( dwMode == 0 )
+               dwMode = STGM_READ | STGM_SHARE_DENY_WRITE;
+        r = SHCreateStreamOnFileW(pszFileName, dwMode, &stm);
+        if( SUCCEEDED( r ) )
+        {
+            r = IPersistStream_Load(StreamThis, stm);
+            ShellLink_UpdatePath(This->sPathRel, pszFileName, This->sWorkDir, &This->sPath);
+            IStream_Release( stm );
+            This->bDirty = FALSE;
+        }
+        TRACE("-- returning hr %08x\n", r);
+        return r;
+}
+
+static BOOL StartLinkProcessor( LPCOLESTR szLink )
 {
-    HRESULT ret = NOERROR;
-    int pid, status;
-    char buffer[MAX_PATH], buff2[MAX_PATH], ascii_filename[MAX_PATH];
-    char *filename, *link_name, *p;
-    char *shell_link_app = NULL;
-    char *icon_name = NULL;
-    char *work_dir = NULL;
-    BOOL bDesktop;
-    HKEY hkey;
+    static const WCHAR szFormat[] = {
+        'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
+        ' ','-','r',' ','"','%','s','"',0 };
+    LONG len;
+    LPWSTR buffer;
+    STARTUPINFOW si;
+    PROCESS_INFORMATION pi;
 
-    _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
+    len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
+    buffer = HeapAlloc( GetProcessHeap(), 0, len );
+    if( !buffer )
+        return FALSE;
 
-    TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
+    wsprintfW( buffer, szFormat, szLink );
 
-    if (!pszFileName || !This->sPath)
-        return ERROR_UNKNOWN;
+    TRACE("starting %s\n",debugstr_w(buffer));
 
-    /* check for .exe extension */
-    if (!(p = strrchr( This->sPath, '.' ))) return NOERROR;
-    if (strchr( p, '\\' ) || strchr( p, '/' )) return NOERROR;
-    if (strcasecmp( p, ".exe" )) return NOERROR;
+    memset(&si, 0, sizeof(si));
+    si.cb = sizeof(si);
+    if (!CreateProcessW( NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) return FALSE;
 
-    /* check if ShellLinker configured */
-    buffer[0] = 0;
-    if (!RegOpenKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\Wine",
-                        0, KEY_ALL_ACCESS, &hkey ))
-    {
-        DWORD type, count = sizeof(buffer);
-        if (RegQueryValueExA( hkey, "ShellLinker", 0, &type, buffer, &count )) buffer[0] = 0;
-        RegCloseKey( hkey );
-    }
-    if (!*buffer) return NOERROR;
-    shell_link_app = heap_strdup( buffer );
+    /* wait for a while to throttle the creation of linker processes */
+    if( WAIT_OBJECT_0 != WaitForSingleObject( pi.hProcess, 10000 ) )
+        WARN("Timed out waiting for shell linker\n");
 
-    if (!WideCharToMultiByte( CP_ACP, 0, pszFileName, -1, ascii_filename, sizeof(ascii_filename), NULL, NULL))
-        return ERROR_UNKNOWN;
-    GetFullPathNameA( ascii_filename, sizeof(buff2), buff2, NULL );
-    filename = heap_strdup( buff2 );
+    CloseHandle( pi.hProcess );
+    CloseHandle( pi.hThread );
 
-    if (SHGetSpecialFolderPathA( 0, buffer, CSIDL_STARTUP, FALSE ))
-    {
-        /* ignore startup for now */
-        if (!strncasecmp( filename, buffer, strlen(buffer) )) goto done;
-    }
-    if (SHGetSpecialFolderPathA( 0, buffer, CSIDL_DESKTOPDIRECTORY, FALSE ))
-    {
-        if (!strncasecmp( filename, buffer, strlen(buffer) ))
-        {
-            link_name = filename + strlen(buffer);
-            bDesktop = TRUE;
-            goto found;
-        }
-    }
-    if (SHGetSpecialFolderPathA( 0, buffer, CSIDL_STARTMENU, FALSE ))
-    {
-        if (!strncasecmp( filename, buffer, strlen(buffer) ))
-        {
-            link_name = filename + strlen(buffer);
-            bDesktop = FALSE;
-            goto found;
-        }
-    }
-    goto done;
-
- found:
-    /* make link name a Unix name */
-    for (p = link_name; *p; p++) if (*p == '\\') *p = '/';
-    /* strip leading slashes */
-    while (*link_name == '/') link_name++;
-    /* remove extension */
-    if ((p = strrchr( link_name, '.' ))) *p = 0;
-
-    /* convert app working dir */
-    if (This->sWorkDir) work_dir = get_unix_file_name( This->sWorkDir );
-
-    /* extract the icon */
-    if (!(icon_name = extract_icon( This->sIcoPath && strlen(This->sIcoPath) ?
-                                      This->sIcoPath : This->sPath,
-                                      This->iIcoNdx )))
-    {
-       /* Couldn't extract icon --  defer this menu entry to runonce. */
-       HKEY hRunOnce;
-       char* buffer = NULL;
+    return TRUE;
+}
 
-       TRACE("Deferring icon creation to reboot.\n");
-       if (RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", 0,
-                NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRunOnce, NULL) != ERROR_SUCCESS)
-       {
-           ret = ERROR_UNKNOWN;
-           goto done;
-       }
-       buffer = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * 3 + (This->sArgs ? strlen(This->sArgs) : 0) +
-                           (This->sDescription ? strlen(This->sDescription) : 0) + 200);
-       sprintf(buffer, "link:%s\xff*%s\xff%d\xff%s\xff%s\xff%s", This->sPath, This->sIcoPath, This->iIcoNdx,
-           This->sArgs ? This->sArgs : "", This->sDescription ? This->sDescription : "",
-           This->sWorkDir ? This->sWorkDir : "");
-       if (RegSetValueExA(hRunOnce, ascii_filename, 0, REG_SZ, buffer, strlen(buffer) + 1) != ERROR_SUCCESS)
-       {
-           HeapFree(GetProcessHeap(), 0, buffer);
-           RegCloseKey(hRunOnce);
-           ret = ERROR_UNKNOWN;
-           goto done;
-       }
-       HeapFree(GetProcessHeap(), 0, buffer);
-       RegCloseKey(hRunOnce);
-       goto done;
-    }
+static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
+{
+    IShellLinkImpl *This = impl_from_IPersistFile(iface);
+    IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
+    HRESULT r;
+    IStream *stm;
+
+    TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
 
-    TRACE("linker app='%s' link='%s' mode=%s path='%s' args='%s' icon='%s' workdir='%s' descr='%s'\n",
-        shell_link_app, link_name, bDesktop ? "desktop" : "menu", This->sPath,
-        This->sArgs ? This->sArgs : "", icon_name, work_dir ? work_dir : "",
-        This->sDescription ? This->sDescription : "" );
+    if (!pszFileName)
+        return E_FAIL;
 
-    if ((pid = fork()) == -1) goto done;
-    if (!pid)
+    r = SHCreateStreamOnFileW( pszFileName, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &stm );
+    if( SUCCEEDED( r ) )
     {
-        int pos = 0;
-        char *argv[20];
-        argv[pos++] = shell_link_app;
-        argv[pos++] = "--link";
-        argv[pos++] = link_name;
-        argv[pos++] = "--path";
-        argv[pos++] = This->sPath;
-        argv[pos++] = bDesktop ? "--desktop" : "--menu";
-        if (This->sArgs && strlen(This->sArgs))
-        {
-            argv[pos++] = "--args";
-            argv[pos++] = This->sArgs;
-        }
-        if (icon_name)
-        {
-            argv[pos++] = "--icon";
-            argv[pos++] = icon_name;
-        }
-        if (This->sWorkDir && strlen(This->sWorkDir))
-        {
-            argv[pos++] = "--workdir";
-            argv[pos++] = This->sWorkDir;
+        r = IPersistStream_Save(StreamThis, stm, FALSE);
+        IStream_Release( stm );
+
+        if( SUCCEEDED( r ) )
+       {
+            StartLinkProcessor( pszFileName );
+
+            This->bDirty = FALSE;
         }
-        if (This->sDescription && strlen(This->sDescription))
+       else
         {
-            argv[pos++] = "--descr";
-            argv[pos++] = This->sDescription;
+            DeleteFileW( pszFileName );
+            WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
         }
-        argv[pos] = NULL;
-        execvp( shell_link_app, argv );
-        _exit(1);
     }
 
-    while (waitpid( pid, &status, 0 ) == -1)
-    {
-        if (errno != EINTR)
-        {
-            ret = ERROR_UNKNOWN;
-            goto done;
-        }
-    }
-    if (status) ret = E_ACCESSDENIED;
-
- done:
-    if (icon_name) unlink( icon_name );
-    HeapFree( GetProcessHeap(), 0, shell_link_app );
-    HeapFree( GetProcessHeap(), 0, filename );
-    HeapFree( GetProcessHeap(), 0, icon_name );
-    HeapFree( GetProcessHeap(), 0, work_dir );
-    return ret;
+    return r;
 }
 
 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
 {
-       _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
+       IShellLinkImpl *This = impl_from_IPersistFile(iface);
        FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
        return NOERROR;
 }
+
 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *ppszFileName)
 {
-       _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
+       IShellLinkImpl *This = impl_from_IPersistFile(iface);
        FIXME("(%p)\n",This);
        return NOERROR;
 }
 
-static ICOM_VTABLE(IPersistFile) pfvt =
+static const IPersistFileVtbl pfvt =
 {
-       ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
        IPersistFile_fnQueryInterface,
        IPersistFile_fnAddRef,
        IPersistFile_fnRelease,
@@ -782,13 +495,10 @@ static ICOM_VTABLE(IPersistFile) pfvt =
 static HRESULT WINAPI IPersistStream_fnQueryInterface(
        IPersistStream* iface,
        REFIID     riid,
-       VOID**     ppvoid)
+       VOID**     ppvObj)
 {
-       _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
-
-       TRACE("(%p)\n",This);
-
-       return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvoid);
+    IShellLinkImpl *This = impl_from_IPersistStream(iface);
+    return ShellLink_QueryInterface( This, riid, ppvObj );
 }
 
 /************************************************************************
@@ -797,11 +507,8 @@ static HRESULT WINAPI IPersistStream_fnQueryInterface(
 static ULONG WINAPI IPersistStream_fnRelease(
        IPersistStream* iface)
 {
-       _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
-
-       TRACE("(%p)\n",This);
-
-       return IShellLinkA_Release((IShellLinkA*)This);
+    IShellLinkImpl *This = impl_from_IPersistStream(iface);
+    return IShellLinkA_Release((IShellLinkA*)This);
 }
 
 /************************************************************************
@@ -810,11 +517,8 @@ static ULONG WINAPI IPersistStream_fnRelease(
 static ULONG WINAPI IPersistStream_fnAddRef(
        IPersistStream* iface)
 {
-       _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
-
-       TRACE("(%p)\n",This);
-
-       return IShellLinkA_AddRef((IShellLinkA*)This);
+    IShellLinkImpl *This = impl_from_IPersistStream(iface);
+    return ShellLink_AddRef( This );
 }
 
 /************************************************************************
@@ -825,16 +529,8 @@ static HRESULT WINAPI IPersistStream_fnGetClassID(
        IPersistStream* iface,
        CLSID* pClassID)
 {
-       _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
-
-       TRACE("(%p)\n", This);
-
-       if (pClassID==0)
-         return E_POINTER;
-
-/*     memcpy(pClassID, &CLSID_???, sizeof(CLSID_???)); */
-
-       return S_OK;
+    IShellLinkImpl *This = impl_from_IPersistStream(iface);
+    return ShellLink_GetClassID( This, pClassID );
 }
 
 /************************************************************************
@@ -843,124 +539,643 @@ static HRESULT WINAPI IPersistStream_fnGetClassID(
 static HRESULT WINAPI IPersistStream_fnIsDirty(
        IPersistStream*  iface)
 {
-       _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
+       IShellLinkImpl *This = impl_from_IPersistStream(iface);
 
        TRACE("(%p)\n", This);
 
        return S_OK;
 }
-/************************************************************************
- * IPersistStream_Load (IPersistStream)
- */
 
-static HRESULT WINAPI IPersistStream_fnLoad(
-       IPersistStream*  iface,
-       IStream*         pLoadStream)
+
+static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
 {
-       PLINK_HEADER lpLinkHeader = HeapAlloc(GetProcessHeap(), 0, LINK_HEADER_SIZE);
-       ULONG   dwBytesRead;
-       DWORD   ret = E_FAIL;
-       char    sTemp[MAX_PATH];
+    DWORD count;
+    USHORT len;
+    LPVOID temp;
+    LPWSTR str;
+    HRESULT r;
 
-       _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
+    TRACE("%p\n", stm);
 
-       TRACE("(%p)(%p)\n", This, pLoadStream);
+    count = 0;
+    r = IStream_Read(stm, &len, sizeof(len), &count);
+    if ( FAILED (r) || ( count != sizeof(len) ) )
+        return E_FAIL;
 
-       if ( ! pLoadStream)
-       {
-         return STG_E_INVALIDPOINTER;
-       }
+    if( unicode )
+        len *= sizeof (WCHAR);
+
+    TRACE("reading %d\n", len);
+    temp = HeapAlloc(GetProcessHeap(), 0, len+sizeof(WCHAR));
+    if( !temp )
+        return E_OUTOFMEMORY;
+    count = 0;
+    r = IStream_Read(stm, temp, len, &count);
+    if( FAILED (r) || ( count != len ) )
+    {
+        HeapFree( GetProcessHeap(), 0, temp );
+        return E_FAIL;
+    }
+
+    TRACE("read %s\n", debugstr_an(temp,len));
+
+    /* convert to unicode if necessary */
+    if( !unicode )
+    {
+        count = MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, NULL, 0 );
+        str = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof (WCHAR) );
+        if( !str )
+        {
+            HeapFree( GetProcessHeap(), 0, temp );
+            return E_OUTOFMEMORY;
+        }
+        MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, str, count );
+        HeapFree( GetProcessHeap(), 0, temp );
+    }
+    else
+    {
+        count /= 2;
+        str = (LPWSTR) temp;
+    }
+    str[count] = 0;
 
-       IStream_AddRef (pLoadStream);
-       if(!lpLinkHeader)
-          goto end;
+    *pstr = str;
 
-        dwBytesRead = 0;
-       if (!(SUCCEEDED(IStream_Read(pLoadStream, lpLinkHeader, LINK_HEADER_SIZE, &dwBytesRead))))
-          goto end;
+    return S_OK;
+}
 
-       if (dwBytesRead != LINK_HEADER_SIZE)
-          goto end;
+static HRESULT Stream_ReadChunk( IStream* stm, LPVOID *data )
+{
+    DWORD size;
+    ULONG count;
+    HRESULT r;
+    struct sized_chunk {
+        DWORD size;
+        unsigned char data[1];
+    } *chunk;
 
-       if ( (lpLinkHeader->MagicStr != 0x0000004CL) || !IsEqualIID(&lpLinkHeader->MagicGuid, &CLSID_ShellLink) )
-          goto end;
+    TRACE("%p\n",stm);
 
-       if(lpLinkHeader->PidlSize)
-       {
-         lpLinkHeader = HeapReAlloc(GetProcessHeap(), 0, lpLinkHeader, LINK_HEADER_SIZE+lpLinkHeader->PidlSize);
-         if (!lpLinkHeader)
-            goto end;
-          dwBytesRead = 0;
-         if (!(SUCCEEDED(IStream_Read(pLoadStream, &(lpLinkHeader->Pidl), lpLinkHeader->PidlSize, &dwBytesRead))))
-            goto end;
-          if(dwBytesRead != lpLinkHeader->PidlSize)
-            goto end;
-
-          if (pcheck (&lpLinkHeader->Pidl))
-          {
-            This->pPidl = ILClone (&lpLinkHeader->Pidl);
-
-            SHGetPathFromIDListA(&lpLinkHeader->Pidl, sTemp);
-            This->sPath = heap_strdup( sTemp );
-          }
-        }
-       This->wHotKey = lpLinkHeader->wHotKey;
-       FileTimeToSystemTime (&lpLinkHeader->Time1, &This->time1);
-       FileTimeToSystemTime (&lpLinkHeader->Time2, &This->time2);
-       FileTimeToSystemTime (&lpLinkHeader->Time3, &This->time3);
-#if 1
-       GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time1, NULL, sTemp, 256);
-       TRACE("-- time1: %s\n", sTemp);
-       GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time2, NULL, sTemp, 256);
-       TRACE("-- time1: %s\n", sTemp);
-       GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time3, NULL, sTemp, 256);
-       TRACE("-- time1: %s\n", sTemp);
-       pdump (This->pPidl);
-#endif
-       ret = S_OK;
+    r = IStream_Read( stm, &size, sizeof(size), &count );
+    if( FAILED( r )  || count != sizeof(size) )
+        return E_FAIL;
 
-end:
-       IStream_Release (pLoadStream);
+    chunk = HeapAlloc( GetProcessHeap(), 0, size );
+    if( !chunk )
+        return E_OUTOFMEMORY;
+
+    chunk->size = size;
+    r = IStream_Read( stm, chunk->data, size - sizeof(size), &count );
+    if( FAILED( r ) || count != (size - sizeof(size)) )
+    {
+        HeapFree( GetProcessHeap(), 0, chunk );
+        return E_FAIL;
+    }
 
-       pdump(This->pPidl);
+    TRACE("Read %d bytes\n",chunk->size);
 
-       HeapFree(GetProcessHeap(), 0, lpLinkHeader);
+    *data = (LPVOID) chunk;
 
-       return ret;
+    return S_OK;
 }
 
-/************************************************************************
- * IPersistStream_Save (IPersistStream)
- */
-static HRESULT WINAPI IPersistStream_fnSave(
-       IPersistStream*  iface,
-       IStream*         pOutStream,
-       BOOL             fClearDirty)
+static BOOL Stream_LoadVolume( LOCAL_VOLUME_INFO *vol, volume_info *volume )
 {
-       _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
+    const int label_sz = sizeof volume->label/sizeof volume->label[0];
+    LPSTR label;
+    int len;
 
-       TRACE("(%p) %p %x\n", This, pOutStream, fClearDirty);
+    volume->serial = vol->dwVolSerial;
+    volume->type = vol->dwType;
 
-       return E_NOTIMPL;
+    if( !vol->dwVolLabelOfs )
+        return FALSE;
+    if( vol->dwSize <= vol->dwVolLabelOfs )
+        return FALSE;
+    len = vol->dwSize - vol->dwVolLabelOfs;
+
+    label = (LPSTR) vol;
+    label += vol->dwVolLabelOfs;
+    MultiByteToWideChar( CP_ACP, 0, label, len, volume->label, label_sz-1);
+
+    return TRUE;
 }
 
-/************************************************************************
- * IPersistStream_GetSizeMax (IPersistStream)
- */
-static HRESULT WINAPI IPersistStream_fnGetSizeMax(
-       IPersistStream*  iface,
-       ULARGE_INTEGER*  pcbSize)
+static LPWSTR Stream_LoadPath( LPSTR p, DWORD maxlen )
 {
-       _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
+    int len = 0, wlen;
+    LPWSTR path;
 
-       TRACE("(%p)\n", This);
+    while( p[len] && (len < maxlen) )
+        len++;
 
-       return E_NOTIMPL;
+    wlen = MultiByteToWideChar(CP_ACP, 0, p, len, NULL, 0);
+    path = HeapAlloc(GetProcessHeap(), 0, (wlen+1)*sizeof(WCHAR));
+    MultiByteToWideChar(CP_ACP, 0, p, len, path, wlen);
+    path[wlen] = 0;
+
+    return path;
 }
 
-static ICOM_VTABLE(IPersistStream) psvt =
+static HRESULT Stream_LoadLocation( IStream *stm,
+                volume_info *volume, LPWSTR *path )
+{
+    char *p = NULL;
+    LOCATION_INFO *loc;
+    HRESULT r;
+    int n;
+
+    r = Stream_ReadChunk( stm, (LPVOID*) &p );
+    if( FAILED(r) )
+        return r;
+
+    loc = (LOCATION_INFO*) p;
+    if (loc->dwTotalSize < sizeof(LOCATION_INFO))
+    {
+        HeapFree( GetProcessHeap(), 0, p );
+        return E_FAIL;
+    }
+
+    /* if there's valid local volume information, load it */
+    if( loc->dwVolTableOfs && 
+       ((loc->dwVolTableOfs + sizeof(LOCAL_VOLUME_INFO)) <= loc->dwTotalSize) )
+    {
+        LOCAL_VOLUME_INFO *volume_info;
+
+        volume_info = (LOCAL_VOLUME_INFO*) &p[loc->dwVolTableOfs];
+        Stream_LoadVolume( volume_info, volume );
+    }
+
+    /* if there's a local path, load it */
+    n = loc->dwLocalPathOfs;
+    if( n && (n < loc->dwTotalSize) )
+        *path = Stream_LoadPath( &p[n], loc->dwTotalSize - n );
+
+    TRACE("type %d serial %08x name %s path %s\n", volume->type,
+          volume->serial, debugstr_w(volume->label), debugstr_w(*path));
+
+    HeapFree( GetProcessHeap(), 0, p );
+    return S_OK;
+}
+
+/*
+ *  The format of the advertised shortcut info seems to be:
+ *
+ *  Offset     Description
+ *  ------     -----------
+ *
+ *    0          Length of the block (4 bytes, usually 0x314)
+ *    4          tag (dword)
+ *    8          string data in ASCII
+ *    8+0x104    string data in UNICODE
+ *
+ * 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 )
+{
+    DWORD size;
+    ULONG count;
+    HRESULT r;
+    EXP_DARWIN_LINK buffer;
+    
+    TRACE("%p\n",stm);
+
+    r = IStream_Read( stm, &buffer.dbh.cbSize, sizeof (DWORD), &count );
+    if( FAILED( r ) )
+        return r;
+
+    /* make sure that we read the size of the structure even on error */
+    size = sizeof buffer - sizeof (DWORD);
+    if( buffer.dbh.cbSize != sizeof buffer )
+    {
+        ERR("Ooops.  This structure is not as expected...\n");
+        return E_FAIL;
+    }
+
+    r = IStream_Read( stm, &buffer.dbh.dwSignature, size, &count );
+    if( FAILED( r ) )
+        return r;
+
+    if( count != size )
+        return E_FAIL;
+
+    TRACE("magic %08x  string = %s\n", buffer.dbh.dwSignature, debugstr_w(buffer.szwDarwinID));
+
+    if( (buffer.dbh.dwSignature&0xffff0000) != 0xa0000000 )
+    {
+        ERR("Unknown magic number %08x in advertised shortcut\n", buffer.dbh.dwSignature);
+        return E_FAIL;
+    }
+
+    *str = HeapAlloc( GetProcessHeap(), 0, 
+                     (lstrlenW(buffer.szwDarwinID)+1) * sizeof(WCHAR) );
+    lstrcpyW( *str, buffer.szwDarwinID );
+
+    return S_OK;
+}
+
+/************************************************************************
+ * IPersistStream_Load (IPersistStream)
+ */
+static HRESULT WINAPI IPersistStream_fnLoad(
+    IPersistStream*  iface,
+    IStream*         stm)
+{
+    LINK_HEADER hdr;
+    ULONG    dwBytesRead;
+    BOOL     unicode;
+    HRESULT  r;
+    DWORD    zero;
+
+    IShellLinkImpl *This = impl_from_IPersistStream(iface);
+
+    TRACE("%p %p\n", This, stm);
+
+    if( !stm )
+        return STG_E_INVALIDPOINTER;
+
+    dwBytesRead = 0;
+    r = IStream_Read(stm, &hdr, sizeof(hdr), &dwBytesRead);
+    if( FAILED( r ) )
+        return r;
+
+    if( dwBytesRead != sizeof(hdr))
+        return E_FAIL;
+    if( hdr.dwSize != sizeof(hdr))
+        return E_FAIL;
+    if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
+        return E_FAIL;
+
+    /* free all the old stuff */
+    ILFree(This->pPidl);
+    This->pPidl = NULL;
+    memset( &This->volume, 0, sizeof This->volume );
+    HeapFree(GetProcessHeap(), 0, This->sPath);
+    This->sPath = NULL;
+    HeapFree(GetProcessHeap(), 0, This->sDescription);
+    This->sDescription = NULL;
+    HeapFree(GetProcessHeap(), 0, This->sPathRel);
+    This->sPathRel = NULL;
+    HeapFree(GetProcessHeap(), 0, This->sWorkDir);
+    This->sWorkDir = NULL;
+    HeapFree(GetProcessHeap(), 0, This->sArgs);
+    This->sArgs = NULL;
+    HeapFree(GetProcessHeap(), 0, This->sIcoPath);
+    This->sIcoPath = NULL;
+    HeapFree(GetProcessHeap(), 0, This->sProduct);
+    This->sProduct = NULL;
+    HeapFree(GetProcessHeap(), 0, This->sComponent);
+    This->sComponent = NULL;
+        
+    This->wHotKey = (WORD)hdr.wHotKey;
+    This->iIcoNdx = hdr.nIcon;
+    FileTimeToSystemTime (&hdr.Time1, &This->time1);
+    FileTimeToSystemTime (&hdr.Time2, &This->time2);
+    FileTimeToSystemTime (&hdr.Time3, &This->time3);
+    if (TRACE_ON(shell))
+    {
+        WCHAR sTemp[MAX_PATH];
+        GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time1,
+                       NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
+        TRACE("-- time1: %s\n", debugstr_w(sTemp) );
+        GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time2,
+                       NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
+        TRACE("-- time2: %s\n", debugstr_w(sTemp) );
+        GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time3,
+                       NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
+        TRACE("-- time3: %s\n", debugstr_w(sTemp) );
+    }
+
+    /* load all the new stuff */
+    if( hdr.dwFlags & SLDF_HAS_ID_LIST )
+    {
+        r = ILLoadFromStream( stm, &This->pPidl );
+        if( FAILED( r ) )
+            return r;
+    }
+    pdump(This->pPidl);
+
+    /* load the location information */
+    if( hdr.dwFlags & SLDF_HAS_LINK_INFO )
+        r = Stream_LoadLocation( stm, &This->volume, &This->sPath );
+    if( FAILED( r ) )
+        goto end;
+
+    unicode = hdr.dwFlags & SLDF_UNICODE;
+    if( hdr.dwFlags & SLDF_HAS_NAME )
+    {
+        r = Stream_LoadString( stm, unicode, &This->sDescription );
+        TRACE("Description  -> %s\n",debugstr_w(This->sDescription));
+    }
+    if( FAILED( r ) )
+        goto end;
+
+    if( hdr.dwFlags & SLDF_HAS_RELPATH )
+    {
+        r = Stream_LoadString( stm, unicode, &This->sPathRel );
+        TRACE("Relative Path-> %s\n",debugstr_w(This->sPathRel));
+    }
+    if( FAILED( r ) )
+        goto end;
+
+    if( hdr.dwFlags & SLDF_HAS_WORKINGDIR )
+    {
+        r = Stream_LoadString( stm, unicode, &This->sWorkDir );
+        TRACE("Working Dir  -> %s\n",debugstr_w(This->sWorkDir));
+    }
+    if( FAILED( r ) )
+        goto end;
+
+    if( hdr.dwFlags & SLDF_HAS_ARGS )
+    {
+        r = Stream_LoadString( stm, unicode, &This->sArgs );
+        TRACE("Working Dir  -> %s\n",debugstr_w(This->sArgs));
+    }
+    if( FAILED( r ) )
+        goto end;
+
+    if( hdr.dwFlags & SLDF_HAS_ICONLOCATION )
+    {
+        r = Stream_LoadString( stm, unicode, &This->sIcoPath );
+        TRACE("Icon file    -> %s\n",debugstr_w(This->sIcoPath));
+    }
+    if( FAILED( r ) )
+        goto end;
+
+    if( hdr.dwFlags & SLDF_HAS_LOGO3ID )
+    {
+        r = Stream_LoadAdvertiseInfo( stm, &This->sProduct );
+        TRACE("Product      -> %s\n",debugstr_w(This->sProduct));
+    }
+    if( FAILED( r ) )
+        goto end;
+
+    if( hdr.dwFlags & SLDF_HAS_DARWINID )
+    {
+        r = Stream_LoadAdvertiseInfo( stm, &This->sComponent );
+        TRACE("Component    -> %s\n",debugstr_w(This->sComponent));
+    }
+    if( FAILED( r ) )
+        goto end;
+
+    r = IStream_Read(stm, &zero, sizeof zero, &dwBytesRead);
+    if( FAILED( r ) || zero || dwBytesRead != sizeof zero )
+        ERR("Last word was not zero\n");
+
+    TRACE("OK\n");
+
+    pdump (This->pPidl);
+
+    return S_OK;
+end:
+    return r;
+}
+
+/************************************************************************
+ * Stream_WriteString
+ *
+ * Helper function for IPersistStream_Save. Writes a unicode string 
+ *  with terminating nul byte to a stream, preceded by the its length.
+ */
+static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
+{
+    USHORT len = lstrlenW( str ) + 1;
+    DWORD count;
+    HRESULT r;
+
+    r = IStream_Write( stm, &len, sizeof(len), &count );
+    if( FAILED( r ) )
+        return r;
+
+    len *= sizeof(WCHAR);
+
+    r = IStream_Write( stm, str, len, &count );
+    if( FAILED( r ) )
+        return r;
+
+    return S_OK;
+}
+
+/************************************************************************
+ * Stream_WriteLocationInfo
+ *
+ * Writes the location info to a stream
+ *
+ * FIXME: One day we might want to write the network volume information
+ *        and the final path.
+ *        Figure out how Windows deals with unicode paths here.
+ */
+static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR path,
+                                         volume_info *volume )
+{
+    DWORD total_size, path_size, volume_info_size, label_size, final_path_size;
+    LOCAL_VOLUME_INFO *vol;
+    LOCATION_INFO *loc;
+    LPSTR szLabel, szPath, szFinalPath;
+    ULONG count = 0;
+
+    TRACE("%p %s %p\n", stm, debugstr_w(path), volume);
+
+    /* figure out the size of everything */
+    label_size = WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
+                                      NULL, 0, NULL, NULL );
+    path_size = WideCharToMultiByte( CP_ACP, 0, path, -1,
+                                     NULL, 0, NULL, NULL );
+    volume_info_size = sizeof *vol + label_size;
+    final_path_size = 1;
+    total_size = sizeof *loc + volume_info_size + path_size + final_path_size;
+
+    /* create pointers to everything */
+    loc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total_size);
+    vol = (LOCAL_VOLUME_INFO*) &loc[1];
+    szLabel = (LPSTR) &vol[1];
+    szPath = &szLabel[label_size];
+    szFinalPath = &szPath[path_size];
+
+    /* fill in the location information header */
+    loc->dwTotalSize = total_size;
+    loc->dwHeaderSize = sizeof (*loc);
+    loc->dwFlags = 1;
+    loc->dwVolTableOfs = sizeof (*loc);
+    loc->dwLocalPathOfs = sizeof (*loc) + volume_info_size;
+    loc->dwNetworkVolTableOfs = 0;
+    loc->dwFinalPathOfs = sizeof (*loc) + volume_info_size + path_size;
+
+    /* fill in the volume information */
+    vol->dwSize = volume_info_size;
+    vol->dwType = volume->type;
+    vol->dwVolSerial = volume->serial;
+    vol->dwVolLabelOfs = sizeof (*vol);
+
+    /* copy in the strings */
+    WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
+                         szLabel, label_size, NULL, NULL );
+    WideCharToMultiByte( CP_ACP, 0, path, -1,
+                         szPath, path_size, NULL, NULL );
+    szFinalPath[0] = 0;
+
+    return IStream_Write( stm, loc, total_size, &count );
+}
+
+static EXP_DARWIN_LINK* shelllink_build_darwinid( LPCWSTR string, DWORD magic )
+{
+    EXP_DARWIN_LINK *buffer;
+    
+    buffer = 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 )
+{
+    EXP_DARWIN_LINK *buffer;
+    ULONG count;
+    
+    TRACE("%p\n",stm);
+
+    buffer = shelllink_build_darwinid( string, magic );
+
+    return IStream_Write( stm, buffer, buffer->dbh.cbSize, &count );
+}
+
+/************************************************************************
+ * IPersistStream_Save (IPersistStream)
+ *
+ * FIXME: makes assumptions about byte order
+ */
+static HRESULT WINAPI IPersistStream_fnSave(
+       IPersistStream*  iface,
+       IStream*         stm,
+       BOOL             fClearDirty)
+{
+    static const WCHAR wOpen[] = {'o','p','e','n',0};
+
+    LINK_HEADER header;
+    WCHAR   exePath[MAX_PATH];
+    ULONG   count;
+    DWORD   zero;
+    HRESULT r;
+
+    IShellLinkImpl *This = impl_from_IPersistStream(iface);
+
+    TRACE("%p %p %x\n", This, stm, fClearDirty);
+
+    *exePath = '\0';
+
+    if (This->sPath)
+    {
+        SHELL_FindExecutable(NULL, This->sPath, wOpen, exePath, MAX_PATH,
+                             NULL, NULL, NULL, NULL);
+        /*
+         * windows can create lnk files to executables that do not exist yet
+         * so if the executable does not exist the just trust the path they
+         * gave us
+         */
+        if (!*exePath) lstrcpyW(exePath,This->sPath);
+    }
+
+    memset(&header, 0, sizeof(header));
+    header.dwSize = sizeof(header);
+    header.fStartup = This->iShowCmd;
+    memcpy(&header.MagicGuid, &CLSID_ShellLink, sizeof(header.MagicGuid) );
+
+    header.wHotKey = This->wHotKey;
+    header.nIcon = This->iIcoNdx;
+    header.dwFlags = SLDF_UNICODE;   /* strings are in unicode */
+    if( This->pPidl )
+        header.dwFlags |= SLDF_HAS_ID_LIST;
+    if( This->sPath )
+        header.dwFlags |= SLDF_HAS_LINK_INFO;
+    if( This->sDescription )
+        header.dwFlags |= SLDF_HAS_NAME;
+    if( This->sWorkDir )
+        header.dwFlags |= SLDF_HAS_WORKINGDIR;
+    if( This->sArgs )
+        header.dwFlags |= SLDF_HAS_ARGS;
+    if( This->sIcoPath )
+        header.dwFlags |= SLDF_HAS_ICONLOCATION;
+    if( This->sProduct )
+        header.dwFlags |= SLDF_HAS_LOGO3ID;
+    if( This->sComponent )
+        header.dwFlags |= SLDF_HAS_DARWINID;
+
+    SystemTimeToFileTime ( &This->time1, &header.Time1 );
+    SystemTimeToFileTime ( &This->time2, &header.Time2 );
+    SystemTimeToFileTime ( &This->time3, &header.Time3 );
+
+    /* write the Shortcut header */
+    r = IStream_Write( stm, &header, sizeof(header), &count );
+    if( FAILED( r ) )
+    {
+        ERR("Write failed at %d\n",__LINE__);
+        return r;
+    }
+
+    TRACE("Writing pidl\n");
+
+    /* write the PIDL to the shortcut */
+    if( This->pPidl )
+    {
+        r = ILSaveToStream( stm, This->pPidl );
+        if( FAILED( r ) )
+        {
+            ERR("Failed to write PIDL at %d\n",__LINE__);
+            return r;
+        }
+    }
+
+    if( This->sPath )
+        Stream_WriteLocationInfo( stm, exePath, &This->volume );
+
+    if( This->sDescription )
+        r = Stream_WriteString( stm, This->sDescription );
+
+    if( This->sPathRel )
+        r = Stream_WriteString( stm, This->sPathRel );
+
+    if( This->sWorkDir )
+        r = Stream_WriteString( stm, This->sWorkDir );
+
+    if( This->sArgs )
+        r = Stream_WriteString( stm, This->sArgs );
+
+    if( This->sIcoPath )
+        r = Stream_WriteString( stm, This->sIcoPath );
+
+    if( This->sProduct )
+        r = Stream_WriteAdvertiseInfo( stm, This->sProduct, EXP_SZ_ICON_SIG );
+
+    if( This->sComponent )
+        r = Stream_WriteAdvertiseInfo( stm, This->sComponent, EXP_DARWIN_ID_SIG );
+
+    /* the last field is a single zero dword */
+    zero = 0;
+    r = IStream_Write( stm, &zero, sizeof zero, &count );
+
+    return S_OK;
+}
+
+/************************************************************************
+ * IPersistStream_GetSizeMax (IPersistStream)
+ */
+static HRESULT WINAPI IPersistStream_fnGetSizeMax(
+       IPersistStream*  iface,
+       ULARGE_INTEGER*  pcbSize)
+{
+       IShellLinkImpl *This = impl_from_IPersistStream(iface);
+
+       TRACE("(%p)\n", This);
+
+       return E_NOTIMPL;
+}
+
+static const IPersistStreamVtbl psvt =
 {
-       ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
        IPersistStream_fnQueryInterface,
        IPersistStream_fnAddRef,
        IPersistStream_fnRelease,
@@ -974,602 +1189,1489 @@ static ICOM_VTABLE(IPersistStream) psvt =
 /**************************************************************************
  *       IShellLink_Constructor
  */
-HRESULT WINAPI IShellLink_Constructor (
-       IUnknown * pUnkOuter,
-       REFIID riid,
-       LPVOID * ppv)
+HRESULT WINAPI IShellLink_Constructor( IUnknown *pUnkOuter,
+               REFIID riid, LPVOID *ppv )
 {
        IShellLinkImpl * sl;
+       HRESULT r;
 
        TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
 
        *ppv = NULL;
 
-       if(pUnkOuter) return CLASS_E_NOAGGREGATION;
-       sl = (IShellLinkImpl *) LocalAlloc(GMEM_ZEROINIT,sizeof(IShellLinkImpl));
-       if (!sl) return E_OUTOFMEMORY;
+       if (pUnkOuter)
+            return CLASS_E_NOAGGREGATION;
+       sl = LocalAlloc(LMEM_ZEROINIT,sizeof(IShellLinkImpl));
+       if (!sl)
+            return E_OUTOFMEMORY;
 
        sl->ref = 1;
-       ICOM_VTBL(sl) = &slvt;
+       sl->lpVtbl = &slvt;
        sl->lpvtblw = &slvtw;
        sl->lpvtblPersistFile = &pfvt;
        sl->lpvtblPersistStream = &psvt;
+       sl->lpvtblShellLinkDataList = &dlvt;
+       sl->lpvtblShellExtInit = &eivt;
+       sl->lpvtblContextMenu = &cmvt;
+       sl->lpvtblObjectWithSite = &owsvt;
+       sl->iShowCmd = SW_SHOWNORMAL;
+       sl->bDirty = FALSE;
+       sl->iIdOpen = -1;
+       sl->site = NULL;
 
        TRACE("(%p)->()\n",sl);
 
-       if (IsEqualIID(riid, &IID_IShellLinkA))
-           *ppv = sl;
-       else if (IsEqualIID(riid, &IID_IShellLinkW))
-           *ppv = &(sl->lpvtblw);
-       else {
-           LocalFree((HLOCAL)sl);
-           ERR("E_NOINTERFACE\n");
-           return E_NOINTERFACE;
-       }
+        r = ShellLink_QueryInterface( sl, riid, ppv );
+        ShellLink_Release( sl );
+        return r;
+}
 
-       return S_OK;
+
+static BOOL SHELL_ExistsFileW(LPCWSTR path)
+{
+    if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
+        return FALSE;
+    return TRUE;
 }
 
 /**************************************************************************
- *  IShellLinkA_QueryInterface
+ *  ShellLink_UpdatePath
+ *     update absolute path in sPath using relative path in sPathRel
  */
-static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
+static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    if (!path || !psPath)
+       return E_INVALIDARG;
 
-       TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
+    if (!*psPath && sPathRel) {
+       WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
+       LPWSTR final = NULL;
 
-       *ppvObj = NULL;
+       /* first try if [directory of link file] + [relative path] finds an existing file */
 
-       if(IsEqualIID(riid, &IID_IUnknown) ||
-          IsEqualIID(riid, &IID_IShellLinkA))
-       {
-         *ppvObj = This;
-       }
-       else if(IsEqualIID(riid, &IID_IShellLinkW))
-       {
-         *ppvObj = (IShellLinkW *)&(This->lpvtblw);
-       }
-       else if(IsEqualIID(riid, &IID_IPersistFile))
-       {
-         *ppvObj = (IPersistFile *)&(This->lpvtblPersistFile);
-       }
-       else if(IsEqualIID(riid, &IID_IPersistStream))
-       {
-         *ppvObj = (IPersistStream *)&(This->lpvtblPersistStream);
+        GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
+        if( !final )
+            final = buffer;
+       lstrcpyW(final, sPathRel);
+
+       *abs_path = '\0';
+
+       if (SHELL_ExistsFileW(buffer)) {
+           if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
+               lstrcpyW(abs_path, buffer);
+       } else {
+           /* try if [working directory] + [relative path] finds an existing file */
+           if (sWorkDir) {
+               lstrcpyW(buffer, sWorkDir);
+               lstrcpyW(PathAddBackslashW(buffer), sPathRel);
+
+               if (SHELL_ExistsFileW(buffer))
+                   if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
+                       lstrcpyW(abs_path, buffer);
+           }
        }
 
-       if(*ppvObj)
-       {
-         IUnknown_AddRef((IUnknown*)(*ppvObj));
-         TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
-         return S_OK;
+       /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
+       if (!*abs_path)
+           lstrcpyW(abs_path, sPathRel);
+
+       *psPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(abs_path)+1)*sizeof(WCHAR));
+       if (!*psPath)
+           return E_OUTOFMEMORY;
+
+       lstrcpyW(*psPath, abs_path);
+    }
+
+    return S_OK;
+}
+
+/**************************************************************************
+ *       IShellLink_ConstructFromFile
+ */
+HRESULT WINAPI IShellLink_ConstructFromFile( IUnknown* pUnkOuter, REFIID riid,
+               LPCITEMIDLIST pidl, LPVOID* ppv)
+{
+    IShellLinkW* psl;
+
+    HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl);
+
+    if (SUCCEEDED(hr)) {
+       IPersistFile* ppf;
+
+       *ppv = NULL;
+
+       hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
+
+       if (SUCCEEDED(hr)) {
+           WCHAR path[MAX_PATH];
+
+           if (SHGetPathFromIDListW(pidl, path)) 
+               hr = IPersistFile_Load(ppf, path, 0);
+            else
+                hr = E_FAIL;
+
+           if (SUCCEEDED(hr))
+               *ppv = (IUnknown*) psl;
+
+           IPersistFile_Release(ppf);
        }
-       TRACE("-- Interface: E_NOINTERFACE\n");
-       return E_NOINTERFACE;
+
+       if (!*ppv)
+           IShellLinkW_Release(psl);
+    }
+
+    return hr;
+}
+
+/**************************************************************************
+ *  IShellLinkA_QueryInterface
+ */
+static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+    return ShellLink_QueryInterface( This, riid, ppvObj );
 }
+
 /******************************************************************************
  * IShellLinkA_AddRef
  */
 static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
-
-       TRACE("(%p)->(count=%lu)\n",This,This->ref);
-
-       return ++(This->ref);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+    return ShellLink_AddRef( This );
 }
+
 /******************************************************************************
  *     IShellLinkA_Release
  */
 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+    return ShellLink_Release( This );
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
+                  INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
+          This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
+
+    if (This->sComponent || This->sProduct)
+        return S_FALSE;
+
+    if (cchMaxPath)
+        pszFile[0] = 0;
+    if (This->sPath)
+        WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
+                             pszFile, cchMaxPath, NULL, NULL);
+
+    if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
+
+    return IShellLinkW_GetIDList((IShellLinkW*)&(This->lpvtblw), ppidl);
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(pidl=%p)\n",This, pidl);
+
+    if (This->pPidl)
+       ILFree(This->pPidl);
+    This->pPidl = ILClone (pidl);
+    This->bDirty = TRUE;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
+
+    if( cchMaxName )
+        pszName[0] = 0;
+    if( This->sDescription )
+        WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
+            pszName, cchMaxName, NULL, NULL);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(pName=%s)\n", This, pszName);
+
+    HeapFree(GetProcessHeap(), 0, This->sDescription);
+    This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
+    if ( !This->sDescription )
+        return E_OUTOFMEMORY;
+
+    This->bDirty = TRUE;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
+
+    if( cchMaxPath )
+        pszDir[0] = 0;
+    if( This->sWorkDir )
+        WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
+                             pszDir, cchMaxPath, NULL, NULL);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(dir=%s)\n",This, pszDir);
+
+    HeapFree(GetProcessHeap(), 0, This->sWorkDir);
+    This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
+    if ( !This->sWorkDir )
+        return E_OUTOFMEMORY;
+
+    This->bDirty = TRUE;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
+
+    if( cchMaxPath )
+        pszArgs[0] = 0;
+    if( This->sArgs )
+        WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
+                             pszArgs, cchMaxPath, NULL, NULL);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(args=%s)\n",This, pszArgs);
+
+    HeapFree(GetProcessHeap(), 0, This->sArgs);
+    This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
+    if( !This->sArgs )
+        return E_OUTOFMEMORY;
+
+    This->bDirty = TRUE;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
+
+    *pwHotkey = This->wHotKey;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
+
+    This->wHotKey = wHotkey;
+    This->bDirty = TRUE;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(%p)\n",This, piShowCmd);
+    *piShowCmd = This->iShowCmd;
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p) %d\n",This, iShowCmd);
+
+    This->iShowCmd = iShowCmd;
+    This->bDirty = TRUE;
+
+    return NOERROR;
+}
+
+static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPITEMIDLIST pidl, LPSTR pszIconPath, int cchIconPath, int* piIcon)
+{
+    LPCITEMIDLIST pidlLast;
+
+    HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
+
+    if (SUCCEEDED(hr)) {
+       IExtractIconA* pei;
+
+       hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconA, NULL, (LPVOID*)&pei);
+
+       if (SUCCEEDED(hr)) {
+           hr = IExtractIconA_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
+
+           IExtractIconA_Release(pei);
+       }
+
+       IShellFolder_Release(psf);
+    }
+
+    return hr;
+}
+
+static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
+
+    pszIconPath[0] = 0;
+    *piIcon = This->iIcoNdx;
+
+    if (This->sIcoPath)
+    {
+        WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
+       return S_OK;
+    }
+
+    if (This->pPidl || This->sPath)
+    {
+       IShellFolder* pdsk;
+
+       HRESULT hr = SHGetDesktopFolder(&pdsk);
+
+       if (SUCCEEDED(hr))
+        {
+           /* first look for an icon using the PIDL (if present) */
+           if (This->pPidl)
+               hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
+           else
+               hr = E_FAIL;
+
+           /* if we couldn't find an icon yet, look for it using the file system path */
+           if (FAILED(hr) && This->sPath)
+            {
+               LPITEMIDLIST pidl;
+
+               hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
+
+               if (SUCCEEDED(hr)) {
+                   hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
+
+                   SHFree(pidl);
+               }
+           }
+
+           IShellFolder_Release(pdsk);
+       }
+
+       return hr;
+    }
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
+
+    HeapFree(GetProcessHeap(), 0, This->sIcoPath);
+    This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
+    if ( !This->sIcoPath )
+        return E_OUTOFMEMORY;
+
+    This->iIcoNdx = iIcon;
+    This->bDirty = TRUE;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(path=%s %x)\n",This, pszPathRel, dwReserved);
+
+    HeapFree(GetProcessHeap(), 0, This->sPathRel);
+    This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
+    This->bDirty = TRUE;
+
+    return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
+}
+
+static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
+{
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
+
+    return IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, fFlags );
+}
+
+static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
+{
+    HRESULT r;
+    LPWSTR str;
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
+
+    TRACE("(%p)->(path=%s)\n",This, pszFile);
+
+    str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
+    if( !str ) 
+        return E_OUTOFMEMORY;
+
+    r = IShellLinkW_SetPath((IShellLinkW*)&(This->lpvtblw), str);
+    HeapFree( GetProcessHeap(), 0, str );
+
+    return r;
+}
+
+/**************************************************************************
+* IShellLink Implementation
+*/
+
+static const IShellLinkAVtbl slvt =
+{
+    IShellLinkA_fnQueryInterface,
+    IShellLinkA_fnAddRef,
+    IShellLinkA_fnRelease,
+    IShellLinkA_fnGetPath,
+    IShellLinkA_fnGetIDList,
+    IShellLinkA_fnSetIDList,
+    IShellLinkA_fnGetDescription,
+    IShellLinkA_fnSetDescription,
+    IShellLinkA_fnGetWorkingDirectory,
+    IShellLinkA_fnSetWorkingDirectory,
+    IShellLinkA_fnGetArguments,
+    IShellLinkA_fnSetArguments,
+    IShellLinkA_fnGetHotkey,
+    IShellLinkA_fnSetHotkey,
+    IShellLinkA_fnGetShowCmd,
+    IShellLinkA_fnSetShowCmd,
+    IShellLinkA_fnGetIconLocation,
+    IShellLinkA_fnSetIconLocation,
+    IShellLinkA_fnSetRelativePath,
+    IShellLinkA_fnResolve,
+    IShellLinkA_fnSetPath
+};
+
+
+/**************************************************************************
+ *  IShellLinkW_fnQueryInterface
+ */
+static HRESULT WINAPI IShellLinkW_fnQueryInterface(
+  IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
+{
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+    return ShellLink_QueryInterface( This, riid, ppvObj );
+}
+
+/******************************************************************************
+ * IShellLinkW_fnAddRef
+ */
+static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
+{
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+    return ShellLink_AddRef( This );
+}
+
+/******************************************************************************
+ * IShellLinkW_fnRelease
+ */
+static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
+{
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+    return ShellLink_Release( This );
+}
+
+static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
+{
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+    TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
+          This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
+
+    if (This->sComponent || This->sProduct)
+        return S_FALSE;
+
+    if (cchMaxPath)
+        pszFile[0] = 0;
+    if (This->sPath)
+        lstrcpynW( pszFile, This->sPath, cchMaxPath );
+
+    if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
+{
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+    TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
+
+    if (!This->pPidl)
+    {
+       *ppidl = NULL;
+        return S_FALSE;
+    }
+    *ppidl = ILClone(This->pPidl);
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
+{
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+    TRACE("(%p)->(pidl=%p)\n",This, pidl);
+
+    if( This->pPidl )
+        ILFree( This->pPidl );
+    This->pPidl = ILClone( pidl );
+    if( !This->pPidl )
+        return E_FAIL;
+
+    This->bDirty = TRUE;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
+{
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+    TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
+
+    pszName[0] = 0;
+    if( This->sDescription )
+        lstrcpynW( pszName, This->sDescription, cchMaxName );
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
+{
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+    TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
+
+    HeapFree(GetProcessHeap(), 0, This->sDescription);
+    This->sDescription = HeapAlloc( GetProcessHeap(), 0,
+                                    (lstrlenW( pszName )+1)*sizeof(WCHAR) );
+    if ( !This->sDescription )
+        return E_OUTOFMEMORY;
+
+    lstrcpyW( This->sDescription, pszName );
+    This->bDirty = TRUE;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
+{
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+
+    TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
+
+    if( cchMaxPath )
+        pszDir[0] = 0;
+    if( This->sWorkDir )
+        lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
+
+    return S_OK;
+}
+
+static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
+{
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
-       TRACE("(%p)->(count=%lu)\n",This,This->ref);
+    TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
 
-       if (!--(This->ref))
-       { TRACE("-- destroying IShellLink(%p)\n",This);
+    HeapFree(GetProcessHeap(), 0, This->sWorkDir);
+    This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
+                                (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
+    if ( !This->sWorkDir )
+        return E_OUTOFMEMORY;
+    lstrcpyW( This->sWorkDir, pszDir );
+    This->bDirty = TRUE;
 
-         if (This->sIcoPath)
-           HeapFree(GetProcessHeap(), 0, This->sIcoPath);
+    return S_OK;
+}
 
-         if (This->sArgs)
-           HeapFree(GetProcessHeap(), 0, This->sArgs);
+static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
+{
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
-         if (This->sWorkDir)
-           HeapFree(GetProcessHeap(), 0, This->sWorkDir);
+    TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
 
-         if (This->sDescription)
-           HeapFree(GetProcessHeap(), 0, This->sDescription);
+    if( cchMaxPath )
+        pszArgs[0] = 0;
+    if( This->sArgs )
+        lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
 
-         if (This->sPath)
-           HeapFree(GetProcessHeap(),0,This->sPath);
+    return NOERROR;
+}
 
-         if (This->pPidl)
-           SHFree(This->pPidl);
+static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
+{
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
-         if (This->lpFileStream)
-           IStream_Release(This->lpFileStream);
+    TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
 
-         This->iIcoNdx = 0;
+    HeapFree(GetProcessHeap(), 0, This->sArgs);
+    This->sArgs = HeapAlloc( GetProcessHeap(), 0,
+                             (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
+    if ( !This->sArgs )
+        return E_OUTOFMEMORY;
+    lstrcpyW( This->sArgs, pszArgs );
+    This->bDirty = TRUE;
 
-         LocalFree((HANDLE)This);
-         return 0;
-       }
-       return This->ref;
+    return S_OK;
 }
 
-static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
+static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
-       TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",This, pszFile, cchMaxPath, pfd, fFlags, debugstr_a(This->sPath));
+    TRACE("(%p)->(%p)\n",This, pwHotkey);
 
-       if (This->sPath)
-         lstrcpynA(pszFile,This->sPath, cchMaxPath);
-       else
-         return E_FAIL;
+    *pwHotkey=This->wHotKey;
 
-       return NOERROR;
+    return S_OK;
 }
-static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
+
+static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
-       TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
+    TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
 
-       *ppidl = ILClone(This->pPidl);
-       return NOERROR;
+    This->wHotKey = wHotkey;
+    This->bDirty = TRUE;
+
+    return S_OK;
 }
-static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
+
+static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
-       TRACE("(%p)->(pidl=%p)\n",This, pidl);
+    TRACE("(%p)->(%p)\n",This, piShowCmd);
 
-       if (This->pPidl)
-           SHFree(This->pPidl);
-       This->pPidl = ILClone (pidl);
-       return NOERROR;
+    *piShowCmd = This->iShowCmd;
+
+    return S_OK;
 }
-static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
+
+static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
-       FIXME("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
-       lstrcpynA(pszName,"Description, FIXME",cchMaxName);
-       return NOERROR;
+    This->iShowCmd = iShowCmd;
+    This->bDirty = TRUE;
+
+    return S_OK;
 }
-static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
+
+static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPITEMIDLIST pidl, LPWSTR pszIconPath, int cchIconPath, int* piIcon)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    LPCITEMIDLIST pidlLast;
 
-       TRACE("(%p)->(pName=%s)\n", This, pszName);
+    HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
 
-       if (This->sDescription)
-           HeapFree(GetProcessHeap(), 0, This->sDescription);
-       if (!(This->sDescription = heap_strdup(pszName)))
-           return E_OUTOFMEMORY;
+    if (SUCCEEDED(hr)) {
+       IExtractIconW* pei;
 
-       return NOERROR;
-}
-static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
-{
-       ICOM_THIS(IShellLinkImpl, iface);
+       hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconW, NULL, (LPVOID*)&pei);
 
-       TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
+       if (SUCCEEDED(hr)) {
+           hr = IExtractIconW_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
 
-       lstrcpynA( pszDir, This->sWorkDir ? This->sWorkDir : "", cchMaxPath );
+           IExtractIconW_Release(pei);
+       }
 
-       return NOERROR;
+       IShellFolder_Release(psf);
+    }
+
+    return hr;
 }
-static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
+
+static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
-       TRACE("(%p)->(dir=%s)\n",This, pszDir);
+    TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
 
-       if (This->sWorkDir)
-           HeapFree(GetProcessHeap(), 0, This->sWorkDir);
-       if (!(This->sWorkDir = heap_strdup(pszDir)))
-           return E_OUTOFMEMORY;
+    pszIconPath[0] = 0;
+    *piIcon = This->iIcoNdx;
 
-       return NOERROR;
-}
-static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
-{
-       ICOM_THIS(IShellLinkImpl, iface);
+    if (This->sIcoPath)
+    {
+       lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
+       return S_OK;
+    }
 
-       TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
+    if (This->pPidl || This->sPath)
+    {
+       IShellFolder* pdsk;
 
-       lstrcpynA( pszArgs, This->sArgs ? This->sArgs : "", cchMaxPath );
+       HRESULT hr = SHGetDesktopFolder(&pdsk);
 
-       return NOERROR;
-}
-static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
-{
-       ICOM_THIS(IShellLinkImpl, iface);
+       if (SUCCEEDED(hr))
+        {
+           /* first look for an icon using the PIDL (if present) */
+           if (This->pPidl)
+               hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
+           else
+               hr = E_FAIL;
+
+           /* if we couldn't find an icon yet, look for it using the file system path */
+           if (FAILED(hr) && This->sPath)
+            {
+               LPITEMIDLIST pidl;
 
-       TRACE("(%p)->(args=%s)\n",This, pszArgs);
+               hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
 
-       if (This->sArgs)
-           HeapFree(GetProcessHeap(), 0, This->sArgs);
-       if (!(This->sArgs = heap_strdup(pszArgs)))
-           return E_OUTOFMEMORY;
+               if (SUCCEEDED(hr))
+                {
+                   hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
 
-       return NOERROR;
+                   SHFree(pidl);
+               }
+           }
+
+           IShellFolder_Release(pdsk);
+       }
+       return hr;
+    }
+    return S_OK;
 }
-static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
+
+static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
-       TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
+    TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
 
-       *pwHotkey = This->wHotKey;
+    HeapFree(GetProcessHeap(), 0, This->sIcoPath);
+    This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
+                                (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
+    if ( !This->sIcoPath )
+        return E_OUTOFMEMORY;
+    lstrcpyW( This->sIcoPath, pszIconPath );
 
-       return NOERROR;
+    This->iIcoNdx = iIcon;
+    This->bDirty = TRUE;
+
+    return S_OK;
 }
-static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
+
+static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
-       TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
+    TRACE("(%p)->(path=%s %x)\n",This, debugstr_w(pszPathRel), dwReserved);
 
-       This->wHotKey = wHotkey;
+    HeapFree(GetProcessHeap(), 0, This->sPathRel);
+    This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
+                                (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
+    if ( !This->sPathRel )
+        return E_OUTOFMEMORY;
+    lstrcpyW( This->sPathRel, pszPathRel );
+    This->bDirty = TRUE;
 
-       return NOERROR;
+    return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
 }
-static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
-{
-       ICOM_THIS(IShellLinkImpl, iface);
 
-       FIXME("(%p)->(%p)\n",This, piShowCmd);
-       *piShowCmd=0;
-       return NOERROR;
-}
-static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
+static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    HRESULT hr = S_OK;
+    BOOL bSuccess;
 
-       FIXME("(%p)->(showcmd=%x)\n",This, iShowCmd);
-       return NOERROR;
-}
-static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
-{
-       ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
-       TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
+    TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
 
-       lstrcpynA( pszIconPath, This->sIcoPath ? This->sIcoPath : "", cchIconPath );
-       *piIcon = This->iIcoNdx;
+    /*FIXME: use IResolveShellLink interface */
 
-       return NOERROR;
-}
-static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
-{
-       ICOM_THIS(IShellLinkImpl, iface);
+    if (!This->sPath && This->pPidl) {
+       WCHAR buffer[MAX_PATH];
+
+       bSuccess = SHGetPathFromIDListW(This->pPidl, buffer);
+
+       if (bSuccess && *buffer) {
+           This->sPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
+           if (!This->sPath)
+               return E_OUTOFMEMORY;
+
+           lstrcpyW(This->sPath, buffer);
 
-       TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
+           This->bDirty = TRUE;
+       } else
+           hr = S_OK;    /* don't report an error occurred while just caching information */
+    }
 
-       if (This->sIcoPath)
-           HeapFree(GetProcessHeap(), 0, This->sIcoPath);
-       if (!(This->sIcoPath = heap_strdup(pszIconPath)))
+    if (!This->sIcoPath && This->sPath) {
+       This->sIcoPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
+       if (!This->sIcoPath)
            return E_OUTOFMEMORY;
-       This->iIcoNdx = iIcon;
 
-       return NOERROR;
+       lstrcpyW(This->sIcoPath, This->sPath);
+       This->iIcoNdx = 0;
+
+       This->bDirty = TRUE;
+    }
+
+    return hr;
 }
-static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
+
+static LPWSTR ShellLink_GetAdvertisedArg(LPCWSTR str)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    LPWSTR ret;
+    LPCWSTR p;
+    DWORD len;
 
-       FIXME("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved);
-       return NOERROR;
+    if( !str )
+        return NULL;
+
+    p = strchrW( str, ':' );
+    if( !p )
+        return NULL;
+    len = p - str;
+    ret = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
+    if( !ret )
+        return ret;
+    memcpy( ret, str, sizeof(WCHAR)*len );
+    ret[len] = 0;
+    return ret;
 }
-static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
+
+static HRESULT ShellLink_SetAdvertiseInfo(IShellLinkImpl *This, LPCWSTR str)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    LPCWSTR szComponent = NULL, szProduct = NULL, p;
+    WCHAR szGuid[39];
+    HRESULT r;
+    GUID guid;
+    int len;
 
-       FIXME("(%p)->(hwnd=%x flags=%lx)\n",This, hwnd, fFlags);
-       return NOERROR;
+    while( str[0] )
+    {
+        /* each segment must start with two colons */
+        if( str[0] != ':' || str[1] != ':' )
+            return E_FAIL;
+
+        /* the last segment is just two colons */
+        if( !str[2] )
+            break;
+        str += 2;
+
+        /* there must be a colon straight after a guid */
+        p = strchrW( str, ':' );
+        if( !p )
+            return E_FAIL;
+        len = p - str;
+        if( len != 38 )
+            return E_FAIL;
+
+        /* get the guid, and check it's validly formatted */
+        memcpy( szGuid, str, sizeof(WCHAR)*len );
+        szGuid[len] = 0;
+        r = CLSIDFromString( szGuid, &guid );
+        if( r != S_OK )
+            return r;
+        str = p + 1;
+
+        /* match it up to a guid that we care about */
+        if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutComponent ) && !szComponent )
+            szComponent = str;
+        else if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutProduct ) && !szProduct )
+            szProduct = str;
+        else
+            return E_FAIL;
+
+        /* skip to the next field */
+        str = strchrW( str, ':' );
+        if( !str )
+            return E_FAIL;
+    }
+
+    /* we have to have a component for an advertised shortcut */
+    if( !szComponent )
+        return E_FAIL;
+
+    This->sComponent = ShellLink_GetAdvertisedArg( szComponent );
+    This->sProduct = ShellLink_GetAdvertisedArg( szProduct );
+
+    TRACE("Component = %s\n", debugstr_w(This->sComponent));
+    TRACE("Product = %s\n", debugstr_w(This->sProduct));
+
+    return S_OK;
 }
-static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
+
+static BOOL ShellLink_GetVolumeInfo(LPWSTR path, volume_info *volume)
+{
+    const int label_sz = sizeof volume->label/sizeof volume->label[0];
+    WCHAR drive[4] = { path[0], ':', '\\', 0 };
+    BOOL r;
+
+    volume->type = GetDriveTypeW(drive);
+    r = GetVolumeInformationW(drive, volume->label, label_sz,
+                              &volume->serial, NULL, NULL, NULL, 0);
+    TRACE("r = %d type %d serial %08x name %s\n", r,
+          volume->type, volume->serial, debugstr_w(volume->label));
+    return r;
+}
+
+static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+    WCHAR buffer[MAX_PATH];
+    LPWSTR fname, unquoted = NULL;
+    HRESULT hr = S_OK;
+    UINT len;
 
-       TRACE("(%p)->(path=%s)\n",This, pszFile);
+    TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
 
-       if (This->sPath)
-           HeapFree(GetProcessHeap(), 0, This->sPath);
-       if (!(This->sPath = heap_strdup(pszFile)))
-           return E_OUTOFMEMORY;
+    /* quotes at the ends of the string are stripped */
+    len = lstrlenW(pszFile);
+    if (pszFile[0] == '"' && pszFile[len-1] == '"')
+    {
+        unquoted = strdupW(pszFile);
+        PathUnquoteSpacesW(unquoted);
+        pszFile = unquoted;
+    }
 
-       return NOERROR;
+    /* any other quote marks are invalid */
+    if (strchrW(pszFile, '"'))
+        return S_FALSE;
+
+    HeapFree(GetProcessHeap(), 0, This->sPath);
+    This->sPath = NULL;
+
+    HeapFree(GetProcessHeap(), 0, This->sComponent);
+    This->sComponent = NULL;
+
+    if (This->pPidl)
+        ILFree(This->pPidl);
+    This->pPidl = NULL;
+
+    if (S_OK != ShellLink_SetAdvertiseInfo( This, pszFile ))
+    {
+        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;
+
+        This->pPidl = SHSimpleIDListFromPathW(pszFile);
+        ShellLink_GetVolumeInfo(buffer, &This->volume);
+
+        This->sPath = HeapAlloc( GetProcessHeap(), 0,
+                             (lstrlenW( buffer )+1) * sizeof (WCHAR) );
+        if (!This->sPath)
+            return E_OUTOFMEMORY;
+
+        lstrcpyW(This->sPath, buffer);
+    }
+    This->bDirty = TRUE;
+    HeapFree(GetProcessHeap(), 0, unquoted);
+
+    return hr;
 }
 
 /**************************************************************************
-* IShellLink Implementation
+* IShellLinkW Implementation
 */
 
-static ICOM_VTABLE(IShellLinkA) slvt =
-{
-       ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
-       IShellLinkA_fnQueryInterface,
-       IShellLinkA_fnAddRef,
-       IShellLinkA_fnRelease,
-       IShellLinkA_fnGetPath,
-       IShellLinkA_fnGetIDList,
-       IShellLinkA_fnSetIDList,
-       IShellLinkA_fnGetDescription,
-       IShellLinkA_fnSetDescription,
-       IShellLinkA_fnGetWorkingDirectory,
-       IShellLinkA_fnSetWorkingDirectory,
-       IShellLinkA_fnGetArguments,
-       IShellLinkA_fnSetArguments,
-       IShellLinkA_fnGetHotkey,
-       IShellLinkA_fnSetHotkey,
-       IShellLinkA_fnGetShowCmd,
-       IShellLinkA_fnSetShowCmd,
-       IShellLinkA_fnGetIconLocation,
-       IShellLinkA_fnSetIconLocation,
-       IShellLinkA_fnSetRelativePath,
-       IShellLinkA_fnResolve,
-       IShellLinkA_fnSetPath
+static const IShellLinkWVtbl slvtw =
+{
+    IShellLinkW_fnQueryInterface,
+    IShellLinkW_fnAddRef,
+    IShellLinkW_fnRelease,
+    IShellLinkW_fnGetPath,
+    IShellLinkW_fnGetIDList,
+    IShellLinkW_fnSetIDList,
+    IShellLinkW_fnGetDescription,
+    IShellLinkW_fnSetDescription,
+    IShellLinkW_fnGetWorkingDirectory,
+    IShellLinkW_fnSetWorkingDirectory,
+    IShellLinkW_fnGetArguments,
+    IShellLinkW_fnSetArguments,
+    IShellLinkW_fnGetHotkey,
+    IShellLinkW_fnSetHotkey,
+    IShellLinkW_fnGetShowCmd,
+    IShellLinkW_fnSetShowCmd,
+    IShellLinkW_fnGetIconLocation,
+    IShellLinkW_fnSetIconLocation,
+    IShellLinkW_fnSetRelativePath,
+    IShellLinkW_fnResolve,
+    IShellLinkW_fnSetPath
 };
 
-
-/**************************************************************************
- *  IShellLinkW_fnQueryInterface
- */
-static HRESULT WINAPI IShellLinkW_fnQueryInterface(
-  IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
+static HRESULT WINAPI
+ShellLink_DataList_QueryInterface( IShellLinkDataList* iface, REFIID riid, void** ppvObject)
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
-
-       return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
+    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+    return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
 }
 
-/******************************************************************************
- * IShellLinkW_fnAddRef
- */
-static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
+static ULONG WINAPI
+ShellLink_DataList_AddRef( IShellLinkDataList* iface )
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+    return IShellLinkA_AddRef((IShellLinkA*)This);
+}
 
-       TRACE("(%p)->(count=%lu)\n",This,This->ref);
+static ULONG WINAPI
+ShellLink_DataList_Release( IShellLinkDataList* iface )
+{
+    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+    return ShellLink_Release( This );
+}
 
-       return IShellLinkA_AddRef((IShellLinkA*)This);
+static HRESULT WINAPI
+ShellLink_AddDataBlock( IShellLinkDataList* iface, void* pDataBlock )
+{
+    FIXME("\n");
+    return E_NOTIMPL;
 }
-/******************************************************************************
- * IShellLinkW_fnRelease
- */
 
-static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
+static HRESULT WINAPI
+ShellLink_CopyDataBlock( IShellLinkDataList* iface, DWORD dwSig, void** ppDataBlock )
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+    LPVOID block = NULL;
+    HRESULT r = E_FAIL;
 
-       TRACE("(%p)->(count=%lu)\n",This,This->ref);
+    TRACE("%p %08x %p\n", iface, dwSig, ppDataBlock );
 
-       return IShellLinkA_Release((IShellLinkA*)This);
+    switch (dwSig)
+    {
+    case EXP_DARWIN_ID_SIG:
+        if (!This->sComponent)
+            break;
+        block = shelllink_build_darwinid( This->sComponent, dwSig );
+        r = 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);
+    }
+    *ppDataBlock = block;
+    return r;
 }
 
-static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
+static HRESULT WINAPI
+ShellLink_RemoveDataBlock( IShellLinkDataList* iface, DWORD dwSig )
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
-
-       FIXME("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)\n",This, pszFile, cchMaxPath, pfd, fFlags);
-        MultiByteToWideChar( CP_ACP, 0, "c:\\foo.bar", -1, pszFile, cchMaxPath );
-       return NOERROR;
+    FIXME("\n");
+    return E_NOTIMPL;
 }
 
-static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
+static HRESULT WINAPI
+ShellLink_GetFlags( IShellLinkDataList* iface, DWORD* pdwFlags )
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+    DWORD flags = 0;
 
-       FIXME("(%p)->(ppidl=%p)\n",This, ppidl);
-       *ppidl = _ILCreateDesktop();
-       return NOERROR;
-}
+    FIXME("%p %p\n", This, pdwFlags );
 
-static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
-{
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    /* FIXME: add more */
+    if (This->sArgs)
+        flags |= SLDF_HAS_ARGS;
+    if (This->sComponent)
+        flags |= SLDF_HAS_DARWINID;
+    if (This->sIcoPath)
+        flags |= SLDF_HAS_ICONLOCATION;
+    if (This->sProduct)
+        flags |= SLDF_HAS_LOGO3ID;
+    if (This->pPidl)
+        flags |= SLDF_HAS_ID_LIST;
 
-       FIXME("(%p)->(pidl=%p)\n",This, pidl);
-       return NOERROR;
+    *pdwFlags = flags;
+
+    return S_OK;
 }
 
-static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
+static HRESULT WINAPI
+ShellLink_SetFlags( IShellLinkDataList* iface, DWORD dwFlags )
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
-
-       FIXME("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
-        MultiByteToWideChar( CP_ACP, 0, "Description, FIXME", -1, pszName, cchMaxName );
-       return NOERROR;
+    FIXME("\n");
+    return E_NOTIMPL;
 }
 
-static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
+static const IShellLinkDataListVtbl dlvt =
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    ShellLink_DataList_QueryInterface,
+    ShellLink_DataList_AddRef,
+    ShellLink_DataList_Release,
+    ShellLink_AddDataBlock,
+    ShellLink_CopyDataBlock,
+    ShellLink_RemoveDataBlock,
+    ShellLink_GetFlags,
+    ShellLink_SetFlags
+};
 
-       TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
+static HRESULT WINAPI
+ShellLink_ExtInit_QueryInterface( IShellExtInit* iface, REFIID riid, void** ppvObject )
+{
+    IShellLinkImpl *This = impl_from_IShellExtInit(iface);
+    return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
+}
 
-       if (This->sDescription)
-           HeapFree(GetProcessHeap(), 0, This->sDescription);
-       if (!(This->sDescription = HEAP_strdupWtoA(GetProcessHeap(), 0, pszName)))
-           return E_OUTOFMEMORY;
+static ULONG WINAPI
+ShellLink_ExtInit_AddRef( IShellExtInit* iface )
+{
+    IShellLinkImpl *This = impl_from_IShellExtInit(iface);
+    return IShellLinkA_AddRef((IShellLinkA*)This);
+}
 
-       return NOERROR;
+static ULONG WINAPI
+ShellLink_ExtInit_Release( IShellExtInit* iface )
+{
+    IShellLinkImpl *This = impl_from_IShellExtInit(iface);
+    return ShellLink_Release( This );
 }
 
-static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
+/**************************************************************************
+ * ShellLink implementation of IShellExtInit::Initialize()
+ *
+ * Loads the shelllink from the dataobject the shell is pointing to.
+ */
+static HRESULT WINAPI
+ShellLink_ExtInit_Initialize( IShellExtInit* iface, LPCITEMIDLIST pidlFolder,
+                              IDataObject *pdtobj, HKEY hkeyProgID )
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellExtInit(iface);
+    FORMATETC format;
+    STGMEDIUM stgm;
+    UINT count;
+    HRESULT r = E_FAIL;
 
-       TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
+    TRACE("%p %p %p %p\n", This, pidlFolder, pdtobj, hkeyProgID );
 
-       MultiByteToWideChar( CP_ACP, 0, This->sWorkDir ? This->sWorkDir : "", -1, pszDir, cchMaxPath );
+    if( !pdtobj )
+        return r;
 
-       return NOERROR;
-}
+    format.cfFormat = CF_HDROP;
+    format.ptd = NULL;
+    format.dwAspect = DVASPECT_CONTENT;
+    format.lindex = -1;
+    format.tymed = TYMED_HGLOBAL;
 
-static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
-{
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    if( FAILED( IDataObject_GetData( pdtobj, &format, &stgm ) ) )
+        return r;
 
-       TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
+    count = DragQueryFileW( stgm.u.hGlobal, -1, NULL, 0 );
+    if( count == 1 )
+    {
+        LPWSTR path;
 
-       if (This->sWorkDir)
-           HeapFree(GetProcessHeap(), 0, This->sWorkDir);
-       if (!(This->sWorkDir = HEAP_strdupWtoA(GetProcessHeap(), 0, pszDir)))
-           return E_OUTOFMEMORY;
+        count = DragQueryFileW( stgm.u.hGlobal, 0, NULL, 0 );
+        count++;
+        path = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
+        if( path )
+        {
+            IPersistFile *pf = (IPersistFile*) &This->lpvtblPersistFile;
 
-       return NOERROR;
+            count = DragQueryFileW( stgm.u.hGlobal, 0, path, count );
+            r = IPersistFile_Load( pf, path, 0 );
+            HeapFree( GetProcessHeap(), 0, path );
+        }
+    }
+    ReleaseStgMedium( &stgm );
+
+    return r;
 }
 
-static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
+static const IShellExtInitVtbl eivt =
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    ShellLink_ExtInit_QueryInterface,
+    ShellLink_ExtInit_AddRef,
+    ShellLink_ExtInit_Release,
+    ShellLink_ExtInit_Initialize
+};
 
-       TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
+static HRESULT WINAPI
+ShellLink_ContextMenu_QueryInterface( IContextMenu* iface, REFIID riid, void** ppvObject )
+{
+    IShellLinkImpl *This = impl_from_IContextMenu(iface);
+    return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
+}
 
-       MultiByteToWideChar( CP_ACP, 0, This->sArgs ? This->sArgs : "", -1, pszArgs, cchMaxPath );
+static ULONG WINAPI
+ShellLink_ContextMenu_AddRef( IContextMenu* iface )
+{
+    IShellLinkImpl *This = impl_from_IContextMenu(iface);
+    return IShellLinkA_AddRef((IShellLinkA*)This);
+}
 
-       return NOERROR;
+static ULONG WINAPI
+ShellLink_ContextMenu_Release( IContextMenu* iface )
+{
+    IShellLinkImpl *This = impl_from_IContextMenu(iface);
+    return ShellLink_Release( This );
 }
 
-static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
+static HRESULT WINAPI
+ShellLink_QueryContextMenu( IContextMenu* iface, HMENU hmenu, UINT indexMenu,
+                            UINT idCmdFirst, UINT idCmdLast, UINT uFlags )
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IContextMenu(iface);
+    static WCHAR szOpen[] = { 'O','p','e','n',0 };
+    MENUITEMINFOW mii;
+    int id = 1;
 
-       TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
+    TRACE("%p %p %u %u %u %u\n", This,
+          hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
 
-       if (This->sArgs)
-           HeapFree(GetProcessHeap(), 0, This->sArgs);
-       if (!(This->sArgs = HEAP_strdupWtoA(GetProcessHeap(), 0, pszArgs)))
-           return E_OUTOFMEMORY;
+    if ( !hmenu )
+        return E_INVALIDARG;
 
-       return NOERROR;
+    memset( &mii, 0, sizeof mii );
+    mii.cbSize = sizeof mii;
+    mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+    mii.dwTypeData = szOpen;
+    mii.cch = strlenW( mii.dwTypeData );
+    mii.wID = idCmdFirst + id++;
+    mii.fState = MFS_DEFAULT | MFS_ENABLED;
+    mii.fType = MFT_STRING;
+    if (!InsertMenuItemW( hmenu, indexMenu, TRUE, &mii ))
+        return E_FAIL;
+    This->iIdOpen = 0;
+
+    return MAKE_HRESULT( SEVERITY_SUCCESS, 0, id );
 }
 
-static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
+static LPWSTR
+shelllink_get_msi_component_path( LPWSTR component )
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    LPWSTR path;
+    DWORD r, sz = 0;
 
-       FIXME("(%p)->(%p)\n",This, pwHotkey);
-       *pwHotkey=0x0;
-       return NOERROR;
-}
+    r = CommandLineFromMsiDescriptor( component, NULL, &sz );
+    if (r != ERROR_SUCCESS)
+         return NULL;
 
-static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
-{
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    sz++;
+    path = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
+    r = CommandLineFromMsiDescriptor( component, path, &sz );
+    if (r != ERROR_SUCCESS)
+    {
+        HeapFree( GetProcessHeap(), 0, path );
+        path = NULL;
+    }
 
-       FIXME("(%p)->(hotkey=%x)\n",This, wHotkey);
-       return NOERROR;
+    TRACE("returning %s\n", debugstr_w( path ) );
+
+    return path;
 }
 
-static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
+static HRESULT WINAPI
+ShellLink_InvokeCommand( IContextMenu* iface, LPCMINVOKECOMMANDINFO lpici )
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IContextMenu(iface);
+    static const WCHAR szOpen[] = { 'O','p','e','n',0 };
+    SHELLEXECUTEINFOW sei;
+    HWND hwnd = NULL; /* FIXME: get using interface set from IObjectWithSite */
+    LPWSTR args = NULL;
+    LPWSTR path = NULL;
+    HRESULT r;
 
-       FIXME("(%p)->(%p)\n",This, piShowCmd);
-       *piShowCmd=0;
-       return NOERROR;
-}
+    TRACE("%p %p\n", This, lpici );
 
-static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
-{
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    if ( lpici->cbSize < sizeof (CMINVOKECOMMANDINFO) )
+        return E_INVALIDARG;
 
-       FIXME("(%p)->(showcmd=%x)\n",This, iShowCmd);
-       return NOERROR;
-}
+    if ( lpici->lpVerb != MAKEINTRESOURCEA(This->iIdOpen) )
+    {
+        ERR("Unknown id %d != %d\n", (INT)lpici->lpVerb, This->iIdOpen );
+        return E_INVALIDARG;
+    }
 
-static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
-{
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    r = IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, 0 );
+    if ( FAILED( r ) )
+        return r;
+
+    if ( This->sComponent )
+    {
+        path = shelllink_get_msi_component_path( This->sComponent );
+        if (!path)
+            return E_FAIL;
+    }
+    else
+        path = strdupW( This->sPath );
+
+    if ( lpici->cbSize == sizeof (CMINVOKECOMMANDINFOEX) &&
+         ( lpici->fMask & CMIC_MASK_UNICODE ) )
+    {
+        LPCMINVOKECOMMANDINFOEX iciex = (LPCMINVOKECOMMANDINFOEX) lpici;
+        DWORD len = 2;
+
+        if ( This->sArgs )
+            len += lstrlenW( This->sArgs );
+        if ( iciex->lpParametersW )
+            len += lstrlenW( iciex->lpParametersW );
+
+        args = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+        args[0] = 0;
+        if ( This->sArgs )
+            lstrcatW( args, This->sArgs );
+        if ( iciex->lpParametersW )
+        {
+            static const WCHAR space[] = { ' ', 0 };
+            lstrcatW( args, space );
+            lstrcatW( args, iciex->lpParametersW );
+        }
+    }
 
-       TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
+    memset( &sei, 0, sizeof sei );
+    sei.cbSize = sizeof sei;
+    sei.fMask = SEE_MASK_UNICODE | SEE_MASK_NOCLOSEPROCESS;
+    sei.lpFile = path;
+    sei.nShow = This->iShowCmd;
+    sei.lpIDList = This->pPidl;
+    sei.lpDirectory = This->sWorkDir;
+    sei.lpParameters = args;
+    sei.lpVerb = szOpen;
+
+    if( ShellExecuteExW( &sei ) )
+    {
+        if ( sei.hProcess )
+        {
+            WaitForSingleObject( sei.hProcess, 10000 );
+            CloseHandle( sei.hProcess );
+        }
+        r = S_OK;
+    }
+    else
+        r = E_FAIL;
 
-        MultiByteToWideChar( CP_ACP, 0, This->sIcoPath ? This->sIcoPath : "", -1, pszIconPath, cchIconPath );
-       *piIcon = This->iIcoNdx;
+    HeapFree( GetProcessHeap(), 0, args );
+    HeapFree( GetProcessHeap(), 0, path );
 
-       return NOERROR;
+    return r;
 }
 
-static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
+static HRESULT WINAPI
+ShellLink_GetCommandString( IContextMenu* iface, UINT_PTR idCmd, UINT uType,
+                            UINT* pwReserved, LPSTR pszName, UINT cchMax )
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IContextMenu(iface);
 
-       TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
+    FIXME("%p %lu %u %p %p %u\n", This,
+          idCmd, uType, pwReserved, pszName, cchMax );
 
-       if (This->sIcoPath)
-           HeapFree(GetProcessHeap(), 0, This->sIcoPath);
-       if (!(This->sIcoPath = HEAP_strdupWtoA(GetProcessHeap(), 0, pszIconPath)))
-           return E_OUTOFMEMORY;
-       This->iIcoNdx = iIcon;
+    return E_NOTIMPL;
+}
 
-       return NOERROR;
+static const IContextMenuVtbl cmvt =
+{
+    ShellLink_ContextMenu_QueryInterface,
+    ShellLink_ContextMenu_AddRef,
+    ShellLink_ContextMenu_Release,
+    ShellLink_QueryContextMenu,
+    ShellLink_InvokeCommand,
+    ShellLink_GetCommandString
+};
+
+static HRESULT WINAPI
+ShellLink_ObjectWithSite_QueryInterface( IObjectWithSite* iface, REFIID riid, void** ppvObject )
+{
+    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
+    return ShellLink_QueryInterface( This, riid, ppvObject );
 }
 
-static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
+static ULONG WINAPI
+ShellLink_ObjectWithSite_AddRef( IObjectWithSite* iface )
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
+    return ShellLink_AddRef( This );
+}
 
-       FIXME("(%p)->(path=%s %lx)\n",This, debugstr_w(pszPathRel), dwReserved);
-       return NOERROR;
+static ULONG WINAPI
+ShellLink_ObjectWithSite_Release( IObjectWithSite* iface )
+{
+    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
+    return ShellLink_Release( This );
 }
 
-static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
+static HRESULT WINAPI
+ShellLink_GetSite( IObjectWithSite *iface, REFIID iid, void ** ppvSite )
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
 
-       FIXME("(%p)->(hwnd=%x flags=%lx)\n",This, hwnd, fFlags);
-       return NOERROR;
+    TRACE("%p %s %p\n", This, debugstr_guid( iid ), ppvSite );
+
+    if ( !This->site )
+        return E_FAIL;
+    return IUnknown_QueryInterface( This->site, iid, ppvSite );
 }
 
-static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
+static HRESULT WINAPI
+ShellLink_SetSite( IObjectWithSite *iface, IUnknown *punk )
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
 
-       TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
+    TRACE("%p %p\n", iface, punk);
 
-       if (This->sPath)
-           HeapFree(GetProcessHeap(), 0, This->sPath);
-       if (!(This->sPath = HEAP_strdupWtoA(GetProcessHeap(), 0, pszFile)))
-           return E_OUTOFMEMORY;
+    if ( punk )
+        IUnknown_AddRef( punk );
+    This->site = punk;
 
-       return NOERROR;
+    return S_OK;
 }
 
-/**************************************************************************
-* IShellLinkW Implementation
-*/
-
-static ICOM_VTABLE(IShellLinkW) slvtw =
-{
-       ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
-       IShellLinkW_fnQueryInterface,
-       IShellLinkW_fnAddRef,
-       IShellLinkW_fnRelease,
-       IShellLinkW_fnGetPath,
-       IShellLinkW_fnGetIDList,
-       IShellLinkW_fnSetIDList,
-       IShellLinkW_fnGetDescription,
-       IShellLinkW_fnSetDescription,
-       IShellLinkW_fnGetWorkingDirectory,
-       IShellLinkW_fnSetWorkingDirectory,
-       IShellLinkW_fnGetArguments,
-       IShellLinkW_fnSetArguments,
-       IShellLinkW_fnGetHotkey,
-       IShellLinkW_fnSetHotkey,
-       IShellLinkW_fnGetShowCmd,
-       IShellLinkW_fnSetShowCmd,
-       IShellLinkW_fnGetIconLocation,
-       IShellLinkW_fnSetIconLocation,
-       IShellLinkW_fnSetRelativePath,
-       IShellLinkW_fnResolve,
-       IShellLinkW_fnSetPath
+static const IObjectWithSiteVtbl owsvt =
+{
+    ShellLink_ObjectWithSite_QueryInterface,
+    ShellLink_ObjectWithSite_AddRef,
+    ShellLink_ObjectWithSite_Release,
+    ShellLink_SetSite,
+    ShellLink_GetSite,
 };