shell32: Fix a memory leak.
[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(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     *pclsid = CLSID_ShellLink;
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 BOOL run_winemenubuilder( const WCHAR *args )
399 {
400     static const WCHAR menubuilder[] = {'\\','w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',0};
401     LONG len;
402     LPWSTR buffer;
403     STARTUPINFOW si;
404     PROCESS_INFORMATION pi;
405     BOOL ret;
406     WCHAR app[MAX_PATH];
407
408     GetSystemDirectoryW( app, MAX_PATH - sizeof(menubuilder)/sizeof(WCHAR) );
409     strcatW( app, menubuilder );
410
411     len = (strlenW( app ) + strlenW( args ) + 1) * sizeof(WCHAR);
412     buffer = HeapAlloc( GetProcessHeap(), 0, len );
413     if( !buffer )
414         return FALSE;
415
416     strcpyW( buffer, app );
417     strcatW( buffer, args );
418
419     TRACE("starting %s\n",debugstr_w(buffer));
420
421     memset(&si, 0, sizeof(si));
422     si.cb = sizeof(si);
423
424     ret = CreateProcessW( app, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
425
426     HeapFree( GetProcessHeap(), 0, buffer );
427
428     if (ret)
429     {
430         CloseHandle( pi.hProcess );
431         CloseHandle( pi.hThread );
432     }
433
434     return ret;
435 }
436
437 static BOOL StartLinkProcessor( LPCOLESTR szLink )
438 {
439     static const WCHAR szFormat[] = {' ','-','w',' ','"','%','s','"',0 };
440     LONG len;
441     LPWSTR buffer;
442     BOOL ret;
443
444     len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
445     buffer = HeapAlloc( GetProcessHeap(), 0, len );
446     if( !buffer )
447         return FALSE;
448
449     wsprintfW( buffer, szFormat, szLink );
450     ret = run_winemenubuilder( buffer );
451     HeapFree( GetProcessHeap(), 0, buffer );
452     return ret;
453 }
454
455 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
456 {
457     IShellLinkImpl *This = impl_from_IPersistFile(iface);
458     IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
459     HRESULT r;
460     IStream *stm;
461
462     TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
463
464     if (!pszFileName)
465         return E_FAIL;
466
467     r = SHCreateStreamOnFileW( pszFileName, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &stm );
468     if( SUCCEEDED( r ) )
469     {
470         r = IPersistStream_Save(StreamThis, stm, FALSE);
471         IStream_Release( stm );
472
473         if( SUCCEEDED( r ) )
474         {
475             StartLinkProcessor( pszFileName );
476
477             This->bDirty = FALSE;
478         }
479         else
480         {
481             DeleteFileW( pszFileName );
482             WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
483         }
484     }
485
486     return r;
487 }
488
489 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
490 {
491         IShellLinkImpl *This = impl_from_IPersistFile(iface);
492         FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
493         return NOERROR;
494 }
495
496 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *ppszFileName)
497 {
498         IShellLinkImpl *This = impl_from_IPersistFile(iface);
499         FIXME("(%p)\n",This);
500         return NOERROR;
501 }
502
503 static const IPersistFileVtbl pfvt =
504 {
505         IPersistFile_fnQueryInterface,
506         IPersistFile_fnAddRef,
507         IPersistFile_fnRelease,
508         IPersistFile_fnGetClassID,
509         IPersistFile_fnIsDirty,
510         IPersistFile_fnLoad,
511         IPersistFile_fnSave,
512         IPersistFile_fnSaveCompleted,
513         IPersistFile_fnGetCurFile
514 };
515
516 /************************************************************************
517  * IPersistStream_QueryInterface
518  */
519 static HRESULT WINAPI IPersistStream_fnQueryInterface(
520         IPersistStream* iface,
521         REFIID     riid,
522         VOID**     ppvObj)
523 {
524     IShellLinkImpl *This = impl_from_IPersistStream(iface);
525     return ShellLink_QueryInterface( This, riid, ppvObj );
526 }
527
528 /************************************************************************
529  * IPersistStream_Release
530  */
531 static ULONG WINAPI IPersistStream_fnRelease(
532         IPersistStream* iface)
533 {
534     IShellLinkImpl *This = impl_from_IPersistStream(iface);
535     return IShellLinkA_Release((IShellLinkA*)This);
536 }
537
538 /************************************************************************
539  * IPersistStream_AddRef
540  */
541 static ULONG WINAPI IPersistStream_fnAddRef(
542         IPersistStream* iface)
543 {
544     IShellLinkImpl *This = impl_from_IPersistStream(iface);
545     return ShellLink_AddRef( This );
546 }
547
548 /************************************************************************
549  * IPersistStream_GetClassID
550  *
551  */
552 static HRESULT WINAPI IPersistStream_fnGetClassID(
553         IPersistStream* iface,
554         CLSID* pClassID)
555 {
556     IShellLinkImpl *This = impl_from_IPersistStream(iface);
557     return ShellLink_GetClassID( This, pClassID );
558 }
559
560 /************************************************************************
561  * IPersistStream_IsDirty (IPersistStream)
562  */
563 static HRESULT WINAPI IPersistStream_fnIsDirty(
564         IPersistStream*  iface)
565 {
566         IShellLinkImpl *This = impl_from_IPersistStream(iface);
567
568         TRACE("(%p)\n", This);
569
570         return S_OK;
571 }
572
573
574 static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
575 {
576     DWORD count;
577     USHORT len;
578     LPVOID temp;
579     LPWSTR str;
580     HRESULT r;
581
582     TRACE("%p\n", stm);
583
584     count = 0;
585     r = IStream_Read(stm, &len, sizeof(len), &count);
586     if ( FAILED (r) || ( count != sizeof(len) ) )
587         return E_FAIL;
588
589     if( unicode )
590         len *= sizeof (WCHAR);
591
592     TRACE("reading %d\n", len);
593     temp = HeapAlloc(GetProcessHeap(), 0, len+sizeof(WCHAR));
594     if( !temp )
595         return E_OUTOFMEMORY;
596     count = 0;
597     r = IStream_Read(stm, temp, len, &count);
598     if( FAILED (r) || ( count != len ) )
599     {
600         HeapFree( GetProcessHeap(), 0, temp );
601         return E_FAIL;
602     }
603
604     TRACE("read %s\n", debugstr_an(temp,len));
605
606     /* convert to unicode if necessary */
607     if( !unicode )
608     {
609         count = MultiByteToWideChar( CP_ACP, 0, temp, len, NULL, 0 );
610         str = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof (WCHAR) );
611         if( !str )
612         {
613             HeapFree( GetProcessHeap(), 0, temp );
614             return E_OUTOFMEMORY;
615         }
616         MultiByteToWideChar( CP_ACP, 0, temp, len, str, count );
617         HeapFree( GetProcessHeap(), 0, temp );
618     }
619     else
620     {
621         count /= 2;
622         str = temp;
623     }
624     str[count] = 0;
625
626     *pstr = str;
627
628     return S_OK;
629 }
630
631 static HRESULT Stream_ReadChunk( IStream* stm, LPVOID *data )
632 {
633     DWORD size;
634     ULONG count;
635     HRESULT r;
636     struct sized_chunk {
637         DWORD size;
638         unsigned char data[1];
639     } *chunk;
640
641     TRACE("%p\n",stm);
642
643     r = IStream_Read( stm, &size, sizeof(size), &count );
644     if( FAILED( r )  || count != sizeof(size) )
645         return E_FAIL;
646
647     chunk = HeapAlloc( GetProcessHeap(), 0, size );
648     if( !chunk )
649         return E_OUTOFMEMORY;
650
651     chunk->size = size;
652     r = IStream_Read( stm, chunk->data, size - sizeof(size), &count );
653     if( FAILED( r ) || count != (size - sizeof(size)) )
654     {
655         HeapFree( GetProcessHeap(), 0, chunk );
656         return E_FAIL;
657     }
658
659     TRACE("Read %d bytes\n",chunk->size);
660
661     *data = chunk;
662
663     return S_OK;
664 }
665
666 static BOOL Stream_LoadVolume( LOCAL_VOLUME_INFO *vol, volume_info *volume )
667 {
668     const int label_sz = sizeof volume->label/sizeof volume->label[0];
669     LPSTR label;
670     int len;
671
672     volume->serial = vol->dwVolSerial;
673     volume->type = vol->dwType;
674
675     if( !vol->dwVolLabelOfs )
676         return FALSE;
677     if( vol->dwSize <= vol->dwVolLabelOfs )
678         return FALSE;
679     len = vol->dwSize - vol->dwVolLabelOfs;
680
681     label = (LPSTR) vol;
682     label += vol->dwVolLabelOfs;
683     MultiByteToWideChar( CP_ACP, 0, label, len, volume->label, label_sz-1);
684
685     return TRUE;
686 }
687
688 static LPWSTR Stream_LoadPath( LPCSTR p, DWORD maxlen )
689 {
690     int len = 0, wlen;
691     LPWSTR path;
692
693     while( p[len] && (len < maxlen) )
694         len++;
695
696     wlen = MultiByteToWideChar(CP_ACP, 0, p, len, NULL, 0);
697     path = HeapAlloc(GetProcessHeap(), 0, (wlen+1)*sizeof(WCHAR));
698     MultiByteToWideChar(CP_ACP, 0, p, len, path, wlen);
699     path[wlen] = 0;
700
701     return path;
702 }
703
704 static HRESULT Stream_LoadLocation( IStream *stm,
705                 volume_info *volume, LPWSTR *path )
706 {
707     char *p = NULL;
708     LOCATION_INFO *loc;
709     HRESULT r;
710     DWORD n;
711
712     r = Stream_ReadChunk( stm, (LPVOID*) &p );
713     if( FAILED(r) )
714         return r;
715
716     loc = (LOCATION_INFO*) p;
717     if (loc->dwTotalSize < sizeof(LOCATION_INFO))
718     {
719         HeapFree( GetProcessHeap(), 0, p );
720         return E_FAIL;
721     }
722
723     /* if there's valid local volume information, load it */
724     if( loc->dwVolTableOfs && 
725        ((loc->dwVolTableOfs + sizeof(LOCAL_VOLUME_INFO)) <= loc->dwTotalSize) )
726     {
727         LOCAL_VOLUME_INFO *volume_info;
728
729         volume_info = (LOCAL_VOLUME_INFO*) &p[loc->dwVolTableOfs];
730         Stream_LoadVolume( volume_info, volume );
731     }
732
733     /* if there's a local path, load it */
734     n = loc->dwLocalPathOfs;
735     if( n && (n < loc->dwTotalSize) )
736         *path = Stream_LoadPath( &p[n], loc->dwTotalSize - n );
737
738     TRACE("type %d serial %08x name %s path %s\n", volume->type,
739           volume->serial, debugstr_w(volume->label), debugstr_w(*path));
740
741     HeapFree( GetProcessHeap(), 0, p );
742     return S_OK;
743 }
744
745 /*
746  *  The format of the advertised shortcut info seems to be:
747  *
748  *  Offset     Description
749  *  ------     -----------
750  *
751  *    0          Length of the block (4 bytes, usually 0x314)
752  *    4          tag (dword)
753  *    8          string data in ASCII
754  *    8+0x104    string data in UNICODE
755  *
756  * In the original Win32 implementation the buffers are not initialized
757  *  to zero, so data trailing the string is random garbage.
758  */
759 static HRESULT Stream_LoadAdvertiseInfo( IStream* stm, LPWSTR *str )
760 {
761     DWORD size;
762     ULONG count;
763     HRESULT r;
764     EXP_DARWIN_LINK buffer;
765     
766     TRACE("%p\n",stm);
767
768     r = IStream_Read( stm, &buffer.dbh.cbSize, sizeof (DWORD), &count );
769     if( FAILED( r ) )
770         return r;
771
772     /* make sure that we read the size of the structure even on error */
773     size = sizeof buffer - sizeof (DWORD);
774     if( buffer.dbh.cbSize != sizeof buffer )
775     {
776         ERR("Ooops.  This structure is not as expected...\n");
777         return E_FAIL;
778     }
779
780     r = IStream_Read( stm, &buffer.dbh.dwSignature, size, &count );
781     if( FAILED( r ) )
782         return r;
783
784     if( count != size )
785         return E_FAIL;
786
787     TRACE("magic %08x  string = %s\n", buffer.dbh.dwSignature, debugstr_w(buffer.szwDarwinID));
788
789     if( (buffer.dbh.dwSignature&0xffff0000) != 0xa0000000 )
790     {
791         ERR("Unknown magic number %08x in advertised shortcut\n", buffer.dbh.dwSignature);
792         return E_FAIL;
793     }
794
795     *str = HeapAlloc( GetProcessHeap(), 0, 
796                      (lstrlenW(buffer.szwDarwinID)+1) * sizeof(WCHAR) );
797     lstrcpyW( *str, buffer.szwDarwinID );
798
799     return S_OK;
800 }
801
802 /************************************************************************
803  * IPersistStream_Load (IPersistStream)
804  */
805 static HRESULT WINAPI IPersistStream_fnLoad(
806     IPersistStream*  iface,
807     IStream*         stm)
808 {
809     LINK_HEADER hdr;
810     ULONG    dwBytesRead;
811     BOOL     unicode;
812     HRESULT  r;
813     DWORD    zero;
814
815     IShellLinkImpl *This = impl_from_IPersistStream(iface);
816
817     TRACE("%p %p\n", This, stm);
818
819     if( !stm )
820         return STG_E_INVALIDPOINTER;
821
822     dwBytesRead = 0;
823     r = IStream_Read(stm, &hdr, sizeof(hdr), &dwBytesRead);
824     if( FAILED( r ) )
825         return r;
826
827     if( dwBytesRead != sizeof(hdr))
828         return E_FAIL;
829     if( hdr.dwSize != sizeof(hdr))
830         return E_FAIL;
831     if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
832         return E_FAIL;
833
834     /* free all the old stuff */
835     ILFree(This->pPidl);
836     This->pPidl = NULL;
837     memset( &This->volume, 0, sizeof This->volume );
838     HeapFree(GetProcessHeap(), 0, This->sPath);
839     This->sPath = NULL;
840     HeapFree(GetProcessHeap(), 0, This->sDescription);
841     This->sDescription = NULL;
842     HeapFree(GetProcessHeap(), 0, This->sPathRel);
843     This->sPathRel = NULL;
844     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
845     This->sWorkDir = NULL;
846     HeapFree(GetProcessHeap(), 0, This->sArgs);
847     This->sArgs = NULL;
848     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
849     This->sIcoPath = NULL;
850     HeapFree(GetProcessHeap(), 0, This->sProduct);
851     This->sProduct = NULL;
852     HeapFree(GetProcessHeap(), 0, This->sComponent);
853     This->sComponent = NULL;
854         
855     This->wHotKey = (WORD)hdr.wHotKey;
856     This->iIcoNdx = hdr.nIcon;
857     FileTimeToSystemTime (&hdr.Time1, &This->time1);
858     FileTimeToSystemTime (&hdr.Time2, &This->time2);
859     FileTimeToSystemTime (&hdr.Time3, &This->time3);
860     if (TRACE_ON(shell))
861     {
862         WCHAR sTemp[MAX_PATH];
863         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time1,
864                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
865         TRACE("-- time1: %s\n", debugstr_w(sTemp) );
866         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time2,
867                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
868         TRACE("-- time2: %s\n", debugstr_w(sTemp) );
869         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time3,
870                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
871         TRACE("-- time3: %s\n", debugstr_w(sTemp) );
872     }
873
874     /* load all the new stuff */
875     if( hdr.dwFlags & SLDF_HAS_ID_LIST )
876     {
877         r = ILLoadFromStream( stm, &This->pPidl );
878         if( FAILED( r ) )
879             return r;
880     }
881     pdump(This->pPidl);
882
883     /* load the location information */
884     if( hdr.dwFlags & SLDF_HAS_LINK_INFO )
885         r = Stream_LoadLocation( stm, &This->volume, &This->sPath );
886     if( FAILED( r ) )
887         goto end;
888
889     unicode = hdr.dwFlags & SLDF_UNICODE;
890     if( hdr.dwFlags & SLDF_HAS_NAME )
891     {
892         r = Stream_LoadString( stm, unicode, &This->sDescription );
893         TRACE("Description  -> %s\n",debugstr_w(This->sDescription));
894     }
895     if( FAILED( r ) )
896         goto end;
897
898     if( hdr.dwFlags & SLDF_HAS_RELPATH )
899     {
900         r = Stream_LoadString( stm, unicode, &This->sPathRel );
901         TRACE("Relative Path-> %s\n",debugstr_w(This->sPathRel));
902     }
903     if( FAILED( r ) )
904         goto end;
905
906     if( hdr.dwFlags & SLDF_HAS_WORKINGDIR )
907     {
908         r = Stream_LoadString( stm, unicode, &This->sWorkDir );
909         TRACE("Working Dir  -> %s\n",debugstr_w(This->sWorkDir));
910     }
911     if( FAILED( r ) )
912         goto end;
913
914     if( hdr.dwFlags & SLDF_HAS_ARGS )
915     {
916         r = Stream_LoadString( stm, unicode, &This->sArgs );
917         TRACE("Working Dir  -> %s\n",debugstr_w(This->sArgs));
918     }
919     if( FAILED( r ) )
920         goto end;
921
922     if( hdr.dwFlags & SLDF_HAS_ICONLOCATION )
923     {
924         r = Stream_LoadString( stm, unicode, &This->sIcoPath );
925         TRACE("Icon file    -> %s\n",debugstr_w(This->sIcoPath));
926     }
927     if( FAILED( r ) )
928         goto end;
929
930     if( hdr.dwFlags & SLDF_HAS_LOGO3ID )
931     {
932         r = Stream_LoadAdvertiseInfo( stm, &This->sProduct );
933         TRACE("Product      -> %s\n",debugstr_w(This->sProduct));
934     }
935     if( FAILED( r ) )
936         goto end;
937
938     if( hdr.dwFlags & SLDF_HAS_DARWINID )
939     {
940         r = Stream_LoadAdvertiseInfo( stm, &This->sComponent );
941         TRACE("Component    -> %s\n",debugstr_w(This->sComponent));
942     }
943     if( FAILED( r ) )
944         goto end;
945
946     r = IStream_Read(stm, &zero, sizeof zero, &dwBytesRead);
947     if( FAILED( r ) || zero || dwBytesRead != sizeof zero )
948     {
949         /* Some lnk files have extra data blocks starting with a
950          * DATABLOCK_HEADER. For instance EXP_SPECIAL_FOLDER and an unknown
951          * one with a 0xa0000003 signature. However these don't seem to matter
952          * too much.
953          */
954         WARN("Last word was not zero\n");
955     }
956
957     TRACE("OK\n");
958
959     pdump (This->pPidl);
960
961     return S_OK;
962 end:
963     return r;
964 }
965
966 /************************************************************************
967  * Stream_WriteString
968  *
969  * Helper function for IPersistStream_Save. Writes a unicode string 
970  *  with terminating nul byte to a stream, preceded by the its length.
971  */
972 static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
973 {
974     USHORT len = lstrlenW( str ) + 1;
975     DWORD count;
976     HRESULT r;
977
978     r = IStream_Write( stm, &len, sizeof(len), &count );
979     if( FAILED( r ) )
980         return r;
981
982     len *= sizeof(WCHAR);
983
984     r = IStream_Write( stm, str, len, &count );
985     if( FAILED( r ) )
986         return r;
987
988     return S_OK;
989 }
990
991 /************************************************************************
992  * Stream_WriteLocationInfo
993  *
994  * Writes the location info to a stream
995  *
996  * FIXME: One day we might want to write the network volume information
997  *        and the final path.
998  *        Figure out how Windows deals with unicode paths here.
999  */
1000 static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR path,
1001                                          volume_info *volume )
1002 {
1003     DWORD total_size, path_size, volume_info_size, label_size, final_path_size;
1004     LOCAL_VOLUME_INFO *vol;
1005     LOCATION_INFO *loc;
1006     LPSTR szLabel, szPath, szFinalPath;
1007     ULONG count = 0;
1008     HRESULT hr;
1009
1010     TRACE("%p %s %p\n", stm, debugstr_w(path), volume);
1011
1012     /* figure out the size of everything */
1013     label_size = WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
1014                                       NULL, 0, NULL, NULL );
1015     path_size = WideCharToMultiByte( CP_ACP, 0, path, -1,
1016                                      NULL, 0, NULL, NULL );
1017     volume_info_size = sizeof *vol + label_size;
1018     final_path_size = 1;
1019     total_size = sizeof *loc + volume_info_size + path_size + final_path_size;
1020
1021     /* create pointers to everything */
1022     loc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total_size);
1023     vol = (LOCAL_VOLUME_INFO*) &loc[1];
1024     szLabel = (LPSTR) &vol[1];
1025     szPath = &szLabel[label_size];
1026     szFinalPath = &szPath[path_size];
1027
1028     /* fill in the location information header */
1029     loc->dwTotalSize = total_size;
1030     loc->dwHeaderSize = sizeof (*loc);
1031     loc->dwFlags = 1;
1032     loc->dwVolTableOfs = sizeof (*loc);
1033     loc->dwLocalPathOfs = sizeof (*loc) + volume_info_size;
1034     loc->dwNetworkVolTableOfs = 0;
1035     loc->dwFinalPathOfs = sizeof (*loc) + volume_info_size + path_size;
1036
1037     /* fill in the volume information */
1038     vol->dwSize = volume_info_size;
1039     vol->dwType = volume->type;
1040     vol->dwVolSerial = volume->serial;
1041     vol->dwVolLabelOfs = sizeof (*vol);
1042
1043     /* copy in the strings */
1044     WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
1045                          szLabel, label_size, NULL, NULL );
1046     WideCharToMultiByte( CP_ACP, 0, path, -1,
1047                          szPath, path_size, NULL, NULL );
1048     szFinalPath[0] = 0;
1049
1050     hr = IStream_Write( stm, loc, total_size, &count );
1051     HeapFree(GetProcessHeap(), 0, loc);
1052
1053     return hr;
1054 }
1055
1056 static EXP_DARWIN_LINK* shelllink_build_darwinid( LPCWSTR string, DWORD magic )
1057 {
1058     EXP_DARWIN_LINK *buffer;
1059     
1060     buffer = LocalAlloc( LMEM_ZEROINIT, sizeof *buffer );
1061     buffer->dbh.cbSize = sizeof *buffer;
1062     buffer->dbh.dwSignature = magic;
1063     lstrcpynW( buffer->szwDarwinID, string, MAX_PATH );
1064     WideCharToMultiByte(CP_ACP, 0, string, -1, buffer->szDarwinID, MAX_PATH, NULL, NULL );
1065
1066     return buffer;
1067 }
1068
1069 static HRESULT Stream_WriteAdvertiseInfo( IStream* stm, LPCWSTR string, DWORD magic )
1070 {
1071     EXP_DARWIN_LINK *buffer;
1072     ULONG count;
1073     
1074     TRACE("%p\n",stm);
1075
1076     buffer = shelllink_build_darwinid( string, magic );
1077
1078     return IStream_Write( stm, buffer, buffer->dbh.cbSize, &count );
1079 }
1080
1081 /************************************************************************
1082  * IPersistStream_Save (IPersistStream)
1083  *
1084  * FIXME: makes assumptions about byte order
1085  */
1086 static HRESULT WINAPI IPersistStream_fnSave(
1087         IPersistStream*  iface,
1088         IStream*         stm,
1089         BOOL             fClearDirty)
1090 {
1091     LINK_HEADER header;
1092     ULONG   count;
1093     DWORD   zero;
1094     HRESULT r;
1095
1096     IShellLinkImpl *This = impl_from_IPersistStream(iface);
1097
1098     TRACE("%p %p %x\n", This, stm, fClearDirty);
1099
1100     memset(&header, 0, sizeof(header));
1101     header.dwSize = sizeof(header);
1102     header.fStartup = This->iShowCmd;
1103     header.MagicGuid = CLSID_ShellLink;
1104
1105     header.wHotKey = This->wHotKey;
1106     header.nIcon = This->iIcoNdx;
1107     header.dwFlags = SLDF_UNICODE;   /* strings are in unicode */
1108     if( This->pPidl )
1109         header.dwFlags |= SLDF_HAS_ID_LIST;
1110     if( This->sPath )
1111         header.dwFlags |= SLDF_HAS_LINK_INFO;
1112     if( This->sDescription )
1113         header.dwFlags |= SLDF_HAS_NAME;
1114     if( This->sWorkDir )
1115         header.dwFlags |= SLDF_HAS_WORKINGDIR;
1116     if( This->sArgs )
1117         header.dwFlags |= SLDF_HAS_ARGS;
1118     if( This->sIcoPath )
1119         header.dwFlags |= SLDF_HAS_ICONLOCATION;
1120     if( This->sProduct )
1121         header.dwFlags |= SLDF_HAS_LOGO3ID;
1122     if( This->sComponent )
1123         header.dwFlags |= SLDF_HAS_DARWINID;
1124
1125     SystemTimeToFileTime ( &This->time1, &header.Time1 );
1126     SystemTimeToFileTime ( &This->time2, &header.Time2 );
1127     SystemTimeToFileTime ( &This->time3, &header.Time3 );
1128
1129     /* write the Shortcut header */
1130     r = IStream_Write( stm, &header, sizeof(header), &count );
1131     if( FAILED( r ) )
1132     {
1133         ERR("Write failed at %d\n",__LINE__);
1134         return r;
1135     }
1136
1137     TRACE("Writing pidl\n");
1138
1139     /* write the PIDL to the shortcut */
1140     if( This->pPidl )
1141     {
1142         r = ILSaveToStream( stm, This->pPidl );
1143         if( FAILED( r ) )
1144         {
1145             ERR("Failed to write PIDL at %d\n",__LINE__);
1146             return r;
1147         }
1148     }
1149
1150     if( This->sPath )
1151         Stream_WriteLocationInfo( stm, This->sPath, &This->volume );
1152
1153     if( This->sDescription )
1154         r = Stream_WriteString( stm, This->sDescription );
1155
1156     if( This->sPathRel )
1157         r = Stream_WriteString( stm, This->sPathRel );
1158
1159     if( This->sWorkDir )
1160         r = Stream_WriteString( stm, This->sWorkDir );
1161
1162     if( This->sArgs )
1163         r = Stream_WriteString( stm, This->sArgs );
1164
1165     if( This->sIcoPath )
1166         r = Stream_WriteString( stm, This->sIcoPath );
1167
1168     if( This->sProduct )
1169         r = Stream_WriteAdvertiseInfo( stm, This->sProduct, EXP_SZ_ICON_SIG );
1170
1171     if( This->sComponent )
1172         r = Stream_WriteAdvertiseInfo( stm, This->sComponent, EXP_DARWIN_ID_SIG );
1173
1174     /* the last field is a single zero dword */
1175     zero = 0;
1176     r = IStream_Write( stm, &zero, sizeof zero, &count );
1177
1178     return S_OK;
1179 }
1180
1181 /************************************************************************
1182  * IPersistStream_GetSizeMax (IPersistStream)
1183  */
1184 static HRESULT WINAPI IPersistStream_fnGetSizeMax(
1185         IPersistStream*  iface,
1186         ULARGE_INTEGER*  pcbSize)
1187 {
1188         IShellLinkImpl *This = impl_from_IPersistStream(iface);
1189
1190         TRACE("(%p)\n", This);
1191
1192         return E_NOTIMPL;
1193 }
1194
1195 static const IPersistStreamVtbl psvt =
1196 {
1197         IPersistStream_fnQueryInterface,
1198         IPersistStream_fnAddRef,
1199         IPersistStream_fnRelease,
1200         IPersistStream_fnGetClassID,
1201         IPersistStream_fnIsDirty,
1202         IPersistStream_fnLoad,
1203         IPersistStream_fnSave,
1204         IPersistStream_fnGetSizeMax
1205 };
1206
1207 /**************************************************************************
1208  *        IShellLink_Constructor
1209  */
1210 HRESULT WINAPI IShellLink_Constructor( IUnknown *pUnkOuter,
1211                REFIID riid, LPVOID *ppv )
1212 {
1213         IShellLinkImpl * sl;
1214         HRESULT r;
1215
1216         TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
1217
1218         *ppv = NULL;
1219
1220         if (pUnkOuter)
1221             return CLASS_E_NOAGGREGATION;
1222         sl = LocalAlloc(LMEM_ZEROINIT,sizeof(IShellLinkImpl));
1223         if (!sl)
1224             return E_OUTOFMEMORY;
1225
1226         sl->ref = 1;
1227         sl->lpVtbl = &slvt;
1228         sl->lpvtblw = &slvtw;
1229         sl->lpvtblPersistFile = &pfvt;
1230         sl->lpvtblPersistStream = &psvt;
1231         sl->lpvtblShellLinkDataList = &dlvt;
1232         sl->lpvtblShellExtInit = &eivt;
1233         sl->lpvtblContextMenu = &cmvt;
1234         sl->lpvtblObjectWithSite = &owsvt;
1235         sl->iShowCmd = SW_SHOWNORMAL;
1236         sl->bDirty = FALSE;
1237         sl->iIdOpen = -1;
1238         sl->site = NULL;
1239
1240         TRACE("(%p)->()\n",sl);
1241
1242         r = ShellLink_QueryInterface( sl, riid, ppv );
1243         ShellLink_Release( sl );
1244         return r;
1245 }
1246
1247
1248 static BOOL SHELL_ExistsFileW(LPCWSTR path)
1249 {
1250     if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
1251         return FALSE;
1252     return TRUE;
1253 }
1254
1255 /**************************************************************************
1256  *  ShellLink_UpdatePath
1257  *      update absolute path in sPath using relative path in sPathRel
1258  */
1259 static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
1260 {
1261     if (!path || !psPath)
1262         return E_INVALIDARG;
1263
1264     if (!*psPath && sPathRel) {
1265         WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
1266         LPWSTR final = NULL;
1267
1268         /* first try if [directory of link file] + [relative path] finds an existing file */
1269
1270         GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
1271         if( !final )
1272             final = buffer;
1273         lstrcpyW(final, sPathRel);
1274
1275         *abs_path = '\0';
1276
1277         if (SHELL_ExistsFileW(buffer)) {
1278             if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
1279                 lstrcpyW(abs_path, buffer);
1280         } else {
1281             /* try if [working directory] + [relative path] finds an existing file */
1282             if (sWorkDir) {
1283                 lstrcpyW(buffer, sWorkDir);
1284                 lstrcpyW(PathAddBackslashW(buffer), sPathRel);
1285
1286                 if (SHELL_ExistsFileW(buffer))
1287                     if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
1288                         lstrcpyW(abs_path, buffer);
1289             }
1290         }
1291
1292         /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
1293         if (!*abs_path)
1294             lstrcpyW(abs_path, sPathRel);
1295
1296         *psPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(abs_path)+1)*sizeof(WCHAR));
1297         if (!*psPath)
1298             return E_OUTOFMEMORY;
1299
1300         lstrcpyW(*psPath, abs_path);
1301     }
1302
1303     return S_OK;
1304 }
1305
1306 /**************************************************************************
1307  *        IShellLink_ConstructFromFile
1308  */
1309 HRESULT WINAPI IShellLink_ConstructFromFile( IUnknown* pUnkOuter, REFIID riid,
1310                LPCITEMIDLIST pidl, LPVOID* ppv)
1311 {
1312     IShellLinkW* psl;
1313
1314     HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl);
1315
1316     if (SUCCEEDED(hr)) {
1317         IPersistFile* ppf;
1318
1319         *ppv = NULL;
1320
1321         hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
1322
1323         if (SUCCEEDED(hr)) {
1324             WCHAR path[MAX_PATH];
1325
1326             if (SHGetPathFromIDListW(pidl, path)) 
1327                 hr = IPersistFile_Load(ppf, path, 0);
1328             else
1329                 hr = E_FAIL;
1330
1331             if (SUCCEEDED(hr))
1332                 *ppv = psl;
1333
1334             IPersistFile_Release(ppf);
1335         }
1336
1337         if (!*ppv)
1338             IShellLinkW_Release(psl);
1339     }
1340
1341     return hr;
1342 }
1343
1344 /**************************************************************************
1345  *  IShellLinkA_QueryInterface
1346  */
1347 static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
1348 {
1349     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1350     return ShellLink_QueryInterface( This, riid, ppvObj );
1351 }
1352
1353 /******************************************************************************
1354  * IShellLinkA_AddRef
1355  */
1356 static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
1357 {
1358     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1359     return ShellLink_AddRef( This );
1360 }
1361
1362 /******************************************************************************
1363  *      IShellLinkA_Release
1364  */
1365 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
1366 {
1367     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1368     return ShellLink_Release( This );
1369 }
1370
1371 static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
1372                   INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1373 {
1374     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1375
1376     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1377           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1378
1379     if (This->sComponent || This->sProduct)
1380         return S_FALSE;
1381
1382     if (cchMaxPath)
1383         pszFile[0] = 0;
1384     if (This->sPath)
1385         WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
1386                              pszFile, cchMaxPath, NULL, NULL);
1387
1388     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1389
1390     return S_OK;
1391 }
1392
1393 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
1394 {
1395     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1396
1397     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1398
1399     return IShellLinkW_GetIDList((IShellLinkW*)&(This->lpvtblw), ppidl);
1400 }
1401
1402 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
1403 {
1404     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1405
1406     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1407
1408     if (This->pPidl)
1409         ILFree(This->pPidl);
1410     This->pPidl = ILClone (pidl);
1411     This->bDirty = TRUE;
1412
1413     return S_OK;
1414 }
1415
1416 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
1417 {
1418     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1419
1420     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1421
1422     if( cchMaxName )
1423         pszName[0] = 0;
1424     if( This->sDescription )
1425         WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
1426             pszName, cchMaxName, NULL, NULL);
1427
1428     return S_OK;
1429 }
1430
1431 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
1432 {
1433     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1434
1435     TRACE("(%p)->(pName=%s)\n", This, pszName);
1436
1437     HeapFree(GetProcessHeap(), 0, This->sDescription);
1438     This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
1439     if ( !This->sDescription )
1440         return E_OUTOFMEMORY;
1441
1442     This->bDirty = TRUE;
1443
1444     return S_OK;
1445 }
1446
1447 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
1448 {
1449     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1450
1451     TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1452
1453     if( cchMaxPath )
1454         pszDir[0] = 0;
1455     if( This->sWorkDir )
1456         WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
1457                              pszDir, cchMaxPath, NULL, NULL);
1458
1459     return S_OK;
1460 }
1461
1462 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
1463 {
1464     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1465
1466     TRACE("(%p)->(dir=%s)\n",This, pszDir);
1467
1468     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1469     This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
1470     if ( !This->sWorkDir )
1471         return E_OUTOFMEMORY;
1472
1473     This->bDirty = TRUE;
1474
1475     return S_OK;
1476 }
1477
1478 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
1479 {
1480     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1481
1482     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1483
1484     if( cchMaxPath )
1485         pszArgs[0] = 0;
1486     if( This->sArgs )
1487         WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
1488                              pszArgs, cchMaxPath, NULL, NULL);
1489
1490     return S_OK;
1491 }
1492
1493 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
1494 {
1495     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1496
1497     TRACE("(%p)->(args=%s)\n",This, pszArgs);
1498
1499     HeapFree(GetProcessHeap(), 0, This->sArgs);
1500     if (pszArgs)
1501     {
1502         This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
1503         if( !This->sArgs )
1504             return E_OUTOFMEMORY;
1505     }
1506     else This->sArgs = NULL;
1507
1508     This->bDirty = TRUE;
1509
1510     return S_OK;
1511 }
1512
1513 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
1514 {
1515     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1516
1517     TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1518
1519     *pwHotkey = This->wHotKey;
1520
1521     return S_OK;
1522 }
1523
1524 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
1525 {
1526     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1527
1528     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1529
1530     This->wHotKey = wHotkey;
1531     This->bDirty = TRUE;
1532
1533     return S_OK;
1534 }
1535
1536 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
1537 {
1538     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1539
1540     TRACE("(%p)->(%p)\n",This, piShowCmd);
1541     *piShowCmd = This->iShowCmd;
1542     return S_OK;
1543 }
1544
1545 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
1546 {
1547     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1548
1549     TRACE("(%p) %d\n",This, iShowCmd);
1550
1551     This->iShowCmd = iShowCmd;
1552     This->bDirty = TRUE;
1553
1554     return NOERROR;
1555 }
1556
1557 static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPCITEMIDLIST pidl,
1558                                           LPSTR pszIconPath, int cchIconPath, int* piIcon)
1559 {
1560     LPCITEMIDLIST pidlLast;
1561
1562     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1563
1564     if (SUCCEEDED(hr)) {
1565         IExtractIconA* pei;
1566
1567         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, &pidlLast, &IID_IExtractIconA, NULL, (LPVOID*)&pei);
1568
1569         if (SUCCEEDED(hr)) {
1570             hr = IExtractIconA_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1571
1572             IExtractIconA_Release(pei);
1573         }
1574
1575         IShellFolder_Release(psf);
1576     }
1577
1578     return hr;
1579 }
1580
1581 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1582 {
1583     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1584
1585     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1586
1587     pszIconPath[0] = 0;
1588     *piIcon = This->iIcoNdx;
1589
1590     if (This->sIcoPath)
1591     {
1592         WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1593         return S_OK;
1594     }
1595
1596     if (This->pPidl || This->sPath)
1597     {
1598         IShellFolder* pdsk;
1599
1600         HRESULT hr = SHGetDesktopFolder(&pdsk);
1601
1602         if (SUCCEEDED(hr))
1603         {
1604             /* first look for an icon using the PIDL (if present) */
1605             if (This->pPidl)
1606                 hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1607             else
1608                 hr = E_FAIL;
1609
1610             /* if we couldn't find an icon yet, look for it using the file system path */
1611             if (FAILED(hr) && This->sPath)
1612             {
1613                 LPITEMIDLIST pidl;
1614
1615                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1616
1617                 if (SUCCEEDED(hr)) {
1618                     hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1619
1620                     SHFree(pidl);
1621                 }
1622             }
1623
1624             IShellFolder_Release(pdsk);
1625         }
1626
1627         return hr;
1628     }
1629     return S_OK;
1630 }
1631
1632 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
1633 {
1634     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1635
1636     TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1637
1638     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1639     This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1640     if ( !This->sIcoPath )
1641         return E_OUTOFMEMORY;
1642
1643     This->iIcoNdx = iIcon;
1644     This->bDirty = TRUE;
1645
1646     return S_OK;
1647 }
1648
1649 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1650 {
1651     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1652
1653     TRACE("(%p)->(path=%s %x)\n",This, pszPathRel, dwReserved);
1654
1655     HeapFree(GetProcessHeap(), 0, This->sPathRel);
1656     This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1657     This->bDirty = TRUE;
1658
1659     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1660 }
1661
1662 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1663 {
1664     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1665
1666     TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
1667
1668     return IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, fFlags );
1669 }
1670
1671 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1672 {
1673     HRESULT r;
1674     LPWSTR str;
1675     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1676
1677     TRACE("(%p)->(path=%s)\n",This, pszFile);
1678
1679     if (!pszFile) return E_INVALIDARG;
1680
1681     str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
1682     if( !str ) 
1683         return E_OUTOFMEMORY;
1684
1685     r = IShellLinkW_SetPath((IShellLinkW*)&(This->lpvtblw), str);
1686     HeapFree( GetProcessHeap(), 0, str );
1687
1688     return r;
1689 }
1690
1691 /**************************************************************************
1692 * IShellLink Implementation
1693 */
1694
1695 static const IShellLinkAVtbl slvt =
1696 {
1697     IShellLinkA_fnQueryInterface,
1698     IShellLinkA_fnAddRef,
1699     IShellLinkA_fnRelease,
1700     IShellLinkA_fnGetPath,
1701     IShellLinkA_fnGetIDList,
1702     IShellLinkA_fnSetIDList,
1703     IShellLinkA_fnGetDescription,
1704     IShellLinkA_fnSetDescription,
1705     IShellLinkA_fnGetWorkingDirectory,
1706     IShellLinkA_fnSetWorkingDirectory,
1707     IShellLinkA_fnGetArguments,
1708     IShellLinkA_fnSetArguments,
1709     IShellLinkA_fnGetHotkey,
1710     IShellLinkA_fnSetHotkey,
1711     IShellLinkA_fnGetShowCmd,
1712     IShellLinkA_fnSetShowCmd,
1713     IShellLinkA_fnGetIconLocation,
1714     IShellLinkA_fnSetIconLocation,
1715     IShellLinkA_fnSetRelativePath,
1716     IShellLinkA_fnResolve,
1717     IShellLinkA_fnSetPath
1718 };
1719
1720
1721 /**************************************************************************
1722  *  IShellLinkW_fnQueryInterface
1723  */
1724 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1725   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1726 {
1727     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1728     return ShellLink_QueryInterface( This, riid, ppvObj );
1729 }
1730
1731 /******************************************************************************
1732  * IShellLinkW_fnAddRef
1733  */
1734 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1735 {
1736     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1737     return ShellLink_AddRef( This );
1738 }
1739
1740 /******************************************************************************
1741  * IShellLinkW_fnRelease
1742  */
1743 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1744 {
1745     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1746     return ShellLink_Release( This );
1747 }
1748
1749 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1750 {
1751     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1752
1753     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1754           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1755
1756     if (This->sComponent || This->sProduct)
1757         return S_FALSE;
1758
1759     if (cchMaxPath)
1760         pszFile[0] = 0;
1761     if (This->sPath)
1762         lstrcpynW( pszFile, This->sPath, cchMaxPath );
1763
1764     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1765
1766     return S_OK;
1767 }
1768
1769 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1770 {
1771     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1772
1773     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1774
1775     if (!This->pPidl)
1776     {
1777         *ppidl = NULL;
1778         return S_FALSE;
1779     }
1780     *ppidl = ILClone(This->pPidl);
1781     return S_OK;
1782 }
1783
1784 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1785 {
1786     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1787
1788     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1789
1790     if( This->pPidl )
1791         ILFree( This->pPidl );
1792     This->pPidl = ILClone( pidl );
1793     if( !This->pPidl )
1794         return E_FAIL;
1795
1796     This->bDirty = TRUE;
1797
1798     return S_OK;
1799 }
1800
1801 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1802 {
1803     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1804
1805     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1806
1807     pszName[0] = 0;
1808     if( This->sDescription )
1809         lstrcpynW( pszName, This->sDescription, cchMaxName );
1810
1811     return S_OK;
1812 }
1813
1814 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1815 {
1816     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1817
1818     TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1819
1820     HeapFree(GetProcessHeap(), 0, This->sDescription);
1821     This->sDescription = HeapAlloc( GetProcessHeap(), 0,
1822                                     (lstrlenW( pszName )+1)*sizeof(WCHAR) );
1823     if ( !This->sDescription )
1824         return E_OUTOFMEMORY;
1825
1826     lstrcpyW( This->sDescription, pszName );
1827     This->bDirty = TRUE;
1828
1829     return S_OK;
1830 }
1831
1832 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1833 {
1834     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1835
1836     TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1837
1838     if( cchMaxPath )
1839         pszDir[0] = 0;
1840     if( This->sWorkDir )
1841         lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
1842
1843     return S_OK;
1844 }
1845
1846 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1847 {
1848     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1849
1850     TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1851
1852     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1853     This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
1854                                 (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
1855     if ( !This->sWorkDir )
1856         return E_OUTOFMEMORY;
1857     lstrcpyW( This->sWorkDir, pszDir );
1858     This->bDirty = TRUE;
1859
1860     return S_OK;
1861 }
1862
1863 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1864 {
1865     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1866
1867     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1868
1869     if( cchMaxPath )
1870         pszArgs[0] = 0;
1871     if( This->sArgs )
1872         lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
1873
1874     return NOERROR;
1875 }
1876
1877 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1878 {
1879     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1880
1881     TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1882
1883     HeapFree(GetProcessHeap(), 0, This->sArgs);
1884     if (pszArgs)
1885     {
1886         This->sArgs = HeapAlloc( GetProcessHeap(), 0,
1887                                  (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
1888         if ( !This->sArgs )
1889             return E_OUTOFMEMORY;
1890         lstrcpyW( This->sArgs, pszArgs );
1891     }
1892     else This->sArgs = NULL;
1893
1894     This->bDirty = TRUE;
1895
1896     return S_OK;
1897 }
1898
1899 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1900 {
1901     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1902
1903     TRACE("(%p)->(%p)\n",This, pwHotkey);
1904
1905     *pwHotkey=This->wHotKey;
1906
1907     return S_OK;
1908 }
1909
1910 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1911 {
1912     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1913
1914     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1915
1916     This->wHotKey = wHotkey;
1917     This->bDirty = TRUE;
1918
1919     return S_OK;
1920 }
1921
1922 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1923 {
1924     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1925
1926     TRACE("(%p)->(%p)\n",This, piShowCmd);
1927
1928     *piShowCmd = This->iShowCmd;
1929
1930     return S_OK;
1931 }
1932
1933 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1934 {
1935     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1936
1937     This->iShowCmd = iShowCmd;
1938     This->bDirty = TRUE;
1939
1940     return S_OK;
1941 }
1942
1943 static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPCITEMIDLIST pidl,
1944                                           LPWSTR pszIconPath, int cchIconPath, int* piIcon)
1945 {
1946     LPCITEMIDLIST pidlLast;
1947
1948     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1949
1950     if (SUCCEEDED(hr)) {
1951         IExtractIconW* pei;
1952
1953         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, &pidlLast, &IID_IExtractIconW, NULL, (LPVOID*)&pei);
1954
1955         if (SUCCEEDED(hr)) {
1956             hr = IExtractIconW_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1957
1958             IExtractIconW_Release(pei);
1959         }
1960
1961         IShellFolder_Release(psf);
1962     }
1963
1964     return hr;
1965 }
1966
1967 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1968 {
1969     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1970
1971     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1972
1973     pszIconPath[0] = 0;
1974     *piIcon = This->iIcoNdx;
1975
1976     if (This->sIcoPath)
1977     {
1978         lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
1979         return S_OK;
1980     }
1981
1982     if (This->pPidl || This->sPath)
1983     {
1984         IShellFolder* pdsk;
1985
1986         HRESULT hr = SHGetDesktopFolder(&pdsk);
1987
1988         if (SUCCEEDED(hr))
1989         {
1990             /* first look for an icon using the PIDL (if present) */
1991             if (This->pPidl)
1992                 hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1993             else
1994                 hr = E_FAIL;
1995
1996             /* if we couldn't find an icon yet, look for it using the file system path */
1997             if (FAILED(hr) && This->sPath)
1998             {
1999                 LPITEMIDLIST pidl;
2000
2001                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
2002
2003                 if (SUCCEEDED(hr))
2004                 {
2005                     hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
2006
2007                     SHFree(pidl);
2008                 }
2009             }
2010
2011             IShellFolder_Release(pdsk);
2012         }
2013         return hr;
2014     }
2015     return S_OK;
2016 }
2017
2018 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
2019 {
2020     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
2021
2022     TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
2023
2024     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
2025     This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
2026                                 (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
2027     if ( !This->sIcoPath )
2028         return E_OUTOFMEMORY;
2029     lstrcpyW( This->sIcoPath, pszIconPath );
2030
2031     This->iIcoNdx = iIcon;
2032     This->bDirty = TRUE;
2033
2034     return S_OK;
2035 }
2036
2037 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
2038 {
2039     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
2040
2041     TRACE("(%p)->(path=%s %x)\n",This, debugstr_w(pszPathRel), dwReserved);
2042
2043     HeapFree(GetProcessHeap(), 0, This->sPathRel);
2044     This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
2045                                 (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
2046     if ( !This->sPathRel )
2047         return E_OUTOFMEMORY;
2048     lstrcpyW( This->sPathRel, pszPathRel );
2049     This->bDirty = TRUE;
2050
2051     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
2052 }
2053
2054 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
2055 {
2056     HRESULT hr = S_OK;
2057     BOOL bSuccess;
2058
2059     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
2060
2061     TRACE("(%p)->(hwnd=%p flags=%x)\n",This, hwnd, fFlags);
2062
2063     /*FIXME: use IResolveShellLink interface */
2064
2065     if (!This->sPath && This->pPidl) {
2066         WCHAR buffer[MAX_PATH];
2067
2068         bSuccess = SHGetPathFromIDListW(This->pPidl, buffer);
2069
2070         if (bSuccess && *buffer) {
2071             This->sPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
2072             if (!This->sPath)
2073                 return E_OUTOFMEMORY;
2074
2075             lstrcpyW(This->sPath, buffer);
2076
2077             This->bDirty = TRUE;
2078         } else
2079             hr = S_OK;    /* don't report an error occurred while just caching information */
2080     }
2081
2082     if (!This->sIcoPath && This->sPath) {
2083         This->sIcoPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
2084         if (!This->sIcoPath)
2085             return E_OUTOFMEMORY;
2086
2087         lstrcpyW(This->sIcoPath, This->sPath);
2088         This->iIcoNdx = 0;
2089
2090         This->bDirty = TRUE;
2091     }
2092
2093     return hr;
2094 }
2095
2096 static LPWSTR ShellLink_GetAdvertisedArg(LPCWSTR str)
2097 {
2098     LPWSTR ret;
2099     LPCWSTR p;
2100     DWORD len;
2101
2102     if( !str )
2103         return NULL;
2104
2105     p = strchrW( str, ':' );
2106     if( !p )
2107         return NULL;
2108     len = p - str;
2109     ret = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
2110     if( !ret )
2111         return ret;
2112     memcpy( ret, str, sizeof(WCHAR)*len );
2113     ret[len] = 0;
2114     return ret;
2115 }
2116
2117 static HRESULT ShellLink_SetAdvertiseInfo(IShellLinkImpl *This, LPCWSTR str)
2118 {
2119     LPCWSTR szComponent = NULL, szProduct = NULL, p;
2120     WCHAR szGuid[39];
2121     HRESULT r;
2122     GUID guid;
2123     int len;
2124
2125     while( str[0] )
2126     {
2127         /* each segment must start with two colons */
2128         if( str[0] != ':' || str[1] != ':' )
2129             return E_FAIL;
2130
2131         /* the last segment is just two colons */
2132         if( !str[2] )
2133             break;
2134         str += 2;
2135
2136         /* there must be a colon straight after a guid */
2137         p = strchrW( str, ':' );
2138         if( !p )
2139             return E_FAIL;
2140         len = p - str;
2141         if( len != 38 )
2142             return E_FAIL;
2143
2144         /* get the guid, and check it's validly formatted */
2145         memcpy( szGuid, str, sizeof(WCHAR)*len );
2146         szGuid[len] = 0;
2147         r = CLSIDFromString( szGuid, &guid );
2148         if( r != S_OK )
2149             return r;
2150         str = p + 1;
2151
2152         /* match it up to a guid that we care about */
2153         if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutComponent ) && !szComponent )
2154             szComponent = str;
2155         else if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutProduct ) && !szProduct )
2156             szProduct = str;
2157         else
2158             return E_FAIL;
2159
2160         /* skip to the next field */
2161         str = strchrW( str, ':' );
2162         if( !str )
2163             return E_FAIL;
2164     }
2165
2166     /* we have to have a component for an advertised shortcut */
2167     if( !szComponent )
2168         return E_FAIL;
2169
2170     This->sComponent = ShellLink_GetAdvertisedArg( szComponent );
2171     This->sProduct = ShellLink_GetAdvertisedArg( szProduct );
2172
2173     TRACE("Component = %s\n", debugstr_w(This->sComponent));
2174     TRACE("Product = %s\n", debugstr_w(This->sProduct));
2175
2176     return S_OK;
2177 }
2178
2179 static BOOL ShellLink_GetVolumeInfo(LPCWSTR path, volume_info *volume)
2180 {
2181     const int label_sz = sizeof volume->label/sizeof volume->label[0];
2182     WCHAR drive[4] = { path[0], ':', '\\', 0 };
2183     BOOL r;
2184
2185     volume->type = GetDriveTypeW(drive);
2186     r = GetVolumeInformationW(drive, volume->label, label_sz,
2187                               &volume->serial, NULL, NULL, NULL, 0);
2188     TRACE("r = %d type %d serial %08x name %s\n", r,
2189           volume->type, volume->serial, debugstr_w(volume->label));
2190     return r;
2191 }
2192
2193 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
2194 {
2195     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
2196     WCHAR buffer[MAX_PATH];
2197     LPWSTR fname, unquoted = NULL;
2198     HRESULT hr = S_OK;
2199     UINT len;
2200
2201     TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
2202
2203     if (!pszFile) return E_INVALIDARG;
2204
2205     /* quotes at the ends of the string are stripped */
2206     len = lstrlenW(pszFile);
2207     if (pszFile[0] == '"' && pszFile[len-1] == '"')
2208     {
2209         unquoted = strdupW(pszFile);
2210         PathUnquoteSpacesW(unquoted);
2211         pszFile = unquoted;
2212     }
2213
2214     /* any other quote marks are invalid */
2215     if (strchrW(pszFile, '"'))
2216     {
2217         HeapFree(GetProcessHeap(), 0, unquoted);
2218         return S_FALSE;
2219     }
2220
2221     HeapFree(GetProcessHeap(), 0, This->sPath);
2222     This->sPath = NULL;
2223
2224     HeapFree(GetProcessHeap(), 0, This->sComponent);
2225     This->sComponent = NULL;
2226
2227     if (This->pPidl)
2228         ILFree(This->pPidl);
2229     This->pPidl = NULL;
2230
2231     if (S_OK != ShellLink_SetAdvertiseInfo( This, pszFile ))
2232     {
2233         if (*pszFile == '\0')
2234             *buffer = '\0';
2235         else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
2236             return E_FAIL;
2237         else if(!PathFileExistsW(buffer) &&
2238                 !SearchPathW(NULL, pszFile, NULL, MAX_PATH, buffer, NULL))
2239           hr = S_FALSE;
2240
2241         This->pPidl = SHSimpleIDListFromPathW(pszFile);
2242         ShellLink_GetVolumeInfo(buffer, &This->volume);
2243
2244         This->sPath = HeapAlloc( GetProcessHeap(), 0,
2245                              (lstrlenW( buffer )+1) * sizeof (WCHAR) );
2246         if (!This->sPath)
2247         {
2248             HeapFree(GetProcessHeap(), 0, unquoted);
2249             return E_OUTOFMEMORY;
2250         }
2251
2252         lstrcpyW(This->sPath, buffer);
2253     }
2254     This->bDirty = TRUE;
2255     HeapFree(GetProcessHeap(), 0, unquoted);
2256
2257     return hr;
2258 }
2259
2260 /**************************************************************************
2261 * IShellLinkW Implementation
2262 */
2263
2264 static const IShellLinkWVtbl slvtw =
2265 {
2266     IShellLinkW_fnQueryInterface,
2267     IShellLinkW_fnAddRef,
2268     IShellLinkW_fnRelease,
2269     IShellLinkW_fnGetPath,
2270     IShellLinkW_fnGetIDList,
2271     IShellLinkW_fnSetIDList,
2272     IShellLinkW_fnGetDescription,
2273     IShellLinkW_fnSetDescription,
2274     IShellLinkW_fnGetWorkingDirectory,
2275     IShellLinkW_fnSetWorkingDirectory,
2276     IShellLinkW_fnGetArguments,
2277     IShellLinkW_fnSetArguments,
2278     IShellLinkW_fnGetHotkey,
2279     IShellLinkW_fnSetHotkey,
2280     IShellLinkW_fnGetShowCmd,
2281     IShellLinkW_fnSetShowCmd,
2282     IShellLinkW_fnGetIconLocation,
2283     IShellLinkW_fnSetIconLocation,
2284     IShellLinkW_fnSetRelativePath,
2285     IShellLinkW_fnResolve,
2286     IShellLinkW_fnSetPath
2287 };
2288
2289 static HRESULT WINAPI
2290 ShellLink_DataList_QueryInterface( IShellLinkDataList* iface, REFIID riid, void** ppvObject)
2291 {
2292     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2293     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2294 }
2295
2296 static ULONG WINAPI
2297 ShellLink_DataList_AddRef( IShellLinkDataList* iface )
2298 {
2299     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2300     return IShellLinkA_AddRef((IShellLinkA*)This);
2301 }
2302
2303 static ULONG WINAPI
2304 ShellLink_DataList_Release( IShellLinkDataList* iface )
2305 {
2306     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2307     return ShellLink_Release( This );
2308 }
2309
2310 static HRESULT WINAPI
2311 ShellLink_AddDataBlock( IShellLinkDataList* iface, void* pDataBlock )
2312 {
2313     FIXME("\n");
2314     return E_NOTIMPL;
2315 }
2316
2317 static HRESULT WINAPI
2318 ShellLink_CopyDataBlock( IShellLinkDataList* iface, DWORD dwSig, void** ppDataBlock )
2319 {
2320     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2321     LPVOID block = NULL;
2322     HRESULT r = E_FAIL;
2323
2324     TRACE("%p %08x %p\n", iface, dwSig, ppDataBlock );
2325
2326     switch (dwSig)
2327     {
2328     case EXP_DARWIN_ID_SIG:
2329         if (!This->sComponent)
2330             break;
2331         block = shelllink_build_darwinid( This->sComponent, dwSig );
2332         r = S_OK;
2333         break;
2334     case EXP_SZ_LINK_SIG:
2335     case NT_CONSOLE_PROPS_SIG:
2336     case NT_FE_CONSOLE_PROPS_SIG:
2337     case EXP_SPECIAL_FOLDER_SIG:
2338     case EXP_SZ_ICON_SIG:
2339         FIXME("valid but unhandled datablock %08x\n", dwSig);
2340         break;
2341     default:
2342         ERR("unknown datablock %08x\n", dwSig);
2343     }
2344     *ppDataBlock = block;
2345     return r;
2346 }
2347
2348 static HRESULT WINAPI
2349 ShellLink_RemoveDataBlock( IShellLinkDataList* iface, DWORD dwSig )
2350 {
2351     FIXME("\n");
2352     return E_NOTIMPL;
2353 }
2354
2355 static HRESULT WINAPI
2356 ShellLink_GetFlags( IShellLinkDataList* iface, DWORD* pdwFlags )
2357 {
2358     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2359     DWORD flags = 0;
2360
2361     FIXME("%p %p\n", This, pdwFlags );
2362
2363     /* FIXME: add more */
2364     if (This->sArgs)
2365         flags |= SLDF_HAS_ARGS;
2366     if (This->sComponent)
2367         flags |= SLDF_HAS_DARWINID;
2368     if (This->sIcoPath)
2369         flags |= SLDF_HAS_ICONLOCATION;
2370     if (This->sProduct)
2371         flags |= SLDF_HAS_LOGO3ID;
2372     if (This->pPidl)
2373         flags |= SLDF_HAS_ID_LIST;
2374
2375     *pdwFlags = flags;
2376
2377     return S_OK;
2378 }
2379
2380 static HRESULT WINAPI
2381 ShellLink_SetFlags( IShellLinkDataList* iface, DWORD dwFlags )
2382 {
2383     FIXME("\n");
2384     return E_NOTIMPL;
2385 }
2386
2387 static const IShellLinkDataListVtbl dlvt =
2388 {
2389     ShellLink_DataList_QueryInterface,
2390     ShellLink_DataList_AddRef,
2391     ShellLink_DataList_Release,
2392     ShellLink_AddDataBlock,
2393     ShellLink_CopyDataBlock,
2394     ShellLink_RemoveDataBlock,
2395     ShellLink_GetFlags,
2396     ShellLink_SetFlags
2397 };
2398
2399 static HRESULT WINAPI
2400 ShellLink_ExtInit_QueryInterface( IShellExtInit* iface, REFIID riid, void** ppvObject )
2401 {
2402     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2403     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2404 }
2405
2406 static ULONG WINAPI
2407 ShellLink_ExtInit_AddRef( IShellExtInit* iface )
2408 {
2409     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2410     return IShellLinkA_AddRef((IShellLinkA*)This);
2411 }
2412
2413 static ULONG WINAPI
2414 ShellLink_ExtInit_Release( IShellExtInit* iface )
2415 {
2416     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2417     return ShellLink_Release( This );
2418 }
2419
2420 /**************************************************************************
2421  * ShellLink implementation of IShellExtInit::Initialize()
2422  *
2423  * Loads the shelllink from the dataobject the shell is pointing to.
2424  */
2425 static HRESULT WINAPI
2426 ShellLink_ExtInit_Initialize( IShellExtInit* iface, LPCITEMIDLIST pidlFolder,
2427                               IDataObject *pdtobj, HKEY hkeyProgID )
2428 {
2429     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2430     FORMATETC format;
2431     STGMEDIUM stgm;
2432     UINT count;
2433     HRESULT r = E_FAIL;
2434
2435     TRACE("%p %p %p %p\n", This, pidlFolder, pdtobj, hkeyProgID );
2436
2437     if( !pdtobj )
2438         return r;
2439
2440     format.cfFormat = CF_HDROP;
2441     format.ptd = NULL;
2442     format.dwAspect = DVASPECT_CONTENT;
2443     format.lindex = -1;
2444     format.tymed = TYMED_HGLOBAL;
2445
2446     if( FAILED( IDataObject_GetData( pdtobj, &format, &stgm ) ) )
2447         return r;
2448
2449     count = DragQueryFileW( stgm.u.hGlobal, -1, NULL, 0 );
2450     if( count == 1 )
2451     {
2452         LPWSTR path;
2453
2454         count = DragQueryFileW( stgm.u.hGlobal, 0, NULL, 0 );
2455         count++;
2456         path = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
2457         if( path )
2458         {
2459             IPersistFile *pf = (IPersistFile*) &This->lpvtblPersistFile;
2460
2461             count = DragQueryFileW( stgm.u.hGlobal, 0, path, count );
2462             r = IPersistFile_Load( pf, path, 0 );
2463             HeapFree( GetProcessHeap(), 0, path );
2464         }
2465     }
2466     ReleaseStgMedium( &stgm );
2467
2468     return r;
2469 }
2470
2471 static const IShellExtInitVtbl eivt =
2472 {
2473     ShellLink_ExtInit_QueryInterface,
2474     ShellLink_ExtInit_AddRef,
2475     ShellLink_ExtInit_Release,
2476     ShellLink_ExtInit_Initialize
2477 };
2478
2479 static HRESULT WINAPI
2480 ShellLink_ContextMenu_QueryInterface( IContextMenu* iface, REFIID riid, void** ppvObject )
2481 {
2482     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2483     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2484 }
2485
2486 static ULONG WINAPI
2487 ShellLink_ContextMenu_AddRef( IContextMenu* iface )
2488 {
2489     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2490     return IShellLinkA_AddRef((IShellLinkA*)This);
2491 }
2492
2493 static ULONG WINAPI
2494 ShellLink_ContextMenu_Release( IContextMenu* iface )
2495 {
2496     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2497     return ShellLink_Release( This );
2498 }
2499
2500 static HRESULT WINAPI
2501 ShellLink_QueryContextMenu( IContextMenu* iface, HMENU hmenu, UINT indexMenu,
2502                             UINT idCmdFirst, UINT idCmdLast, UINT uFlags )
2503 {
2504     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2505     static WCHAR szOpen[] = { 'O','p','e','n',0 };
2506     MENUITEMINFOW mii;
2507     int id = 1;
2508
2509     TRACE("%p %p %u %u %u %u\n", This,
2510           hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
2511
2512     if ( !hmenu )
2513         return E_INVALIDARG;
2514
2515     memset( &mii, 0, sizeof mii );
2516     mii.cbSize = sizeof mii;
2517     mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
2518     mii.dwTypeData = szOpen;
2519     mii.cch = strlenW( mii.dwTypeData );
2520     mii.wID = idCmdFirst + id++;
2521     mii.fState = MFS_DEFAULT | MFS_ENABLED;
2522     mii.fType = MFT_STRING;
2523     if (!InsertMenuItemW( hmenu, indexMenu, TRUE, &mii ))
2524         return E_FAIL;
2525     This->iIdOpen = 0;
2526
2527     return MAKE_HRESULT( SEVERITY_SUCCESS, 0, id );
2528 }
2529
2530 static LPWSTR
2531 shelllink_get_msi_component_path( LPWSTR component )
2532 {
2533     LPWSTR path;
2534     DWORD r, sz = 0;
2535
2536     r = CommandLineFromMsiDescriptor( component, NULL, &sz );
2537     if (r != ERROR_SUCCESS)
2538          return NULL;
2539
2540     sz++;
2541     path = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
2542     r = CommandLineFromMsiDescriptor( component, path, &sz );
2543     if (r != ERROR_SUCCESS)
2544     {
2545         HeapFree( GetProcessHeap(), 0, path );
2546         path = NULL;
2547     }
2548
2549     TRACE("returning %s\n", debugstr_w( path ) );
2550
2551     return path;
2552 }
2553
2554 static HRESULT WINAPI
2555 ShellLink_InvokeCommand( IContextMenu* iface, LPCMINVOKECOMMANDINFO lpici )
2556 {
2557     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2558     static const WCHAR szOpen[] = { 'O','p','e','n',0 };
2559     SHELLEXECUTEINFOW sei;
2560     HWND hwnd = NULL; /* FIXME: get using interface set from IObjectWithSite */
2561     LPWSTR args = NULL;
2562     LPWSTR path = NULL;
2563     HRESULT r;
2564
2565     TRACE("%p %p\n", This, lpici );
2566
2567     if ( lpici->cbSize < sizeof (CMINVOKECOMMANDINFO) )
2568         return E_INVALIDARG;
2569
2570     if ( lpici->lpVerb != MAKEINTRESOURCEA(This->iIdOpen) )
2571     {
2572         ERR("Unknown id %p != %d\n", lpici->lpVerb, This->iIdOpen );
2573         return E_INVALIDARG;
2574     }
2575
2576     r = IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, 0 );
2577     if ( FAILED( r ) )
2578         return r;
2579
2580     if ( This->sComponent )
2581     {
2582         path = shelllink_get_msi_component_path( This->sComponent );
2583         if (!path)
2584             return E_FAIL;
2585     }
2586     else
2587         path = strdupW( This->sPath );
2588
2589     if ( lpici->cbSize == sizeof (CMINVOKECOMMANDINFOEX) &&
2590          ( lpici->fMask & CMIC_MASK_UNICODE ) )
2591     {
2592         LPCMINVOKECOMMANDINFOEX iciex = (LPCMINVOKECOMMANDINFOEX) lpici;
2593         DWORD len = 2;
2594
2595         if ( This->sArgs )
2596             len += lstrlenW( This->sArgs );
2597         if ( iciex->lpParametersW )
2598             len += lstrlenW( iciex->lpParametersW );
2599
2600         args = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
2601         args[0] = 0;
2602         if ( This->sArgs )
2603             lstrcatW( args, This->sArgs );
2604         if ( iciex->lpParametersW )
2605         {
2606             static const WCHAR space[] = { ' ', 0 };
2607             lstrcatW( args, space );
2608             lstrcatW( args, iciex->lpParametersW );
2609         }
2610     }
2611
2612     memset( &sei, 0, sizeof sei );
2613     sei.cbSize = sizeof sei;
2614     sei.fMask = SEE_MASK_UNICODE | (lpici->fMask & (SEE_MASK_NOASYNC|SEE_MASK_ASYNCOK|SEE_MASK_FLAG_NO_UI));
2615     sei.lpFile = path;
2616     sei.nShow = This->iShowCmd;
2617     sei.lpIDList = This->pPidl;
2618     sei.lpDirectory = This->sWorkDir;
2619     sei.lpParameters = args;
2620     sei.lpVerb = szOpen;
2621
2622     if( ShellExecuteExW( &sei ) )
2623         r = S_OK;
2624     else
2625         r = E_FAIL;
2626
2627     HeapFree( GetProcessHeap(), 0, args );
2628     HeapFree( GetProcessHeap(), 0, path );
2629
2630     return r;
2631 }
2632
2633 static HRESULT WINAPI
2634 ShellLink_GetCommandString( IContextMenu* iface, UINT_PTR idCmd, UINT uType,
2635                             UINT* pwReserved, LPSTR pszName, UINT cchMax )
2636 {
2637     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2638
2639     FIXME("%p %lu %u %p %p %u\n", This,
2640           idCmd, uType, pwReserved, pszName, cchMax );
2641
2642     return E_NOTIMPL;
2643 }
2644
2645 static const IContextMenuVtbl cmvt =
2646 {
2647     ShellLink_ContextMenu_QueryInterface,
2648     ShellLink_ContextMenu_AddRef,
2649     ShellLink_ContextMenu_Release,
2650     ShellLink_QueryContextMenu,
2651     ShellLink_InvokeCommand,
2652     ShellLink_GetCommandString
2653 };
2654
2655 static HRESULT WINAPI
2656 ShellLink_ObjectWithSite_QueryInterface( IObjectWithSite* iface, REFIID riid, void** ppvObject )
2657 {
2658     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2659     return ShellLink_QueryInterface( This, riid, ppvObject );
2660 }
2661
2662 static ULONG WINAPI
2663 ShellLink_ObjectWithSite_AddRef( IObjectWithSite* iface )
2664 {
2665     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2666     return ShellLink_AddRef( This );
2667 }
2668
2669 static ULONG WINAPI
2670 ShellLink_ObjectWithSite_Release( IObjectWithSite* iface )
2671 {
2672     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2673     return ShellLink_Release( This );
2674 }
2675
2676 static HRESULT WINAPI
2677 ShellLink_GetSite( IObjectWithSite *iface, REFIID iid, void ** ppvSite )
2678 {
2679     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2680
2681     TRACE("%p %s %p\n", This, debugstr_guid( iid ), ppvSite );
2682
2683     if ( !This->site )
2684         return E_FAIL;
2685     return IUnknown_QueryInterface( This->site, iid, ppvSite );
2686 }
2687
2688 static HRESULT WINAPI
2689 ShellLink_SetSite( IObjectWithSite *iface, IUnknown *punk )
2690 {
2691     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2692
2693     TRACE("%p %p\n", iface, punk);
2694
2695     if ( punk )
2696         IUnknown_AddRef( punk );
2697     This->site = punk;
2698
2699     return S_OK;
2700 }
2701
2702 static const IObjectWithSiteVtbl owsvt =
2703 {
2704     ShellLink_ObjectWithSite_QueryInterface,
2705     ShellLink_ObjectWithSite_AddRef,
2706     ShellLink_ObjectWithSite_Release,
2707     ShellLink_SetSite,
2708     ShellLink_GetSite,
2709 };