Fix by Royce3:
[reactos.git] / reactos / lib / shell32 / shelllink.c
index 4cc02c2..2022532 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
  *   Nearly complete informations 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 <ctype.h>
-#include <string.h>
-#include <stdarg.h>
-#include <stdio.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#include <errno.h>
-#include <limits.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 "shell32_main.h"
 #include "shlguid.h"
 #include "shlwapi.h"
+#include "msi.h"
+#include "appmgmt.h"
+
+#include "initguid.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 
-/* link file formats */
+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);
 
-/* flag1: lnk elements: simple link has 0x0B */
-#define SCF_PIDL   1
-#define SCF_NORMAL 2
-#define SCF_DESCRIPTION 4
-#define SCF_RELATIVE 8
-#define SCF_WORKDIR 0x10
-#define SCF_ARGS 0x20
-#define SCF_CUSTOMICON 0x40
-#define SCF_UNICODE 0x80
+/* link file formats */
 
 #include "pshpack1.h"
 
@@ -110,23 +106,38 @@ typedef struct _LOCAL_VOLUME_INFO
     DWORD dwVolLabelOfs;
 } LOCAL_VOLUME_INFO;
 
+typedef struct volume_info_t
+{
+    DWORD type;
+    DWORD serial;
+    WCHAR label[12];  /* assume 8.3 */
+} volume_info;
+
 #include "poppack.h"
 
-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;
+       const IShellLinkAVtbl *lpVtbl;
+       const IShellLinkWVtbl *lpvtblw;
+       const IPersistFileVtbl *lpvtblPersistFile;
+       const IPersistStreamVtbl *lpvtblPersistStream;
+       const IShellLinkDataListVtbl *lpvtblShellLinkDataList;
+       const IShellExtInitVtbl *lpvtblShellExtInit;
+       const IContextMenuVtbl *lpvtblContextMenu;
+       const IObjectWithSiteVtbl *lpvtblObjectWithSite;
 
-       ICOM_VTABLE(IShellLinkW)*       lpvtblw;
-       ICOM_VTABLE(IPersistFile)*      lpvtblPersistFile;
-       ICOM_VTABLE(IPersistStream)*    lpvtblPersistStream;
+       LONG            ref;
 
        /* data structures according to the informations in the link */
        LPITEMIDLIST    pPidl;
@@ -135,27 +146,57 @@ typedef struct
        SYSTEMTIME      time2;
        SYSTEMTIME      time3;
 
-       DWORD           iShowCmd;
-       LPWSTR          sIcoPath;
-       INT             iIcoNdx;
-       LPWSTR          sPath;
-       LPWSTR          sArgs;
-       LPWSTR          sWorkDir;
-       LPWSTR          sDescription;
-       LPWSTR          sPathRel;
-
-       BOOL            bDirty;
+       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)
+static inline IShellLinkImpl *impl_from_IShellLinkW( IShellLinkW *iface )
+{
+    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblw));
+}
+
+static inline IShellLinkImpl *impl_from_IPersistFile( IPersistFile *iface )
+{
+    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistFile));
+}
+
+static inline IShellLinkImpl *impl_from_IPersistStream( IPersistStream *iface )
+{
+    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistStream));
+}
+
+static inline IShellLinkImpl *impl_from_IShellLinkDataList( IShellLinkDataList *iface )
+{
+    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellLinkDataList));
+}
+
+static inline IShellLinkImpl *impl_from_IShellExtInit( IShellExtInit *iface )
+{
+    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellExtInit));
+}
 
-#define _IPersistFile_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblPersistFile)))
-#define _ICOM_THIS_From_IPersistFile(class, name) class* This = (class*)(((char*)name)-_IPersistFile_Offset)
+static inline IShellLinkImpl *impl_from_IContextMenu( IContextMenu *iface )
+{
+    return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblContextMenu));
+}
 
-#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)
+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);
 
@@ -170,6 +211,118 @@ inline static LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
     return p;
 }
 
+inline static LPWSTR strdupW( LPCWSTR src )
+{
+    LPWSTR dest;
+    if (!src) return NULL;
+    dest = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(src)+1)*sizeof(WCHAR) );
+    if (dest)
+        lstrcpyW(dest, src);
+    return dest;
+}
+
+/**************************************************************************
+ *  ShellLink::QueryInterface implementation
+ */
+static HRESULT ShellLink_QueryInterface( IShellLinkImpl *This, REFIID riid,  LPVOID *ppvObj)
+{
+    TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
+
+    *ppvObj = NULL;
+
+    if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IShellLinkA))
+    {
+        *ppvObj = This;
+    }
+    else if(IsEqualIID(riid, &IID_IShellLinkW))
+    {
+        *ppvObj = &(This->lpvtblw);
+    }
+    else if(IsEqualIID(riid, &IID_IPersistFile))
+    {
+        *ppvObj = &(This->lpvtblPersistFile);
+    }
+    else if(IsEqualIID(riid, &IID_IPersistStream))
+    {
+        *ppvObj = &(This->lpvtblPersistStream);
+    }
+    else if(IsEqualIID(riid, &IID_IShellLinkDataList))
+    {
+        *ppvObj = &(This->lpvtblShellLinkDataList);
+    }
+    else if(IsEqualIID(riid, &IID_IShellExtInit))
+    {
+        *ppvObj = &(This->lpvtblShellExtInit);
+    }
+    else if(IsEqualIID(riid, &IID_IContextMenu))
+    {
+        *ppvObj = &(This->lpvtblContextMenu);
+    }
+    else if(IsEqualIID(riid, &IID_IObjectWithSite))
+    {
+        *ppvObj = &(This->lpvtblObjectWithSite);
+    }
+
+    if(*ppvObj)
+    {
+        IUnknown_AddRef((IUnknown*)(*ppvObj));
+        TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
+        return S_OK;
+    }
+    ERR("-- Interface: E_NOINTERFACE\n");
+    return E_NOINTERFACE;
+}
+
+/**************************************************************************
+ *  ShellLink::AddRef implementation
+ */
+static ULONG ShellLink_AddRef( IShellLinkImpl *This )
+{
+    ULONG refCount = InterlockedIncrement(&This->ref);
+
+    TRACE("(%p)->(count=%lu)\n", This, refCount - 1);
+
+    return refCount;
+}
+
+/**************************************************************************
+ *  ShellLink::Release implementation
+ */
+static ULONG ShellLink_Release( IShellLinkImpl *This )
+{
+    ULONG refCount = InterlockedDecrement(&This->ref);
+
+    TRACE("(%p)->(count=%lu)\n", This, refCount + 1);
+
+    if (refCount)
+        return refCount;
+
+    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);
+
+    return 0;
+}
+
+static HRESULT ShellLink_GetClassID( IShellLinkImpl *This, CLSID *pclsid )
+{
+    TRACE("%p %p\n", This, pclsid);
+
+    memcpy( pclsid, &CLSID_ShellLink, sizeof (CLSID) );
+    return S_OK;
+}
 
 /**************************************************************************
  *  IPersistFile_QueryInterface
@@ -179,11 +332,8 @@ static HRESULT WINAPI IPersistFile_fnQueryInterface(
        REFIID riid,
        LPVOID *ppvObj)
 {
-       _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
-
-       TRACE("(%p)\n",This);
-
-       return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
+    IShellLinkImpl *This = impl_from_IPersistFile(iface);
+    return ShellLink_QueryInterface( This, riid, ppvObj );
 }
 
 /******************************************************************************
@@ -191,33 +341,28 @@ static HRESULT WINAPI IPersistFile_fnQueryInterface(
  */
 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
 {
-       _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
-
-       TRACE("(%p)->(count=%lu)\n",This,This->ref);
-
-       return IShellLinkA_AddRef((IShellLinkA*)This);
+    IShellLinkImpl *This = impl_from_IPersistFile(iface);
+    return ShellLink_AddRef( This );
 }
