fonts: Add a Tahoma replacement. Almost entirely based on a patch by Larry Snyder.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * NOTES
22  *   Nearly complete information 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 #include "appmgmt.h"
56
57 #include "initguid.h"
58
59 WINE_DEFAULT_DEBUG_CHANNEL(shell);
60
61 DEFINE_GUID( SHELL32_AdvtShortcutProduct,
62        0x9db1186f,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
63 DEFINE_GUID( SHELL32_AdvtShortcutComponent,
64        0x9db1186e,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
65
66 /* link file formats */
67
68 #include "pshpack1.h"
69
70 typedef struct _LINK_HEADER
71 {
72         DWORD    dwSize;        /* 0x00 size of the header - 0x4c */
73         GUID     MagicGuid;     /* 0x04 is CLSID_ShellLink */
74         DWORD    dwFlags;       /* 0x14 describes elements following */
75         DWORD    dwFileAttr;    /* 0x18 attributes of the target file */
76         FILETIME Time1;         /* 0x1c */
77         FILETIME Time2;         /* 0x24 */
78         FILETIME Time3;         /* 0x2c */
79         DWORD    dwFileLength;  /* 0x34 File length */
80         DWORD    nIcon;         /* 0x38 icon number */
81         DWORD   fStartup;       /* 0x3c startup type */
82         DWORD   wHotKey;        /* 0x40 hotkey */
83         DWORD   Unknown5;       /* 0x44 */
84         DWORD   Unknown6;       /* 0x48 */
85 } LINK_HEADER, * PLINK_HEADER;
86
87 #define SHLINK_LOCAL  0
88 #define SHLINK_REMOTE 1
89
90 typedef struct _LOCATION_INFO
91 {
92     DWORD  dwTotalSize;
93     DWORD  dwHeaderSize;
94     DWORD  dwFlags;
95     DWORD  dwVolTableOfs;
96     DWORD  dwLocalPathOfs;
97     DWORD  dwNetworkVolTableOfs;
98     DWORD  dwFinalPathOfs;
99 } LOCATION_INFO;
100
101 typedef struct _LOCAL_VOLUME_INFO
102 {
103     DWORD dwSize;
104     DWORD dwType;
105     DWORD dwVolSerial;
106     DWORD dwVolLabelOfs;
107 } LOCAL_VOLUME_INFO;
108
109 typedef struct volume_info_t
110 {
111     DWORD type;
112     DWORD serial;
113     WCHAR label[12];  /* assume 8.3 */
114 } volume_info;
115
116 #include "poppack.h"
117
118 static const IShellLinkAVtbl slvt;
119 static const IShellLinkWVtbl slvtw;
120 static const IPersistFileVtbl pfvt;
121 static const IPersistStreamVtbl psvt;
122 static const IShellLinkDataListVtbl dlvt;
123 static const IShellExtInitVtbl eivt;
124 static const IContextMenuVtbl cmvt;
125 static const IObjectWithSiteVtbl owsvt;
126
127 /* IShellLink Implementation */
128
129 typedef struct
130 {
131         const IShellLinkAVtbl *lpVtbl;
132         const IShellLinkWVtbl *lpvtblw;
133         const IPersistFileVtbl *lpvtblPersistFile;
134         const IPersistStreamVtbl *lpvtblPersistStream;
135         const IShellLinkDataListVtbl *lpvtblShellLinkDataList;
136         const IShellExtInitVtbl *lpvtblShellExtInit;
137         const IContextMenuVtbl *lpvtblContextMenu;
138         const IObjectWithSiteVtbl *lpvtblObjectWithSite;
139
140         LONG            ref;
141
142         /* data structures according to the information in the link */
143         LPITEMIDLIST    pPidl;
144         WORD            wHotKey;
145         SYSTEMTIME      time1;
146         SYSTEMTIME      time2;
147         SYSTEMTIME      time3;
148
149         DWORD         iShowCmd;
150         LPWSTR        sIcoPath;
151         INT           iIcoNdx;
152         LPWSTR        sPath;
153         LPWSTR        sArgs;
154         LPWSTR        sWorkDir;
155         LPWSTR        sDescription;
156         LPWSTR        sPathRel;
157         LPWSTR        sProduct;
158         LPWSTR        sComponent;
159         volume_info   volume;
160
161         BOOL          bDirty;
162         INT           iIdOpen;  /* id of the "Open" entry in the context menu */
163         IUnknown      *site;
164 } IShellLinkImpl;
165
166 static inline IShellLinkImpl *impl_from_IShellLinkW( IShellLinkW *iface )
167 {
168     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblw));
169 }
170
171 static inline IShellLinkImpl *impl_from_IPersistFile( IPersistFile *iface )
172 {
173     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistFile));
174 }
175
176 static inline IShellLinkImpl *impl_from_IPersistStream( IPersistStream *iface )
177 {
178     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistStream));
179 }
180
181 static inline IShellLinkImpl *impl_from_IShellLinkDataList( IShellLinkDataList *iface )
182 {
183     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellLinkDataList));
184 }
185
186 static inline IShellLinkImpl *impl_from_IShellExtInit( IShellExtInit *iface )
187 {
188     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellExtInit));
189 }
190
191 static inline IShellLinkImpl *impl_from_IContextMenu( IContextMenu *iface )
192 {
193     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblContextMenu));
194 }
195
196 static inline IShellLinkImpl *impl_from_IObjectWithSite( IObjectWithSite *iface )
197 {
198     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblObjectWithSite));
199 }
200
201 static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath);
202
203 /* strdup on the process heap */
204 static inline LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
205 {
206     INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
207     LPWSTR p = HeapAlloc( heap, flags, len*sizeof (WCHAR) );
208     if( !p )
209         return p;
210     MultiByteToWideChar( CP_ACP, 0, str, -1, p, len );
211     return p;
212 }
213
214 static inline LPWSTR strdupW( LPCWSTR src )
215 {
216     LPWSTR dest;
217     if (!src) return NULL;
218     dest = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(src)+1)*sizeof(WCHAR) );
219     if (dest)
220         lstrcpyW(dest, src);
221     return dest;
222 }
223
224 /**************************************************************************
225  *  ShellLink::QueryInterface implementation
226  */
227 static HRESULT ShellLink_QueryInterface( IShellLinkImpl *This, REFIID riid,  LPVOID *ppvObj)
228 {
229     TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
230
231     *ppvObj = NULL;
232
233     if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IShellLinkA))
234     {
235         *ppvObj = This;
236     }
237     else if(IsEqualIID(riid, &IID_IShellLinkW))
238     {
239         *ppvObj = &(This->lpvtblw);
240     }
241     else if(IsEqualIID(riid, &IID_IPersistFile))
242     {
243         *ppvObj = &(This->lpvtblPersistFile);
244     }
245     else if(IsEqualIID(riid, &IID_IPersistStream))
246     {
247         *ppvObj = &(This->lpvtblPersistStream);
248     }
249     else if(IsEqualIID(riid, &IID_IShellLinkDataList))
250     {
251         *ppvObj = &(This->lpvtblShellLinkDataList);
252     }
253     else if(IsEqualIID(riid, &IID_IShellExtInit))
254     {
255         *ppvObj = &(This->lpvtblShellExtInit);
256     }
257     else if(IsEqualIID(riid, &IID_IContextMenu))
258     {
259         *ppvObj = &(This->lpvtblContextMenu);
260     }
261     else if(IsEqualIID(riid, &IID_IObjectWithSite))
262     {
263         *ppvObj = &(This->lpvtblObjectWithSite);
264     }
265
266     if(*ppvObj)
267     {
268         IUnknown_AddRef((IUnknown*)(*ppvObj));
269         TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
270         return S_OK;
271     }
272     ERR("-- Interface: E_NOINTERFACE\n");
273     return E_NOINTERFACE;
274 }
275
276 /**************************************************************************
277  *  ShellLink::AddRef implementation
278  */
279 static ULONG ShellLink_AddRef( IShellLinkImpl *This )
280 {
281     ULONG refCount = InterlockedIncrement(&This->ref);
282
283     TRACE("(%p)->(count=%u)\n", This, refCount - 1);
284
285     return refCount;
286 }
287
288 /**************************************************************************
289  *  ShellLink::Release implementation
290  */
291 static ULONG ShellLink_Release( IShellLinkImpl *This )
292 {
293     ULONG refCount = InterlockedDecrement(&This->ref);
294
295     TRACE("(%p)->(count=%u)\n", This, refCount + 1);
296
297     if (refCount)
298         return refCount;
299
300     TRACE("-- destroying IShellLink(%p)\n",This);
301
302     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
303     HeapFree(GetProcessHeap(), 0, This->sArgs);
304     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
305     HeapFree(GetProcessHeap(), 0, This->sDescription);
306     HeapFree(GetProcessHeap(),0,This->sPath);
307
308     if (This->site)
309         IUnknown_Release( This->site );
310
311     if (This->pPidl)
312         ILFree(This->pPidl);
313
314     LocalFree((HANDLE)This);
315
316     return 0;
317 }
318
319 static HRESULT ShellLink_GetClassID( IShellLinkImpl *This, CLSID *pclsid )
320 {
321     TRACE("%p %p\n", This, pclsid);
322
323     memcpy( pclsid, &CLSID_ShellLink, sizeof (CLSID) );
324     return S_OK;
325 }
326
327 /**************************************************************************
328  *  IPersistFile_QueryInterface
329  */
330 static HRESULT WINAPI IPersistFile_fnQueryInterface(
331         IPersistFile* iface,
332         REFIID riid,
333         LPVOID *ppvObj)
334 {
335     IShellLinkImpl *This = impl_from_IPersistFile(iface);
336     return ShellLink_QueryInterface( This, riid, ppvObj );
337 }
338
339 /******************************************************************************
340  * IPersistFile_AddRef
341  */
342 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
343 {
344     IShellLinkImpl *This = impl_from_IPersistFile(iface);
345     return ShellLink_AddRef( This );
346 }
347
348 /******************************************************************************
349  * IPersistFile_Release
350  */
351 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile* iface)
352 {
353     IShellLinkImpl *This = impl_from_IPersistFile(iface);
354     return IShellLinkA_Release((IShellLinkA*)This);
355 }
356
357 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
358 {
359     IShellLinkImpl *This = impl_from_IPersistFile(iface);
360     return ShellLink_GetClassID( This, pClassID );
361 }
362
363 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
364 {
365         IShellLinkImpl *This = impl_from_IPersistFile(iface);
366
367         TRACE("(%p)\n",This);
368
369         if (This->bDirty)
370             return S_OK;
371
372         return S_FALSE;
373 }
374
375 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
376 {
377         IShellLinkImpl *This = impl_from_IPersistFile(iface);
378         IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
379         HRESULT r;
380         IStream *stm;
381
382         TRACE("(%p, %s, %x)\n",This, debugstr_w(pszFileName), dwMode);
383
384         if( dwMode == 0 )
385                 dwMode = STGM_READ | STGM_SHARE_DENY_WRITE;
386         r = SHCreateStreamOnFileW(pszFileName, dwMode, &stm);
387         if( SUCCEEDED( r ) )
388         {
389             r = IPersistStream_Load(StreamThis, stm);
390             ShellLink_UpdatePath(This->sPathRel, pszFileName, This->sWorkDir, &This->sPath);
391             IStream_Release( stm );
392             This->bDirty = FALSE;
393         }
394         TRACE("-- returning hr %08x\n", r);
395         return r;
396 }
397
398 static BOOL StartLinkProcessor( LPCOLESTR szLink )
399 {
400     static const WCHAR szFormat[] = {
401         'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
402         ' ','-','w',' ','"','%','s','"',0 };
403     LONG len;
404     LPWSTR buffer;
405     STARTUPINFOW si;
406     PROCESS_INFORMATION pi;
407
408     len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
409     buffer = HeapAlloc( GetProcessHeap(), 0, len );
410     if( !buffer )
411         return FALSE;
412
413     wsprintfW( buffer, szFormat, szLink );
414
415     TRACE("starting %s\n",debugstr_w(buffer));
416
417     memset(&si, 0, sizeof(si));
418     si.cb = sizeof(si);
419     if (!CreateProcessW( NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) return FALSE;
420     CloseHandle( pi.hProcess );
421     CloseHandle( pi.hThread );
422
423     return TRUE;
424 }
425
426 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
427 {
428     IShellLinkImpl *This = impl_from_IPersistFile(iface);
429     IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
430     HRESULT r;
431     IStream *stm;
432
433     TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
434
435     if (!pszFileName)
436         return E_FAIL;
437
438     r = SHCreateStreamOnFileW( pszFileName, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &stm );
439     if( SUCCEEDED( r ) )
440     {
441         r = IPersistStream_Save(StreamThis, stm, FALSE);
442         IStream_Release( stm );
443
444         if( SUCCEEDED( r ) )
445         {
446             StartLinkProcessor( pszFileName );
447
448             This->bDirty = FALSE;
449         }
450         else
451         {
452             DeleteFileW( pszFileName );
453             WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
454         }
455     }
456
457     return r;
458 }
459
460 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
461 {
462         IShellLinkImpl *This = impl_from_IPersistFile(iface);
463         FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
464         return NOERROR;
465 }
466
467 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *ppszFileName)
468 {
469         IShellLinkImpl *This = impl_from_IPersistFile(iface);
470         FIXME("(%p)\n",This);
471         return NOERROR;
472 }
473
474 static const IPersistFileVtbl pfvt =
475 {
476         IPersistFile_fnQueryInterface,
477         IPersistFile_fnAddRef,
478         IPersistFile_fnRelease,
479         IPersistFile_fnGetClassID,
480         IPersistFile_fnIsDirty,
481         IPersistFile_fnLoad,
482         IPersistFile_fnSave,
483         IPersistFile_fnSaveCompleted,
484         IPersistFile_fnGetCurFile
485 };
486
487 /************************************************************************
488  * IPersistStream_QueryInterface
489  */
490 static HRESULT WINAPI IPersistStream_fnQueryInterface(
491         IPersistStream* iface,
492         REFIID     riid,
493         VOID**     ppvObj)
494 {
495     IShellLinkImpl *This = impl_from_IPersistStream(iface);
496     return ShellLink_QueryInterface( This, riid, ppvObj );
497 }
498
499 /************************************************************************
500  * IPersistStream_Release
501  */
502 static ULONG WINAPI IPersistStream_fnRelease(
503         IPersistStream* iface)
504 {
505     IShellLinkImpl *This = impl_from_IPersistStream(iface);
506     return IShellLinkA_Release((IShellLinkA*)This);
507 }
508
509 /************************************************************************
510  * IPersistStream_AddRef
511  */
512 static ULONG WINAPI IPersistStream_fnAddRef(
513         IPersistStream* iface)
514 {
515     IShellLinkImpl *This = impl_from_IPersistStream(iface);
516     return ShellLink_AddRef( This );
517 }
518
519 /************************************************************************
520  * IPersistStream_GetClassID
521  *
522  */
523 static HRESULT WINAPI IPersistStream_fnGetClassID(
524         IPersistStream* iface,
525         CLSID* pClassID)
526 {
527     IShellLinkImpl *This = impl_from_IPersistStream(iface);
528     return ShellLink_GetClassID( This, pClassID );
529 }
530
531 /************************************************************************
532  * IPersistStream_IsDirty (IPersistStream)
533  */
534 static HRESULT WINAPI IPersistStream_fnIsDirty(
535         IPersistStream*  iface)
536 {
537         IShellLinkImpl *This = impl_from_IPersistStream(iface);
538
539         TRACE("(%p)\n", This);
540
541         return S_OK;
542 }
543
544
545 static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
546 {
547     DWORD count;
548     USHORT len;
549     LPVOID temp;
550     LPWSTR str;
551     HRESULT r;
552
553     TRACE("%p\n", stm);
554
555     count = 0;
556     r = IStream_Read(stm, &len, sizeof(len), &count);
557     if ( FAILED (r) || ( count != sizeof(len) ) )
558         return E_FAIL;
559
560     if( unicode )
561         len *= sizeof (WCHAR);
562
563     TRACE("reading %d\n", len);
564     temp = HeapAlloc(GetProcessHeap(), 0, len+sizeof(WCHAR));
565     if( !temp )
566         return E_OUTOFMEMORY;
567     count = 0;
568     r = IStream_Read(stm, temp, len, &count);
569     if( FAILED (r) || ( count != len ) )
570     {
571         HeapFree( GetProcessHeap(), 0, temp );
572         return E_FAIL;
573     }
574
575     TRACE("read %s\n", debugstr_an(temp,len));
576
577     /* convert to unicode if necessary */
578     if( !unicode )
579     {
580         count = MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, NULL, 0 );
581         str = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof (WCHAR) );
582         if( !str )
583         {
584             HeapFree( GetProcessHeap(), 0, temp );
585             return E_OUTOFMEMORY;
586         }
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 %d 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( LPCSTR 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 %d serial %08x 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 %08x  string = %s\n", buffer.dbh.dwSignature, debugstr_w(buffer.szwDarwinID));
759
760     if( (buffer.dbh.dwSignature&0xffff0000) != 0xa0000000 )
761     {
762         ERR("Unknown magic number %08x 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 EXP_DARWIN_LINK* shelllink_build_darwinid( LPCWSTR string, DWORD magic )
1017 {
1018     EXP_DARWIN_LINK *buffer;
1019     
1020     buffer = LocalAlloc( LMEM_ZEROINIT, sizeof *buffer );
1021     buffer->dbh.cbSize = sizeof *buffer;
1022     buffer->dbh.dwSignature = magic;
1023     lstrcpynW( buffer->szwDarwinID, string, MAX_PATH );
1024     WideCharToMultiByte(CP_ACP, 0, string, -1, buffer->szDarwinID, MAX_PATH, NULL, NULL );
1025
1026     return buffer;
1027 }
1028
1029 static HRESULT Stream_WriteAdvertiseInfo( IStream* stm, LPCWSTR string, DWORD magic )
1030 {
1031     EXP_DARWIN_LINK *buffer;
1032     ULONG count;
1033     
1034     TRACE("%p\n",stm);
1035
1036     buffer = shelllink_build_darwinid( string, magic );
1037
1038     return IStream_Write( stm, buffer, buffer->dbh.cbSize, &count );
1039 }
1040
1041 /************************************************************************
1042  * IPersistStream_Save (IPersistStream)
1043  *
1044  * FIXME: makes assumptions about byte order
1045  */
1046 static HRESULT WINAPI IPersistStream_fnSave(
1047         IPersistStream*  iface,
1048         IStream*         stm,
1049         BOOL             fClearDirty)
1050 {
1051     LINK_HEADER header;
1052     ULONG   count;
1053     DWORD   zero;
1054     HRESULT r;
1055
1056     IShellLinkImpl *This = impl_from_IPersistStream(iface);
1057
1058     TRACE("%p %p %x\n", This, stm, fClearDirty);
1059
1060     memset(&header, 0, sizeof(header));
1061     header.dwSize = sizeof(header);
1062     header.fStartup = This->iShowCmd;
1063     memcpy(&header.MagicGuid, &CLSID_ShellLink, sizeof(header.MagicGuid) );
1064
1065     header.wHotKey = This->wHotKey;
1066     header.nIcon = This->iIcoNdx;
1067     header.dwFlags = SLDF_UNICODE;   /* strings are in unicode */
1068     if( This->pPidl )
1069         header.dwFlags |= SLDF_HAS_ID_LIST;
1070     if( This->sPath )
1071         header.dwFlags |= SLDF_HAS_LINK_INFO;
1072     if( This->sDescription )
1073         header.dwFlags |= SLDF_HAS_NAME;
1074     if( This->sWorkDir )
1075         header.dwFlags |= SLDF_HAS_WORKINGDIR;
1076     if( This->sArgs )
1077         header.dwFlags |= SLDF_HAS_ARGS;
1078     if( This->sIcoPath )
1079         header.dwFlags |= SLDF_HAS_ICONLOCATION;
1080     if( This->sProduct )
1081         header.dwFlags |= SLDF_HAS_LOGO3ID;
1082     if( This->sComponent )
1083         header.dwFlags |= SLDF_HAS_DARWINID;
1084
1085     SystemTimeToFileTime ( &This->time1, &header.Time1 );
1086     SystemTimeToFileTime ( &This->time2, &header.Time2 );
1087     SystemTimeToFileTime ( &This->time3, &header.Time3 );
1088
1089     /* write the Shortcut header */
1090     r = IStream_Write( stm, &header, sizeof(header), &count );
1091     if( FAILED( r ) )
1092     {
1093         ERR("Write failed at %d\n",__LINE__);
1094         return r;
1095     }
1096
1097     TRACE("Writing pidl\n");
1098
1099     /* write the PIDL to the shortcut */
1100     if( This->pPidl )
1101     {
1102         r = ILSaveToStream( stm, This->pPidl );
1103         if( FAILED( r ) )
1104         {
1105             ERR("Failed to write PIDL at %d\n",__LINE__);
1106             return r;
1107         }
1108     }
1109
1110     if( This->sPath )
1111         Stream_WriteLocationInfo( stm, This->sPath, &This->volume );
1112
1113     if( This->sDescription )
1114         r = Stream_WriteString( stm, This->sDescription );
1115
1116     if( This->sPathRel )
1117         r = Stream_WriteString( stm, This->sPathRel );
1118
1119     if( This->sWorkDir )
1120         r = Stream_WriteString( stm, This->sWorkDir );
1121
1122     if( This->sArgs )
1123         r = Stream_WriteString( stm, This->sArgs );
1124
1125     if( This->sIcoPath )
1126         r = Stream_WriteString( stm, This->sIcoPath );
1127
1128     if( This->sProduct )
1129         r = Stream_WriteAdvertiseInfo( stm, This->sProduct, EXP_SZ_ICON_SIG );
1130
1131     if( This->sComponent )
1132         r = Stream_WriteAdvertiseInfo( stm, This->sComponent, EXP_DARWIN_ID_SIG );
1133
1134     /* the last field is a single zero dword */
1135     zero = 0;
1136     r = IStream_Write( stm, &zero, sizeof zero, &count );
1137
1138     return S_OK;
1139 }
1140
1141 /************************************************************************
1142  * IPersistStream_GetSizeMax (IPersistStream)
1143  */
1144 static HRESULT WINAPI IPersistStream_fnGetSizeMax(
1145         IPersistStream*  iface,
1146         ULARGE_INTEGER*  pcbSize)
1147 {
1148         IShellLinkImpl *This = impl_from_IPersistStream(iface);
1149
1150         TRACE("(%p)\n", This);
1151
1152         return E_NOTIMPL;
1153 }
1154
1155 static const IPersistStreamVtbl psvt =
1156 {
1157         IPersistStream_fnQueryInterface,
1158         IPersistStream_fnAddRef,
1159         IPersistStream_fnRelease,
1160         IPersistStream_fnGetClassID,
1161         IPersistStream_fnIsDirty,
1162         IPersistStream_fnLoad,
1163         IPersistStream_fnSave,
1164         IPersistStream_fnGetSizeMax
1165 };
1166
1167 /**************************************************************************
1168  *        IShellLink_Constructor
1169  */
1170 HRESULT WINAPI IShellLink_Constructor( IUnknown *pUnkOuter,
1171                REFIID riid, LPVOID *ppv )
1172 {
1173         IShellLinkImpl * sl;
1174         HRESULT r;
1175
1176         TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
1177
1178         *ppv = NULL;
1179
1180         if (pUnkOuter)
1181             return CLASS_E_NOAGGREGATION;
1182         sl = LocalAlloc(LMEM_ZEROINIT,sizeof(IShellLinkImpl));
1183         if (!sl)
1184             return E_OUTOFMEMORY;
1185
1186         sl->ref = 1;
1187         sl->lpVtbl = &slvt;
1188         sl->lpvtblw = &slvtw;
1189         sl->lpvtblPersistFile = &pfvt;
1190         sl->lpvtblPersistStream = &psvt;
1191         sl->lpvtblShellLinkDataList = &dlvt;
1192         sl->lpvtblShellExtInit = &eivt;
1193         sl->lpvtblContextMenu = &cmvt;
1194         sl->lpvtblObjectWithSite = &owsvt;
1195         sl->iShowCmd = SW_SHOWNORMAL;
1196         sl->bDirty = FALSE;
1197         sl->iIdOpen = -1;
1198         sl->site = NULL;
1199
1200         TRACE("(%p)->()\n",sl);
1201
1202         r = ShellLink_QueryInterface( sl, riid, ppv );
1203         ShellLink_Release( sl );
1204         return r;
1205 }
1206
1207
1208 static BOOL SHELL_ExistsFileW(LPCWSTR path)
1209 {
1210     if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
1211         return FALSE;
1212     return TRUE;
1213 }
1214
1215 /**************************************************************************
1216  *  ShellLink_UpdatePath
1217  *      update absolute path in sPath using relative path in sPathRel
1218  */
1219 static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
1220 {
1221     if (!path || !psPath)
1222         return E_INVALIDARG;
1223
1224     if (!*psPath && sPathRel) {
1225         WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
1226         LPWSTR final = NULL;
1227
1228         /* first try if [directory of link file] + [relative path] finds an existing file */
1229
1230         GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
1231         if( !final )
1232             final = buffer;
1233         lstrcpyW(final, sPathRel);
1234
1235         *abs_path = '\0';
1236
1237         if (SHELL_ExistsFileW(buffer)) {
1238             if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
1239                 lstrcpyW(abs_path, buffer);
1240         } else {
1241             /* try if [working directory] + [relative path] finds an existing file */
1242             if (sWorkDir) {
1243                 lstrcpyW(buffer, sWorkDir);
1244                 lstrcpyW(PathAddBackslashW(buffer), sPathRel);
1245
1246                 if (SHELL_ExistsFileW(buffer))
1247                     if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
1248                         lstrcpyW(abs_path, buffer);
1249             }
1250         }
1251
1252         /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
1253         if (!*abs_path)
1254             lstrcpyW(abs_path, sPathRel);
1255
1256         *psPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(abs_path)+1)*sizeof(WCHAR));
1257         if (!*psPath)
1258             return E_OUTOFMEMORY;
1259
1260         lstrcpyW(*psPath, abs_path);
1261     }
1262
1263     return S_OK;
1264 }
1265
1266 /**************************************************************************
1267  *        IShellLink_ConstructFromFile
1268  */
1269 HRESULT WINAPI IShellLink_ConstructFromFile( IUnknown* pUnkOuter, REFIID riid,
1270                LPCITEMIDLIST pidl, LPVOID* ppv)
1271 {
1272     IShellLinkW* psl;
1273
1274     HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl);
1275
1276     if (SUCCEEDED(hr)) {
1277         IPersistFile* ppf;
1278
1279         *ppv = NULL;
1280
1281         hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
1282
1283         if (SUCCEEDED(hr)) {
1284             WCHAR path[MAX_PATH];
1285
1286             if (SHGetPathFromIDListW(pidl, path)) 
1287                 hr = IPersistFile_Load(ppf, path, 0);
1288             else
1289                 hr = E_FAIL;
1290
1291             if (SUCCEEDED(hr))
1292                 *ppv = (IUnknown*) psl;
1293
1294             IPersistFile_Release(ppf);
1295         }
1296
1297         if (!*ppv)
1298             IShellLinkW_Release(psl);
1299     }
1300
1301     return hr;
1302 }
1303
1304 /**************************************************************************
1305  *  IShellLinkA_QueryInterface
1306  */
1307 static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
1308 {
1309     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1310     return ShellLink_QueryInterface( This, riid, ppvObj );
1311 }
1312
1313 /******************************************************************************
1314  * IShellLinkA_AddRef
1315  */
1316 static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
1317 {
1318     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1319     return ShellLink_AddRef( This );
1320 }
1321
1322 /******************************************************************************
1323  *      IShellLinkA_Release
1324  */
1325 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
1326 {
1327     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1328     return ShellLink_Release( This );
1329 }
1330
1331 static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
1332                   INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1333 {
1334     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1335
1336     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1337           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1338
1339     if (This->sComponent || This->sProduct)
1340         return S_FALSE;
1341
1342     if (cchMaxPath)
1343         pszFile[0] = 0;
1344     if (This->sPath)
1345         WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
1346                              pszFile, cchMaxPath, NULL, NULL);
1347
1348     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1349
1350     return S_OK;
1351 }
1352
1353 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
1354 {
1355     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1356
1357     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1358
1359     return IShellLinkW_GetIDList((IShellLinkW*)&(This->lpvtblw), ppidl);
1360 }
1361
1362 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
1363 {
1364     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1365
1366     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1367
1368     if (This->pPidl)
1369         ILFree(This->pPidl);
1370     This->pPidl = ILClone (pidl);
1371     This->bDirty = TRUE;
1372
1373     return S_OK;
1374 }
1375
1376 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
1377 {
1378     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1379
1380     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1381
1382     if( cchMaxName )
1383         pszName[0] = 0;
1384     if( This->sDescription )
1385         WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
1386             pszName, cchMaxName, NULL, NULL);
1387
1388     return S_OK;
1389 }
1390
1391 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
1392 {
1393     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1394
1395     TRACE("(%p)->(pName=%s)\n", This, pszName);
1396
1397     HeapFree(GetProcessHeap(), 0, This->sDescription);
1398     This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
1399     if ( !This->sDescription )
1400         return E_OUTOFMEMORY;
1401
1402     This->bDirty = TRUE;
1403
1404     return S_OK;
1405 }
1406
1407 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
1408 {
1409     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1410
1411     TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1412
1413     if( cchMaxPath )
1414         pszDir[0] = 0;
1415     if( This->sWorkDir )
1416         WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
1417                              pszDir, cchMaxPath, NULL, NULL);
1418
1419     return S_OK;
1420 }
1421
1422 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
1423 {
1424     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1425
1426     TRACE("(%p)->(dir=%s)\n",This, pszDir);
1427
1428     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1429     This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
1430     if ( !This->sWorkDir )
1431         return E_OUTOFMEMORY;
1432
1433     This->bDirty = TRUE;
1434
1435     return S_OK;
1436 }
1437
1438 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
1439 {
1440     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1441
1442     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1443
1444     if( cchMaxPath )
1445         pszArgs[0] = 0;
1446     if( This->sArgs )
1447         WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
1448                              pszArgs, cchMaxPath, NULL, NULL);
1449
1450     return S_OK;
1451 }
1452
1453 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
1454 {
1455     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1456
1457     TRACE("(%p)->(args=%s)\n",This, pszArgs);
1458
1459     HeapFree(GetProcessHeap(), 0, This->sArgs);
1460     This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
1461     if( !This->sArgs )
1462         return E_OUTOFMEMORY;
1463
1464     This->bDirty = TRUE;
1465
1466     return S_OK;
1467 }
1468
1469 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
1470 {
1471     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1472
1473     TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1474
1475     *pwHotkey = This->wHotKey;
1476
1477     return S_OK;
1478 }
1479
1480 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
1481 {
1482     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1483
1484     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1485
1486     This->wHotKey = wHotkey;
1487     This->bDirty = TRUE;
1488
1489     return S_OK;
1490 }
1491
1492 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
1493 {
1494     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1495
1496     TRACE("(%p)->(%p)\n",This, piShowCmd);
1497     *piShowCmd = This->iShowCmd;
1498     return S_OK;
1499 }
1500
1501 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
1502 {
1503     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1504
1505     TRACE("(%p) %d\n",This, iShowCmd);
1506
1507     This->iShowCmd = iShowCmd;
1508     This->bDirty = TRUE;
1509
1510     return NOERROR;
1511 }
1512
1513 static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPCITEMIDLIST pidl,
1514                                           LPSTR pszIconPath, int cchIconPath, int* piIcon)
1515 {
1516     LPCITEMIDLIST pidlLast;
1517
1518     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1519
1520     if (SUCCEEDED(hr)) {
1521         IExtractIconA* pei;
1522
1523         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconA, NULL, (LPVOID*)&pei);
1524
1525         if (SUCCEEDED(hr)) {
1526             hr = IExtractIconA_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1527
1528             IExtractIconA_Release(pei);
1529         }
1530
1531         IShellFolder_Release(psf);
1532     }
1533
1534     return hr;
1535 }
1536
1537 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1538 {
1539     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1540
1541     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1542
1543     pszIconPath[0] = 0;
1544     *piIcon = This->iIcoNdx;
1545
1546     if (This->sIcoPath)
1547     {
1548         WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1549         return S_OK;
1550     }
1551
1552     if (This->pPidl || This->sPath)
1553     {
1554         IShellFolder* pdsk;
1555
1556         HRESULT hr = SHGetDesktopFolder(&pdsk);
1557
1558         if (SUCCEEDED(hr))
1559         {
1560             /* first look for an icon using the PIDL (if present) */
1561             if (This->pPidl)
1562                 hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1563             else
1564                 hr = E_FAIL;
1565
1566             /* if we couldn't find an icon yet, look for it using the file system path */
1567             if (FAILED(hr) && This->sPath)
1568             {
1569                 LPITEMIDLIST pidl;
1570
1571                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1572
1573                 if (SUCCEEDED(hr)) {
1574                     hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1575
1576                     SHFree(pidl);
1577                 }
1578             }
1579
1580             IShellFolder_Release(pdsk);
1581         }
1582
1583         return hr;
1584     }
1585     return S_OK;
1586 }
1587
1588 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
1589 {
1590     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1591
1592     TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1593
1594     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1595     This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1596     if ( !This->sIcoPath )
1597         return E_OUTOFMEMORY;
1598
1599     This->iIcoNdx = iIcon;
1600     This->bDirty = TRUE;
1601
1602     return S_OK;
1603 }
1604
1605 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1606 {
1607     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1608
1609     TRACE("(%p)->(path=%s %x)\n",This, pszPathRel, dwReserved);
1610
1611     HeapFree(GetProcessHeap(), 0, This->sPathRel);
1612     This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1613     This->bDirty = TRUE;
1614
1615     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1616 }
1617
1618 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1619 {
1620     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1621
1622     TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
1623
1624     return IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, fFlags );
1625 }
1626
1627 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1628 {
1629     HRESULT r;
1630     LPWSTR str;
1631     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1632
1633     TRACE("(%p)->(path=%s)\n",This, pszFile);
1634
1635     str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
1636     if( !str ) 
1637         return E_OUTOFMEMORY;
1638
1639     r = IShellLinkW_SetPath((IShellLinkW*)&(This->lpvtblw), str);
1640     HeapFree( GetProcessHeap(), 0, str );
1641
1642     return r;
1643 }
1644
1645 /**************************************************************************
1646 * IShellLink Implementation
1647 */
1648
1649 static const IShellLinkAVtbl slvt =
1650 {
1651     IShellLinkA_fnQueryInterface,
1652     IShellLinkA_fnAddRef,
1653     IShellLinkA_fnRelease,
1654     IShellLinkA_fnGetPath,
1655     IShellLinkA_fnGetIDList,
1656     IShellLinkA_fnSetIDList,
1657     IShellLinkA_fnGetDescription,
1658     IShellLinkA_fnSetDescription,
1659     IShellLinkA_fnGetWorkingDirectory,
1660     IShellLinkA_fnSetWorkingDirectory,
1661     IShellLinkA_fnGetArguments,
1662     IShellLinkA_fnSetArguments,
1663     IShellLinkA_fnGetHotkey,
1664     IShellLinkA_fnSetHotkey,
1665     IShellLinkA_fnGetShowCmd,
1666     IShellLinkA_fnSetShowCmd,
1667     IShellLinkA_fnGetIconLocation,
1668     IShellLinkA_fnSetIconLocation,
1669     IShellLinkA_fnSetRelativePath,
1670     IShellLinkA_fnResolve,
1671     IShellLinkA_fnSetPath
1672 };
1673
1674
1675 /**************************************************************************
1676  *  IShellLinkW_fnQueryInterface
1677  */
1678 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1679   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1680 {
1681     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1682     return ShellLink_QueryInterface( This, riid, ppvObj );
1683 }
1684
1685 /******************************************************************************
1686  * IShellLinkW_fnAddRef
1687  */
1688 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1689 {
1690     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1691     return ShellLink_AddRef( This );
1692 }
1693
1694 /******************************************************************************
1695  * IShellLinkW_fnRelease
1696  */
1697 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1698 {
1699     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1700     return ShellLink_Release( This );
1701 }
1702
1703 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1704 {
1705     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1706
1707     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1708           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1709
1710     if (This->sComponent || This->sProduct)
1711         return S_FALSE;
1712
1713     if (cchMaxPath)
1714         pszFile[0] = 0;
1715     if (This->sPath)
1716         lstrcpynW( pszFile, This->sPath, cchMaxPath );
1717
1718     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1719
1720     return S_OK;
1721 }
1722
1723 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1724 {
1725     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1726
1727     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1728
1729     if (!This->pPidl)
1730     {
1731         *ppidl = NULL;
1732         return S_FALSE;
1733     }
1734     *ppidl = ILClone(This->pPidl);
1735     return S_OK;
1736 }
1737
1738 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1739 {
1740     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1741
1742     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1743
1744     if( This->pPidl )
1745         ILFree( This->pPidl );
1746     This->pPidl = ILClone( pidl );
1747     if( !This->pPidl )
1748         return E_FAIL;
1749
1750     This->bDirty = TRUE;
1751
1752     return S_OK;
1753 }
1754
1755 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1756 {
1757     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1758
1759     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1760
1761     pszName[0] = 0;
1762     if( This->sDescription )
1763         lstrcpynW( pszName, This->sDescription, cchMaxName );
1764
1765     return S_OK;
1766 }
1767
1768 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1769 {
1770     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1771
1772     TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1773
1774     HeapFree(GetProcessHeap(), 0, This->sDescription);
1775     This->sDescription = HeapAlloc( GetProcessHeap(), 0,
1776                                     (lstrlenW( pszName )+1)*sizeof(WCHAR) );
1777     if ( !This->sDescription )
1778         return E_OUTOFMEMORY;
1779
1780     lstrcpyW( This->sDescription, pszName );
1781     This->bDirty = TRUE;
1782
1783     return S_OK;
1784 }
1785
1786 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1787 {
1788     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1789
1790     TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1791
1792     if( cchMaxPath )
1793         pszDir[0] = 0;
1794     if( This->sWorkDir )
1795         lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
1796
1797     return S_OK;
1798 }
1799
1800 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1801 {
1802     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1803
1804     TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1805
1806     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1807     This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
1808                                 (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
1809     if ( !This->sWorkDir )
1810         return E_OUTOFMEMORY;
1811     lstrcpyW( This->sWorkDir, pszDir );
1812     This->bDirty = TRUE;
1813
1814     return S_OK;
1815 }
1816
1817 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1818 {
1819     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1820
1821     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1822
1823     if( cchMaxPath )
1824         pszArgs[0] = 0;
1825     if( This->sArgs )
1826         lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
1827
1828     return NOERROR;
1829 }
1830
1831 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1832 {
1833     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1834
1835     TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1836
1837     HeapFree(GetProcessHeap(), 0, This->sArgs);
1838     This->sArgs = HeapAlloc( GetProcessHeap(), 0,
1839                              (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
1840     if ( !This->sArgs )
1841         return E_OUTOFMEMORY;
1842     lstrcpyW( This->sArgs, pszArgs );
1843     This->bDirty = TRUE;
1844
1845     return S_OK;
1846 }
1847
1848 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1849 {
1850     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1851
1852     TRACE("(%p)->(%p)\n",This, pwHotkey);
1853
1854     *pwHotkey=This->wHotKey;
1855
1856     return S_OK;
1857 }
1858
1859 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1860 {
1861     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1862
1863     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1864
1865     This->wHotKey = wHotkey;
1866     This->bDirty = TRUE;
1867
1868     return S_OK;
1869 }
1870
1871 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1872 {
1873     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1874
1875     TRACE("(%p)->(%p)\n",This, piShowCmd);
1876
1877     *piShowCmd = This->iShowCmd;
1878
1879     return S_OK;
1880 }
1881
1882 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1883 {
1884     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1885
1886     This->iShowCmd = iShowCmd;
1887     This->bDirty = TRUE;
1888
1889     return S_OK;
1890 }
1891
1892 static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPCITEMIDLIST pidl,
1893                                           LPWSTR pszIconPath, int cchIconPath, int* piIcon)
1894 {
1895     LPCITEMIDLIST pidlLast;
1896
1897     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1898
1899     if (SUCCEEDED(hr)) {
1900         IExtractIconW* pei;
1901
1902         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconW, NULL, (LPVOID*)&pei);
1903
1904         if (SUCCEEDED(hr)) {
1905             hr = IExtractIconW_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1906
1907             IExtractIconW_Release(pei);
1908         }
1909
1910         IShellFolder_Release(psf);
1911     }
1912
1913     return hr;
1914 }
1915
1916 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1917 {
1918     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1919
1920     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1921
1922     pszIconPath[0] = 0;
1923     *piIcon = This->iIcoNdx;
1924
1925     if (This->sIcoPath)
1926     {
1927         lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
1928         return S_OK;
1929     }
1930
1931     if (This->pPidl || This->sPath)
1932     {
1933         IShellFolder* pdsk;
1934
1935         HRESULT hr = SHGetDesktopFolder(&pdsk);
1936
1937         if (SUCCEEDED(hr))
1938         {
1939             /* first look for an icon using the PIDL (if present) */
1940             if (This->pPidl)
1941                 hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1942             else
1943                 hr = E_FAIL;
1944
1945             /* if we couldn't find an icon yet, look for it using the file system path */
1946             if (FAILED(hr) && This->sPath)
1947             {
1948                 LPITEMIDLIST pidl;
1949
1950                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1951
1952                 if (SUCCEEDED(hr))
1953                 {
1954                     hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1955
1956                     SHFree(pidl);
1957                 }
1958             }
1959
1960             IShellFolder_Release(pdsk);
1961         }
1962         return hr;
1963     }
1964     return S_OK;
1965 }
1966
1967 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
1968 {
1969     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1970
1971     TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1972
1973     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1974     This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
1975                                 (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
1976     if ( !This->sIcoPath )
1977         return E_OUTOFMEMORY;
1978     lstrcpyW( This->sIcoPath, pszIconPath );
1979
1980     This->iIcoNdx = iIcon;
1981     This->bDirty = TRUE;
1982
1983     return S_OK;
1984 }
1985
1986 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
1987 {
1988     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1989
1990     TRACE("(%p)->(path=%s %x)\n",This, debugstr_w(pszPathRel), dwReserved);
1991
1992     HeapFree(GetProcessHeap(), 0, This->sPathRel);
1993     This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
1994                                 (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
1995     if ( !This->sPathRel )
1996         return E_OUTOFMEMORY;
1997     lstrcpyW( This->sPathRel, pszPathRel );
1998     This->bDirty = TRUE;
1999
2000     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
2001 }
2002
2003 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
2004 {
2005     HRESULT hr = S_OK;
2006     BOOL bSuccess;
2007
2008     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
2009
2010     TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
2011
2012     /*FIXME: use IResolveShellLink interface */
2013
2014     if (!This->sPath && This->pPidl) {
2015         WCHAR buffer[MAX_PATH];
2016
2017         bSuccess = SHGetPathFromIDListW(This->pPidl, buffer);
2018
2019         if (bSuccess && *buffer) {
2020             This->sPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
2021             if (!This->sPath)
2022                 return E_OUTOFMEMORY;
2023
2024             lstrcpyW(This->sPath, buffer);
2025
2026             This->bDirty = TRUE;
2027         } else
2028             hr = S_OK;    /* don't report an error occurred while just caching information */
2029     }
2030
2031     if (!This->sIcoPath && This->sPath) {
2032         This->sIcoPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
2033         if (!This->sIcoPath)
2034             return E_OUTOFMEMORY;
2035
2036         lstrcpyW(This->sIcoPath, This->sPath);
2037         This->iIcoNdx = 0;
2038
2039         This->bDirty = TRUE;
2040     }
2041
2042     return hr;
2043 }
2044
2045 static LPWSTR ShellLink_GetAdvertisedArg(LPCWSTR str)
2046 {
2047     LPWSTR ret;
2048     LPCWSTR p;
2049     DWORD len;
2050
2051     if( !str )
2052         return NULL;
2053
2054     p = strchrW( str, ':' );
2055     if( !p )
2056         return NULL;
2057     len = p - str;
2058     ret = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
2059     if( !ret )
2060         return ret;
2061     memcpy( ret, str, sizeof(WCHAR)*len );
2062     ret[len] = 0;
2063     return ret;
2064 }
2065
2066 static HRESULT ShellLink_SetAdvertiseInfo(IShellLinkImpl *This, LPCWSTR str)
2067 {
2068     LPCWSTR szComponent = NULL, szProduct = NULL, p;
2069     WCHAR szGuid[39];
2070     HRESULT r;
2071     GUID guid;
2072     int len;
2073
2074     while( str[0] )
2075     {
2076         /* each segment must start with two colons */
2077         if( str[0] != ':' || str[1] != ':' )
2078             return E_FAIL;
2079
2080         /* the last segment is just two colons */
2081         if( !str[2] )
2082             break;
2083         str += 2;
2084
2085         /* there must be a colon straight after a guid */
2086         p = strchrW( str, ':' );
2087         if( !p )
2088             return E_FAIL;
2089         len = p - str;
2090         if( len != 38 )
2091             return E_FAIL;
2092
2093         /* get the guid, and check it's validly formatted */
2094         memcpy( szGuid, str, sizeof(WCHAR)*len );
2095         szGuid[len] = 0;
2096         r = CLSIDFromString( szGuid, &guid );
2097         if( r != S_OK )
2098             return r;
2099         str = p + 1;
2100
2101         /* match it up to a guid that we care about */
2102         if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutComponent ) && !szComponent )
2103             szComponent = str;
2104         else if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutProduct ) && !szProduct )
2105             szProduct = str;
2106         else
2107             return E_FAIL;
2108
2109         /* skip to the next field */
2110         str = strchrW( str, ':' );
2111         if( !str )
2112             return E_FAIL;
2113     }
2114
2115     /* we have to have a component for an advertised shortcut */
2116     if( !szComponent )
2117         return E_FAIL;
2118
2119     This->sComponent = ShellLink_GetAdvertisedArg( szComponent );
2120     This->sProduct = ShellLink_GetAdvertisedArg( szProduct );
2121
2122     TRACE("Component = %s\n", debugstr_w(This->sComponent));
2123     TRACE("Product = %s\n", debugstr_w(This->sProduct));
2124
2125     return S_OK;
2126 }
2127
2128 static BOOL ShellLink_GetVolumeInfo(LPCWSTR path, volume_info *volume)
2129 {
2130     const int label_sz = sizeof volume->label/sizeof volume->label[0];
2131     WCHAR drive[4] = { path[0], ':', '\\', 0 };
2132     BOOL r;
2133
2134     volume->type = GetDriveTypeW(drive);
2135     r = GetVolumeInformationW(drive, volume->label, label_sz,
2136                               &volume->serial, NULL, NULL, NULL, 0);
2137     TRACE("r = %d type %d serial %08x name %s\n", r,
2138           volume->type, volume->serial, debugstr_w(volume->label));
2139     return r;
2140 }
2141
2142 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
2143 {
2144     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
2145     WCHAR buffer[MAX_PATH];
2146     LPWSTR fname, unquoted = NULL;
2147     HRESULT hr = S_OK;
2148     UINT len;
2149
2150     TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
2151
2152     /* quotes at the ends of the string are stripped */
2153     len = lstrlenW(pszFile);
2154     if (pszFile[0] == '"' && pszFile[len-1] == '"')
2155     {
2156         unquoted = strdupW(pszFile);
2157         PathUnquoteSpacesW(unquoted);
2158         pszFile = unquoted;
2159     }
2160
2161     /* any other quote marks are invalid */
2162     if (strchrW(pszFile, '"'))
2163         return S_FALSE;
2164
2165     HeapFree(GetProcessHeap(), 0, This->sPath);
2166     This->sPath = NULL;
2167
2168     HeapFree(GetProcessHeap(), 0, This->sComponent);
2169     This->sComponent = NULL;
2170
2171     if (This->pPidl)
2172         ILFree(This->pPidl);
2173     This->pPidl = NULL;
2174
2175     if (S_OK != ShellLink_SetAdvertiseInfo( This, pszFile ))
2176     {
2177         if (*pszFile == '\0')
2178             *buffer = '\0';
2179         else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
2180             return E_FAIL;
2181         else if(!PathFileExistsW(buffer) &&
2182                 !SearchPathW(NULL, pszFile, NULL, MAX_PATH, buffer, NULL))
2183           hr = S_FALSE;
2184
2185         This->pPidl = SHSimpleIDListFromPathW(pszFile);
2186         ShellLink_GetVolumeInfo(buffer, &This->volume);
2187
2188         This->sPath = HeapAlloc( GetProcessHeap(), 0,
2189                              (lstrlenW( buffer )+1) * sizeof (WCHAR) );
2190         if (!This->sPath)
2191             return E_OUTOFMEMORY;
2192
2193         lstrcpyW(This->sPath, buffer);
2194     }
2195     This->bDirty = TRUE;
2196     HeapFree(GetProcessHeap(), 0, unquoted);
2197
2198     return hr;
2199 }
2200
2201 /**************************************************************************
2202 * IShellLinkW Implementation
2203 */
2204
2205 static const IShellLinkWVtbl slvtw =
2206 {
2207     IShellLinkW_fnQueryInterface,
2208     IShellLinkW_fnAddRef,
2209     IShellLinkW_fnRelease,
2210     IShellLinkW_fnGetPath,
2211     IShellLinkW_fnGetIDList,
2212     IShellLinkW_fnSetIDList,
2213     IShellLinkW_fnGetDescription,
2214     IShellLinkW_fnSetDescription,
2215     IShellLinkW_fnGetWorkingDirectory,
2216     IShellLinkW_fnSetWorkingDirectory,
2217     IShellLinkW_fnGetArguments,
2218     IShellLinkW_fnSetArguments,
2219     IShellLinkW_fnGetHotkey,
2220     IShellLinkW_fnSetHotkey,
2221     IShellLinkW_fnGetShowCmd,
2222     IShellLinkW_fnSetShowCmd,
2223     IShellLinkW_fnGetIconLocation,
2224     IShellLinkW_fnSetIconLocation,
2225     IShellLinkW_fnSetRelativePath,
2226     IShellLinkW_fnResolve,
2227     IShellLinkW_fnSetPath
2228 };
2229
2230 static HRESULT WINAPI
2231 ShellLink_DataList_QueryInterface( IShellLinkDataList* iface, REFIID riid, void** ppvObject)
2232 {
2233     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2234     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2235 }
2236
2237 static ULONG WINAPI
2238 ShellLink_DataList_AddRef( IShellLinkDataList* iface )
2239 {
2240     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2241     return IShellLinkA_AddRef((IShellLinkA*)This);
2242 }
2243
2244 static ULONG WINAPI
2245 ShellLink_DataList_Release( IShellLinkDataList* iface )
2246 {
2247     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2248     return ShellLink_Release( This );
2249 }
2250
2251 static HRESULT WINAPI
2252 ShellLink_AddDataBlock( IShellLinkDataList* iface, void* pDataBlock )
2253 {
2254     FIXME("\n");
2255     return E_NOTIMPL;
2256 }
2257
2258 static HRESULT WINAPI
2259 ShellLink_CopyDataBlock( IShellLinkDataList* iface, DWORD dwSig, void** ppDataBlock )
2260 {
2261     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2262     LPVOID block = NULL;
2263     HRESULT r = E_FAIL;
2264
2265     TRACE("%p %08x %p\n", iface, dwSig, ppDataBlock );
2266
2267     switch (dwSig)
2268     {
2269     case EXP_DARWIN_ID_SIG:
2270         if (!This->sComponent)
2271             break;
2272         block = shelllink_build_darwinid( This->sComponent, dwSig );
2273         r = S_OK;
2274         break;
2275     case EXP_SZ_LINK_SIG:
2276     case NT_CONSOLE_PROPS_SIG:
2277     case NT_FE_CONSOLE_PROPS_SIG:
2278     case EXP_SPECIAL_FOLDER_SIG:
2279     case EXP_SZ_ICON_SIG:
2280         FIXME("valid but unhandled datablock %08x\n", dwSig);
2281         break;
2282     default:
2283         ERR("unknown datablock %08x\n", dwSig);
2284     }
2285     *ppDataBlock = block;
2286     return r;
2287 }
2288
2289 static HRESULT WINAPI
2290 ShellLink_RemoveDataBlock( IShellLinkDataList* iface, DWORD dwSig )
2291 {
2292     FIXME("\n");
2293     return E_NOTIMPL;
2294 }
2295
2296 static HRESULT WINAPI
2297 ShellLink_GetFlags( IShellLinkDataList* iface, DWORD* pdwFlags )
2298 {
2299     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2300     DWORD flags = 0;
2301
2302     FIXME("%p %p\n", This, pdwFlags );
2303
2304     /* FIXME: add more */
2305     if (This->sArgs)
2306         flags |= SLDF_HAS_ARGS;
2307     if (This->sComponent)
2308         flags |= SLDF_HAS_DARWINID;
2309     if (This->sIcoPath)
2310         flags |= SLDF_HAS_ICONLOCATION;
2311     if (This->sProduct)
2312         flags |= SLDF_HAS_LOGO3ID;
2313     if (This->pPidl)
2314         flags |= SLDF_HAS_ID_LIST;
2315
2316     *pdwFlags = flags;
2317
2318     return S_OK;
2319 }
2320
2321 static HRESULT WINAPI
2322 ShellLink_SetFlags( IShellLinkDataList* iface, DWORD dwFlags )
2323 {
2324     FIXME("\n");
2325     return E_NOTIMPL;
2326 }
2327
2328 static const IShellLinkDataListVtbl dlvt =
2329 {
2330     ShellLink_DataList_QueryInterface,
2331     ShellLink_DataList_AddRef,
2332     ShellLink_DataList_Release,
2333     ShellLink_AddDataBlock,
2334     ShellLink_CopyDataBlock,
2335     ShellLink_RemoveDataBlock,
2336     ShellLink_GetFlags,
2337     ShellLink_SetFlags
2338 };
2339
2340 static HRESULT WINAPI
2341 ShellLink_ExtInit_QueryInterface( IShellExtInit* iface, REFIID riid, void** ppvObject )
2342 {
2343     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2344     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2345 }
2346
2347 static ULONG WINAPI
2348 ShellLink_ExtInit_AddRef( IShellExtInit* iface )
2349 {
2350     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2351     return IShellLinkA_AddRef((IShellLinkA*)This);
2352 }
2353
2354 static ULONG WINAPI
2355 ShellLink_ExtInit_Release( IShellExtInit* iface )
2356 {
2357     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2358     return ShellLink_Release( This );
2359 }
2360
2361 /**************************************************************************
2362  * ShellLink implementation of IShellExtInit::Initialize()
2363  *
2364  * Loads the shelllink from the dataobject the shell is pointing to.
2365  */
2366 static HRESULT WINAPI
2367 ShellLink_ExtInit_Initialize( IShellExtInit* iface, LPCITEMIDLIST pidlFolder,
2368                               IDataObject *pdtobj, HKEY hkeyProgID )
2369 {
2370     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2371     FORMATETC format;
2372     STGMEDIUM stgm;
2373     UINT count;
2374     HRESULT r = E_FAIL;
2375
2376     TRACE("%p %p %p %p\n", This, pidlFolder, pdtobj, hkeyProgID );
2377
2378     if( !pdtobj )
2379         return r;
2380
2381     format.cfFormat = CF_HDROP;
2382     format.ptd = NULL;
2383     format.dwAspect = DVASPECT_CONTENT;
2384     format.lindex = -1;
2385     format.tymed = TYMED_HGLOBAL;
2386
2387     if( FAILED( IDataObject_GetData( pdtobj, &format, &stgm ) ) )
2388         return r;
2389
2390     count = DragQueryFileW( stgm.u.hGlobal, -1, NULL, 0 );
2391     if( count == 1 )
2392     {
2393         LPWSTR path;
2394
2395         count = DragQueryFileW( stgm.u.hGlobal, 0, NULL, 0 );
2396         count++;
2397         path = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
2398         if( path )
2399         {
2400             IPersistFile *pf = (IPersistFile*) &This->lpvtblPersistFile;
2401
2402             count = DragQueryFileW( stgm.u.hGlobal, 0, path, count );
2403             r = IPersistFile_Load( pf, path, 0 );
2404             HeapFree( GetProcessHeap(), 0, path );
2405         }
2406     }
2407     ReleaseStgMedium( &stgm );
2408
2409     return r;
2410 }
2411
2412 static const IShellExtInitVtbl eivt =
2413 {
2414     ShellLink_ExtInit_QueryInterface,
2415     ShellLink_ExtInit_AddRef,
2416     ShellLink_ExtInit_Release,
2417     ShellLink_ExtInit_Initialize
2418 };
2419
2420 static HRESULT WINAPI
2421 ShellLink_ContextMenu_QueryInterface( IContextMenu* iface, REFIID riid, void** ppvObject )
2422 {
2423     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2424     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2425 }
2426
2427 static ULONG WINAPI
2428 ShellLink_ContextMenu_AddRef( IContextMenu* iface )
2429 {
2430     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2431     return IShellLinkA_AddRef((IShellLinkA*)This);
2432 }
2433
2434 static ULONG WINAPI
2435 ShellLink_ContextMenu_Release( IContextMenu* iface )
2436 {
2437     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2438     return ShellLink_Release( This );
2439 }
2440
2441 static HRESULT WINAPI
2442 ShellLink_QueryContextMenu( IContextMenu* iface, HMENU hmenu, UINT indexMenu,
2443                             UINT idCmdFirst, UINT idCmdLast, UINT uFlags )
2444 {
2445     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2446     static WCHAR szOpen[] = { 'O','p','e','n',0 };
2447     MENUITEMINFOW mii;
2448     int id = 1;
2449
2450     TRACE("%p %p %u %u %u %u\n", This,
2451           hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
2452
2453     if ( !hmenu )
2454         return E_INVALIDARG;
2455
2456     memset( &mii, 0, sizeof mii );
2457     mii.cbSize = sizeof mii;
2458     mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
2459     mii.dwTypeData = szOpen;
2460     mii.cch = strlenW( mii.dwTypeData );
2461     mii.wID = idCmdFirst + id++;
2462     mii.fState = MFS_DEFAULT | MFS_ENABLED;
2463     mii.fType = MFT_STRING;
2464     if (!InsertMenuItemW( hmenu, indexMenu, TRUE, &mii ))
2465         return E_FAIL;
2466     This->iIdOpen = 0;
2467
2468     return MAKE_HRESULT( SEVERITY_SUCCESS, 0, id );
2469 }
2470
2471 static LPWSTR
2472 shelllink_get_msi_component_path( LPWSTR component )
2473 {
2474     LPWSTR path;
2475     DWORD r, sz = 0;
2476
2477     r = CommandLineFromMsiDescriptor( component, NULL, &sz );
2478     if (r != ERROR_SUCCESS)
2479          return NULL;
2480
2481     sz++;
2482     path = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
2483     r = CommandLineFromMsiDescriptor( component, path, &sz );
2484     if (r != ERROR_SUCCESS)
2485     {
2486         HeapFree( GetProcessHeap(), 0, path );
2487         path = NULL;
2488     }
2489
2490     TRACE("returning %s\n", debugstr_w( path ) );
2491
2492     return path;
2493 }
2494
2495 static HRESULT WINAPI
2496 ShellLink_InvokeCommand( IContextMenu* iface, LPCMINVOKECOMMANDINFO lpici )
2497 {
2498     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2499     static const WCHAR szOpen[] = { 'O','p','e','n',0 };
2500     SHELLEXECUTEINFOW sei;
2501     HWND hwnd = NULL; /* FIXME: get using interface set from IObjectWithSite */
2502     LPWSTR args = NULL;
2503     LPWSTR path = NULL;
2504     HRESULT r;
2505
2506     TRACE("%p %p\n", This, lpici );
2507
2508     if ( lpici->cbSize < sizeof (CMINVOKECOMMANDINFO) )
2509         return E_INVALIDARG;
2510
2511     if ( lpici->lpVerb != MAKEINTRESOURCEA(This->iIdOpen) )
2512     {
2513         ERR("Unknown id %d != %d\n", (INT)lpici->lpVerb, This->iIdOpen );
2514         return E_INVALIDARG;
2515     }
2516
2517     r = IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, 0 );
2518     if ( FAILED( r ) )
2519         return r;
2520
2521     if ( This->sComponent )
2522     {
2523         path = shelllink_get_msi_component_path( This->sComponent );
2524         if (!path)
2525             return E_FAIL;
2526     }
2527     else
2528         path = strdupW( This->sPath );
2529
2530     if ( lpici->cbSize == sizeof (CMINVOKECOMMANDINFOEX) &&
2531          ( lpici->fMask & CMIC_MASK_UNICODE ) )
2532     {
2533         LPCMINVOKECOMMANDINFOEX iciex = (LPCMINVOKECOMMANDINFOEX) lpici;
2534         DWORD len = 2;
2535
2536         if ( This->sArgs )
2537             len += lstrlenW( This->sArgs );
2538         if ( iciex->lpParametersW )
2539             len += lstrlenW( iciex->lpParametersW );
2540
2541         args = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
2542         args[0] = 0;
2543         if ( This->sArgs )
2544             lstrcatW( args, This->sArgs );
2545         if ( iciex->lpParametersW )
2546         {
2547             static const WCHAR space[] = { ' ', 0 };
2548             lstrcatW( args, space );
2549             lstrcatW( args, iciex->lpParametersW );
2550         }
2551     }
2552
2553     memset( &sei, 0, sizeof sei );
2554     sei.cbSize = sizeof sei;
2555     sei.fMask = SEE_MASK_UNICODE | SEE_MASK_NOCLOSEPROCESS;
2556     sei.lpFile = path;
2557     sei.nShow = This->iShowCmd;
2558     sei.lpIDList = This->pPidl;
2559     sei.lpDirectory = This->sWorkDir;
2560     sei.lpParameters = args;
2561     sei.lpVerb = szOpen;
2562
2563     if( ShellExecuteExW( &sei ) )
2564     {
2565         if ( sei.hProcess )
2566         {
2567             WaitForSingleObject( sei.hProcess, 10000 );
2568             CloseHandle( sei.hProcess );
2569         }
2570         r = S_OK;
2571     }
2572     else
2573         r = E_FAIL;
2574
2575     HeapFree( GetProcessHeap(), 0, args );
2576     HeapFree( GetProcessHeap(), 0, path );
2577
2578     return r;
2579 }
2580
2581 static HRESULT WINAPI
2582 ShellLink_GetCommandString( IContextMenu* iface, UINT_PTR idCmd, UINT uType,
2583                             UINT* pwReserved, LPSTR pszName, UINT cchMax )
2584 {
2585     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2586
2587     FIXME("%p %lu %u %p %p %u\n", This,
2588           idCmd, uType, pwReserved, pszName, cchMax );
2589
2590     return E_NOTIMPL;
2591 }
2592
2593 static const IContextMenuVtbl cmvt =
2594 {
2595     ShellLink_ContextMenu_QueryInterface,
2596     ShellLink_ContextMenu_AddRef,
2597     ShellLink_ContextMenu_Release,
2598     ShellLink_QueryContextMenu,
2599     ShellLink_InvokeCommand,
2600     ShellLink_GetCommandString
2601 };
2602
2603 static HRESULT WINAPI
2604 ShellLink_ObjectWithSite_QueryInterface( IObjectWithSite* iface, REFIID riid, void** ppvObject )
2605 {
2606     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2607     return ShellLink_QueryInterface( This, riid, ppvObject );
2608 }
2609
2610 static ULONG WINAPI
2611 ShellLink_ObjectWithSite_AddRef( IObjectWithSite* iface )
2612 {
2613     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2614     return ShellLink_AddRef( This );
2615 }
2616
2617 static ULONG WINAPI
2618 ShellLink_ObjectWithSite_Release( IObjectWithSite* iface )
2619 {
2620     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2621     return ShellLink_Release( This );
2622 }
2623
2624 static HRESULT WINAPI
2625 ShellLink_GetSite( IObjectWithSite *iface, REFIID iid, void ** ppvSite )
2626 {
2627     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2628
2629     TRACE("%p %s %p\n", This, debugstr_guid( iid ), ppvSite );
2630
2631     if ( !This->site )
2632         return E_FAIL;
2633     return IUnknown_QueryInterface( This->site, iid, ppvSite );
2634 }
2635
2636 static HRESULT WINAPI
2637 ShellLink_SetSite( IObjectWithSite *iface, IUnknown *punk )
2638 {
2639     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2640
2641     TRACE("%p %p\n", iface, punk);
2642
2643     if ( punk )
2644         IUnknown_AddRef( punk );
2645     This->site = punk;
2646
2647     return S_OK;
2648 }
2649
2650 static const IObjectWithSiteVtbl owsvt =
2651 {
2652     ShellLink_ObjectWithSite_QueryInterface,
2653     ShellLink_ObjectWithSite_AddRef,
2654     ShellLink_ObjectWithSite_Release,
2655     ShellLink_SetSite,
2656     ShellLink_GetSite,
2657 };