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