Add the IAutoComplete and IAutoComplete2 implementation (but methods
[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 ICOM_VTABLE(IShellLinkA)         slvt;
116 static ICOM_VTABLE(IShellLinkW)         slvtw;
117 static ICOM_VTABLE(IPersistFile)        pfvt;
118 static ICOM_VTABLE(IPersistStream)      psvt;
119
120 /* IShellLink Implementation */
121
122 typedef struct
123 {
124         ICOM_VFIELD(IShellLinkA);
125         DWORD                           ref;
126
127         ICOM_VTABLE(IShellLinkW)*       lpvtblw;
128         ICOM_VTABLE(IPersistFile)*      lpvtblPersistFile;
129         ICOM_VTABLE(IPersistStream)*    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 #define _IPersistStream_From_ICOM_THIS(class, name) class* StreamThis = (class*)(((char*)name)+_IPersistStream_Offset)
159
160 static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath);
161
162 /* strdup on the process heap */
163 inline static LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
164 {
165     INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
166     LPWSTR p = HeapAlloc( heap, flags, len*sizeof (WCHAR) );
167     if( !p )
168         return p;
169     MultiByteToWideChar( CP_ACP, 0, str, -1, p, len );
170     return p;
171 }
172
173
174 /**************************************************************************
175  *  IPersistFile_QueryInterface
176  */
177 static HRESULT WINAPI IPersistFile_fnQueryInterface(
178         IPersistFile* iface,
179         REFIID riid,
180         LPVOID *ppvObj)
181 {
182         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
183
184         TRACE("(%p)\n",This);
185
186         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
187 }
188
189 /******************************************************************************
190  * IPersistFile_AddRef
191  */
192 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
193 {
194         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
195
196         TRACE("(%p)->(count=%lu)\n",This,This->ref);
197
198         return IShellLinkA_AddRef((IShellLinkA*)This);
199 }
200 /******************************************************************************
201  * IPersistFile_Release
202  */
203 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile* iface)
204 {
205         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
206
207         TRACE("(%p)->(count=%lu)\n",This,This->ref);
208
209         return IShellLinkA_Release((IShellLinkA*)This);
210 }
211
212 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
213 {
214         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
215         FIXME("(%p)\n",This);
216         return NOERROR;
217 }
218 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
219 {
220         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
221
222         TRACE("(%p)\n",This);
223
224         if (This->bDirty)
225             return S_OK;
226
227         return S_FALSE;
228 }
229 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
230 {
231         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
232         _IPersistStream_From_ICOM_THIS(IPersistStream, This);
233         HRESULT r;
234         IStream *stm;
235
236         TRACE("(%p, %s)\n",This, debugstr_w(pszFileName));
237
238         r = CreateStreamOnFile(pszFileName, dwMode, &stm);
239         if( SUCCEEDED( r ) )
240         {
241             r = IPersistStream_Load(StreamThis, stm);
242             ShellLink_UpdatePath(This->sPathRel, pszFileName, This->sWorkDir, &This->sPath);
243             IStream_Release( stm );
244             This->bDirty = FALSE;
245         }
246
247         return r;
248 }
249
250 static BOOL StartLinkProcessor( LPCOLESTR szLink )
251 {
252     const WCHAR szFormat[] = {'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
253                               ' ','-','r',' ','"','%','s','"',0 };
254     LONG len;
255     LPWSTR buffer;
256     STARTUPINFOW si;
257     PROCESS_INFORMATION pi;
258
259     len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
260     buffer = HeapAlloc( GetProcessHeap(), 0, len );
261     if( !buffer )
262         return FALSE;
263
264     wsprintfW( buffer, szFormat, szLink );
265
266     TRACE("starting %s\n",debugstr_w(buffer));
267
268     memset(&si, 0, sizeof(si));
269     si.cb = sizeof(si);
270     if (!CreateProcessW( NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) return FALSE;
271
272     /* wait for a while to throttle the creation of linker processes */
273     if( WAIT_OBJECT_0 != WaitForSingleObject( pi.hProcess, 10000 ) )
274         WARN("Timed out waiting for shell linker\n");
275
276     CloseHandle( pi.hProcess );
277     CloseHandle( pi.hThread );
278
279     return TRUE;
280 }
281
282 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
283 {
284     _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
285     _IPersistStream_From_ICOM_THIS(IPersistStream, This);
286     HRESULT r;
287     IStream *stm;
288
289     TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
290
291     if (!pszFileName || !This->sPath)
292         return E_FAIL;
293
294     r = CreateStreamOnFile(pszFileName, STGM_READWRITE | STGM_CREATE, &stm);
295     if( SUCCEEDED( r ) )
296     {
297         r = IPersistStream_Save(StreamThis, stm, FALSE);
298         IStream_Release( stm );
299
300         if( SUCCEEDED( r ) )
301         {
302             StartLinkProcessor( pszFileName );
303
304             This->bDirty = FALSE;
305         }
306         else
307         {
308             DeleteFileW( pszFileName );
309             WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
310         }
311     }
312
313     return r;
314 }
315
316 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
317 {
318         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
319         FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
320         return NOERROR;
321 }
322 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *ppszFileName)
323 {
324         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
325         FIXME("(%p)\n",This);
326         return NOERROR;
327 }
328
329 static ICOM_VTABLE(IPersistFile) pfvt =
330 {
331         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
332         IPersistFile_fnQueryInterface,
333         IPersistFile_fnAddRef,
334         IPersistFile_fnRelease,
335         IPersistFile_fnGetClassID,
336         IPersistFile_fnIsDirty,
337         IPersistFile_fnLoad,
338         IPersistFile_fnSave,
339         IPersistFile_fnSaveCompleted,
340         IPersistFile_fnGetCurFile
341 };
342
343 /************************************************************************
344  * IPersistStream_QueryInterface
345  */
346 static HRESULT WINAPI IPersistStream_fnQueryInterface(
347         IPersistStream* iface,
348         REFIID     riid,
349         VOID**     ppvoid)
350 {
351         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
352
353         TRACE("(%p)\n",This);
354
355         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvoid);
356 }
357
358 /************************************************************************
359  * IPersistStream_Release
360  */
361 static ULONG WINAPI IPersistStream_fnRelease(
362         IPersistStream* iface)
363 {
364         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
365
366         TRACE("(%p)\n",This);
367
368         return IShellLinkA_Release((IShellLinkA*)This);
369 }
370
371 /************************************************************************
372  * IPersistStream_AddRef
373  */
374 static ULONG WINAPI IPersistStream_fnAddRef(
375         IPersistStream* iface)
376 {
377         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
378
379         TRACE("(%p)\n",This);
380
381         return IShellLinkA_AddRef((IShellLinkA*)This);
382 }
383
384 /************************************************************************
385  * IPersistStream_GetClassID
386  *
387  */
388 static HRESULT WINAPI IPersistStream_fnGetClassID(
389         IPersistStream* iface,
390         CLSID* pClassID)
391 {
392         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
393
394         TRACE("(%p)\n", This);
395
396         if (pClassID==0)
397           return E_POINTER;
398
399 /*      memcpy(pClassID, &CLSID_???, sizeof(CLSID_???)); */
400
401         return S_OK;
402 }
403
404 /************************************************************************
405  * IPersistStream_IsDirty (IPersistStream)
406  */
407 static HRESULT WINAPI IPersistStream_fnIsDirty(
408         IPersistStream*  iface)
409 {
410         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
411
412         TRACE("(%p)\n", This);
413
414         return S_OK;
415 }
416
417
418 static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
419 {
420     DWORD count;
421     USHORT len;
422     LPVOID temp;
423     LPWSTR str;
424     HRESULT r;
425
426     TRACE("%p\n", stm);
427
428     count = 0;
429     r = IStream_Read(stm, &len, sizeof(len), &count);
430     if ( FAILED (r) || ( count != sizeof(len) ) )
431         return E_FAIL;
432
433     if( unicode )
434         len *= sizeof (WCHAR);
435
436     TRACE("reading %d\n", len);
437     temp = HeapAlloc(GetProcessHeap(), 0, len+sizeof(WCHAR));
438     if( !temp )
439         return E_OUTOFMEMORY;
440     count = 0;
441     r = IStream_Read(stm, temp, len, &count);
442     if( FAILED (r) || ( count != len ) )
443     {
444         HeapFree( GetProcessHeap(), 0, temp );
445         return E_FAIL;
446     }
447
448     TRACE("read %s\n", debugstr_an(temp,len));
449
450     /* convert to unicode if necessary */
451     if( !unicode )
452     {
453         count = MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, NULL, 0 );
454         str = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof (WCHAR) );
455         if( str )
456             MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, str, count );
457         HeapFree( GetProcessHeap(), 0, temp );
458     }
459     else
460     {
461         count /= 2;
462         str = (LPWSTR) temp;
463     }
464     str[count] = 0;
465
466     *pstr = str;
467
468     return S_OK;
469 }
470
471 static HRESULT Stream_LoadLocation( IStream* stm )
472 {
473     DWORD size;
474     ULONG count;
475     HRESULT r;
476     LOCATION_INFO *loc;
477
478     TRACE("%p\n",stm);
479
480     r = IStream_Read( stm, &size, sizeof(size), &count );
481     if( FAILED( r ) )
482         return r;
483     if( count != sizeof(loc->dwTotalSize) )
484         return E_FAIL;
485
486     loc = HeapAlloc( GetProcessHeap(), 0, size );
487     if( ! loc )
488         return E_OUTOFMEMORY;
489
490     r = IStream_Read( stm, &loc->dwHeaderSize, size-sizeof(size), &count );
491     if( FAILED( r ) )
492         goto end;
493     if( count != (size - sizeof(size)) )
494     {
495         r = E_FAIL;
496         goto end;
497     }
498     loc->dwTotalSize = size;
499
500     TRACE("Read %ld bytes\n",count);
501
502     /* FIXME: do something useful with it */
503     HeapFree( GetProcessHeap(), 0, loc );
504
505     return S_OK;
506 end:
507     HeapFree( GetProcessHeap(), 0, loc );
508     return r;
509 }
510
511 /************************************************************************
512  * IPersistStream_Load (IPersistStream)
513  */
514 static HRESULT WINAPI IPersistStream_fnLoad(
515         IPersistStream*  iface,
516     IStream*         stm)
517 {
518     LINK_HEADER hdr;
519     ULONG    dwBytesRead;
520     BOOL     unicode;
521     WCHAR    sTemp[MAX_PATH];
522     HRESULT  r;
523
524     _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
525
526     TRACE("(%p)(%p)\n", This, stm);
527
528     if( !stm )
529           return STG_E_INVALIDPOINTER;
530
531     dwBytesRead = 0;
532     r = IStream_Read(stm, &hdr, sizeof(hdr), &dwBytesRead);
533     if( FAILED( r ) )
534         return r;
535
536     if( dwBytesRead != sizeof(hdr))
537         return E_FAIL;
538     if( hdr.dwSize != sizeof(hdr))
539         return E_FAIL;
540     if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
541         return E_FAIL;
542
543     /* if( hdr.dwFlags & SCF_PIDL ) */  /* FIXME: seems to always have a PIDL */
544     {
545         r = ILLoadFromStream( stm, &This->pPidl );
546         if( FAILED( r ) )
547             return r;
548     }
549     This->wHotKey = (WORD)hdr.wHotKey;
550     This->iIcoNdx = hdr.nIcon;
551     FileTimeToSystemTime (&hdr.Time1, &This->time1);
552     FileTimeToSystemTime (&hdr.Time2, &This->time2);
553     FileTimeToSystemTime (&hdr.Time3, &This->time3);
554 #if 1
555     GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time1, NULL, sTemp, 256);
556     TRACE("-- time1: %s\n", debugstr_w(sTemp) );
557     GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time2, NULL, sTemp, 256);
558     TRACE("-- time1: %s\n", debugstr_w(sTemp) );
559     GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time3, NULL, sTemp, 256);
560     TRACE("-- time1: %s\n", debugstr_w(sTemp) );
561     pdump (This->pPidl);
562 #endif
563     if( hdr.dwFlags & SCF_NORMAL )
564         r = Stream_LoadLocation( stm );
565     if( FAILED( r ) )
566         goto end;
567     unicode = hdr.dwFlags & SCF_UNICODE;
568     if( hdr.dwFlags & SCF_DESCRIPTION )
569     {
570         r = Stream_LoadString( stm, unicode, &This->sDescription );
571         TRACE("Description  -> %s\n",debugstr_w(This->sDescription));
572     }
573     if( FAILED( r ) )
574         goto end;
575
576     if( hdr.dwFlags & SCF_RELATIVE )
577     {
578         r = Stream_LoadString( stm, unicode, &This->sPathRel );
579         TRACE("Relative Path-> %s\n",debugstr_w(This->sPathRel));
580     }
581     if( FAILED( r ) )
582         goto end;
583
584     if( hdr.dwFlags & SCF_WORKDIR )
585           {
586         r = Stream_LoadString( stm, unicode, &This->sWorkDir );
587         TRACE("Working Dir  -> %s\n",debugstr_w(This->sWorkDir));
588     }
589     if( FAILED( r ) )
590         goto end;
591
592     if( hdr.dwFlags & SCF_ARGS )
593     {
594         r = Stream_LoadString( stm, unicode, &This->sArgs );
595         TRACE("Working Dir  -> %s\n",debugstr_w(This->sArgs));
596     }
597     if( FAILED( r ) )
598         goto end;
599
600     if( hdr.dwFlags & SCF_CUSTOMICON )
601     {
602         r = Stream_LoadString( stm, unicode, &This->sIcoPath );
603         TRACE("Icon file    -> %s\n",debugstr_w(This->sIcoPath));
604     }
605     if( FAILED( r ) )
606         goto end;
607
608     TRACE("OK\n");
609
610     pdump (This->pPidl);
611
612     return S_OK;
613 end:
614     return r;
615 }
616
617 /************************************************************************
618  * Stream_WriteString
619  *
620  * Helper function for IPersistStream_Save. Writes a unicode string 
621  *  with terminating nul byte to a stream, preceded by the its length.
622  */
623 static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
624 {
625     USHORT len = lstrlenW( str ) + 1;
626     DWORD count;
627     HRESULT r;
628
629     r = IStream_Write( stm, &len, sizeof(len), &count );
630     if( FAILED( r ) )
631         return r;
632
633     len *= sizeof(WCHAR);
634
635     r = IStream_Write( stm, str, len, &count );
636     if( FAILED( r ) )
637         return r;
638
639     return S_OK;
640 }
641
642 static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR filename )
643 {
644     LOCATION_INFO loc;
645     ULONG count;
646
647     FIXME("writing empty location info\n");
648
649     memset( &loc, 0, sizeof(loc) );
650     loc.dwTotalSize = sizeof(loc) - sizeof(loc.dwTotalSize);
651
652     /* FIXME: fill this in */
653
654     return IStream_Write( stm, &loc, loc.dwTotalSize, &count );
655 }
656
657 /************************************************************************
658  * IPersistStream_Save (IPersistStream)
659  *
660  * FIXME: makes assumptions about byte order
661  */
662 static HRESULT WINAPI IPersistStream_fnSave(
663         IPersistStream*  iface,
664         IStream*         stm,
665         BOOL             fClearDirty)
666 {
667     static const WCHAR wOpen[] = {'o','p','e','n',0};
668
669     LINK_HEADER header;
670     WCHAR   exePath[MAX_PATH];
671     ULONG   count;
672     HRESULT r;
673
674     _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
675
676     TRACE("(%p) %p %x\n", This, stm, fClearDirty);
677
678     *exePath = '\0';
679
680     if (This->sPath)
681         SHELL_FindExecutable(NULL, This->sPath, wOpen, exePath, MAX_PATH, NULL, NULL, NULL, NULL);
682
683     /* if there's no PIDL, generate one */
684     if( ! This->pPidl )
685     {
686         if( !*exePath )
687             return E_FAIL;
688
689         This->pPidl = ILCreateFromPathW(exePath);
690     }
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 ICOM_VTABLE(IPersistStream) psvt =
772 {
773         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
774         IPersistStream_fnQueryInterface,
775         IPersistStream_fnAddRef,
776         IPersistStream_fnRelease,
777         IPersistStream_fnGetClassID,
778         IPersistStream_fnIsDirty,
779         IPersistStream_fnLoad,
780         IPersistStream_fnSave,
781         IPersistStream_fnGetSizeMax
782 };
783
784 /**************************************************************************
785  *        IShellLink_Constructor
786  */
787 HRESULT WINAPI IShellLink_Constructor (
788         IUnknown * pUnkOuter,
789         REFIID riid,
790         LPVOID * ppv)
791 {
792         IShellLinkImpl * sl;
793
794         TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
795
796         *ppv = NULL;
797
798         if(pUnkOuter) return CLASS_E_NOAGGREGATION;
799         sl = (IShellLinkImpl *) LocalAlloc(GMEM_ZEROINIT,sizeof(IShellLinkImpl));
800         if (!sl) return E_OUTOFMEMORY;
801
802         sl->ref = 1;
803         sl->lpVtbl = &slvt;
804         sl->lpvtblw = &slvtw;
805         sl->lpvtblPersistFile = &pfvt;
806         sl->lpvtblPersistStream = &psvt;
807         sl->iShowCmd = SW_SHOWNORMAL;
808         sl->bDirty = FALSE;
809
810         TRACE("(%p)->()\n",sl);
811
812         if (IsEqualIID(riid, &IID_IUnknown) ||
813             IsEqualIID(riid, &IID_IShellLinkA))
814             *ppv = sl;
815         else if (IsEqualIID(riid, &IID_IShellLinkW))
816             *ppv = &(sl->lpvtblw);
817         else {
818             LocalFree((HLOCAL)sl);
819             ERR("E_NOINTERFACE\n");
820             return E_NOINTERFACE;
821         }
822
823         return S_OK;
824 }
825
826
827 static BOOL SHELL_ExistsFileW(LPCWSTR path)
828 {
829     HANDLE hfile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
830
831     if (hfile != INVALID_HANDLE_VALUE) {
832         CloseHandle(hfile);
833         return TRUE;
834     } else
835         return FALSE;
836 }
837
838 /**************************************************************************
839  *  ShellLink_UpdatePath
840  *      update absolute path in sPath using relative path in sPathRel
841  */
842 static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
843 {
844     if (!path || !psPath)
845         return E_INVALIDARG;
846
847     if (!*psPath && sPathRel) {
848         WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
849         LPWSTR final = NULL;
850
851         /* first try if [directory of link file] + [relative path] finds an existing file */
852
853         GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
854         if( !final )
855             final = buffer;
856         lstrcpyW(final, sPathRel);
857
858         *abs_path = '\0';
859
860         if (SHELL_ExistsFileW(buffer)) {
861             if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
862                 lstrcpyW(abs_path, buffer);
863         } else {
864             /* try if [working directory] + [relative path] finds an existing file */
865             if (sWorkDir) {
866                 lstrcpyW(buffer, sWorkDir);
867                 lstrcpyW(PathAddBackslashW(buffer), sPathRel);
868
869                 if (SHELL_ExistsFileW(buffer))
870                     if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
871                         lstrcpyW(abs_path, buffer);
872             }
873         }
874
875         /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
876         if (!*abs_path)
877             lstrcpyW(abs_path, sPathRel);
878
879         *psPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(abs_path)+1)*sizeof(WCHAR));
880         if (!*psPath)
881             return E_OUTOFMEMORY;
882
883         lstrcpyW(*psPath, abs_path);
884     }
885
886     return S_OK;
887 }
888
889 /**************************************************************************
890  *        IShellLink_ConstructFromFile
891  */
892 HRESULT WINAPI IShellLink_ConstructFromFile (
893         IUnknown* pUnkOuter,
894         REFIID riid,
895         LPCITEMIDLIST pidl,
896         LPVOID* ppv
897 )
898 {
899     IShellLinkW* psl;
900
901     HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl);
902
903     if (SUCCEEDED(hr)) {
904         IPersistFile* ppf;
905
906         *ppv = NULL;
907
908         hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
909
910         if (SUCCEEDED(hr)) {
911             WCHAR path[MAX_PATH];
912
913             if (SHGetPathFromIDListW(pidl, path)) 
914                 hr = IPersistFile_Load(ppf, path, 0);
915             else
916                 hr = E_FAIL;
917
918             if (SUCCEEDED(hr))
919                 *ppv = (IUnknown*) psl;
920
921             IPersistFile_Release(ppf);
922         }
923
924         if (!*ppv)
925             IShellLinkW_Release(psl);
926     }
927
928     return hr;
929 }
930
931 /**************************************************************************
932  *  IShellLinkA_QueryInterface
933  */
934 static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
935 {
936         ICOM_THIS(IShellLinkImpl, iface);
937
938         TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
939
940         *ppvObj = NULL;
941
942         if(IsEqualIID(riid, &IID_IUnknown) ||
943            IsEqualIID(riid, &IID_IShellLinkA))
944         {
945           *ppvObj = This;
946         }
947         else if(IsEqualIID(riid, &IID_IShellLinkW))
948         {
949           *ppvObj = (IShellLinkW *)&(This->lpvtblw);
950         }
951         else if(IsEqualIID(riid, &IID_IPersistFile))
952         {
953           *ppvObj = (IPersistFile *)&(This->lpvtblPersistFile);
954         }
955         else if(IsEqualIID(riid, &IID_IPersistStream))
956         {
957           *ppvObj = (IPersistStream *)&(This->lpvtblPersistStream);
958         }
959
960         if(*ppvObj)
961         {
962           IUnknown_AddRef((IUnknown*)(*ppvObj));
963           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
964           return S_OK;
965         }
966         TRACE("-- Interface: E_NOINTERFACE\n");
967         return E_NOINTERFACE;
968 }
969 /******************************************************************************
970  * IShellLinkA_AddRef
971  */
972 static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
973 {
974         ICOM_THIS(IShellLinkImpl, iface);
975
976         TRACE("(%p)->(count=%lu)\n",This,This->ref);
977
978         return ++(This->ref);
979 }
980 /******************************************************************************
981  *      IShellLinkA_Release
982  */
983 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
984 {
985     ICOM_THIS(IShellLinkImpl, iface);
986
987     TRACE("(%p)->(count=%lu)\n",This,This->ref);
988
989     if (--(This->ref))
990         return This->ref;
991
992     TRACE("-- destroying IShellLink(%p)\n",This);
993
994     if (This->sIcoPath)
995         HeapFree(GetProcessHeap(), 0, This->sIcoPath);
996
997     if (This->sArgs)
998         HeapFree(GetProcessHeap(), 0, This->sArgs);
999
1000     if (This->sWorkDir)
1001         HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1002
1003     if (This->sDescription)
1004         HeapFree(GetProcessHeap(), 0, This->sDescription);
1005
1006     if (This->sPath)
1007         HeapFree(GetProcessHeap(),0,This->sPath);
1008
1009     if (This->pPidl)
1010         ILFree(This->pPidl);
1011
1012     LocalFree((HANDLE)This);
1013
1014     return 0;
1015 }
1016
1017 static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
1018                   INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1019 {
1020     ICOM_THIS(IShellLinkImpl, iface);
1021
1022     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
1023           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1024
1025     if( cchMaxPath )
1026         pszFile[0] = 0;
1027     if (This->sPath)
1028         WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
1029                              pszFile, cchMaxPath, NULL, NULL);
1030
1031     return NOERROR;
1032 }
1033
1034 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
1035 {
1036         ICOM_THIS(IShellLinkImpl, iface);
1037
1038         TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1039
1040         *ppidl = ILClone(This->pPidl);
1041
1042         return NOERROR;
1043 }
1044
1045 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
1046 {
1047     ICOM_THIS(IShellLinkImpl, iface);
1048
1049     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1050
1051     if (This->pPidl)
1052         ILFree(This->pPidl);
1053     This->pPidl = ILClone (pidl);
1054     This->bDirty = TRUE;
1055
1056     return S_OK;
1057 }
1058
1059 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
1060 {
1061     ICOM_THIS(IShellLinkImpl, iface);
1062
1063     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1064
1065     if( cchMaxName )
1066         pszName[0] = 0;
1067     if( This->sDescription )
1068         WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
1069             pszName, cchMaxName, NULL, NULL);
1070
1071     return S_OK;
1072 }
1073 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
1074 {
1075     ICOM_THIS(IShellLinkImpl, iface);
1076
1077     TRACE("(%p)->(pName=%s)\n", This, pszName);
1078
1079     if (This->sDescription)
1080         HeapFree(GetProcessHeap(), 0, This->sDescription);
1081     This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
1082     if ( !This->sDescription )
1083         return E_OUTOFMEMORY;
1084
1085     This->bDirty = TRUE;
1086
1087     return S_OK;
1088 }
1089
1090 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
1091 {
1092     ICOM_THIS(IShellLinkImpl, iface);
1093
1094     TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1095
1096     if( cchMaxPath )
1097         pszDir[0] = 0;
1098     if( This->sWorkDir )
1099         WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
1100                              pszDir, cchMaxPath, NULL, NULL);
1101
1102     return S_OK;
1103 }
1104
1105 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
1106 {
1107     ICOM_THIS(IShellLinkImpl, iface);
1108
1109     TRACE("(%p)->(dir=%s)\n",This, pszDir);
1110
1111     if (This->sWorkDir)
1112         HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1113     This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
1114     if ( !This->sWorkDir )
1115         return E_OUTOFMEMORY;
1116
1117     This->bDirty = TRUE;
1118
1119     return S_OK;
1120 }
1121
1122 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
1123 {
1124     ICOM_THIS(IShellLinkImpl, iface);
1125
1126     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1127
1128     if( cchMaxPath )
1129         pszArgs[0] = 0;
1130     if( This->sArgs )
1131         WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
1132                              pszArgs, cchMaxPath, NULL, NULL);
1133
1134     return S_OK;
1135 }
1136
1137 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
1138 {
1139     ICOM_THIS(IShellLinkImpl, iface);
1140
1141     TRACE("(%p)->(args=%s)\n",This, pszArgs);
1142
1143     if (This->sArgs)
1144         HeapFree(GetProcessHeap(), 0, This->sArgs);
1145     This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
1146     if( !This->sArgs )
1147         return E_OUTOFMEMORY;
1148
1149     This->bDirty = TRUE;
1150
1151     return S_OK;
1152 }
1153
1154 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
1155 {
1156         ICOM_THIS(IShellLinkImpl, iface);
1157
1158         TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1159
1160         *pwHotkey = This->wHotKey;
1161
1162         return S_OK;
1163 }
1164
1165 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
1166 {
1167         ICOM_THIS(IShellLinkImpl, iface);
1168
1169         TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1170
1171         This->wHotKey = wHotkey;
1172         This->bDirty = TRUE;
1173
1174         return S_OK;
1175 }
1176
1177 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
1178 {
1179     ICOM_THIS(IShellLinkImpl, iface);
1180
1181     TRACE("(%p)->(%p)\n",This, piShowCmd);
1182     *piShowCmd = This->iShowCmd;
1183     return S_OK;
1184 }
1185
1186 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
1187 {
1188     ICOM_THIS(IShellLinkImpl, iface);
1189
1190     TRACE("(%p) %d\n",This, iShowCmd);
1191
1192     This->iShowCmd = iShowCmd;
1193     This->bDirty = TRUE;
1194
1195     return NOERROR;
1196 }
1197
1198 static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPITEMIDLIST pidl, LPSTR pszIconPath, int cchIconPath, int* piIcon)
1199 {
1200     LPCITEMIDLIST pidlLast;
1201
1202     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1203
1204     if (SUCCEEDED(hr)) {
1205         IExtractIconA* pei;
1206
1207         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconA, NULL, (LPVOID*)&pei);
1208
1209         if (SUCCEEDED(hr)) {
1210             hr = IExtractIconA_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1211
1212             IExtractIconA_Release(pei);
1213         }
1214
1215         IShellFolder_Release(psf);
1216     }
1217
1218     return hr;
1219 }
1220
1221 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1222 {
1223     ICOM_THIS(IShellLinkImpl, iface);
1224
1225     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1226
1227     if (cchIconPath)
1228         pszIconPath[0] = 0;
1229
1230     if (This->sIcoPath) {
1231         WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1232         *piIcon = This->iIcoNdx;
1233         return S_OK;
1234     }
1235
1236     if (This->pPidl || This->sPath) {
1237         IShellFolder* pdsk;
1238
1239         HRESULT hr = SHGetDesktopFolder(&pdsk);
1240
1241         if (SUCCEEDED(hr)) {
1242             /* first look for an icon using the PIDL (if present) */
1243             if (This->pPidl)
1244                 hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1245             else
1246                 hr = E_FAIL;
1247
1248             /* if we couldn't find an icon yet, look for it using the file system path */
1249             if (FAILED(hr) && This->sPath) {
1250                 LPITEMIDLIST pidl;
1251
1252                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1253
1254                 if (SUCCEEDED(hr)) {
1255                     hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1256
1257                     SHFree(pidl);
1258                 }
1259             }
1260
1261             IShellFolder_Release(pdsk);
1262         }
1263
1264         return hr;
1265     } else
1266         return E_FAIL;
1267 }
1268
1269 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
1270 {
1271     ICOM_THIS(IShellLinkImpl, iface);
1272
1273     TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1274
1275     if (This->sIcoPath)
1276         HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1277     This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1278     if ( !This->sIcoPath )
1279         return E_OUTOFMEMORY;
1280
1281     This->iIcoNdx = iIcon;
1282     This->bDirty = TRUE;
1283
1284     return S_OK;
1285 }
1286
1287 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1288 {
1289     ICOM_THIS(IShellLinkImpl, iface);
1290
1291     FIXME("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved);
1292
1293     if (This->sPathRel)
1294         HeapFree(GetProcessHeap(), 0, This->sPathRel);
1295     This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1296     This->bDirty = TRUE;
1297
1298     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1299 }
1300
1301 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1302 {
1303         ICOM_THIS(IShellLinkImpl, iface);
1304
1305         FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1306         return S_OK;
1307 }
1308
1309 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1310 {
1311     ICOM_THIS(IShellLinkImpl, iface);
1312
1313     TRACE("(%p)->(path=%s)\n",This, pszFile);
1314
1315     if (This->sPath)
1316         HeapFree(GetProcessHeap(), 0, This->sPath);
1317     This->sPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
1318     if( !This->sPath )
1319         return E_OUTOFMEMORY;
1320
1321     This->bDirty = TRUE;
1322
1323     return S_OK;
1324 }
1325
1326 /**************************************************************************
1327 * IShellLink Implementation
1328 */
1329
1330 static ICOM_VTABLE(IShellLinkA) slvt =
1331 {
1332         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1333         IShellLinkA_fnQueryInterface,
1334         IShellLinkA_fnAddRef,
1335         IShellLinkA_fnRelease,
1336         IShellLinkA_fnGetPath,
1337         IShellLinkA_fnGetIDList,
1338         IShellLinkA_fnSetIDList,
1339         IShellLinkA_fnGetDescription,
1340         IShellLinkA_fnSetDescription,
1341         IShellLinkA_fnGetWorkingDirectory,
1342         IShellLinkA_fnSetWorkingDirectory,
1343         IShellLinkA_fnGetArguments,
1344         IShellLinkA_fnSetArguments,
1345         IShellLinkA_fnGetHotkey,
1346         IShellLinkA_fnSetHotkey,
1347         IShellLinkA_fnGetShowCmd,
1348         IShellLinkA_fnSetShowCmd,
1349         IShellLinkA_fnGetIconLocation,
1350         IShellLinkA_fnSetIconLocation,
1351         IShellLinkA_fnSetRelativePath,
1352         IShellLinkA_fnResolve,
1353         IShellLinkA_fnSetPath
1354 };
1355
1356
1357 /**************************************************************************
1358  *  IShellLinkW_fnQueryInterface
1359  */
1360 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1361   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1362 {
1363         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1364
1365         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
1366 }
1367
1368 /******************************************************************************
1369  * IShellLinkW_fnAddRef
1370  */
1371 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1372 {
1373         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1374
1375         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1376
1377         return IShellLinkA_AddRef((IShellLinkA*)This);
1378 }
1379 /******************************************************************************
1380  * IShellLinkW_fnRelease
1381  */
1382
1383 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1384 {
1385         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1386
1387         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1388
1389         return IShellLinkA_Release((IShellLinkA*)This);
1390 }
1391
1392 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1393 {
1394     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1395
1396     FIXME("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)\n",This, pszFile, cchMaxPath, pfd, fFlags);
1397     if( cchMaxPath )
1398         pszFile[0] = 0;
1399     if( This->sPath )
1400         lstrcpynW( pszFile, This->sPath, cchMaxPath );
1401
1402     return NOERROR;
1403 }
1404
1405 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1406 {
1407     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1408
1409     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1410
1411     if( This->pPidl)
1412         *ppidl = ILClone( This->pPidl );
1413     else
1414         *ppidl = NULL;
1415
1416     return S_OK;
1417 }
1418
1419 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1420 {
1421     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1422
1423     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1424
1425     if( This->pPidl )
1426         ILFree( This->pPidl );
1427     This->pPidl = ILClone( pidl );
1428     if( !This->pPidl )
1429         return E_FAIL;
1430
1431     This->bDirty = TRUE;
1432
1433     return S_OK;
1434 }
1435
1436 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1437 {
1438     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1439
1440     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1441
1442     if( cchMaxName )
1443         pszName[0] = 0;
1444     if( This->sDescription )
1445         lstrcpynW( pszName, This->sDescription, cchMaxName );
1446
1447     return S_OK;
1448 }
1449
1450 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1451 {
1452     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1453
1454     TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1455
1456     if (This->sDescription)
1457         HeapFree(GetProcessHeap(), 0, This->sDescription);
1458     This->sDescription = HeapAlloc( GetProcessHeap(), 0,
1459                                     (lstrlenW( pszName )+1)*sizeof(WCHAR) );
1460     if ( !This->sDescription )
1461         return E_OUTOFMEMORY;
1462
1463     lstrcpyW( This->sDescription, pszName );
1464     This->bDirty = TRUE;
1465
1466     return S_OK;
1467 }
1468
1469 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1470 {
1471     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1472
1473     TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1474
1475     if( cchMaxPath )
1476         pszDir[0] = 0;
1477     if( This->sWorkDir )
1478         lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
1479
1480     return S_OK;
1481 }
1482
1483 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1484 {
1485     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1486
1487     TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1488
1489     if (This->sWorkDir)
1490         HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1491     This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
1492                                 (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
1493     if ( !This->sWorkDir )
1494         return E_OUTOFMEMORY;
1495     lstrcpyW( This->sWorkDir, pszDir );
1496     This->bDirty = TRUE;
1497
1498     return S_OK;
1499 }
1500
1501 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1502 {
1503     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1504
1505     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1506
1507     if( cchMaxPath )
1508         pszArgs[0] = 0;
1509     if( This->sArgs )
1510         lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
1511
1512     return NOERROR;
1513 }
1514
1515 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1516 {
1517     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1518
1519     TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1520
1521     if (This->sArgs)
1522         HeapFree(GetProcessHeap(), 0, This->sArgs);
1523     This->sArgs = HeapAlloc( GetProcessHeap(), 0,
1524                              (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
1525     if ( !This->sArgs )
1526         return E_OUTOFMEMORY;
1527     lstrcpyW( This->sArgs, pszArgs );
1528     This->bDirty = TRUE;
1529
1530     return S_OK;
1531 }
1532
1533 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1534 {
1535     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1536
1537     TRACE("(%p)->(%p)\n",This, pwHotkey);
1538
1539     *pwHotkey=This->wHotKey;
1540
1541     return S_OK;
1542 }
1543
1544 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1545 {
1546     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1547
1548     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1549
1550     This->wHotKey = wHotkey;
1551     This->bDirty = TRUE;
1552
1553     return S_OK;
1554 }
1555
1556 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1557 {
1558     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1559
1560     TRACE("(%p)->(%p)\n",This, piShowCmd);
1561
1562     *piShowCmd = This->iShowCmd;
1563
1564     return S_OK;
1565 }
1566
1567 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1568 {
1569     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1570
1571     This->iShowCmd = iShowCmd;
1572     This->bDirty = TRUE;
1573
1574     return S_OK;
1575 }
1576
1577 static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPITEMIDLIST pidl, LPWSTR pszIconPath, int cchIconPath, int* piIcon)
1578 {
1579     LPCITEMIDLIST pidlLast;
1580
1581     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1582
1583     if (SUCCEEDED(hr)) {
1584         IExtractIconW* pei;
1585
1586         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconW, NULL, (LPVOID*)&pei);
1587
1588         if (SUCCEEDED(hr)) {
1589             hr = IExtractIconW_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1590
1591             IExtractIconW_Release(pei);
1592         }
1593
1594         IShellFolder_Release(psf);
1595     }
1596
1597     return hr;
1598 }
1599
1600 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1601 {
1602     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1603
1604     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1605
1606     if (cchIconPath)
1607         pszIconPath[0] = 0;
1608
1609     if (This->sIcoPath) {
1610         lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
1611         *piIcon = This->iIcoNdx;
1612         return S_OK;
1613     }
1614
1615     if (This->pPidl || This->sPath) {
1616         IShellFolder* pdsk;
1617
1618         HRESULT hr = SHGetDesktopFolder(&pdsk);
1619
1620         if (SUCCEEDED(hr)) {
1621             /* first look for an icon using the PIDL (if present) */
1622             if (This->pPidl)
1623                 hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1624             else
1625                 hr = E_FAIL;
1626
1627             /* if we couldn't find an icon yet, look for it using the file system path */
1628             if (FAILED(hr) && This->sPath) {
1629                 LPITEMIDLIST pidl;
1630
1631                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1632
1633                 if (SUCCEEDED(hr)) {
1634                     hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1635
1636                     SHFree(pidl);
1637                 }
1638             }
1639
1640             IShellFolder_Release(pdsk);
1641         }
1642
1643         return hr;
1644     } else
1645         return E_FAIL;
1646 }
1647
1648 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
1649 {
1650     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1651
1652     TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1653
1654     if (This->sIcoPath)
1655         HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1656     This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
1657                                 (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
1658     if ( !This->sIcoPath )
1659         return E_OUTOFMEMORY;
1660     lstrcpyW( This->sIcoPath, pszIconPath );
1661
1662     This->iIcoNdx = iIcon;
1663     This->bDirty = TRUE;
1664
1665     return S_OK;
1666 }
1667
1668 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
1669 {
1670     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1671
1672     TRACE("(%p)->(path=%s %lx)\n",This, debugstr_w(pszPathRel), dwReserved);
1673
1674     if (This->sPathRel)
1675         HeapFree(GetProcessHeap(), 0, This->sPathRel);
1676     This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
1677                                 (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
1678     if ( !This->sPathRel )
1679         return E_OUTOFMEMORY;
1680     lstrcpyW( This->sPathRel, pszPathRel );
1681     This->bDirty = TRUE;
1682
1683     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1684 }
1685
1686 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
1687 {
1688     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1689
1690     FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1691
1692     return S_OK;
1693 }
1694
1695 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
1696 {
1697     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1698
1699     TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
1700
1701     if (This->sPath)
1702         HeapFree(GetProcessHeap(), 0, This->sPath);
1703     This->sPath = HeapAlloc( GetProcessHeap(), 0,
1704                              (lstrlenW( pszFile )+1) * sizeof (WCHAR) );
1705     if ( !This->sPath )
1706         return E_OUTOFMEMORY;
1707
1708     lstrcpyW( This->sPath, pszFile );
1709     This->bDirty = TRUE;
1710
1711     return S_OK;
1712 }
1713
1714 /**************************************************************************
1715 * IShellLinkW Implementation
1716 */
1717
1718 static ICOM_VTABLE(IShellLinkW) slvtw =
1719 {
1720         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1721         IShellLinkW_fnQueryInterface,
1722         IShellLinkW_fnAddRef,
1723         IShellLinkW_fnRelease,
1724         IShellLinkW_fnGetPath,
1725         IShellLinkW_fnGetIDList,
1726         IShellLinkW_fnSetIDList,
1727         IShellLinkW_fnGetDescription,
1728         IShellLinkW_fnSetDescription,
1729         IShellLinkW_fnGetWorkingDirectory,
1730         IShellLinkW_fnSetWorkingDirectory,
1731         IShellLinkW_fnGetArguments,
1732         IShellLinkW_fnSetArguments,
1733         IShellLinkW_fnGetHotkey,
1734         IShellLinkW_fnSetHotkey,
1735         IShellLinkW_fnGetShowCmd,
1736         IShellLinkW_fnSetShowCmd,
1737         IShellLinkW_fnGetIconLocation,
1738         IShellLinkW_fnSetIconLocation,
1739         IShellLinkW_fnSetRelativePath,
1740         IShellLinkW_fnResolve,
1741         IShellLinkW_fnSetPath
1742 };