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