+
 /******************************************************************************
  * 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);
+    IShellLinkImpl *This = impl_from_IPersistFile(iface);
+    return IShellLinkA_Release((IShellLinkA*)This);
 }
 
 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
 {
-       _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
-       FIXME("(%p)\n",This);
-       return NOERROR;
+    IShellLinkImpl *This = impl_from_IPersistFile(iface);
+    return ShellLink_GetClassID( This, pClassID );
 }
+
 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
 {
-       _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
+       IShellLinkImpl *This = impl_from_IPersistFile(iface);
 
        TRACE("(%p)\n",This);
 
@@ -226,30 +371,35 @@ static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
 
        return S_FALSE;
 }
+
 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
 {
-       _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
-       _IPersistStream_From_ICOM_THIS(IPersistStream, This);
+       IShellLinkImpl *This = impl_from_IPersistFile(iface);
+       IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
         HRESULT r;
         IStream *stm;
 
-        TRACE("(%p, %s)\n",This, debugstr_w(pszFileName));
+        TRACE("(%p, %s, %lx)\n",This, debugstr_w(pszFileName), dwMode);
 
-        r = CreateStreamOnFile(pszFileName, dwMode, &stm);
+        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 %08lx\n", r);
         return r;
 }
 
 static BOOL StartLinkProcessor( LPCOLESTR szLink )
 {
-    const WCHAR szFormat[] = {'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
-                              ' ','-','r',' ','"','%','s','"',0 };
+    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;
@@ -280,25 +430,29 @@ static BOOL StartLinkProcessor( LPCOLESTR szLink )
 
 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
 {
-    _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
-    _IPersistStream_From_ICOM_THIS(IPersistStream, This);
+    IShellLinkImpl *This = impl_from_IPersistFile(iface);
+    IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
     HRESULT r;
     IStream *stm;
 
     TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
 
-    if (!pszFileName || !This->sPath)
-        return ERROR_UNKNOWN;
+    if (!pszFileName)
+        return E_FAIL;
 
-    r = CreateStreamOnFile(pszFileName, STGM_READWRITE | STGM_CREATE, &stm);
+    r = SHCreateStreamOnFileW( pszFileName, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &stm );
     if( SUCCEEDED( r ) )
     {
         r = IPersistStream_Save(StreamThis, stm, FALSE);
         IStream_Release( stm );
 
         if( SUCCEEDED( r ) )
+       {
             StartLinkProcessor( pszFileName );
-        else
+
+            This->bDirty = FALSE;
+        }
+       else
         {
             DeleteFileW( pszFileName );
             WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
@@ -310,20 +464,20 @@ static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFile
 
 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,
@@ -341,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 );
 }
 
 /************************************************************************
@@ -356,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);
 }
 
 /************************************************************************
@@ -369,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 );
 }
 
 /************************************************************************
@@ -384,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 );
 }
 
 /************************************************************************
@@ -402,7 +539,7 @@ 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);
 
@@ -463,65 +600,196 @@ static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
     return S_OK;
 }
 
-static HRESULT Stream_LoadLocation( IStream* stm )
+static HRESULT Stream_ReadChunk( IStream* stm, LPVOID *data )
 {
     DWORD size;
     ULONG count;
     HRESULT r;
-    LOCATION_INFO *loc;
+    struct sized_chunk {
+        DWORD size;
+        unsigned char data[1];
+    } *chunk;
 
     TRACE("%p\n",stm);
 
     r = IStream_Read( stm, &size, sizeof(size), &count );
-    if( FAILED( r ) )
-        return r;
-    if( count != sizeof(loc->dwTotalSize) )
+    if( FAILED( r )  || count != sizeof(size) )
         return E_FAIL;
 
-    loc = HeapAlloc( GetProcessHeap(), 0, size );
-    if( ! loc )
+    chunk = HeapAlloc( GetProcessHeap(), 0, size );
+    if( !chunk )
         return E_OUTOFMEMORY;
 
-    r = IStream_Read( stm, &loc->dwHeaderSize, size-sizeof(size), &count );
+    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;
+    }
+
+    TRACE("Read %ld bytes\n",chunk->size);
+
+    *data = (LPVOID) chunk;
+
+    return S_OK;
+}
+
+static BOOL Stream_LoadVolume( LOCAL_VOLUME_INFO *vol, volume_info *volume )
+{
+    const int label_sz = sizeof volume->label/sizeof volume->label[0];
+    LPSTR label;
+    int len;
+
+    volume->serial = vol->dwVolSerial;
+    volume->type = vol->dwType;
+
+    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;
+}
+
+static LPWSTR Stream_LoadPath( LPSTR p, DWORD maxlen )
+{
+    int len = 0, wlen;
+    LPWSTR path;
+
+    while( p[len] && (len < maxlen) )
+        len++;
+
+    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 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) - sizeof(DWORD))
+    {
+        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 %ld serial %08lx 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 ) )
-        goto end;
-    if( count != (size - sizeof(size)) )
+        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 )
     {
-        r = E_FAIL;
-        goto end;
+        ERR("Ooops.  This structure is not as expected...\n");
+        return E_FAIL;
     }
-    loc->dwTotalSize = size;
 
-    TRACE("Read %ld bytes\n",count);
+    r = IStream_Read( stm, &buffer.dbh.dwSignature, size, &count );
+    if( FAILED( r ) )
+        return r;
+
+    if( count != size )
+        return E_FAIL;
+
+    TRACE("magic %08lx  string = %s\n", buffer.dbh.dwSignature, debugstr_w(buffer.szwDarwinID));
+
+    if( (buffer.dbh.dwSignature&0xffff0000) != 0xa0000000 )
+    {
+        ERR("Unknown magic number %08lx in advertised shortcut\n", buffer.dbh.dwSignature);
+        return E_FAIL;
+    }
 
-    /* FIXME: do something useful with it */
-    HeapFree( GetProcessHeap(), 0, loc );
+    *str = HeapAlloc( GetProcessHeap(), 0, 
+                     (lstrlenW(buffer.szwDarwinID)+1) * sizeof(WCHAR) );
+    lstrcpyW( *str, buffer.szwDarwinID );
 
     return S_OK;
