Implementation of IPersistFile::IsDirty().
[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
28 #include <ctype.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #include <errno.h>
36 #include <limits.h>
37 #ifdef HAVE_SYS_WAIT_H
38 # include <sys/wait.h>
39 #endif
40 #include "wine/debug.h"
41 #include "wine/port.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 = TRUE;
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 ERROR_UNKNOWN;
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 = TRUE;
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 = 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     LINK_HEADER header;
668     ULONG    count;
669     HRESULT    r;
670
671         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
672
673     TRACE("(%p) %p %x\n", This, stm, fClearDirty);
674
675     /* if there's no PIDL, generate one */
676     if( ! This->pPidl )
677     {
678         if( ! This->sPath )
679             return E_FAIL;
680         This->pPidl = ILCreateFromPathW( This->sPath );
681     }
682
683     memset(&header, 0, sizeof(header));
684     header.dwSize = sizeof(header);
685     memcpy(&header.MagicGuid, &CLSID_ShellLink, sizeof(header.MagicGuid) );
686
687     header.wHotKey = This->wHotKey;
688     header.nIcon = This->iIcoNdx;
689     header.dwFlags = SCF_UNICODE;   /* strings are in unicode */
690     header.dwFlags |= SCF_NORMAL;   /* how do we determine this ? */
691     if( This->pPidl )
692         header.dwFlags |= SCF_PIDL;
693     if( This->sDescription )
694         header.dwFlags |= SCF_DESCRIPTION;
695     if( This->sWorkDir )
696         header.dwFlags |= SCF_WORKDIR;
697     if( This->sArgs )
698         header.dwFlags |= SCF_ARGS;
699     if( This->sIcoPath )
700         header.dwFlags |= SCF_CUSTOMICON;
701
702     SystemTimeToFileTime ( &This->time1, &header.Time1 );
703     SystemTimeToFileTime ( &This->time2, &header.Time2 );
704     SystemTimeToFileTime ( &This->time3, &header.Time3 );
705
706     /* write the Shortcut header */
707     r = IStream_Write( stm, &header, sizeof(header), &count );
708     if( FAILED( r ) )
709     {
710         ERR("Write failed at %d\n",__LINE__);
711         return r;
712     }
713
714     TRACE("Writing pidl \n");
715
716     /* write the PIDL to the shortcut */
717     if( This->pPidl )
718     {
719         r = ILSaveToStream( stm, This->pPidl );
720         if( FAILED( r ) )
721         {
722             ERR("Failed to write PIDL at %d\n",__LINE__);
723             return r;
724         }
725     }
726
727     TRACE("Path = %s\n", debugstr_w(This->sPath));
728     if( ! This->sPath )
729         return E_FAIL;
730     Stream_WriteLocationInfo( stm, This->sPath );
731
732     TRACE("Description = %s\n", debugstr_w(This->sDescription));
733     if( This->sDescription )
734         r = Stream_WriteString( stm, This->sDescription );
735
736     if( This->sPathRel )
737         r = Stream_WriteString( stm, This->sPathRel );
738
739     if( This->sWorkDir )
740         r = Stream_WriteString( stm, This->sWorkDir );
741
742     if( This->sArgs )
743         r = Stream_WriteString( stm, This->sArgs );
744
745     if( This->sIcoPath )
746         r = Stream_WriteString( stm, This->sIcoPath );
747
748     return S_OK;
749 }
750
751 /************************************************************************
752  * IPersistStream_GetSizeMax (IPersistStream)
753  */
754 static HRESULT WINAPI IPersistStream_fnGetSizeMax(
755         IPersistStream*  iface,
756         ULARGE_INTEGER*  pcbSize)
757 {
758         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
759
760         TRACE("(%p)\n", This);
761
762         return E_NOTIMPL;
763 }
764
765 static ICOM_VTABLE(IPersistStream) psvt =
766 {
767         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
768         IPersistStream_fnQueryInterface,
769         IPersistStream_fnAddRef,
770         IPersistStream_fnRelease,
771         IPersistStream_fnGetClassID,
772         IPersistStream_fnIsDirty,
773         IPersistStream_fnLoad,
774         IPersistStream_fnSave,
775         IPersistStream_fnGetSizeMax
776 };
777
778 /**************************************************************************
779  *        IShellLink_Constructor
780  */
781 HRESULT WINAPI IShellLink_Constructor (
782         IUnknown * pUnkOuter,
783         REFIID riid,
784         LPVOID * ppv)
785 {
786         IShellLinkImpl * sl;
787
788         TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
789
790         *ppv = NULL;
791
792         if(pUnkOuter) return CLASS_E_NOAGGREGATION;
793         sl = (IShellLinkImpl *) LocalAlloc(GMEM_ZEROINIT,sizeof(IShellLinkImpl));
794         if (!sl) return E_OUTOFMEMORY;
795
796         sl->ref = 1;
797         sl->lpVtbl = &slvt;
798         sl->lpvtblw = &slvtw;
799         sl->lpvtblPersistFile = &pfvt;
800         sl->lpvtblPersistStream = &psvt;
801         sl->iShowCmd = SW_SHOWNORMAL;
802         sl->bDirty = FALSE;
803
804         TRACE("(%p)->()\n",sl);
805
806         if (IsEqualIID(riid, &IID_IUnknown) ||
807             IsEqualIID(riid, &IID_IShellLinkA))
808             *ppv = sl;
809         else if (IsEqualIID(riid, &IID_IShellLinkW))
810             *ppv = &(sl->lpvtblw);
811         else {
812             LocalFree((HLOCAL)sl);
813             ERR("E_NOINTERFACE\n");
814             return E_NOINTERFACE;
815         }
816
817         return S_OK;
818 }
819
820
821 static BOOL SHELL_ExistsFileW(LPCWSTR path)
822 {
823     HANDLE hfile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
824
825     if (hfile != INVALID_HANDLE_VALUE) {
826         CloseHandle(hfile);
827         return TRUE;
828     } else
829         return FALSE;
830 }
831
832 /**************************************************************************
833  *  ShellLink_UpdatePath
834  *      update absolute path in sPath using relative path in sPathRel
835  */
836 static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
837 {
838     if (!path || !psPath)
839         return E_INVALIDARG;
840
841     if (!*psPath && sPathRel) {
842         WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
843         LPWSTR final = NULL;
844
845         /* first try if [directory of link file] + [relative path] finds an existing file */
846
847         GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
848         if( !final )
849             final = buffer;
850         lstrcpyW(final, sPathRel);
851
852         *abs_path = '\0';
853
854         if (SHELL_ExistsFileW(buffer)) {
855             if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
856                 lstrcpyW(abs_path, buffer);
857         } else {
858             /* try if [working directory] + [relative path] finds an existing file */
859             if (sWorkDir) {
860                 lstrcpyW(buffer, sWorkDir);
861                 lstrcpyW(PathAddBackslashW(buffer), sPathRel);
862
863                 if (SHELL_ExistsFileW(buffer))
864                     if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
865                         lstrcpyW(abs_path, buffer);
866             }
867         }
868
869         /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
870         if (!*abs_path)
871             lstrcpyW(abs_path, sPathRel);
872
873         *psPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(abs_path)+1)*sizeof(WCHAR));
874         if (!*psPath)
875             return E_OUTOFMEMORY;
876
877         lstrcpyW(*psPath, abs_path);
878     }
879
880     return S_OK;
881 }
882
883 /**************************************************************************
884  *        IShellLink_ConstructFromFile
885  */
886 HRESULT WINAPI IShellLink_ConstructFromFile (
887         IUnknown* pUnkOuter,
888         REFIID riid,
889         LPCITEMIDLIST pidl,
890         LPVOID* ppv
891 )
892 {
893     IShellLinkW* psl;
894
895     HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl);
896
897     if (SUCCEEDED(hr)) {
898         IPersistFile* ppf;
899
900         *ppv = NULL;
901
902         hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
903
904         if (SUCCEEDED(hr)) {
905             WCHAR path[MAX_PATH];
906
907             if (SHGetPathFromIDListW(pidl, path)) 
908                 hr = IPersistFile_Load(ppf, path, 0);
909             else
910                 hr = E_FAIL;
911
912             if (SUCCEEDED(hr))
913                 *ppv = (IUnknown*) psl;
914
915             IPersistFile_Release(ppf);
916         }
917
918         if (!*ppv)
919             IShellLinkW_Release(psl);
920     }
921
922     return hr;
923 }
924
925 /**************************************************************************
926  *  IShellLinkA_QueryInterface
927  */
928 static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
929 {
930         ICOM_THIS(IShellLinkImpl, iface);
931
932         TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
933
934         *ppvObj = NULL;
935
936         if(IsEqualIID(riid, &IID_IUnknown) ||
937            IsEqualIID(riid, &IID_IShellLinkA))
938         {
939           *ppvObj = This;
940         }
941         else if(IsEqualIID(riid, &IID_IShellLinkW))
942         {
943           *ppvObj = (IShellLinkW *)&(This->lpvtblw);
944         }
945         else if(IsEqualIID(riid, &IID_IPersistFile))
946         {
947           *ppvObj = (IPersistFile *)&(This->lpvtblPersistFile);
948         }
949         else if(IsEqualIID(riid, &IID_IPersistStream))
950         {
951           *ppvObj = (IPersistStream *)&(This->lpvtblPersistStream);
952         }
953
954         if(*ppvObj)
955         {
956           IUnknown_AddRef((IUnknown*)(*ppvObj));
957           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
958           return S_OK;
959         }
960         TRACE("-- Interface: E_NOINTERFACE\n");
961         return E_NOINTERFACE;
962 }
963 /******************************************************************************
964  * IShellLinkA_AddRef
965  */
966 static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
967 {
968         ICOM_THIS(IShellLinkImpl, iface);
969
970         TRACE("(%p)->(count=%lu)\n",This,This->ref);
971
972         return ++(This->ref);
973 }
974 /******************************************************************************
975  *      IShellLinkA_Release
976  */
977 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
978 {
979     ICOM_THIS(IShellLinkImpl, iface);
980
981     TRACE("(%p)->(count=%lu)\n",This,This->ref);
982
983     if (--(This->ref))
984         return This->ref;
985
986     TRACE("-- destroying IShellLink(%p)\n",This);
987
988     if (This->sIcoPath)
989         HeapFree(GetProcessHeap(), 0, This->sIcoPath);
990
991     if (This->sArgs)
992         HeapFree(GetProcessHeap(), 0, This->sArgs);
993
994     if (This->sWorkDir)
995         HeapFree(GetProcessHeap(), 0, This->sWorkDir);
996
997     if (This->sDescription)
998         HeapFree(GetProcessHeap(), 0, This->sDescription);
999
1000     if (This->sPath)
1001         HeapFree(GetProcessHeap(),0,This->sPath);
1002
1003     if (This->pPidl)
1004         ILFree(This->pPidl);
1005
1006     LocalFree((HANDLE)This);
1007
1008     return 0;
1009 }
1010
1011 static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
1012                   INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1013 {
1014     ICOM_THIS(IShellLinkImpl, iface);
1015
1016     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
1017           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1018
1019     if( cchMaxPath )
1020         pszFile[0] = 0;
1021     if (This->sPath)
1022         WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
1023                              pszFile, cchMaxPath, NULL, NULL);
1024
1025     return NOERROR;
1026 }
1027
1028 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
1029 {
1030         ICOM_THIS(IShellLinkImpl, iface);
1031
1032         TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1033
1034         *ppidl = ILClone(This->pPidl);
1035
1036         return NOERROR;
1037 }
1038
1039 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
1040 {
1041     ICOM_THIS(IShellLinkImpl, iface);
1042
1043     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1044
1045     if (This->pPidl)
1046         ILFree(This->pPidl);
1047     This->pPidl = ILClone (pidl);
1048     This->bDirty = TRUE;
1049
1050     return S_OK;
1051 }
1052
1053 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
1054 {
1055     ICOM_THIS(IShellLinkImpl, iface);
1056
1057     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1058
1059     if( cchMaxName )
1060         pszName[0] = 0;
1061     if( This->sDescription )
1062         WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
1063             pszName, cchMaxName, NULL, NULL);
1064
1065     return S_OK;
1066 }
1067 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
1068 {
1069     ICOM_THIS(IShellLinkImpl, iface);
1070
1071     TRACE("(%p)->(pName=%s)\n", This, pszName);
1072
1073     if (This->sDescription)
1074         HeapFree(GetProcessHeap(), 0, This->sDescription);
1075     This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
1076     if ( !This->sDescription )
1077         return E_OUTOFMEMORY;
1078
1079     This->bDirty = TRUE;
1080
1081     return S_OK;
1082 }
1083
1084 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
1085 {
1086     ICOM_THIS(IShellLinkImpl, iface);
1087
1088     TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1089
1090     if( cchMaxPath )
1091         pszDir[0] = 0;
1092     if( This->sWorkDir )
1093         WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
1094                              pszDir, cchMaxPath, NULL, NULL);
1095
1096     return S_OK;
1097 }
1098
1099 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
1100 {
1101     ICOM_THIS(IShellLinkImpl, iface);
1102
1103     TRACE("(%p)->(dir=%s)\n",This, pszDir);
1104
1105     if (This->sWorkDir)
1106         HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1107     This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
1108     if ( !This->sWorkDir )
1109         return E_OUTOFMEMORY;
1110
1111     This->bDirty = TRUE;
1112
1113     return S_OK;
1114 }
1115
1116 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
1117 {
1118     ICOM_THIS(IShellLinkImpl, iface);
1119
1120     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1121
1122     if( cchMaxPath )
1123         pszArgs[0] = 0;
1124     if( This->sArgs )
1125         WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
1126                              pszArgs, cchMaxPath, NULL, NULL);
1127
1128     return S_OK;
1129 }
1130
1131 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
1132 {
1133     ICOM_THIS(IShellLinkImpl, iface);
1134
1135     TRACE("(%p)->(args=%s)\n",This, pszArgs);
1136
1137     if (This->sArgs)
1138         HeapFree(GetProcessHeap(), 0, This->sArgs);
1139     This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
1140     if( !This->sArgs )
1141         return E_OUTOFMEMORY;
1142
1143     This->bDirty = TRUE;
1144
1145     return S_OK;
1146 }
1147
1148 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
1149 {
1150         ICOM_THIS(IShellLinkImpl, iface);
1151
1152         TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1153
1154         *pwHotkey = This->wHotKey;
1155
1156         return S_OK;
1157 }
1158
1159 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
1160 {
1161         ICOM_THIS(IShellLinkImpl, iface);
1162
1163         TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1164
1165         This->wHotKey = wHotkey;
1166         This->bDirty = TRUE;
1167
1168         return S_OK;
1169 }
1170
1171 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
1172 {
1173     ICOM_THIS(IShellLinkImpl, iface);
1174
1175     TRACE("(%p)->(%p)\n",This, piShowCmd);
1176     *piShowCmd = This->iShowCmd;
1177     return S_OK;
1178 }
1179
1180 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
1181 {
1182     ICOM_THIS(IShellLinkImpl, iface);
1183
1184     TRACE("(%p) %d\n",This, iShowCmd);
1185
1186     This->iShowCmd = iShowCmd;
1187     This->bDirty = TRUE;
1188
1189     return NOERROR;
1190 }
1191
1192 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1193 {
1194     ICOM_THIS(IShellLinkImpl, iface);
1195
1196     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1197
1198     if( cchIconPath )
1199         pszIconPath[0] = 0;
1200     if( This->sIcoPath )
1201         WideCharToMultiByte( CP_ACP, 0, This->sIcoPath, -1,
1202                              pszIconPath, cchIconPath, NULL, NULL);
1203     *piIcon = This->iIcoNdx;
1204
1205     return NOERROR;
1206 }
1207
1208 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
1209 {
1210     ICOM_THIS(IShellLinkImpl, iface);
1211
1212     TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1213
1214     if (This->sIcoPath)
1215         HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1216     This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1217     if ( !This->sIcoPath )
1218         return E_OUTOFMEMORY;
1219
1220     This->iIcoNdx = iIcon;
1221     This->bDirty = TRUE;
1222
1223     return S_OK;
1224 }
1225
1226 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1227 {
1228     ICOM_THIS(IShellLinkImpl, iface);
1229
1230     FIXME("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved);
1231
1232     if (This->sPathRel)
1233         HeapFree(GetProcessHeap(), 0, This->sPathRel);
1234     This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1235     This->bDirty = TRUE;
1236
1237     return S_OK;
1238 }
1239
1240 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1241 {
1242         ICOM_THIS(IShellLinkImpl, iface);
1243
1244         FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1245         return S_OK;
1246 }
1247
1248 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1249 {
1250     ICOM_THIS(IShellLinkImpl, iface);
1251
1252     TRACE("(%p)->(path=%s)\n",This, pszFile);
1253
1254     if (This->sPath)
1255         HeapFree(GetProcessHeap(), 0, This->sPath);
1256     This->sPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
1257     if( !This->sPath )
1258         return E_OUTOFMEMORY;
1259
1260     This->bDirty = TRUE;
1261
1262     return S_OK;
1263 }
1264
1265 /**************************************************************************
1266 * IShellLink Implementation
1267 */
1268
1269 static ICOM_VTABLE(IShellLinkA) slvt =
1270 {
1271         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1272         IShellLinkA_fnQueryInterface,
1273         IShellLinkA_fnAddRef,
1274         IShellLinkA_fnRelease,
1275         IShellLinkA_fnGetPath,
1276         IShellLinkA_fnGetIDList,
1277         IShellLinkA_fnSetIDList,
1278         IShellLinkA_fnGetDescription,
1279         IShellLinkA_fnSetDescription,
1280         IShellLinkA_fnGetWorkingDirectory,
1281         IShellLinkA_fnSetWorkingDirectory,
1282         IShellLinkA_fnGetArguments,
1283         IShellLinkA_fnSetArguments,
1284         IShellLinkA_fnGetHotkey,
1285         IShellLinkA_fnSetHotkey,
1286         IShellLinkA_fnGetShowCmd,
1287         IShellLinkA_fnSetShowCmd,
1288         IShellLinkA_fnGetIconLocation,
1289         IShellLinkA_fnSetIconLocation,
1290         IShellLinkA_fnSetRelativePath,
1291         IShellLinkA_fnResolve,
1292         IShellLinkA_fnSetPath
1293 };
1294
1295
1296 /**************************************************************************
1297  *  IShellLinkW_fnQueryInterface
1298  */
1299 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1300   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1301 {
1302         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1303
1304         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
1305 }
1306
1307 /******************************************************************************
1308  * IShellLinkW_fnAddRef
1309  */
1310 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1311 {
1312         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1313
1314         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1315
1316         return IShellLinkA_AddRef((IShellLinkA*)This);
1317 }
1318 /******************************************************************************
1319  * IShellLinkW_fnRelease
1320  */
1321
1322 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1323 {
1324         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1325
1326         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1327
1328         return IShellLinkA_Release((IShellLinkA*)This);
1329 }
1330
1331 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1332 {
1333     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1334
1335     FIXME("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)\n",This, pszFile, cchMaxPath, pfd, fFlags);
1336     if( cchMaxPath )
1337         pszFile[0] = 0;
1338     if( This->sPath )
1339         lstrcpynW( pszFile, This->sPath, cchMaxPath );
1340
1341     return NOERROR;
1342 }
1343
1344 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1345 {
1346     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1347
1348     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1349
1350     if( This->pPidl)
1351         *ppidl = ILClone( This->pPidl );
1352     else
1353         *ppidl = NULL;
1354
1355     return S_OK;
1356 }
1357
1358 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1359 {
1360     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1361
1362     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1363
1364     if( This->pPidl )
1365         ILFree( This->pPidl );
1366     This->pPidl = ILClone( pidl );
1367     if( !This->pPidl )
1368         return E_FAIL;
1369
1370     This->bDirty = TRUE;
1371
1372     return S_OK;
1373 }
1374
1375 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1376 {
1377     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1378
1379     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1380
1381     if( cchMaxName )
1382         pszName[0] = 0;
1383     if( This->sDescription )
1384         lstrcpynW( pszName, This->sDescription, cchMaxName );
1385
1386     return S_OK;
1387 }
1388
1389 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1390 {
1391     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1392
1393     TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1394
1395     if (This->sDescription)
1396         HeapFree(GetProcessHeap(), 0, This->sDescription);
1397     This->sDescription = HeapAlloc( GetProcessHeap(), 0,
1398                                     (lstrlenW( pszName )+1)*sizeof(WCHAR) );
1399     if ( !This->sDescription )
1400         return E_OUTOFMEMORY;
1401
1402     lstrcpyW( This->sDescription, pszName );
1403     This->bDirty = TRUE;
1404
1405     return S_OK;
1406 }
1407
1408 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1409 {
1410     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1411
1412     TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1413
1414     if( cchMaxPath )
1415         pszDir[0] = 0;
1416     if( This->sWorkDir )
1417         lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
1418
1419     return S_OK;
1420 }
1421
1422 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1423 {
1424     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1425
1426     TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1427
1428     if (This->sWorkDir)
1429         HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1430     This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
1431                                 (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
1432     if ( !This->sWorkDir )
1433         return E_OUTOFMEMORY;
1434     lstrcpyW( This->sWorkDir, pszDir );
1435     This->bDirty = TRUE;
1436
1437     return S_OK;
1438 }
1439
1440 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1441 {
1442     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1443
1444     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1445
1446     if( cchMaxPath )
1447         pszArgs[0] = 0;
1448     if( This->sArgs )
1449         lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
1450
1451     return NOERROR;
1452 }
1453
1454 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1455 {
1456     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1457
1458     TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1459
1460     if (This->sArgs)
1461         HeapFree(GetProcessHeap(), 0, This->sArgs);
1462     This->sArgs = HeapAlloc( GetProcessHeap(), 0,
1463                              (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
1464     if ( !This->sArgs )
1465         return E_OUTOFMEMORY;
1466     lstrcpyW( This->sArgs, pszArgs );
1467     This->bDirty = TRUE;
1468
1469     return S_OK;
1470 }
1471
1472 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1473 {
1474     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1475
1476     TRACE("(%p)->(%p)\n",This, pwHotkey);
1477
1478     *pwHotkey=This->wHotKey;
1479
1480     return S_OK;
1481 }
1482
1483 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1484 {
1485     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1486
1487     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1488
1489     This->wHotKey = wHotkey;
1490     This->bDirty = TRUE;
1491
1492     return S_OK;
1493 }
1494
1495 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1496 {
1497     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1498
1499     TRACE("(%p)->(%p)\n",This, piShowCmd);
1500
1501     *piShowCmd = This->iShowCmd;
1502
1503     return S_OK;
1504 }
1505
1506 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1507 {
1508     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1509
1510     This->iShowCmd = iShowCmd;
1511     This->bDirty = TRUE;
1512
1513     return S_OK;
1514 }
1515
1516 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1517 {
1518     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1519
1520     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1521
1522     if( cchIconPath )
1523         pszIconPath[0] = 0;
1524     if( This->sIcoPath )
1525         lstrcpynW( pszIconPath, This->sIcoPath, cchIconPath );
1526     *piIcon = This->iIcoNdx;
1527
1528     return S_OK;
1529 }
1530
1531 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
1532 {
1533     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1534
1535     TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1536
1537     if (This->sIcoPath)
1538         HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1539     This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
1540                                 (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
1541     if ( !This->sIcoPath )
1542         return E_OUTOFMEMORY;
1543     lstrcpyW( This->sIcoPath, pszIconPath );
1544
1545     This->iIcoNdx = iIcon;
1546     This->bDirty = TRUE;
1547
1548     return S_OK;
1549 }
1550
1551 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
1552 {
1553     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1554
1555     TRACE("(%p)->(path=%s %lx)\n",This, debugstr_w(pszPathRel), dwReserved);
1556
1557     if (This->sPathRel)
1558         HeapFree(GetProcessHeap(), 0, This->sPathRel);
1559     This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
1560                                 (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
1561     if ( !This->sPathRel )
1562         return E_OUTOFMEMORY;
1563     lstrcpyW( This->sPathRel, pszPathRel );
1564     This->bDirty = TRUE;
1565
1566     return S_OK;
1567 }
1568
1569 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
1570 {
1571     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1572
1573     FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1574
1575     return S_OK;
1576 }
1577
1578 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
1579 {
1580     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1581
1582     TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
1583
1584     if (This->sPath)
1585         HeapFree(GetProcessHeap(), 0, This->sPath);
1586     This->sPath = HeapAlloc( GetProcessHeap(), 0,
1587                              (lstrlenW( pszFile )+1) * sizeof (WCHAR) );
1588     if ( !This->sPath )
1589         return E_OUTOFMEMORY;
1590
1591     lstrcpyW( This->sPath, pszFile );
1592     This->bDirty = TRUE;
1593
1594     return S_OK;
1595 }
1596
1597 /**************************************************************************
1598 * IShellLinkW Implementation
1599 */
1600
1601 static ICOM_VTABLE(IShellLinkW) slvtw =
1602 {
1603         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1604         IShellLinkW_fnQueryInterface,
1605         IShellLinkW_fnAddRef,
1606         IShellLinkW_fnRelease,
1607         IShellLinkW_fnGetPath,
1608         IShellLinkW_fnGetIDList,
1609         IShellLinkW_fnSetIDList,
1610         IShellLinkW_fnGetDescription,
1611         IShellLinkW_fnSetDescription,
1612         IShellLinkW_fnGetWorkingDirectory,
1613         IShellLinkW_fnSetWorkingDirectory,
1614         IShellLinkW_fnGetArguments,
1615         IShellLinkW_fnSetArguments,
1616         IShellLinkW_fnGetHotkey,
1617         IShellLinkW_fnSetHotkey,
1618         IShellLinkW_fnGetShowCmd,
1619         IShellLinkW_fnSetShowCmd,
1620         IShellLinkW_fnGetIconLocation,
1621         IShellLinkW_fnSetIconLocation,
1622         IShellLinkW_fnSetRelativePath,
1623         IShellLinkW_fnResolve,
1624         IShellLinkW_fnSetPath
1625 };