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