Fix obviously wrong condition in an "if" statement.
[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     return NOERROR;
1031 }
1032
1033 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
1034 {
1035         IShellLinkImpl *This = (IShellLinkImpl *)iface;
1036
1037         TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1038
1039         *ppidl = ILClone(This->pPidl);
1040
1041         return NOERROR;
1042 }
1043
1044 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
1045 {
1046     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1047
1048     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1049
1050     if (This->pPidl)
1051         ILFree(This->pPidl);
1052     This->pPidl = ILClone (pidl);
1053     This->bDirty = TRUE;
1054
1055     return S_OK;
1056 }
1057
1058 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
1059 {
1060     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1061
1062     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1063
1064     if( cchMaxName )
1065         pszName[0] = 0;
1066     if( This->sDescription )
1067         WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
1068             pszName, cchMaxName, NULL, NULL);
1069
1070     return S_OK;
1071 }
1072 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
1073 {
1074     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1075
1076     TRACE("(%p)->(pName=%s)\n", This, pszName);
1077
1078     if (This->sDescription)
1079         HeapFree(GetProcessHeap(), 0, This->sDescription);
1080     This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
1081     if ( !This->sDescription )
1082         return E_OUTOFMEMORY;
1083
1084     This->bDirty = TRUE;
1085
1086     return S_OK;
1087 }
1088
1089 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
1090 {
1091     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1092
1093     TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1094
1095     if( cchMaxPath )
1096         pszDir[0] = 0;
1097     if( This->sWorkDir )
1098         WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
1099                              pszDir, cchMaxPath, NULL, NULL);
1100
1101     return S_OK;
1102 }
1103
1104 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
1105 {
1106     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1107
1108     TRACE("(%p)->(dir=%s)\n",This, pszDir);
1109
1110     if (This->sWorkDir)
1111         HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1112     This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
1113     if ( !This->sWorkDir )
1114         return E_OUTOFMEMORY;
1115
1116     This->bDirty = TRUE;
1117
1118     return S_OK;
1119 }
1120
1121 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
1122 {
1123     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1124
1125     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1126
1127     if( cchMaxPath )
1128         pszArgs[0] = 0;
1129     if( This->sArgs )
1130         WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
1131                              pszArgs, cchMaxPath, NULL, NULL);
1132
1133     return S_OK;
1134 }
1135
1136 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
1137 {
1138     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1139
1140     TRACE("(%p)->(args=%s)\n",This, pszArgs);
1141
1142     if (This->sArgs)
1143         HeapFree(GetProcessHeap(), 0, This->sArgs);
1144     This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
1145     if( !This->sArgs )
1146         return E_OUTOFMEMORY;
1147
1148     This->bDirty = TRUE;
1149
1150     return S_OK;
1151 }
1152
1153 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
1154 {
1155         IShellLinkImpl *This = (IShellLinkImpl *)iface;
1156
1157         TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1158
1159         *pwHotkey = This->wHotKey;
1160
1161         return S_OK;
1162 }
1163
1164 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
1165 {
1166         IShellLinkImpl *This = (IShellLinkImpl *)iface;
1167
1168         TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1169
1170         This->wHotKey = wHotkey;
1171         This->bDirty = TRUE;
1172
1173         return S_OK;
1174 }
1175
1176 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
1177 {
1178     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1179
1180     TRACE("(%p)->(%p)\n",This, piShowCmd);
1181     *piShowCmd = This->iShowCmd;
1182     return S_OK;
1183 }
1184
1185 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
1186 {
1187     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1188
1189     TRACE("(%p) %d\n",This, iShowCmd);
1190
1191     This->iShowCmd = iShowCmd;
1192     This->bDirty = TRUE;
1193
1194     return NOERROR;
1195 }
1196
1197 static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPITEMIDLIST pidl, LPSTR pszIconPath, int cchIconPath, int* piIcon)
1198 {
1199     LPCITEMIDLIST pidlLast;
1200
1201     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1202
1203     if (SUCCEEDED(hr)) {
1204         IExtractIconA* pei;
1205
1206         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconA, NULL, (LPVOID*)&pei);
1207
1208         if (SUCCEEDED(hr)) {
1209             hr = IExtractIconA_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1210
1211             IExtractIconA_Release(pei);
1212         }
1213
1214         IShellFolder_Release(psf);
1215     }
1216
1217     return hr;
1218 }
1219
1220 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1221 {
1222     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1223
1224     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1225
1226     if (cchIconPath)
1227         pszIconPath[0] = 0;
1228
1229     if (This->sIcoPath) {
1230         WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1231         *piIcon = This->iIcoNdx;
1232         return S_OK;
1233     }
1234
1235     if (This->pPidl || This->sPath) {
1236         IShellFolder* pdsk;
1237
1238         HRESULT hr = SHGetDesktopFolder(&pdsk);
1239
1240         if (SUCCEEDED(hr)) {
1241             /* first look for an icon using the PIDL (if present) */
1242             if (This->pPidl)
1243                 hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1244             else
1245                 hr = E_FAIL;
1246
1247             /* if we couldn't find an icon yet, look for it using the file system path */
1248             if (FAILED(hr) && This->sPath) {
1249                 LPITEMIDLIST pidl;
1250
1251                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1252
1253                 if (SUCCEEDED(hr)) {
1254                     hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1255
1256                     SHFree(pidl);
1257                 }
1258             }
1259
1260             IShellFolder_Release(pdsk);
1261         }
1262
1263         return hr;
1264     } else
1265         return E_FAIL;
1266 }
1267
1268 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
1269 {
1270     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1271
1272     TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1273
1274     if (This->sIcoPath)
1275         HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1276     This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1277     if ( !This->sIcoPath )
1278         return E_OUTOFMEMORY;
1279
1280     This->iIcoNdx = iIcon;
1281     This->bDirty = TRUE;
1282
1283     return S_OK;
1284 }
1285
1286 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1287 {
1288     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1289
1290     FIXME("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved);
1291
1292     if (This->sPathRel)
1293         HeapFree(GetProcessHeap(), 0, This->sPathRel);
1294     This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1295     This->bDirty = TRUE;
1296
1297     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1298 }
1299
1300 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1301 {
1302     HRESULT hr = S_OK;
1303
1304     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1305
1306     FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1307
1308     /*FIXME: use IResolveShellLink interface */
1309
1310     if (!This->sPath && This->pPidl) {
1311         WCHAR buffer[MAX_PATH];
1312
1313         hr = SHELL_GetPathFromIDListW(This->pPidl, buffer, MAX_PATH);
1314
1315         if (SUCCEEDED(hr) && *buffer) {
1316             This->sPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
1317             if (!This->sPath)
1318                 return E_OUTOFMEMORY;
1319
1320             lstrcpyW(This->sPath, buffer);
1321
1322             This->bDirty = TRUE;
1323         } else
1324             hr = S_OK;    /* don't report any error occured while just caching information */
1325     }
1326
1327     if (!This->sIcoPath && This->sPath) {
1328         This->sIcoPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
1329         if (!This->sIcoPath)
1330             return E_OUTOFMEMORY;
1331
1332         lstrcpyW(This->sIcoPath, This->sPath);
1333         This->iIcoNdx = 0;
1334
1335         This->bDirty = TRUE;
1336     }
1337
1338     return hr;
1339 }
1340
1341 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1342 {
1343     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1344     char buffer[MAX_PATH];
1345     LPSTR fname;
1346
1347     TRACE("(%p)->(path=%s)\n",This, pszFile);
1348
1349     if (!GetFullPathNameA(pszFile, MAX_PATH, buffer, &fname))
1350         return E_FAIL;
1351
1352     if (This->sPath)
1353         HeapFree(GetProcessHeap(), 0, This->sPath);
1354
1355     This->sPath = HEAP_strdupAtoW(GetProcessHeap(), 0, buffer);
1356     if( !This->sPath )
1357         return E_OUTOFMEMORY;
1358
1359     This->bDirty = TRUE;
1360
1361     return S_OK;
1362 }
1363
1364 /**************************************************************************
1365 * IShellLink Implementation
1366 */
1367
1368 static IShellLinkAVtbl slvt =
1369 {
1370         IShellLinkA_fnQueryInterface,
1371         IShellLinkA_fnAddRef,
1372         IShellLinkA_fnRelease,
1373         IShellLinkA_fnGetPath,
1374         IShellLinkA_fnGetIDList,
1375         IShellLinkA_fnSetIDList,
1376         IShellLinkA_fnGetDescription,
1377         IShellLinkA_fnSetDescription,
1378         IShellLinkA_fnGetWorkingDirectory,
1379         IShellLinkA_fnSetWorkingDirectory,
1380         IShellLinkA_fnGetArguments,
1381         IShellLinkA_fnSetArguments,
1382         IShellLinkA_fnGetHotkey,
1383         IShellLinkA_fnSetHotkey,
1384         IShellLinkA_fnGetShowCmd,
1385         IShellLinkA_fnSetShowCmd,
1386         IShellLinkA_fnGetIconLocation,
1387         IShellLinkA_fnSetIconLocation,
1388         IShellLinkA_fnSetRelativePath,
1389         IShellLinkA_fnResolve,
1390         IShellLinkA_fnSetPath
1391 };
1392
1393
1394 /**************************************************************************
1395  *  IShellLinkW_fnQueryInterface
1396  */
1397 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1398   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1399 {
1400         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1401
1402         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
1403 }
1404
1405 /******************************************************************************
1406  * IShellLinkW_fnAddRef
1407  */
1408 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1409 {
1410         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1411
1412         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1413
1414         return IShellLinkA_AddRef((IShellLinkA*)This);
1415 }
1416 /******************************************************************************
1417  * IShellLinkW_fnRelease
1418  */
1419
1420 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1421 {
1422         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1423
1424         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1425
1426         return IShellLinkA_Release((IShellLinkA*)This);
1427 }
1428
1429 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1430 {
1431     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1432
1433     FIXME("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)\n",This, pszFile, cchMaxPath, pfd, fFlags);
1434     if( cchMaxPath )
1435         pszFile[0] = 0;
1436     if( This->sPath )
1437         lstrcpynW( pszFile, This->sPath, cchMaxPath );
1438
1439     return NOERROR;
1440 }
1441
1442 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1443 {
1444     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1445
1446     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1447
1448     if( This->pPidl)
1449         *ppidl = ILClone( This->pPidl );
1450     else
1451         *ppidl = NULL;
1452
1453     return S_OK;
1454 }
1455
1456 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1457 {
1458     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1459
1460     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1461
1462     if( This->pPidl )
1463         ILFree( This->pPidl );
1464     This->pPidl = ILClone( pidl );
1465     if( !This->pPidl )
1466         return E_FAIL;
1467
1468     This->bDirty = TRUE;
1469
1470     return S_OK;
1471 }
1472
1473 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1474 {
1475     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1476
1477     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1478
1479     if( cchMaxName )
1480         pszName[0] = 0;
1481     if( This->sDescription )
1482         lstrcpynW( pszName, This->sDescription, cchMaxName );
1483
1484     return S_OK;
1485 }
1486
1487 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1488 {
1489     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1490
1491     TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1492
1493     if (This->sDescription)
1494         HeapFree(GetProcessHeap(), 0, This->sDescription);
1495     This->sDescription = HeapAlloc( GetProcessHeap(), 0,
1496                                     (lstrlenW( pszName )+1)*sizeof(WCHAR) );
1497     if ( !This->sDescription )
1498         return E_OUTOFMEMORY;
1499
1500     lstrcpyW( This->sDescription, pszName );
1501     This->bDirty = TRUE;
1502
1503     return S_OK;
1504 }
1505
1506 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1507 {
1508     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1509
1510     TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1511
1512     if( cchMaxPath )
1513         pszDir[0] = 0;
1514     if( This->sWorkDir )
1515         lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
1516
1517     return S_OK;
1518 }
1519
1520 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1521 {
1522     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1523
1524     TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1525
1526     if (This->sWorkDir)
1527         HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1528     This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
1529                                 (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
1530     if ( !This->sWorkDir )
1531         return E_OUTOFMEMORY;
1532     lstrcpyW( This->sWorkDir, pszDir );
1533     This->bDirty = TRUE;
1534
1535     return S_OK;
1536 }
1537
1538 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1539 {
1540     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1541
1542     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1543
1544     if( cchMaxPath )
1545         pszArgs[0] = 0;
1546     if( This->sArgs )
1547         lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
1548
1549     return NOERROR;
1550 }
1551
1552 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1553 {
1554     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1555
1556     TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1557
1558     if (This->sArgs)
1559         HeapFree(GetProcessHeap(), 0, This->sArgs);
1560     This->sArgs = HeapAlloc( GetProcessHeap(), 0,
1561                              (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
1562     if ( !This->sArgs )
1563         return E_OUTOFMEMORY;
1564     lstrcpyW( This->sArgs, pszArgs );
1565     This->bDirty = TRUE;
1566
1567     return S_OK;
1568 }
1569
1570 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1571 {
1572     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1573
1574     TRACE("(%p)->(%p)\n",This, pwHotkey);
1575
1576     *pwHotkey=This->wHotKey;
1577
1578     return S_OK;
1579 }
1580
1581 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1582 {
1583     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1584
1585     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1586
1587     This->wHotKey = wHotkey;
1588     This->bDirty = TRUE;
1589
1590     return S_OK;
1591 }
1592
1593 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1594 {
1595     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1596
1597     TRACE("(%p)->(%p)\n",This, piShowCmd);
1598
1599     *piShowCmd = This->iShowCmd;
1600
1601     return S_OK;
1602 }
1603
1604 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1605 {
1606     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1607
1608     This->iShowCmd = iShowCmd;
1609     This->bDirty = TRUE;
1610
1611     return S_OK;
1612 }
1613
1614 static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPITEMIDLIST pidl, LPWSTR pszIconPath, int cchIconPath, int* piIcon)
1615 {
1616     LPCITEMIDLIST pidlLast;
1617
1618     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1619
1620     if (SUCCEEDED(hr)) {
1621         IExtractIconW* pei;
1622
1623         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconW, NULL, (LPVOID*)&pei);
1624
1625         if (SUCCEEDED(hr)) {
1626             hr = IExtractIconW_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1627
1628             IExtractIconW_Release(pei);
1629         }
1630
1631         IShellFolder_Release(psf);
1632     }
1633
1634     return hr;
1635 }
1636
1637 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1638 {
1639     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1640
1641     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1642
1643     if (cchIconPath)
1644         pszIconPath[0] = 0;
1645
1646     if (This->sIcoPath) {
1647         lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
1648         *piIcon = This->iIcoNdx;
1649         return S_OK;
1650     }
1651
1652     if (This->pPidl || This->sPath) {
1653         IShellFolder* pdsk;
1654
1655         HRESULT hr = SHGetDesktopFolder(&pdsk);
1656
1657         if (SUCCEEDED(hr)) {
1658             /* first look for an icon using the PIDL (if present) */
1659             if (This->pPidl)
1660                 hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1661             else
1662                 hr = E_FAIL;
1663
1664             /* if we couldn't find an icon yet, look for it using the file system path */
1665             if (FAILED(hr) && This->sPath) {
1666                 LPITEMIDLIST pidl;
1667
1668                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1669
1670                 if (SUCCEEDED(hr)) {
1671                     hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1672
1673                     SHFree(pidl);
1674                 }
1675             }
1676
1677             IShellFolder_Release(pdsk);
1678         }
1679
1680         return hr;
1681     } else
1682         return E_FAIL;
1683 }
1684
1685 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
1686 {
1687     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1688
1689     TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1690
1691     if (This->sIcoPath)
1692         HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1693     This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
1694                                 (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
1695     if ( !This->sIcoPath )
1696         return E_OUTOFMEMORY;
1697     lstrcpyW( This->sIcoPath, pszIconPath );
1698
1699     This->iIcoNdx = iIcon;
1700     This->bDirty = TRUE;
1701
1702     return S_OK;
1703 }
1704
1705 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
1706 {
1707     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1708
1709     TRACE("(%p)->(path=%s %lx)\n",This, debugstr_w(pszPathRel), dwReserved);
1710
1711     if (This->sPathRel)
1712         HeapFree(GetProcessHeap(), 0, This->sPathRel);
1713     This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
1714                                 (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
1715     if ( !This->sPathRel )
1716         return E_OUTOFMEMORY;
1717     lstrcpyW( This->sPathRel, pszPathRel );
1718     This->bDirty = TRUE;
1719
1720     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1721 }
1722
1723 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
1724 {
1725     HRESULT hr = S_OK;
1726
1727     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1728
1729     FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1730
1731     /*FIXME: use IResolveShellLink interface */
1732
1733     if (!This->sPath && This->pPidl) {
1734         WCHAR buffer[MAX_PATH];
1735
1736         hr = SHELL_GetPathFromIDListW(This->pPidl, buffer, MAX_PATH);
1737
1738         if (SUCCEEDED(hr) && *buffer) {
1739             This->sPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
1740             if (!This->sPath)
1741                 return E_OUTOFMEMORY;
1742
1743             lstrcpyW(This->sPath, buffer);
1744
1745             This->bDirty = TRUE;
1746         } else
1747             hr = S_OK;    /* don't report any error occured while just caching information */
1748     }
1749
1750     if (!This->sIcoPath && This->sPath) {
1751         This->sIcoPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
1752         if (!This->sIcoPath)
1753             return E_OUTOFMEMORY;
1754
1755         lstrcpyW(This->sIcoPath, This->sPath);
1756         This->iIcoNdx = 0;
1757
1758         This->bDirty = TRUE;
1759     }
1760
1761     return hr;
1762 }
1763
1764 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
1765 {
1766     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1767     WCHAR buffer[MAX_PATH];
1768     LPWSTR fname;
1769
1770     TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
1771
1772     if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
1773         return E_FAIL;
1774
1775     if (This->sPath)
1776         HeapFree(GetProcessHeap(), 0, This->sPath);
1777
1778     This->sPath = HeapAlloc( GetProcessHeap(), 0,
1779                              (lstrlenW( buffer )+1) * sizeof (WCHAR) );
1780     if (!This->sPath)
1781         return E_OUTOFMEMORY;
1782
1783     lstrcpyW(This->sPath, buffer);
1784     This->bDirty = TRUE;
1785
1786     return S_OK;
1787 }
1788
1789 /**************************************************************************
1790 * IShellLinkW Implementation
1791 */
1792
1793 static IShellLinkWVtbl slvtw =
1794 {
1795         IShellLinkW_fnQueryInterface,
1796         IShellLinkW_fnAddRef,
1797         IShellLinkW_fnRelease,
1798         IShellLinkW_fnGetPath,
1799         IShellLinkW_fnGetIDList,
1800         IShellLinkW_fnSetIDList,
1801         IShellLinkW_fnGetDescription,
1802         IShellLinkW_fnSetDescription,
1803         IShellLinkW_fnGetWorkingDirectory,
1804         IShellLinkW_fnSetWorkingDirectory,
1805         IShellLinkW_fnGetArguments,
1806         IShellLinkW_fnSetArguments,
1807         IShellLinkW_fnGetHotkey,
1808         IShellLinkW_fnSetHotkey,
1809         IShellLinkW_fnGetShowCmd,
1810         IShellLinkW_fnSetShowCmd,
1811         IShellLinkW_fnGetIconLocation,
1812         IShellLinkW_fnSetIconLocation,
1813         IShellLinkW_fnSetRelativePath,
1814         IShellLinkW_fnResolve,
1815         IShellLinkW_fnSetPath
1816 };