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