-end:
-    HeapFree( GetProcessHeap(), 0, loc );
-    return r;
 }
 
 /************************************************************************
  * IPersistStream_Load (IPersistStream)
  */
 static HRESULT WINAPI IPersistStream_fnLoad(
-       IPersistStream*  iface,
+    IPersistStream*  iface,
     IStream*         stm)
 {
     LINK_HEADER hdr;
     ULONG    dwBytesRead;
     BOOL     unicode;
-    WCHAR    sTemp[MAX_PATH];
     HRESULT  r;
+    DWORD    zero;
 
-    _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IPersistStream(iface);
 
-    TRACE("(%p)(%p)\n", This, stm);
+    TRACE("%p %p\n", This, stm);
 
     if( !stm )
-       return STG_E_INVALIDPOINTER;
+        return STG_E_INVALIDPOINTER;
 
     dwBytesRead = 0;
     r = IStream_Read(stm, &hdr, sizeof(hdr), &dwBytesRead);
@@ -535,32 +803,63 @@ static HRESULT WINAPI IPersistStream_fnLoad(
     if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
         return E_FAIL;
 
-    if( hdr.dwFlags & SCF_PIDL )
-    {
-        r = ILLoadFromStream( stm, &This->pPidl );
-        if( FAILED( r ) )
-            return r;
-    }
+    /* 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 1
-    GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time1, NULL, sTemp, 256);
-    TRACE("-- time1: %s\n", debugstr_w(sTemp) );
-    GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time2, NULL, sTemp, 256);
-    TRACE("-- time1: %s\n", debugstr_w(sTemp) );
-    GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time3, NULL, sTemp, 256);
-    TRACE("-- time1: %s\n", debugstr_w(sTemp) );
-    pdump (This->pPidl);
-#endif
-    if( hdr.dwFlags & SCF_NORMAL )
-        r = Stream_LoadLocation( stm );
+    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 & SCF_UNICODE;
-    if( hdr.dwFlags & SCF_DESCRIPTION )
+
+    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));
@@ -568,7 +867,7 @@ static HRESULT WINAPI IPersistStream_fnLoad(
     if( FAILED( r ) )
         goto end;
 
-    if( hdr.dwFlags & SCF_RELATIVE )
+    if( hdr.dwFlags & SLDF_HAS_RELPATH )
     {
         r = Stream_LoadString( stm, unicode, &This->sPathRel );
         TRACE("Relative Path-> %s\n",debugstr_w(This->sPathRel));
@@ -576,7 +875,7 @@ static HRESULT WINAPI IPersistStream_fnLoad(
     if( FAILED( r ) )
         goto end;
 
-    if( hdr.dwFlags & SCF_WORKDIR )
+    if( hdr.dwFlags & SLDF_HAS_WORKINGDIR )
     {
         r = Stream_LoadString( stm, unicode, &This->sWorkDir );
         TRACE("Working Dir  -> %s\n",debugstr_w(This->sWorkDir));
@@ -584,7 +883,7 @@ static HRESULT WINAPI IPersistStream_fnLoad(
     if( FAILED( r ) )
         goto end;
 
-    if( hdr.dwFlags & SCF_ARGS )
+    if( hdr.dwFlags & SLDF_HAS_ARGS )
     {
         r = Stream_LoadString( stm, unicode, &This->sArgs );
         TRACE("Working Dir  -> %s\n",debugstr_w(This->sArgs));
@@ -592,7 +891,7 @@ static HRESULT WINAPI IPersistStream_fnLoad(
     if( FAILED( r ) )
         goto end;
 
-    if( hdr.dwFlags & SCF_CUSTOMICON )
+    if( hdr.dwFlags & SLDF_HAS_ICONLOCATION )
     {
         r = Stream_LoadString( stm, unicode, &This->sIcoPath );
         TRACE("Icon file    -> %s\n",debugstr_w(This->sIcoPath));
@@ -600,6 +899,26 @@ static HRESULT WINAPI IPersistStream_fnLoad(
     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);
@@ -634,19 +953,90 @@ static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
     return S_OK;
 }
 
-static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR filename )
+/************************************************************************
+ * 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 )
 {
-    LOCATION_INFO loc;
+    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);
 
-    FIXME("writing empty location info\n");
-
-    memset( &loc, 0, sizeof(loc) );
-    loc.dwTotalSize = sizeof(loc) - sizeof(loc.dwTotalSize);
-
-    /* FIXME: fill this in */
+    buffer = shelllink_build_darwinid( string, magic );
 
-    return IStream_Write( stm, &loc, loc.dwTotalSize, &count );
+    return IStream_Write( stm, buffer, buffer->dbh.cbSize, &count );
 }
 
 /************************************************************************
@@ -664,44 +1054,51 @@ static HRESULT WINAPI IPersistStream_fnSave(
     LINK_HEADER header;
     WCHAR   exePath[MAX_PATH];
     ULONG   count;
+    DWORD   zero;
     HRESULT r;
 
-    _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IPersistStream(iface);
 
-    TRACE("(%p) %p %x\n", This, stm, fClearDirty);
+    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);
-
-    /* if there's no PIDL, generate one */
-    if( ! This->pPidl )
     {
-        if( !*exePath )
-            return E_FAIL;
-
-        This->pPidl = ILCreateFromPathW(exePath);
+        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 = SCF_UNICODE;   /* strings are in unicode */
-    header.dwFlags |= SCF_NORMAL;   /* how do we determine this ? */
+    header.dwFlags = SLDF_UNICODE;   /* strings are in unicode */
     if( This->pPidl )
-        header.dwFlags |= SCF_PIDL;
+        header.dwFlags |= SLDF_HAS_ID_LIST;
+    if( This->sPath )
+        header.dwFlags |= SLDF_HAS_LINK_INFO;
     if( This->sDescription )
-        header.dwFlags |= SCF_DESCRIPTION;
+        header.dwFlags |= SLDF_HAS_NAME;
     if( This->sWorkDir )
-        header.dwFlags |= SCF_WORKDIR;
+        header.dwFlags |= SLDF_HAS_WORKINGDIR;
     if( This->sArgs )
-        header.dwFlags |= SCF_ARGS;
+        header.dwFlags |= SLDF_HAS_ARGS;
     if( This->sIcoPath )
-        header.dwFlags |= SCF_CUSTOMICON;
+        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 );
@@ -715,7 +1112,7 @@ static HRESULT WINAPI IPersistStream_fnSave(
         return r;
     }
 
-    TRACE("Writing pidl \n");
+    TRACE("Writing pidl\n");
 
     /* write the PIDL to the shortcut */
     if( This->pPidl )
@@ -728,9 +1125,9 @@ static HRESULT WINAPI IPersistStream_fnSave(
         }
     }
 
