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