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