Fix bug found by Piotr Caban, where our function tried to delete a
[wine] / dlls / shell32 / shelllink.c
1 /*
2  *
3  *      Copyright 1997  Marcus Meissner
4  *      Copyright 1998  Juergen Schmied
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * NOTES
21  *   Nearly complete informations about the binary formats 
22  *   of .lnk files available at http://www.wotsit.org
23  *
24  */
25
26 #include "config.h"
27 #include "wine/port.h"
28
29 #include <ctype.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36 #include <errno.h>
37 #include <limits.h>
38 #ifdef HAVE_SYS_WAIT_H
39 # include <sys/wait.h>
40 #endif
41 #include "wine/debug.h"
42 #include "winerror.h"
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winreg.h"
47
48 #include "winuser.h"
49 #include "wingdi.h"
50 #include "shlobj.h"
51 #include "undocshell.h"
52
53 #include "pidl.h"
54 #include "shell32_main.h"
55 #include "shlguid.h"
56 #include "shlwapi.h"
57
58 WINE_DEFAULT_DEBUG_CHANNEL(shell);
59
60 /* link file formats */
61
62 /* flag1: lnk elements: simple link has 0x0B */
63 #define SCF_PIDL   1
64 #define SCF_NORMAL 2
65 #define SCF_DESCRIPTION 4
66 #define SCF_RELATIVE 8
67 #define SCF_WORKDIR 0x10
68 #define SCF_ARGS 0x20
69 #define SCF_CUSTOMICON 0x40
70 #define SCF_UNICODE 0x80
71
72 #include "pshpack1.h"
73
74 typedef struct _LINK_HEADER
75 {
76         DWORD    dwSize;        /* 0x00 size of the header - 0x4c */
77         GUID     MagicGuid;     /* 0x04 is CLSID_ShellLink */
78         DWORD    dwFlags;       /* 0x14 describes elements following */
79         DWORD    dwFileAttr;    /* 0x18 attributes of the target file */
80         FILETIME Time1;         /* 0x1c */
81         FILETIME Time2;         /* 0x24 */
82         FILETIME Time3;         /* 0x2c */
83         DWORD    dwFileLength;  /* 0x34 File length */
84         DWORD    nIcon;         /* 0x38 icon number */
85         DWORD   fStartup;       /* 0x3c startup type */
86         DWORD   wHotKey;        /* 0x40 hotkey */
87         DWORD   Unknown5;       /* 0x44 */
88         DWORD   Unknown6;       /* 0x48 */
89 } LINK_HEADER, * PLINK_HEADER;
90
91 #define SHLINK_LOCAL  0
92 #define SHLINK_REMOTE 1
93
94 typedef struct _LOCATION_INFO
95 {
96     DWORD  dwTotalSize;
97     DWORD  dwHeaderSize;
98     DWORD  dwFlags;
99     DWORD  dwVolTableOfs;
100     DWORD  dwLocalPathOfs;
101     DWORD  dwNetworkVolTableOfs;
102     DWORD  dwFinalPathOfs;
103 } LOCATION_INFO;
104
105 typedef struct _LOCAL_VOLUME_INFO
106 {
107     DWORD dwSize;
108     DWORD dwType;
109     DWORD dwVolSerial;
110     DWORD dwVolLabelOfs;
111 } LOCAL_VOLUME_INFO;
112
113 #include "poppack.h"
114
115 static IShellLinkAVtbl          slvt;
116 static IShellLinkWVtbl          slvtw;
117 static IPersistFileVtbl pfvt;
118 static IPersistStreamVtbl       psvt;
119
120 /* IShellLink Implementation */
121
122 typedef struct
123 {
124         IShellLinkAVtbl    *lpVtbl;
125         DWORD               ref;
126
127         IShellLinkWVtbl    *lpvtblw;
128         IPersistFileVtbl   *lpvtblPersistFile;
129         IPersistStreamVtbl *lpvtblPersistStream;
130
131         /* data structures according to the informations in the link */
132         LPITEMIDLIST    pPidl;
133         WORD            wHotKey;
134         SYSTEMTIME      time1;
135         SYSTEMTIME      time2;
136         SYSTEMTIME      time3;
137
138         DWORD         iShowCmd;
139         LPWSTR        sIcoPath;
140         INT           iIcoNdx;
141         LPWSTR        sPath;
142         LPWSTR        sArgs;
143         LPWSTR        sWorkDir;
144         LPWSTR        sDescription;
145         LPWSTR        sPathRel;
146
147         BOOL            bDirty;
148 } IShellLinkImpl;
149
150 #define _IShellLinkW_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblw)))
151 #define _ICOM_THIS_From_IShellLinkW(class, name) class* This = (class*)(((char*)name)-_IShellLinkW_Offset)
152
153 #define _IPersistFile_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblPersistFile)))
154 #define _ICOM_THIS_From_IPersistFile(class, name) class* This = (class*)(((char*)name)-_IPersistFile_Offset)
155
156 #define _IPersistStream_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblPersistStream)))
157 #define _ICOM_THIS_From_IPersistStream(class, name) class* This = (class*)(((char*)name)-_IPersistStream_Offset)
158
159 static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath);
160
161 /* strdup on the process heap */
162 inline static LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
163 {
164     INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
165     LPWSTR p = HeapAlloc( heap, flags, len*sizeof (WCHAR) );
166     if( !p )
167         return p;
168     MultiByteToWideChar( CP_ACP, 0, str, -1, p, len );
169     return p;
170 }
171
172
173 /**************************************************************************
174  *  IPersistFile_QueryInterface
175  */
176 static HRESULT WINAPI IPersistFile_fnQueryInterface(
177         IPersistFile* iface,
178         REFIID riid,
179         LPVOID *ppvObj)
180 {
181         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
182
183         TRACE("(%p)\n",This);
184
185         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
186 }
187
188 /******************************************************************************
189  * IPersistFile_AddRef
190  */
191 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
192 {
193         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
194
195         TRACE("(%p)->(count=%lu)\n",This,This->ref);
196
197         return IShellLinkA_AddRef((IShellLinkA*)This);
198 }
199 /******************************************************************************
200  * IPersistFile_Release
201  */
202 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile* iface)
203 {
204         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
205
206         TRACE("(%p)->(count=%lu)\n",This,This->ref);
207
208         return IShellLinkA_Release((IShellLinkA*)This);
209 }
210
211 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
212 {
213         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
214         FIXME("(%p)\n",This);
215         return NOERROR;
216 }
217 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
218 {
219         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
220
221         TRACE("(%p)\n",This);
222
223         if (This->bDirty)
224             return S_OK;
225
226         return S_FALSE;
227 }
228 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
229 {
230         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
231         IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
232         HRESULT r;
233         IStream *stm;
234
235         TRACE("(%p, %s)\n",This, debugstr_w(pszFileName));
236
237         r = CreateStreamOnFile(pszFileName, dwMode, &stm);
238         if( SUCCEEDED( r ) )
239         {
240             r = IPersistStream_Load(StreamThis, stm);
241             ShellLink_UpdatePath(This->sPathRel, pszFileName, This->sWorkDir, &This->sPath);
242             IStream_Release( stm );
243             This->bDirty = FALSE;
244         }
245
246         return r;
247 }
248
249 static BOOL StartLinkProcessor( LPCOLESTR szLink )
250 {
251     static const WCHAR szFormat[] = {'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
252                               ' ','-','r',' ','"','%','s','"',0 };
253     LONG len;
254     LPWSTR buffer;
255     STARTUPINFOW si;
256     PROCESS_INFORMATION pi;
257
258     len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
259     buffer = HeapAlloc( GetProcessHeap(), 0, len );
260     if( !buffer )
261         return FALSE;
262
263     wsprintfW( buffer, szFormat, szLink );
264
265     TRACE("starting %s\n",debugstr_w(buffer));
266
267     memset(&si, 0, sizeof(si));
268     si.cb = sizeof(si);
269     if (!CreateProcessW( NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) return FALSE;
270
271     /* wait for a while to throttle the creation of linker processes */
272     if( WAIT_OBJECT_0 != WaitForSingleObject( pi.hProcess, 10000 ) )
273         WARN("Timed out waiting for shell linker\n");
274
275     CloseHandle( pi.hProcess );
276     CloseHandle( pi.hThread );
277
278     return TRUE;
279 }
280
281 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
282 {
283     _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
284     IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
285     HRESULT r;
286     IStream *stm;
287
288     TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
289
290     if (!pszFileName || !This->sPath)
291         return E_FAIL;
292
293     r = CreateStreamOnFile(pszFileName, STGM_READWRITE | STGM_CREATE, &stm);
294     if( SUCCEEDED( r ) )
295     {
296         r = IPersistStream_Save(StreamThis, stm, FALSE);
297         IStream_Release( stm );
298
299         if( SUCCEEDED( r ) )
300         {
301             StartLinkProcessor( pszFileName );
302
303             This->bDirty = FALSE;
304         }
305         else
306         {
307             DeleteFileW( pszFileName );
308             WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
309         }
310     }
311
312     return r;
313 }
314
315 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
316 {
317         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
318         FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
319         return NOERROR;
320 }
321 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *ppszFileName)
322 {
323         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
324         FIXME("(%p)\n",This);
325         return NOERROR;
326 }
327
328 static IPersistFileVtbl pfvt =
329 {
330         IPersistFile_fnQueryInterface,
331         IPersistFile_fnAddRef,
332         IPersistFile_fnRelease,
333         IPersistFile_fnGetClassID,
334         IPersistFile_fnIsDirty,
335         IPersistFile_fnLoad,
336         IPersistFile_fnSave,
337         IPersistFile_fnSaveCompleted,
338         IPersistFile_fnGetCurFile
339 };
340
341 /************************************************************************
342  * IPersistStream_QueryInterface
343  */
344 static HRESULT WINAPI IPersistStream_fnQueryInterface(
345         IPersistStream* iface,
346         REFIID     riid,
347         VOID**     ppvoid)
348 {
349         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
350
351         TRACE("(%p)\n",This);
352
353         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvoid);
354 }
355
356 /************************************************************************
357  * IPersistStream_Release
358  */
359 static ULONG WINAPI IPersistStream_fnRelease(
360         IPersistStream* iface)
361 {
362         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
363
364         TRACE("(%p)\n",This);
365
366         return IShellLinkA_Release((IShellLinkA*)This);
367 }
368
369 /************************************************************************
370  * IPersistStream_AddRef
371  */
372 static ULONG WINAPI IPersistStream_fnAddRef(
373         IPersistStream* iface)
374 {
375         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
376
377         TRACE("(%p)\n",This);
378
379         return IShellLinkA_AddRef((IShellLinkA*)This);
380 }
381
382 /************************************************************************
383  * IPersistStream_GetClassID
384  *
385  */
386 static HRESULT WINAPI IPersistStream_fnGetClassID(
387         IPersistStream* iface,
388         CLSID* pClassID)
389 {
390         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
391
392         TRACE("(%p)\n", This);
393
394         if (pClassID==0)
395           return E_POINTER;
396
397 /*      memcpy(pClassID, &CLSID_???, sizeof(CLSID_???)); */
398
399         return S_OK;
400 }
401
402 /************************************************************************
403  * IPersistStream_IsDirty (IPersistStream)
404  */
405 static HRESULT WINAPI IPersistStream_fnIsDirty(
406         IPersistStream*  iface)
407 {
408         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
409
410         TRACE("(%p)\n", This);
411
412         return S_OK;
413 }
414
415
416 static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
417 {
418     DWORD count;
419     USHORT len;
420     LPVOID temp;
421     LPWSTR str;
422     HRESULT r;
423
424     TRACE("%p\n", stm);
425
426     count = 0;
427     r = IStream_Read(stm, &len, sizeof(len), &count);
428     if ( FAILED (r) || ( count != sizeof(len) ) )
429         return E_FAIL;
430
431     if( unicode )
432         len *= sizeof (WCHAR);
433
434     TRACE("reading %d\n", len);
435     temp = HeapAlloc(GetProcessHeap(), 0, len+sizeof(WCHAR));
436     if( !temp )
437         return E_OUTOFMEMORY;
438     count = 0;
439     r = IStream_Read(stm, temp, len, &count);
440     if( FAILED (r) || ( count != len ) )
441     {
442         HeapFree( GetProcessHeap(), 0, temp );
443         return E_FAIL;
444     }
445
446     TRACE("read %s\n", debugstr_an(temp,len));
447
448     /* convert to unicode if necessary */
449     if( !unicode )
450     {
451         count = MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, NULL, 0 );
452         str = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof (WCHAR) );
453         if( str )
454             MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, str, count );
455         HeapFree( GetProcessHeap(), 0, temp );
456     }
457     else
458     {
459         count /= 2;
460         str = (LPWSTR) temp;
461     }
462     str[count] = 0;
463
464     *pstr = str;
465
466     return S_OK;
467 }
468
469 static HRESULT Stream_LoadLocation( IStream* stm )
470 {
471     DWORD size;
472     ULONG count;
473     HRESULT r;
474     LOCATION_INFO *loc;
475
476     TRACE("%p\n",stm);
477
478     r = IStream_Read( stm, &size, sizeof(size), &count );
479     if( FAILED( r ) )
480         return r;
481     if( count != sizeof(loc->dwTotalSize) )
482         return E_FAIL;
483
484     loc = HeapAlloc( GetProcessHeap(), 0, size );
485     if( ! loc )
486         return E_OUTOFMEMORY;
487
488     r = IStream_Read( stm, &loc->dwHeaderSize, size-sizeof(size), &count );
489     if( FAILED( r ) )
490         goto end;
491     if( count != (size - sizeof(size)) )
492     {
493         r = E_FAIL;
494         goto end;
495     }
496     loc->dwTotalSize = size;
497
498     TRACE("Read %ld bytes\n",count);
499
500     /* FIXME: do something useful with it */
501     HeapFree( GetProcessHeap(), 0, loc );
502
503     return S_OK;
504 end:
505     HeapFree( GetProcessHeap(), 0, loc );
506     return r;
507 }
508
509 /************************************************************************
510  * IPersistStream_Load (IPersistStream)
511  */
512 static HRESULT WINAPI IPersistStream_fnLoad(
513         IPersistStream*  iface,
514     IStream*         stm)
515 {
516     LINK_HEADER hdr;
517     ULONG    dwBytesRead;
518     BOOL     unicode;
519     WCHAR    sTemp[MAX_PATH];
520     HRESULT  r;
521
522     _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
523
524     TRACE("(%p)(%p)\n", This, stm);
525
526     if( !stm )
527           return STG_E_INVALIDPOINTER;
528
529     dwBytesRead = 0;
530     r = IStream_Read(stm, &hdr, sizeof(hdr), &dwBytesRead);
531     if( FAILED( r ) )
532         return r;
533
534     if( dwBytesRead != sizeof(hdr))
535         return E_FAIL;
536     if( hdr.dwSize != sizeof(hdr))
537         return E_FAIL;
538     if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
539         return E_FAIL;
540
541     /* if( hdr.dwFlags & SCF_PIDL ) */  /* FIXME: seems to always have a PIDL */
542     {
543         r = ILLoadFromStream( stm, &This->pPidl );
544         if( FAILED( r ) )
545             return r;
546     }
547     This->wHotKey = (WORD)hdr.wHotKey;
548     This->iIcoNdx = hdr.nIcon;
549     FileTimeToSystemTime (&hdr.Time1, &This->time1);
550     FileTimeToSystemTime (&hdr.Time2, &This->time2);
551     FileTimeToSystemTime (&hdr.Time3, &This->time3);
552 #if 1
553     GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time1, NULL, sTemp, 256);
554     TRACE("-- time1: %s\n", debugstr_w(sTemp) );
555     GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time2, NULL, sTemp, 256);
556     TRACE("-- time1: %s\n", debugstr_w(sTemp) );
557     GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time3, NULL, sTemp, 256);
558     TRACE("-- time1: %s\n", debugstr_w(sTemp) );
559     pdump (This->pPidl);
560 #endif
561     if( hdr.dwFlags & SCF_NORMAL )
562         r = Stream_LoadLocation( stm );
563     if( FAILED( r ) )
564         goto end;
565     unicode = hdr.dwFlags & SCF_UNICODE;
566     if( hdr.dwFlags & SCF_DESCRIPTION )
567     {
568         r = Stream_LoadString( stm, unicode, &This->sDescription );
569         TRACE("Description  -> %s\n",debugstr_w(This->sDescription));
570     }
571     if( FAILED( r ) )
572         goto end;
573
574     if( hdr.dwFlags & SCF_RELATIVE )
575     {
576         r = Stream_LoadString( stm, unicode, &This->sPathRel );
577         TRACE("Relative Path-> %s\n",debugstr_w(This->sPathRel));
578     }
579     if( FAILED( r ) )
580         goto end;
581
582     if( hdr.dwFlags & SCF_WORKDIR )
583           {
584         r = Stream_LoadString( stm, unicode, &This->sWorkDir );
585         TRACE("Working Dir  -> %s\n",debugstr_w(This->sWorkDir));
586     }
587     if( FAILED( r ) )
588         goto end;
589
590     if( hdr.dwFlags & SCF_ARGS )
591     {
592         r = Stream_LoadString( stm, unicode, &This->sArgs );
593         TRACE("Working Dir  -> %s\n",debugstr_w(This->sArgs));
594     }
595     if( FAILED( r ) )
596         goto end;
597
598     if( hdr.dwFlags & SCF_CUSTOMICON )
599     {
600         r = Stream_LoadString( stm, unicode, &This->sIcoPath );
601         TRACE("Icon file    -> %s\n",debugstr_w(This->sIcoPath));
602     }
603     if( FAILED( r ) )
604         goto end;
605
606     TRACE("OK\n");
607
608     pdump (This->pPidl);
609
610     return S_OK;
611 end:
612     return r;
613 }
614
615 /************************************************************************
616  * Stream_WriteString
617  *
618  * Helper function for IPersistStream_Save. Writes a unicode string 
619  *  with terminating nul byte to a stream, preceded by the its length.
620  */
621 static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
622 {
623     USHORT len = lstrlenW( str ) + 1;
624     DWORD count;
625     HRESULT r;
626
627     r = IStream_Write( stm, &len, sizeof(len), &count );
628     if( FAILED( r ) )
629         return r;
630
631     len *= sizeof(WCHAR);
632
633     r = IStream_Write( stm, str, len, &count );
634     if( FAILED( r ) )
635         return r;
636
637     return S_OK;
638 }
639
640 static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR filename )
641 {
642     LOCATION_INFO loc;
643     ULONG count;
644
645     FIXME("writing empty location info\n");
646
647     memset( &loc, 0, sizeof(loc) );
648     loc.dwTotalSize = sizeof(loc) - sizeof(loc.dwTotalSize);
649
650     /* FIXME: fill this in */
651
652     return IStream_Write( stm, &loc, loc.dwTotalSize, &count );
653 }
654
655 /************************************************************************
656  * IPersistStream_Save (IPersistStream)
657  *
658  * FIXME: makes assumptions about byte order
659  */
660 static HRESULT WINAPI IPersistStream_fnSave(
661         IPersistStream*  iface,
662         IStream*         stm,
663         BOOL             fClearDirty)
664 {
665     static const WCHAR wOpen[] = {'o','p','e','n',0};
666
667     LINK_HEADER header;
668     WCHAR   exePath[MAX_PATH];
669     ULONG   count;
670     HRESULT r;
671
672     _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
673
674     TRACE("(%p) %p %x\n", This, stm, fClearDirty);
675
676     *exePath = '\0';
677
678     if (This->sPath)
679     {
680         SHELL_FindExecutable(NULL, This->sPath, wOpen, exePath, MAX_PATH, NULL, NULL, NULL, NULL);
681         /*
682          * windows can create lnk files to executables that do not exist yet
683          * so if the executable does not exist the just trust the path they
684          * gave us
685          */
686         if( !*exePath ) strcpyW(exePath,This->sPath);
687     }
688
689     /* if there's no PIDL, generate one */
690     if( ! This->pPidl ) This->pPidl = ILCreateFromPathW(exePath);
691
692     memset(&header, 0, sizeof(header));
693     header.dwSize = sizeof(header);
694     memcpy(&header.MagicGuid, &CLSID_ShellLink, sizeof(header.MagicGuid) );
695
696     header.wHotKey = This->wHotKey;
697     header.nIcon = This->iIcoNdx;
698     header.dwFlags = SCF_UNICODE;   /* strings are in unicode */
699     header.dwFlags |= SCF_NORMAL;   /* how do we determine this ? */
700     if( This->pPidl )
701         header.dwFlags |= SCF_PIDL;
702     if( This->sDescription )
703         header.dwFlags |= SCF_DESCRIPTION;
704     if( This->sWorkDir )
705         header.dwFlags |= SCF_WORKDIR;
706     if( This->sArgs )
707         header.dwFlags |= SCF_ARGS;
708     if( This->sIcoPath )
709         header.dwFlags |= SCF_CUSTOMICON;
710
711     SystemTimeToFileTime ( &This->time1, &header.Time1 );
712     SystemTimeToFileTime ( &This->time2, &header.Time2 );
713     SystemTimeToFileTime ( &This->time3, &header.Time3 );
714
715     /* write the Shortcut header */
716     r = IStream_Write( stm, &header, sizeof(header), &count );
717     if( FAILED( r ) )
718     {
719         ERR("Write failed at %d\n",__LINE__);
720         return r;
721     }
722
723     TRACE("Writing pidl \n");
724
725     /* write the PIDL to the shortcut */
726     if( This->pPidl )
727     {
728         r = ILSaveToStream( stm, This->pPidl );
729         if( FAILED( r ) )
730         {
731             ERR("Failed to write PIDL at %d\n",__LINE__);
732             return r;
733         }
734     }
735
736     Stream_WriteLocationInfo( stm, exePath );
737
738     TRACE("Description = %s\n", debugstr_w(This->sDescription));
739     if( This->sDescription )
740         r = Stream_WriteString( stm, This->sDescription );
741
742     if( This->sPathRel )
743         r = Stream_WriteString( stm, This->sPathRel );
744
745     if( This->sWorkDir )
746         r = Stream_WriteString( stm, This->sWorkDir );
747
748     if( This->sArgs )
749         r = Stream_WriteString( stm, This->sArgs );
750
751     if( This->sIcoPath )
752         r = Stream_WriteString( stm, This->sIcoPath );
753
754     return S_OK;
755 }
756
757 /************************************************************************
758  * IPersistStream_GetSizeMax (IPersistStream)
759  */
760 static HRESULT WINAPI IPersistStream_fnGetSizeMax(
761         IPersistStream*  iface,
762         ULARGE_INTEGER*  pcbSize)
763 {
764         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
765
766         TRACE("(%p)\n", This);
767
768         return E_NOTIMPL;
769 }
770
771 static IPersistStreamVtbl psvt =
772 {
773         IPersistStream_fnQueryInterface,
774         IPersistStream_fnAddRef,
775         IPersistStream_fnRelease,
776         IPersistStream_fnGetClassID,
777         IPersistStream_fnIsDirty,
778         IPersistStream_fnLoad,
779         IPersistStream_fnSave,
780         IPersistStream_fnGetSizeMax
781 };
782
783 /**************************************************************************
784  *        IShellLink_Constructor
785  */
786 HRESULT WINAPI IShellLink_Constructor (
787         IUnknown * pUnkOuter,
788         REFIID riid,
789         LPVOID * ppv)
790 {
791         IShellLinkImpl * sl;
792
793         TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
794
795         *ppv = NULL;
796
797         if(pUnkOuter) return CLASS_E_NOAGGREGATION;
798         sl = (IShellLinkImpl *) LocalAlloc(GMEM_ZEROINIT,sizeof(IShellLinkImpl));
799         if (!sl) return E_OUTOFMEMORY;
800
801         sl->ref = 1;
802         sl->lpVtbl = &slvt;
803         sl->lpvtblw = &slvtw;
804         sl->lpvtblPersistFile = &pfvt;
805         sl->lpvtblPersistStream = &psvt;
806         sl->iShowCmd = SW_SHOWNORMAL;
807         sl->bDirty = FALSE;
808
809         TRACE("(%p)->()\n",sl);
810
811         if (IsEqualIID(riid, &IID_IUnknown) ||
812             IsEqualIID(riid, &IID_IShellLinkA))
813             *ppv = sl;
814         else if (IsEqualIID(riid, &IID_IShellLinkW))
815             *ppv = &(sl->lpvtblw);
816         else {
817             LocalFree((HLOCAL)sl);
818             ERR("E_NOINTERFACE\n");
819             return E_NOINTERFACE;
820         }
821
822         return S_OK;
823 }
824
825
826 static BOOL SHELL_ExistsFileW(LPCWSTR path)
827 {
828     HANDLE hfile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
829
830     if (hfile != INVALID_HANDLE_VALUE) {
831         CloseHandle(hfile);
832         return TRUE;
833     } else
834         return FALSE;
835 }
836
837 /**************************************************************************
838  *  ShellLink_UpdatePath
839  *      update absolute path in sPath using relative path in sPathRel
840  */
841 static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
842 {
843     if (!path || !psPath)
844         return E_INVALIDARG;
845
846     if (!*psPath && sPathRel) {
847         WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
848         LPWSTR final = NULL;
849
850         /* first try if [directory of link file] + [relative path] finds an existing file */
851
852         GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
853         if( !final )
854             final = buffer;
855         lstrcpyW(final, sPathRel);
856
857         *abs_path = '\0';
858
859         if (SHELL_ExistsFileW(buffer)) {
860             if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
861                 lstrcpyW(abs_path, buffer);
862         } else {
863             /* try if [working directory] + [relative path] finds an existing file */
864             if (sWorkDir) {
865                 lstrcpyW(buffer, sWorkDir);
866                 lstrcpyW(PathAddBackslashW(buffer), sPathRel);
867
868                 if (SHELL_ExistsFileW(buffer))
869                     if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
870                         lstrcpyW(abs_path, buffer);
871             }
872         }
873
874         /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
875         if (!*abs_path)
876             lstrcpyW(abs_path, sPathRel);
877
878         *psPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(abs_path)+1)*sizeof(WCHAR));
879         if (!*psPath)
880             return E_OUTOFMEMORY;
881
882         lstrcpyW(*psPath, abs_path);
883     }
884
885     return S_OK;
886 }
887
888 /**************************************************************************
889  *        IShellLink_ConstructFromFile
890  */
891 HRESULT WINAPI IShellLink_ConstructFromFile (
892         IUnknown* pUnkOuter,
893         REFIID riid,
894         LPCITEMIDLIST pidl,
895         LPVOID* ppv
896 )
897 {
898     IShellLinkW* psl;
899
900     HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl);
901
902     if (SUCCEEDED(hr)) {
903         IPersistFile* ppf;
904
905         *ppv = NULL;
906
907         hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
908
909         if (SUCCEEDED(hr)) {
910             WCHAR path[MAX_PATH];
911
912             if (SHGetPathFromIDListW(pidl, path)) 
913                 hr = IPersistFile_Load(ppf, path, 0);
914             else
915                 hr = E_FAIL;
916
917             if (SUCCEEDED(hr))
918                 *ppv = (IUnknown*) psl;
919
920             IPersistFile_Release(ppf);
921         }
922
923         if (!*ppv)
924             IShellLinkW_Release(psl);
925     }
926
927     return hr;
928 }
929
930 /**************************************************************************
931  *  IShellLinkA_QueryInterface
932  */
933 static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
934 {
935         IShellLinkImpl *This = (IShellLinkImpl *)iface;
936
937         TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
938
939         *ppvObj = NULL;
940
941         if(IsEqualIID(riid, &IID_IUnknown) ||
942            IsEqualIID(riid, &IID_IShellLinkA))
943         {
944           *ppvObj = This;
945         }
946         else if(IsEqualIID(riid, &IID_IShellLinkW))
947         {
948           *ppvObj = (IShellLinkW *)&(This->lpvtblw);
949         }
950         else if(IsEqualIID(riid, &IID_IPersistFile))
951         {
952           *ppvObj = (IPersistFile *)&(This->lpvtblPersistFile);
953         }
954         else if(IsEqualIID(riid, &IID_IPersistStream))
955         {
956           *ppvObj = (IPersistStream *)&(This->lpvtblPersistStream);
957         }
958
959         if(*ppvObj)
960         {
961           IUnknown_AddRef((IUnknown*)(*ppvObj));
962           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
963           return S_OK;
964         }
965         TRACE("-- Interface: E_NOINTERFACE\n");
966         return E_NOINTERFACE;
967 }
968 /******************************************************************************
969  * IShellLinkA_AddRef
970  */
971 static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
972 {
973         IShellLinkImpl *This = (IShellLinkImpl *)iface;
974
975         TRACE("(%p)->(count=%lu)\n",This,This->ref);
976
977         return ++(This->ref);
978 }
979 /******************************************************************************
980  *      IShellLinkA_Release
981  */
982 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
983 {
984     IShellLinkImpl *This = (IShellLinkImpl *)iface;
985
986     TRACE("(%p)->(count=%lu)\n",This,This->ref);
987
988     if (--(This->ref))
989         return This->ref;
990
991     TRACE("-- destroying IShellLink(%p)\n",This);
992
993     if (This->sIcoPath)
994         HeapFree(GetProcessHeap(), 0, This->sIcoPath);
995
996     if (This->sArgs)
997         HeapFree(GetProcessHeap(), 0, This->sArgs);
998
999     if (This->sWorkDir)
1000         HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1001
1002     if (This->sDescription)
1003         HeapFree(GetProcessHeap(), 0, This->sDescription);
1004
1005     if (This->sPath)
1006         HeapFree(GetProcessHeap(),0,This->sPath);
1007
1008     if (This->pPidl)
1009         ILFree(This->pPidl);
1010
1011     LocalFree((HANDLE)This);
1012
1013     return 0;
1014 }
1015
1016 static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
1017                   INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1018 {
1019     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1020
1021     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
1022           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1023
1024     if( cchMaxPath )
1025         pszFile[0] = 0;
1026     if (This->sPath)
1027         WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
1028                              pszFile, cchMaxPath, NULL, NULL);
1029
1030     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1031
1032     return NOERROR;
1033 }
1034
1035 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
1036 {
1037         IShellLinkImpl *This = (IShellLinkImpl *)iface;
1038
1039         TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1040
1041         *ppidl = ILClone(This->pPidl);
1042
1043         return NOERROR;
1044 }
1045
1046 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
1047 {
1048     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1049
1050     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1051
1052     if (This->pPidl)
1053         ILFree(This->pPidl);
1054     This->pPidl = ILClone (pidl);
1055     This->bDirty = TRUE;
1056
1057     return S_OK;
1058 }
1059
1060 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
1061 {
1062     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1063
1064     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1065
1066     if( cchMaxName )
1067         pszName[0] = 0;
1068     if( This->sDescription )
1069         WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
1070             pszName, cchMaxName, NULL, NULL);
1071
1072     return S_OK;
1073 }
1074 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
1075 {
1076     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1077
1078     TRACE("(%p)->(pName=%s)\n", This, pszName);
1079
1080     if (This->sDescription)
1081         HeapFree(GetProcessHeap(), 0, This->sDescription);
1082     This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
1083     if ( !This->sDescription )
1084         return E_OUTOFMEMORY;
1085
1086     This->bDirty = TRUE;
1087
1088     return S_OK;
1089 }
1090
1091 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
1092 {
1093     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1094
1095     TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1096
1097     if( cchMaxPath )
1098         pszDir[0] = 0;
1099     if( This->sWorkDir )
1100         WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
1101                              pszDir, cchMaxPath, NULL, NULL);
1102
1103     return S_OK;
1104 }
1105
1106 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
1107 {
1108     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1109
1110     TRACE("(%p)->(dir=%s)\n",This, pszDir);
1111
1112     if (This->sWorkDir)
1113         HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1114     This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
1115     if ( !This->sWorkDir )
1116         return E_OUTOFMEMORY;
1117
1118     This->bDirty = TRUE;
1119
1120     return S_OK;
1121 }
1122
1123 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
1124 {
1125     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1126
1127     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1128
1129     if( cchMaxPath )
1130         pszArgs[0] = 0;
1131     if( This->sArgs )
1132         WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
1133                              pszArgs, cchMaxPath, NULL, NULL);
1134
1135     return S_OK;
1136 }
1137
1138 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
1139 {
1140     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1141
1142     TRACE("(%p)->(args=%s)\n",This, pszArgs);
1143
1144     if (This->sArgs)
1145         HeapFree(GetProcessHeap(), 0, This->sArgs);
1146     This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
1147     if( !This->sArgs )
1148         return E_OUTOFMEMORY;
1149
1150     This->bDirty = TRUE;
1151
1152     return S_OK;
1153 }
1154
1155 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
1156 {
1157         IShellLinkImpl *This = (IShellLinkImpl *)iface;
1158
1159         TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1160
1161         *pwHotkey = This->wHotKey;
1162
1163         return S_OK;
1164 }
1165
1166 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
1167 {
1168         IShellLinkImpl *This = (IShellLinkImpl *)iface;
1169
1170         TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1171
1172         This->wHotKey = wHotkey;
1173         This->bDirty = TRUE;
1174
1175         return S_OK;
1176 }
1177
1178 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
1179 {
1180     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1181
1182     TRACE("(%p)->(%p)\n",This, piShowCmd);
1183     *piShowCmd = This->iShowCmd;
1184     return S_OK;
1185 }
1186
1187 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
1188 {
1189     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1190
1191     TRACE("(%p) %d\n",This, iShowCmd);
1192
1193     This->iShowCmd = iShowCmd;
1194     This->bDirty = TRUE;
1195
1196     return NOERROR;
1197 }
1198
1199 static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPITEMIDLIST pidl, LPSTR pszIconPath, int cchIconPath, int* piIcon)
1200 {
1201     LPCITEMIDLIST pidlLast;
1202
1203     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1204
1205     if (SUCCEEDED(hr)) {
1206         IExtractIconA* pei;
1207
1208         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconA, NULL, (LPVOID*)&pei);
1209
1210         if (SUCCEEDED(hr)) {
1211             hr = IExtractIconA_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1212
1213             IExtractIconA_Release(pei);
1214         }
1215
1216         IShellFolder_Release(psf);
1217     }
1218
1219     return hr;
1220 }
1221
1222 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1223 {
1224     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1225
1226     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1227
1228     if (cchIconPath)
1229         pszIconPath[0] = 0;
1230
1231     if (This->sIcoPath) {
1232         WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1233         *piIcon = This->iIcoNdx;
1234         return S_OK;
1235     }
1236
1237     if (This->pPidl || This->sPath) {
1238         IShellFolder* pdsk;
1239
1240         HRESULT hr = SHGetDesktopFolder(&pdsk);
1241
1242         if (SUCCEEDED(hr)) {
1243             /* first look for an icon using the PIDL (if present) */
1244             if (This->pPidl)
1245                 hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1246             else
1247                 hr = E_FAIL;
1248
1249             /* if we couldn't find an icon yet, look for it using the file system path */
1250             if (FAILED(hr) && This->sPath) {
1251                 LPITEMIDLIST pidl;
1252
1253                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1254
1255                 if (SUCCEEDED(hr)) {
1256                     hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1257
1258                     SHFree(pidl);
1259                 }
1260             }
1261
1262             IShellFolder_Release(pdsk);
1263         }
1264
1265         return hr;
1266     } else
1267         return E_FAIL;
1268 }
1269
1270 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
1271 {
1272     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1273
1274     TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1275
1276     if (This->sIcoPath)
1277         HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1278     This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1279     if ( !This->sIcoPath )
1280         return E_OUTOFMEMORY;
1281
1282     This->iIcoNdx = iIcon;
1283     This->bDirty = TRUE;
1284
1285     return S_OK;
1286 }
1287
1288 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1289 {
1290     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1291
1292     FIXME("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved);
1293
1294     if (This->sPathRel)
1295         HeapFree(GetProcessHeap(), 0, This->sPathRel);
1296     This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1297     This->bDirty = TRUE;
1298
1299     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1300 }
1301
1302 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1303 {
1304     HRESULT hr = S_OK;
1305
1306     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1307
1308     FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1309
1310     /*FIXME: use IResolveShellLink interface */
1311
1312     if (!This->sPath && This->pPidl) {
1313         WCHAR buffer[MAX_PATH];
1314
1315         hr = SHELL_GetPathFromIDListW(This->pPidl, buffer, MAX_PATH);
1316
1317         if (SUCCEEDED(hr) && *buffer) {
1318             This->sPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
1319             if (!This->sPath)
1320                 return E_OUTOFMEMORY;
1321
1322             lstrcpyW(This->sPath, buffer);
1323
1324             This->bDirty = TRUE;
1325         } else
1326             hr = S_OK;    /* don't report an error occurred while just caching information */
1327     }
1328
1329     if (!This->sIcoPath && This->sPath) {
1330         This->sIcoPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
1331         if (!This->sIcoPath)
1332             return E_OUTOFMEMORY;
1333
1334         lstrcpyW(This->sIcoPath, This->sPath);
1335         This->iIcoNdx = 0;
1336
1337         This->bDirty = TRUE;
1338     }
1339
1340     return hr;
1341 }
1342
1343 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1344 {
1345     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1346     char buffer[MAX_PATH];
1347     LPSTR fname;
1348
1349     TRACE("(%p)->(path=%s)\n",This, pszFile);
1350
1351     if (!GetFullPathNameA(pszFile, MAX_PATH, buffer, &fname))
1352         return E_FAIL;
1353
1354     if (This->sPath)
1355         HeapFree(GetProcessHeap(), 0, This->sPath);
1356
1357     This->sPath = HEAP_strdupAtoW(GetProcessHeap(), 0, buffer);
1358     if( !This->sPath )
1359         return E_OUTOFMEMORY;
1360
1361     This->bDirty = TRUE;
1362
1363     return S_OK;
1364 }
1365
1366 /**************************************************************************
1367 * IShellLink Implementation
1368 */
1369
1370 static IShellLinkAVtbl slvt =
1371 {
1372         IShellLinkA_fnQueryInterface,
1373         IShellLinkA_fnAddRef,
1374         IShellLinkA_fnRelease,
1375         IShellLinkA_fnGetPath,
1376         IShellLinkA_fnGetIDList,
1377         IShellLinkA_fnSetIDList,
1378         IShellLinkA_fnGetDescription,
1379         IShellLinkA_fnSetDescription,
1380         IShellLinkA_fnGetWorkingDirectory,
1381         IShellLinkA_fnSetWorkingDirectory,
1382         IShellLinkA_fnGetArguments,
1383         IShellLinkA_fnSetArguments,
1384         IShellLinkA_fnGetHotkey,
1385         IShellLinkA_fnSetHotkey,
1386         IShellLinkA_fnGetShowCmd,
1387         IShellLinkA_fnSetShowCmd,
1388         IShellLinkA_fnGetIconLocation,
1389         IShellLinkA_fnSetIconLocation,
1390         IShellLinkA_fnSetRelativePath,
1391         IShellLinkA_fnResolve,
1392         IShellLinkA_fnSetPath
1393 };
1394
1395
1396 /**************************************************************************
1397  *  IShellLinkW_fnQueryInterface
1398  */
1399 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1400   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1401 {
1402         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1403
1404         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
1405 }
1406
1407 /******************************************************************************
1408  * IShellLinkW_fnAddRef
1409  */
1410 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1411 {
1412         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1413
1414         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1415
1416         return IShellLinkA_AddRef((IShellLinkA*)This);
1417 }
1418 /******************************************************************************
1419  * IShellLinkW_fnRelease
1420  */
1421
1422 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1423 {
1424         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1425
1426         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1427
1428         return IShellLinkA_Release((IShellLinkA*)This);
1429 }
1430
1431 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1432 {
1433     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1434
1435     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)\n",
1436                   This, pszFile, cchMaxPath, pfd, fFlags);
1437
1438     if( cchMaxPath )
1439         pszFile[0] = 0;
1440     if( This->sPath )
1441         lstrcpynW( pszFile, This->sPath, cchMaxPath );
1442
1443     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1444
1445     return NOERROR;
1446 }
1447
1448 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1449 {
1450     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1451
1452     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1453
1454     if( This->pPidl)
1455         *ppidl = ILClone( This->pPidl );
1456     else
1457         *ppidl = NULL;
1458
1459     return S_OK;
1460 }
1461
1462 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1463 {
1464     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1465
1466     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1467
1468     if( This->pPidl )
1469         ILFree( This->pPidl );
1470     This->pPidl = ILClone( pidl );
1471     if( !This->pPidl )
1472         return E_FAIL;
1473
1474     This->bDirty = TRUE;
1475
1476     return S_OK;
1477 }
1478
1479 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1480 {
1481     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1482
1483     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1484
1485     if( cchMaxName )
1486         pszName[0] = 0;
1487     if( This->sDescription )
1488         lstrcpynW( pszName, This->sDescription, cchMaxName );
1489
1490     return S_OK;
1491 }
1492
1493 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1494 {
1495     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1496
1497     TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1498
1499     if (This->sDescription)
1500         HeapFree(GetProcessHeap(), 0, This->sDescription);
1501     This->sDescription = HeapAlloc( GetProcessHeap(), 0,
1502                                     (lstrlenW( pszName )+1)*sizeof(WCHAR) );
1503     if ( !This->sDescription )
1504         return E_OUTOFMEMORY;
1505
1506     lstrcpyW( This->sDescription, pszName );
1507     This->bDirty = TRUE;
1508
1509     return S_OK;
1510 }
1511
1512 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1513 {
1514     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1515
1516     TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1517
1518     if( cchMaxPath )
1519         pszDir[0] = 0;
1520     if( This->sWorkDir )
1521         lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
1522
1523     return S_OK;
1524 }
1525
1526 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1527 {
1528     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1529
1530     TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1531
1532     if (This->sWorkDir)
1533         HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1534     This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
1535                                 (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
1536     if ( !This->sWorkDir )
1537         return E_OUTOFMEMORY;
1538     lstrcpyW( This->sWorkDir, pszDir );
1539     This->bDirty = TRUE;
1540
1541     return S_OK;
1542 }
1543
1544 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1545 {
1546     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1547
1548     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1549
1550     if( cchMaxPath )
1551         pszArgs[0] = 0;
1552     if( This->sArgs )
1553         lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
1554
1555     return NOERROR;
1556 }
1557
1558 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1559 {
1560     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1561
1562     TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1563
1564     if (This->sArgs)
1565         HeapFree(GetProcessHeap(), 0, This->sArgs);
1566     This->sArgs = HeapAlloc( GetProcessHeap(), 0,
1567                              (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
1568     if ( !This->sArgs )
1569         return E_OUTOFMEMORY;
1570     lstrcpyW( This->sArgs, pszArgs );
1571     This->bDirty = TRUE;
1572
1573     return S_OK;
1574 }
1575
1576 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1577 {
1578     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1579
1580     TRACE("(%p)->(%p)\n",This, pwHotkey);
1581
1582     *pwHotkey=This->wHotKey;
1583
1584     return S_OK;
1585 }
1586
1587 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1588 {
1589     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1590
1591     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1592
1593     This->wHotKey = wHotkey;
1594     This->bDirty = TRUE;
1595
1596     return S_OK;
1597 }
1598
1599 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1600 {
1601     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1602
1603     TRACE("(%p)->(%p)\n",This, piShowCmd);
1604
1605     *piShowCmd = This->iShowCmd;
1606
1607     return S_OK;
1608 }
1609
1610 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1611 {
1612     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1613
1614     This->iShowCmd = iShowCmd;
1615     This->bDirty = TRUE;
1616
1617     return S_OK;
1618 }
1619
1620 static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPITEMIDLIST pidl, LPWSTR pszIconPath, int cchIconPath, int* piIcon)
1621 {
1622     LPCITEMIDLIST pidlLast;
1623
1624     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1625
1626     if (SUCCEEDED(hr)) {
1627         IExtractIconW* pei;
1628
1629         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconW, NULL, (LPVOID*)&pei);
1630
1631         if (SUCCEEDED(hr)) {
1632             hr = IExtractIconW_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1633
1634             IExtractIconW_Release(pei);
1635         }
1636
1637         IShellFolder_Release(psf);
1638     }
1639
1640     return hr;
1641 }
1642
1643 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1644 {
1645     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1646
1647     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1648
1649     if (cchIconPath)
1650         pszIconPath[0] = 0;
1651
1652     if (This->sIcoPath) {
1653         lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
1654         *piIcon = This->iIcoNdx;
1655         return S_OK;
1656     }
1657
1658     if (This->pPidl || This->sPath) {
1659         IShellFolder* pdsk;
1660
1661         HRESULT hr = SHGetDesktopFolder(&pdsk);
1662
1663         if (SUCCEEDED(hr)) {
1664             /* first look for an icon using the PIDL (if present) */
1665             if (This->pPidl)
1666                 hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1667             else
1668                 hr = E_FAIL;
1669
1670             /* if we couldn't find an icon yet, look for it using the file system path */
1671             if (FAILED(hr) && This->sPath) {
1672                 LPITEMIDLIST pidl;
1673
1674                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1675
1676                 if (SUCCEEDED(hr)) {
1677                     hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1678
1679                     SHFree(pidl);
1680                 }
1681             }
1682
1683             IShellFolder_Release(pdsk);
1684         }
1685
1686         return hr;
1687     } else
1688         return E_FAIL;
1689 }
1690
1691 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
1692 {
1693     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1694
1695     TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1696
1697     if (This->sIcoPath)
1698         HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1699     This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
1700                                 (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
1701     if ( !This->sIcoPath )
1702         return E_OUTOFMEMORY;
1703     lstrcpyW( This->sIcoPath, pszIconPath );
1704
1705     This->iIcoNdx = iIcon;
1706     This->bDirty = TRUE;
1707
1708     return S_OK;
1709 }
1710
1711 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
1712 {
1713     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1714
1715     TRACE("(%p)->(path=%s %lx)\n",This, debugstr_w(pszPathRel), dwReserved);
1716
1717     if (This->sPathRel)
1718         HeapFree(GetProcessHeap(), 0, This->sPathRel);
1719     This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
1720                                 (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
1721     if ( !This->sPathRel )
1722         return E_OUTOFMEMORY;
1723     lstrcpyW( This->sPathRel, pszPathRel );
1724     This->bDirty = TRUE;
1725
1726     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1727 }
1728
1729 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
1730 {
1731     HRESULT hr = S_OK;
1732
1733     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1734
1735     FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1736
1737     /*FIXME: use IResolveShellLink interface */
1738
1739     if (!This->sPath && This->pPidl) {
1740         WCHAR buffer[MAX_PATH];
1741
1742         hr = SHELL_GetPathFromIDListW(This->pPidl, buffer, MAX_PATH);
1743
1744         if (SUCCEEDED(hr) && *buffer) {
1745             This->sPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
1746             if (!This->sPath)
1747                 return E_OUTOFMEMORY;
1748
1749             lstrcpyW(This->sPath, buffer);
1750
1751             This->bDirty = TRUE;
1752         } else
1753             hr = S_OK;    /* don't report an error occurred while just caching information */
1754     }
1755
1756     if (!This->sIcoPath && This->sPath) {
1757         This->sIcoPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
1758         if (!This->sIcoPath)
1759             return E_OUTOFMEMORY;
1760
1761         lstrcpyW(This->sIcoPath, This->sPath);
1762         This->iIcoNdx = 0;
1763
1764         This->bDirty = TRUE;
1765     }
1766
1767     return hr;
1768 }
1769
1770 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
1771 {
1772     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1773     WCHAR buffer[MAX_PATH];
1774     LPWSTR fname;
1775
1776     TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
1777
1778     if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
1779         return E_FAIL;
1780
1781     if (This->sPath)
1782         HeapFree(GetProcessHeap(), 0, This->sPath);
1783
1784     This->sPath = HeapAlloc( GetProcessHeap(), 0,
1785                              (lstrlenW( buffer )+1) * sizeof (WCHAR) );
1786     if (!This->sPath)
1787         return E_OUTOFMEMORY;
1788
1789     lstrcpyW(This->sPath, buffer);
1790     This->bDirty = TRUE;
1791
1792     return S_OK;
1793 }
1794
1795 /**************************************************************************
1796 * IShellLinkW Implementation
1797 */
1798
1799 static IShellLinkWVtbl slvtw =
1800 {
1801         IShellLinkW_fnQueryInterface,
1802         IShellLinkW_fnAddRef,
1803         IShellLinkW_fnRelease,
1804         IShellLinkW_fnGetPath,
1805         IShellLinkW_fnGetIDList,
1806         IShellLinkW_fnSetIDList,
1807         IShellLinkW_fnGetDescription,
1808         IShellLinkW_fnSetDescription,
1809         IShellLinkW_fnGetWorkingDirectory,
1810         IShellLinkW_fnSetWorkingDirectory,
1811         IShellLinkW_fnGetArguments,
1812         IShellLinkW_fnSetArguments,
1813         IShellLinkW_fnGetHotkey,
1814         IShellLinkW_fnSetHotkey,
1815         IShellLinkW_fnGetShowCmd,
1816         IShellLinkW_fnSetShowCmd,
1817         IShellLinkW_fnGetIconLocation,
1818         IShellLinkW_fnSetIconLocation,
1819         IShellLinkW_fnSetRelativePath,
1820         IShellLinkW_fnResolve,
1821         IShellLinkW_fnSetPath
1822 };