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