Fixed crash in unixfs shellfolder handling with winamp.
[wine] / dlls / shell32 / shelllink.c
1 /*
2  *
3  *      Copyright 1997  Marcus Meissner
4  *      Copyright 1998  Juergen Schmied
5  *      Copyright 2005  Mike McCormack
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * NOTES
22  *   Nearly complete informations about the binary formats 
23  *   of .lnk files available at http://www.wotsit.org
24  *
25  *  You can use winedump to examine the contents of a link file:
26  *   winedump lnk sc.lnk
27  *
28  *  MSI advertised shortcuts are totally undocumented.  They provide an
29  *   icon for a program that is not yet installed, and invoke MSI to
30  *   install the program when the shortcut is clicked on.  They are
31  *   created by passing a special string to SetPath, and the information
32  *   in that string is parsed an stored.
33  */
34
35 #define COBJMACROS
36 #define NONAMELESSUNION
37
38 #include "wine/debug.h"
39 #include "winerror.h"
40 #include "windef.h"
41 #include "winbase.h"
42 #include "winnls.h"
43 #include "winreg.h"
44
45 #include "winuser.h"
46 #include "wingdi.h"
47 #include "shlobj.h"
48 #include "undocshell.h"
49
50 #include "pidl.h"
51 #include "shell32_main.h"
52 #include "shlguid.h"
53 #include "shlwapi.h"
54 #include "msi.h"
55
56 #include "initguid.h"
57
58 WINE_DEFAULT_DEBUG_CHANNEL(shell);
59
60 DEFINE_GUID( SHELL32_AdvtShortcutProduct,
61        0x9db1186f,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
62 DEFINE_GUID( SHELL32_AdvtShortcutComponent,
63        0x9db1186e,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
64
65 /* link file formats */
66
67 #include "pshpack1.h"
68
69 typedef struct _LINK_HEADER
70 {
71         DWORD    dwSize;        /* 0x00 size of the header - 0x4c */
72         GUID     MagicGuid;     /* 0x04 is CLSID_ShellLink */
73         DWORD    dwFlags;       /* 0x14 describes elements following */
74         DWORD    dwFileAttr;    /* 0x18 attributes of the target file */
75         FILETIME Time1;         /* 0x1c */
76         FILETIME Time2;         /* 0x24 */
77         FILETIME Time3;         /* 0x2c */
78         DWORD    dwFileLength;  /* 0x34 File length */
79         DWORD    nIcon;         /* 0x38 icon number */
80         DWORD   fStartup;       /* 0x3c startup type */
81         DWORD   wHotKey;        /* 0x40 hotkey */
82         DWORD   Unknown5;       /* 0x44 */
83         DWORD   Unknown6;       /* 0x48 */
84 } LINK_HEADER, * PLINK_HEADER;
85
86 #define SHLINK_LOCAL  0
87 #define SHLINK_REMOTE 1
88
89 typedef struct _LOCATION_INFO
90 {
91     DWORD  dwTotalSize;
92     DWORD  dwHeaderSize;
93     DWORD  dwFlags;
94     DWORD  dwVolTableOfs;
95     DWORD  dwLocalPathOfs;
96     DWORD  dwNetworkVolTableOfs;
97     DWORD  dwFinalPathOfs;
98 } LOCATION_INFO;
99
100 typedef struct _LOCAL_VOLUME_INFO
101 {
102     DWORD dwSize;
103     DWORD dwType;
104     DWORD dwVolSerial;
105     DWORD dwVolLabelOfs;
106 } LOCAL_VOLUME_INFO;
107
108 typedef struct volume_info_t
109 {
110     DWORD type;
111     DWORD serial;
112     WCHAR label[12];  /* assume 8.3 */
113 } volume_info;
114
115 #include "poppack.h"
116
117 static const IShellLinkAVtbl slvt;
118 static const IShellLinkWVtbl slvtw;
119 static const IPersistFileVtbl pfvt;
120 static const IPersistStreamVtbl psvt;
121 static const IShellLinkDataListVtbl dlvt;
122 static const IShellExtInitVtbl eivt;
123 static const IContextMenuVtbl cmvt;
124 static const IObjectWithSiteVtbl owsvt;
125
126 /* IShellLink Implementation */
127
128 typedef struct
129 {
130         const IShellLinkAVtbl *lpVtbl;
131         const IShellLinkWVtbl *lpvtblw;
132         const IPersistFileVtbl *lpvtblPersistFile;
133         const IPersistStreamVtbl *lpvtblPersistStream;
134         const IShellLinkDataListVtbl *lpvtblShellLinkDataList;
135         const IShellExtInitVtbl *lpvtblShellExtInit;
136         const IContextMenuVtbl *lpvtblContextMenu;
137         const IObjectWithSiteVtbl *lpvtblObjectWithSite;
138
139         LONG            ref;
140
141         /* data structures according to the informations in the link */
142         LPITEMIDLIST    pPidl;
143         WORD            wHotKey;
144         SYSTEMTIME      time1;
145         SYSTEMTIME      time2;
146         SYSTEMTIME      time3;
147
148         DWORD         iShowCmd;
149         LPWSTR        sIcoPath;
150         INT           iIcoNdx;
151         LPWSTR        sPath;
152         LPWSTR        sArgs;
153         LPWSTR        sWorkDir;
154         LPWSTR        sDescription;
155         LPWSTR        sPathRel;
156         LPWSTR        sProduct;
157         LPWSTR        sComponent;
158         volume_info   volume;
159
160         BOOL          bDirty;
161         INT           iIdOpen;  /* id of the "Open" entry in the context menu */
162         IUnknown      *site;
163 } IShellLinkImpl;
164
165 static inline IShellLinkImpl *impl_from_IShellLinkW( IShellLinkW *iface )
166 {
167     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblw));
168 }
169
170 static inline IShellLinkImpl *impl_from_IPersistFile( IPersistFile *iface )
171 {
172     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistFile));
173 }
174
175 static inline IShellLinkImpl *impl_from_IPersistStream( IPersistStream *iface )
176 {
177     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistStream));
178 }
179
180 static inline IShellLinkImpl *impl_from_IShellLinkDataList( IShellLinkDataList *iface )
181 {
182     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellLinkDataList));
183 }
184
185 static inline IShellLinkImpl *impl_from_IShellExtInit( IShellExtInit *iface )
186 {
187     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellExtInit));
188 }
189
190 static inline IShellLinkImpl *impl_from_IContextMenu( IContextMenu *iface )
191 {
192     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblContextMenu));
193 }
194
195 static inline IShellLinkImpl *impl_from_IObjectWithSite( IObjectWithSite *iface )
196 {
197     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblObjectWithSite));
198 }
199
200 static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath);
201
202 /* strdup on the process heap */
203 inline static LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
204 {
205     INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
206     LPWSTR p = HeapAlloc( heap, flags, len*sizeof (WCHAR) );
207     if( !p )
208         return p;
209     MultiByteToWideChar( CP_ACP, 0, str, -1, p, len );
210     return p;
211 }
212
213 inline static LPWSTR strdupW( LPCWSTR src )
214 {
215     LPWSTR dest;
216     if (!src) return NULL;
217     dest = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(src)+1)*sizeof(WCHAR) );
218     if (dest)
219         lstrcpyW(dest, src);
220     return dest;
221 }
222
223 /**************************************************************************
224  *  ShellLink::QueryInterface implementation
225  */
226 static HRESULT ShellLink_QueryInterface( IShellLinkImpl *This, REFIID riid,  LPVOID *ppvObj)
227 {
228     TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
229
230     *ppvObj = NULL;
231
232     if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IShellLinkA))
233     {
234         *ppvObj = This;
235     }
236     else if(IsEqualIID(riid, &IID_IShellLinkW))
237     {
238         *ppvObj = &(This->lpvtblw);
239     }
240     else if(IsEqualIID(riid, &IID_IPersistFile))
241     {
242         *ppvObj = &(This->lpvtblPersistFile);
243     }
244     else if(IsEqualIID(riid, &IID_IPersistStream))
245     {
246         *ppvObj = &(This->lpvtblPersistStream);
247     }
248     else if(IsEqualIID(riid, &IID_IShellLinkDataList))
249     {
250         *ppvObj = &(This->lpvtblShellLinkDataList);
251     }
252     else if(IsEqualIID(riid, &IID_IShellExtInit))
253     {
254         *ppvObj = &(This->lpvtblShellExtInit);
255     }
256     else if(IsEqualIID(riid, &IID_IContextMenu))
257     {
258         *ppvObj = &(This->lpvtblContextMenu);
259     }
260     else if(IsEqualIID(riid, &IID_IObjectWithSite))
261     {
262         *ppvObj = &(This->lpvtblObjectWithSite);
263     }
264
265     if(*ppvObj)
266     {
267         IUnknown_AddRef((IUnknown*)(*ppvObj));
268         TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
269         return S_OK;
270     }
271     ERR("-- Interface: E_NOINTERFACE\n");
272     return E_NOINTERFACE;
273 }
274
275 /**************************************************************************
276  *  ShellLink::AddRef implementation
277  */
278 static ULONG ShellLink_AddRef( IShellLinkImpl *This )
279 {
280     ULONG refCount = InterlockedIncrement(&This->ref);
281
282     TRACE("(%p)->(count=%lu)\n", This, refCount - 1);
283
284     return refCount;
285 }
286
287 /**************************************************************************
288  *  ShellLink::Release implementation
289  */
290 static ULONG ShellLink_Release( IShellLinkImpl *This )
291 {
292     ULONG refCount = InterlockedDecrement(&This->ref);
293
294     TRACE("(%p)->(count=%lu)\n", This, refCount + 1);
295
296     if (refCount)
297         return refCount;
298
299     TRACE("-- destroying IShellLink(%p)\n",This);
300
301     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
302     HeapFree(GetProcessHeap(), 0, This->sArgs);
303     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
304     HeapFree(GetProcessHeap(), 0, This->sDescription);
305     HeapFree(GetProcessHeap(),0,This->sPath);
306
307     if (This->site)
308         IUnknown_Release( This->site );
309
310     if (This->pPidl)
311         ILFree(This->pPidl);
312
313     LocalFree((HANDLE)This);
314
315     return 0;
316 }
317
318 static HRESULT ShellLink_GetClassID( IShellLinkImpl *This, CLSID *pclsid )
319 {
320     TRACE("%p %p\n", This, pclsid);
321
322     memcpy( pclsid, &CLSID_ShellLink, sizeof (CLSID) );
323     return S_OK;
324 }
325
326 /**************************************************************************
327  *  IPersistFile_QueryInterface
328  */
329 static HRESULT WINAPI IPersistFile_fnQueryInterface(
330         IPersistFile* iface,
331         REFIID riid,
332         LPVOID *ppvObj)
333 {
334     IShellLinkImpl *This = impl_from_IPersistFile(iface);
335     return ShellLink_QueryInterface( This, riid, ppvObj );
336 }
337
338 /******************************************************************************
339  * IPersistFile_AddRef
340  */
341 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
342 {
343     IShellLinkImpl *This = impl_from_IPersistFile(iface);
344     return ShellLink_AddRef( This );
345 }
346
347 /******************************************************************************
348  * IPersistFile_Release
349  */
350 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile* iface)
351 {
352     IShellLinkImpl *This = impl_from_IPersistFile(iface);
353     return IShellLinkA_Release((IShellLinkA*)This);
354 }
355
356 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
357 {
358     IShellLinkImpl *This = impl_from_IPersistFile(iface);
359     return ShellLink_GetClassID( This, pClassID );
360 }
361
362 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
363 {
364         IShellLinkImpl *This = impl_from_IPersistFile(iface);
365
366         TRACE("(%p)\n",This);
367
368         if (This->bDirty)
369             return S_OK;
370
371         return S_FALSE;
372 }
373
374 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
375 {
376         IShellLinkImpl *This = impl_from_IPersistFile(iface);
377         IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
378         HRESULT r;
379         IStream *stm;
380
381         TRACE("(%p, %s, %lx)\n",This, debugstr_w(pszFileName), dwMode);
382
383         if( dwMode == 0 )
384                 dwMode = STGM_READ | STGM_SHARE_DENY_WRITE;
385         r = SHCreateStreamOnFileW(pszFileName, dwMode, &stm);
386         if( SUCCEEDED( r ) )
387         {
388             r = IPersistStream_Load(StreamThis, stm);
389             ShellLink_UpdatePath(This->sPathRel, pszFileName, This->sWorkDir, &This->sPath);
390             IStream_Release( stm );
391             This->bDirty = FALSE;
392         }
393         TRACE("-- returning hr %08lx\n", r);
394         return r;
395 }
396
397 static BOOL StartLinkProcessor( LPCOLESTR szLink )
398 {
399     static const WCHAR szFormat[] = {
400         'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
401         ' ','-','r',' ','"','%','s','"',0 };
402     LONG len;
403     LPWSTR buffer;
404     STARTUPINFOW si;
405     PROCESS_INFORMATION pi;
406
407     len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
408     buffer = HeapAlloc( GetProcessHeap(), 0, len );
409     if( !buffer )
410         return FALSE;
411
412     wsprintfW( buffer, szFormat, szLink );
413
414     TRACE("starting %s\n",debugstr_w(buffer));
415
416     memset(&si, 0, sizeof(si));
417     si.cb = sizeof(si);
418     if (!CreateProcessW( NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) return FALSE;
419
420     /* wait for a while to throttle the creation of linker processes */
421     if( WAIT_OBJECT_0 != WaitForSingleObject( pi.hProcess, 10000 ) )
422         WARN("Timed out waiting for shell linker\n");
423
424     CloseHandle( pi.hProcess );
425     CloseHandle( pi.hThread );
426
427     return TRUE;
428 }
429
430 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
431 {
432     IShellLinkImpl *This = impl_from_IPersistFile(iface);
433     IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
434     HRESULT r;
435     IStream *stm;
436
437     TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
438
439     if (!pszFileName)
440         return E_FAIL;
441
442     r = SHCreateStreamOnFileW( pszFileName, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &stm );
443     if( SUCCEEDED( r ) )
444     {
445         r = IPersistStream_Save(StreamThis, stm, FALSE);
446         IStream_Release( stm );
447
448         if( SUCCEEDED( r ) )
449         {
450             StartLinkProcessor( pszFileName );
451
452             This->bDirty = FALSE;
453         }
454         else
455         {
456             DeleteFileW( pszFileName );
457             WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
458         }
459     }
460
461     return r;
462 }
463
464 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
465 {
466         IShellLinkImpl *This = impl_from_IPersistFile(iface);
467         FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
468         return NOERROR;
469 }
470
471 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *ppszFileName)
472 {
473         IShellLinkImpl *This = impl_from_IPersistFile(iface);
474         FIXME("(%p)\n",This);
475         return NOERROR;
476 }
477
478 static const IPersistFileVtbl pfvt =
479 {
480         IPersistFile_fnQueryInterface,
481         IPersistFile_fnAddRef,
482         IPersistFile_fnRelease,
483         IPersistFile_fnGetClassID,
484         IPersistFile_fnIsDirty,
485         IPersistFile_fnLoad,
486         IPersistFile_fnSave,
487         IPersistFile_fnSaveCompleted,
488         IPersistFile_fnGetCurFile
489 };
490
491 /************************************************************************
492  * IPersistStream_QueryInterface
493  */
494 static HRESULT WINAPI IPersistStream_fnQueryInterface(
495         IPersistStream* iface,
496         REFIID     riid,
497         VOID**     ppvObj)
498 {
499     IShellLinkImpl *This = impl_from_IPersistStream(iface);
500     return ShellLink_QueryInterface( This, riid, ppvObj );
501 }
502
503 /************************************************************************
504  * IPersistStream_Release
505  */
506 static ULONG WINAPI IPersistStream_fnRelease(
507         IPersistStream* iface)
508 {
509     IShellLinkImpl *This = impl_from_IPersistStream(iface);
510     return IShellLinkA_Release((IShellLinkA*)This);
511 }
512
513 /************************************************************************
514  * IPersistStream_AddRef
515  */
516 static ULONG WINAPI IPersistStream_fnAddRef(
517         IPersistStream* iface)
518 {
519     IShellLinkImpl *This = impl_from_IPersistStream(iface);
520     return ShellLink_AddRef( This );
521 }
522
523 /************************************************************************
524  * IPersistStream_GetClassID
525  *
526  */
527 static HRESULT WINAPI IPersistStream_fnGetClassID(
528         IPersistStream* iface,
529         CLSID* pClassID)
530 {
531     IShellLinkImpl *This = impl_from_IPersistStream(iface);
532     return ShellLink_GetClassID( This, pClassID );
533 }
534
535 /************************************************************************
536  * IPersistStream_IsDirty (IPersistStream)
537  */
538 static HRESULT WINAPI IPersistStream_fnIsDirty(
539         IPersistStream*  iface)
540 {
541         IShellLinkImpl *This = impl_from_IPersistStream(iface);
542
543         TRACE("(%p)\n", This);
544
545         return S_OK;
546 }
547
548
549 static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
550 {
551     DWORD count;
552     USHORT len;
553     LPVOID temp;
554     LPWSTR str;
555     HRESULT r;
556
557     TRACE("%p\n", stm);
558
559     count = 0;
560     r = IStream_Read(stm, &len, sizeof(len), &count);
561     if ( FAILED (r) || ( count != sizeof(len) ) )
562         return E_FAIL;
563
564     if( unicode )
565         len *= sizeof (WCHAR);
566
567     TRACE("reading %d\n", len);
568     temp = HeapAlloc(GetProcessHeap(), 0, len+sizeof(WCHAR));
569     if( !temp )
570         return E_OUTOFMEMORY;
571     count = 0;
572     r = IStream_Read(stm, temp, len, &count);
573     if( FAILED (r) || ( count != len ) )
574     {
575         HeapFree( GetProcessHeap(), 0, temp );
576         return E_FAIL;
577     }
578
579     TRACE("read %s\n", debugstr_an(temp,len));
580
581     /* convert to unicode if necessary */
582     if( !unicode )
583     {
584         count = MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, NULL, 0 );
585         str = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof (WCHAR) );
586         if( str )
587             MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, str, count );
588         HeapFree( GetProcessHeap(), 0, temp );
589     }
590     else
591     {
592         count /= 2;
593         str = (LPWSTR) temp;
594     }
595     str[count] = 0;
596
597     *pstr = str;
598
599     return S_OK;
600 }
601
602 static HRESULT Stream_ReadChunk( IStream* stm, LPVOID *data )
603 {
604     DWORD size;
605     ULONG count;
606     HRESULT r;
607     struct sized_chunk {
608         DWORD size;
609         unsigned char data[1];
610     } *chunk;
611
612     TRACE("%p\n",stm);
613
614     r = IStream_Read( stm, &size, sizeof(size), &count );
615     if( FAILED( r )  || count != sizeof(size) )
616         return E_FAIL;
617
618     chunk = HeapAlloc( GetProcessHeap(), 0, size );
619     if( !chunk )
620         return E_OUTOFMEMORY;
621
622     chunk->size = size;
623     r = IStream_Read( stm, chunk->data, size - sizeof(size), &count );
624     if( FAILED( r ) || count != (size - sizeof(size)) )
625     {
626         HeapFree( GetProcessHeap(), 0, chunk );
627         return E_FAIL;
628     }
629
630     TRACE("Read %ld bytes\n",chunk->size);
631
632     *data = (LPVOID) chunk;
633
634     return S_OK;
635 }
636
637 static BOOL Stream_LoadVolume( LOCAL_VOLUME_INFO *vol, volume_info *volume )
638 {
639     const int label_sz = sizeof volume->label/sizeof volume->label[0];
640     LPSTR label;
641     int len;
642
643     volume->serial = vol->dwVolSerial;
644     volume->type = vol->dwType;
645
646     if( !vol->dwVolLabelOfs )
647         return FALSE;
648     if( vol->dwSize <= vol->dwVolLabelOfs )
649         return FALSE;
650     len = vol->dwSize - vol->dwVolLabelOfs;
651
652     label = (LPSTR) vol;
653     label += vol->dwVolLabelOfs;
654     MultiByteToWideChar( CP_ACP, 0, label, len, volume->label, label_sz-1);
655
656     return TRUE;
657 }
658
659 static LPWSTR Stream_LoadPath( LPSTR p, DWORD maxlen )
660 {
661     int len = 0, wlen;
662     LPWSTR path;
663
664     while( p[len] && (len < maxlen) )
665         len++;
666
667     wlen = MultiByteToWideChar(CP_ACP, 0, p, len, NULL, 0);
668     path = HeapAlloc(GetProcessHeap(), 0, (wlen+1)*sizeof(WCHAR));
669     MultiByteToWideChar(CP_ACP, 0, p, len, path, wlen);
670     path[wlen] = 0;
671
672     return path;
673 }
674
675 static HRESULT Stream_LoadLocation( IStream *stm,
676                 volume_info *volume, LPWSTR *path )
677 {
678     char *p = NULL;
679     LOCATION_INFO *loc;
680     HRESULT r;
681     int n;
682
683     r = Stream_ReadChunk( stm, (LPVOID*) &p );
684     if( FAILED(r) )
685         return r;
686
687     loc = (LOCATION_INFO*) p;
688     if (loc->dwTotalSize < sizeof(LOCATION_INFO))
689     {
690         HeapFree( GetProcessHeap(), 0, p );
691         return E_FAIL;
692     }
693
694     /* if there's valid local volume information, load it */
695     if( loc->dwVolTableOfs && 
696        ((loc->dwVolTableOfs + sizeof(LOCAL_VOLUME_INFO)) <= loc->dwTotalSize) )
697     {
698         LOCAL_VOLUME_INFO *volume_info;
699
700         volume_info = (LOCAL_VOLUME_INFO*) &p[loc->dwVolTableOfs];
701         Stream_LoadVolume( volume_info, volume );
702     }
703
704     /* if there's a local path, load it */
705     n = loc->dwLocalPathOfs;
706     if( n && (n < loc->dwTotalSize) )
707         *path = Stream_LoadPath( &p[n], loc->dwTotalSize - n );
708
709     TRACE("type %ld serial %08lx name %s path %s\n", volume->type,
710           volume->serial, debugstr_w(volume->label), debugstr_w(*path));
711
712     HeapFree( GetProcessHeap(), 0, p );
713     return S_OK;
714 }
715
716 /*
717  *  The format of the advertised shortcut info seems to be:
718  *
719  *  Offset     Description
720  *  ------     -----------
721  *
722  *    0          Length of the block (4 bytes, usually 0x314)
723  *    4          tag (dword)
724  *    8          string data in ASCII
725  *    8+0x104    string data in UNICODE
726  *
727  * In the original Win32 implementation the buffers are not initialized
728  *  to zero, so data trailing the string is random garbage.
729  */
730 static HRESULT Stream_LoadAdvertiseInfo( IStream* stm, LPWSTR *str )
731 {
732     DWORD size;
733     ULONG count;
734     HRESULT r;
735     EXP_DARWIN_LINK buffer;
736     
737     TRACE("%p\n",stm);
738
739     r = IStream_Read( stm, &buffer.dbh.cbSize, sizeof (DWORD), &count );
740     if( FAILED( r ) )
741         return r;
742
743     /* make sure that we read the size of the structure even on error */
744     size = sizeof buffer - sizeof (DWORD);
745     if( buffer.dbh.cbSize != sizeof buffer )
746     {
747         ERR("Ooops.  This structure is not as expected...\n");
748         return E_FAIL;
749     }
750
751     r = IStream_Read( stm, &buffer.dbh.dwSignature, size, &count );
752     if( FAILED( r ) )
753         return r;
754
755     if( count != size )
756         return E_FAIL;
757
758     TRACE("magic %08lx  string = %s\n", buffer.dbh.dwSignature, debugstr_w(buffer.szwDarwinID));
759
760     if( (buffer.dbh.dwSignature&0xffff0000) != 0xa0000000 )
761     {
762         ERR("Unknown magic number %08lx in advertised shortcut\n", buffer.dbh.dwSignature);
763         return E_FAIL;
764     }
765
766     *str = HeapAlloc( GetProcessHeap(), 0, 
767                      (lstrlenW(buffer.szwDarwinID)+1) * sizeof(WCHAR) );
768     lstrcpyW( *str, buffer.szwDarwinID );
769
770     return S_OK;
771 }
772
773 /************************************************************************
774  * IPersistStream_Load (IPersistStream)
775  */
776 static HRESULT WINAPI IPersistStream_fnLoad(
777     IPersistStream*  iface,
778     IStream*         stm)
779 {
780     LINK_HEADER hdr;
781     ULONG    dwBytesRead;
782     BOOL     unicode;
783     HRESULT  r;
784     DWORD    zero;
785
786     IShellLinkImpl *This = impl_from_IPersistStream(iface);
787
788     TRACE("%p %p\n", This, stm);
789
790     if( !stm )
791         return STG_E_INVALIDPOINTER;
792
793     dwBytesRead = 0;
794     r = IStream_Read(stm, &hdr, sizeof(hdr), &dwBytesRead);
795     if( FAILED( r ) )
796         return r;
797
798     if( dwBytesRead != sizeof(hdr))
799         return E_FAIL;
800     if( hdr.dwSize != sizeof(hdr))
801         return E_FAIL;
802     if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
803         return E_FAIL;
804
805     /* free all the old stuff */
806     ILFree(This->pPidl);
807     This->pPidl = NULL;
808     memset( &This->volume, 0, sizeof This->volume );
809     HeapFree(GetProcessHeap(), 0, This->sPath);
810     This->sPath = NULL;
811     HeapFree(GetProcessHeap(), 0, This->sDescription);
812     This->sDescription = NULL;
813     HeapFree(GetProcessHeap(), 0, This->sPathRel);
814     This->sPathRel = NULL;
815     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
816     This->sWorkDir = NULL;
817     HeapFree(GetProcessHeap(), 0, This->sArgs);
818     This->sArgs = NULL;
819     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
820     This->sIcoPath = NULL;
821     HeapFree(GetProcessHeap(), 0, This->sProduct);
822     This->sProduct = NULL;
823     HeapFree(GetProcessHeap(), 0, This->sComponent);
824     This->sComponent = NULL;
825         
826     This->wHotKey = (WORD)hdr.wHotKey;
827     This->iIcoNdx = hdr.nIcon;
828     FileTimeToSystemTime (&hdr.Time1, &This->time1);
829     FileTimeToSystemTime (&hdr.Time2, &This->time2);
830     FileTimeToSystemTime (&hdr.Time3, &This->time3);
831     if (TRACE_ON(shell))
832     {
833         WCHAR sTemp[MAX_PATH];
834         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time1,
835                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
836         TRACE("-- time1: %s\n", debugstr_w(sTemp) );
837         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time2,
838                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
839         TRACE("-- time2: %s\n", debugstr_w(sTemp) );
840         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time3,
841                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
842         TRACE("-- time3: %s\n", debugstr_w(sTemp) );
843     }
844
845     /* load all the new stuff */
846     if( hdr.dwFlags & SLDF_HAS_ID_LIST )
847     {
848         r = ILLoadFromStream( stm, &This->pPidl );
849         if( FAILED( r ) )
850             return r;
851     }
852     pdump(This->pPidl);
853
854     /* load the location information */
855     if( hdr.dwFlags & SLDF_HAS_LINK_INFO )
856         r = Stream_LoadLocation( stm, &This->volume, &This->sPath );
857     if( FAILED( r ) )
858         goto end;
859
860     unicode = hdr.dwFlags & SLDF_UNICODE;
861     if( hdr.dwFlags & SLDF_HAS_NAME )
862     {
863         r = Stream_LoadString( stm, unicode, &This->sDescription );
864         TRACE("Description  -> %s\n",debugstr_w(This->sDescription));
865     }
866     if( FAILED( r ) )
867         goto end;
868
869     if( hdr.dwFlags & SLDF_HAS_RELPATH )
870     {
871         r = Stream_LoadString( stm, unicode, &This->sPathRel );
872         TRACE("Relative Path-> %s\n",debugstr_w(This->sPathRel));
873     }
874     if( FAILED( r ) )
875         goto end;
876
877     if( hdr.dwFlags & SLDF_HAS_WORKINGDIR )
878     {
879         r = Stream_LoadString( stm, unicode, &This->sWorkDir );
880         TRACE("Working Dir  -> %s\n",debugstr_w(This->sWorkDir));
881     }
882     if( FAILED( r ) )
883         goto end;
884
885     if( hdr.dwFlags & SLDF_HAS_ARGS )
886     {
887         r = Stream_LoadString( stm, unicode, &This->sArgs );
888         TRACE("Working Dir  -> %s\n",debugstr_w(This->sArgs));
889     }
890     if( FAILED( r ) )
891         goto end;
892
893     if( hdr.dwFlags & SLDF_HAS_ICONLOCATION )
894     {
895         r = Stream_LoadString( stm, unicode, &This->sIcoPath );
896         TRACE("Icon file    -> %s\n",debugstr_w(This->sIcoPath));
897     }
898     if( FAILED( r ) )
899         goto end;
900
901     if( hdr.dwFlags & SLDF_HAS_LOGO3ID )
902     {
903         r = Stream_LoadAdvertiseInfo( stm, &This->sProduct );
904         TRACE("Product      -> %s\n",debugstr_w(This->sProduct));
905     }
906     if( FAILED( r ) )
907         goto end;
908
909     if( hdr.dwFlags & SLDF_HAS_DARWINID )
910     {
911         r = Stream_LoadAdvertiseInfo( stm, &This->sComponent );
912         TRACE("Component    -> %s\n",debugstr_w(This->sComponent));
913     }
914     if( FAILED( r ) )
915         goto end;
916
917     r = IStream_Read(stm, &zero, sizeof zero, &dwBytesRead);
918     if( FAILED( r ) || zero || dwBytesRead != sizeof zero )
919         ERR("Last word was not zero\n");
920
921     TRACE("OK\n");
922
923     pdump (This->pPidl);
924
925     return S_OK;
926 end:
927     return r;
928 }
929
930 /************************************************************************
931  * Stream_WriteString
932  *
933  * Helper function for IPersistStream_Save. Writes a unicode string 
934  *  with terminating nul byte to a stream, preceded by the its length.
935  */
936 static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
937 {
938     USHORT len = lstrlenW( str ) + 1;
939     DWORD count;
940     HRESULT r;
941
942     r = IStream_Write( stm, &len, sizeof(len), &count );
943     if( FAILED( r ) )
944         return r;
945
946     len *= sizeof(WCHAR);
947
948     r = IStream_Write( stm, str, len, &count );
949     if( FAILED( r ) )
950         return r;
951
952     return S_OK;
953 }
954
955 /************************************************************************
956  * Stream_WriteLocationInfo
957  *
958  * Writes the location info to a stream
959  *
960  * FIXME: One day we might want to write the network volume information
961  *        and the final path.
962  *        Figure out how Windows deals with unicode paths here.
963  */
964 static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR path,
965                                          volume_info *volume )
966 {
967     DWORD total_size, path_size, volume_info_size, label_size, final_path_size;
968     LOCAL_VOLUME_INFO *vol;
969     LOCATION_INFO *loc;
970     LPSTR szLabel, szPath, szFinalPath;
971     ULONG count = 0;
972
973     TRACE("%p %s %p\n", stm, debugstr_w(path), volume);
974
975     /* figure out the size of everything */
976     label_size = WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
977                                       NULL, 0, NULL, NULL );
978     path_size = WideCharToMultiByte( CP_ACP, 0, path, -1,
979                                      NULL, 0, NULL, NULL );
980     volume_info_size = sizeof *vol + label_size;
981     final_path_size = 1;
982     total_size = sizeof *loc + volume_info_size + path_size + final_path_size;
983
984     /* create pointers to everything */
985     loc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total_size);
986     vol = (LOCAL_VOLUME_INFO*) &loc[1];
987     szLabel = (LPSTR) &vol[1];
988     szPath = &szLabel[label_size];
989     szFinalPath = &szPath[path_size];
990
991     /* fill in the location information header */
992     loc->dwTotalSize = total_size;
993     loc->dwHeaderSize = sizeof (*loc);
994     loc->dwFlags = 1;
995     loc->dwVolTableOfs = sizeof (*loc);
996     loc->dwLocalPathOfs = sizeof (*loc) + volume_info_size;
997     loc->dwNetworkVolTableOfs = 0;
998     loc->dwFinalPathOfs = sizeof (*loc) + volume_info_size + path_size;
999
1000     /* fill in the volume information */
1001     vol->dwSize = volume_info_size;
1002     vol->dwType = volume->type;
1003     vol->dwVolSerial = volume->serial;
1004     vol->dwVolLabelOfs = sizeof (*vol);
1005
1006     /* copy in the strings */
1007     WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
1008                          szLabel, label_size, NULL, NULL );
1009     WideCharToMultiByte( CP_ACP, 0, path, -1,
1010                          szPath, path_size, NULL, NULL );
1011     szFinalPath[0] = 0;
1012
1013     return IStream_Write( stm, loc, total_size, &count );
1014 }
1015
1016 static HRESULT Stream_WriteAdvertiseInfo( IStream* stm, LPCWSTR string, DWORD magic )
1017 {
1018     ULONG count;
1019     EXP_DARWIN_LINK buffer;
1020     
1021     TRACE("%p\n",stm);
1022
1023     memset( &buffer, 0, sizeof buffer );
1024     buffer.dbh.cbSize = sizeof buffer;
1025     buffer.dbh.dwSignature = magic;
1026     lstrcpynW( buffer.szwDarwinID, string, MAX_PATH );
1027     WideCharToMultiByte(CP_ACP, 0, string, -1, buffer.szDarwinID, MAX_PATH, NULL, NULL );
1028
1029     return IStream_Write( stm, &buffer, buffer.dbh.cbSize, &count );
1030 }
1031
1032 /************************************************************************
1033  * IPersistStream_Save (IPersistStream)
1034  *
1035  * FIXME: makes assumptions about byte order
1036  */
1037 static HRESULT WINAPI IPersistStream_fnSave(
1038         IPersistStream*  iface,
1039         IStream*         stm,
1040         BOOL             fClearDirty)
1041 {
1042     static const WCHAR wOpen[] = {'o','p','e','n',0};
1043
1044     LINK_HEADER header;
1045     WCHAR   exePath[MAX_PATH];
1046     ULONG   count;
1047     DWORD   zero;
1048     HRESULT r;
1049
1050     IShellLinkImpl *This = impl_from_IPersistStream(iface);
1051
1052     TRACE("%p %p %x\n", This, stm, fClearDirty);
1053
1054     *exePath = '\0';
1055
1056     if (This->sPath)
1057     {
1058         SHELL_FindExecutable(NULL, This->sPath, wOpen, exePath, MAX_PATH,
1059                              NULL, NULL, NULL, NULL);
1060         /*
1061          * windows can create lnk files to executables that do not exist yet
1062          * so if the executable does not exist the just trust the path they
1063          * gave us
1064          */
1065         if (!*exePath) lstrcpyW(exePath,This->sPath);
1066     }
1067
1068     memset(&header, 0, sizeof(header));
1069     header.dwSize = sizeof(header);
1070     header.fStartup = This->iShowCmd;
1071     memcpy(&header.MagicGuid, &CLSID_ShellLink, sizeof(header.MagicGuid) );
1072
1073     header.wHotKey = This->wHotKey;
1074     header.nIcon = This->iIcoNdx;
1075     header.dwFlags = SLDF_UNICODE;   /* strings are in unicode */
1076     if( This->pPidl )
1077         header.dwFlags |= SLDF_HAS_ID_LIST;
1078     if( This->sPath )
1079         header.dwFlags |= SLDF_HAS_LINK_INFO;
1080     if( This->sDescription )
1081         header.dwFlags |= SLDF_HAS_NAME;
1082     if( This->sWorkDir )
1083         header.dwFlags |= SLDF_HAS_WORKINGDIR;
1084     if( This->sArgs )
1085         header.dwFlags |= SLDF_HAS_ARGS;
1086     if( This->sIcoPath )
1087         header.dwFlags |= SLDF_HAS_ICONLOCATION;
1088     if( This->sProduct )
1089         header.dwFlags |= SLDF_HAS_LOGO3ID;
1090     if( This->sComponent )
1091         header.dwFlags |= SLDF_HAS_DARWINID;
1092
1093     SystemTimeToFileTime ( &This->time1, &header.Time1 );
1094     SystemTimeToFileTime ( &This->time2, &header.Time2 );
1095     SystemTimeToFileTime ( &This->time3, &header.Time3 );
1096
1097     /* write the Shortcut header */
1098     r = IStream_Write( stm, &header, sizeof(header), &count );
1099     if( FAILED( r ) )
1100     {
1101         ERR("Write failed at %d\n",__LINE__);
1102         return r;
1103     }
1104
1105     TRACE("Writing pidl \n");
1106
1107     /* write the PIDL to the shortcut */
1108     if( This->pPidl )
1109     {
1110         r = ILSaveToStream( stm, This->pPidl );
1111         if( FAILED( r ) )
1112         {
1113             ERR("Failed to write PIDL at %d\n",__LINE__);
1114             return r;
1115         }
1116     }
1117
1118     if( This->sPath )
1119         Stream_WriteLocationInfo( stm, exePath, &This->volume );
1120
1121     if( This->sDescription )
1122         r = Stream_WriteString( stm, This->sDescription );
1123
1124     if( This->sPathRel )
1125         r = Stream_WriteString( stm, This->sPathRel );
1126
1127     if( This->sWorkDir )
1128         r = Stream_WriteString( stm, This->sWorkDir );
1129
1130     if( This->sArgs )
1131         r = Stream_WriteString( stm, This->sArgs );
1132
1133     if( This->sIcoPath )
1134         r = Stream_WriteString( stm, This->sIcoPath );
1135
1136     if( This->sProduct )
1137         r = Stream_WriteAdvertiseInfo( stm, This->sProduct, EXP_SZ_ICON_SIG );
1138
1139     if( This->sComponent )
1140         r = Stream_WriteAdvertiseInfo( stm, This->sComponent, EXP_DARWIN_ID_SIG );
1141
1142     /* the last field is a single zero dword */
1143     zero = 0;
1144     r = IStream_Write( stm, &zero, sizeof zero, &count );
1145
1146     return S_OK;
1147 }
1148
1149 /************************************************************************
1150  * IPersistStream_GetSizeMax (IPersistStream)
1151  */
1152 static HRESULT WINAPI IPersistStream_fnGetSizeMax(
1153         IPersistStream*  iface,
1154         ULARGE_INTEGER*  pcbSize)
1155 {
1156         IShellLinkImpl *This = impl_from_IPersistStream(iface);
1157
1158         TRACE("(%p)\n", This);
1159
1160         return E_NOTIMPL;
1161 }
1162
1163 static const IPersistStreamVtbl psvt =
1164 {
1165         IPersistStream_fnQueryInterface,
1166         IPersistStream_fnAddRef,
1167         IPersistStream_fnRelease,
1168         IPersistStream_fnGetClassID,
1169         IPersistStream_fnIsDirty,
1170         IPersistStream_fnLoad,
1171         IPersistStream_fnSave,
1172         IPersistStream_fnGetSizeMax
1173 };
1174
1175 /**************************************************************************
1176  *        IShellLink_Constructor
1177  */
1178 HRESULT WINAPI IShellLink_Constructor( IUnknown *pUnkOuter,
1179                REFIID riid, LPVOID *ppv )
1180 {
1181         IShellLinkImpl * sl;
1182         HRESULT r;
1183
1184         TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
1185
1186         *ppv = NULL;
1187
1188         if (pUnkOuter)
1189             return CLASS_E_NOAGGREGATION;
1190         sl = LocalAlloc(LMEM_ZEROINIT,sizeof(IShellLinkImpl));
1191         if (!sl)
1192             return E_OUTOFMEMORY;
1193
1194         sl->ref = 1;
1195         sl->lpVtbl = &slvt;
1196         sl->lpvtblw = &slvtw;
1197         sl->lpvtblPersistFile = &pfvt;
1198         sl->lpvtblPersistStream = &psvt;
1199         sl->lpvtblShellLinkDataList = &dlvt;
1200         sl->lpvtblShellExtInit = &eivt;
1201         sl->lpvtblContextMenu = &cmvt;
1202         sl->lpvtblObjectWithSite = &owsvt;
1203         sl->iShowCmd = SW_SHOWNORMAL;
1204         sl->bDirty = FALSE;
1205         sl->iIdOpen = -1;
1206         sl->site = NULL;
1207
1208         TRACE("(%p)->()\n",sl);
1209
1210         r = ShellLink_QueryInterface( sl, riid, ppv );
1211         ShellLink_Release( sl );
1212         return r;
1213 }
1214
1215
1216 static BOOL SHELL_ExistsFileW(LPCWSTR path)
1217 {
1218     if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
1219         return FALSE;
1220     return TRUE;
1221 }
1222
1223 /**************************************************************************
1224  *  ShellLink_UpdatePath
1225  *      update absolute path in sPath using relative path in sPathRel
1226  */
1227 static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
1228 {
1229     if (!path || !psPath)
1230         return E_INVALIDARG;
1231
1232     if (!*psPath && sPathRel) {
1233         WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
1234         LPWSTR final = NULL;
1235
1236         /* first try if [directory of link file] + [relative path] finds an existing file */
1237
1238         GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
1239         if( !final )
1240             final = buffer;
1241         lstrcpyW(final, sPathRel);
1242
1243         *abs_path = '\0';
1244
1245         if (SHELL_ExistsFileW(buffer)) {
1246             if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
1247                 lstrcpyW(abs_path, buffer);
1248         } else {
1249             /* try if [working directory] + [relative path] finds an existing file */
1250             if (sWorkDir) {
1251                 lstrcpyW(buffer, sWorkDir);
1252                 lstrcpyW(PathAddBackslashW(buffer), sPathRel);
1253
1254                 if (SHELL_ExistsFileW(buffer))
1255                     if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
1256                         lstrcpyW(abs_path, buffer);
1257             }
1258         }
1259
1260         /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
1261         if (!*abs_path)
1262             lstrcpyW(abs_path, sPathRel);
1263
1264         *psPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(abs_path)+1)*sizeof(WCHAR));
1265         if (!*psPath)
1266             return E_OUTOFMEMORY;
1267
1268         lstrcpyW(*psPath, abs_path);
1269     }
1270
1271     return S_OK;
1272 }
1273
1274 /**************************************************************************
1275  *        IShellLink_ConstructFromFile
1276  */
1277 HRESULT WINAPI IShellLink_ConstructFromFile( IUnknown* pUnkOuter, REFIID riid,
1278                LPCITEMIDLIST pidl, LPVOID* ppv)
1279 {
1280     IShellLinkW* psl;
1281
1282     HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl);
1283
1284     if (SUCCEEDED(hr)) {
1285         IPersistFile* ppf;
1286
1287         *ppv = NULL;
1288
1289         hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
1290
1291         if (SUCCEEDED(hr)) {
1292             WCHAR path[MAX_PATH];
1293
1294             if (SHGetPathFromIDListW(pidl, path)) 
1295                 hr = IPersistFile_Load(ppf, path, 0);
1296             else
1297                 hr = E_FAIL;
1298
1299             if (SUCCEEDED(hr))
1300                 *ppv = (IUnknown*) psl;
1301
1302             IPersistFile_Release(ppf);
1303         }
1304
1305         if (!*ppv)
1306             IShellLinkW_Release(psl);
1307     }
1308
1309     return hr;
1310 }
1311
1312 /**************************************************************************
1313  *  IShellLinkA_QueryInterface
1314  */
1315 static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
1316 {
1317     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1318     return ShellLink_QueryInterface( This, riid, ppvObj );
1319 }
1320
1321 /******************************************************************************
1322  * IShellLinkA_AddRef
1323  */
1324 static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
1325 {
1326     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1327     return ShellLink_AddRef( This );
1328 }
1329
1330 /******************************************************************************
1331  *      IShellLinkA_Release
1332  */
1333 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
1334 {
1335     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1336     return ShellLink_Release( This );
1337 }
1338
1339 static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
1340                   INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1341 {
1342     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1343
1344     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
1345           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1346
1347     if (This->sComponent || This->sProduct)
1348         return S_FALSE;
1349
1350     if (cchMaxPath)
1351         pszFile[0] = 0;
1352     if (This->sPath)
1353         WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
1354                              pszFile, cchMaxPath, NULL, NULL);
1355
1356     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1357
1358     return S_OK;
1359 }
1360
1361 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
1362 {
1363     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1364
1365     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1366
1367     return IShellLinkW_GetIDList((IShellLinkW*)&(This->lpvtblw), ppidl);
1368 }
1369
1370 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
1371 {
1372     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1373
1374     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1375
1376     if (This->pPidl)
1377         ILFree(This->pPidl);
1378     This->pPidl = ILClone (pidl);
1379     This->bDirty = TRUE;
1380
1381     return S_OK;
1382 }
1383
1384 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
1385 {
1386     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1387
1388     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1389
1390     if( cchMaxName )
1391         pszName[0] = 0;
1392     if( This->sDescription )
1393         WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
1394             pszName, cchMaxName, NULL, NULL);
1395
1396     return S_OK;
1397 }
1398
1399 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
1400 {
1401     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1402
1403     TRACE("(%p)->(pName=%s)\n", This, pszName);
1404
1405     HeapFree(GetProcessHeap(), 0, This->sDescription);
1406     This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
1407     if ( !This->sDescription )
1408         return E_OUTOFMEMORY;
1409
1410     This->bDirty = TRUE;
1411
1412     return S_OK;
1413 }
1414
1415 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
1416 {
1417     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1418
1419     TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1420
1421     if( cchMaxPath )
1422         pszDir[0] = 0;
1423     if( This->sWorkDir )
1424         WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
1425                              pszDir, cchMaxPath, NULL, NULL);
1426
1427     return S_OK;
1428 }
1429
1430 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
1431 {
1432     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1433
1434     TRACE("(%p)->(dir=%s)\n",This, pszDir);
1435
1436     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1437     This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
1438     if ( !This->sWorkDir )
1439         return E_OUTOFMEMORY;
1440
1441     This->bDirty = TRUE;
1442
1443     return S_OK;
1444 }
1445
1446 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
1447 {
1448     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1449
1450     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1451
1452     if( cchMaxPath )
1453         pszArgs[0] = 0;
1454     if( This->sArgs )
1455         WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
1456                              pszArgs, cchMaxPath, NULL, NULL);
1457
1458     return S_OK;
1459 }
1460
1461 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
1462 {
1463     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1464
1465     TRACE("(%p)->(args=%s)\n",This, pszArgs);
1466
1467     HeapFree(GetProcessHeap(), 0, This->sArgs);
1468     This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
1469     if( !This->sArgs )
1470         return E_OUTOFMEMORY;
1471
1472     This->bDirty = TRUE;
1473
1474     return S_OK;
1475 }
1476
1477 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
1478 {
1479     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1480
1481     TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1482
1483     *pwHotkey = This->wHotKey;
1484
1485     return S_OK;
1486 }
1487
1488 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
1489 {
1490     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1491
1492     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1493
1494     This->wHotKey = wHotkey;
1495     This->bDirty = TRUE;
1496
1497     return S_OK;
1498 }
1499
1500 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
1501 {
1502     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1503
1504     TRACE("(%p)->(%p)\n",This, piShowCmd);
1505     *piShowCmd = This->iShowCmd;
1506     return S_OK;
1507 }
1508
1509 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
1510 {
1511     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1512
1513     TRACE("(%p) %d\n",This, iShowCmd);
1514
1515     This->iShowCmd = iShowCmd;
1516     This->bDirty = TRUE;
1517
1518     return NOERROR;
1519 }
1520
1521 static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPITEMIDLIST pidl, LPSTR pszIconPath, int cchIconPath, int* piIcon)
1522 {
1523     LPCITEMIDLIST pidlLast;
1524
1525     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1526
1527     if (SUCCEEDED(hr)) {
1528         IExtractIconA* pei;
1529
1530         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconA, NULL, (LPVOID*)&pei);
1531
1532         if (SUCCEEDED(hr)) {
1533             hr = IExtractIconA_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1534
1535             IExtractIconA_Release(pei);
1536         }
1537
1538         IShellFolder_Release(psf);
1539     }
1540
1541     return hr;
1542 }
1543
1544 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1545 {
1546     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1547
1548     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1549
1550     pszIconPath[0] = 0;
1551     *piIcon = This->iIcoNdx;
1552
1553     if (This->sIcoPath)
1554     {
1555         WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1556         return S_OK;
1557     }
1558
1559     if (This->pPidl || This->sPath)
1560     {
1561         IShellFolder* pdsk;
1562
1563         HRESULT hr = SHGetDesktopFolder(&pdsk);
1564
1565         if (SUCCEEDED(hr))
1566         {
1567             /* first look for an icon using the PIDL (if present) */
1568             if (This->pPidl)
1569                 hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1570             else
1571                 hr = E_FAIL;
1572
1573             /* if we couldn't find an icon yet, look for it using the file system path */
1574             if (FAILED(hr) && This->sPath)
1575             {
1576                 LPITEMIDLIST pidl;
1577
1578                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1579
1580                 if (SUCCEEDED(hr)) {
1581                     hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1582
1583                     SHFree(pidl);
1584                 }
1585             }
1586
1587             IShellFolder_Release(pdsk);
1588         }
1589
1590         return hr;
1591     }
1592     return S_OK;
1593 }
1594
1595 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
1596 {
1597     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1598
1599     TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1600
1601     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1602     This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1603     if ( !This->sIcoPath )
1604         return E_OUTOFMEMORY;
1605
1606     This->iIcoNdx = iIcon;
1607     This->bDirty = TRUE;
1608
1609     return S_OK;
1610 }
1611
1612 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1613 {
1614     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1615
1616     TRACE("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved);
1617
1618     HeapFree(GetProcessHeap(), 0, This->sPathRel);
1619     This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1620     This->bDirty = TRUE;
1621
1622     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1623 }
1624
1625 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1626 {
1627     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1628
1629     TRACE("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1630
1631     return IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, fFlags );
1632 }
1633
1634 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1635 {
1636     HRESULT r;
1637     LPWSTR str;
1638     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1639
1640     TRACE("(%p)->(path=%s)\n",This, pszFile);
1641
1642     str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
1643     if( !str ) 
1644         return E_OUTOFMEMORY;
1645
1646     r = IShellLinkW_SetPath((IShellLinkW*)&(This->lpvtblw), str);
1647     HeapFree( GetProcessHeap(), 0, str );
1648
1649     return r;
1650 }
1651
1652 /**************************************************************************
1653 * IShellLink Implementation
1654 */
1655
1656 static const IShellLinkAVtbl slvt =
1657 {
1658     IShellLinkA_fnQueryInterface,
1659     IShellLinkA_fnAddRef,
1660     IShellLinkA_fnRelease,
1661     IShellLinkA_fnGetPath,
1662     IShellLinkA_fnGetIDList,
1663     IShellLinkA_fnSetIDList,
1664     IShellLinkA_fnGetDescription,
1665     IShellLinkA_fnSetDescription,
1666     IShellLinkA_fnGetWorkingDirectory,
1667     IShellLinkA_fnSetWorkingDirectory,
1668     IShellLinkA_fnGetArguments,
1669     IShellLinkA_fnSetArguments,
1670     IShellLinkA_fnGetHotkey,
1671     IShellLinkA_fnSetHotkey,
1672     IShellLinkA_fnGetShowCmd,
1673     IShellLinkA_fnSetShowCmd,
1674     IShellLinkA_fnGetIconLocation,
1675     IShellLinkA_fnSetIconLocation,
1676     IShellLinkA_fnSetRelativePath,
1677     IShellLinkA_fnResolve,
1678     IShellLinkA_fnSetPath
1679 };
1680
1681
1682 /**************************************************************************
1683  *  IShellLinkW_fnQueryInterface
1684  */
1685 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1686   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1687 {
1688     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1689     return ShellLink_QueryInterface( This, riid, ppvObj );
1690 }
1691
1692 /******************************************************************************
1693  * IShellLinkW_fnAddRef
1694  */
1695 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1696 {
1697     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1698     return ShellLink_AddRef( This );
1699 }
1700
1701 /******************************************************************************
1702  * IShellLinkW_fnRelease
1703  */
1704 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1705 {
1706     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1707     return ShellLink_Release( This );
1708 }
1709
1710 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1711 {
1712     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1713
1714     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
1715           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1716
1717     if (This->sComponent || This->sProduct)
1718         return S_FALSE;
1719
1720     if (cchMaxPath)
1721         pszFile[0] = 0;
1722     if (This->sPath)
1723         lstrcpynW( pszFile, This->sPath, cchMaxPath );
1724
1725     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1726
1727     return S_OK;
1728 }
1729
1730 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1731 {
1732     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1733
1734     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1735
1736     if (!This->pPidl)
1737         return S_FALSE;
1738     *ppidl = ILClone(This->pPidl);
1739     return S_OK;
1740 }
1741
1742 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1743 {
1744     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1745
1746     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1747
1748     if( This->pPidl )
1749         ILFree( This->pPidl );
1750     This->pPidl = ILClone( pidl );
1751     if( !This->pPidl )
1752         return E_FAIL;
1753
1754     This->bDirty = TRUE;
1755
1756     return S_OK;
1757 }
1758
1759 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1760 {
1761     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1762
1763     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1764
1765     pszName[0] = 0;
1766     if( This->sDescription )
1767         lstrcpynW( pszName, This->sDescription, cchMaxName );
1768
1769     return S_OK;
1770 }
1771
1772 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1773 {
1774     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1775
1776     TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1777
1778     HeapFree(GetProcessHeap(), 0, This->sDescription);
1779     This->sDescription = HeapAlloc( GetProcessHeap(), 0,
1780                                     (lstrlenW( pszName )+1)*sizeof(WCHAR) );
1781     if ( !This->sDescription )
1782         return E_OUTOFMEMORY;
1783
1784     lstrcpyW( This->sDescription, pszName );
1785     This->bDirty = TRUE;
1786
1787     return S_OK;
1788 }
1789
1790 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1791 {
1792     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1793
1794     TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1795
1796     if( cchMaxPath )
1797         pszDir[0] = 0;
1798     if( This->sWorkDir )
1799         lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
1800
1801     return S_OK;
1802 }
1803
1804 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1805 {
1806     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1807
1808     TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1809
1810     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1811     This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
1812                                 (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
1813     if ( !This->sWorkDir )
1814         return E_OUTOFMEMORY;
1815     lstrcpyW( This->sWorkDir, pszDir );
1816     This->bDirty = TRUE;
1817
1818     return S_OK;
1819 }
1820
1821 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1822 {
1823     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1824
1825     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1826
1827     if( cchMaxPath )
1828         pszArgs[0] = 0;
1829     if( This->sArgs )
1830         lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
1831
1832     return NOERROR;
1833 }
1834
1835 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1836 {
1837     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1838
1839     TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1840
1841     HeapFree(GetProcessHeap(), 0, This->sArgs);
1842     This->sArgs = HeapAlloc( GetProcessHeap(), 0,
1843                              (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
1844     if ( !This->sArgs )
1845         return E_OUTOFMEMORY;
1846     lstrcpyW( This->sArgs, pszArgs );
1847     This->bDirty = TRUE;
1848
1849     return S_OK;
1850 }
1851
1852 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1853 {
1854     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1855
1856     TRACE("(%p)->(%p)\n",This, pwHotkey);
1857
1858     *pwHotkey=This->wHotKey;
1859
1860     return S_OK;
1861 }
1862
1863 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1864 {
1865     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1866
1867     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1868
1869     This->wHotKey = wHotkey;
1870     This->bDirty = TRUE;
1871
1872     return S_OK;
1873 }
1874
1875 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1876 {
1877     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1878
1879     TRACE("(%p)->(%p)\n",This, piShowCmd);
1880
1881     *piShowCmd = This->iShowCmd;
1882
1883     return S_OK;
1884 }
1885
1886 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1887 {
1888     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1889
1890     This->iShowCmd = iShowCmd;
1891     This->bDirty = TRUE;
1892
1893     return S_OK;
1894 }
1895
1896 static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPITEMIDLIST pidl, LPWSTR pszIconPath, int cchIconPath, int* piIcon)
1897 {
1898     LPCITEMIDLIST pidlLast;
1899
1900     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1901
1902     if (SUCCEEDED(hr)) {
1903         IExtractIconW* pei;
1904
1905         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconW, NULL, (LPVOID*)&pei);
1906
1907         if (SUCCEEDED(hr)) {
1908             hr = IExtractIconW_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1909
1910             IExtractIconW_Release(pei);
1911         }
1912
1913         IShellFolder_Release(psf);
1914     }
1915
1916     return hr;
1917 }
1918
1919 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1920 {
1921     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1922
1923     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1924
1925     pszIconPath[0] = 0;
1926     *piIcon = This->iIcoNdx;
1927
1928     if (This->sIcoPath)
1929     {
1930         lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
1931         return S_OK;
1932     }
1933
1934     if (This->pPidl || This->sPath)
1935     {
1936         IShellFolder* pdsk;
1937
1938         HRESULT hr = SHGetDesktopFolder(&pdsk);
1939
1940         if (SUCCEEDED(hr))
1941         {
1942             /* first look for an icon using the PIDL (if present) */
1943             if (This->pPidl)
1944                 hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1945             else
1946                 hr = E_FAIL;
1947
1948             /* if we couldn't find an icon yet, look for it using the file system path */
1949             if (FAILED(hr) && This->sPath)
1950             {
1951                 LPITEMIDLIST pidl;
1952
1953                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1954
1955                 if (SUCCEEDED(hr))
1956                 {
1957                     hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1958
1959                     SHFree(pidl);
1960                 }
1961             }
1962
1963             IShellFolder_Release(pdsk);
1964         }
1965         return hr;
1966     }
1967     return S_OK;
1968 }
1969
1970 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
1971 {
1972     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1973
1974     TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1975
1976     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1977     This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
1978                                 (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
1979     if ( !This->sIcoPath )
1980         return E_OUTOFMEMORY;
1981     lstrcpyW( This->sIcoPath, pszIconPath );
1982
1983     This->iIcoNdx = iIcon;
1984     This->bDirty = TRUE;
1985
1986     return S_OK;
1987 }
1988
1989 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
1990 {
1991     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1992
1993     TRACE("(%p)->(path=%s %lx)\n",This, debugstr_w(pszPathRel), dwReserved);
1994
1995     HeapFree(GetProcessHeap(), 0, This->sPathRel);
1996     This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
1997                                 (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
1998     if ( !This->sPathRel )
1999         return E_OUTOFMEMORY;
2000     lstrcpyW( This->sPathRel, pszPathRel );
2001     This->bDirty = TRUE;
2002
2003     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
2004 }
2005
2006 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
2007 {
2008     HRESULT hr = S_OK;
2009     BOOL bSuccess;
2010
2011     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
2012
2013     TRACE("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
2014
2015     /*FIXME: use IResolveShellLink interface */
2016
2017     if (!This->sPath && This->pPidl) {
2018         WCHAR buffer[MAX_PATH];
2019
2020         bSuccess = SHGetPathFromIDListW(This->pPidl, buffer);
2021
2022         if (bSuccess && *buffer) {
2023             This->sPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
2024             if (!This->sPath)
2025                 return E_OUTOFMEMORY;
2026
2027             lstrcpyW(This->sPath, buffer);
2028
2029             This->bDirty = TRUE;
2030         } else
2031             hr = S_OK;    /* don't report an error occurred while just caching information */
2032     }
2033
2034     if (!This->sIcoPath && This->sPath) {
2035         This->sIcoPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
2036         if (!This->sIcoPath)
2037             return E_OUTOFMEMORY;
2038
2039         lstrcpyW(This->sIcoPath, This->sPath);
2040         This->iIcoNdx = 0;
2041
2042         This->bDirty = TRUE;
2043     }
2044
2045     return hr;
2046 }
2047
2048 static LPWSTR ShellLink_GetAdvertisedArg(LPCWSTR str)
2049 {
2050     LPWSTR ret;
2051     LPCWSTR p;
2052     DWORD len;
2053
2054     if( !str )
2055         return NULL;
2056
2057     p = strchrW( str, ':' );
2058     if( !p )
2059         return NULL;
2060     len = p - str;
2061     ret = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
2062     if( !ret )
2063         return ret;
2064     memcpy( ret, str, sizeof(WCHAR)*len );
2065     ret[len] = 0;
2066     return ret;
2067 }
2068
2069 static HRESULT ShellLink_SetAdvertiseInfo(IShellLinkImpl *This, LPCWSTR str)
2070 {
2071     LPCWSTR szComponent = NULL, szProduct = NULL, p;
2072     WCHAR szGuid[39];
2073     HRESULT r;
2074     GUID guid;
2075     int len;
2076
2077     while( str[0] )
2078     {
2079         /* each segment must start with two colons */
2080         if( str[0] != ':' || str[1] != ':' )
2081             return E_FAIL;
2082
2083         /* the last segment is just two colons */
2084         if( !str[2] )
2085             break;
2086         str += 2;
2087
2088         /* there must be a colon straight after a guid */
2089         p = strchrW( str, ':' );
2090         if( !p )
2091             return E_FAIL;
2092         len = p - str;
2093         if( len != 38 )
2094             return E_FAIL;
2095
2096         /* get the guid, and check it's validly formatted */
2097         memcpy( szGuid, str, sizeof(WCHAR)*len );
2098         szGuid[len] = 0;
2099         r = CLSIDFromString( szGuid, &guid );
2100         if( r != S_OK )
2101             return r;
2102         str = p + 1;
2103
2104         /* match it up to a guid that we care about */
2105         if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutComponent ) && !szComponent )
2106             szComponent = str;
2107         else if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutProduct ) && !szProduct )
2108             szProduct = str;
2109         else
2110             return E_FAIL;
2111
2112         /* skip to the next field */
2113         str = strchrW( str, ':' );
2114         if( !str )
2115             return E_FAIL;
2116     }
2117
2118     /* we have to have a component for an advertised shortcut */
2119     if( !szComponent )
2120         return E_FAIL;
2121
2122     This->sComponent = ShellLink_GetAdvertisedArg( szComponent );
2123     This->sProduct = ShellLink_GetAdvertisedArg( szProduct );
2124
2125     TRACE("Component = %s\n", debugstr_w(This->sComponent));
2126     TRACE("Product = %s\n", debugstr_w(This->sProduct));
2127
2128     return S_OK;
2129 }
2130
2131 static BOOL ShellLink_GetVolumeInfo(LPWSTR path, volume_info *volume)
2132 {
2133     const int label_sz = sizeof volume->label/sizeof volume->label[0];
2134     WCHAR drive[4] = { path[0], ':', '\\', 0 };
2135     BOOL r;
2136
2137     volume->type = GetDriveTypeW(drive);
2138     r = GetVolumeInformationW(drive, volume->label, label_sz,
2139                               &volume->serial, NULL, NULL, NULL, 0);
2140     TRACE("r = %d type %ld serial %08lx name %s\n", r,
2141           volume->type, volume->serial, debugstr_w(volume->label));
2142     return r;
2143 }
2144
2145 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
2146 {
2147     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
2148     WCHAR buffer[MAX_PATH];
2149     LPWSTR fname;
2150     HRESULT hr = S_OK;
2151
2152     TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
2153
2154     HeapFree(GetProcessHeap(), 0, This->sPath);
2155     This->sPath = NULL;
2156
2157     HeapFree(GetProcessHeap(), 0, This->sComponent);
2158     This->sComponent = NULL;
2159
2160     if (This->pPidl)
2161         ILFree(This->pPidl);
2162     This->pPidl = NULL;
2163
2164     if (S_OK != ShellLink_SetAdvertiseInfo( This, pszFile ))
2165     {
2166         if (*pszFile == '\0')
2167             *buffer = '\0';
2168         else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
2169             return E_FAIL;
2170         else if(!PathFileExistsW(buffer))
2171             hr = S_FALSE;
2172
2173         This->pPidl = SHSimpleIDListFromPathW(pszFile);
2174         ShellLink_GetVolumeInfo(buffer, &This->volume);
2175
2176         This->sPath = HeapAlloc( GetProcessHeap(), 0,
2177                              (lstrlenW( buffer )+1) * sizeof (WCHAR) );
2178         if (!This->sPath)
2179             return E_OUTOFMEMORY;
2180
2181         lstrcpyW(This->sPath, buffer);
2182     }
2183     This->bDirty = TRUE;
2184
2185     return hr;
2186 }
2187
2188 /**************************************************************************
2189 * IShellLinkW Implementation
2190 */
2191
2192 static const IShellLinkWVtbl slvtw =
2193 {
2194     IShellLinkW_fnQueryInterface,
2195     IShellLinkW_fnAddRef,
2196     IShellLinkW_fnRelease,
2197     IShellLinkW_fnGetPath,
2198     IShellLinkW_fnGetIDList,
2199     IShellLinkW_fnSetIDList,
2200     IShellLinkW_fnGetDescription,
2201     IShellLinkW_fnSetDescription,
2202     IShellLinkW_fnGetWorkingDirectory,
2203     IShellLinkW_fnSetWorkingDirectory,
2204     IShellLinkW_fnGetArguments,
2205     IShellLinkW_fnSetArguments,
2206     IShellLinkW_fnGetHotkey,
2207     IShellLinkW_fnSetHotkey,
2208     IShellLinkW_fnGetShowCmd,
2209     IShellLinkW_fnSetShowCmd,
2210     IShellLinkW_fnGetIconLocation,
2211     IShellLinkW_fnSetIconLocation,
2212     IShellLinkW_fnSetRelativePath,
2213     IShellLinkW_fnResolve,
2214     IShellLinkW_fnSetPath
2215 };
2216
2217 static HRESULT WINAPI
2218 ShellLink_DataList_QueryInterface( IShellLinkDataList* iface, REFIID riid, void** ppvObject)
2219 {
2220     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2221     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2222 }
2223
2224 static ULONG WINAPI
2225 ShellLink_DataList_AddRef( IShellLinkDataList* iface )
2226 {
2227     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2228     return IShellLinkA_AddRef((IShellLinkA*)This);
2229 }
2230
2231 static ULONG WINAPI
2232 ShellLink_DataList_Release( IShellLinkDataList* iface )
2233 {
2234     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2235     return ShellLink_Release( This );
2236 }
2237
2238 static HRESULT WINAPI
2239 ShellLink_AddDataBlock( IShellLinkDataList* iface, void* pDataBlock )
2240 {
2241     FIXME("\n");
2242     return E_NOTIMPL;
2243 }
2244
2245 static HRESULT WINAPI
2246 ShellLink_CopyDataBlock( IShellLinkDataList* iface, DWORD dwSig, void** ppDataBlock )
2247 {
2248     FIXME("\n");
2249     return E_NOTIMPL;
2250 }
2251
2252 static HRESULT WINAPI
2253 ShellLink_RemoveDataBlock( IShellLinkDataList* iface, DWORD dwSig )
2254 {
2255     FIXME("\n");
2256     return E_NOTIMPL;
2257 }
2258
2259 static HRESULT WINAPI
2260 ShellLink_GetFlags( IShellLinkDataList* iface, DWORD* pdwFlags )
2261 {
2262     FIXME("\n");
2263     return E_NOTIMPL;
2264 }
2265
2266 static HRESULT WINAPI
2267 ShellLink_SetFlags( IShellLinkDataList* iface, DWORD dwFlags )
2268 {
2269     FIXME("\n");
2270     return E_NOTIMPL;
2271 }
2272
2273 static const IShellLinkDataListVtbl dlvt =
2274 {
2275     ShellLink_DataList_QueryInterface,
2276     ShellLink_DataList_AddRef,
2277     ShellLink_DataList_Release,
2278     ShellLink_AddDataBlock,
2279     ShellLink_CopyDataBlock,
2280     ShellLink_RemoveDataBlock,
2281     ShellLink_GetFlags,
2282     ShellLink_SetFlags
2283 };
2284
2285 static HRESULT WINAPI
2286 ShellLink_ExtInit_QueryInterface( IShellExtInit* iface, REFIID riid, void** ppvObject )
2287 {
2288     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2289     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2290 }
2291
2292 static ULONG WINAPI
2293 ShellLink_ExtInit_AddRef( IShellExtInit* iface )
2294 {
2295     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2296     return IShellLinkA_AddRef((IShellLinkA*)This);
2297 }
2298
2299 static ULONG WINAPI
2300 ShellLink_ExtInit_Release( IShellExtInit* iface )
2301 {
2302     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2303     return ShellLink_Release( This );
2304 }
2305
2306 /**************************************************************************
2307  * ShellLink implementation of IShellExtInit::Initialize()
2308  *
2309  * Loads the shelllink from the dataobject the shell is pointing to.
2310  */
2311 static HRESULT WINAPI
2312 ShellLink_ExtInit_Initialize( IShellExtInit* iface, LPCITEMIDLIST pidlFolder,
2313                               IDataObject *pdtobj, HKEY hkeyProgID )
2314 {
2315     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2316     FORMATETC format;
2317     STGMEDIUM stgm;
2318     UINT count;
2319     HRESULT r = E_FAIL;
2320
2321     TRACE("%p %p %p %p\n", This, pidlFolder, pdtobj, hkeyProgID );
2322
2323     if( !pdtobj )
2324         return r;
2325
2326     format.cfFormat = CF_HDROP;
2327     format.ptd = NULL;
2328     format.dwAspect = DVASPECT_CONTENT;
2329     format.lindex = -1;
2330     format.tymed = TYMED_HGLOBAL;
2331
2332     if( FAILED( IDataObject_GetData( pdtobj, &format, &stgm ) ) )
2333         return r;
2334
2335     count = DragQueryFileW( stgm.u.hGlobal, -1, NULL, 0 );
2336     if( count == 1 )
2337     {
2338         LPWSTR path;
2339
2340         count = DragQueryFileW( stgm.u.hGlobal, 0, NULL, 0 );
2341         count++;
2342         path = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
2343         if( path )
2344         {
2345             IPersistFile *pf = (IPersistFile*) &This->lpvtblPersistFile;
2346
2347             count = DragQueryFileW( stgm.u.hGlobal, 0, path, count );
2348             r = IPersistFile_Load( pf, path, 0 );
2349             HeapFree( GetProcessHeap(), 0, path );
2350         }
2351     }
2352     ReleaseStgMedium( &stgm );
2353
2354     return r;
2355 }
2356
2357 static const IShellExtInitVtbl eivt =
2358 {
2359     ShellLink_ExtInit_QueryInterface,
2360     ShellLink_ExtInit_AddRef,
2361     ShellLink_ExtInit_Release,
2362     ShellLink_ExtInit_Initialize
2363 };
2364
2365 static HRESULT WINAPI
2366 ShellLink_ContextMenu_QueryInterface( IContextMenu* iface, REFIID riid, void** ppvObject )
2367 {
2368     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2369     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2370 }
2371
2372 static ULONG WINAPI
2373 ShellLink_ContextMenu_AddRef( IContextMenu* iface )
2374 {
2375     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2376     return IShellLinkA_AddRef((IShellLinkA*)This);
2377 }
2378
2379 static ULONG WINAPI
2380 ShellLink_ContextMenu_Release( IContextMenu* iface )
2381 {
2382     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2383     return ShellLink_Release( This );
2384 }
2385
2386 static HRESULT WINAPI
2387 ShellLink_QueryContextMenu( IContextMenu* iface, HMENU hmenu, UINT indexMenu,
2388                             UINT idCmdFirst, UINT idCmdLast, UINT uFlags )
2389 {
2390     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2391     static const WCHAR szOpen[] = { 'O','p','e','n',0 };
2392     MENUITEMINFOW mii;
2393     int id = 1;
2394
2395     TRACE("%p %p %u %u %u %u\n", This,
2396           hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
2397
2398     if ( !hmenu )
2399         return E_INVALIDARG;
2400
2401     memset( &mii, 0, sizeof mii );
2402     mii.cbSize = sizeof mii;
2403     mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
2404     mii.dwTypeData = (LPWSTR)szOpen;
2405     mii.cch = strlenW( mii.dwTypeData );
2406     mii.wID = idCmdFirst + id++;
2407     mii.fState = MFS_DEFAULT | MFS_ENABLED;
2408     mii.fType = MFT_STRING;
2409     if (!InsertMenuItemW( hmenu, indexMenu, TRUE, &mii ))
2410         return E_FAIL;
2411     This->iIdOpen = 0;
2412
2413     return MAKE_HRESULT( SEVERITY_SUCCESS, 0, id );
2414 }
2415
2416 static LPWSTR shelllink_get_msi_component_path( LPCWSTR component )
2417 {
2418     UINT WINAPI (*pMsiDecomposeDescriptorW)(LPCWSTR,LPWSTR,LPWSTR,LPWSTR,DWORD*);
2419     INSTALLSTATE WINAPI (*pMsiGetComponentPathW)(LPCWSTR,LPCWSTR,LPWSTR,DWORD*);
2420     WCHAR szProd[MAX_FEATURE_CHARS+1], szFeat[MAX_FEATURE_CHARS+1],
2421           szComp[MAX_FEATURE_CHARS+1], szCompPath[MAX_PATH];
2422     INSTALLSTATE state;
2423     LPWSTR path = NULL;
2424     HMODULE hmsi = NULL;
2425     DWORD sz = 0;
2426     UINT ret;
2427
2428     TRACE("%s\n", debugstr_w( component ) );
2429
2430     hmsi = LoadLibraryA("msi");
2431     if (!hmsi)
2432         goto end;
2433
2434     pMsiDecomposeDescriptorW = (LPVOID) GetProcAddress(hmsi, "MsiDecomposeDescriptorW");
2435     pMsiGetComponentPathW = (LPVOID) GetProcAddress(hmsi, "MsiGetComponentPathW");
2436     if (!pMsiDecomposeDescriptorW || !pMsiGetComponentPathW)
2437         goto end;
2438
2439     ret = pMsiDecomposeDescriptorW( component, szProd, szFeat, szComp, &sz );
2440     if (ret != ERROR_SUCCESS)
2441     {
2442         ERR("failed to decompose descriptor %s\n", debugstr_w( component ) );
2443         goto end;
2444     }
2445
2446     sz = MAX_PATH;
2447     state = pMsiGetComponentPathW( szProd, szComp, szCompPath, &sz );
2448     if (state != INSTALLSTATE_LOCAL)
2449     {
2450         ERR("MsiGetComponentPathW failed with error %d\n", ret );
2451         goto end;
2452     }
2453
2454     path = strdupW( szCompPath );
2455
2456 end:
2457     if (hmsi)
2458         FreeLibrary( hmsi );
2459
2460     TRACE("returning %s\n", debugstr_w( path ) );
2461
2462     return path;
2463 }
2464
2465 static HRESULT WINAPI
2466 ShellLink_InvokeCommand( IContextMenu* iface, LPCMINVOKECOMMANDINFO lpici )
2467 {
2468     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2469     SHELLEXECUTEINFOW sei;
2470     HWND hwnd = NULL; /* FIXME: get using interface set from IObjectWithSite */
2471     LPWSTR args = NULL;
2472     LPWSTR path = NULL;
2473     HRESULT r;
2474
2475     TRACE("%p %p\n", This, lpici );
2476
2477     if ( lpici->cbSize < sizeof (CMINVOKECOMMANDINFO) )
2478         return E_INVALIDARG;
2479
2480     if ( lpici->lpVerb != MAKEINTRESOURCEA(This->iIdOpen) )
2481     {
2482         ERR("Unknown id %d != %d\n", (INT)lpici->lpVerb, This->iIdOpen );
2483         return E_INVALIDARG;
2484     }
2485
2486     r = IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, 0 );
2487     if ( FAILED( r ) )
2488         return r;
2489
2490     if ( This->sComponent )
2491     {
2492         path = shelllink_get_msi_component_path( This->sComponent );
2493         if (!path)
2494             return E_FAIL;
2495     }
2496     else
2497         path = strdupW( This->sPath );
2498
2499     if ( lpici->cbSize == sizeof (CMINVOKECOMMANDINFOEX) &&
2500          ( lpici->fMask & CMIC_MASK_UNICODE ) )
2501     {
2502         LPCMINVOKECOMMANDINFOEX iciex = (LPCMINVOKECOMMANDINFOEX) lpici;
2503         DWORD len = 0;
2504
2505         if ( This->sArgs )
2506             len += lstrlenW( This->sArgs );
2507         if ( iciex->lpParametersW )
2508             len += lstrlenW( iciex->lpParametersW );
2509
2510         args = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
2511         args[0] = 0;
2512         if ( This->sArgs )
2513             lstrcatW( args, This->sArgs );
2514         if ( iciex->lpParametersW )
2515             lstrcatW( args, iciex->lpParametersW );
2516     }
2517
2518     memset( &sei, 0, sizeof sei );
2519     sei.cbSize = sizeof sei;
2520     sei.lpFile = path;
2521     sei.nShow = This->iShowCmd;
2522     sei.lpIDList = This->pPidl;
2523     sei.lpDirectory = This->sWorkDir;
2524     sei.lpParameters = args;
2525
2526     if( ShellExecuteExW( &sei ) )
2527         r = S_OK;
2528     else
2529         r = E_FAIL;
2530
2531     HeapFree( GetProcessHeap(), 0, args );
2532     HeapFree( GetProcessHeap(), 0, path );
2533
2534     return r;
2535 }
2536
2537 static HRESULT WINAPI
2538 ShellLink_GetCommandString( IContextMenu* iface, UINT_PTR idCmd, UINT uType,
2539                             UINT* pwReserved, LPSTR pszName, UINT cchMax )
2540 {
2541     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2542
2543     FIXME("%p %u %u %p %p %u\n", This,
2544           idCmd, uType, pwReserved, pszName, cchMax );
2545
2546     return E_NOTIMPL;
2547 }
2548
2549 static const IContextMenuVtbl cmvt =
2550 {
2551     ShellLink_ContextMenu_QueryInterface,
2552     ShellLink_ContextMenu_AddRef,
2553     ShellLink_ContextMenu_Release,
2554     ShellLink_QueryContextMenu,
2555     ShellLink_InvokeCommand,
2556     ShellLink_GetCommandString
2557 };
2558
2559 static HRESULT WINAPI
2560 ShellLink_ObjectWithSite_QueryInterface( IObjectWithSite* iface, REFIID riid, void** ppvObject )
2561 {
2562     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2563     return ShellLink_QueryInterface( This, riid, ppvObject );
2564 }
2565
2566 static ULONG WINAPI
2567 ShellLink_ObjectWithSite_AddRef( IObjectWithSite* iface )
2568 {
2569     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2570     return ShellLink_AddRef( This );
2571 }
2572
2573 static ULONG WINAPI
2574 ShellLink_ObjectWithSite_Release( IObjectWithSite* iface )
2575 {
2576     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2577     return ShellLink_Release( This );
2578 }
2579
2580 static HRESULT WINAPI
2581 ShellLink_GetSite( IObjectWithSite *iface, REFIID iid, void ** ppvSite )
2582 {
2583     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2584
2585     TRACE("%p %s %p\n", This, debugstr_guid( iid ), ppvSite );
2586
2587     if ( !This->site )
2588         return E_FAIL;
2589     return IUnknown_QueryInterface( This->site, iid, ppvSite );
2590 }
2591
2592 static HRESULT WINAPI
2593 ShellLink_SetSite( IObjectWithSite *iface, IUnknown *punk )
2594 {
2595     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2596
2597     TRACE("%p %p\n", iface, punk);
2598
2599     if ( punk )
2600         IUnknown_AddRef( punk );
2601     This->site = punk;
2602
2603     return S_OK;
2604 }
2605
2606 static const IObjectWithSiteVtbl owsvt =
2607 {
2608     ShellLink_ObjectWithSite_QueryInterface,
2609     ShellLink_ObjectWithSite_AddRef,
2610     ShellLink_ObjectWithSite_Release,
2611     ShellLink_SetSite,
2612     ShellLink_GetSite,
2613 };