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