-    Stream_WriteLocationInfo( stm, exePath );
+    if( This->sPath )
+        Stream_WriteLocationInfo( stm, exePath, &This->volume );
 
-    TRACE("Description = %s\n", debugstr_w(This->sDescription));
     if( This->sDescription )
         r = Stream_WriteString( stm, This->sDescription );
 
@@ -746,6 +1143,16 @@ static HRESULT WINAPI IPersistStream_fnSave(
     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;
 }
 
@@ -756,16 +1163,15 @@ static HRESULT WINAPI IPersistStream_fnGetSizeMax(
        IPersistStream*  iface,
        ULARGE_INTEGER*  pcbSize)
 {
-       _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
+       IShellLinkImpl *This = impl_from_IPersistStream(iface);
 
        TRACE("(%p)\n", This);
 
        return E_NOTIMPL;
 }
 
-static ICOM_VTABLE(IPersistStream) psvt =
+static const IPersistStreamVtbl psvt =
 {
-       ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
        IPersistStream_fnQueryInterface,
        IPersistStream_fnAddRef,
        IPersistStream_fnRelease,
@@ -779,54 +1185,49 @@ 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;
        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_IUnknown) ||
-           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;
-       }
-
-       return S_OK;
+        r = ShellLink_QueryInterface( sl, riid, ppv );
+        ShellLink_Release( sl );
+        return r;
 }
 
+
 static BOOL SHELL_ExistsFileW(LPCWSTR path)
 {
-    HANDLE hfile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
-
-    if (hfile != INVALID_HANDLE_VALUE) {
-       CloseHandle(hfile);
-       return TRUE;
-    } else
+    if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
         return FALSE;
+    return TRUE;
 }
 
 /**************************************************************************
@@ -840,22 +1241,14 @@ static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWork
 
     if (!*psPath && sPathRel) {
        WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
+       LPWSTR final = NULL;
 
        /* first try if [directory of link file] + [relative path] finds an existing file */
-       LPCWSTR src = path;
-       LPWSTR last_slash = NULL;
-       LPWSTR dest = buffer;
-       LPWSTR final;
-
-       /* copy path without file name to buffer */
-       while(*src) {
-           if (*src=='/' || *src=='\\')
-               last_slash = dest;
-
-           *dest++ = *src++;
-       }
 
-       lstrcpyW(last_slash? last_slash+1: buffer, sPathRel);
+        GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
+        if( !final )
+            final = buffer;
+       lstrcpyW(final, sPathRel);
 
        *abs_path = '\0';
 
@@ -891,12 +1284,8 @@ static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWork
 /**************************************************************************
  *       IShellLink_ConstructFromFile
  */
-HRESULT WINAPI IShellLink_ConstructFromFile (
-       IUnknown* pUnkOuter,
-       REFIID riid,
-       LPCITEMIDLIST pidl,
-       LPVOID* ppv
-)
+HRESULT WINAPI IShellLink_ConstructFromFile( IUnknown* pUnkOuter, REFIID riid,
+               LPCITEMIDLIST pidl, LPVOID* ppv)
 {
     IShellLinkW* psl;
 
@@ -912,14 +1301,13 @@ HRESULT WINAPI IShellLink_ConstructFromFile (
        if (SUCCEEDED(hr)) {
            WCHAR path[MAX_PATH];
 
-           hr = SHELL_GetPathFromIDListW(pidl, path, MAX_PATH);
-
-           if (SUCCEEDED(hr)) {
+           if (SHGetPathFromIDListW(pidl, path)) 
                hr = IPersistFile_Load(ppf, path, 0);
+            else
+                hr = E_FAIL;
 
-               if (SUCCEEDED(hr))
-                   *ppv = (IUnknown*) psl;
-           }
+           if (SUCCEEDED(hr))
+               *ppv = (IUnknown*) psl;
 
            IPersistFile_Release(ppf);
        }
@@ -934,52 +1322,19 @@ HRESULT WINAPI IShellLink_ConstructFromFile (
 /**************************************************************************
  *  IShellLinkA_QueryInterface
  */
-static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid, LPVOID *ppvObj)
+static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
-
-       TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
-
-       *ppvObj = NULL;
-
-       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);
-       }
-
-       if(*ppvObj)
-       {
-         IUnknown_AddRef((IUnknown*)(*ppvObj));
-         TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
-         return S_OK;
-       }
-       TRACE("-- Interface: E_NOINTERFACE\n");
-       return E_NOINTERFACE;
-}
+    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 );
 }
 
 /******************************************************************************
@@ -987,81 +1342,49 @@ static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
  */
 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
