Fix segmentation fault caused by incorrect referencing of client audio
[wine] / dlls / shell32 / shelllink.c
1 /*
2  *
3  *      Copyright 1997  Marcus Meissner
4  *      Copyright 1998  Juergen Schmied
5  *      Copyright 2005  Mike McCormack
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * NOTES
22  *   Nearly complete informations about the binary formats 
23  *   of .lnk files available at http://www.wotsit.org
24  *
25  *  You can use winedump to examine the contents of a link file:
26  *   winedump lnk sc.lnk
27  *
28  *  MSI advertised shortcuts are totally undocumented.  They provide an
29  *   icon for a program that is not yet installed, and invoke MSI to
30  *   install the program when the shortcut is clicked on.  They are
31  *   created by passing a special string to SetPath, and the information
32  *   in that string is parsed an stored.
33  */
34
35 #include "config.h"
36 #include "wine/port.h"
37
38 #include <ctype.h>
39 #include <string.h>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #ifdef HAVE_UNISTD_H
43 # include <unistd.h>
44 #endif
45 #include <errno.h>
46 #include <limits.h>
47 #ifdef HAVE_SYS_WAIT_H
48 # include <sys/wait.h>
49 #endif
50
51 #define COBJMACROS
52
53 #include "wine/debug.h"
54 #include "winerror.h"
55 #include "windef.h"
56 #include "winbase.h"
57 #include "winnls.h"
58 #include "winreg.h"
59
60 #include "winuser.h"
61 #include "wingdi.h"
62 #include "shlobj.h"
63 #include "undocshell.h"
64
65 #include "pidl.h"
66 #include "shell32_main.h"
67 #include "shlguid.h"
68 #include "shlwapi.h"
69
70 #include "initguid.h"
71
72 WINE_DEFAULT_DEBUG_CHANNEL(shell);
73
74 DEFINE_GUID( SHELL32_AdvtShortcutProduct,
75        0x9db1186f,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
76 DEFINE_GUID( SHELL32_AdvtShortcutComponent,
77        0x9db1186e,0x40df,0x11d1,0xaa,0x8c,0x00,0xc0,0x4f,0xb6,0x78,0x63);
78
79 /* link file formats */
80
81 #include "pshpack1.h"
82
83 typedef struct _LINK_HEADER
84 {
85         DWORD    dwSize;        /* 0x00 size of the header - 0x4c */
86         GUID     MagicGuid;     /* 0x04 is CLSID_ShellLink */
87         DWORD    dwFlags;       /* 0x14 describes elements following */
88         DWORD    dwFileAttr;    /* 0x18 attributes of the target file */
89         FILETIME Time1;         /* 0x1c */
90         FILETIME Time2;         /* 0x24 */
91         FILETIME Time3;         /* 0x2c */
92         DWORD    dwFileLength;  /* 0x34 File length */
93         DWORD    nIcon;         /* 0x38 icon number */
94         DWORD   fStartup;       /* 0x3c startup type */
95         DWORD   wHotKey;        /* 0x40 hotkey */
96         DWORD   Unknown5;       /* 0x44 */
97         DWORD   Unknown6;       /* 0x48 */
98 } LINK_HEADER, * PLINK_HEADER;
99
100 #define SHLINK_LOCAL  0
101 #define SHLINK_REMOTE 1
102
103 typedef struct _LOCATION_INFO
104 {
105     DWORD  dwTotalSize;
106     DWORD  dwHeaderSize;
107     DWORD  dwFlags;
108     DWORD  dwVolTableOfs;
109     DWORD  dwLocalPathOfs;
110     DWORD  dwNetworkVolTableOfs;
111     DWORD  dwFinalPathOfs;
112 } LOCATION_INFO;
113
114 typedef struct _LOCAL_VOLUME_INFO
115 {
116     DWORD dwSize;
117     DWORD dwType;
118     DWORD dwVolSerial;
119     DWORD dwVolLabelOfs;
120 } LOCAL_VOLUME_INFO;
121
122 typedef struct volume_info_t
123 {
124     DWORD type;
125     DWORD serial;
126     WCHAR label[12];  /* assume 8.3 */
127 } volume_info;
128
129 #include "poppack.h"
130
131 static IShellLinkAVtbl          slvt;
132 static IShellLinkWVtbl          slvtw;
133 static IPersistFileVtbl pfvt;
134 static IPersistStreamVtbl       psvt;
135
136 /* IShellLink Implementation */
137
138 typedef struct
139 {
140         IShellLinkAVtbl    *lpVtbl;
141         DWORD               ref;
142
143         IShellLinkWVtbl    *lpvtblw;
144         IPersistFileVtbl   *lpvtblPersistFile;
145         IPersistStreamVtbl *lpvtblPersistStream;
146
147         /* data structures according to the informations in the link */
148         LPITEMIDLIST    pPidl;
149         WORD            wHotKey;
150         SYSTEMTIME      time1;
151         SYSTEMTIME      time2;
152         SYSTEMTIME      time3;
153
154         DWORD         iShowCmd;
155         LPWSTR        sIcoPath;
156         INT           iIcoNdx;
157         LPWSTR        sPath;
158         LPWSTR        sArgs;
159         LPWSTR        sWorkDir;
160         LPWSTR        sDescription;
161         LPWSTR        sPathRel;
162         LPWSTR        sProduct;
163         LPWSTR        sComponent;
164         volume_info   volume;
165
166         BOOL            bDirty;
167 } IShellLinkImpl;
168
169 #define _IShellLinkW_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblw)))
170 #define _ICOM_THIS_From_IShellLinkW(class, name) class* This = (class*)(((char*)name)-_IShellLinkW_Offset)
171
172 #define _IPersistFile_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblPersistFile)))
173 #define _ICOM_THIS_From_IPersistFile(class, name) class* This = (class*)(((char*)name)-_IPersistFile_Offset)
174
175 #define _IPersistStream_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblPersistStream)))
176 #define _ICOM_THIS_From_IPersistStream(class, name) class* This = (class*)(((char*)name)-_IPersistStream_Offset)
177
178 static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath);
179
180 /* strdup on the process heap */
181 inline static LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
182 {
183     INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
184     LPWSTR p = HeapAlloc( heap, flags, len*sizeof (WCHAR) );
185     if( !p )
186         return p;
187     MultiByteToWideChar( CP_ACP, 0, str, -1, p, len );
188     return p;
189 }
190
191 /**************************************************************************
192  *  IPersistFile_QueryInterface
193  */
194 static HRESULT WINAPI IPersistFile_fnQueryInterface(
195         IPersistFile* iface,
196         REFIID riid,
197         LPVOID *ppvObj)
198 {
199         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
200
201         TRACE("(%p)\n",This);
202
203         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
204 }
205
206 /******************************************************************************
207  * IPersistFile_AddRef
208  */
209 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
210 {
211         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
212
213         TRACE("(%p)->(count=%lu)\n",This,This->ref);
214
215         return IShellLinkA_AddRef((IShellLinkA*)This);
216 }
217 /******************************************************************************
218  * IPersistFile_Release
219  */
220 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile* iface)
221 {
222         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
223
224         TRACE("(%p)->(count=%lu)\n",This,This->ref);
225
226         return IShellLinkA_Release((IShellLinkA*)This);
227 }
228
229 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
230 {
231         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
232         FIXME("(%p)\n",This);
233         return NOERROR;
234 }
235 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
236 {
237         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
238
239         TRACE("(%p)\n",This);
240
241         if (This->bDirty)
242             return S_OK;
243
244         return S_FALSE;
245 }
246 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
247 {
248         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
249         IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
250         HRESULT r;
251         IStream *stm;
252
253         TRACE("(%p, %s, %lx)\n",This, debugstr_w(pszFileName), dwMode);
254
255         r = CreateStreamOnFile(pszFileName, dwMode, &stm);
256         if( SUCCEEDED( r ) )
257         {
258             r = IPersistStream_Load(StreamThis, stm);
259             ShellLink_UpdatePath(This->sPathRel, pszFileName, This->sWorkDir, &This->sPath);
260             IStream_Release( stm );
261             This->bDirty = FALSE;
262         }
263         TRACE("-- returning hr %08lx\n", r);
264         return r;
265 }
266
267 static BOOL StartLinkProcessor( LPCOLESTR szLink )
268 {
269     static const WCHAR szFormat[] = {
270         'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
271         ' ','-','r',' ','"','%','s','"',0 };
272     LONG len;
273     LPWSTR buffer;
274     STARTUPINFOW si;
275     PROCESS_INFORMATION pi;
276
277     len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
278     buffer = HeapAlloc( GetProcessHeap(), 0, len );
279     if( !buffer )
280         return FALSE;
281
282     wsprintfW( buffer, szFormat, szLink );
283
284     TRACE("starting %s\n",debugstr_w(buffer));
285
286     memset(&si, 0, sizeof(si));
287     si.cb = sizeof(si);
288     if (!CreateProcessW( NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) return FALSE;
289
290     /* wait for a while to throttle the creation of linker processes */
291     if( WAIT_OBJECT_0 != WaitForSingleObject( pi.hProcess, 10000 ) )
292         WARN("Timed out waiting for shell linker\n");
293
294     CloseHandle( pi.hProcess );
295     CloseHandle( pi.hThread );
296
297     return TRUE;
298 }
299
300 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
301 {
302     _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
303     IPersistStream *StreamThis = (IPersistStream *)&This->lpvtblPersistStream;
304     HRESULT r;
305     IStream *stm;
306
307     TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
308
309     if (!pszFileName)
310         return E_FAIL;
311
312     r = CreateStreamOnFile(pszFileName, STGM_READWRITE | STGM_CREATE, &stm);
313     if( SUCCEEDED( r ) )
314     {
315         r = IPersistStream_Save(StreamThis, stm, FALSE);
316         IStream_Release( stm );
317
318         if( SUCCEEDED( r ) )
319         {
320             StartLinkProcessor( pszFileName );
321
322             This->bDirty = FALSE;
323         }
324         else
325         {
326             DeleteFileW( pszFileName );
327             WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
328         }
329     }
330
331     return r;
332 }
333
334 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
335 {
336         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
337         FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
338         return NOERROR;
339 }
340 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *ppszFileName)
341 {
342         _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
343         FIXME("(%p)\n",This);
344         return NOERROR;
345 }
346
347 static IPersistFileVtbl pfvt =
348 {
349         IPersistFile_fnQueryInterface,
350         IPersistFile_fnAddRef,
351         IPersistFile_fnRelease,
352         IPersistFile_fnGetClassID,
353         IPersistFile_fnIsDirty,
354         IPersistFile_fnLoad,
355         IPersistFile_fnSave,
356         IPersistFile_fnSaveCompleted,
357         IPersistFile_fnGetCurFile
358 };
359
360 /************************************************************************
361  * IPersistStream_QueryInterface
362  */
363 static HRESULT WINAPI IPersistStream_fnQueryInterface(
364         IPersistStream* iface,
365         REFIID     riid,
366         VOID**     ppvoid)
367 {
368         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
369
370         TRACE("(%p)\n",This);
371
372         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvoid);
373 }
374
375 /************************************************************************
376  * IPersistStream_Release
377  */
378 static ULONG WINAPI IPersistStream_fnRelease(
379         IPersistStream* iface)
380 {
381         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
382
383         TRACE("(%p)\n",This);
384
385         return IShellLinkA_Release((IShellLinkA*)This);
386 }
387
388 /************************************************************************
389  * IPersistStream_AddRef
390  */
391 static ULONG WINAPI IPersistStream_fnAddRef(
392         IPersistStream* iface)
393 {
394         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
395
396         TRACE("(%p)\n",This);
397
398         return IShellLinkA_AddRef((IShellLinkA*)This);
399 }
400
401 /************************************************************************
402  * IPersistStream_GetClassID
403  *
404  */
405 static HRESULT WINAPI IPersistStream_fnGetClassID(
406         IPersistStream* iface,
407         CLSID* pClassID)
408 {
409         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
410
411         TRACE("(%p)\n", This);
412
413         if (pClassID==0)
414           return E_POINTER;
415
416 /*      memcpy(pClassID, &CLSID_???, sizeof(CLSID_???)); */
417
418         return S_OK;
419 }
420
421 /************************************************************************
422  * IPersistStream_IsDirty (IPersistStream)
423  */
424 static HRESULT WINAPI IPersistStream_fnIsDirty(
425         IPersistStream*  iface)
426 {
427         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
428
429         TRACE("(%p)\n", This);
430
431         return S_OK;
432 }
433
434
435 static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
436 {
437     DWORD count;
438     USHORT len;
439     LPVOID temp;
440     LPWSTR str;
441     HRESULT r;
442
443     TRACE("%p\n", stm);
444
445     count = 0;
446     r = IStream_Read(stm, &len, sizeof(len), &count);
447     if ( FAILED (r) || ( count != sizeof(len) ) )
448         return E_FAIL;
449
450     if( unicode )
451         len *= sizeof (WCHAR);
452
453     TRACE("reading %d\n", len);
454     temp = HeapAlloc(GetProcessHeap(), 0, len+sizeof(WCHAR));
455     if( !temp )
456         return E_OUTOFMEMORY;
457     count = 0;
458     r = IStream_Read(stm, temp, len, &count);
459     if( FAILED (r) || ( count != len ) )
460     {
461         HeapFree( GetProcessHeap(), 0, temp );
462         return E_FAIL;
463     }
464
465     TRACE("read %s\n", debugstr_an(temp,len));
466
467     /* convert to unicode if necessary */
468     if( !unicode )
469     {
470         count = MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, NULL, 0 );
471         str = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof (WCHAR) );
472         if( str )
473             MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, str, count );
474         HeapFree( GetProcessHeap(), 0, temp );
475     }
476     else
477     {
478         count /= 2;
479         str = (LPWSTR) temp;
480     }
481     str[count] = 0;
482
483     *pstr = str;
484
485     return S_OK;
486 }
487
488 static HRESULT Stream_ReadChunk( IStream* stm, LPVOID *data )
489 {
490     DWORD size;
491     ULONG count;
492     HRESULT r;
493     struct sized_chunk {
494         DWORD size;
495         unsigned char data[1];
496     } *chunk;
497
498     TRACE("%p\n",stm);
499
500     r = IStream_Read( stm, &size, sizeof(size), &count );
501     if( FAILED( r )  || count != sizeof(size) )
502         return E_FAIL;
503
504     chunk = HeapAlloc( GetProcessHeap(), 0, size );
505     if( !chunk )
506         return E_OUTOFMEMORY;
507
508     chunk->size = size;
509     r = IStream_Read( stm, chunk->data, size - sizeof(size), &count );
510     if( FAILED( r ) || count != (size - sizeof(size)) )
511     {
512         HeapFree( GetProcessHeap(), 0, chunk );
513         return E_FAIL;
514     }
515
516     TRACE("Read %ld bytes\n",chunk->size);
517
518     *data = (LPVOID) chunk;
519
520     return S_OK;
521 }
522
523 static BOOL Stream_LoadVolume( LOCAL_VOLUME_INFO *vol, volume_info *volume )
524 {
525     const int label_sz = sizeof volume->label/sizeof volume->label[0];
526     LPSTR label;
527     int len;
528
529     volume->serial = vol->dwVolSerial;
530     volume->type = vol->dwType;
531
532     if( !vol->dwVolLabelOfs )
533         return FALSE;
534     if( vol->dwSize <= vol->dwVolLabelOfs )
535         return FALSE;
536     len = vol->dwSize - vol->dwVolLabelOfs;
537
538     label = (LPSTR) vol;
539     label += vol->dwVolLabelOfs;
540     MultiByteToWideChar( CP_ACP, 0, label, len, volume->label, label_sz-1);
541
542     return TRUE;
543 }
544
545 static LPWSTR Stream_LoadPath( LPSTR p, DWORD maxlen )
546 {
547     int len = 0, wlen;
548     LPWSTR path;
549
550     while( p[len] && (len < maxlen) )
551         len++;
552
553     wlen = MultiByteToWideChar(CP_ACP, 0, p, len, NULL, 0);
554     path = HeapAlloc(GetProcessHeap(), 0, (wlen+1)*sizeof(WCHAR));
555     MultiByteToWideChar(CP_ACP, 0, p, len, path, wlen);
556     path[wlen] = 0;
557
558     return path;
559 }
560
561 static HRESULT Stream_LoadLocation( IStream *stm,
562                 volume_info *volume, LPWSTR *path )
563 {
564     unsigned char *p = NULL;
565     LOCATION_INFO *loc;
566     HRESULT r;
567     int n;
568
569     r = Stream_ReadChunk( stm, (LPVOID*) &p );
570     if( FAILED(r) )
571         return r;
572
573     loc = (LOCATION_INFO*) p;
574     if (loc->dwTotalSize < sizeof(LOCATION_INFO))
575     {
576         HeapFree( GetProcessHeap(), 0, p );
577         return E_FAIL;
578     }
579
580     /* if there's valid local volume information, load it */
581     if( loc->dwVolTableOfs && 
582        ((loc->dwVolTableOfs + sizeof(LOCAL_VOLUME_INFO)) <= loc->dwTotalSize) )
583     {
584         LOCAL_VOLUME_INFO *volume_info;
585
586         volume_info = (LOCAL_VOLUME_INFO*) &p[loc->dwVolTableOfs];
587         Stream_LoadVolume( volume_info, volume );
588     }
589
590     /* if there's a local path, load it */
591     n = loc->dwLocalPathOfs;
592     if( n && (n < loc->dwTotalSize) )
593         *path = Stream_LoadPath( &p[n], loc->dwTotalSize - n );
594
595     TRACE("type %ld serial %08lx name %s path %s\n", volume->type,
596           volume->serial, debugstr_w(volume->label), debugstr_w(*path));
597
598     HeapFree( GetProcessHeap(), 0, p );
599     return S_OK;
600 }
601
602 /*
603  *  The format of the advertised shortcut info seems to be:
604  *
605  *  Offset     Description
606  *  ------     -----------
607  *
608  *    0          Length of the block (4 bytes, usually 0x314)
609  *    4          tag (dword)
610  *    8          string data in ASCII
611  *    8+0x104    string data in UNICODE
612  *
613  * In the original Win32 implementation the buffers are not initialized
614  *  to zero, so data trailing the string is random garbage.
615  */
616 static HRESULT Stream_LoadAdvertiseInfo( IStream* stm, LPWSTR *str )
617 {
618     DWORD size;
619     ULONG count;
620     HRESULT r;
621     EXP_DARWIN_LINK buffer;
622     
623     TRACE("%p\n",stm);
624
625     r = IStream_Read( stm, &buffer.dbh.cbSize, sizeof (DWORD), &count );
626     if( FAILED( r ) )
627         return r;
628
629     /* make sure that we read the size of the structure even on error */
630     size = sizeof buffer - sizeof (DWORD);
631     if( buffer.dbh.cbSize != sizeof buffer )
632     {
633         ERR("Ooops.  This structure is not as expected...\n");
634         return E_FAIL;
635     }
636
637     r = IStream_Read( stm, &buffer.dbh.dwSignature, size, &count );
638     if( FAILED( r ) )
639         return r;
640
641     if( count != size )
642         return E_FAIL;
643
644     TRACE("magic %08lx  string = %s\n", buffer.dbh.dwSignature, debugstr_w(buffer.szwDarwinID));
645
646     if( (buffer.dbh.dwSignature&0xffff0000) != 0xa0000000 )
647     {
648         ERR("Unknown magic number %08lx in advertised shortcut\n", buffer.dbh.dwSignature);
649         return E_FAIL;
650     }
651
652     *str = HeapAlloc( GetProcessHeap(), 0, 
653                      (strlenW(buffer.szwDarwinID)+1) * sizeof(WCHAR) );
654     strcpyW( *str, buffer.szwDarwinID );
655
656     return S_OK;
657 }
658
659 /************************************************************************
660  * IPersistStream_Load (IPersistStream)
661  */
662 static HRESULT WINAPI IPersistStream_fnLoad(
663     IPersistStream*  iface,
664     IStream*         stm)
665 {
666     LINK_HEADER hdr;
667     ULONG    dwBytesRead;
668     BOOL     unicode;
669     HRESULT  r;
670     DWORD    zero;
671
672     _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
673
674     TRACE("%p %p\n", This, stm);
675
676     if( !stm )
677         return STG_E_INVALIDPOINTER;
678
679     dwBytesRead = 0;
680     r = IStream_Read(stm, &hdr, sizeof(hdr), &dwBytesRead);
681     if( FAILED( r ) )
682         return r;
683
684     if( dwBytesRead != sizeof(hdr))
685         return E_FAIL;
686     if( hdr.dwSize != sizeof(hdr))
687         return E_FAIL;
688     if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
689         return E_FAIL;
690
691     /* free all the old stuff */
692     ILFree(This->pPidl);
693     This->pPidl = NULL;
694     memset( &This->volume, 0, sizeof This->volume );
695     HeapFree(GetProcessHeap(), 0, This->sPath);
696     This->sPath = NULL;
697     HeapFree(GetProcessHeap(), 0, This->sDescription);
698     This->sDescription = NULL;
699     HeapFree(GetProcessHeap(), 0, This->sPathRel);
700     This->sPathRel = NULL;
701     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
702     This->sWorkDir = NULL;
703     HeapFree(GetProcessHeap(), 0, This->sArgs);
704     This->sArgs = NULL;
705     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
706     This->sIcoPath = NULL;
707     HeapFree(GetProcessHeap(), 0, This->sProduct);
708     This->sProduct = NULL;
709     HeapFree(GetProcessHeap(), 0, This->sComponent);
710     This->sComponent = NULL;
711         
712     This->wHotKey = (WORD)hdr.wHotKey;
713     This->iIcoNdx = hdr.nIcon;
714     FileTimeToSystemTime (&hdr.Time1, &This->time1);
715     FileTimeToSystemTime (&hdr.Time2, &This->time2);
716     FileTimeToSystemTime (&hdr.Time3, &This->time3);
717     if (TRACE_ON(shell))
718     {
719         WCHAR sTemp[MAX_PATH];
720         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time1,
721                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
722         TRACE("-- time1: %s\n", debugstr_w(sTemp) );
723         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time2,
724                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
725         TRACE("-- time2: %s\n", debugstr_w(sTemp) );
726         GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE, &This->time3,
727                        NULL, sTemp, sizeof(sTemp)/sizeof(*sTemp));
728         TRACE("-- time3: %s\n", debugstr_w(sTemp) );
729     }
730
731     /* load all the new stuff */
732     if( hdr.dwFlags & SLDF_HAS_ID_LIST )
733     {
734         r = ILLoadFromStream( stm, &This->pPidl );
735         if( FAILED( r ) )
736             return r;
737     }
738     pdump(This->pPidl);
739
740     /* load the location information */
741     if( hdr.dwFlags & SLDF_HAS_LINK_INFO )
742         r = Stream_LoadLocation( stm, &This->volume, &This->sPath );
743     if( FAILED( r ) )
744         goto end;
745
746     unicode = hdr.dwFlags & SLDF_UNICODE;
747     if( hdr.dwFlags & SLDF_HAS_NAME )
748     {
749         r = Stream_LoadString( stm, unicode, &This->sDescription );
750         TRACE("Description  -> %s\n",debugstr_w(This->sDescription));
751     }
752     if( FAILED( r ) )
753         goto end;
754
755     if( hdr.dwFlags & SLDF_HAS_RELPATH )
756     {
757         r = Stream_LoadString( stm, unicode, &This->sPathRel );
758         TRACE("Relative Path-> %s\n",debugstr_w(This->sPathRel));
759     }
760     if( FAILED( r ) )
761         goto end;
762
763     if( hdr.dwFlags & SLDF_HAS_WORKINGDIR )
764     {
765         r = Stream_LoadString( stm, unicode, &This->sWorkDir );
766         TRACE("Working Dir  -> %s\n",debugstr_w(This->sWorkDir));
767     }
768     if( FAILED( r ) )
769         goto end;
770
771     if( hdr.dwFlags & SLDF_HAS_ARGS )
772     {
773         r = Stream_LoadString( stm, unicode, &This->sArgs );
774         TRACE("Working Dir  -> %s\n",debugstr_w(This->sArgs));
775     }
776     if( FAILED( r ) )
777         goto end;
778
779     if( hdr.dwFlags & SLDF_HAS_ICONLOCATION )
780     {
781         r = Stream_LoadString( stm, unicode, &This->sIcoPath );
782         TRACE("Icon file    -> %s\n",debugstr_w(This->sIcoPath));
783     }
784     if( FAILED( r ) )
785         goto end;
786
787     if( hdr.dwFlags & SLDF_HAS_LOGO3ID )
788     {
789         r = Stream_LoadAdvertiseInfo( stm, &This->sProduct );
790         TRACE("Product      -> %s\n",debugstr_w(This->sProduct));
791     }
792     if( FAILED( r ) )
793         goto end;
794
795     if( hdr.dwFlags & SLDF_HAS_DARWINID )
796     {
797         r = Stream_LoadAdvertiseInfo( stm, &This->sComponent );
798         TRACE("Component    -> %s\n",debugstr_w(This->sComponent));
799     }
800     if( FAILED( r ) )
801         goto end;
802
803     r = IStream_Read(stm, &zero, sizeof zero, &dwBytesRead);
804     if( FAILED( r ) || zero || dwBytesRead != sizeof zero )
805         ERR("Last word was not zero\n");
806
807     TRACE("OK\n");
808
809     pdump (This->pPidl);
810
811     return S_OK;
812 end:
813     return r;
814 }
815
816 /************************************************************************
817  * Stream_WriteString
818  *
819  * Helper function for IPersistStream_Save. Writes a unicode string 
820  *  with terminating nul byte to a stream, preceded by the its length.
821  */
822 static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
823 {
824     USHORT len = lstrlenW( str ) + 1;
825     DWORD count;
826     HRESULT r;
827
828     r = IStream_Write( stm, &len, sizeof(len), &count );
829     if( FAILED( r ) )
830         return r;
831
832     len *= sizeof(WCHAR);
833
834     r = IStream_Write( stm, str, len, &count );
835     if( FAILED( r ) )
836         return r;
837
838     return S_OK;
839 }
840
841 /************************************************************************
842  * Stream_WriteLocationInfo
843  *
844  * Writes the location info to a stream
845  *
846  * FIXME: One day we might want to write the network volume information
847  *        and the final path.
848  *        Figure out how Windows deals with unicode paths here.
849  */
850 static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR path,
851                                          volume_info *volume )
852 {
853     DWORD total_size, path_size, volume_info_size, label_size, final_path_size;
854     LOCAL_VOLUME_INFO *vol;
855     LOCATION_INFO *loc;
856     LPSTR szLabel, szPath, szFinalPath;
857     ULONG count = 0;
858
859     TRACE("%p %s %p\n", stm, debugstr_w(path), volume);
860
861     /* figure out the size of everything */
862     label_size = WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
863                                       NULL, 0, NULL, NULL );
864     path_size = WideCharToMultiByte( CP_ACP, 0, path, -1,
865                                      NULL, 0, NULL, NULL );
866     volume_info_size = sizeof *vol + label_size;
867     final_path_size = 1;
868     total_size = sizeof *loc + volume_info_size + path_size + final_path_size;
869
870     /* create pointers to everything */
871     loc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total_size);
872     vol = (LOCAL_VOLUME_INFO*) &loc[1];
873     szLabel = (LPSTR) &vol[1];
874     szPath = &szLabel[label_size];
875     szFinalPath = &szPath[path_size];
876
877     /* fill in the location information header */
878     loc->dwTotalSize = total_size;
879     loc->dwHeaderSize = sizeof (*loc);
880     loc->dwFlags = 1;
881     loc->dwVolTableOfs = sizeof (*loc);
882     loc->dwLocalPathOfs = sizeof (*loc) + volume_info_size;
883     loc->dwNetworkVolTableOfs = 0;
884     loc->dwFinalPathOfs = sizeof (*loc) + volume_info_size + path_size;
885
886     /* fill in the volume information */
887     vol->dwSize = volume_info_size;
888     vol->dwType = volume->type;
889     vol->dwVolSerial = volume->serial;
890     vol->dwVolLabelOfs = sizeof (*vol);
891
892     /* copy in the strings */
893     WideCharToMultiByte( CP_ACP, 0, volume->label, -1,
894                          szLabel, label_size, NULL, NULL );
895     WideCharToMultiByte( CP_ACP, 0, path, -1,
896                          szPath, path_size, NULL, NULL );
897     szFinalPath[0] = 0;
898
899     return IStream_Write( stm, loc, total_size, &count );
900 }
901
902 static HRESULT Stream_WriteAdvertiseInfo( IStream* stm, LPCWSTR string, DWORD magic )
903 {
904     ULONG count;
905     EXP_DARWIN_LINK buffer;
906     
907     TRACE("%p\n",stm);
908
909     memset( &buffer, 0, sizeof buffer );
910     buffer.dbh.cbSize = sizeof buffer;
911     buffer.dbh.dwSignature = magic;
912     lstrcpynW( buffer.szwDarwinID, string, MAX_PATH );
913     WideCharToMultiByte(CP_ACP, 0, string, -1, buffer.szDarwinID, MAX_PATH, NULL, NULL );
914
915     return IStream_Write( stm, &buffer, buffer.dbh.cbSize, &count );
916 }
917
918 /************************************************************************
919  * IPersistStream_Save (IPersistStream)
920  *
921  * FIXME: makes assumptions about byte order
922  */
923 static HRESULT WINAPI IPersistStream_fnSave(
924         IPersistStream*  iface,
925         IStream*         stm,
926         BOOL             fClearDirty)
927 {
928     static const WCHAR wOpen[] = {'o','p','e','n',0};
929
930     LINK_HEADER header;
931     WCHAR   exePath[MAX_PATH];
932     ULONG   count;
933     DWORD   zero;
934     HRESULT r;
935
936     _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
937
938     TRACE("%p %p %x\n", This, stm, fClearDirty);
939
940     *exePath = '\0';
941
942     if (This->sPath)
943     {
944         SHELL_FindExecutable(NULL, This->sPath, wOpen, exePath, MAX_PATH,
945                              NULL, NULL, NULL, NULL);
946         /*
947          * windows can create lnk files to executables that do not exist yet
948          * so if the executable does not exist the just trust the path they
949          * gave us
950          */
951         if (!*exePath) strcpyW(exePath,This->sPath);
952     }
953
954     memset(&header, 0, sizeof(header));
955     header.dwSize = sizeof(header);
956     header.fStartup = This->iShowCmd;
957     memcpy(&header.MagicGuid, &CLSID_ShellLink, sizeof(header.MagicGuid) );
958
959     header.wHotKey = This->wHotKey;
960     header.nIcon = This->iIcoNdx;
961     header.dwFlags = SLDF_UNICODE;   /* strings are in unicode */
962     if( This->pPidl )
963         header.dwFlags |= SLDF_HAS_ID_LIST;
964     if( This->sPath )
965         header.dwFlags |= SLDF_HAS_LINK_INFO;
966     if( This->sDescription )
967         header.dwFlags |= SLDF_HAS_NAME;
968     if( This->sWorkDir )
969         header.dwFlags |= SLDF_HAS_WORKINGDIR;
970     if( This->sArgs )
971         header.dwFlags |= SLDF_HAS_ARGS;
972     if( This->sIcoPath )
973         header.dwFlags |= SLDF_HAS_ICONLOCATION;
974     if( This->sProduct )
975         header.dwFlags |= SLDF_HAS_LOGO3ID;
976     if( This->sComponent )
977         header.dwFlags |= SLDF_HAS_DARWINID;
978
979     SystemTimeToFileTime ( &This->time1, &header.Time1 );
980     SystemTimeToFileTime ( &This->time2, &header.Time2 );
981     SystemTimeToFileTime ( &This->time3, &header.Time3 );
982
983     /* write the Shortcut header */
984     r = IStream_Write( stm, &header, sizeof(header), &count );
985     if( FAILED( r ) )
986     {
987         ERR("Write failed at %d\n",__LINE__);
988         return r;
989     }
990
991     TRACE("Writing pidl \n");
992
993     /* write the PIDL to the shortcut */
994     if( This->pPidl )
995     {
996         r = ILSaveToStream( stm, This->pPidl );
997         if( FAILED( r ) )
998         {
999             ERR("Failed to write PIDL at %d\n",__LINE__);
1000             return r;
1001         }
1002     }
1003
1004     if( This->sPath )
1005         Stream_WriteLocationInfo( stm, exePath, &This->volume );
1006
1007     if( This->sDescription )
1008         r = Stream_WriteString( stm, This->sDescription );
1009
1010     if( This->sPathRel )
1011         r = Stream_WriteString( stm, This->sPathRel );
1012
1013     if( This->sWorkDir )
1014         r = Stream_WriteString( stm, This->sWorkDir );
1015
1016     if( This->sArgs )
1017         r = Stream_WriteString( stm, This->sArgs );
1018
1019     if( This->sIcoPath )
1020         r = Stream_WriteString( stm, This->sIcoPath );
1021
1022     if( This->sProduct )
1023         r = Stream_WriteAdvertiseInfo( stm, This->sProduct, EXP_SZ_ICON_SIG );
1024
1025     if( This->sComponent )
1026         r = Stream_WriteAdvertiseInfo( stm, This->sComponent, EXP_DARWIN_ID_SIG );
1027
1028     /* the last field is a single zero dword */
1029     zero = 0;
1030     r = IStream_Write( stm, &zero, sizeof zero, &count );
1031
1032     return S_OK;
1033 }
1034
1035 /************************************************************************
1036  * IPersistStream_GetSizeMax (IPersistStream)
1037  */
1038 static HRESULT WINAPI IPersistStream_fnGetSizeMax(
1039         IPersistStream*  iface,
1040         ULARGE_INTEGER*  pcbSize)
1041 {
1042         _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
1043
1044         TRACE("(%p)\n", This);
1045
1046         return E_NOTIMPL;
1047 }
1048
1049 static IPersistStreamVtbl psvt =
1050 {
1051         IPersistStream_fnQueryInterface,
1052         IPersistStream_fnAddRef,
1053         IPersistStream_fnRelease,
1054         IPersistStream_fnGetClassID,
1055         IPersistStream_fnIsDirty,
1056         IPersistStream_fnLoad,
1057         IPersistStream_fnSave,
1058         IPersistStream_fnGetSizeMax
1059 };
1060
1061 /**************************************************************************
1062  *        IShellLink_Constructor
1063  */
1064 HRESULT WINAPI IShellLink_Constructor( IUnknown *pUnkOuter,
1065                REFIID riid, LPVOID *ppv )
1066 {
1067         IShellLinkImpl * sl;
1068
1069         TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
1070
1071         *ppv = NULL;
1072
1073         if (pUnkOuter)
1074             return CLASS_E_NOAGGREGATION;
1075         sl = LocalAlloc(LMEM_ZEROINIT,sizeof(IShellLinkImpl));
1076         if (!sl)
1077             return E_OUTOFMEMORY;
1078
1079         sl->ref = 1;
1080         sl->lpVtbl = &slvt;
1081         sl->lpvtblw = &slvtw;
1082         sl->lpvtblPersistFile = &pfvt;
1083         sl->lpvtblPersistStream = &psvt;
1084         sl->iShowCmd = SW_SHOWNORMAL;
1085         sl->bDirty = FALSE;
1086
1087         TRACE("(%p)->()\n",sl);
1088
1089         if (IsEqualIID(riid, &IID_IUnknown) ||
1090             IsEqualIID(riid, &IID_IShellLinkA))
1091             *ppv = sl;
1092         else if (IsEqualIID(riid, &IID_IShellLinkW))
1093             *ppv = &(sl->lpvtblw);
1094         else {
1095             LocalFree((HLOCAL)sl);
1096             ERR("E_NOINTERFACE\n");
1097             return E_NOINTERFACE;
1098         }
1099
1100         return S_OK;
1101 }
1102
1103
1104 static BOOL SHELL_ExistsFileW(LPCWSTR path)
1105 {
1106     if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
1107         return FALSE;
1108     return TRUE;
1109 }
1110
1111 /**************************************************************************
1112  *  ShellLink_UpdatePath
1113  *      update absolute path in sPath using relative path in sPathRel
1114  */
1115 static HRESULT ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
1116 {
1117     if (!path || !psPath)
1118         return E_INVALIDARG;
1119
1120     if (!*psPath && sPathRel) {
1121         WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
1122         LPWSTR final = NULL;
1123
1124         /* first try if [directory of link file] + [relative path] finds an existing file */
1125
1126         GetFullPathNameW( path, MAX_PATH*2, buffer, &final );
1127         if( !final )
1128             final = buffer;
1129         lstrcpyW(final, sPathRel);
1130
1131         *abs_path = '\0';
1132
1133         if (SHELL_ExistsFileW(buffer)) {
1134             if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
1135                 lstrcpyW(abs_path, buffer);
1136         } else {
1137             /* try if [working directory] + [relative path] finds an existing file */
1138             if (sWorkDir) {
1139                 lstrcpyW(buffer, sWorkDir);
1140                 lstrcpyW(PathAddBackslashW(buffer), sPathRel);
1141
1142                 if (SHELL_ExistsFileW(buffer))
1143                     if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
1144                         lstrcpyW(abs_path, buffer);
1145             }
1146         }
1147
1148         /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
1149         if (!*abs_path)
1150             lstrcpyW(abs_path, sPathRel);
1151
1152         *psPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(abs_path)+1)*sizeof(WCHAR));
1153         if (!*psPath)
1154             return E_OUTOFMEMORY;
1155
1156         lstrcpyW(*psPath, abs_path);
1157     }
1158
1159     return S_OK;
1160 }
1161
1162 /**************************************************************************
1163  *        IShellLink_ConstructFromFile
1164  */
1165 HRESULT WINAPI IShellLink_ConstructFromFile( IUnknown* pUnkOuter, REFIID riid,
1166                LPCITEMIDLIST pidl, LPVOID* ppv)
1167 {
1168     IShellLinkW* psl;
1169
1170     HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl);
1171
1172     if (SUCCEEDED(hr)) {
1173         IPersistFile* ppf;
1174
1175         *ppv = NULL;
1176
1177         hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
1178
1179         if (SUCCEEDED(hr)) {
1180             WCHAR path[MAX_PATH];
1181
1182             if (SHGetPathFromIDListW(pidl, path)) 
1183                 hr = IPersistFile_Load(ppf, path, 0);
1184             else
1185                 hr = E_FAIL;
1186
1187             if (SUCCEEDED(hr))
1188                 *ppv = (IUnknown*) psl;
1189
1190             IPersistFile_Release(ppf);
1191         }
1192
1193         if (!*ppv)
1194             IShellLinkW_Release(psl);
1195     }
1196
1197     return hr;
1198 }
1199
1200 /**************************************************************************
1201  *  IShellLinkA_QueryInterface
1202  */
1203 static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid,  LPVOID *ppvObj)
1204 {
1205         IShellLinkImpl *This = (IShellLinkImpl *)iface;
1206
1207         TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
1208
1209         *ppvObj = NULL;
1210
1211         if(IsEqualIID(riid, &IID_IUnknown) ||
1212            IsEqualIID(riid, &IID_IShellLinkA))
1213         {
1214           *ppvObj = This;
1215         }
1216         else if(IsEqualIID(riid, &IID_IShellLinkW))
1217         {
1218           *ppvObj = (IShellLinkW *)&(This->lpvtblw);
1219         }
1220         else if(IsEqualIID(riid, &IID_IPersistFile))
1221         {
1222           *ppvObj = (IPersistFile *)&(This->lpvtblPersistFile);
1223         }
1224         else if(IsEqualIID(riid, &IID_IPersistStream))
1225         {
1226           *ppvObj = (IPersistStream *)&(This->lpvtblPersistStream);
1227         }
1228
1229         if(*ppvObj)
1230         {
1231           IUnknown_AddRef((IUnknown*)(*ppvObj));
1232           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
1233           return S_OK;
1234         }
1235         TRACE("-- Interface: E_NOINTERFACE\n");
1236         return E_NOINTERFACE;
1237 }
1238 /******************************************************************************
1239  * IShellLinkA_AddRef
1240  */
1241 static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
1242 {
1243         IShellLinkImpl *This = (IShellLinkImpl *)iface;
1244         ULONG refCount = InterlockedIncrement(&This->ref);
1245
1246         TRACE("(%p)->(count=%lu)\n", This, refCount - 1);
1247
1248         return refCount;
1249 }
1250 /******************************************************************************
1251  *      IShellLinkA_Release
1252  */
1253 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
1254 {
1255     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1256     ULONG refCount = InterlockedDecrement(&This->ref);
1257
1258     TRACE("(%p)->(count=%lu)\n", This, refCount + 1);
1259
1260     if (refCount)
1261         return refCount;
1262
1263     TRACE("-- destroying IShellLink(%p)\n",This);
1264
1265     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1266     HeapFree(GetProcessHeap(), 0, This->sArgs);
1267     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1268     HeapFree(GetProcessHeap(), 0, This->sDescription);
1269     HeapFree(GetProcessHeap(),0,This->sPath);
1270
1271     if (This->pPidl)
1272         ILFree(This->pPidl);
1273
1274     LocalFree((HANDLE)This);
1275
1276     return 0;
1277 }
1278
1279 static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
1280                   INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1281 {
1282     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1283
1284     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
1285           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1286
1287     if (This->sComponent || This->sProduct)
1288         return S_FALSE;
1289
1290     if (cchMaxPath)
1291         pszFile[0] = 0;
1292     if (This->sPath)
1293         WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
1294                              pszFile, cchMaxPath, NULL, NULL);
1295
1296     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1297
1298     return S_OK;
1299 }
1300
1301 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
1302 {
1303     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1304
1305     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1306
1307     return IShellLinkW_GetIDList((IShellLinkW*)&(This->lpvtblw), ppidl);
1308 }
1309
1310 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
1311 {
1312     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1313
1314     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1315
1316     if (This->pPidl)
1317         ILFree(This->pPidl);
1318     This->pPidl = ILClone (pidl);
1319     This->bDirty = TRUE;
1320
1321     return S_OK;
1322 }
1323
1324 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
1325 {
1326     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1327
1328     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1329
1330     if( cchMaxName )
1331         pszName[0] = 0;
1332     if( This->sDescription )
1333         WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
1334             pszName, cchMaxName, NULL, NULL);
1335
1336     return S_OK;
1337 }
1338 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
1339 {
1340     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1341
1342     TRACE("(%p)->(pName=%s)\n", This, pszName);
1343
1344     HeapFree(GetProcessHeap(), 0, This->sDescription);
1345     This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
1346     if ( !This->sDescription )
1347         return E_OUTOFMEMORY;
1348
1349     This->bDirty = TRUE;
1350
1351     return S_OK;
1352 }
1353
1354 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
1355 {
1356     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1357
1358     TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1359
1360     if( cchMaxPath )
1361         pszDir[0] = 0;
1362     if( This->sWorkDir )
1363         WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
1364                              pszDir, cchMaxPath, NULL, NULL);
1365
1366     return S_OK;
1367 }
1368
1369 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
1370 {
1371     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1372
1373     TRACE("(%p)->(dir=%s)\n",This, pszDir);
1374
1375     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1376     This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
1377     if ( !This->sWorkDir )
1378         return E_OUTOFMEMORY;
1379
1380     This->bDirty = TRUE;
1381
1382     return S_OK;
1383 }
1384
1385 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
1386 {
1387     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1388
1389     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1390
1391     if( cchMaxPath )
1392         pszArgs[0] = 0;
1393     if( This->sArgs )
1394         WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
1395                              pszArgs, cchMaxPath, NULL, NULL);
1396
1397     return S_OK;
1398 }
1399
1400 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
1401 {
1402     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1403
1404     TRACE("(%p)->(args=%s)\n",This, pszArgs);
1405
1406     HeapFree(GetProcessHeap(), 0, This->sArgs);
1407     This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
1408     if( !This->sArgs )
1409         return E_OUTOFMEMORY;
1410
1411     This->bDirty = TRUE;
1412
1413     return S_OK;
1414 }
1415
1416 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
1417 {
1418         IShellLinkImpl *This = (IShellLinkImpl *)iface;
1419
1420         TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1421
1422         *pwHotkey = This->wHotKey;
1423
1424         return S_OK;
1425 }
1426
1427 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
1428 {
1429         IShellLinkImpl *This = (IShellLinkImpl *)iface;
1430
1431         TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1432
1433         This->wHotKey = wHotkey;
1434         This->bDirty = TRUE;
1435
1436         return S_OK;
1437 }
1438
1439 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
1440 {
1441     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1442
1443     TRACE("(%p)->(%p)\n",This, piShowCmd);
1444     *piShowCmd = This->iShowCmd;
1445     return S_OK;
1446 }
1447
1448 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
1449 {
1450     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1451
1452     TRACE("(%p) %d\n",This, iShowCmd);
1453
1454     This->iShowCmd = iShowCmd;
1455     This->bDirty = TRUE;
1456
1457     return NOERROR;
1458 }
1459
1460 static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPITEMIDLIST pidl, LPSTR pszIconPath, int cchIconPath, int* piIcon)
1461 {
1462     LPCITEMIDLIST pidlLast;
1463
1464     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1465
1466     if (SUCCEEDED(hr)) {
1467         IExtractIconA* pei;
1468
1469         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconA, NULL, (LPVOID*)&pei);
1470
1471         if (SUCCEEDED(hr)) {
1472             hr = IExtractIconA_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1473
1474             IExtractIconA_Release(pei);
1475         }
1476
1477         IShellFolder_Release(psf);
1478     }
1479
1480     return hr;
1481 }
1482
1483 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath,INT cchIconPath,INT *piIcon)
1484 {
1485     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1486
1487     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1488
1489     pszIconPath[0] = 0;
1490     *piIcon = This->iIcoNdx;
1491
1492     if (This->sIcoPath)
1493     {
1494         WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1495         return S_OK;
1496     }
1497
1498     if (This->pPidl || This->sPath)
1499     {
1500         IShellFolder* pdsk;
1501
1502         HRESULT hr = SHGetDesktopFolder(&pdsk);
1503
1504         if (SUCCEEDED(hr))
1505         {
1506             /* first look for an icon using the PIDL (if present) */
1507             if (This->pPidl)
1508                 hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1509             else
1510                 hr = E_FAIL;
1511
1512             /* if we couldn't find an icon yet, look for it using the file system path */
1513             if (FAILED(hr) && This->sPath)
1514             {
1515                 LPITEMIDLIST pidl;
1516
1517                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1518
1519                 if (SUCCEEDED(hr)) {
1520                     hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1521
1522                     SHFree(pidl);
1523                 }
1524             }
1525
1526             IShellFolder_Release(pdsk);
1527         }
1528
1529         return hr;
1530     }
1531     return S_OK;
1532 }
1533
1534 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath,INT iIcon)
1535 {
1536     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1537
1538     TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1539
1540     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1541     This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1542     if ( !This->sIcoPath )
1543         return E_OUTOFMEMORY;
1544
1545     This->iIcoNdx = iIcon;
1546     This->bDirty = TRUE;
1547
1548     return S_OK;
1549 }
1550
1551 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1552 {
1553     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1554
1555     TRACE("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved);
1556
1557     HeapFree(GetProcessHeap(), 0, This->sPathRel);
1558     This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1559     This->bDirty = TRUE;
1560
1561     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1562 }
1563
1564 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1565 {
1566     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1567
1568     TRACE("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1569
1570     return IShellLinkW_Resolve( (IShellLinkW*)&(This->lpvtblw), hwnd, fFlags );
1571 }
1572
1573 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1574 {
1575     HRESULT r;
1576     LPWSTR str;
1577     IShellLinkImpl *This = (IShellLinkImpl *)iface;
1578
1579     TRACE("(%p)->(path=%s)\n",This, pszFile);
1580
1581     str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
1582     if( !str ) 
1583         return E_OUTOFMEMORY;
1584
1585     r = IShellLinkW_SetPath((IShellLinkW*)&(This->lpvtblw), str);
1586     HeapFree( GetProcessHeap(), 0, str );
1587
1588     return r;
1589 }
1590
1591 /**************************************************************************
1592 * IShellLink Implementation
1593 */
1594
1595 static IShellLinkAVtbl slvt =
1596 {
1597         IShellLinkA_fnQueryInterface,
1598         IShellLinkA_fnAddRef,
1599         IShellLinkA_fnRelease,
1600         IShellLinkA_fnGetPath,
1601         IShellLinkA_fnGetIDList,
1602         IShellLinkA_fnSetIDList,
1603         IShellLinkA_fnGetDescription,
1604         IShellLinkA_fnSetDescription,
1605         IShellLinkA_fnGetWorkingDirectory,
1606         IShellLinkA_fnSetWorkingDirectory,
1607         IShellLinkA_fnGetArguments,
1608         IShellLinkA_fnSetArguments,
1609         IShellLinkA_fnGetHotkey,
1610         IShellLinkA_fnSetHotkey,
1611         IShellLinkA_fnGetShowCmd,
1612         IShellLinkA_fnSetShowCmd,
1613         IShellLinkA_fnGetIconLocation,
1614         IShellLinkA_fnSetIconLocation,
1615         IShellLinkA_fnSetRelativePath,
1616         IShellLinkA_fnResolve,
1617         IShellLinkA_fnSetPath
1618 };
1619
1620
1621 /**************************************************************************
1622  *  IShellLinkW_fnQueryInterface
1623  */
1624 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1625   IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1626 {
1627         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1628
1629         return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
1630 }
1631
1632 /******************************************************************************
1633  * IShellLinkW_fnAddRef
1634  */
1635 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1636 {
1637         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1638
1639         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1640
1641         return IShellLinkA_AddRef((IShellLinkA*)This);
1642 }
1643 /******************************************************************************
1644  * IShellLinkW_fnRelease
1645  */
1646
1647 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1648 {
1649         _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1650
1651         TRACE("(%p)->(count=%lu)\n",This,This->ref);
1652
1653         return IShellLinkA_Release((IShellLinkA*)This);
1654 }
1655
1656 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1657 {
1658     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1659
1660     TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
1661           This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1662
1663     if (This->sComponent || This->sProduct)
1664         return S_FALSE;
1665
1666     if (cchMaxPath)
1667         pszFile[0] = 0;
1668     if (This->sPath)
1669         lstrcpynW( pszFile, This->sPath, cchMaxPath );
1670
1671     if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1672
1673     return S_OK;
1674 }
1675
1676 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1677 {
1678     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1679
1680     TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1681
1682     if (!This->pPidl)
1683         return S_FALSE;
1684     *ppidl = ILClone(This->pPidl);
1685     return S_OK;
1686 }
1687
1688 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1689 {
1690     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1691
1692     TRACE("(%p)->(pidl=%p)\n",This, pidl);
1693
1694     if( This->pPidl )
1695         ILFree( This->pPidl );
1696     This->pPidl = ILClone( pidl );
1697     if( !This->pPidl )
1698         return E_FAIL;
1699
1700     This->bDirty = TRUE;
1701
1702     return S_OK;
1703 }
1704
1705 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1706 {
1707     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1708
1709     TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1710
1711     pszName[0] = 0;
1712     if( This->sDescription )
1713         lstrcpynW( pszName, This->sDescription, cchMaxName );
1714
1715     return S_OK;
1716 }
1717
1718 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1719 {
1720     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1721
1722     TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1723
1724     HeapFree(GetProcessHeap(), 0, This->sDescription);
1725     This->sDescription = HeapAlloc( GetProcessHeap(), 0,
1726                                     (lstrlenW( pszName )+1)*sizeof(WCHAR) );
1727     if ( !This->sDescription )
1728         return E_OUTOFMEMORY;
1729
1730     lstrcpyW( This->sDescription, pszName );
1731     This->bDirty = TRUE;
1732
1733     return S_OK;
1734 }
1735
1736 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1737 {
1738     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1739
1740     TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1741
1742     if( cchMaxPath )
1743         pszDir[0] = 0;
1744     if( This->sWorkDir )
1745         lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
1746
1747     return S_OK;
1748 }
1749
1750 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1751 {
1752     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1753
1754     TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1755
1756     HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1757     This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
1758                                 (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
1759     if ( !This->sWorkDir )
1760         return E_OUTOFMEMORY;
1761     lstrcpyW( This->sWorkDir, pszDir );
1762     This->bDirty = TRUE;
1763
1764     return S_OK;
1765 }
1766
1767 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1768 {
1769     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1770
1771     TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1772
1773     if( cchMaxPath )
1774         pszArgs[0] = 0;
1775     if( This->sArgs )
1776         lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
1777
1778     return NOERROR;
1779 }
1780
1781 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1782 {
1783     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1784
1785     TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1786
1787     HeapFree(GetProcessHeap(), 0, This->sArgs);
1788     This->sArgs = HeapAlloc( GetProcessHeap(), 0,
1789                              (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
1790     if ( !This->sArgs )
1791         return E_OUTOFMEMORY;
1792     lstrcpyW( This->sArgs, pszArgs );
1793     This->bDirty = TRUE;
1794
1795     return S_OK;
1796 }
1797
1798 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1799 {
1800     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1801
1802     TRACE("(%p)->(%p)\n",This, pwHotkey);
1803
1804     *pwHotkey=This->wHotKey;
1805
1806     return S_OK;
1807 }
1808
1809 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1810 {
1811     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1812
1813     TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1814
1815     This->wHotKey = wHotkey;
1816     This->bDirty = TRUE;
1817
1818     return S_OK;
1819 }
1820
1821 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1822 {
1823     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1824
1825     TRACE("(%p)->(%p)\n",This, piShowCmd);
1826
1827     *piShowCmd = This->iShowCmd;
1828
1829     return S_OK;
1830 }
1831
1832 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1833 {
1834     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1835
1836     This->iShowCmd = iShowCmd;
1837     This->bDirty = TRUE;
1838
1839     return S_OK;
1840 }
1841
1842 static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPITEMIDLIST pidl, LPWSTR pszIconPath, int cchIconPath, int* piIcon)
1843 {
1844     LPCITEMIDLIST pidlLast;
1845
1846     HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1847
1848     if (SUCCEEDED(hr)) {
1849         IExtractIconW* pei;
1850
1851         hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconW, NULL, (LPVOID*)&pei);
1852
1853         if (SUCCEEDED(hr)) {
1854             hr = IExtractIconW_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1855
1856             IExtractIconW_Release(pei);
1857         }
1858
1859         IShellFolder_Release(psf);
1860     }
1861
1862     return hr;
1863 }
1864
1865 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath,INT cchIconPath,INT *piIcon)
1866 {
1867     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1868
1869     TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1870
1871     pszIconPath[0] = 0;
1872     *piIcon = This->iIcoNdx;
1873
1874     if (This->sIcoPath)
1875     {
1876         lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
1877         return S_OK;
1878     }
1879
1880     if (This->pPidl || This->sPath)
1881     {
1882         IShellFolder* pdsk;
1883
1884         HRESULT hr = SHGetDesktopFolder(&pdsk);
1885
1886         if (SUCCEEDED(hr))
1887         {
1888             /* first look for an icon using the PIDL (if present) */
1889             if (This->pPidl)
1890                 hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1891             else
1892                 hr = E_FAIL;
1893
1894             /* if we couldn't find an icon yet, look for it using the file system path */
1895             if (FAILED(hr) && This->sPath)
1896             {
1897                 LPITEMIDLIST pidl;
1898
1899                 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1900
1901                 if (SUCCEEDED(hr))
1902                 {
1903                     hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1904
1905                     SHFree(pidl);
1906                 }
1907             }
1908
1909             IShellFolder_Release(pdsk);
1910         }
1911         return hr;
1912     }
1913     return S_OK;
1914 }
1915
1916 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath,INT iIcon)
1917 {
1918     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1919
1920     TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1921
1922     HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1923     This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
1924                                 (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
1925     if ( !This->sIcoPath )
1926         return E_OUTOFMEMORY;
1927     lstrcpyW( This->sIcoPath, pszIconPath );
1928
1929     This->iIcoNdx = iIcon;
1930     This->bDirty = TRUE;
1931
1932     return S_OK;
1933 }
1934
1935 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
1936 {
1937     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1938
1939     TRACE("(%p)->(path=%s %lx)\n",This, debugstr_w(pszPathRel), dwReserved);
1940
1941     HeapFree(GetProcessHeap(), 0, This->sPathRel);
1942     This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
1943                                 (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
1944     if ( !This->sPathRel )
1945         return E_OUTOFMEMORY;
1946     lstrcpyW( This->sPathRel, pszPathRel );
1947     This->bDirty = TRUE;
1948
1949     return ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1950 }
1951
1952 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
1953 {
1954     HRESULT hr = S_OK;
1955
1956     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1957
1958     TRACE("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1959
1960     /*FIXME: use IResolveShellLink interface */
1961
1962     if (!This->sPath && This->pPidl) {
1963         WCHAR buffer[MAX_PATH];
1964
1965         hr = SHELL_GetPathFromIDListW(This->pPidl, buffer, MAX_PATH);
1966
1967         if (SUCCEEDED(hr) && *buffer) {
1968             This->sPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
1969             if (!This->sPath)
1970                 return E_OUTOFMEMORY;
1971
1972             lstrcpyW(This->sPath, buffer);
1973
1974             This->bDirty = TRUE;
1975         } else
1976             hr = S_OK;    /* don't report an error occurred while just caching information */
1977     }
1978
1979     if (!This->sIcoPath && This->sPath) {
1980         This->sIcoPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
1981         if (!This->sIcoPath)
1982             return E_OUTOFMEMORY;
1983
1984         lstrcpyW(This->sIcoPath, This->sPath);
1985         This->iIcoNdx = 0;
1986
1987         This->bDirty = TRUE;
1988     }
1989
1990     return hr;
1991 }
1992
1993 static LPWSTR ShellLink_GetAdvertisedArg(LPCWSTR str)
1994 {
1995     LPWSTR ret;
1996     LPCWSTR p;
1997     DWORD len;
1998
1999     if( !str )
2000         return NULL;
2001
2002     p = strchrW( str, ':' );
2003     if( !p )
2004         return NULL;
2005     len = p - str;
2006     ret = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1));
2007     if( !ret )
2008         return ret;
2009     memcpy( ret, str, sizeof(WCHAR)*len );
2010     ret[len] = 0;
2011     return ret;
2012 }
2013
2014 static HRESULT ShellLink_SetAdvertiseInfo(IShellLinkImpl *This, LPCWSTR str)
2015 {
2016     LPCWSTR szComponent = NULL, szProduct = NULL, p;
2017     WCHAR szGuid[39];
2018     HRESULT r;
2019     GUID guid;
2020     int len;
2021
2022     while( str[0] )
2023     {
2024         /* each segment must start with two colons */
2025         if( str[0] != ':' || str[1] != ':' )
2026             return E_FAIL;
2027
2028         /* the last segment is just two colons */
2029         if( !str[2] )
2030             break;
2031         str += 2;
2032
2033         /* there must be a colon straight after a guid */
2034         p = strchrW( str, ':' );
2035         if( !p )
2036             return E_FAIL;
2037         len = p - str;
2038         if( len != 38 )
2039             return E_FAIL;
2040
2041         /* get the guid, and check it's validly formatted */
2042         memcpy( szGuid, str, sizeof(WCHAR)*len );
2043         szGuid[len] = 0;
2044         r = CLSIDFromString( szGuid, &guid );
2045         if( r != S_OK )
2046             return r;
2047         str = p + 1;
2048
2049         /* match it up to a guid that we care about */
2050         if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutComponent ) && !szComponent )
2051             szComponent = str;
2052         else if( IsEqualGUID( &guid, &SHELL32_AdvtShortcutProduct ) && !szProduct )
2053             szProduct = str;
2054         else
2055             return E_FAIL;
2056
2057         /* skip to the next field */
2058         str = strchrW( str, ':' );
2059         if( !str )
2060             return E_FAIL;
2061     }
2062
2063     /* we have to have a component for an advertised shortcut */
2064     if( !szComponent )
2065         return E_FAIL;
2066
2067     This->sComponent = ShellLink_GetAdvertisedArg( szComponent );
2068     This->sProduct = ShellLink_GetAdvertisedArg( szProduct );
2069
2070     TRACE("Component = %s\n", debugstr_w(This->sComponent));
2071     TRACE("Product = %s\n", debugstr_w(This->sProduct));
2072
2073     return S_OK;
2074 }
2075
2076 static BOOL ShellLink_GetVolumeInfo(LPWSTR path, volume_info *volume)
2077 {
2078     const int label_sz = sizeof volume->label/sizeof volume->label[0];
2079     WCHAR drive[4] = { path[0], ':', '\\', 0 };
2080     BOOL r;
2081
2082     volume->type = GetDriveTypeW(drive);
2083     r = GetVolumeInformationW(drive, volume->label, label_sz,
2084                               &volume->serial, NULL, NULL, NULL, 0);
2085     TRACE("r = %d type %ld serial %08lx name %s\n", r,
2086           volume->type, volume->serial, debugstr_w(volume->label));
2087     return r;
2088 }
2089
2090 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
2091 {
2092     _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
2093     WCHAR buffer[MAX_PATH];
2094     LPWSTR fname;
2095     HRESULT hr = S_OK;
2096
2097     TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
2098
2099     HeapFree(GetProcessHeap(), 0, This->sPath);
2100     This->sPath = NULL;
2101
2102     HeapFree(GetProcessHeap(), 0, This->sComponent);
2103     This->sComponent = NULL;
2104
2105     if (This->pPidl)
2106         ILFree(This->pPidl);
2107     This->pPidl = NULL;
2108
2109     if (S_OK != ShellLink_SetAdvertiseInfo( This, pszFile ))
2110     {
2111         if (*pszFile == '\0')
2112             *buffer = '\0';
2113         else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
2114             return E_FAIL;
2115         else if(!PathFileExistsW(buffer))
2116             hr = S_FALSE;
2117
2118         This->pPidl = SHSimpleIDListFromPathW(pszFile);
2119         ShellLink_GetVolumeInfo(buffer, &This->volume);
2120
2121         This->sPath = HeapAlloc( GetProcessHeap(), 0,
2122                              (lstrlenW( buffer )+1) * sizeof (WCHAR) );
2123         if (!This->sPath)
2124             return E_OUTOFMEMORY;
2125
2126         lstrcpyW(This->sPath, buffer);
2127     }
2128     This->bDirty = TRUE;
2129
2130     return hr;
2131 }
2132
2133 /**************************************************************************
2134 * IShellLinkW Implementation
2135 */
2136
2137 static IShellLinkWVtbl slvtw =
2138 {
2139         IShellLinkW_fnQueryInterface,
2140         IShellLinkW_fnAddRef,
2141         IShellLinkW_fnRelease,
2142         IShellLinkW_fnGetPath,
2143         IShellLinkW_fnGetIDList,
2144         IShellLinkW_fnSetIDList,
2145         IShellLinkW_fnGetDescription,
2146         IShellLinkW_fnSetDescription,
2147         IShellLinkW_fnGetWorkingDirectory,
2148         IShellLinkW_fnSetWorkingDirectory,
2149         IShellLinkW_fnGetArguments,
2150         IShellLinkW_fnSetArguments,
2151         IShellLinkW_fnGetHotkey,
2152         IShellLinkW_fnSetHotkey,
2153         IShellLinkW_fnGetShowCmd,
2154         IShellLinkW_fnSetShowCmd,
2155         IShellLinkW_fnGetIconLocation,
2156         IShellLinkW_fnSetIconLocation,
2157         IShellLinkW_fnSetRelativePath,
2158         IShellLinkW_fnResolve,
2159         IShellLinkW_fnSetPath
2160 };