new result buffer length parameter for SHELL_FindExecutable()
[reactos.git] / reactos / lib / shell32 / shelllink.c
1 /*
2 *
3 * Copyright 1997 Marcus Meissner
4 * Copyright 1998 Juergen Schmied
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * NOTES
21 * Nearly complete informations about the binary formats
22 * of .lnk files available at http://www.wotsit.org
23 *
24 */
25
26 #include "config.h"
27
28 #include <ctype.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #include <errno.h>
36 #include <limits.h>
37 #ifdef HAVE_SYS_WAIT_H
38 # include <sys/wait.h>
39 #endif
40 #include "wine/debug.h"
41 #include "wine/port.h"
42 #include "winerror.h"
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winreg.h"
47
48 #include "winuser.h"
49 #include "wingdi.h"
50 #include "shlobj.h"
51 #include "undocshell.h"
52
53 #include "pidl.h"
54 #include "shell32_main.h"
55 #include "shlguid.h"
56 #include "shlwapi.h"
57
58 WINE_DEFAULT_DEBUG_CHANNEL(shell);
59
60 /* link file formats */
61
62 /* flag1: lnk elements: simple link has 0x0B */
63 #define SCF_PIDL 1
64 #define SCF_NORMAL 2
65 #define SCF_DESCRIPTION 4
66 #define SCF_RELATIVE 8
67 #define SCF_WORKDIR 0x10
68 #define SCF_ARGS 0x20
69 #define SCF_CUSTOMICON 0x40
70 #define SCF_UNICODE 0x80
71
72 #include "pshpack1.h"
73
74 typedef struct _LINK_HEADER
75 {
76 DWORD dwSize; /* 0x00 size of the header - 0x4c */
77 GUID MagicGuid; /* 0x04 is CLSID_ShellLink */
78 DWORD dwFlags; /* 0x14 describes elements following */
79 DWORD dwFileAttr; /* 0x18 attributes of the target file */
80 FILETIME Time1; /* 0x1c */
81 FILETIME Time2; /* 0x24 */
82 FILETIME Time3; /* 0x2c */
83 DWORD dwFileLength; /* 0x34 File length */
84 DWORD nIcon; /* 0x38 icon number */
85 DWORD fStartup; /* 0x3c startup type */
86 DWORD wHotKey; /* 0x40 hotkey */
87 DWORD Unknown5; /* 0x44 */
88 DWORD Unknown6; /* 0x48 */
89 } LINK_HEADER, * PLINK_HEADER;
90
91 #define SHLINK_LOCAL 0
92 #define SHLINK_REMOTE 1
93
94 typedef struct _LOCATION_INFO
95 {
96 DWORD dwTotalSize;
97 DWORD dwHeaderSize;
98 DWORD dwFlags;
99 DWORD dwVolTableOfs;
100 DWORD dwLocalPathOfs;
101 DWORD dwNetworkVolTableOfs;
102 DWORD dwFinalPathOfs;
103 } LOCATION_INFO;
104
105 typedef struct _LOCAL_VOLUME_INFO
106 {
107 DWORD dwSize;
108 DWORD dwType;
109 DWORD dwVolSerial;
110 DWORD dwVolLabelOfs;
111 } LOCAL_VOLUME_INFO;
112
113 #include "poppack.h"
114
115 static ICOM_VTABLE(IShellLinkA) slvt;
116 static ICOM_VTABLE(IShellLinkW) slvtw;
117 static ICOM_VTABLE(IPersistFile) pfvt;
118 static ICOM_VTABLE(IPersistStream) psvt;
119
120 /* IShellLink Implementation */
121
122 typedef struct
123 {
124 ICOM_VFIELD(IShellLinkA);
125 DWORD ref;
126
127 ICOM_VTABLE(IShellLinkW)* lpvtblw;
128 ICOM_VTABLE(IPersistFile)* lpvtblPersistFile;
129 ICOM_VTABLE(IPersistStream)* lpvtblPersistStream;
130
131 /* data structures according to the informations in the link */
132 LPITEMIDLIST pPidl;
133 WORD wHotKey;
134 SYSTEMTIME time1;
135 SYSTEMTIME time2;
136 SYSTEMTIME time3;
137
138 DWORD iShowCmd;
139 LPWSTR sIcoPath;
140 INT iIcoNdx;
141 LPWSTR sPath;
142 LPWSTR sArgs;
143 LPWSTR sWorkDir;
144 LPWSTR sDescription;
145 LPWSTR sPathRel;
146
147 BOOL bDirty;
148 } IShellLinkImpl;
149
150 #define _IShellLinkW_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblw)))
151 #define _ICOM_THIS_From_IShellLinkW(class, name) class* This = (class*)(((char*)name)-_IShellLinkW_Offset)
152
153 #define _IPersistFile_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblPersistFile)))
154 #define _ICOM_THIS_From_IPersistFile(class, name) class* This = (class*)(((char*)name)-_IPersistFile_Offset)
155
156 #define _IPersistStream_Offset ((int)(&(((IShellLinkImpl*)0)->lpvtblPersistStream)))
157 #define _ICOM_THIS_From_IPersistStream(class, name) class* This = (class*)(((char*)name)-_IPersistStream_Offset)
158 #define _IPersistStream_From_ICOM_THIS(class, name) class* StreamThis = (class*)(((char*)name)+_IPersistStream_Offset)
159
160
161 /* strdup on the process heap */
162 inline static LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str)
163 {
164 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
165 LPWSTR p = HeapAlloc( heap, flags, len*sizeof (WCHAR) );
166 if( !p )
167 return p;
168 MultiByteToWideChar( CP_ACP, 0, str, -1, p, len );
169 return p;
170 }
171
172
173 /**************************************************************************
174 * IPersistFile_QueryInterface
175 */
176 static HRESULT WINAPI IPersistFile_fnQueryInterface(
177 IPersistFile* iface,
178 REFIID riid,
179 LPVOID *ppvObj)
180 {
181 _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
182
183 TRACE("(%p)\n",This);
184
185 return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
186 }
187
188 /******************************************************************************
189 * IPersistFile_AddRef
190 */
191 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile* iface)
192 {
193 _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
194
195 TRACE("(%p)->(count=%lu)\n",This,This->ref);
196
197 return IShellLinkA_AddRef((IShellLinkA*)This);
198 }
199 /******************************************************************************
200 * IPersistFile_Release
201 */
202 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile* iface)
203 {
204 _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
205
206 TRACE("(%p)->(count=%lu)\n",This,This->ref);
207
208 return IShellLinkA_Release((IShellLinkA*)This);
209 }
210
211 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile* iface, CLSID *pClassID)
212 {
213 _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
214 FIXME("(%p)\n",This);
215 return NOERROR;
216 }
217 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile* iface)
218 {
219 _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
220
221 FIXME("(%p)\n",This);
222
223 if (This->bDirty)
224 return S_OK;
225
226 return S_FALSE;
227 }
228 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile* iface, LPCOLESTR pszFileName, DWORD dwMode)
229 {
230 _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
231 _IPersistStream_From_ICOM_THIS(IPersistStream, This);
232 HRESULT r;
233 IStream *stm;
234
235 TRACE("(%p, %s)\n",This, debugstr_w(pszFileName));
236
237 r = CreateStreamOnFile(pszFileName, dwMode, &stm);
238 if( SUCCEEDED( r ) )
239 {
240 r = IPersistStream_Load(StreamThis, stm);
241 IStream_Release( stm );
242 }
243
244 return r;
245 }
246
247 static BOOL StartLinkProcessor( LPCOLESTR szLink )
248 {
249 const WCHAR szFormat[] = {'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
250 ' ','-','r',' ','"','%','s','"',0 };
251 LONG len;
252 LPWSTR buffer;
253 STARTUPINFOW si;
254 PROCESS_INFORMATION pi;
255
256 len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
257 buffer = HeapAlloc( GetProcessHeap(), 0, len );
258 if( !buffer )
259 return FALSE;
260
261 wsprintfW( buffer, szFormat, szLink );
262
263 TRACE("starting %s\n",debugstr_w(buffer));
264
265 memset(&si, 0, sizeof(si));
266 si.cb = sizeof(si);
267 if (!CreateProcessW( NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) return FALSE;
268
269 /* wait for a while to throttle the creation of linker processes */
270 if( WAIT_OBJECT_0 != WaitForSingleObject( pi.hProcess, 10000 ) )
271 WARN("Timed out waiting for shell linker\n");
272
273 CloseHandle( pi.hProcess );
274 CloseHandle( pi.hThread );
275
276 return TRUE;
277 }
278
279 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile* iface, LPCOLESTR pszFileName, BOOL fRemember)
280 {
281 _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
282 _IPersistStream_From_ICOM_THIS(IPersistStream, This);
283 HRESULT r;
284 IStream *stm;
285
286 TRACE("(%p)->(%s)\n",This,debugstr_w(pszFileName));
287
288 if (!pszFileName || !This->sPath)
289 return ERROR_UNKNOWN;
290
291 r = CreateStreamOnFile(pszFileName, STGM_READWRITE | STGM_CREATE, &stm);
292 if( SUCCEEDED( r ) )
293 {
294 r = IPersistStream_Save(StreamThis, stm, FALSE);
295 IStream_Release( stm );
296
297 if( SUCCEEDED( r ) )
298 StartLinkProcessor( pszFileName );
299 else
300 {
301 DeleteFileW( pszFileName );
302 WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName) );
303 }
304 }
305
306 return r;
307 }
308
309 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile* iface, LPCOLESTR pszFileName)
310 {
311 _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
312 FIXME("(%p)->(%s)\n",This,debugstr_w(pszFileName));
313 return NOERROR;
314 }
315 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile* iface, LPOLESTR *ppszFileName)
316 {
317 _ICOM_THIS_From_IPersistFile(IShellLinkImpl, iface);
318 FIXME("(%p)\n",This);
319 return NOERROR;
320 }
321
322 static ICOM_VTABLE(IPersistFile) pfvt =
323 {
324 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
325 IPersistFile_fnQueryInterface,
326 IPersistFile_fnAddRef,
327 IPersistFile_fnRelease,
328 IPersistFile_fnGetClassID,
329 IPersistFile_fnIsDirty,
330 IPersistFile_fnLoad,
331 IPersistFile_fnSave,
332 IPersistFile_fnSaveCompleted,
333 IPersistFile_fnGetCurFile
334 };
335
336 /************************************************************************
337 * IPersistStream_QueryInterface
338 */
339 static HRESULT WINAPI IPersistStream_fnQueryInterface(
340 IPersistStream* iface,
341 REFIID riid,
342 VOID** ppvoid)
343 {
344 _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
345
346 TRACE("(%p)\n",This);
347
348 return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvoid);
349 }
350
351 /************************************************************************
352 * IPersistStream_Release
353 */
354 static ULONG WINAPI IPersistStream_fnRelease(
355 IPersistStream* iface)
356 {
357 _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
358
359 TRACE("(%p)\n",This);
360
361 return IShellLinkA_Release((IShellLinkA*)This);
362 }
363
364 /************************************************************************
365 * IPersistStream_AddRef
366 */
367 static ULONG WINAPI IPersistStream_fnAddRef(
368 IPersistStream* iface)
369 {
370 _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
371
372 TRACE("(%p)\n",This);
373
374 return IShellLinkA_AddRef((IShellLinkA*)This);
375 }
376
377 /************************************************************************
378 * IPersistStream_GetClassID
379 *
380 */
381 static HRESULT WINAPI IPersistStream_fnGetClassID(
382 IPersistStream* iface,
383 CLSID* pClassID)
384 {
385 _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
386
387 TRACE("(%p)\n", This);
388
389 if (pClassID==0)
390 return E_POINTER;
391
392 /* memcpy(pClassID, &CLSID_???, sizeof(CLSID_???)); */
393
394 return S_OK;
395 }
396
397 /************************************************************************
398 * IPersistStream_IsDirty (IPersistStream)
399 */
400 static HRESULT WINAPI IPersistStream_fnIsDirty(
401 IPersistStream* iface)
402 {
403 _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
404
405 TRACE("(%p)\n", This);
406
407 return S_OK;
408 }
409
410
411 static HRESULT Stream_LoadString( IStream* stm, BOOL unicode, LPWSTR *pstr )
412 {
413 DWORD count;
414 USHORT len;
415 LPVOID temp;
416 LPWSTR str;
417 HRESULT r;
418
419 TRACE("%p\n", stm);
420
421 count = 0;
422 r = IStream_Read(stm, &len, sizeof(len), &count);
423 if ( FAILED (r) || ( count != sizeof(len) ) )
424 return E_FAIL;
425
426 if( unicode )
427 len *= sizeof (WCHAR);
428
429 TRACE("reading %d\n", len);
430 temp = HeapAlloc(GetProcessHeap(), 0, len+sizeof(WCHAR));
431 if( !temp )
432 return E_OUTOFMEMORY;
433 count = 0;
434 r = IStream_Read(stm, temp, len, &count);
435 if( FAILED (r) || ( count != len ) )
436 {
437 HeapFree( GetProcessHeap(), 0, temp );
438 return E_FAIL;
439 }
440
441 TRACE("read %s\n", debugstr_an(temp,len));
442
443 /* convert to unicode if necessary */
444 if( !unicode )
445 {
446 count = MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, NULL, 0 );
447 str = HeapAlloc( GetProcessHeap(), 0, (count+1)*sizeof (WCHAR) );
448 if( str )
449 MultiByteToWideChar( CP_ACP, 0, (LPSTR) temp, len, str, count );
450 HeapFree( GetProcessHeap(), 0, temp );
451 }
452 else
453 {
454 count /= 2;
455 str = (LPWSTR) temp;
456 }
457 str[count] = 0;
458
459 *pstr = str;
460
461 return S_OK;
462 }
463
464 static HRESULT Stream_LoadLocation( IStream* stm )
465 {
466 DWORD size;
467 ULONG count;
468 HRESULT r;
469 LOCATION_INFO *loc;
470
471 TRACE("%p\n",stm);
472
473 r = IStream_Read( stm, &size, sizeof(size), &count );
474 if( FAILED( r ) )
475 return r;
476 if( count != sizeof(loc->dwTotalSize) )
477 return E_FAIL;
478
479 loc = HeapAlloc( GetProcessHeap(), 0, size );
480 if( ! loc )
481 return E_OUTOFMEMORY;
482
483 r = IStream_Read( stm, &loc->dwHeaderSize, size-sizeof(size), &count );
484 if( FAILED( r ) )
485 goto end;
486 if( count != (size - sizeof(size)) )
487 {
488 r = E_FAIL;
489 goto end;
490 }
491 loc->dwTotalSize = size;
492
493 TRACE("Read %ld bytes\n",count);
494
495 /* FIXME: do something useful with it */
496 HeapFree( GetProcessHeap(), 0, loc );
497
498 return S_OK;
499 end:
500 HeapFree( GetProcessHeap(), 0, loc );
501 return r;
502 }
503
504 /************************************************************************
505 * IPersistStream_Load (IPersistStream)
506 */
507 static HRESULT WINAPI IPersistStream_fnLoad(
508 IPersistStream* iface,
509 IStream* stm)
510 {
511 LINK_HEADER hdr;
512 ULONG dwBytesRead;
513 BOOL unicode;
514 WCHAR sTemp[MAX_PATH];
515 HRESULT r;
516
517 _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
518
519 TRACE("(%p)(%p)\n", This, stm);
520
521 if( !stm )
522 return STG_E_INVALIDPOINTER;
523
524 dwBytesRead = 0;
525 r = IStream_Read(stm, &hdr, sizeof(hdr), &dwBytesRead);
526 if( FAILED( r ) )
527 return r;
528
529 if( dwBytesRead != sizeof(hdr))
530 return E_FAIL;
531 if( hdr.dwSize != sizeof(hdr))
532 return E_FAIL;
533 if( !IsEqualIID(&hdr.MagicGuid, &CLSID_ShellLink) )
534 return E_FAIL;
535
536 if( hdr.dwFlags & SCF_PIDL )
537 {
538 r = ILLoadFromStream( stm, &This->pPidl );
539 if( FAILED( r ) )
540 return r;
541 }
542 This->wHotKey = (WORD)hdr.wHotKey;
543 This->iIcoNdx = hdr.nIcon;
544 FileTimeToSystemTime (&hdr.Time1, &This->time1);
545 FileTimeToSystemTime (&hdr.Time2, &This->time2);
546 FileTimeToSystemTime (&hdr.Time3, &This->time3);
547 #if 1
548 GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time1, NULL, sTemp, 256);
549 TRACE("-- time1: %s\n", debugstr_w(sTemp) );
550 GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time2, NULL, sTemp, 256);
551 TRACE("-- time1: %s\n", debugstr_w(sTemp) );
552 GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&This->time3, NULL, sTemp, 256);
553 TRACE("-- time1: %s\n", debugstr_w(sTemp) );
554 pdump (This->pPidl);
555 #endif
556 if( hdr.dwFlags & SCF_NORMAL )
557 r = Stream_LoadLocation( stm );
558 if( FAILED( r ) )
559 goto end;
560 unicode = hdr.dwFlags & SCF_UNICODE;
561 if( hdr.dwFlags & SCF_DESCRIPTION )
562 {
563 r = Stream_LoadString( stm, unicode, &This->sDescription );
564 TRACE("Description -> %s\n",debugstr_w(This->sDescription));
565 }
566 if( FAILED( r ) )
567 goto end;
568
569 if( hdr.dwFlags & SCF_RELATIVE )
570 {
571 r = Stream_LoadString( stm, unicode, &This->sPathRel );
572 TRACE("Relative Path-> %s\n",debugstr_w(This->sPathRel));
573 }
574 if( FAILED( r ) )
575 goto end;
576
577 if( hdr.dwFlags & SCF_WORKDIR )
578 {
579 r = Stream_LoadString( stm, unicode, &This->sWorkDir );
580 TRACE("Working Dir -> %s\n",debugstr_w(This->sWorkDir));
581 }
582 if( FAILED( r ) )
583 goto end;
584
585 if( hdr.dwFlags & SCF_ARGS )
586 {
587 r = Stream_LoadString( stm, unicode, &This->sArgs );
588 TRACE("Working Dir -> %s\n",debugstr_w(This->sArgs));
589 }
590 if( FAILED( r ) )
591 goto end;
592
593 if( hdr.dwFlags & SCF_CUSTOMICON )
594 {
595 r = Stream_LoadString( stm, unicode, &This->sIcoPath );
596 TRACE("Icon file -> %s\n",debugstr_w(This->sIcoPath));
597 }
598 if( FAILED( r ) )
599 goto end;
600
601 TRACE("OK\n");
602
603 pdump (This->pPidl);
604
605 return S_OK;
606 end:
607 return r;
608 }
609
610 /************************************************************************
611 * Stream_WriteString
612 *
613 * Helper function for IPersistStream_Save. Writes a unicode string
614 * with terminating nul byte to a stream, preceded by the its length.
615 */
616 static HRESULT Stream_WriteString( IStream* stm, LPCWSTR str )
617 {
618 USHORT len = lstrlenW( str ) + 1;
619 DWORD count;
620 HRESULT r;
621
622 r = IStream_Write( stm, &len, sizeof(len), &count );
623 if( FAILED( r ) )
624 return r;
625
626 len *= sizeof(WCHAR);
627
628 r = IStream_Write( stm, str, len, &count );
629 if( FAILED( r ) )
630 return r;
631
632 return S_OK;
633 }
634
635 static HRESULT Stream_WriteLocationInfo( IStream* stm, LPCWSTR filename )
636 {
637 LOCATION_INFO loc;
638 ULONG count;
639
640 FIXME("writing empty location info\n");
641
642 memset( &loc, 0, sizeof(loc) );
643 loc.dwTotalSize = sizeof(loc) - sizeof(loc.dwTotalSize);
644
645 /* FIXME: fill this in */
646
647 return IStream_Write( stm, &loc, loc.dwTotalSize, &count );
648 }
649
650 /************************************************************************
651 * IPersistStream_Save (IPersistStream)
652 *
653 * FIXME: makes assumptions about byte order
654 */
655 static HRESULT WINAPI IPersistStream_fnSave(
656 IPersistStream* iface,
657 IStream* stm,
658 BOOL fClearDirty)
659 {
660 static const WCHAR wOpen[] = {'o','p','e','n',0};
661
662 LINK_HEADER header;
663 WCHAR exePath[MAX_PATH];
664 ULONG count;
665 HRESULT r;
666
667 _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
668
669 TRACE("(%p) %p %x\n", This, stm, fClearDirty);
670
671 *exePath = '\0';
672
673 if (This->sPath)
674 SHELL_FindExecutable(NULL, This->sPath, wOpen, exePath, MAX_PATH, NULL, NULL, NULL, NULL);
675
676 /* if there's no PIDL, generate one */
677 if( ! This->pPidl )
678 {
679 if( !*exePath )
680 return E_FAIL;
681
682 This->pPidl = ILCreateFromPathW(exePath);
683 }
684
685 memset(&header, 0, sizeof(header));
686 header.dwSize = sizeof(header);
687 memcpy(&header.MagicGuid, &CLSID_ShellLink, sizeof(header.MagicGuid) );
688
689 header.wHotKey = This->wHotKey;
690 header.nIcon = This->iIcoNdx;
691 header.dwFlags = SCF_UNICODE; /* strings are in unicode */
692 header.dwFlags |= SCF_NORMAL; /* how do we determine this ? */
693 if( This->pPidl )
694 header.dwFlags |= SCF_PIDL;
695 if( This->sDescription )
696 header.dwFlags |= SCF_DESCRIPTION;
697 if( This->sWorkDir )
698 header.dwFlags |= SCF_WORKDIR;
699 if( This->sArgs )
700 header.dwFlags |= SCF_ARGS;
701 if( This->sIcoPath )
702 header.dwFlags |= SCF_CUSTOMICON;
703
704 SystemTimeToFileTime ( &This->time1, &header.Time1 );
705 SystemTimeToFileTime ( &This->time2, &header.Time2 );
706 SystemTimeToFileTime ( &This->time3, &header.Time3 );
707
708 /* write the Shortcut header */
709 r = IStream_Write( stm, &header, sizeof(header), &count );
710 if( FAILED( r ) )
711 {
712 ERR("Write failed at %d\n",__LINE__);
713 return r;
714 }
715
716 TRACE("Writing pidl \n");
717
718 /* write the PIDL to the shortcut */
719 if( This->pPidl )
720 {
721 r = ILSaveToStream( stm, This->pPidl );
722 if( FAILED( r ) )
723 {
724 ERR("Failed to write PIDL at %d\n",__LINE__);
725 return r;
726 }
727 }
728
729 Stream_WriteLocationInfo( stm, exePath );
730
731 TRACE("Description = %s\n", debugstr_w(This->sDescription));
732 if( This->sDescription )
733 r = Stream_WriteString( stm, This->sDescription );
734
735 if( This->sPathRel )
736 r = Stream_WriteString( stm, This->sPathRel );
737
738 if( This->sWorkDir )
739 r = Stream_WriteString( stm, This->sWorkDir );
740
741 if( This->sArgs )
742 r = Stream_WriteString( stm, This->sArgs );
743
744 if( This->sIcoPath )
745 r = Stream_WriteString( stm, This->sIcoPath );
746
747 return S_OK;
748 }
749
750 /************************************************************************
751 * IPersistStream_GetSizeMax (IPersistStream)
752 */
753 static HRESULT WINAPI IPersistStream_fnGetSizeMax(
754 IPersistStream* iface,
755 ULARGE_INTEGER* pcbSize)
756 {
757 _ICOM_THIS_From_IPersistStream(IShellLinkImpl, iface);
758
759 TRACE("(%p)\n", This);
760
761 return E_NOTIMPL;
762 }
763
764 static ICOM_VTABLE(IPersistStream) psvt =
765 {
766 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
767 IPersistStream_fnQueryInterface,
768 IPersistStream_fnAddRef,
769 IPersistStream_fnRelease,
770 IPersistStream_fnGetClassID,
771 IPersistStream_fnIsDirty,
772 IPersistStream_fnLoad,
773 IPersistStream_fnSave,
774 IPersistStream_fnGetSizeMax
775 };
776
777 /**************************************************************************
778 * IShellLink_Constructor
779 */
780 HRESULT WINAPI IShellLink_Constructor (
781 IUnknown * pUnkOuter,
782 REFIID riid,
783 LPVOID * ppv)
784 {
785 IShellLinkImpl * sl;
786
787 TRACE("unkOut=%p riid=%s\n",pUnkOuter, debugstr_guid(riid));
788
789 *ppv = NULL;
790
791 if(pUnkOuter) return CLASS_E_NOAGGREGATION;
792 sl = (IShellLinkImpl *) LocalAlloc(GMEM_ZEROINIT,sizeof(IShellLinkImpl));
793 if (!sl) return E_OUTOFMEMORY;
794
795 sl->ref = 1;
796 sl->lpVtbl = &slvt;
797 sl->lpvtblw = &slvtw;
798 sl->lpvtblPersistFile = &pfvt;
799 sl->lpvtblPersistStream = &psvt;
800 sl->iShowCmd = SW_SHOWNORMAL;
801 sl->bDirty = FALSE;
802
803 TRACE("(%p)->()\n",sl);
804
805 if (IsEqualIID(riid, &IID_IUnknown) ||
806 IsEqualIID(riid, &IID_IShellLinkA))
807 *ppv = sl;
808 else if (IsEqualIID(riid, &IID_IShellLinkW))
809 *ppv = &(sl->lpvtblw);
810 else {
811 LocalFree((HLOCAL)sl);
812 ERR("E_NOINTERFACE\n");
813 return E_NOINTERFACE;
814 }
815
816 return S_OK;
817 }
818
819 static BOOL SHELL_ExistsFileW(LPCWSTR path)
820 {
821 HANDLE hfile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
822
823 if (hfile != INVALID_HANDLE_VALUE) {
824 CloseHandle(hfile);
825 return TRUE;
826 } else
827 return FALSE;
828 }
829
830 /**************************************************************************
831 * SHELL_ShellLink_UpdatePath
832 * update absolute path in sPath using relative path in sPathRel
833 */
834 static HRESULT SHELL_ShellLink_UpdatePath(LPWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
835 {
836 if (!path || !psPath)
837 return E_INVALIDARG;
838
839 if (!*psPath && sPathRel) {
840 WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
841
842 /* first try if [directory of link file] + [relative path] finds an existing file */
843 LPCWSTR src = path;
844 LPWSTR last_slash = NULL;
845 LPWSTR dest = buffer;
846 LPWSTR final;
847
848 /* copy path without file name to buffer */
849 while(*src) {
850 if (*src=='/' || *src=='\\')
851 last_slash = dest;
852
853 *dest++ = *src++;
854 }
855
856 lstrcpyW(last_slash? last_slash+1: buffer, sPathRel);
857
858 *abs_path = '\0';
859
860 if (SHELL_ExistsFileW(buffer)) {
861 if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
862 lstrcpyW(abs_path, buffer);
863 } else {
864 /* try if [working directory] + [relative path] finds an existing file */
865 if (sWorkDir) {
866 lstrcpyW(buffer, sWorkDir);
867 lstrcpyW(PathAddBackslashW(buffer), sPathRel);
868
869 if (SHELL_ExistsFileW(buffer))
870 if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
871 lstrcpyW(abs_path, buffer);
872 }
873 }
874
875 /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
876 if (!*abs_path)
877 lstrcpyW(abs_path, sPathRel);
878
879 *psPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(abs_path)+1)*sizeof(WCHAR));
880 if (!*psPath)
881 return E_OUTOFMEMORY;
882
883 lstrcpyW(*psPath, abs_path);
884 }
885
886 return S_OK;
887 }
888
889 /**************************************************************************
890 * IShellLink_ConstructFromFile
891 */
892 HRESULT WINAPI IShellLink_ConstructFromFile (
893 IUnknown* pUnkOuter,
894 REFIID riid,
895 LPCITEMIDLIST pidl,
896 LPVOID* ppv
897 )
898 {
899 IShellLinkW* psl;
900
901 HRESULT hr = IShellLink_Constructor(NULL, riid, (LPVOID*)&psl);
902
903 if (SUCCEEDED(hr)) {
904 IPersistFile* ppf;
905
906 *ppv = NULL;
907
908 hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
909
910 if (SUCCEEDED(hr)) {
911 WCHAR path[MAX_PATH];
912
913 if (SHGetPathFromIDListW(pidl, path)) {
914 hr = IPersistFile_Load(ppf, path, 0);
915
916 if (SUCCEEDED(hr)) {
917 *ppv = (IUnknown*) psl;
918
919 /*
920 The following code is here, not in IPersistStream_fnLoad() because
921 to be able to convert the relative path into the absolute path,
922 we need to know the path of the shell link file.
923 */
924 if (IsEqualIID(riid, &IID_IShellLinkW)) {
925 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, psl);
926
927 hr = SHELL_ShellLink_UpdatePath(This->sPathRel, path, This->sWorkDir, &This->sPath);
928 } else {
929 ICOM_THIS(IShellLinkImpl, psl);
930
931 hr = SHELL_ShellLink_UpdatePath(This->sPathRel, path, This->sWorkDir, &This->sPath);
932 }
933 }
934 }
935
936 IPersistFile_Release(ppf);
937 }
938
939 if (!*ppv)
940 IShellLinkW_Release(psl);
941 }
942
943 return hr;
944 }
945
946 /**************************************************************************
947 * IShellLinkA_QueryInterface
948 */
949 static HRESULT WINAPI IShellLinkA_fnQueryInterface( IShellLinkA * iface, REFIID riid, LPVOID *ppvObj)
950 {
951 ICOM_THIS(IShellLinkImpl, iface);
952
953 TRACE("(%p)->(\n\tIID:\t%s)\n",This,debugstr_guid(riid));
954
955 *ppvObj = NULL;
956
957 if(IsEqualIID(riid, &IID_IUnknown) ||
958 IsEqualIID(riid, &IID_IShellLinkA))
959 {
960 *ppvObj = This;
961 }
962 else if(IsEqualIID(riid, &IID_IShellLinkW))
963 {
964 *ppvObj = (IShellLinkW *)&(This->lpvtblw);
965 }
966 else if(IsEqualIID(riid, &IID_IPersistFile))
967 {
968 *ppvObj = (IPersistFile *)&(This->lpvtblPersistFile);
969 }
970 else if(IsEqualIID(riid, &IID_IPersistStream))
971 {
972 *ppvObj = (IPersistStream *)&(This->lpvtblPersistStream);
973 }
974
975 if(*ppvObj)
976 {
977 IUnknown_AddRef((IUnknown*)(*ppvObj));
978 TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
979 return S_OK;
980 }
981 TRACE("-- Interface: E_NOINTERFACE\n");
982 return E_NOINTERFACE;
983 }
984
985 /******************************************************************************
986 * IShellLinkA_AddRef
987 */
988 static ULONG WINAPI IShellLinkA_fnAddRef(IShellLinkA * iface)
989 {
990 ICOM_THIS(IShellLinkImpl, iface);
991
992 TRACE("(%p)->(count=%lu)\n",This,This->ref);
993
994 return ++(This->ref);
995 }
996
997 /******************************************************************************
998 * IShellLinkA_Release
999 */
1000 static ULONG WINAPI IShellLinkA_fnRelease(IShellLinkA * iface)
1001 {
1002 ICOM_THIS(IShellLinkImpl, iface);
1003
1004 TRACE("(%p)->(count=%lu)\n",This,This->ref);
1005
1006 if (--(This->ref))
1007 return This->ref;
1008
1009 TRACE("-- destroying IShellLink(%p)\n",This);
1010
1011 if (This->sIcoPath)
1012 HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1013
1014 if (This->sArgs)
1015 HeapFree(GetProcessHeap(), 0, This->sArgs);
1016
1017 if (This->sWorkDir)
1018 HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1019
1020 if (This->sDescription)
1021 HeapFree(GetProcessHeap(), 0, This->sDescription);
1022
1023 if (This->sPath)
1024 HeapFree(GetProcessHeap(), 0, This->sPath);
1025
1026 if (This->pPidl)
1027 ILFree(This->pPidl);
1028
1029 LocalFree((HANDLE)This);
1030
1031 return 0;
1032 }
1033
1034 static HRESULT WINAPI IShellLinkA_fnGetPath(IShellLinkA * iface, LPSTR pszFile,
1035 INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
1036 {
1037 ICOM_THIS(IShellLinkImpl, iface);
1038
1039 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)(%s)\n",
1040 This, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(This->sPath));
1041
1042 if( cchMaxPath )
1043 pszFile[0] = 0;
1044 if (This->sPath)
1045 WideCharToMultiByte( CP_ACP, 0, This->sPath, -1,
1046 pszFile, cchMaxPath, NULL, NULL);
1047
1048 if (pfd) {
1049
1050 FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1051
1052 }
1053
1054 return NOERROR;
1055 }
1056
1057 static HRESULT WINAPI IShellLinkA_fnGetIDList(IShellLinkA * iface, LPITEMIDLIST * ppidl)
1058 {
1059 ICOM_THIS(IShellLinkImpl, iface);
1060
1061 TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1062
1063 *ppidl = ILClone(This->pPidl);
1064
1065 return NOERROR;
1066 }
1067
1068 static HRESULT WINAPI IShellLinkA_fnSetIDList(IShellLinkA * iface, LPCITEMIDLIST pidl)
1069 {
1070 ICOM_THIS(IShellLinkImpl, iface);
1071
1072 TRACE("(%p)->(pidl=%p)\n",This, pidl);
1073
1074 if (This->pPidl)
1075 ILFree(This->pPidl);
1076
1077 This->pPidl = ILClone (pidl);
1078 This->bDirty = TRUE;
1079
1080 return S_OK;
1081 }
1082
1083 static HRESULT WINAPI IShellLinkA_fnGetDescription(IShellLinkA * iface, LPSTR pszName,INT cchMaxName)
1084 {
1085 ICOM_THIS(IShellLinkImpl, iface);
1086
1087 TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1088
1089 if( cchMaxName )
1090 pszName[0] = 0;
1091 if( This->sDescription )
1092 WideCharToMultiByte( CP_ACP, 0, This->sDescription, -1,
1093 pszName, cchMaxName, NULL, NULL);
1094
1095 return S_OK;
1096 }
1097 static HRESULT WINAPI IShellLinkA_fnSetDescription(IShellLinkA * iface, LPCSTR pszName)
1098 {
1099 ICOM_THIS(IShellLinkImpl, iface);
1100
1101 TRACE("(%p)->(pName=%s)\n", This, pszName);
1102
1103 if (This->sDescription)
1104 HeapFree(GetProcessHeap(), 0, This->sDescription);
1105 This->sDescription = HEAP_strdupAtoW( GetProcessHeap(), 0, pszName);
1106 if ( !This->sDescription )
1107 return E_OUTOFMEMORY;
1108
1109 This->bDirty = TRUE;
1110
1111 return S_OK;
1112 }
1113
1114 static HRESULT WINAPI IShellLinkA_fnGetWorkingDirectory(IShellLinkA * iface, LPSTR pszDir,INT cchMaxPath)
1115 {
1116 ICOM_THIS(IShellLinkImpl, iface);
1117
1118 TRACE("(%p)->(%p len=%u)\n", This, pszDir, cchMaxPath);
1119
1120 if( cchMaxPath )
1121 pszDir[0] = 0;
1122 if( This->sWorkDir )
1123 WideCharToMultiByte( CP_ACP, 0, This->sWorkDir, -1,
1124 pszDir, cchMaxPath, NULL, NULL);
1125
1126 return S_OK;
1127 }
1128
1129 static HRESULT WINAPI IShellLinkA_fnSetWorkingDirectory(IShellLinkA * iface, LPCSTR pszDir)
1130 {
1131 ICOM_THIS(IShellLinkImpl, iface);
1132
1133 TRACE("(%p)->(dir=%s)\n",This, pszDir);
1134
1135 if (This->sWorkDir)
1136 HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1137 This->sWorkDir = HEAP_strdupAtoW( GetProcessHeap(), 0, pszDir);
1138 if ( !This->sWorkDir )
1139 return E_OUTOFMEMORY;
1140
1141 This->bDirty = TRUE;
1142
1143 return S_OK;
1144 }
1145
1146 static HRESULT WINAPI IShellLinkA_fnGetArguments(IShellLinkA * iface, LPSTR pszArgs,INT cchMaxPath)
1147 {
1148 ICOM_THIS(IShellLinkImpl, iface);
1149
1150 TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1151
1152 if( cchMaxPath )
1153 pszArgs[0] = 0;
1154 if( This->sArgs )
1155 WideCharToMultiByte( CP_ACP, 0, This->sArgs, -1,
1156 pszArgs, cchMaxPath, NULL, NULL);
1157
1158 return S_OK;
1159 }
1160
1161 static HRESULT WINAPI IShellLinkA_fnSetArguments(IShellLinkA * iface, LPCSTR pszArgs)
1162 {
1163 ICOM_THIS(IShellLinkImpl, iface);
1164
1165 TRACE("(%p)->(args=%s)\n",This, pszArgs);
1166
1167 if (This->sArgs)
1168 HeapFree(GetProcessHeap(), 0, This->sArgs);
1169 This->sArgs = HEAP_strdupAtoW( GetProcessHeap(), 0, pszArgs);
1170 if( !This->sArgs )
1171 return E_OUTOFMEMORY;
1172
1173 This->bDirty = TRUE;
1174
1175 return S_OK;
1176 }
1177
1178 static HRESULT WINAPI IShellLinkA_fnGetHotkey(IShellLinkA * iface, WORD *pwHotkey)
1179 {
1180 ICOM_THIS(IShellLinkImpl, iface);
1181
1182 TRACE("(%p)->(%p)(0x%08x)\n",This, pwHotkey, This->wHotKey);
1183
1184 *pwHotkey = This->wHotKey;
1185
1186 return S_OK;
1187 }
1188
1189 static HRESULT WINAPI IShellLinkA_fnSetHotkey(IShellLinkA * iface, WORD wHotkey)
1190 {
1191 ICOM_THIS(IShellLinkImpl, iface);
1192
1193 TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1194
1195 This->wHotKey = wHotkey;
1196 This->bDirty = TRUE;
1197
1198 return S_OK;
1199 }
1200
1201 static HRESULT WINAPI IShellLinkA_fnGetShowCmd(IShellLinkA * iface, INT *piShowCmd)
1202 {
1203 ICOM_THIS(IShellLinkImpl, iface);
1204
1205 TRACE("(%p)->(%p)\n",This, piShowCmd);
1206 *piShowCmd = This->iShowCmd;
1207 return S_OK;
1208 }
1209
1210 static HRESULT WINAPI IShellLinkA_fnSetShowCmd(IShellLinkA * iface, INT iShowCmd)
1211 {
1212 ICOM_THIS(IShellLinkImpl, iface);
1213
1214 TRACE("(%p) %d\n",This, iShowCmd);
1215
1216 This->iShowCmd = iShowCmd;
1217 This->bDirty = TRUE;
1218
1219 return NOERROR;
1220 }
1221
1222 static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPITEMIDLIST pidl, LPSTR pszIconPath, int cchIconPath, int* piIcon)
1223 {
1224 LPCITEMIDLIST pidlLast;
1225
1226 HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1227
1228 if (SUCCEEDED(hr)) {
1229 IExtractIconA* pei;
1230
1231 hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconA, NULL, (LPVOID*)&pei);
1232
1233 if (SUCCEEDED(hr)) {
1234 hr = IExtractIconA_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1235
1236 IExtractIconA_Release(pei);
1237 }
1238
1239 IShellFolder_Release(psf);
1240 }
1241
1242 return hr;
1243 }
1244
1245 static HRESULT WINAPI IShellLinkA_fnGetIconLocation(IShellLinkA * iface, LPSTR pszIconPath, INT cchIconPath, INT *piIcon)
1246 {
1247 ICOM_THIS(IShellLinkImpl, iface);
1248
1249 TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1250
1251 if (cchIconPath)
1252 pszIconPath[0] = 0;
1253
1254 if (This->sIcoPath) {
1255 WideCharToMultiByte(CP_ACP, 0, This->sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1256 *piIcon = This->iIcoNdx;
1257 return S_OK;
1258 }
1259
1260 if (This->pPidl || This->sPath) {
1261 IShellFolder* pdsk;
1262
1263 HRESULT hr = SHGetDesktopFolder(&pdsk);
1264
1265 if (SUCCEEDED(hr)) {
1266 /* first look for an icon using the PIDL (if present) */
1267 if (This->pPidl)
1268 hr = SHELL_PidlGeticonLocationA(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1269 else
1270 hr = E_FAIL;
1271
1272 /* if we couldn't find an icon yet, look for it using the file system path */
1273 if (FAILED(hr) && This->sPath) {
1274 LPITEMIDLIST pidl;
1275
1276 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1277
1278 if (SUCCEEDED(hr)) {
1279 hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1280
1281 SHFree(pidl);
1282 }
1283 }
1284
1285 IShellFolder_Release(pdsk);
1286 }
1287
1288 return hr;
1289 } else
1290 return E_FAIL;
1291 }
1292
1293 static HRESULT WINAPI IShellLinkA_fnSetIconLocation(IShellLinkA * iface, LPCSTR pszIconPath, INT iIcon)
1294 {
1295 ICOM_THIS(IShellLinkImpl, iface);
1296
1297 TRACE("(%p)->(path=%s iicon=%u)\n",This, pszIconPath, iIcon);
1298
1299 if (This->sIcoPath)
1300 HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1301 This->sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1302 if ( !This->sIcoPath )
1303 return E_OUTOFMEMORY;
1304
1305 This->iIcoNdx = iIcon;
1306 This->bDirty = TRUE;
1307
1308 return S_OK;
1309 }
1310
1311 static HRESULT WINAPI IShellLinkA_fnSetRelativePath(IShellLinkA * iface, LPCSTR pszPathRel, DWORD dwReserved)
1312 {
1313 ICOM_THIS(IShellLinkImpl, iface);
1314
1315 FIXME("(%p)->(path=%s %lx)\n",This, pszPathRel, dwReserved);
1316
1317 if (This->sPathRel)
1318 HeapFree(GetProcessHeap(), 0, This->sPathRel);
1319
1320 This->sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1321 This->bDirty = TRUE;
1322
1323 return SHELL_ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1324 }
1325
1326 static HRESULT WINAPI IShellLinkA_fnResolve(IShellLinkA * iface, HWND hwnd, DWORD fFlags)
1327 {
1328 HRESULT hr = S_OK;
1329
1330 ICOM_THIS(IShellLinkImpl, iface);
1331
1332 FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1333
1334 /*FIXME: use IResolveShellLink interface */
1335
1336 if (!This->sPath && This->pPidl) {
1337 WCHAR buffer[MAX_PATH];
1338
1339 hr = SHELL_GetPathFromIDListW(This->pPidl, buffer, MAX_PATH);
1340
1341 if (SUCCEEDED(hr) && *buffer) {
1342 This->sPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
1343 if (!This->sPath)
1344 return E_OUTOFMEMORY;
1345
1346 lstrcpyW(This->sPath, buffer);
1347
1348 This->bDirty = TRUE;
1349 } else
1350 hr = S_OK; /* don't report any error occured while just caching information */
1351 }
1352
1353 if (!This->sIcoPath && This->sPath) {
1354 This->sIcoPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
1355 if (!This->sIcoPath)
1356 return E_OUTOFMEMORY;
1357
1358 lstrcpyW(This->sIcoPath, This->sPath);
1359 This->iIcoNdx = 0;
1360
1361 This->bDirty = TRUE;
1362 }
1363
1364 return hr;
1365 }
1366
1367 static HRESULT WINAPI IShellLinkA_fnSetPath(IShellLinkA * iface, LPCSTR pszFile)
1368 {
1369 ICOM_THIS(IShellLinkImpl, iface);
1370
1371 TRACE("(%p)->(path=%s)\n",This, pszFile);
1372
1373 if (This->sPath)
1374 HeapFree(GetProcessHeap(), 0, This->sPath);
1375 This->sPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
1376 if( !This->sPath )
1377 return E_OUTOFMEMORY;
1378
1379 This->bDirty = TRUE;
1380
1381 return S_OK;
1382 }
1383
1384 /**************************************************************************
1385 * IShellLink Implementation
1386 */
1387
1388 static ICOM_VTABLE(IShellLinkA) slvt =
1389 {
1390 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1391 IShellLinkA_fnQueryInterface,
1392 IShellLinkA_fnAddRef,
1393 IShellLinkA_fnRelease,
1394 IShellLinkA_fnGetPath,
1395 IShellLinkA_fnGetIDList,
1396 IShellLinkA_fnSetIDList,
1397 IShellLinkA_fnGetDescription,
1398 IShellLinkA_fnSetDescription,
1399 IShellLinkA_fnGetWorkingDirectory,
1400 IShellLinkA_fnSetWorkingDirectory,
1401 IShellLinkA_fnGetArguments,
1402 IShellLinkA_fnSetArguments,
1403 IShellLinkA_fnGetHotkey,
1404 IShellLinkA_fnSetHotkey,
1405 IShellLinkA_fnGetShowCmd,
1406 IShellLinkA_fnSetShowCmd,
1407 IShellLinkA_fnGetIconLocation,
1408 IShellLinkA_fnSetIconLocation,
1409 IShellLinkA_fnSetRelativePath,
1410 IShellLinkA_fnResolve,
1411 IShellLinkA_fnSetPath
1412 };
1413
1414
1415 /**************************************************************************
1416 * IShellLinkW_fnQueryInterface
1417 */
1418 static HRESULT WINAPI IShellLinkW_fnQueryInterface(
1419 IShellLinkW * iface, REFIID riid, LPVOID *ppvObj)
1420 {
1421 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1422
1423 return IShellLinkA_QueryInterface((IShellLinkA*)This, riid, ppvObj);
1424 }
1425
1426 /******************************************************************************
1427 * IShellLinkW_fnAddRef
1428 */
1429 static ULONG WINAPI IShellLinkW_fnAddRef(IShellLinkW * iface)
1430 {
1431 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1432
1433 TRACE("(%p)->(count=%lu)\n",This,This->ref);
1434
1435 return IShellLinkA_AddRef((IShellLinkA*)This);
1436 }
1437 /******************************************************************************
1438 * IShellLinkW_fnRelease
1439 */
1440
1441 static ULONG WINAPI IShellLinkW_fnRelease(IShellLinkW * iface)
1442 {
1443 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1444
1445 TRACE("(%p)->(count=%lu)\n",This,This->ref);
1446
1447 return IShellLinkA_Release((IShellLinkA*)This);
1448 }
1449
1450 static HRESULT WINAPI IShellLinkW_fnGetPath(IShellLinkW * iface, LPWSTR pszFile,INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1451 {
1452 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1453
1454 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%lu)\n",
1455 This, pszFile, cchMaxPath, pfd, fFlags);
1456
1457 if( cchMaxPath )
1458 pszFile[0] = 0;
1459 if( This->sPath )
1460 lstrcpynW( pszFile, This->sPath, cchMaxPath );
1461
1462 if (pfd) {
1463
1464 FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", This);
1465
1466 }
1467
1468 return NOERROR;
1469 }
1470
1471 static HRESULT WINAPI IShellLinkW_fnGetIDList(IShellLinkW * iface, LPITEMIDLIST * ppidl)
1472 {
1473 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1474
1475 TRACE("(%p)->(ppidl=%p)\n",This, ppidl);
1476
1477 if( This->pPidl)
1478 *ppidl = ILClone( This->pPidl );
1479 else
1480 *ppidl = NULL;
1481
1482 return S_OK;
1483 }
1484
1485 static HRESULT WINAPI IShellLinkW_fnSetIDList(IShellLinkW * iface, LPCITEMIDLIST pidl)
1486 {
1487 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1488
1489 TRACE("(%p)->(pidl=%p)\n",This, pidl);
1490
1491 if( This->pPidl )
1492 ILFree( This->pPidl );
1493 This->pPidl = ILClone( pidl );
1494 if( !This->pPidl )
1495 return E_FAIL;
1496
1497 This->bDirty = TRUE;
1498
1499 return S_OK;
1500 }
1501
1502 static HRESULT WINAPI IShellLinkW_fnGetDescription(IShellLinkW * iface, LPWSTR pszName,INT cchMaxName)
1503 {
1504 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1505
1506 TRACE("(%p)->(%p len=%u)\n",This, pszName, cchMaxName);
1507
1508 if( cchMaxName )
1509 pszName[0] = 0;
1510 if( This->sDescription )
1511 lstrcpynW( pszName, This->sDescription, cchMaxName );
1512
1513 return S_OK;
1514 }
1515
1516 static HRESULT WINAPI IShellLinkW_fnSetDescription(IShellLinkW * iface, LPCWSTR pszName)
1517 {
1518 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1519
1520 TRACE("(%p)->(desc=%s)\n",This, debugstr_w(pszName));
1521
1522 if (This->sDescription)
1523 HeapFree(GetProcessHeap(), 0, This->sDescription);
1524 This->sDescription = HeapAlloc( GetProcessHeap(), 0,
1525 (lstrlenW( pszName )+1)*sizeof(WCHAR) );
1526 if ( !This->sDescription )
1527 return E_OUTOFMEMORY;
1528
1529 lstrcpyW( This->sDescription, pszName );
1530 This->bDirty = TRUE;
1531
1532 return S_OK;
1533 }
1534
1535 static HRESULT WINAPI IShellLinkW_fnGetWorkingDirectory(IShellLinkW * iface, LPWSTR pszDir,INT cchMaxPath)
1536 {
1537 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1538
1539 TRACE("(%p)->(%p len %u)\n", This, pszDir, cchMaxPath);
1540
1541 if( cchMaxPath )
1542 pszDir[0] = 0;
1543 if( This->sWorkDir )
1544 lstrcpynW( pszDir, This->sWorkDir, cchMaxPath );
1545
1546 return S_OK;
1547 }
1548
1549 static HRESULT WINAPI IShellLinkW_fnSetWorkingDirectory(IShellLinkW * iface, LPCWSTR pszDir)
1550 {
1551 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1552
1553 TRACE("(%p)->(dir=%s)\n",This, debugstr_w(pszDir));
1554
1555 if (This->sWorkDir)
1556 HeapFree(GetProcessHeap(), 0, This->sWorkDir);
1557 This->sWorkDir = HeapAlloc( GetProcessHeap(), 0,
1558 (lstrlenW( pszDir )+1)*sizeof (WCHAR) );
1559 if ( !This->sWorkDir )
1560 return E_OUTOFMEMORY;
1561
1562 lstrcpyW( This->sWorkDir, pszDir );
1563 This->bDirty = TRUE;
1564
1565 return S_OK;
1566 }
1567
1568 static HRESULT WINAPI IShellLinkW_fnGetArguments(IShellLinkW * iface, LPWSTR pszArgs,INT cchMaxPath)
1569 {
1570 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1571
1572 TRACE("(%p)->(%p len=%u)\n", This, pszArgs, cchMaxPath);
1573
1574 if( cchMaxPath )
1575 pszArgs[0] = 0;
1576 if( This->sArgs )
1577 lstrcpynW( pszArgs, This->sArgs, cchMaxPath );
1578
1579 return NOERROR;
1580 }
1581
1582 static HRESULT WINAPI IShellLinkW_fnSetArguments(IShellLinkW * iface, LPCWSTR pszArgs)
1583 {
1584 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1585
1586 TRACE("(%p)->(args=%s)\n",This, debugstr_w(pszArgs));
1587
1588 if (This->sArgs)
1589 HeapFree(GetProcessHeap(), 0, This->sArgs);
1590 This->sArgs = HeapAlloc( GetProcessHeap(), 0,
1591 (lstrlenW( pszArgs )+1)*sizeof (WCHAR) );
1592 if ( !This->sArgs )
1593 return E_OUTOFMEMORY;
1594
1595 lstrcpyW( This->sArgs, pszArgs );
1596 This->bDirty = TRUE;
1597
1598 return S_OK;
1599 }
1600
1601 static HRESULT WINAPI IShellLinkW_fnGetHotkey(IShellLinkW * iface, WORD *pwHotkey)
1602 {
1603 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1604
1605 TRACE("(%p)->(%p)\n",This, pwHotkey);
1606
1607 *pwHotkey=This->wHotKey;
1608
1609 return S_OK;
1610 }
1611
1612 static HRESULT WINAPI IShellLinkW_fnSetHotkey(IShellLinkW * iface, WORD wHotkey)
1613 {
1614 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1615
1616 TRACE("(%p)->(hotkey=%x)\n",This, wHotkey);
1617
1618 This->wHotKey = wHotkey;
1619 This->bDirty = TRUE;
1620
1621 return S_OK;
1622 }
1623
1624 static HRESULT WINAPI IShellLinkW_fnGetShowCmd(IShellLinkW * iface, INT *piShowCmd)
1625 {
1626 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1627
1628 TRACE("(%p)->(%p)\n",This, piShowCmd);
1629
1630 *piShowCmd = This->iShowCmd;
1631
1632 return S_OK;
1633 }
1634
1635 static HRESULT WINAPI IShellLinkW_fnSetShowCmd(IShellLinkW * iface, INT iShowCmd)
1636 {
1637 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1638
1639 This->iShowCmd = iShowCmd;
1640 This->bDirty = TRUE;
1641
1642 return S_OK;
1643 }
1644
1645 static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPITEMIDLIST pidl, LPWSTR pszIconPath, int cchIconPath, int* piIcon)
1646 {
1647 LPCITEMIDLIST pidlLast;
1648
1649 HRESULT hr = SHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&psf, &pidlLast);
1650
1651 if (SUCCEEDED(hr)) {
1652 IExtractIconW* pei;
1653
1654 hr = IShellFolder_GetUIObjectOf(psf, 0, 1, (LPCITEMIDLIST*)&pidlLast, &IID_IExtractIconW, NULL, (LPVOID*)&pei);
1655
1656 if (SUCCEEDED(hr)) {
1657 hr = IExtractIconW_GetIconLocation(pei, 0, pszIconPath, MAX_PATH, piIcon, NULL);
1658
1659 IExtractIconW_Release(pei);
1660 }
1661
1662 IShellFolder_Release(psf);
1663 }
1664
1665 return hr;
1666 }
1667
1668 static HRESULT WINAPI IShellLinkW_fnGetIconLocation(IShellLinkW * iface, LPWSTR pszIconPath, INT cchIconPath, INT *piIcon)
1669 {
1670 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1671
1672 TRACE("(%p)->(%p len=%u iicon=%p)\n", This, pszIconPath, cchIconPath, piIcon);
1673
1674 if (cchIconPath)
1675 pszIconPath[0] = 0;
1676
1677 if (This->sIcoPath) {
1678 lstrcpynW(pszIconPath, This->sIcoPath, cchIconPath);
1679 *piIcon = This->iIcoNdx;
1680 return S_OK;
1681 }
1682
1683 if (This->pPidl || This->sPath) {
1684 IShellFolder* pdsk;
1685
1686 HRESULT hr = SHGetDesktopFolder(&pdsk);
1687
1688 if (SUCCEEDED(hr)) {
1689 /* first look for an icon using the PIDL (if present) */
1690 if (This->pPidl)
1691 hr = SHELL_PidlGeticonLocationW(pdsk, This->pPidl, pszIconPath, cchIconPath, piIcon);
1692 else
1693 hr = E_FAIL;
1694
1695 /* if we couldn't find an icon yet, look for it using the file system path */
1696 if (FAILED(hr) && This->sPath) {
1697 LPITEMIDLIST pidl;
1698
1699 hr = IShellFolder_ParseDisplayName(pdsk, 0, NULL, This->sPath, NULL, &pidl, NULL);
1700
1701 if (SUCCEEDED(hr)) {
1702 hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1703
1704 SHFree(pidl);
1705 }
1706 }
1707
1708 IShellFolder_Release(pdsk);
1709 }
1710
1711 return hr;
1712 } else
1713 return E_FAIL;
1714 }
1715
1716 static HRESULT WINAPI IShellLinkW_fnSetIconLocation(IShellLinkW * iface, LPCWSTR pszIconPath, INT iIcon)
1717 {
1718 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1719
1720 TRACE("(%p)->(path=%s iicon=%u)\n",This, debugstr_w(pszIconPath), iIcon);
1721
1722 if (This->sIcoPath)
1723 HeapFree(GetProcessHeap(), 0, This->sIcoPath);
1724 This->sIcoPath = HeapAlloc( GetProcessHeap(), 0,
1725 (lstrlenW( pszIconPath )+1)*sizeof (WCHAR) );
1726 if ( !This->sIcoPath )
1727 return E_OUTOFMEMORY;
1728 lstrcpyW( This->sIcoPath, pszIconPath );
1729
1730 This->iIcoNdx = iIcon;
1731 This->bDirty = TRUE;
1732
1733 return S_OK;
1734 }
1735
1736 static HRESULT WINAPI IShellLinkW_fnSetRelativePath(IShellLinkW * iface, LPCWSTR pszPathRel, DWORD dwReserved)
1737 {
1738 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1739
1740 TRACE("(%p)->(path=%s %lx)\n",This, debugstr_w(pszPathRel), dwReserved);
1741
1742 if (This->sPathRel)
1743 HeapFree(GetProcessHeap(), 0, This->sPathRel);
1744 This->sPathRel = HeapAlloc( GetProcessHeap(), 0,
1745 (lstrlenW( pszPathRel )+1) * sizeof (WCHAR) );
1746 if ( !This->sPathRel )
1747 return E_OUTOFMEMORY;
1748
1749 lstrcpyW( This->sPathRel, pszPathRel );
1750 This->bDirty = TRUE;
1751
1752 return SHELL_ShellLink_UpdatePath(This->sPathRel, This->sPath, This->sWorkDir, &This->sPath);
1753 }
1754
1755 static HRESULT WINAPI IShellLinkW_fnResolve(IShellLinkW * iface, HWND hwnd, DWORD fFlags)
1756 {
1757 HRESULT hr = S_OK;
1758
1759 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1760
1761 FIXME("(%p)->(hwnd=%p flags=%lx)\n",This, hwnd, fFlags);
1762
1763 /*FIXME: use IResolveShellLink interface */
1764
1765 if (!This->sPath && This->pPidl) {
1766 WCHAR buffer[MAX_PATH];
1767
1768 hr = SHELL_GetPathFromIDListW(This->pPidl, buffer, MAX_PATH);
1769
1770 if (SUCCEEDED(hr) && *buffer) {
1771 This->sPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(buffer)+1)*sizeof(WCHAR));
1772 if (!This->sPath)
1773 return E_OUTOFMEMORY;
1774
1775 lstrcpyW(This->sPath, buffer);
1776
1777 This->bDirty = TRUE;
1778 } else
1779 hr = S_OK; /* don't report any error occured while just caching information */
1780 }
1781
1782 if (!This->sIcoPath && This->sPath) {
1783 This->sIcoPath = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (lstrlenW(This->sPath)+1)*sizeof(WCHAR));
1784 if (!This->sIcoPath)
1785 return E_OUTOFMEMORY;
1786
1787 lstrcpyW(This->sIcoPath, This->sPath);
1788 This->iIcoNdx = 0;
1789
1790 This->bDirty = TRUE;
1791 }
1792
1793 return hr;
1794 }
1795
1796 static HRESULT WINAPI IShellLinkW_fnSetPath(IShellLinkW * iface, LPCWSTR pszFile)
1797 {
1798 _ICOM_THIS_From_IShellLinkW(IShellLinkImpl, iface);
1799
1800 TRACE("(%p)->(path=%s)\n",This, debugstr_w(pszFile));
1801
1802 if (This->sPath)
1803 HeapFree(GetProcessHeap(), 0, This->sPath);
1804 This->sPath = HeapAlloc( GetProcessHeap(), 0,
1805 (lstrlenW( pszFile )+1) * sizeof (WCHAR) );
1806 if ( !This->sPath )
1807 return E_OUTOFMEMORY;
1808
1809 lstrcpyW( This->sPath, pszFile );
1810 This->bDirty = TRUE;
1811
1812 return S_OK;
1813 }
1814
1815 /**************************************************************************
1816 * IShellLinkW Implementation
1817 */
1818
1819 static ICOM_VTABLE(IShellLinkW) slvtw =
1820 {
1821 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1822 IShellLinkW_fnQueryInterface,
1823 IShellLinkW_fnAddRef,
1824 IShellLinkW_fnRelease,
1825 IShellLinkW_fnGetPath,
1826 IShellLinkW_fnGetIDList,
1827 IShellLinkW_fnSetIDList,
1828 IShellLinkW_fnGetDescription,
1829 IShellLinkW_fnSetDescription,
1830 IShellLinkW_fnGetWorkingDirectory,
1831 IShellLinkW_fnSetWorkingDirectory,
1832 IShellLinkW_fnGetArguments,
1833 IShellLinkW_fnSetArguments,
1834 IShellLinkW_fnGetHotkey,
1835 IShellLinkW_fnSetHotkey,
1836 IShellLinkW_fnGetShowCmd,
1837 IShellLinkW_fnSetShowCmd,
1838 IShellLinkW_fnGetIconLocation,
1839 IShellLinkW_fnSetIconLocation,
1840 IShellLinkW_fnSetRelativePath,
1841 IShellLinkW_fnResolve,
1842 IShellLinkW_fnSetPath
1843 };