-
-    TRACE("(%p)->(count=%lu)\n",This,This->ref);
-
-    if (--(This->ref))
-        return This->ref;
-
-    TRACE("-- destroying IShellLink(%p)\n",This);
-
-    if (This->sIcoPath)
-        HeapFree(GetProcessHeap(), 0, This->sIcoPath);
-
-    if (This->sArgs)
-        HeapFree(GetProcessHeap(), 0, This->sArgs);
-
-    if (This->sWorkDir)
-        HeapFree(GetProcessHeap(), 0, This->sWorkDir);
-
-    if (This->sDescription)
-        HeapFree(GetProcessHeap(), 0, This->sDescription);
-
-    if (This->sPath)
-        HeapFree(GetProcessHeap(), 0, This->sPath);
-
-    if (This->pPidl)
-        ILFree(This->pPidl);
-
-    LocalFree((HANDLE)This);
-
-    return 0;
+    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)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
 
-    if( cchMaxPath )
+    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);
-
-       }
+    if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
 
-    return NOERROR;
+    return S_OK;
 }
 
 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
-
-       TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
-       *ppidl = ILClone(This->pPidl);
+    TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
 
-       return NOERROR;
+    return IShellLinkW_GetIDList((IShellLinkW*)&(This->lpvtblw), ppidl);
 }
 
 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p)->(pidl=%p)\n",This, pidl);
 
     if (This->pPidl)
-        ILFree(This->pPidl);
-
+       ILFree(This->pPidl);
     This->pPidl = ILClone (pidl);
     This->bDirty = TRUE;
 
@@ -1070,7 +1393,7 @@ static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST
 
 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
 
@@ -1082,14 +1405,14 @@ static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR ps
 
     return S_OK;
 }
+
 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p)->(pName=%s)\n", This, pszName);
 
-    if (This->sDescription)
-        HeapFree(GetProcessHeap(), 0, This->sDescription);
+    HeapFree(GetProcessHeap(), 0, This->sDescription);
     This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
     if ( !This->sDescription )
         return E_OUTOFMEMORY;
@@ -1101,7 +1424,7 @@ static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR p
 
 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
 
@@ -1116,12 +1439,11 @@ static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPS
 
 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p)->(dir=%s)\n",This, pszDir);
 
-    if (This->sWorkDir)
-        HeapFree(GetProcessHeap(), 0, This->sWorkDir);
+    HeapFree(GetProcessHeap(), 0, This->sWorkDir);
     This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
     if ( !This->sWorkDir )
         return E_OUTOFMEMORY;
@@ -1133,7 +1455,7 @@ static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPC
 
 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
 
@@ -1148,12 +1470,11 @@ static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszA
 
 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p)->(args=%s)\n",This, pszArgs);
 
-    if (This->sArgs)
-        HeapFree(GetProcessHeap(), 0, This->sArgs);
+    HeapFree(GetProcessHeap(), 0, This->sArgs);
     This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
     if( !This->sArgs )
         return E_OUTOFMEMORY;
@@ -1165,18 +1486,18 @@ static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR psz
 
 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
 {
-       ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
-       TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
+    TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
 
-       *pwHotkey = This->wHotKey;
+    *pwHotkey = This->wHotKey;
 
-       return S_OK;
+    return S_OK;
 }
 
 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
 
@@ -1188,7 +1509,7 @@ static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
 
 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p)->(%p)\n",This, piShowCmd);
     *piShowCmd = This->iShowCmd;
@@ -1197,7 +1518,7 @@ static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowC
 
 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p) %d\n",This, iShowCmd);
 
@@ -1230,27 +1551,29 @@ static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPITEMIDLIST pidl,
     return hr;
 }
 
-static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath, INT cchIconPath, INT *piIcon)
+static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
 
-    if (cchIconPath)
-        pszIconPath[0] = 0;
+    pszIconPath[0] = 0;
+    *piIcon = This->iIcoNdx;
 
-    if (This->sIcoPath) {
+    if (This->sIcoPath)
+    {
         WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
-       *piIcon = This->iIcoNdx;
        return S_OK;
     }
 
-    if (This->pPidl || This->sPath) {
+    if (This->pPidl || This->sPath)
+    {
        IShellFolder* pdsk;
 
        HRESULT hr = SHGetDesktopFolder(&pdsk);
 
-       if (SUCCEEDED(hr)) {
+       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);
@@ -1258,7 +1581,8 @@ static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR p
                hr = E_FAIL;
 
            /* if we couldn't find an icon yet, look for it using the file system path */
-           if (FAILED(hr) && This->sPath) {
+           if (FAILED(hr) && This->sPath)
+            {
                LPITEMIDLIST pidl;
 
                hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
@@ -1274,18 +1598,17 @@ static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR p
        }
 
        return hr;
-    } else
-        return E_FAIL;
+    }
+    return S_OK;
 }
 
-static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath, INT iIcon)
+static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
 
-    if (This->sIcoPath)
-        HeapFree(GetProcessHeap(), 0, This->sIcoPath);
+    HeapFree(GetProcessHeap(), 0, This->sIcoPath);
     This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
     if ( !This->sIcoPath )
         return E_OUTOFMEMORY;
@@ -1298,13 +1621,11 @@ static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR
 
 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
