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