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