-
-    FIXME("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved);
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
-    if (This->sPathRel)
-        HeapFree(GetProcessHeap(), 0, This->sPathRel);
+    TRACE("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved);
 
+    HeapFree(GetProcessHeap(), 0, This->sPathRel);
     This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
     This->bDirty = TRUE;
 
@@ -1313,96 +1634,58 @@ static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR
 
 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
 {
-    HRESULT hr = S_OK;
-
-    ICOM_THIS(IShellLinkImpl, iface);
-
-    FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
-
-    /*FIXME: use IResolveShellLink interface */
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
-    if (!This->sPath && This->pPidl) {
-       WCHAR buffer[MAX_PATH];
-
-       hr = SHELL_GetPathFromIDListW(This->pPidl, buffer, MAX_PATH);
-
-       if (SUCCEEDED(hr) && *buffer) {
-           This->sPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
-           if (!This->sPath)
-               return E_OUTOFMEMORY;
-
-           lstrcpyW(This->sPath, buffer);
-
-           This->bDirty = TRUE;
-       } else
-           hr = S_OK;    /* don't report any error occured while just caching information */
-    }
-
-    if (!This->sIcoPath && This->sPath) {
-       This->sIcoPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
-       if (!This->sIcoPath)
-           return E_OUTOFMEMORY;
-
-       lstrcpyW(This->sIcoPath, This->sPath);
-       This->iIcoNdx = 0;
-
-       This->bDirty = TRUE;
-    }
+    TRACE("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
 
-    return hr;
+    return IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, fFlags );
 }
 
 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
 {
-    ICOM_THIS(IShellLinkImpl, iface);
-    char buffer[MAX_PATH];
-    LPSTR fname;
+    HRESULT r;
+    LPWSTR str;
+    IShellLinkImpl *This = (IShellLinkImpl *)iface;
 
     TRACE("(%p)->(path=%s)\n",This, pszFile);
 
-    if (!GetFullPathNameA(pszFile, MAX_PATH, buffer, &fname))
-       return E_FAIL;
-
-    if (This->sPath)
-        HeapFree(GetProcessHeap(), 0, This->sPath);
-
-    This->sPath = HEAP_strdupAtoW(GetProcessHeap(), 0, buffer);
-    if( !This->sPath )
+    str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
+    if( !str ) 
         return E_OUTOFMEMORY;
 
-    This->bDirty = TRUE;
+    r = IShellLinkW_SetPath((IShellLinkW*)&(This->lpvtblw), str);
+    HeapFree( GetProcessHeap(), 0, str );
 
-    return S_OK;
+    return r;
 }
 
 /**************************************************************************
 * IShellLink 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 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
 };
 
 
@@ -1412,9 +1695,8 @@ static ICOM_VTABLE(IShellLinkA) slvt =
 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
-
-       return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+    return ShellLink_QueryInterface( This, riid, ppvObj );
 }
 
 /******************************************************************************
@@ -1422,63 +1704,54 @@ static HRESULT WINAPI IShellLinkW_fnQueryInterface(
  */
 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
-
-       TRACE("(%p)->(count=%lu)\n",This,This->ref);
-
-       return IShellLinkA_AddRef((IShellLinkA*)This);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
+    return ShellLink_AddRef( This );
 }
+
 /******************************************************************************
  * IShellLinkW_fnRelease
  */
-
 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
 {
-       _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
-
-       TRACE("(%p)->(count=%lu)\n",This,This->ref);
-
-       return IShellLinkA_Release((IShellLinkA*)This);
+    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)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
-    TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)\n",
-                 This, pszFile, cchMaxPath, pfd, fFlags);
+    TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
+          This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
 
-    if( cchMaxPath )
+    if (This->sComponent || This->sProduct)
+        return S_FALSE;
+
+    if (cchMaxPath)
         pszFile[0] = 0;
-    if( This->sPath )
+    if (This->sPath)
         lstrcpynW( pszFile, This->sPath, cchMaxPath );
 
-       if (pfd) {
-
-               FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
-
-       }
+    if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
 
-    return NOERROR;
+    return S_OK;
 }
 
 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
 
-    if( This->pPidl)
-        *ppidl = ILClone( This->pPidl );
-    else
-        *ppidl = NULL;
-
+    if (!This->pPidl)
+        return S_FALSE;
+    *ppidl = ILClone(This->pPidl);
     return S_OK;
 }
 
 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(pidl=%p)\n",This, pidl);
 
@@ -1495,12 +1768,11 @@ static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST
 
 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
 
-    if( cchMaxName )
-        pszName[0] = 0;
+    pszName[0] = 0;
     if( This->sDescription )
         lstrcpynW( pszName, This->sDescription, cchMaxName );
 
@@ -1509,12 +1781,11 @@ static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR p
 
 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
 
-    if (This->sDescription)
-        HeapFree(GetProcessHeap(), 0, This->sDescription);
+    HeapFree(GetProcessHeap(), 0, This->sDescription);
     This->sDescription = HeapAlloc( GetProcessHeap(), 0,
                                     (lstrlenW( pszName )+1)*sizeof(WCHAR) );
     if ( !This->sDescription )
@@ -1528,7 +1799,7 @@ static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR
 
 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
 
@@ -1542,17 +1813,15 @@ static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPW
 
 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
 
-    if (This->sWorkDir)
-        HeapFree(GetProcessHeap(), 0, This->sWorkDir);
+    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;
 
@@ -1561,7 +1830,7 @@ static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPC
 
 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
 
@@ -1575,17 +1844,15 @@ static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR psz
 
 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
 
-    if (This->sArgs)
-        HeapFree(GetProcessHeap(), 0, This->sArgs);
+    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;
 
@@ -1594,7 +1861,7 @@ static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR ps
 
 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(%p)\n",This, pwHotkey);
 
@@ -1605,7 +1872,7 @@ static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotke
 
 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
 
@@ -1617,7 +1884,7 @@ static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
 
 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(%p)\n",This, piShowCmd);
 
@@ -1628,7 +1895,7 @@ static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowC
 
 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     This->iShowCmd = iShowCmd;
     This->bDirty = TRUE;
@@ -1659,27 +1926,29 @@ static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPITEMIDLIST pidl,
     return hr;
 }
 
-static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath, INT cchIconPath, INT *piIcon)
+static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
 
-    if (cchIconPath)
-        pszIconPath[0] = 0;
+    pszIconPath[0] = 0;
+    *piIcon = This->iIcoNdx;
 
