wintrust: Use a helper function to get a signer's cert info from a message.
[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(LPWSTR 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( 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 %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(LPWSTR 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, LPITEMIDLIST pidl, LPSTR pszIconPath, int cchIconPath, int* piIcon)
1514 {
1515     LPCITEMIDLIST pidlLast;
1516
1517     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1518
1519     if (SUCCEEDED(hr)) {
1520         IExtractIconA* pei;
1521
1522         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconA, NULL, (LPVOID*)&pei);
1523
1524         if (SUCCEEDED(hr)) {
1525             hr = IExtractIconA_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1526
1527             IExtractIconA_Release(pei);
1528         }
1529
1530         IShellFolder_Release(psf);
1531     }
1532
1533     return hr;
1534 }
1535
1536 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1537 {
1538     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1539
1540     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1541
1542     pszIconPath[0] = 0;
1543     *piIcon = This->iIcoNdx;
1544
1545     if (This->sIcoPath)
1546     {
1547         WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1548         return S_OK;
1549     }
1550
1551     if (This->pPidl || This->sPath)
1552     {
1553         IShellFolder* pdsk;
1554
1555         HRESULT hr = SHGetDesktopFolder(&pdsk);
1556
1557         if (SUCCEEDED(hr))
1558         {
1559             /* first look for an icon using the PIDL (if present) */
1560             if (This->pPidl)
1561                 hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1562             else
1563                 hr = E_FAIL;
1564
1565             /* if we couldn't find an icon yet, look for it using the file system path */
1566             if (FAILED(hr) && This->sPath)
1567             {
1568                 LPITEMIDLIST pidl;
1569
1570                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1571
1572                 if (SUCCEEDED(hr)) {
1573                     hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1574
1575                     SHFree(pidl);
1576                 }
1577             }
1578
1579             IShellFolder_Release(pdsk);
1580         }
1581
1582         return hr;
1583     }
1584     return S_OK;
1585 }
1586
1587 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
1588 {
1589     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1590
1591     TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1592
1593     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1594     This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1595     if ( !This->sIcoPath )
1596         return E_OUTOFMEMORY;
1597
1598     This->iIcoNdx = iIcon;
1599     This->bDirty = TRUE;
1600
1601     return S_OK;
1602 }
1603
1604 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1605 {
1606     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1607
1608     TRACE("(%p)->(path=%s %x)\n",This, pszPathRel, dwReserved);
1609
1610     HeapFree(GetProcessHeap(), 0, This->sPathRel);
1611     This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1612     This->bDirty = TRUE;
1613
1614     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1615 }
1616
1617 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1618 {
1619     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1620
1621     TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
1622
1623     return IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, fFlags );
1624 }
1625
1626 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1627 {
1628     HRESULT r;
1629     LPWSTR str;
1630     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1631
1632     TRACE("(%p)->(path=%s)\n",This, pszFile);
1633
1634     str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
1635     if( !str ) 
1636         return E_OUTOFMEMORY;
1637
1638     r = IShellLinkW_SetPath((IShellLinkW*)&(This->lpvtblw), str);
1639     HeapFree( GetProcessHeap(), 0, str );
1640
1641     return r;
1642 }
1643
1644 /**************************************************************************
1645 * IShellLink Implementation
1646 */
1647
1648 static const IShellLinkAVtbl slvt =
1649 {
1650     IShellLinkA_fnQueryInterface,
1651     IShellLinkA_fnAddRef,
1652     IShellLinkA_fnRelease,
1653     IShellLinkA_fnGetPath,
1654     IShellLinkA_fnGetIDList,
1655     IShellLinkA_fnSetIDList,
1656     IShellLinkA_fnGetDescription,
1657     IShellLinkA_fnSetDescription,
1658     IShellLinkA_fnGetWorkingDirectory,
1659     IShellLinkA_fnSetWorkingDirectory,
1660     IShellLinkA_fnGetArguments,
1661     IShellLinkA_fnSetArguments,
1662     IShellLinkA_fnGetHotkey,
1663     IShellLinkA_fnSetHotkey,
1664     IShellLinkA_fnGetShowCmd,
1665     IShellLinkA_fnSetShowCmd,
1666     IShellLinkA_fnGetIconLocation,
1667     IShellLinkA_fnSetIconLocation,
1668     IShellLinkA_fnSetRelativePath,
1669     IShellLinkA_fnResolve,
1670     IShellLinkA_fnSetPath
1671 };
1672
1673
1674 /**************************************************************************
1675  *  IShellLinkW_fnQueryInterface
1676  */
1677 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1678   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1679 {
1680     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1681     return ShellLink_QueryInterface( This, riid, ppvObj );
1682 }
1683
1684 /******************************************************************************
1685  * IShellLinkW_fnAddRef
1686  */
1687 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1688 {
1689     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1690     return ShellLink_AddRef( This );
1691 }
1692
1693 /******************************************************************************
1694  * IShellLinkW_fnRelease
1695  */
1696 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1697 {
1698     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1699     return ShellLink_Release( This );
1700 }
1701
1702 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1703 {
1704     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1705
1706     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1707           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1708
1709     if (This->sComponent || This->sProduct)
1710         return S_FALSE;
1711
1712     if (cchMaxPath)
1713         pszFile[0] = 0;
1714     if (This->sPath)
1715         lstrcpynW( pszFile, This->sPath, cchMaxPath );
1716
1717     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1718
1719     return S_OK;
1720 }
1721
1722 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1723 {
1724     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1725
1726     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1727
1728     if (!This->pPidl)
1729     {
1730         *ppidl = NULL;
1731         return S_FALSE;
1732     }
1733     *ppidl = ILClone(This->pPidl);
1734     return S_OK;
1735 }
1736
1737 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1738 {
1739     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1740
1741     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1742
1743     if( This->pPidl )
1744         ILFree( This->pPidl );
1745     This->pPidl = ILClone( pidl );
1746     if( !This->pPidl )
1747         return E_FAIL;
1748
1749     This->bDirty = TRUE;
1750
1751     return S_OK;
1752 }
1753
1754 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1755 {
1756     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1757
1758     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1759
1760     pszName[0] = 0;
1761     if( This->sDescription )
1762         lstrcpynW( pszName, This->sDescription, cchMaxName );
1763
1764     return S_OK;
1765 }
1766
1767 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1768 {
1769     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1770
1771     TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1772
1773     HeapFree(GetProcessHeap(), 0, This->sDescription);
1774     This->sDescription = HeapAlloc( GetProcessHeap(), 0,
1775                                     (lstrlenW( pszName )+1)*sizeof(WCHAR) );
1776     if ( !This->sDescription )
1777         return E_OUTOFMEMORY;
1778
1779     lstrcpyW( This->sDescription, pszName );
1780     This->bDirty = TRUE;
1781
1782     return S_OK;
1783 }
1784
1785 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1786 {
1787     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1788
1789     TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1790
1791     if( cchMaxPath )
1792         pszDir[0] = 0;
1793     if( This->sWorkDir )
1794         lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
1795
1796     return S_OK;
1797 }
1798
1799 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1800 {
1801     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1802
1803     TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1804
1805     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1806     This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
1807                                 (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
1808     if ( !This->sWorkDir )
1809         return E_OUTOFMEMORY;
1810     lstrcpyW( This->sWorkDir, pszDir );
1811     This->bDirty = TRUE;
1812
1813     return S_OK;
1814 }
1815
1816 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1817 {
1818     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1819
1820     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1821
1822     if( cchMaxPath )
1823         pszArgs[0] = 0;
1824     if( This->sArgs )
1825         lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
1826
1827     return NOERROR;
1828 }
1829
1830 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1831 {
1832     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1833
1834     TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1835
1836     HeapFree(GetProcessHeap(), 0, This->sArgs);
1837     This->sArgs = HeapAlloc( GetProcessHeap(), 0,
1838                              (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
1839     if ( !This->sArgs )
1840         return E_OUTOFMEMORY;
1841     lstrcpyW( This->sArgs, pszArgs );
1842     This->bDirty = TRUE;
1843
1844     return S_OK;
1845 }
1846
1847 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1848 {
1849     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1850
1851     TRACE("(%p)->(%p)\n",This, pwHotkey);
1852
1853     *pwHotkey=This->wHotKey;
1854
1855     return S_OK;
1856 }
1857
1858 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1859 {
1860     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1861
1862     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1863
1864     This->wHotKey = wHotkey;
1865     This->bDirty = TRUE;
1866
1867     return S_OK;
1868 }
1869
1870 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1871 {
1872     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1873
1874     TRACE("(%p)->(%p)\n",This, piShowCmd);
1875
1876     *piShowCmd = This->iShowCmd;
1877
1878     return S_OK;
1879 }
1880
1881 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1882 {
1883     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1884
1885     This->iShowCmd = iShowCmd;
1886     This->bDirty = TRUE;
1887
1888     return S_OK;
1889 }
1890
1891 static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPITEMIDLIST pidl, LPWSTR pszIconPath, int cchIconPath, int* piIcon)
1892 {
1893     LPCITEMIDLIST pidlLast;
1894
1895     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1896
1897     if (SUCCEEDED(hr)) {
1898         IExtractIconW* pei;
1899
1900         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconW, NULL, (LPVOID*)&pei);
1901
1902         if (SUCCEEDED(hr)) {
1903             hr = IExtractIconW_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1904
1905             IExtractIconW_Release(pei);
1906         }
1907
1908         IShellFolder_Release(psf);
1909     }
1910
1911     return hr;
1912 }
1913
1914 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1915 {
1916     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1917
1918     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1919
1920     pszIconPath[0] = 0;
1921     *piIcon = This->iIcoNdx;
1922
1923     if (This->sIcoPath)
1924     {
1925         lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
1926         return S_OK;
1927     }
1928
1929     if (This->pPidl || This->sPath)
1930     {
1931         IShellFolder* pdsk;
1932
1933         HRESULT hr = SHGetDesktopFolder(&pdsk);
1934
1935         if (SUCCEEDED(hr))
1936         {
1937             /* first look for an icon using the PIDL (if present) */
1938             if (This->pPidl)
1939                 hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1940             else
1941                 hr = E_FAIL;
1942
1943             /* if we couldn't find an icon yet, look for it using the file system path */
1944             if (FAILED(hr) && This->sPath)
1945             {
1946                 LPITEMIDLIST pidl;
1947
1948                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1949
1950                 if (SUCCEEDED(hr))
1951                 {
1952                     hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1953
1954                     SHFree(pidl);
1955                 }
1956             }
1957
1958             IShellFolder_Release(pdsk);
1959         }
1960         return hr;
1961     }
1962     return S_OK;
1963 }
1964
1965 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
1966 {
1967     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1968
1969     TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1970
1971     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1972     This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
1973                                 (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
1974     if ( !This->sIcoPath )
1975         return E_OUTOFMEMORY;
1976     lstrcpyW( This->sIcoPath, pszIconPath );
1977
1978     This->iIcoNdx = iIcon;
1979     This->bDirty = TRUE;
1980
1981     return S_OK;
1982 }
1983
1984 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
1985 {
1986     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1987
1988     TRACE("(%p)->(path=%s %x)\n",This, debugstr_w(pszPathRel), dwReserved);
1989
1990     HeapFree(GetProcessHeap(), 0, This->sPathRel);
1991     This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
1992                                 (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
1993     if ( !This->sPathRel )
1994         return E_OUTOFMEMORY;
1995     lstrcpyW( This->sPathRel, pszPathRel );
1996     This->bDirty = TRUE;
1997
1998     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1999 }
2000
2001 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
2002 {
2003     HRESULT hr = S_OK;
2004     BOOL bSuccess;
2005
2006     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
2007
2008     TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
2009
2010     /*FIXME: use IResolveShellLink interface */
2011
2012     if (!This->sPath && This->pPidl) {
2013         WCHAR buffer[MAX_PATH];
2014
2015         bSuccess = SHGetPathFromIDListW(This->pPidl, buffer);
2016
2017         if (bSuccess && *buffer) {
2018             This->sPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
2019             if (!This->sPath)
2020                 return E_OUTOFMEMORY;
2021
2022             lstrcpyW(This->sPath, buffer);
2023
2024             This->bDirty = TRUE;
2025         } else
2026             hr = S_OK;    /* don't report an error occurred while just caching information */
2027     }
2028
2029     if (!This->sIcoPath && This->sPath) {
2030         This->sIcoPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
2031         if (!This->sIcoPath)
2032             return E_OUTOFMEMORY;
2033
2034         lstrcpyW(This->sIcoPath, This->sPath);
2035         This->iIcoNdx = 0;
2036
2037         This->bDirty = TRUE;
2038     }
2039
2040     return hr;
2041 }
2042
2043 static LPWSTR ShellLink_GetAdvertisedArg(LPCWSTR str)
2044 {
2045     LPWSTR ret;
2046     LPCWSTR p;
2047     DWORD len;
2048
2049     if( !str )
2050         return NULL;
2051
2052     p = strchrW( str, ':' );
2053     if( !p )
2054         return NULL;
2055     len = p - str;
2056     ret = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
2057     if( !ret )
2058         return ret;
2059     memcpy( ret, str, sizeof(WCHAR)*len );
2060     ret[len] = 0;
2061     return ret;
2062 }
2063
2064 static HRESULT ShellLink_SetAdvertiseInfo(IShellLinkImpl *This, LPCWSTR str)
2065 {
2066     LPCWSTR szComponent = NULL, szProduct = NULL, p;
2067     WCHAR szGuid[39];
2068     HRESULT r;
2069     GUID guid;
2070     int len;
2071
2072     while( str[0] )
2073     {
2074         /* each segment must start with two colons */
2075         if( str[0] != ':' || str[1] != ':' )
2076             return E_FAIL;
2077
2078         /* the last segment is just two colons */
2079         if( !str[2] )
2080             break;
2081         str += 2;
2082
2083         /* there must be a colon straight after a guid */
2084         p = strchrW( str, ':' );
2085         if( !p )
2086             return E_FAIL;
2087         len = p - str;
2088         if( len != 38 )
2089             return E_FAIL;
2090
2091         /* get the guid, and check it's validly formatted */
2092         memcpy( szGuid, str, sizeof(WCHAR)*len );
2093         szGuid[len] = 0;
2094         r = CLSIDFromString( szGuid, &guid );
2095         if( r != S_OK )
2096             return r;
2097         str = p + 1;
2098
2099         /* match it up to a guid that we care about */
2100         if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutComponent ) && !szComponent )
2101             szComponent = str;
2102         else if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutProduct ) && !szProduct )
2103             szProduct = str;
2104         else
2105             return E_FAIL;
2106
2107         /* skip to the next field */
2108         str = strchrW( str, ':' );
2109         if( !str )
2110             return E_FAIL;
2111     }
2112
2113     /* we have to have a component for an advertised shortcut */
2114     if( !szComponent )
2115         return E_FAIL;
2116
2117     This->sComponent = ShellLink_GetAdvertisedArg( szComponent );
2118     This->sProduct = ShellLink_GetAdvertisedArg( szProduct );
2119
2120     TRACE("Component = %s\n", debugstr_w(This->sComponent));
2121     TRACE("Product = %s\n", debugstr_w(This->sProduct));
2122
2123     return S_OK;
2124 }
2125
2126 static BOOL ShellLink_GetVolumeInfo(LPWSTR path, volume_info *volume)
2127 {
2128     const int label_sz = sizeof volume->label/sizeof volume->label[0];
2129     WCHAR drive[4] = { path[0], ':', '\\', 0 };
2130     BOOL r;
2131
2132     volume->type = GetDriveTypeW(drive);
2133     r = GetVolumeInformationW(drive, volume->label, label_sz,
2134                               &volume->serial, NULL, NULL, NULL, 0);
2135     TRACE("r = %d type %d serial %08x name %s\n", r,
2136           volume->type, volume->serial, debugstr_w(volume->label));
2137     return r;
2138 }
2139
2140 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
2141 {
2142     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
2143     WCHAR buffer[MAX_PATH];
2144     LPWSTR fname, unquoted = NULL;
2145     HRESULT hr = S_OK;
2146     UINT len;
2147
2148     TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
2149
2150     /* quotes at the ends of the string are stripped */
2151     len = lstrlenW(pszFile);
2152     if (pszFile[0] == '"' && pszFile[len-1] == '"')
2153     {
2154         unquoted = strdupW(pszFile);
2155         PathUnquoteSpacesW(unquoted);
2156         pszFile = unquoted;
2157     }
2158
2159     /* any other quote marks are invalid */
2160     if (strchrW(pszFile, '"'))
2161         return S_FALSE;
2162
2163     HeapFree(GetProcessHeap(), 0, This->sPath);
2164     This->sPath = NULL;
2165
2166     HeapFree(GetProcessHeap(), 0, This->sComponent);
2167     This->sComponent = NULL;
2168
2169     if (This->pPidl)
2170         ILFree(This->pPidl);
2171     This->pPidl = NULL;
2172
2173     if (S_OK != ShellLink_SetAdvertiseInfo( This, pszFile ))
2174     {
2175         if (*pszFile == '\0')
2176             *buffer = '\0';
2177         else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
2178             return E_FAIL;
2179         else if(!PathFileExistsW(buffer) &&
2180                 !SearchPathW(NULL, pszFile, NULL, MAX_PATH, buffer, NULL))
2181           hr = S_FALSE;
2182
2183         This->pPidl = SHSimpleIDListFromPathW(pszFile);
2184         ShellLink_GetVolumeInfo(buffer, &This->volume);
2185
2186         This->sPath = HeapAlloc( GetProcessHeap(), 0,
2187                              (lstrlenW( buffer )+1) * sizeof (WCHAR) );
2188         if (!This->sPath)
2189             return E_OUTOFMEMORY;
2190
2191         lstrcpyW(This->sPath, buffer);
2192     }
2193     This->bDirty = TRUE;
2194     HeapFree(GetProcessHeap(), 0, unquoted);
2195
2196     return hr;
2197 }
2198
2199 /**************************************************************************
2200 * IShellLinkW Implementation
2201 */
2202
2203 static const IShellLinkWVtbl slvtw =
2204 {
2205     IShellLinkW_fnQueryInterface,
2206     IShellLinkW_fnAddRef,
2207     IShellLinkW_fnRelease,
2208     IShellLinkW_fnGetPath,
2209     IShellLinkW_fnGetIDList,
2210     IShellLinkW_fnSetIDList,
2211     IShellLinkW_fnGetDescription,
2212     IShellLinkW_fnSetDescription,
2213     IShellLinkW_fnGetWorkingDirectory,
2214     IShellLinkW_fnSetWorkingDirectory,
2215     IShellLinkW_fnGetArguments,
2216     IShellLinkW_fnSetArguments,
2217     IShellLinkW_fnGetHotkey,
2218     IShellLinkW_fnSetHotkey,
2219     IShellLinkW_fnGetShowCmd,
2220     IShellLinkW_fnSetShowCmd,
2221     IShellLinkW_fnGetIconLocation,
2222     IShellLinkW_fnSetIconLocation,
2223     IShellLinkW_fnSetRelativePath,
2224     IShellLinkW_fnResolve,
2225     IShellLinkW_fnSetPath
2226 };
2227
2228 static HRESULT WINAPI
2229 ShellLink_DataList_QueryInterface( IShellLinkDataList* iface, REFIID riid, void** ppvObject)
2230 {
2231     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2232     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2233 }
2234
2235 static ULONG WINAPI
2236 ShellLink_DataList_AddRef( IShellLinkDataList* iface )
2237 {
2238     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2239     return IShellLinkA_AddRef((IShellLinkA*)This);
2240 }
2241
2242 static ULONG WINAPI
2243 ShellLink_DataList_Release( IShellLinkDataList* iface )
2244 {
2245     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2246     return ShellLink_Release( This );
2247 }
2248
2249 static HRESULT WINAPI
2250 ShellLink_AddDataBlock( IShellLinkDataList* iface, void* pDataBlock )
2251 {
2252     FIXME("\n");
2253     return E_NOTIMPL;
2254 }
2255
2256 static HRESULT WINAPI
2257 ShellLink_CopyDataBlock( IShellLinkDataList* iface, DWORD dwSig, void** ppDataBlock )
2258 {
2259     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2260     LPVOID block = NULL;
2261     HRESULT r = E_FAIL;
2262
2263     TRACE("%p %08x %p\n", iface, dwSig, ppDataBlock );
2264
2265     switch (dwSig)
2266     {
2267     case EXP_DARWIN_ID_SIG:
2268         if (!This->sComponent)
2269             break;
2270         block = shelllink_build_darwinid( This->sComponent, dwSig );
2271         r = S_OK;
2272         break;
2273     case EXP_SZ_LINK_SIG:
2274     case NT_CONSOLE_PROPS_SIG:
2275     case NT_FE_CONSOLE_PROPS_SIG:
2276     case EXP_SPECIAL_FOLDER_SIG:
2277     case EXP_SZ_ICON_SIG:
2278         FIXME("valid but unhandled datablock %08x\n", dwSig);
2279         break;
2280     default:
2281         ERR("unknown datablock %08x\n", dwSig);
2282     }
2283     *ppDataBlock = block;
2284     return r;
2285 }
2286
2287 static HRESULT WINAPI
2288 ShellLink_RemoveDataBlock( IShellLinkDataList* iface, DWORD dwSig )
2289 {
2290     FIXME("\n");
2291     return E_NOTIMPL;
2292 }
2293
2294 static HRESULT WINAPI
2295 ShellLink_GetFlags( IShellLinkDataList* iface, DWORD* pdwFlags )
2296 {
2297     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2298     DWORD flags = 0;
2299
2300     FIXME("%p %p\n", This, pdwFlags );
2301
2302     /* FIXME: add more */
2303     if (This->sArgs)
2304         flags |= SLDF_HAS_ARGS;
2305     if (This->sComponent)
2306         flags |= SLDF_HAS_DARWINID;
2307     if (This->sIcoPath)
2308         flags |= SLDF_HAS_ICONLOCATION;
2309     if (This->sProduct)
2310         flags |= SLDF_HAS_LOGO3ID;
2311     if (This->pPidl)
2312         flags |= SLDF_HAS_ID_LIST;
2313
2314     *pdwFlags = flags;
2315
2316     return S_OK;
2317 }
2318
2319 static HRESULT WINAPI
2320 ShellLink_SetFlags( IShellLinkDataList* iface, DWORD dwFlags )
2321 {
2322     FIXME("\n");
2323     return E_NOTIMPL;
2324 }
2325
2326 static const IShellLinkDataListVtbl dlvt =
2327 {
2328     ShellLink_DataList_QueryInterface,
2329     ShellLink_DataList_AddRef,
2330     ShellLink_DataList_Release,
2331     ShellLink_AddDataBlock,
2332     ShellLink_CopyDataBlock,
2333     ShellLink_RemoveDataBlock,
2334     ShellLink_GetFlags,
2335     ShellLink_SetFlags
2336 };
2337
2338 static HRESULT WINAPI
2339 ShellLink_ExtInit_QueryInterface( IShellExtInit* iface, REFIID riid, void** ppvObject )
2340 {
2341     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2342     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2343 }
2344
2345 static ULONG WINAPI
2346 ShellLink_ExtInit_AddRef( IShellExtInit* iface )
2347 {
2348     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2349     return IShellLinkA_AddRef((IShellLinkA*)This);
2350 }
2351
2352 static ULONG WINAPI
2353 ShellLink_ExtInit_Release( IShellExtInit* iface )
2354 {
2355     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2356     return ShellLink_Release( This );
2357 }
2358
2359 /**************************************************************************
2360  * ShellLink implementation of IShellExtInit::Initialize()
2361  *
2362  * Loads the shelllink from the dataobject the shell is pointing to.
2363  */
2364 static HRESULT WINAPI
2365 ShellLink_ExtInit_Initialize( IShellExtInit* iface, LPCITEMIDLIST pidlFolder,
2366                               IDataObject *pdtobj, HKEY hkeyProgID )
2367 {
2368     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2369     FORMATETC format;
2370     STGMEDIUM stgm;
2371     UINT count;
2372     HRESULT r = E_FAIL;
2373
2374     TRACE("%p %p %p %p\n", This, pidlFolder, pdtobj, hkeyProgID );
2375
2376     if( !pdtobj )
2377         return r;
2378
2379     format.cfFormat = CF_HDROP;
2380     format.ptd = NULL;
2381     format.dwAspect = DVASPECT_CONTENT;
2382     format.lindex = -1;
2383     format.tymed = TYMED_HGLOBAL;
2384
2385     if( FAILED( IDataObject_GetData( pdtobj, &format, &stgm ) ) )
2386         return r;
2387
2388     count = DragQueryFileW( stgm.u.hGlobal, -1, NULL, 0 );
2389     if( count == 1 )
2390     {
2391         LPWSTR path;
2392
2393         count = DragQueryFileW( stgm.u.hGlobal, 0, NULL, 0 );
2394         count++;
2395         path = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
2396         if( path )
2397         {
2398             IPersistFile *pf = (IPersistFile*) &This->lpvtblPersistFile;
2399
2400             count = DragQueryFileW( stgm.u.hGlobal, 0, path, count );
2401             r = IPersistFile_Load( pf, path, 0 );
2402             HeapFree( GetProcessHeap(), 0, path );
2403         }
2404     }
2405     ReleaseStgMedium( &stgm );
2406
2407     return r;
2408 }
2409
2410 static const IShellExtInitVtbl eivt =
2411 {
2412     ShellLink_ExtInit_QueryInterface,
2413     ShellLink_ExtInit_AddRef,
2414     ShellLink_ExtInit_Release,
2415     ShellLink_ExtInit_Initialize
2416 };
2417
2418 static HRESULT WINAPI
2419 ShellLink_ContextMenu_QueryInterface( IContextMenu* iface, REFIID riid, void** ppvObject )
2420 {
2421     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2422     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2423 }
2424
2425 static ULONG WINAPI
2426 ShellLink_ContextMenu_AddRef( IContextMenu* iface )
2427 {
2428     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2429     return IShellLinkA_AddRef((IShellLinkA*)This);
2430 }
2431
2432 static ULONG WINAPI
2433 ShellLink_ContextMenu_Release( IContextMenu* iface )
2434 {
2435     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2436     return ShellLink_Release( This );
2437 }
2438
2439 static HRESULT WINAPI
2440 ShellLink_QueryContextMenu( IContextMenu* iface, HMENU hmenu, UINT indexMenu,
2441                             UINT idCmdFirst, UINT idCmdLast, UINT uFlags )
2442 {
2443     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2444     static WCHAR szOpen[] = { 'O','p','e','n',0 };
2445     MENUITEMINFOW mii;
2446     int id = 1;
2447
2448     TRACE("%p %p %u %u %u %u\n", This,
2449           hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
2450
2451     if ( !hmenu )
2452         return E_INVALIDARG;
2453
2454     memset( &mii, 0, sizeof mii );
2455     mii.cbSize = sizeof mii;
2456     mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
2457     mii.dwTypeData = szOpen;
2458     mii.cch = strlenW( mii.dwTypeData );
2459     mii.wID = idCmdFirst + id++;
2460     mii.fState = MFS_DEFAULT | MFS_ENABLED;
2461     mii.fType = MFT_STRING;
2462     if (!InsertMenuItemW( hmenu, indexMenu, TRUE, &mii ))
2463         return E_FAIL;
2464     This->iIdOpen = 0;
2465
2466     return MAKE_HRESULT( SEVERITY_SUCCESS, 0, id );
2467 }
2468
2469 static LPWSTR
2470 shelllink_get_msi_component_path( LPWSTR component )
2471 {
2472     LPWSTR path;
2473     DWORD r, sz = 0;
2474
2475     r = CommandLineFromMsiDescriptor( component, NULL, &sz );
2476     if (r != ERROR_SUCCESS)
2477          return NULL;
2478
2479     sz++;
2480     path = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
2481     r = CommandLineFromMsiDescriptor( component, path, &sz );
2482     if (r != ERROR_SUCCESS)
2483     {
2484         HeapFree( GetProcessHeap(), 0, path );
2485         path = NULL;
2486     }
2487
2488     TRACE("returning %s\n", debugstr_w( path ) );
2489
2490     return path;
2491 }
2492
2493 static HRESULT WINAPI
2494 ShellLink_InvokeCommand( IContextMenu* iface, LPCMINVOKECOMMANDINFO lpici )
2495 {
2496     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2497     static const WCHAR szOpen[] = { 'O','p','e','n',0 };
2498     SHELLEXECUTEINFOW sei;
2499     HWND hwnd = NULL; /* FIXME: get using interface set from IObjectWithSite */
2500     LPWSTR args = NULL;
2501     LPWSTR path = NULL;
2502     HRESULT r;
2503
2504     TRACE("%p %p\n", This, lpici );
2505
2506     if ( lpici->cbSize < sizeof (CMINVOKECOMMANDINFO) )
2507         return E_INVALIDARG;
2508
2509     if ( lpici->lpVerb != MAKEINTRESOURCEA(This->iIdOpen) )
2510     {
2511         ERR("Unknown id %d != %d\n", (INT)lpici->lpVerb, This->iIdOpen );
2512         return E_INVALIDARG;
2513     }
2514
2515     r = IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, 0 );
2516     if ( FAILED( r ) )
2517         return r;
2518
2519     if ( This->sComponent )
2520     {
2521         path = shelllink_get_msi_component_path( This->sComponent );
2522         if (!path)
2523             return E_FAIL;
2524     }
2525     else
2526         path = strdupW( This->sPath );
2527
2528     if ( lpici->cbSize == sizeof (CMINVOKECOMMANDINFOEX) &&
2529          ( lpici->fMask & CMIC_MASK_UNICODE ) )
2530     {
2531         LPCMINVOKECOMMANDINFOEX iciex = (LPCMINVOKECOMMANDINFOEX) lpici;
2532         DWORD len = 2;
2533
2534         if ( This->sArgs )
2535             len += lstrlenW( This->sArgs );
2536         if ( iciex->lpParametersW )
2537             len += lstrlenW( iciex->lpParametersW );
2538
2539         args = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
2540         args[0] = 0;
2541         if ( This->sArgs )
2542             lstrcatW( args, This->sArgs );
2543         if ( iciex->lpParametersW )
2544         {
2545             static const WCHAR space[] = { ' ', 0 };
2546             lstrcatW( args, space );
2547             lstrcatW( args, iciex->lpParametersW );
2548         }
2549     }
2550
2551     memset( &sei, 0, sizeof sei );
2552     sei.cbSize = sizeof sei;
2553     sei.fMask = SEE_MASK_UNICODE | SEE_MASK_NOCLOSEPROCESS;
2554     sei.lpFile = path;
2555     sei.nShow = This->iShowCmd;
2556     sei.lpIDList = This->pPidl;
2557     sei.lpDirectory = This->sWorkDir;
2558     sei.lpParameters = args;
2559     sei.lpVerb = szOpen;
2560
2561     if( ShellExecuteExW( &sei ) )
2562     {
2563         if ( sei.hProcess )
2564         {
2565             WaitForSingleObject( sei.hProcess, 10000 );
2566             CloseHandle( sei.hProcess );
2567         }
2568         r = S_OK;
2569     }
2570     else
2571         r = E_FAIL;
2572
2573     HeapFree( GetProcessHeap(), 0, args );
2574     HeapFree( GetProcessHeap(), 0, path );
2575
2576     return r;
2577 }
2578
2579 static HRESULT WINAPI
2580 ShellLink_GetCommandString( IContextMenu* iface, UINT_PTR idCmd, UINT uType,
2581                             UINT* pwReserved, LPSTR pszName, UINT cchMax )
2582 {
2583     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2584
2585     FIXME("%p %lu %u %p %p %u\n", This,
2586           idCmd, uType, pwReserved, pszName, cchMax );
2587
2588     return E_NOTIMPL;
2589 }
2590
2591 static const IContextMenuVtbl cmvt =
2592 {
2593     ShellLink_ContextMenu_QueryInterface,
2594     ShellLink_ContextMenu_AddRef,
2595     ShellLink_ContextMenu_Release,
2596     ShellLink_QueryContextMenu,
2597     ShellLink_InvokeCommand,
2598     ShellLink_GetCommandString
2599 };
2600
2601 static HRESULT WINAPI
2602 ShellLink_ObjectWithSite_QueryInterface( IObjectWithSite* iface, REFIID riid, void** ppvObject )
2603 {
2604     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2605     return ShellLink_QueryInterface( This, riid, ppvObject );
2606 }
2607
2608 static ULONG WINAPI
2609 ShellLink_ObjectWithSite_AddRef( IObjectWithSite* iface )
2610 {
2611     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2612     return ShellLink_AddRef( This );
2613 }
2614
2615 static ULONG WINAPI
2616 ShellLink_ObjectWithSite_Release( IObjectWithSite* iface )
2617 {
2618     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2619     return ShellLink_Release( This );
2620 }
2621
2622 static HRESULT WINAPI
2623 ShellLink_GetSite( IObjectWithSite *iface, REFIID iid, void ** ppvSite )
2624 {
2625     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2626
2627     TRACE("%p %s %p\n", This, debugstr_guid( iid ), ppvSite );
2628
2629     if ( !This->site )
2630         return E_FAIL;
2631     return IUnknown_QueryInterface( This->site, iid, ppvSite );
2632 }
2633
2634 static HRESULT WINAPI
2635 ShellLink_SetSite( IObjectWithSite *iface, IUnknown *punk )
2636 {
2637     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2638
2639     TRACE("%p %p\n", iface, punk);
2640
2641     if ( punk )
2642         IUnknown_AddRef( punk );
2643     This->site = punk;
2644
2645     return S_OK;
2646 }
2647
2648 static const IObjectWithSiteVtbl owsvt =
2649 {
2650     ShellLink_ObjectWithSite_QueryInterface,
2651     ShellLink_ObjectWithSite_AddRef,
2652     ShellLink_ObjectWithSite_Release,
2653     ShellLink_SetSite,
2654     ShellLink_GetSite,
2655 };