Add the IObjectWithSite interface.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * NOTES
22  *   Nearly complete informations 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
55 #include "initguid.h"
56
57 WINE_DEFAULT_DEBUG_CHANNEL(shell);
58
59 DEFINE_GUID( SHELL32_AdvtShortcutProduct,
60        0x9db1186f,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
61 DEFINE_GUID( SHELL32_AdvtShortcutComponent,
62        0x9db1186e,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
63
64 /* link file formats */
65
66 #include "pshpack1.h"
67
68 typedef struct _LINK_HEADER
69 {
70         DWORD    dwSize;        /* 0x00 size of the header - 0x4c */
71         GUID     MagicGuid;     /* 0x04 is CLSID_ShellLink */
72         DWORD    dwFlags;       /* 0x14 describes elements following */
73         DWORD    dwFileAttr;    /* 0x18 attributes of the target file */
74         FILETIME Time1;         /* 0x1c */
75         FILETIME Time2;         /* 0x24 */
76         FILETIME Time3;         /* 0x2c */
77         DWORD    dwFileLength;  /* 0x34 File length */
78         DWORD    nIcon;         /* 0x38 icon number */
79         DWORD   fStartup;       /* 0x3c startup type */
80         DWORD   wHotKey;        /* 0x40 hotkey */
81         DWORD   Unknown5;       /* 0x44 */
82         DWORD   Unknown6;       /* 0x48 */
83 } LINK_HEADER, * PLINK_HEADER;
84
85 #define SHLINK_LOCAL  0
86 #define SHLINK_REMOTE 1
87
88 typedef struct _LOCATION_INFO
89 {
90     DWORD  dwTotalSize;
91     DWORD  dwHeaderSize;
92     DWORD  dwFlags;
93     DWORD  dwVolTableOfs;
94     DWORD  dwLocalPathOfs;
95     DWORD  dwNetworkVolTableOfs;
96     DWORD  dwFinalPathOfs;
97 } LOCATION_INFO;
98
99 typedef struct _LOCAL_VOLUME_INFO
100 {
101     DWORD dwSize;
102     DWORD dwType;
103     DWORD dwVolSerial;
104     DWORD dwVolLabelOfs;
105 } LOCAL_VOLUME_INFO;
106
107 typedef struct volume_info_t
108 {
109     DWORD type;
110     DWORD serial;
111     WCHAR label[12];  /* assume 8.3 */
112 } volume_info;
113
114 #include "poppack.h"
115
116 static const IShellLinkAVtbl slvt;
117 static const IShellLinkWVtbl slvtw;
118 static const IPersistFileVtbl pfvt;
119 static const IPersistStreamVtbl psvt;
120 static const IShellLinkDataListVtbl dlvt;
121 static const IShellExtInitVtbl eivt;
122 static const IContextMenuVtbl cmvt;
123 static const IObjectWithSiteVtbl owsvt;
124
125 /* IShellLink Implementation */
126
127 typedef struct
128 {
129         const IShellLinkAVtbl *lpVtbl;
130         const IShellLinkWVtbl *lpvtblw;
131         const IPersistFileVtbl *lpvtblPersistFile;
132         const IPersistStreamVtbl *lpvtblPersistStream;
133         const IShellLinkDataListVtbl *lpvtblShellLinkDataList;
134         const IShellExtInitVtbl *lpvtblShellExtInit;
135         const IContextMenuVtbl *lpvtblContextMenu;
136         const IObjectWithSiteVtbl *lpvtblObjectWithSite;
137
138         LONG            ref;
139
140         /* data structures according to the informations in the link */
141         LPITEMIDLIST    pPidl;
142         WORD            wHotKey;
143         SYSTEMTIME      time1;
144         SYSTEMTIME      time2;
145         SYSTEMTIME      time3;
146
147         DWORD         iShowCmd;
148         LPWSTR        sIcoPath;
149         INT           iIcoNdx;
150         LPWSTR        sPath;
151         LPWSTR        sArgs;
152         LPWSTR        sWorkDir;
153         LPWSTR        sDescription;
154         LPWSTR        sPathRel;
155         LPWSTR        sProduct;
156         LPWSTR        sComponent;
157         volume_info   volume;
158
159         BOOL          bDirty;
160         IUnknown      *site;
161 } IShellLinkImpl;
162
163 static inline IShellLinkImpl *impl_from_IShellLinkW( IShellLinkW *iface )
164 {
165     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblw));
166 }
167
168 static inline IShellLinkImpl *impl_from_IPersistFile( IPersistFile *iface )
169 {
170     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistFile));
171 }
172
173 static inline IShellLinkImpl *impl_from_IPersistStream( IPersistStream *iface )
174 {
175     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblPersistStream));
176 }
177
178 static inline IShellLinkImpl *impl_from_IShellLinkDataList( IShellLinkDataList *iface )
179 {
180     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellLinkDataList));
181 }
182
183 static inline IShellLinkImpl *impl_from_IShellExtInit( IShellExtInit *iface )
184 {
185     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblShellExtInit));
186 }
187
188 static inline IShellLinkImpl *impl_from_IContextMenu( IContextMenu *iface )
189 {
190     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblContextMenu));
191 }
192
193 static inline IShellLinkImpl *impl_from_IObjectWithSite( IObjectWithSite *iface )
194 {
195     return (IShellLinkImpl *)((char*)iface - FIELD_OFFSET(IShellLinkImpl, lpvtblObjectWithSite));
196 }
197
198 static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath);
199
200 /* strdup on the process heap */
201 inline static LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
202 {
203     INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
204     LPWSTR p = HeapAlloc( heap, flags, len*sizeof (WCHAR) );
205     if( !p )
206         return p;
207     MultiByteToWideChar( CP_ACP, 0, str, -1, p, len );
208     return p;
209 }
210
211 /**************************************************************************
212  *  ShellLink::QueryInterface implementation
213  */
214 static HRESULT ShellLink_QueryInterface( IShellLinkImpl *This, REFIID riid,  LPVOID *ppvObj)
215 {
216     TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
217
218     *ppvObj = NULL;
219
220     if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IShellLinkA))
221     {
222         *ppvObj = This;
223     }
224     else if(IsEqualIID(riid, &IID_IShellLinkW))
225     {
226         *ppvObj = &(This->lpvtblw);
227     }
228     else if(IsEqualIID(riid, &IID_IPersistFile))
229     {
230         *ppvObj = &(This->lpvtblPersistFile);
231     }
232     else if(IsEqualIID(riid, &IID_IPersistStream))
233     {
234         *ppvObj = &(This->lpvtblPersistStream);
235     }
236     else if(IsEqualIID(riid, &IID_IShellLinkDataList))
237     {
238         *ppvObj = &(This->lpvtblShellLinkDataList);
239     }
240     else if(IsEqualIID(riid, &IID_IShellExtInit))
241     {
242         *ppvObj = &(This->lpvtblShellExtInit);
243     }
244     else if(IsEqualIID(riid, &IID_IContextMenu))
245     {
246         *ppvObj = &(This->lpvtblContextMenu);
247     }
248     else if(IsEqualIID(riid, &IID_IObjectWithSite))
249     {
250         *ppvObj = &(This->lpvtblObjectWithSite);
251     }
252
253     if(*ppvObj)
254     {
255         IUnknown_AddRef((IUnknown*)(*ppvObj));
256         TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
257         return S_OK;
258     }
259     ERR("-- Interface: E_NOINTERFACE\n");
260     return E_NOINTERFACE;
261 }
262
263 /**************************************************************************
264  *  ShellLink::AddRef implementation
265  */
266 static ULONG ShellLink_AddRef( IShellLinkImpl *This )
267 {
268     ULONG refCount = InterlockedIncrement(&This->ref);
269
270     TRACE("(%p)->(count=%lu)\n", This, refCount - 1);
271
272     return refCount;
273 }
274
275 /**************************************************************************
276  *  ShellLink::Release implementation
277  */
278 static ULONG ShellLink_Release( IShellLinkImpl *This )
279 {
280     ULONG refCount = InterlockedDecrement(&This->ref);
281
282     TRACE("(%p)->(count=%lu)\n", This, refCount + 1);
283
284     if (refCount)
285         return refCount;
286
287     TRACE("-- destroying IShellLink(%p)\n",This);
288
289     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
290     HeapFree(GetProcessHeap(), 0, This->sArgs);
291     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
292     HeapFree(GetProcessHeap(), 0, This->sDescription);
293     HeapFree(GetProcessHeap(),0,This->sPath);
294
295     if (This->site)
296         IUnknown_Release( This->site );
297
298     if (This->pPidl)
299         ILFree(This->pPidl);
300
301     LocalFree((HANDLE)This);
302
303     return 0;
304 }
305
306 static HRESULT ShellLink_GetClassID( IShellLinkImpl *This, CLSID *pclsid )
307 {
308     TRACE("%p %p\n", This, pclsid);
309
310     memcpy( pclsid, &CLSID_ShellLink, sizeof (CLSID) );
311     return S_OK;
312 }
313
314 /**************************************************************************
315  *  IPersistFile_QueryInterface
316  */
317 static HRESULT WINAPI IPersistFile_fnQueryInterface(
318         IPersistFile* iface,
319         REFIID riid,
320         LPVOID *ppvObj)
321 {
322     IShellLinkImpl *This = impl_from_IPersistFile(iface);
323     return ShellLink_QueryInterface( This, riid, ppvObj );
324 }
325
326 /******************************************************************************
327  * IPersistFile_AddRef
328  */
329 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
330 {
331     IShellLinkImpl *This = impl_from_IPersistFile(iface);
332     return ShellLink_AddRef( This );
333 }
334
335 /******************************************************************************
336  * IPersistFile_Release
337  */
338 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile* iface)
339 {
340     IShellLinkImpl *This = impl_from_IPersistFile(iface);
341     return IShellLinkA_Release((IShellLinkA*)This);
342 }
343
344 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
345 {
346     IShellLinkImpl *This = impl_from_IPersistFile(iface);
347     return ShellLink_GetClassID( This, pClassID );
348 }
349
350 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
351 {
352         IShellLinkImpl *This = impl_from_IPersistFile(iface);
353
354         TRACE("(%p)\n",This);
355
356         if (This->bDirty)
357             return S_OK;
358
359         return S_FALSE;
360 }
361
362 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
363 {
364         IShellLinkImpl *This = impl_from_IPersistFile(iface);
365         IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
366         HRESULT r;
367         IStream *stm;
368
369         TRACE("(%p, %s, %lx)\n",This, debugstr_w(pszFileName), dwMode);
370
371         if( dwMode == 0 )
372                 dwMode = STGM_READ | STGM_SHARE_DENY_WRITE;
373         r = SHCreateStreamOnFileW(pszFileName, dwMode, &stm);
374         if( SUCCEEDED( r ) )
375         {
376             r = IPersistStream_Load(StreamThis, stm);
377             ShellLink_UpdatePath(This->sPathRel, pszFileName, This->sWorkDir, &This->sPath);
378             IStream_Release( stm );
379             This->bDirty = FALSE;
380         }
381         TRACE("-- returning hr %08lx\n", r);
382         return r;
383 }
384
385 static BOOL StartLinkProcessor( LPCOLESTR szLink )
386 {
387     static const WCHAR szFormat[] = {
388         'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
389         ' ','-','r',' ','"','%','s','"',0 };
390     LONG len;
391     LPWSTR buffer;
392     STARTUPINFOW si;
393     PROCESS_INFORMATION pi;
394
395     len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
396     buffer = HeapAlloc( GetProcessHeap(), 0, len );
397     if( !buffer )
398         return FALSE;
399
400     wsprintfW( buffer, szFormat, szLink );
401
402     TRACE("starting %s\n",debugstr_w(buffer));
403
404     memset(&si, 0, sizeof(si));
405     si.cb = sizeof(si);
406     if (!CreateProcessW( NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) return FALSE;
407
408     /* wait for a while to throttle the creation of linker processes */
409     if( WAIT_OBJECT_0 != WaitForSingleObject( pi.hProcess, 10000 ) )
410         WARN("Timed out waiting for shell linker\n");
411
412     CloseHandle( pi.hProcess );
413     CloseHandle( pi.hThread );
414
415     return TRUE;
416 }
417
418 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
419 {
420     IShellLinkImpl *This = impl_from_IPersistFile(iface);
421     IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
422     HRESULT r;
423     IStream *stm;
424
425     TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
426
427     if (!pszFileName)
428         return E_FAIL;
429
430     r = SHCreateStreamOnFileW( pszFileName, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &stm );
431     if( SUCCEEDED( r ) )
432     {
433         r = IPersistStream_Save(StreamThis, stm, FALSE);
434         IStream_Release( stm );
435
436         if( SUCCEEDED( r ) )
437         {
438             StartLinkProcessor( pszFileName );
439
440             This->bDirty = FALSE;
441         }
442         else
443         {
444             DeleteFileW( pszFileName );
445             WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
446         }
447     }
448
449     return r;
450 }
451
452 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
453 {
454         IShellLinkImpl *This = impl_from_IPersistFile(iface);
455         FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
456         return NOERROR;
457 }
458
459 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *ppszFileName)
460 {
461         IShellLinkImpl *This = impl_from_IPersistFile(iface);
462         FIXME("(%p)\n",This);
463         return NOERROR;
464 }
465
466 static const IPersistFileVtbl pfvt =
467 {
468         IPersistFile_fnQueryInterface,
469         IPersistFile_fnAddRef,
470         IPersistFile_fnRelease,
471         IPersistFile_fnGetClassID,
472         IPersistFile_fnIsDirty,
473         IPersistFile_fnLoad,
474         IPersistFile_fnSave,
475         IPersistFile_fnSaveCompleted,
476         IPersistFile_fnGetCurFile
477 };
478
479 /************************************************************************
480  * IPersistStream_QueryInterface
481  */
482 static HRESULT WINAPI IPersistStream_fnQueryInterface(
483         IPersistStream* iface,
484         REFIID     riid,
485         VOID**     ppvObj)
486 {
487     IShellLinkImpl *This = impl_from_IPersistStream(iface);
488     return ShellLink_QueryInterface( This, riid, ppvObj );
489 }
490
491 /************************************************************************
492  * IPersistStream_Release
493  */
494 static ULONG WINAPI IPersistStream_fnRelease(
495         IPersistStream* iface)
496 {
497     IShellLinkImpl *This = impl_from_IPersistStream(iface);
498     return IShellLinkA_Release((IShellLinkA*)This);
499 }
500
501 /************************************************************************
502  * IPersistStream_AddRef
503  */
504 static ULONG WINAPI IPersistStream_fnAddRef(
505         IPersistStream* iface)
506 {
507     IShellLinkImpl *This = impl_from_IPersistStream(iface);
508     return ShellLink_AddRef( This );
509 }
510
511 /************************************************************************
512  * IPersistStream_GetClassID
513  *
514  */
515 static HRESULT WINAPI IPersistStream_fnGetClassID(
516         IPersistStream* iface,
517         CLSID* pClassID)
518 {
519     IShellLinkImpl *This = impl_from_IPersistStream(iface);
520     return ShellLink_GetClassID( This, pClassID );
521 }
522
523 /************************************************************************
524  * IPersistStream_IsDirty (IPersistStream)
525  */
526 static HRESULT WINAPI IPersistStream_fnIsDirty(
527         IPersistStream*  iface)
528 {
529         IShellLinkImpl *This = impl_from_IPersistStream(iface);
530
531         TRACE("(%p)\n", This);
532
533         return S_OK;
534 }
535
536
537 static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
538 {
539     DWORD count;
540     USHORT len;
541     LPVOID temp;
542     LPWSTR str;
543     HRESULT r;
544
545     TRACE("%p\n", stm);
546
547     count = 0;
548     r = IStream_Read(stm, &len, sizeof(len), &count);
549     if ( FAILED (r) || ( count != sizeof(len) ) )
550         return E_FAIL;
551
552     if( unicode )
553         len *= sizeof (WCHAR);
554
555     TRACE("reading %d\n", len);
556     temp = HeapAlloc(GetProcessHeap(), 0, len+sizeof(WCHAR));
557     if( !temp )
558         return E_OUTOFMEMORY;
559     count = 0;
560     r = IStream_Read(stm, temp, len, &count);
561     if( FAILED (r) || ( count != len ) )
562     {
563         HeapFree( GetProcessHeap(), 0, temp );
564         return E_FAIL;
565     }
566
567     TRACE("read %s\n", debugstr_an(temp,len));
568
569     /* convert to unicode if necessary */
570     if( !unicode )
571     {
572         count = MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, NULL, 0 );
573         str = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof (WCHAR) );
574         if( str )
575             MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, str, count );
576         HeapFree( GetProcessHeap(), 0, temp );
577     }
578     else
579     {
580         count /= 2;
581         str = (LPWSTR) temp;
582     }
583     str[count] = 0;
584
585     *pstr = str;
586
587     return S_OK;
588 }
589
590 static HRESULT Stream_ReadChunk( IStream* stm, LPVOID *data )
591 {
592     DWORD size;
593     ULONG count;
594     HRESULT r;
595     struct sized_chunk {
596         DWORD size;
597         unsigned char data[1];
598     } *chunk;
599
600     TRACE("%p\n",stm);
601
602     r = IStream_Read( stm, &size, sizeof(size), &count );
603     if( FAILED( r )  || count != sizeof(size) )
604         return E_FAIL;
605
606     chunk = HeapAlloc( GetProcessHeap(), 0, size );
607     if( !chunk )
608         return E_OUTOFMEMORY;
609
610     chunk->size = size;
611     r = IStream_Read( stm, chunk->data, size - sizeof(size), &count );
612     if( FAILED( r ) || count != (size - sizeof(size)) )
613     {
614         HeapFree( GetProcessHeap(), 0, chunk );
615         return E_FAIL;
616     }
617
618     TRACE("Read %ld bytes\n",chunk->size);
619
620     *data = (LPVOID) chunk;
621
622     return S_OK;
623 }
624
625 static BOOL Stream_LoadVolume( LOCAL_VOLUME_INFO *vol, volume_info *volume )
626 {
627     const int label_sz = sizeof volume->label/sizeof volume->label[0];
628     LPSTR label;
629     int len;
630
631     volume->serial = vol->dwVolSerial;
632     volume->type = vol->dwType;
633
634     if( !vol->dwVolLabelOfs )
635         return FALSE;
636     if( vol->dwSize <= vol->dwVolLabelOfs )
637         return FALSE;
638     len = vol->dwSize - vol->dwVolLabelOfs;
639
640     label = (LPSTR) vol;
641     label += vol->dwVolLabelOfs;
642     MultiByteToWideChar( CP_ACP, 0, label, len, volume->label, label_sz-1);
643
644     return TRUE;
645 }
646
647 static LPWSTR Stream_LoadPath( LPSTR p, DWORD maxlen )
648 {
649     int len = 0, wlen;
650     LPWSTR path;
651
652     while( p[len] && (len < maxlen) )
653         len++;
654
655     wlen = MultiByteToWideChar(CP_ACP, 0, p, len, NULL, 0);
656     path = HeapAlloc(GetProcessHeap(), 0, (wlen+1)*sizeof(WCHAR));
657     MultiByteToWideChar(CP_ACP, 0, p, len, path, wlen);
658     path[wlen] = 0;
659
660     return path;
661 }
662
663 static HRESULT Stream_LoadLocation( IStream *stm,
664                 volume_info *volume, LPWSTR *path )
665 {
666     char *p = NULL;
667     LOCATION_INFO *loc;
668     HRESULT r;
669     int n;
670
671     r = Stream_ReadChunk( stm, (LPVOID*) &p );
672     if( FAILED(r) )
673         return r;
674
675     loc = (LOCATION_INFO*) p;
676     if (loc->dwTotalSize < sizeof(LOCATION_INFO))
677     {
678         HeapFree( GetProcessHeap(), 0, p );
679         return E_FAIL;
680     }
681
682     /* if there's valid local volume information, load it */
683     if( loc->dwVolTableOfs && 
684        ((loc->dwVolTableOfs + sizeof(LOCAL_VOLUME_INFO)) <= loc->dwTotalSize) )
685     {
686         LOCAL_VOLUME_INFO *volume_info;
687
688         volume_info = (LOCAL_VOLUME_INFO*) &p[loc->dwVolTableOfs];
689         Stream_LoadVolume( volume_info, volume );
690     }
691
692     /* if there's a local path, load it */
693     n = loc->dwLocalPathOfs;
694     if( n && (n < loc->dwTotalSize) )
695         *path = Stream_LoadPath( &p[n], loc->dwTotalSize - n );
696
697     TRACE("type %ld serial %08lx name %s path %s\n", volume->type,
698           volume->serial, debugstr_w(volume->label), debugstr_w(*path));
699
700     HeapFree( GetProcessHeap(), 0, p );
701     return S_OK;
702 }
703
704 /*
705  *  The format of the advertised shortcut info seems to be:
706  *
707  *  Offset     Description
708  *  ------     -----------
709  *
710  *    0          Length of the block (4 bytes, usually 0x314)
711  *    4          tag (dword)
712  *    8          string data in ASCII
713  *    8+0x104    string data in UNICODE
714  *
715  * In the original Win32 implementation the buffers are not initialized
716  *  to zero, so data trailing the string is random garbage.
717  */
718 static HRESULT Stream_LoadAdvertiseInfo( IStream* stm, LPWSTR *str )
719 {
720     DWORD size;
721     ULONG count;
722     HRESULT r;
723     EXP_DARWIN_LINK buffer;
724     
725     TRACE("%p\n",stm);
726
727     r = IStream_Read( stm, &buffer.dbh.cbSize, sizeof (DWORD), &count );
728     if( FAILED( r ) )
729         return r;
730
731     /* make sure that we read the size of the structure even on error */
732     size = sizeof buffer - sizeof (DWORD);
733     if( buffer.dbh.cbSize != sizeof buffer )
734     {
735         ERR("Ooops.  This structure is not as expected...\n");
736         return E_FAIL;
737     }
738
739     r = IStream_Read( stm, &buffer.dbh.dwSignature, size, &count );
740     if( FAILED( r ) )
741         return r;
742
743     if( count != size )
744         return E_FAIL;
745
746     TRACE("magic %08lx  string = %s\n", buffer.dbh.dwSignature, debugstr_w(buffer.szwDarwinID));
747
748     if( (buffer.dbh.dwSignature&0xffff0000) != 0xa0000000 )
749     {
750         ERR("Unknown magic number %08lx in advertised shortcut\n", buffer.dbh.dwSignature);
751         return E_FAIL;
752     }
753
754     *str = HeapAlloc( GetProcessHeap(), 0, 
755                      (lstrlenW(buffer.szwDarwinID)+1) * sizeof(WCHAR) );
756     lstrcpyW( *str, buffer.szwDarwinID );
757
758     return S_OK;
759 }
760
761 /************************************************************************
762  * IPersistStream_Load (IPersistStream)
763  */
764 static HRESULT WINAPI IPersistStream_fnLoad(
765     IPersistStream*  iface,
766     IStream*         stm)
767 {
768     LINK_HEADER hdr;
769     ULONG    dwBytesRead;
770     BOOL     unicode;
771     HRESULT  r;
772     DWORD    zero;
773
774     IShellLinkImpl *This = impl_from_IPersistStream(iface);
775
776     TRACE("%p %p\n", This, stm);
777
778     if( !stm )
779         return STG_E_INVALIDPOINTER;
780
781     dwBytesRead = 0;
782     r = IStream_Read(stm, &hdr, sizeof(hdr), &dwBytesRead);
783     if( FAILED( r ) )
784         return r;
785
786     if( dwBytesRead != sizeof(hdr))
787         return E_FAIL;
788     if( hdr.dwSize != sizeof(hdr))
789         return E_FAIL;
790     if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
791         return E_FAIL;
792
793     /* free all the old stuff */
794     ILFree(This->pPidl);
795     This->pPidl = NULL;
796     memset( &This->volume, 0, sizeof This->volume );
797     HeapFree(GetProcessHeap(), 0, This->sPath);
798     This->sPath = NULL;
799     HeapFree(GetProcessHeap(), 0, This->sDescription);
800     This->sDescription = NULL;
801     HeapFree(GetProcessHeap(), 0, This->sPathRel);
802     This->sPathRel = NULL;
803     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
804     This->sWorkDir = NULL;
805     HeapFree(GetProcessHeap(), 0, This->sArgs);
806     This->sArgs = NULL;
807     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
808     This->sIcoPath = NULL;
809     HeapFree(GetProcessHeap(), 0, This->sProduct);
810     This->sProduct = NULL;
811     HeapFree(GetProcessHeap(), 0, This->sComponent);
812     This->sComponent = NULL;
813         
814     This->wHotKey = (WORD)hdr.wHotKey;
815     This->iIcoNdx = hdr.nIcon;
816     FileTimeToSystemTime (&hdr.Time1, &This->time1);
817     FileTimeToSystemTime (&hdr.Time2, &This->time2);
818     FileTimeToSystemTime (&hdr.Time3, &This->time3);
819     if (TRACE_ON(shell))
820     {
821         WCHAR sTemp[MAX_PATH];
822         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time1,
823                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
824         TRACE("-- time1: %s\n", debugstr_w(sTemp) );
825         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time2,
826                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
827         TRACE("-- time2: %s\n", debugstr_w(sTemp) );
828         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time3,
829                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
830         TRACE("-- time3: %s\n", debugstr_w(sTemp) );
831     }
832
833     /* load all the new stuff */
834     if( hdr.dwFlags & SLDF_HAS_ID_LIST )
835     {
836         r = ILLoadFromStream( stm, &This->pPidl );
837         if( FAILED( r ) )
838             return r;
839     }
840     pdump(This->pPidl);
841
842     /* load the location information */
843     if( hdr.dwFlags & SLDF_HAS_LINK_INFO )
844         r = Stream_LoadLocation( stm, &This->volume, &This->sPath );
845     if( FAILED( r ) )
846         goto end;
847
848     unicode = hdr.dwFlags & SLDF_UNICODE;
849     if( hdr.dwFlags & SLDF_HAS_NAME )
850     {
851         r = Stream_LoadString( stm, unicode, &This->sDescription );
852         TRACE("Description  -> %s\n",debugstr_w(This->sDescription));
853     }
854     if( FAILED( r ) )
855         goto end;
856
857     if( hdr.dwFlags & SLDF_HAS_RELPATH )
858     {
859         r = Stream_LoadString( stm, unicode, &This->sPathRel );
860         TRACE("Relative Path-> %s\n",debugstr_w(This->sPathRel));
861     }
862     if( FAILED( r ) )
863         goto end;
864
865     if( hdr.dwFlags & SLDF_HAS_WORKINGDIR )
866     {
867         r = Stream_LoadString( stm, unicode, &This->sWorkDir );
868         TRACE("Working Dir  -> %s\n",debugstr_w(This->sWorkDir));
869     }
870     if( FAILED( r ) )
871         goto end;
872
873     if( hdr.dwFlags & SLDF_HAS_ARGS )
874     {
875         r = Stream_LoadString( stm, unicode, &This->sArgs );
876         TRACE("Working Dir  -> %s\n",debugstr_w(This->sArgs));
877     }
878     if( FAILED( r ) )
879         goto end;
880
881     if( hdr.dwFlags & SLDF_HAS_ICONLOCATION )
882     {
883         r = Stream_LoadString( stm, unicode, &This->sIcoPath );
884         TRACE("Icon file    -> %s\n",debugstr_w(This->sIcoPath));
885     }
886     if( FAILED( r ) )
887         goto end;
888
889     if( hdr.dwFlags & SLDF_HAS_LOGO3ID )
890     {
891         r = Stream_LoadAdvertiseInfo( stm, &This->sProduct );
892         TRACE("Product      -> %s\n",debugstr_w(This->sProduct));
893     }
894     if( FAILED( r ) )
895         goto end;
896
897     if( hdr.dwFlags & SLDF_HAS_DARWINID )
898     {
899         r = Stream_LoadAdvertiseInfo( stm, &This->sComponent );
900         TRACE("Component    -> %s\n",debugstr_w(This->sComponent));
901     }
902     if( FAILED( r ) )
903         goto end;
904
905     r = IStream_Read(stm, &zero, sizeof zero, &dwBytesRead);
906     if( FAILED( r ) || zero || dwBytesRead != sizeof zero )
907         ERR("Last word was not zero\n");
908
909     TRACE("OK\n");
910
911     pdump (This->pPidl);
912
913     return S_OK;
914 end:
915     return r;
916 }
917
918 /************************************************************************
919  * Stream_WriteString
920  *
921  * Helper function for IPersistStream_Save. Writes a unicode string 
922  *  with terminating nul byte to a stream, preceded by the its length.
923  */
924 static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
925 {
926     USHORT len = lstrlenW( str ) + 1;
927     DWORD count;
928     HRESULT r;
929
930     r = IStream_Write( stm, &len, sizeof(len), &count );
931     if( FAILED( r ) )
932         return r;
933
934     len *= sizeof(WCHAR);
935
936     r = IStream_Write( stm, str, len, &count );
937     if( FAILED( r ) )
938         return r;
939
940     return S_OK;
941 }
942
943 /************************************************************************
944  * Stream_WriteLocationInfo
945  *
946  * Writes the location info to a stream
947  *
948  * FIXME: One day we might want to write the network volume information
949  *        and the final path.
950  *        Figure out how Windows deals with unicode paths here.
951  */
952 static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR path,
953                                          volume_info *volume )
954 {
955     DWORD total_size, path_size, volume_info_size, label_size, final_path_size;
956     LOCAL_VOLUME_INFO *vol;
957     LOCATION_INFO *loc;
958     LPSTR szLabel, szPath, szFinalPath;
959     ULONG count = 0;
960
961     TRACE("%p %s %p\n", stm, debugstr_w(path), volume);
962
963     /* figure out the size of everything */
964     label_size = WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
965                                       NULL, 0, NULL, NULL );
966     path_size = WideCharToMultiByte( CP_ACP, 0, path, -1,
967                                      NULL, 0, NULL, NULL );
968     volume_info_size = sizeof *vol + label_size;
969     final_path_size = 1;
970     total_size = sizeof *loc + volume_info_size + path_size + final_path_size;
971
972     /* create pointers to everything */
973     loc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total_size);
974     vol = (LOCAL_VOLUME_INFO*) &loc[1];
975     szLabel = (LPSTR) &vol[1];
976     szPath = &szLabel[label_size];
977     szFinalPath = &szPath[path_size];
978
979     /* fill in the location information header */
980     loc->dwTotalSize = total_size;
981     loc->dwHeaderSize = sizeof (*loc);
982     loc->dwFlags = 1;
983     loc->dwVolTableOfs = sizeof (*loc);
984     loc->dwLocalPathOfs = sizeof (*loc) + volume_info_size;
985     loc->dwNetworkVolTableOfs = 0;
986     loc->dwFinalPathOfs = sizeof (*loc) + volume_info_size + path_size;
987
988     /* fill in the volume information */
989     vol->dwSize = volume_info_size;
990     vol->dwType = volume->type;
991     vol->dwVolSerial = volume->serial;
992     vol->dwVolLabelOfs = sizeof (*vol);
993
994     /* copy in the strings */
995     WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
996                          szLabel, label_size, NULL, NULL );
997     WideCharToMultiByte( CP_ACP, 0, path, -1,
998                          szPath, path_size, NULL, NULL );
999     szFinalPath[0] = 0;
1000
1001     return IStream_Write( stm, loc, total_size, &count );
1002 }
1003
1004 static HRESULT Stream_WriteAdvertiseInfo( IStream* stm, LPCWSTR string, DWORD magic )
1005 {
1006     ULONG count;
1007     EXP_DARWIN_LINK buffer;
1008     
1009     TRACE("%p\n",stm);
1010
1011     memset( &buffer, 0, sizeof buffer );
1012     buffer.dbh.cbSize = sizeof buffer;
1013     buffer.dbh.dwSignature = magic;
1014     lstrcpynW( buffer.szwDarwinID, string, MAX_PATH );
1015     WideCharToMultiByte(CP_ACP, 0, string, -1, buffer.szDarwinID, MAX_PATH, NULL, NULL );
1016
1017     return IStream_Write( stm, &buffer, buffer.dbh.cbSize, &count );
1018 }
1019
1020 /************************************************************************
1021  * IPersistStream_Save (IPersistStream)
1022  *
1023  * FIXME: makes assumptions about byte order
1024  */
1025 static HRESULT WINAPI IPersistStream_fnSave(
1026         IPersistStream*  iface,
1027         IStream*         stm,
1028         BOOL             fClearDirty)
1029 {
1030     static const WCHAR wOpen[] = {'o','p','e','n',0};
1031
1032     LINK_HEADER header;
1033     WCHAR   exePath[MAX_PATH];
1034     ULONG   count;
1035     DWORD   zero;
1036     HRESULT r;
1037
1038     IShellLinkImpl *This = impl_from_IPersistStream(iface);
1039
1040     TRACE("%p %p %x\n", This, stm, fClearDirty);
1041
1042     *exePath = '\0';
1043
1044     if (This->sPath)
1045     {
1046         SHELL_FindExecutable(NULL, This->sPath, wOpen, exePath, MAX_PATH,
1047                              NULL, NULL, NULL, NULL);
1048         /*
1049          * windows can create lnk files to executables that do not exist yet
1050          * so if the executable does not exist the just trust the path they
1051          * gave us
1052          */
1053         if (!*exePath) lstrcpyW(exePath,This->sPath);
1054     }
1055
1056     memset(&header, 0, sizeof(header));
1057     header.dwSize = sizeof(header);
1058     header.fStartup = This->iShowCmd;
1059     memcpy(&header.MagicGuid, &CLSID_ShellLink, sizeof(header.MagicGuid) );
1060
1061     header.wHotKey = This->wHotKey;
1062     header.nIcon = This->iIcoNdx;
1063     header.dwFlags = SLDF_UNICODE;   /* strings are in unicode */
1064     if( This->pPidl )
1065         header.dwFlags |= SLDF_HAS_ID_LIST;
1066     if( This->sPath )
1067         header.dwFlags |= SLDF_HAS_LINK_INFO;
1068     if( This->sDescription )
1069         header.dwFlags |= SLDF_HAS_NAME;
1070     if( This->sWorkDir )
1071         header.dwFlags |= SLDF_HAS_WORKINGDIR;
1072     if( This->sArgs )
1073         header.dwFlags |= SLDF_HAS_ARGS;
1074     if( This->sIcoPath )
1075         header.dwFlags |= SLDF_HAS_ICONLOCATION;
1076     if( This->sProduct )
1077         header.dwFlags |= SLDF_HAS_LOGO3ID;
1078     if( This->sComponent )
1079         header.dwFlags |= SLDF_HAS_DARWINID;
1080
1081     SystemTimeToFileTime ( &This->time1, &header.Time1 );
1082     SystemTimeToFileTime ( &This->time2, &header.Time2 );
1083     SystemTimeToFileTime ( &This->time3, &header.Time3 );
1084
1085     /* write the Shortcut header */
1086     r = IStream_Write( stm, &header, sizeof(header), &count );
1087     if( FAILED( r ) )
1088     {
1089         ERR("Write failed at %d\n",__LINE__);
1090         return r;
1091     }
1092
1093     TRACE("Writing pidl \n");
1094
1095     /* write the PIDL to the shortcut */
1096     if( This->pPidl )
1097     {
1098         r = ILSaveToStream( stm, This->pPidl );
1099         if( FAILED( r ) )
1100         {
1101             ERR("Failed to write PIDL at %d\n",__LINE__);
1102             return r;
1103         }
1104     }
1105
1106     if( This->sPath )
1107         Stream_WriteLocationInfo( stm, exePath, &This->volume );
1108
1109     if( This->sDescription )
1110         r = Stream_WriteString( stm, This->sDescription );
1111
1112     if( This->sPathRel )
1113         r = Stream_WriteString( stm, This->sPathRel );
1114
1115     if( This->sWorkDir )
1116         r = Stream_WriteString( stm, This->sWorkDir );
1117
1118     if( This->sArgs )
1119         r = Stream_WriteString( stm, This->sArgs );
1120
1121     if( This->sIcoPath )
1122         r = Stream_WriteString( stm, This->sIcoPath );
1123
1124     if( This->sProduct )
1125         r = Stream_WriteAdvertiseInfo( stm, This->sProduct, EXP_SZ_ICON_SIG );
1126
1127     if( This->sComponent )
1128         r = Stream_WriteAdvertiseInfo( stm, This->sComponent, EXP_DARWIN_ID_SIG );
1129
1130     /* the last field is a single zero dword */
1131     zero = 0;
1132     r = IStream_Write( stm, &zero, sizeof zero, &count );
1133
1134     return S_OK;
1135 }
1136
1137 /************************************************************************
1138  * IPersistStream_GetSizeMax (IPersistStream)
1139  */
1140 static HRESULT WINAPI IPersistStream_fnGetSizeMax(
1141         IPersistStream*  iface,
1142         ULARGE_INTEGER*  pcbSize)
1143 {
1144         IShellLinkImpl *This = impl_from_IPersistStream(iface);
1145
1146         TRACE("(%p)\n", This);
1147
1148         return E_NOTIMPL;
1149 }
1150
1151 static const IPersistStreamVtbl psvt =
1152 {
1153         IPersistStream_fnQueryInterface,
1154         IPersistStream_fnAddRef,
1155         IPersistStream_fnRelease,
1156         IPersistStream_fnGetClassID,
1157         IPersistStream_fnIsDirty,
1158         IPersistStream_fnLoad,
1159         IPersistStream_fnSave,
1160         IPersistStream_fnGetSizeMax
1161 };
1162
1163 /**************************************************************************
1164  *        IShellLink_Constructor
1165  */
1166 HRESULT WINAPI IShellLink_Constructor( IUnknown *pUnkOuter,
1167                REFIID riid, LPVOID *ppv )
1168 {
1169         IShellLinkImpl * sl;
1170         HRESULT r;
1171
1172         TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
1173
1174         *ppv = NULL;
1175
1176         if (pUnkOuter)
1177             return CLASS_E_NOAGGREGATION;
1178         sl = LocalAlloc(LMEM_ZEROINIT,sizeof(IShellLinkImpl));
1179         if (!sl)
1180             return E_OUTOFMEMORY;
1181
1182         sl->ref = 1;
1183         sl->lpVtbl = &slvt;
1184         sl->lpvtblw = &slvtw;
1185         sl->lpvtblPersistFile = &pfvt;
1186         sl->lpvtblPersistStream = &psvt;
1187         sl->lpvtblShellLinkDataList = &dlvt;
1188         sl->lpvtblShellExtInit = &eivt;
1189         sl->lpvtblContextMenu = &cmvt;
1190         sl->lpvtblObjectWithSite = &owsvt;
1191         sl->iShowCmd = SW_SHOWNORMAL;
1192         sl->bDirty = FALSE;
1193         sl->site = NULL;
1194
1195         TRACE("(%p)->()\n",sl);
1196
1197         r = ShellLink_QueryInterface( sl, riid, ppv );
1198         ShellLink_Release( sl );
1199         return r;
1200 }
1201
1202
1203 static BOOL SHELL_ExistsFileW(LPCWSTR path)
1204 {
1205     if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
1206         return FALSE;
1207     return TRUE;
1208 }
1209
1210 /**************************************************************************
1211  *  ShellLink_UpdatePath
1212  *      update absolute path in sPath using relative path in sPathRel
1213  */
1214 static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
1215 {
1216     if (!path || !psPath)
1217         return E_INVALIDARG;
1218
1219     if (!*psPath && sPathRel) {
1220         WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
1221         LPWSTR final = NULL;
1222
1223         /* first try if [directory of link file] + [relative path] finds an existing file */
1224
1225         GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
1226         if( !final )
1227             final = buffer;
1228         lstrcpyW(final, sPathRel);
1229
1230         *abs_path = '\0';
1231
1232         if (SHELL_ExistsFileW(buffer)) {
1233             if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
1234                 lstrcpyW(abs_path, buffer);
1235         } else {
1236             /* try if [working directory] + [relative path] finds an existing file */
1237             if (sWorkDir) {
1238                 lstrcpyW(buffer, sWorkDir);
1239                 lstrcpyW(PathAddBackslashW(buffer), sPathRel);
1240
1241                 if (SHELL_ExistsFileW(buffer))
1242                     if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
1243                         lstrcpyW(abs_path, buffer);
1244             }
1245         }
1246
1247         /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
1248         if (!*abs_path)
1249             lstrcpyW(abs_path, sPathRel);
1250
1251         *psPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(abs_path)+1)*sizeof(WCHAR));
1252         if (!*psPath)
1253             return E_OUTOFMEMORY;
1254
1255         lstrcpyW(*psPath, abs_path);
1256     }
1257
1258     return S_OK;
1259 }
1260
1261 /**************************************************************************
1262  *        IShellLink_ConstructFromFile
1263  */
1264 HRESULT WINAPI IShellLink_ConstructFromFile( IUnknown* pUnkOuter, REFIID riid,
1265                LPCITEMIDLIST pidl, LPVOID* ppv)
1266 {
1267     IShellLinkW* psl;
1268
1269     HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl);
1270
1271     if (SUCCEEDED(hr)) {
1272         IPersistFile* ppf;
1273
1274         *ppv = NULL;
1275
1276         hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
1277
1278         if (SUCCEEDED(hr)) {
1279             WCHAR path[MAX_PATH];
1280
1281             if (SHGetPathFromIDListW(pidl, path)) 
1282                 hr = IPersistFile_Load(ppf, path, 0);
1283             else
1284                 hr = E_FAIL;
1285
1286             if (SUCCEEDED(hr))
1287                 *ppv = (IUnknown*) psl;
1288
1289             IPersistFile_Release(ppf);
1290         }
1291
1292         if (!*ppv)
1293             IShellLinkW_Release(psl);
1294     }
1295
1296     return hr;
1297 }
1298
1299 /**************************************************************************
1300  *  IShellLinkA_QueryInterface
1301  */
1302 static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
1303 {
1304     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1305     return ShellLink_QueryInterface( This, riid, ppvObj );
1306 }
1307
1308 /******************************************************************************
1309  * IShellLinkA_AddRef
1310  */
1311 static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
1312 {
1313     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1314     return ShellLink_AddRef( This );
1315 }
1316
1317 /******************************************************************************
1318  *      IShellLinkA_Release
1319  */
1320 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
1321 {
1322     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1323     return ShellLink_Release( This );
1324 }
1325
1326 static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
1327                   INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1328 {
1329     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1330
1331     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
1332           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1333
1334     if (This->sComponent || This->sProduct)
1335         return S_FALSE;
1336
1337     if (cchMaxPath)
1338         pszFile[0] = 0;
1339     if (This->sPath)
1340         WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
1341                              pszFile, cchMaxPath, NULL, NULL);
1342
1343     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1344
1345     return S_OK;
1346 }
1347
1348 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
1349 {
1350     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1351
1352     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1353
1354     return IShellLinkW_GetIDList((IShellLinkW*)&(This->lpvtblw), ppidl);
1355 }
1356
1357 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
1358 {
1359     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1360
1361     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1362
1363     if (This->pPidl)
1364         ILFree(This->pPidl);
1365     This->pPidl = ILClone (pidl);
1366     This->bDirty = TRUE;
1367
1368     return S_OK;
1369 }
1370
1371 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
1372 {
1373     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1374
1375     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1376
1377     if( cchMaxName )
1378         pszName[0] = 0;
1379     if( This->sDescription )
1380         WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
1381             pszName, cchMaxName, NULL, NULL);
1382
1383     return S_OK;
1384 }
1385
1386 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
1387 {
1388     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1389
1390     TRACE("(%p)->(pName=%s)\n", This, pszName);
1391
1392     HeapFree(GetProcessHeap(), 0, This->sDescription);
1393     This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
1394     if ( !This->sDescription )
1395         return E_OUTOFMEMORY;
1396
1397     This->bDirty = TRUE;
1398
1399     return S_OK;
1400 }
1401
1402 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
1403 {
1404     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1405
1406     TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1407
1408     if( cchMaxPath )
1409         pszDir[0] = 0;
1410     if( This->sWorkDir )
1411         WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
1412                              pszDir, cchMaxPath, NULL, NULL);
1413
1414     return S_OK;
1415 }
1416
1417 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
1418 {
1419     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1420
1421     TRACE("(%p)->(dir=%s)\n",This, pszDir);
1422
1423     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1424     This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
1425     if ( !This->sWorkDir )
1426         return E_OUTOFMEMORY;
1427
1428     This->bDirty = TRUE;
1429
1430     return S_OK;
1431 }
1432
1433 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
1434 {
1435     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1436
1437     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1438
1439     if( cchMaxPath )
1440         pszArgs[0] = 0;
1441     if( This->sArgs )
1442         WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
1443                              pszArgs, cchMaxPath, NULL, NULL);
1444
1445     return S_OK;
1446 }
1447
1448 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
1449 {
1450     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1451
1452     TRACE("(%p)->(args=%s)\n",This, pszArgs);
1453
1454     HeapFree(GetProcessHeap(), 0, This->sArgs);
1455     This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
1456     if( !This->sArgs )
1457         return E_OUTOFMEMORY;
1458
1459     This->bDirty = TRUE;
1460
1461     return S_OK;
1462 }
1463
1464 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
1465 {
1466     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1467
1468     TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1469
1470     *pwHotkey = This->wHotKey;
1471
1472     return S_OK;
1473 }
1474
1475 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
1476 {
1477     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1478
1479     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1480
1481     This->wHotKey = wHotkey;
1482     This->bDirty = TRUE;
1483
1484     return S_OK;
1485 }
1486
1487 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
1488 {
1489     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1490
1491     TRACE("(%p)->(%p)\n",This, piShowCmd);
1492     *piShowCmd = This->iShowCmd;
1493     return S_OK;
1494 }
1495
1496 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
1497 {
1498     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1499
1500     TRACE("(%p) %d\n",This, iShowCmd);
1501
1502     This->iShowCmd = iShowCmd;
1503     This->bDirty = TRUE;
1504
1505     return NOERROR;
1506 }
1507
1508 static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPITEMIDLIST pidl, LPSTR pszIconPath, int cchIconPath, int* piIcon)
1509 {
1510     LPCITEMIDLIST pidlLast;
1511
1512     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1513
1514     if (SUCCEEDED(hr)) {
1515         IExtractIconA* pei;
1516
1517         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconA, NULL, (LPVOID*)&pei);
1518
1519         if (SUCCEEDED(hr)) {
1520             hr = IExtractIconA_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1521
1522             IExtractIconA_Release(pei);
1523         }
1524
1525         IShellFolder_Release(psf);
1526     }
1527
1528     return hr;
1529 }
1530
1531 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1532 {
1533     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1534
1535     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1536
1537     pszIconPath[0] = 0;
1538     *piIcon = This->iIcoNdx;
1539
1540     if (This->sIcoPath)
1541     {
1542         WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1543         return S_OK;
1544     }
1545
1546     if (This->pPidl || This->sPath)
1547     {
1548         IShellFolder* pdsk;
1549
1550         HRESULT hr = SHGetDesktopFolder(&pdsk);
1551
1552         if (SUCCEEDED(hr))
1553         {
1554             /* first look for an icon using the PIDL (if present) */
1555             if (This->pPidl)
1556                 hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1557             else
1558                 hr = E_FAIL;
1559
1560             /* if we couldn't find an icon yet, look for it using the file system path */
1561             if (FAILED(hr) && This->sPath)
1562             {
1563                 LPITEMIDLIST pidl;
1564
1565                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1566
1567                 if (SUCCEEDED(hr)) {
1568                     hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1569
1570                     SHFree(pidl);
1571                 }
1572             }
1573
1574             IShellFolder_Release(pdsk);
1575         }
1576
1577         return hr;
1578     }
1579     return S_OK;
1580 }
1581
1582 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
1583 {
1584     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1585
1586     TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1587
1588     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1589     This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1590     if ( !This->sIcoPath )
1591         return E_OUTOFMEMORY;
1592
1593     This->iIcoNdx = iIcon;
1594     This->bDirty = TRUE;
1595
1596     return S_OK;
1597 }
1598
1599 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1600 {
1601     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1602
1603     TRACE("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved);
1604
1605     HeapFree(GetProcessHeap(), 0, This->sPathRel);
1606     This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1607     This->bDirty = TRUE;
1608
1609     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1610 }
1611
1612 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1613 {
1614     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1615
1616     TRACE("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1617
1618     return IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, fFlags );
1619 }
1620
1621 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1622 {
1623     HRESULT r;
1624     LPWSTR str;
1625     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1626
1627     TRACE("(%p)->(path=%s)\n",This, pszFile);
1628
1629     str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
1630     if( !str ) 
1631         return E_OUTOFMEMORY;
1632
1633     r = IShellLinkW_SetPath((IShellLinkW*)&(This->lpvtblw), str);
1634     HeapFree( GetProcessHeap(), 0, str );
1635
1636     return r;
1637 }
1638
1639 /**************************************************************************
1640 * IShellLink Implementation
1641 */
1642
1643 static const IShellLinkAVtbl slvt =
1644 {
1645     IShellLinkA_fnQueryInterface,
1646     IShellLinkA_fnAddRef,
1647     IShellLinkA_fnRelease,
1648     IShellLinkA_fnGetPath,
1649     IShellLinkA_fnGetIDList,
1650     IShellLinkA_fnSetIDList,
1651     IShellLinkA_fnGetDescription,
1652     IShellLinkA_fnSetDescription,
1653     IShellLinkA_fnGetWorkingDirectory,
1654     IShellLinkA_fnSetWorkingDirectory,
1655     IShellLinkA_fnGetArguments,
1656     IShellLinkA_fnSetArguments,
1657     IShellLinkA_fnGetHotkey,
1658     IShellLinkA_fnSetHotkey,
1659     IShellLinkA_fnGetShowCmd,
1660     IShellLinkA_fnSetShowCmd,
1661     IShellLinkA_fnGetIconLocation,
1662     IShellLinkA_fnSetIconLocation,
1663     IShellLinkA_fnSetRelativePath,
1664     IShellLinkA_fnResolve,
1665     IShellLinkA_fnSetPath
1666 };
1667
1668
1669 /**************************************************************************
1670  *  IShellLinkW_fnQueryInterface
1671  */
1672 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1673   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1674 {
1675     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1676     return ShellLink_QueryInterface( This, riid, ppvObj );
1677 }
1678
1679 /******************************************************************************
1680  * IShellLinkW_fnAddRef
1681  */
1682 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1683 {
1684     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1685     return ShellLink_AddRef( This );
1686 }
1687
1688 /******************************************************************************
1689  * IShellLinkW_fnRelease
1690  */
1691 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1692 {
1693     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1694     return ShellLink_Release( This );
1695 }
1696
1697 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1698 {
1699     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1700
1701     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
1702           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1703
1704     if (This->sComponent || This->sProduct)
1705         return S_FALSE;
1706
1707     if (cchMaxPath)
1708         pszFile[0] = 0;
1709     if (This->sPath)
1710         lstrcpynW( pszFile, This->sPath, cchMaxPath );
1711
1712     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1713
1714     return S_OK;
1715 }
1716
1717 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1718 {
1719     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1720
1721     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1722
1723     if (!This->pPidl)
1724         return S_FALSE;
1725     *ppidl = ILClone(This->pPidl);
1726     return S_OK;
1727 }
1728
1729 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1730 {
1731     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1732
1733     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1734
1735     if( This->pPidl )
1736         ILFree( This->pPidl );
1737     This->pPidl = ILClone( pidl );
1738     if( !This->pPidl )
1739         return E_FAIL;
1740
1741     This->bDirty = TRUE;
1742
1743     return S_OK;
1744 }
1745
1746 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1747 {
1748     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1749
1750     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1751
1752     pszName[0] = 0;
1753     if( This->sDescription )
1754         lstrcpynW( pszName, This->sDescription, cchMaxName );
1755
1756     return S_OK;
1757 }
1758
1759 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1760 {
1761     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1762
1763     TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1764
1765     HeapFree(GetProcessHeap(), 0, This->sDescription);
1766     This->sDescription = HeapAlloc( GetProcessHeap(), 0,
1767                                     (lstrlenW( pszName )+1)*sizeof(WCHAR) );
1768     if ( !This->sDescription )
1769         return E_OUTOFMEMORY;
1770
1771     lstrcpyW( This->sDescription, pszName );
1772     This->bDirty = TRUE;
1773
1774     return S_OK;
1775 }
1776
1777 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1778 {
1779     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1780
1781     TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1782
1783     if( cchMaxPath )
1784         pszDir[0] = 0;
1785     if( This->sWorkDir )
1786         lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
1787
1788     return S_OK;
1789 }
1790
1791 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1792 {
1793     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1794
1795     TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1796
1797     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1798     This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
1799                                 (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
1800     if ( !This->sWorkDir )
1801         return E_OUTOFMEMORY;
1802     lstrcpyW( This->sWorkDir, pszDir );
1803     This->bDirty = TRUE;
1804
1805     return S_OK;
1806 }
1807
1808 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1809 {
1810     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1811
1812     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1813
1814     if( cchMaxPath )
1815         pszArgs[0] = 0;
1816     if( This->sArgs )
1817         lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
1818
1819     return NOERROR;
1820 }
1821
1822 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1823 {
1824     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1825
1826     TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1827
1828     HeapFree(GetProcessHeap(), 0, This->sArgs);
1829     This->sArgs = HeapAlloc( GetProcessHeap(), 0,
1830                              (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
1831     if ( !This->sArgs )
1832         return E_OUTOFMEMORY;
1833     lstrcpyW( This->sArgs, pszArgs );
1834     This->bDirty = TRUE;
1835
1836     return S_OK;
1837 }
1838
1839 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1840 {
1841     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1842
1843     TRACE("(%p)->(%p)\n",This, pwHotkey);
1844
1845     *pwHotkey=This->wHotKey;
1846
1847     return S_OK;
1848 }
1849
1850 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1851 {
1852     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1853
1854     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1855
1856     This->wHotKey = wHotkey;
1857     This->bDirty = TRUE;
1858
1859     return S_OK;
1860 }
1861
1862 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1863 {
1864     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1865
1866     TRACE("(%p)->(%p)\n",This, piShowCmd);
1867
1868     *piShowCmd = This->iShowCmd;
1869
1870     return S_OK;
1871 }
1872
1873 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1874 {
1875     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1876
1877     This->iShowCmd = iShowCmd;
1878     This->bDirty = TRUE;
1879
1880     return S_OK;
1881 }
1882
1883 static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPITEMIDLIST pidl, LPWSTR pszIconPath, int cchIconPath, int* piIcon)
1884 {
1885     LPCITEMIDLIST pidlLast;
1886
1887     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1888
1889     if (SUCCEEDED(hr)) {
1890         IExtractIconW* pei;
1891
1892         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconW, NULL, (LPVOID*)&pei);
1893
1894         if (SUCCEEDED(hr)) {
1895             hr = IExtractIconW_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1896
1897             IExtractIconW_Release(pei);
1898         }
1899
1900         IShellFolder_Release(psf);
1901     }
1902
1903     return hr;
1904 }
1905
1906 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1907 {
1908     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1909
1910     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1911
1912     pszIconPath[0] = 0;
1913     *piIcon = This->iIcoNdx;
1914
1915     if (This->sIcoPath)
1916     {
1917         lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
1918         return S_OK;
1919     }
1920
1921     if (This->pPidl || This->sPath)
1922     {
1923         IShellFolder* pdsk;
1924
1925         HRESULT hr = SHGetDesktopFolder(&pdsk);
1926
1927         if (SUCCEEDED(hr))
1928         {
1929             /* first look for an icon using the PIDL (if present) */
1930             if (This->pPidl)
1931                 hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1932             else
1933                 hr = E_FAIL;
1934
1935             /* if we couldn't find an icon yet, look for it using the file system path */
1936             if (FAILED(hr) && This->sPath)
1937             {
1938                 LPITEMIDLIST pidl;
1939
1940                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1941
1942                 if (SUCCEEDED(hr))
1943                 {
1944                     hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1945
1946                     SHFree(pidl);
1947                 }
1948             }
1949
1950             IShellFolder_Release(pdsk);
1951         }
1952         return hr;
1953     }
1954     return S_OK;
1955 }
1956
1957 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
1958 {
1959     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1960
1961     TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1962
1963     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1964     This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
1965                                 (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
1966     if ( !This->sIcoPath )
1967         return E_OUTOFMEMORY;
1968     lstrcpyW( This->sIcoPath, pszIconPath );
1969
1970     This->iIcoNdx = iIcon;
1971     This->bDirty = TRUE;
1972
1973     return S_OK;
1974 }
1975
1976 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
1977 {
1978     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1979
1980     TRACE("(%p)->(path=%s %lx)\n",This, debugstr_w(pszPathRel), dwReserved);
1981
1982     HeapFree(GetProcessHeap(), 0, This->sPathRel);
1983     This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
1984                                 (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
1985     if ( !This->sPathRel )
1986         return E_OUTOFMEMORY;
1987     lstrcpyW( This->sPathRel, pszPathRel );
1988     This->bDirty = TRUE;
1989
1990     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1991 }
1992
1993 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
1994 {
1995     HRESULT hr = S_OK;
1996     BOOL bSuccess;
1997
1998     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
1999
2000     TRACE("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
2001
2002     /*FIXME: use IResolveShellLink interface */
2003
2004     if (!This->sPath && This->pPidl) {
2005         WCHAR buffer[MAX_PATH];
2006
2007         bSuccess = SHGetPathFromIDListW(This->pPidl, buffer);
2008
2009         if (bSuccess && *buffer) {
2010             This->sPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
2011             if (!This->sPath)
2012                 return E_OUTOFMEMORY;
2013
2014             lstrcpyW(This->sPath, buffer);
2015
2016             This->bDirty = TRUE;
2017         } else
2018             hr = S_OK;    /* don't report an error occurred while just caching information */
2019     }
2020
2021     if (!This->sIcoPath && This->sPath) {
2022         This->sIcoPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
2023         if (!This->sIcoPath)
2024             return E_OUTOFMEMORY;
2025
2026         lstrcpyW(This->sIcoPath, This->sPath);
2027         This->iIcoNdx = 0;
2028
2029         This->bDirty = TRUE;
2030     }
2031
2032     return hr;
2033 }
2034
2035 static LPWSTR ShellLink_GetAdvertisedArg(LPCWSTR str)
2036 {
2037     LPWSTR ret;
2038     LPCWSTR p;
2039     DWORD len;
2040
2041     if( !str )
2042         return NULL;
2043
2044     p = strchrW( str, ':' );
2045     if( !p )
2046         return NULL;
2047     len = p - str;
2048     ret = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
2049     if( !ret )
2050         return ret;
2051     memcpy( ret, str, sizeof(WCHAR)*len );
2052     ret[len] = 0;
2053     return ret;
2054 }
2055
2056 static HRESULT ShellLink_SetAdvertiseInfo(IShellLinkImpl *This, LPCWSTR str)
2057 {
2058     LPCWSTR szComponent = NULL, szProduct = NULL, p;
2059     WCHAR szGuid[39];
2060     HRESULT r;
2061     GUID guid;
2062     int len;
2063
2064     while( str[0] )
2065     {
2066         /* each segment must start with two colons */
2067         if( str[0] != ':' || str[1] != ':' )
2068             return E_FAIL;
2069
2070         /* the last segment is just two colons */
2071         if( !str[2] )
2072             break;
2073         str += 2;
2074
2075         /* there must be a colon straight after a guid */
2076         p = strchrW( str, ':' );
2077         if( !p )
2078             return E_FAIL;
2079         len = p - str;
2080         if( len != 38 )
2081             return E_FAIL;
2082
2083         /* get the guid, and check it's validly formatted */
2084         memcpy( szGuid, str, sizeof(WCHAR)*len );
2085         szGuid[len] = 0;
2086         r = CLSIDFromString( szGuid, &guid );
2087         if( r != S_OK )
2088             return r;
2089         str = p + 1;
2090
2091         /* match it up to a guid that we care about */
2092         if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutComponent ) && !szComponent )
2093             szComponent = str;
2094         else if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutProduct ) && !szProduct )
2095             szProduct = str;
2096         else
2097             return E_FAIL;
2098
2099         /* skip to the next field */
2100         str = strchrW( str, ':' );
2101         if( !str )
2102             return E_FAIL;
2103     }
2104
2105     /* we have to have a component for an advertised shortcut */
2106     if( !szComponent )
2107         return E_FAIL;
2108
2109     This->sComponent = ShellLink_GetAdvertisedArg( szComponent );
2110     This->sProduct = ShellLink_GetAdvertisedArg( szProduct );
2111
2112     TRACE("Component = %s\n", debugstr_w(This->sComponent));
2113     TRACE("Product = %s\n", debugstr_w(This->sProduct));
2114
2115     return S_OK;
2116 }
2117
2118 static BOOL ShellLink_GetVolumeInfo(LPWSTR path, volume_info *volume)
2119 {
2120     const int label_sz = sizeof volume->label/sizeof volume->label[0];
2121     WCHAR drive[4] = { path[0], ':', '\\', 0 };
2122     BOOL r;
2123
2124     volume->type = GetDriveTypeW(drive);
2125     r = GetVolumeInformationW(drive, volume->label, label_sz,
2126                               &volume->serial, NULL, NULL, NULL, 0);
2127     TRACE("r = %d type %ld serial %08lx name %s\n", r,
2128           volume->type, volume->serial, debugstr_w(volume->label));
2129     return r;
2130 }
2131
2132 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
2133 {
2134     IShellLinkImpl *This = impl_from_IShellLinkW(iface);
2135     WCHAR buffer[MAX_PATH];
2136     LPWSTR fname;
2137     HRESULT hr = S_OK;
2138
2139     TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
2140
2141     HeapFree(GetProcessHeap(), 0, This->sPath);
2142     This->sPath = NULL;
2143
2144     HeapFree(GetProcessHeap(), 0, This->sComponent);
2145     This->sComponent = NULL;
2146
2147     if (This->pPidl)
2148         ILFree(This->pPidl);
2149     This->pPidl = NULL;
2150
2151     if (S_OK != ShellLink_SetAdvertiseInfo( This, pszFile ))
2152     {
2153         if (*pszFile == '\0')
2154             *buffer = '\0';
2155         else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
2156             return E_FAIL;
2157         else if(!PathFileExistsW(buffer))
2158             hr = S_FALSE;
2159
2160         This->pPidl = SHSimpleIDListFromPathW(pszFile);
2161         ShellLink_GetVolumeInfo(buffer, &This->volume);
2162
2163         This->sPath = HeapAlloc( GetProcessHeap(), 0,
2164                              (lstrlenW( buffer )+1) * sizeof (WCHAR) );
2165         if (!This->sPath)
2166             return E_OUTOFMEMORY;
2167
2168         lstrcpyW(This->sPath, buffer);
2169     }
2170     This->bDirty = TRUE;
2171
2172     return hr;
2173 }
2174
2175 /**************************************************************************
2176 * IShellLinkW Implementation
2177 */
2178
2179 static const IShellLinkWVtbl slvtw =
2180 {
2181     IShellLinkW_fnQueryInterface,
2182     IShellLinkW_fnAddRef,
2183     IShellLinkW_fnRelease,
2184     IShellLinkW_fnGetPath,
2185     IShellLinkW_fnGetIDList,
2186     IShellLinkW_fnSetIDList,
2187     IShellLinkW_fnGetDescription,
2188     IShellLinkW_fnSetDescription,
2189     IShellLinkW_fnGetWorkingDirectory,
2190     IShellLinkW_fnSetWorkingDirectory,
2191     IShellLinkW_fnGetArguments,
2192     IShellLinkW_fnSetArguments,
2193     IShellLinkW_fnGetHotkey,
2194     IShellLinkW_fnSetHotkey,
2195     IShellLinkW_fnGetShowCmd,
2196     IShellLinkW_fnSetShowCmd,
2197     IShellLinkW_fnGetIconLocation,
2198     IShellLinkW_fnSetIconLocation,
2199     IShellLinkW_fnSetRelativePath,
2200     IShellLinkW_fnResolve,
2201     IShellLinkW_fnSetPath
2202 };
2203
2204 static HRESULT WINAPI
2205 ShellLink_DataList_QueryInterface( IShellLinkDataList* iface, REFIID riid, void** ppvObject)
2206 {
2207     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2208     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2209 }
2210
2211 static ULONG WINAPI
2212 ShellLink_DataList_AddRef( IShellLinkDataList* iface )
2213 {
2214     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2215     return IShellLinkA_AddRef((IShellLinkA*)This);
2216 }
2217
2218 static ULONG WINAPI
2219 ShellLink_DataList_Release( IShellLinkDataList* iface )
2220 {
2221     IShellLinkImpl *This = impl_from_IShellLinkDataList(iface);
2222     return ShellLink_Release( This );
2223 }
2224
2225 static HRESULT WINAPI
2226 ShellLink_AddDataBlock( IShellLinkDataList* iface, void* pDataBlock )
2227 {
2228     FIXME("\n");
2229     return E_NOTIMPL;
2230 }
2231
2232 static HRESULT WINAPI
2233 ShellLink_CopyDataBlock( IShellLinkDataList* iface, DWORD dwSig, void** ppDataBlock )
2234 {
2235     FIXME("\n");
2236     return E_NOTIMPL;
2237 }
2238
2239 static HRESULT WINAPI
2240 ShellLink_RemoveDataBlock( IShellLinkDataList* iface, DWORD dwSig )
2241 {
2242     FIXME("\n");
2243     return E_NOTIMPL;
2244 }
2245
2246 static HRESULT WINAPI
2247 ShellLink_GetFlags( IShellLinkDataList* iface, DWORD* pdwFlags )
2248 {
2249     FIXME("\n");
2250     return E_NOTIMPL;
2251 }
2252
2253 static HRESULT WINAPI
2254 ShellLink_SetFlags( IShellLinkDataList* iface, DWORD dwFlags )
2255 {
2256     FIXME("\n");
2257     return E_NOTIMPL;
2258 }
2259
2260 static const IShellLinkDataListVtbl dlvt =
2261 {
2262     ShellLink_DataList_QueryInterface,
2263     ShellLink_DataList_AddRef,
2264     ShellLink_DataList_Release,
2265     ShellLink_AddDataBlock,
2266     ShellLink_CopyDataBlock,
2267     ShellLink_RemoveDataBlock,
2268     ShellLink_GetFlags,
2269     ShellLink_SetFlags
2270 };
2271
2272 static HRESULT WINAPI
2273 ShellLink_ExtInit_QueryInterface( IShellExtInit* iface, REFIID riid, void** ppvObject )
2274 {
2275     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2276     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2277 }
2278
2279 static ULONG WINAPI
2280 ShellLink_ExtInit_AddRef( IShellExtInit* iface )
2281 {
2282     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2283     return IShellLinkA_AddRef((IShellLinkA*)This);
2284 }
2285
2286 static ULONG WINAPI
2287 ShellLink_ExtInit_Release( IShellExtInit* iface )
2288 {
2289     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2290     return ShellLink_Release( This );
2291 }
2292
2293 /**************************************************************************
2294  * ShellLink implementation of IShellExtInit::Initialize()
2295  *
2296  * Loads the shelllink from the dataobject the shell is pointing to.
2297  */
2298 static HRESULT WINAPI
2299 ShellLink_ExtInit_Initialize( IShellExtInit* iface, LPCITEMIDLIST pidlFolder,
2300                               IDataObject *pdtobj, HKEY hkeyProgID )
2301 {
2302     IShellLinkImpl *This = impl_from_IShellExtInit(iface);
2303     FORMATETC format;
2304     STGMEDIUM stgm;
2305     UINT count;
2306     HRESULT r = E_FAIL;
2307
2308     TRACE("%p %p %p %p\n", This, pidlFolder, pdtobj, hkeyProgID );
2309
2310     if( !pdtobj )
2311         return r;
2312
2313     format.cfFormat = CF_HDROP;
2314     format.ptd = NULL;
2315     format.dwAspect = DVASPECT_CONTENT;
2316     format.lindex = -1;
2317     format.tymed = TYMED_HGLOBAL;
2318
2319     if( FAILED( IDataObject_GetData( pdtobj, &format, &stgm ) ) )
2320         return r;
2321
2322     count = DragQueryFileW( stgm.u.hGlobal, -1, NULL, 0 );
2323     if( count == 1 )
2324     {
2325         LPWSTR path;
2326
2327         count = DragQueryFileW( stgm.u.hGlobal, 0, NULL, 0 );
2328         count++;
2329         path = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
2330         if( path )
2331         {
2332             IPersistFile *pf = (IPersistFile*) &This->lpvtblPersistFile;
2333
2334             count = DragQueryFileW( stgm.u.hGlobal, 0, path, count );
2335             r = IPersistFile_Load( pf, path, 0 );
2336             HeapFree( GetProcessHeap(), 0, path );
2337         }
2338     }
2339     ReleaseStgMedium( &stgm );
2340
2341     return r;
2342 }
2343
2344 static const IShellExtInitVtbl eivt =
2345 {
2346     ShellLink_ExtInit_QueryInterface,
2347     ShellLink_ExtInit_AddRef,
2348     ShellLink_ExtInit_Release,
2349     ShellLink_ExtInit_Initialize
2350 };
2351
2352 static HRESULT WINAPI
2353 ShellLink_ContextMenu_QueryInterface( IContextMenu* iface, REFIID riid, void** ppvObject )
2354 {
2355     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2356     return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObject);
2357 }
2358
2359 static ULONG WINAPI
2360 ShellLink_ContextMenu_AddRef( IContextMenu* iface )
2361 {
2362     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2363     return IShellLinkA_AddRef((IShellLinkA*)This);
2364 }
2365
2366 static ULONG WINAPI
2367 ShellLink_ContextMenu_Release( IContextMenu* iface )
2368 {
2369     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2370     return ShellLink_Release( This );
2371 }
2372
2373 static HRESULT WINAPI
2374 ShellLink_QueryContextMenu( IContextMenu* iface, HMENU hmenu, UINT indexMenu,
2375                             UINT idCmdFirst, UINT idCmdLast, UINT uFlags )
2376 {
2377     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2378
2379     FIXME("%p %p %u %u %u %u\n", This,
2380           hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
2381
2382     return E_NOTIMPL;
2383 }
2384
2385 static HRESULT WINAPI
2386 ShellLink_InvokeCommand( IContextMenu* iface, LPCMINVOKECOMMANDINFO lpici )
2387 {
2388     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2389
2390     FIXME("%p %p\n", This, lpici );
2391
2392     return E_NOTIMPL;
2393 }
2394
2395 static HRESULT WINAPI
2396 ShellLink_GetCommandString( IContextMenu* iface, UINT_PTR idCmd, UINT uType,
2397                             UINT* pwReserved, LPSTR pszName, UINT cchMax )
2398 {
2399     IShellLinkImpl *This = impl_from_IContextMenu(iface);
2400
2401     FIXME("%p %u %u %p %p %u\n", This,
2402           idCmd, uType, pwReserved, pszName, cchMax );
2403
2404     return E_NOTIMPL;
2405 }
2406
2407 static const IContextMenuVtbl cmvt =
2408 {
2409     ShellLink_ContextMenu_QueryInterface,
2410     ShellLink_ContextMenu_AddRef,
2411     ShellLink_ContextMenu_Release,
2412     ShellLink_QueryContextMenu,
2413     ShellLink_InvokeCommand,
2414     ShellLink_GetCommandString
2415 };
2416
2417 static HRESULT WINAPI
2418 ShellLink_ObjectWithSite_QueryInterface( IObjectWithSite* iface, REFIID riid, void** ppvObject )
2419 {
2420     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2421     return ShellLink_QueryInterface( This, riid, ppvObject );
2422 }
2423
2424 static ULONG WINAPI
2425 ShellLink_ObjectWithSite_AddRef( IObjectWithSite* iface )
2426 {
2427     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2428     return ShellLink_AddRef( This );
2429 }
2430
2431 static ULONG WINAPI
2432 ShellLink_ObjectWithSite_Release( IObjectWithSite* iface )
2433 {
2434     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2435     return ShellLink_Release( This );
2436 }
2437
2438 static HRESULT WINAPI
2439 ShellLink_GetSite( IObjectWithSite *iface, REFIID iid, void ** ppvSite )
2440 {
2441     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2442
2443     TRACE("%p %s %p\n", This, debugstr_guid( iid ), ppvSite );
2444
2445     if ( !This->site )
2446         return E_FAIL;
2447     return IUnknown_QueryInterface( This->site, iid, ppvSite );
2448 }
2449
2450 static HRESULT WINAPI
2451 ShellLink_SetSite( IObjectWithSite *iface, IUnknown *punk )
2452 {
2453     IShellLinkImpl *This = impl_from_IObjectWithSite(iface);
2454
2455     TRACE("%p %p\n", iface, punk);
2456
2457     if ( punk )
2458         IUnknown_AddRef( punk );
2459     This->site = punk;
2460
2461     return S_OK;
2462 }
2463
2464 static const IObjectWithSiteVtbl owsvt =
2465 {
2466     ShellLink_ObjectWithSite_QueryInterface,
2467     ShellLink_ObjectWithSite_AddRef,
2468     ShellLink_ObjectWithSite_Release,
2469     ShellLink_SetSite,
2470     ShellLink_GetSite,
2471 };