-    if (This->sIcoPath) {
+    if (This->sIcoPath)
+    {
        lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
-       *piIcon = This->iIcoNdx;
        return S_OK;
     }
 
-    if (This->pPidl || This->sPath) {
+    if (This->pPidl || This->sPath)
+    {
        IShellFolder* pdsk;
 
        HRESULT hr = SHGetDesktopFolder(&pdsk);
 
-       if (SUCCEEDED(hr)) {
+       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);
@@ -1687,12 +1956,14 @@ static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR
                hr = E_FAIL;
 
            /* if we couldn't find an icon yet, look for it using the file system path */
-           if (FAILED(hr) && This->sPath) {
+           if (FAILED(hr) && This->sPath)
+            {
                LPITEMIDLIST pidl;
 
                hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
 
-               if (SUCCEEDED(hr)) {
+               if (SUCCEEDED(hr))
+                {
                    hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
 
                    SHFree(pidl);
@@ -1701,20 +1972,18 @@ static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR
 
            IShellFolder_Release(pdsk);
        }
-
        return hr;
-    } else
-        return E_FAIL;
+    }
+    return S_OK;
 }
 
-static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath, INT iIcon)
+static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
 
-    if (This->sIcoPath)
-        HeapFree(GetProcessHeap(), 0, This->sIcoPath);
+    HeapFree(GetProcessHeap(), 0, This->sIcoPath);
     This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
                                 (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
     if ( !This->sIcoPath )
@@ -1729,17 +1998,15 @@ static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR
 
 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
 {
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
     TRACE("(%p)->(path=%s %lx)\n",This, debugstr_w(pszPathRel), dwReserved);
 
-    if (This->sPathRel)
-        HeapFree(GetProcessHeap(), 0, This->sPathRel);
+    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;
 
@@ -1749,20 +2016,21 @@ static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR
 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
 {
     HRESULT hr = S_OK;
+    BOOL bSuccess;
 
-    _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
 
-    FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
+    TRACE("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
 
     /*FIXME: use IResolveShellLink interface */
 
     if (!This->sPath && This->pPidl) {
        WCHAR buffer[MAX_PATH];
 
-       hr = SHELL_GetPathFromIDListW(This->pPidl, buffer, MAX_PATH);
+       bSuccess = SHGetPathFromIDListW(This->pPidl, buffer);
 
-       if (SUCCEEDED(hr) && *buffer) {
-           This->sPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
+       if (bSuccess && *buffer) {
+           This->sPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
            if (!This->sPath)
                return E_OUTOFMEMORY;
 
@@ -1770,11 +2038,11 @@ static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWOR
 
            This->bDirty = TRUE;
        } else
-           hr = S_OK;    /* don't report any error occured while just caching information */
+           hr = S_OK;    /* don't report an error occurred while just caching information */
     }
 
     if (!This->sIcoPath && This->sPath) {
-       This->sIcoPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
+       This->sIcoPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
        if (!This->sIcoPath)
            return E_OUTOFMEMORY;
 
@@ -1787,57 +2055,593 @@ static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWOR
     return hr;
 }
 
+static LPWSTR ShellLink_GetAdvertisedArg(LPCWSTR str)
+{
+    LPWSTR ret;
+    LPCWSTR p;
+    DWORD len;
+
+    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 ShellLink_SetAdvertiseInfo(IShellLinkImpl *This, LPCWSTR str)
+{
+    LPCWSTR szComponent = NULL, szProduct = NULL, p;
+    WCHAR szGuid[39];
+    HRESULT r;
+    GUID guid;
+    int len;
+
+    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 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 %ld serial %08lx 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_From_IShellLinkW(IShellLinkImpl, iface);
+    IShellLinkImpl *This = impl_from_IShellLinkW(iface);
     WCHAR buffer[MAX_PATH];
     LPWSTR fname;
+    HRESULT hr = S_OK;
 
     TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
 
-    if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
-       return E_FAIL;
+    HeapFree(GetProcessHeap(), 0, This->sPath);
+    This->sPath = NULL;
 
-    if (This->sPath)
-        HeapFree(GetProcessHeap(), 0, This->sPath);
+    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))
+            hr = S_FALSE;
 
-    This->sPath = HeapAlloc( GetProcessHeap(), 0,
+        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;
+        if (!This->sPath)
+            return E_OUTOFMEMORY;
 
-    lstrcpyW( This->sPath, buffer );
+        lstrcpyW(This->sPath, buffer);
+    }
     This->bDirty = TRUE;
 
-    return S_OK;
+    return hr;
 }
 
 /**************************************************************************
 * 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 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
+};
+
+static HRESULT WINAPI
+ShellLink_DataList_QueryInterface( IShellLinkDataList* iface, REFIID riid, void** ppvObject)
+{
+    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+    return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
+}
+
+static ULONG WINAPI
+ShellLink_DataList_AddRef( IShellLinkDataList* iface )
+{
+    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+    return IShellLinkA_AddRef((IShellLinkA*)This);
+}
+
+static ULONG WINAPI
+ShellLink_DataList_Release( IShellLinkDataList* iface )
+{
+    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+    return ShellLink_Release( This );
+}
+
+static HRESULT WINAPI
+ShellLink_AddDataBlock( IShellLinkDataList* iface, void* pDataBlock )
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+ShellLink_CopyDataBlock( IShellLinkDataList* iface, DWORD dwSig, void** ppDataBlock )
+{
+    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+    LPVOID block = NULL;
+    HRESULT r = E_FAIL;
+
+    TRACE("%p %08lx %p\n", iface, dwSig, ppDataBlock );
+
+    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 %08lx\n", dwSig);
+        break;
+    default:
+        ERR("unknown datablock %08lx\n", dwSig);
+    }
+    *ppDataBlock = block;
+    return r;
+}
+
+static HRESULT WINAPI
+ShellLink_RemoveDataBlock( IShellLinkDataList* iface, DWORD dwSig )
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+ShellLink_GetFlags( IShellLinkDataList* iface, DWORD* pdwFlags )
+{
+    IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
+    DWORD flags = 0;
+
+    FIXME("%p %p\n", This, pdwFlags );
+
+    /* 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;
+
+    *pdwFlags = flags;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI
+ShellLink_SetFlags( IShellLinkDataList* iface, DWORD dwFlags )
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static const IShellLinkDataListVtbl dlvt =
+{
+    ShellLink_DataList_QueryInterface,
+    ShellLink_DataList_AddRef,
+    ShellLink_DataList_Release,
+    ShellLink_AddDataBlock,
+    ShellLink_CopyDataBlock,
+    ShellLink_RemoveDataBlock,
+    ShellLink_GetFlags,
+    ShellLink_SetFlags
+};
+
+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);
+}
+
+static ULONG WINAPI
+ShellLink_ExtInit_AddRef( IShellExtInit* iface )
+{
+    IShellLinkImpl *This = impl_from_IShellExtInit(iface);
+    return IShellLinkA_AddRef((IShellLinkA*)This);
+}
+
+static ULONG WINAPI
+ShellLink_ExtInit_Release( IShellExtInit* iface )
+{
+    IShellLinkImpl *This = impl_from_IShellExtInit(iface);
+    return ShellLink_Release( This );
+}
+
+/**************************************************************************
+ * 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 )
+{
+    IShellLinkImpl *This = impl_from_IShellExtInit(iface);
+    FORMATETC format;
+    STGMEDIUM stgm;
+    UINT count;
+    HRESULT r = E_FAIL;
+
+    TRACE("%p %p %p %p\n", This, pidlFolder, pdtobj, hkeyProgID );
+
+    if( !pdtobj )
+        return r;
+
+    format.cfFormat = CF_HDROP;
+    format.ptd = NULL;
+    format.dwAspect = DVASPECT_CONTENT;
+    format.lindex = -1;
+    format.tymed = TYMED_HGLOBAL;
+
+    if( FAILED( IDataObject_GetData( pdtobj, &format, &stgm ) ) )
+        return r;
+
+    count = DragQueryFileW( stgm.u.hGlobal, -1, NULL, 0 );
+    if( count == 1 )
+    {
+        LPWSTR path;
+
+        count = DragQueryFileW( stgm.u.hGlobal, 0, NULL, 0 );
+        count++;
+        path = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
+        if( path )
+        {
+            IPersistFile *pf = (IPersistFile*) &This->lpvtblPersistFile;
+
+            count = DragQueryFileW( stgm.u.hGlobal, 0, path, count );
+            r = IPersistFile_Load( pf, path, 0 );
+            HeapFree( GetProcessHeap(), 0, path );
+        }
+    }
+    ReleaseStgMedium( &stgm );
+
+    return r;
+}
+
+static const IShellExtInitVtbl eivt =
+{
+    ShellLink_ExtInit_QueryInterface,
+    ShellLink_ExtInit_AddRef,
+    ShellLink_ExtInit_Release,
+    ShellLink_ExtInit_Initialize
+};
+
+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);
+}
+
+static ULONG WINAPI
+ShellLink_ContextMenu_AddRef( IContextMenu* iface )
+{
+    IShellLinkImpl *This = impl_from_IContextMenu(iface);
+    return IShellLinkA_AddRef((IShellLinkA*)This);
+}
+
+static ULONG WINAPI
+ShellLink_ContextMenu_Release( IContextMenu* iface )
+{
+    IShellLinkImpl *This = impl_from_IContextMenu(iface);
+    return ShellLink_Release( This );
+}
+
+static HRESULT WINAPI
+ShellLink_QueryContextMenu( IContextMenu* iface, HMENU hmenu, UINT indexMenu,
+                            UINT idCmdFirst, UINT idCmdLast, UINT uFlags )
+{
+    IShellLinkImpl *This = impl_from_IContextMenu(iface);
+    static const WCHAR szOpen[] = { 'O','p','e','n',0 };
+    MENUITEMINFOW mii;
+    int id = 1;
+
+    TRACE("%p %p %u %u %u %u\n", This,
+          hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
+
+    if ( !hmenu )
+        return E_INVALIDARG;
+
+    memset( &mii, 0, sizeof mii );
+    mii.cbSize = sizeof mii;
+    mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+    mii.dwTypeData = (LPWSTR)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 LPWSTR
+shelllink_get_msi_component_path( LPWSTR component )
+{
+    LPWSTR path = NULL;
+    DWORD r, sz = 0;
+
+    r = CommandLineFromMsiDescriptor( component, NULL, &sz );
+    if (r != ERROR_SUCCESS)
+         return path;
+
+    sz++;
+    path = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
+    r = CommandLineFromMsiDescriptor( component, path, &sz );
+    if (r != ERROR_SUCCESS)
+    {
+        HeapFree( GetProcessHeap(), 0, path );
+        path = NULL;
+    }
+
+    TRACE("returning %s\n", debugstr_w( path ) );
+
+    return path;
+}
+
+static HRESULT WINAPI
+ShellLink_InvokeCommand( IContextMenu* iface, LPCMINVOKECOMMANDINFO lpici )
+{
+    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;
+
+    TRACE("%p %p\n", This, lpici );
+
+    if ( lpici->cbSize < sizeof (CMINVOKECOMMANDINFO) )
+        return E_INVALIDARG;
+
+    if ( lpici->lpVerb != MAKEINTRESOURCEA(This->iIdOpen) )
+    {
+        ERR("Unknown id %d != %d\n", (INT)lpici->lpVerb, This->iIdOpen );
+        return E_INVALIDARG;
+    }
+
+    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 );
+        }
+    }
+
+    memset( &sei, 0, sizeof sei );
+    sei.cbSize = sizeof sei;
+    sei.fMask = SEE_MASK_UNICODE;
+    sei.lpFile = path;
+    sei.nShow = This->iShowCmd;
+    sei.lpIDList = This->pPidl;
+    sei.lpDirectory = This->sWorkDir;
+    sei.lpParameters = args;
+    sei.lpVerb = szOpen;
+
+    if ( ShellExecuteExW( &sei ) && sei.hInstApp > 32 )
+        r = S_OK;
+    else
+        r = E_FAIL;
+
+    HeapFree( GetProcessHeap(), 0, args );
+    HeapFree( GetProcessHeap(), 0, path );
+
+    return r;
+}
+
+static HRESULT WINAPI
+ShellLink_GetCommandString( IContextMenu* iface, UINT_PTR idCmd, UINT uType,
+                            UINT* pwReserved, LPSTR pszName, UINT cchMax )
+{
+    IShellLinkImpl *This = impl_from_IContextMenu(iface);
+
+    FIXME("%p %u %u %p %p %u\n", This,
+          idCmd, uType, pwReserved, pszName, cchMax );
+
+    return E_NOTIMPL;
+}
+
+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 ULONG WINAPI
+ShellLink_ObjectWithSite_AddRef( IObjectWithSite* iface )
+{
+    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
+    return ShellLink_AddRef( This );
+}
+
+static ULONG WINAPI
+ShellLink_ObjectWithSite_Release( IObjectWithSite* iface )
+{
+    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
+    return ShellLink_Release( This );
+}
+
+static HRESULT WINAPI
+ShellLink_GetSite( IObjectWithSite *iface, REFIID iid, void ** ppvSite )
+{
+    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
+
+    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
+ShellLink_SetSite( IObjectWithSite *iface, IUnknown *punk )
+{
+    IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
+
+    TRACE("%p %p\n", iface, punk);
+
+    if ( punk )
+        IUnknown_AddRef( punk );
+    This->site = punk;
+
+    return S_OK;
+}
+
+static const IObjectWithSiteVtbl owsvt =
+{
+    ShellLink_ObjectWithSite_QueryInterface,
+    ShellLink_ObjectWithSite_AddRef,
+    ShellLink_ObjectWithSite_Release,
+    ShellLink_SetSite,
+    ShellLink_GetSite,
 };