Sync with trunk rev.61910 to get latest improvements and bugfixes.
[reactos.git] / dll / win32 / shell32 / shelllink.cpp
1 /*
2 *
3 * Copyright 1997 Marcus Meissner
4 * Copyright 1998 Juergen Schmied
5 * Copyright 2005 Mike McCormack
6 * Copyright 2009 Andrew Hill
7 * Copyright 2013 Dominik Hornung
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 *
23 * NOTES
24 * Nearly complete information about the binary formats
25 * of .lnk files available at http://www.wotsit.org
26 *
27 * You can use winedump to examine the contents of a link file:
28 * winedump lnk sc.lnk
29 *
30 * MSI advertised shortcuts are totally undocumented. They provide an
31 * icon for a program that is not yet installed, and invoke MSI to
32 * install the program when the shortcut is clicked on. They are
33 * created by passing a special string to SetPath, and the information
34 * in that string is parsed an stored.
35 */
36
37 #include "precomp.h"
38
39 #include <appmgmt.h>
40
41 WINE_DEFAULT_DEBUG_CHANNEL(shell);
42
43 #define SHLINK_LOCAL 0
44 #define SHLINK_REMOTE 1
45 #define MAX_PROPERTY_SHEET_PAGE 32
46
47 /* link file formats */
48
49 #include "pshpack1.h"
50
51 struct LOCATION_INFO
52 {
53 DWORD dwTotalSize;
54 DWORD dwHeaderSize;
55 DWORD dwFlags;
56 DWORD dwVolTableOfs;
57 DWORD dwLocalPathOfs;
58 DWORD dwNetworkVolTableOfs;
59 DWORD dwFinalPathOfs;
60 };
61
62 struct LOCAL_VOLUME_INFO
63 {
64 DWORD dwSize;
65 DWORD dwType;
66 DWORD dwVolSerial;
67 DWORD dwVolLabelOfs;
68 };
69
70 struct volume_info
71 {
72 DWORD type;
73 DWORD serial;
74 WCHAR label[12]; /* assume 8.3 */
75 };
76
77 #include "poppack.h"
78
79 /* IShellLink Implementation */
80
81 static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath);
82
83 /* strdup on the process heap */
84 static LPWSTR __inline HEAP_strdupAtoW(HANDLE heap, DWORD flags, LPCSTR str)
85 {
86 INT len;
87 LPWSTR p;
88
89 assert(str);
90
91 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
92 p = (LPWSTR)HeapAlloc(heap, flags, len * sizeof(WCHAR));
93 if (!p)
94 return p;
95 MultiByteToWideChar(CP_ACP, 0, str, -1, p, len);
96 return p;
97 }
98
99 static LPWSTR __inline strdupW(LPCWSTR src)
100 {
101 LPWSTR dest;
102 if (!src) return NULL;
103 dest = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(src) + 1) * sizeof(WCHAR));
104 if (dest)
105 wcscpy(dest, src);
106 return dest;
107 }
108
109 CShellLink::CShellLink()
110 {
111 pPidl = NULL;
112 wHotKey = 0;
113 memset(&time1, 0, sizeof(time1));
114 memset(&time2, 0, sizeof(time2));
115 memset(&time3, 0, sizeof(time3));
116 iShowCmd = SW_SHOWNORMAL;
117 sIcoPath = NULL;
118 iIcoNdx = 0;
119 sPath = NULL;
120 sArgs = NULL;
121 sWorkDir = NULL;
122 sDescription = NULL;
123 sPathRel = NULL;
124 sProduct = NULL;
125 sComponent = NULL;
126 memset(&volume, 0, sizeof(volume));
127 sLinkPath = NULL;
128 bRunAs = FALSE;
129 bDirty = FALSE;
130 iIdOpen = -1;
131 }
132
133 CShellLink::~CShellLink()
134 {
135 TRACE("-- destroying IShellLink(%p)\n", this);
136
137 HeapFree(GetProcessHeap(), 0, sIcoPath);
138 HeapFree(GetProcessHeap(), 0, sArgs);
139 HeapFree(GetProcessHeap(), 0, sWorkDir);
140 HeapFree(GetProcessHeap(), 0, sDescription);
141 HeapFree(GetProcessHeap(), 0, sPath);
142 HeapFree(GetProcessHeap(), 0, sLinkPath);
143
144 if (pPidl)
145 ILFree(pPidl);
146 }
147
148 HRESULT WINAPI CShellLink::GetClassID(CLSID *pclsid)
149 {
150 TRACE("%p %p\n", this, pclsid);
151
152 if (pclsid == NULL)
153 return E_POINTER;
154 *pclsid = CLSID_ShellLink;
155 return S_OK;
156 }
157
158 HRESULT WINAPI CShellLink::IsDirty()
159 {
160 TRACE("(%p)\n", this);
161
162 if (bDirty)
163 return S_OK;
164
165 return S_FALSE;
166 }
167
168 HRESULT WINAPI CShellLink::Load(LPCOLESTR pszFileName, DWORD dwMode)
169 {
170 TRACE("(%p, %s, %x)\n", this, debugstr_w(pszFileName), dwMode);
171
172 if (dwMode == 0)
173 dwMode = STGM_READ | STGM_SHARE_DENY_WRITE;
174
175 CComPtr<IStream> stm;
176 HRESULT hr = SHCreateStreamOnFileW(pszFileName, dwMode, &stm);
177 if (SUCCEEDED(hr))
178 {
179 HeapFree(GetProcessHeap(), 0, sLinkPath);
180 sLinkPath = strdupW(pszFileName);
181 hr = Load(stm);
182 ShellLink_UpdatePath(sPathRel, pszFileName, sWorkDir, &sPath);
183 bDirty = FALSE;
184 }
185 TRACE("-- returning hr %08x\n", hr);
186 return hr;
187 }
188
189 HRESULT WINAPI CShellLink::Save(LPCOLESTR pszFileName, BOOL fRemember)
190 {
191 TRACE("(%p)->(%s)\n", this, debugstr_w(pszFileName));
192
193 if (!pszFileName)
194 return E_FAIL;
195
196 CComPtr<IStream> stm;
197 HRESULT hr = SHCreateStreamOnFileW(pszFileName, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &stm);
198 if (SUCCEEDED(hr))
199 {
200 hr = Save(stm, FALSE);
201
202 if (SUCCEEDED(hr))
203 {
204 if (sLinkPath)
205 HeapFree(GetProcessHeap(), 0, sLinkPath);
206
207 sLinkPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(pszFileName) + 1) * sizeof(WCHAR));
208 if (sLinkPath)
209 wcscpy(sLinkPath, pszFileName);
210
211 bDirty = FALSE;
212 }
213 else
214 {
215 DeleteFileW(pszFileName);
216 WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName));
217 }
218 }
219
220 return hr;
221 }
222
223 HRESULT WINAPI CShellLink::SaveCompleted(LPCOLESTR pszFileName)
224 {
225 FIXME("(%p)->(%s)\n", this, debugstr_w(pszFileName));
226 return S_OK;
227 }
228
229 HRESULT WINAPI CShellLink::GetCurFile(LPOLESTR *ppszFileName)
230 {
231 *ppszFileName = NULL;
232
233 if (!sLinkPath)
234 {
235 /* IPersistFile::GetCurFile called before IPersistFile::Save */
236 return S_FALSE;
237 }
238
239 *ppszFileName = (LPOLESTR)CoTaskMemAlloc((wcslen(sLinkPath) + 1) * sizeof(WCHAR));
240 if (!*ppszFileName)
241 {
242 /* out of memory */
243 return E_OUTOFMEMORY;
244 }
245
246 /* copy last saved filename */
247 wcscpy(*ppszFileName, sLinkPath);
248
249 return S_OK;
250 }
251
252 /************************************************************************
253 * IPersistStream_IsDirty (IPersistStream)
254 */
255
256 static HRESULT Stream_LoadString(IStream* stm, BOOL unicode, LPWSTR *pstr)
257 {
258 TRACE("%p\n", stm);
259
260 USHORT len;
261 DWORD count = 0;
262 HRESULT hr = stm->Read(&len, sizeof(len), &count);
263 if (FAILED(hr) || count != sizeof(len))
264 return E_FAIL;
265
266 if (unicode)
267 len *= sizeof (WCHAR);
268
269 TRACE("reading %d\n", len);
270 LPSTR temp = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len + sizeof(WCHAR));
271 if (!temp)
272 return E_OUTOFMEMORY;
273 count = 0;
274 hr = stm->Read(temp, len, &count);
275 if(FAILED(hr) || count != len)
276 {
277 HeapFree(GetProcessHeap(), 0, temp);
278 return E_FAIL;
279 }
280
281 TRACE("read %s\n", debugstr_an(temp, len));
282
283 /* convert to unicode if necessary */
284 LPWSTR str;
285 if (!unicode)
286 {
287 count = MultiByteToWideChar(CP_ACP, 0, temp, len, NULL, 0);
288 str = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (count + 1) * sizeof (WCHAR));
289 if (!str)
290 {
291 HeapFree(GetProcessHeap(), 0, temp);
292 return E_OUTOFMEMORY;
293 }
294 MultiByteToWideChar(CP_ACP, 0, temp, len, str, count);
295 HeapFree(GetProcessHeap(), 0, temp);
296 }
297 else
298 {
299 count /= 2;
300 str = (LPWSTR)temp;
301 }
302 str[count] = 0;
303
304 *pstr = str;
305
306 return S_OK;
307 }
308
309 static HRESULT Stream_ReadChunk(IStream* stm, LPVOID *data)
310 {
311 struct sized_chunk
312 {
313 DWORD size;
314 unsigned char data[1];
315 } *chunk;
316
317 TRACE("%p\n", stm);
318
319 DWORD size;
320 ULONG count;
321 HRESULT hr = stm->Read(&size, sizeof(size), &count);
322 if (FAILED(hr) || count != sizeof(size))
323 return E_FAIL;
324
325 chunk = (sized_chunk *)HeapAlloc(GetProcessHeap(), 0, size);
326 if (!chunk)
327 return E_OUTOFMEMORY;
328
329 chunk->size = size;
330 hr = stm->Read(chunk->data, size - sizeof(size), &count);
331 if (FAILED(hr) || count != (size - sizeof(size)))
332 {
333 HeapFree(GetProcessHeap(), 0, chunk);
334 return E_FAIL;
335 }
336
337 TRACE("Read %d bytes\n", chunk->size);
338
339 *data = chunk;
340
341 return S_OK;
342 }
343
344 static BOOL Stream_LoadVolume(LOCAL_VOLUME_INFO *vol, CShellLink::volume_info *volume)
345 {
346 volume->serial = vol->dwVolSerial;
347 volume->type = vol->dwType;
348
349 if (!vol->dwVolLabelOfs)
350 return FALSE;
351 if (vol->dwSize <= vol->dwVolLabelOfs)
352 return FALSE;
353 INT len = vol->dwSize - vol->dwVolLabelOfs;
354
355 LPSTR label = (LPSTR)vol;
356 label += vol->dwVolLabelOfs;
357 MultiByteToWideChar(CP_ACP, 0, label, len, volume->label, _countof(volume->label));
358
359 return TRUE;
360 }
361
362 static LPWSTR Stream_LoadPath(LPCSTR p, DWORD maxlen)
363 {
364 UINT len = 0;
365
366 while (p[len] && len < maxlen)
367 len++;
368
369 UINT wlen = MultiByteToWideChar(CP_ACP, 0, p, len, NULL, 0);
370 LPWSTR path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wlen + 1) * sizeof(WCHAR));
371 if (!path)
372 return NULL;
373 MultiByteToWideChar(CP_ACP, 0, p, len, path, wlen);
374 path[wlen] = 0;
375
376 return path;
377 }
378
379 static HRESULT Stream_LoadLocation(IStream *stm,
380 CShellLink::volume_info *volume, LPWSTR *path)
381 {
382 char *p = NULL;
383 HRESULT hr = Stream_ReadChunk(stm, (LPVOID*) &p);
384 if (FAILED(hr))
385 return hr;
386
387 LOCATION_INFO *loc = (LOCATION_INFO*) p;
388 if (loc->dwTotalSize < sizeof(LOCATION_INFO))
389 {
390 HeapFree(GetProcessHeap(), 0, p);
391 return E_FAIL;
392 }
393
394 /* if there's valid local volume information, load it */
395 if (loc->dwVolTableOfs &&
396 ((loc->dwVolTableOfs + sizeof(LOCAL_VOLUME_INFO)) <= loc->dwTotalSize))
397 {
398 LOCAL_VOLUME_INFO *volume_info;
399
400 volume_info = (LOCAL_VOLUME_INFO*) &p[loc->dwVolTableOfs];
401 Stream_LoadVolume(volume_info, volume);
402 }
403
404 /* if there's a local path, load it */
405 DWORD n = loc->dwLocalPathOfs;
406 if (n && n < loc->dwTotalSize)
407 *path = Stream_LoadPath(&p[n], loc->dwTotalSize - n);
408
409 TRACE("type %d serial %08x name %s path %s\n", volume->type,
410 volume->serial, debugstr_w(volume->label), debugstr_w(*path));
411
412 HeapFree(GetProcessHeap(), 0, p);
413 return S_OK;
414 }
415
416 /*
417 * The format of the advertised shortcut info seems to be:
418 *
419 * Offset Description
420 * ------ -----------
421 *
422 * 0 Length of the block (4 bytes, usually 0x314)
423 * 4 tag (dword)
424 * 8 string data in ASCII
425 * 8+0x104 string data in UNICODE
426 *
427 * In the original Win32 implementation the buffers are not initialized
428 * to zero, so data trailing the string is random garbage.
429 */
430 static HRESULT Stream_LoadAdvertiseInfo(IStream* stm, LPWSTR *str)
431 {
432 TRACE("%p\n", stm);
433
434 ULONG count;
435 EXP_DARWIN_LINK buffer;
436 HRESULT hr = stm->Read(&buffer.dbh.cbSize, sizeof (DWORD), &count);
437 if (FAILED(hr))
438 return hr;
439
440 /* make sure that we read the size of the structure even on error */
441 DWORD size = sizeof buffer - sizeof (DWORD);
442 if (buffer.dbh.cbSize != sizeof buffer)
443 {
444 ERR("Ooops. This structure is not as expected...\n");
445 return E_FAIL;
446 }
447
448 hr = stm->Read(&buffer.dbh.dwSignature, size, &count);
449 if (FAILED(hr))
450 return hr;
451
452 if (count != size)
453 return E_FAIL;
454
455 TRACE("magic %08x string = %s\n", buffer.dbh.dwSignature, debugstr_w(buffer.szwDarwinID));
456
457 if ((buffer.dbh.dwSignature & 0xffff0000) != 0xa0000000)
458 {
459 ERR("Unknown magic number %08x in advertised shortcut\n", buffer.dbh.dwSignature);
460 return E_FAIL;
461 }
462
463 *str = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
464 (wcslen(buffer.szwDarwinID) + 1) * sizeof(WCHAR));
465 wcscpy(*str, buffer.szwDarwinID);
466
467 return S_OK;
468 }
469
470 /************************************************************************
471 * IPersistStream_Load (IPersistStream)
472 */
473 HRESULT WINAPI CShellLink::Load(IStream *stm)
474 {
475 TRACE("%p %p\n", this, stm);
476
477 if (!stm)
478 return STG_E_INVALIDPOINTER;
479
480 SHELL_LINK_HEADER ShlLnkHeader;
481 ULONG dwBytesRead = 0;
482 HRESULT hr = stm->Read(&ShlLnkHeader, sizeof(ShlLnkHeader), &dwBytesRead);
483 if (FAILED(hr))
484 return hr;
485
486 if (dwBytesRead != sizeof(ShlLnkHeader))
487 return E_FAIL;
488 if (ShlLnkHeader.dwSize != sizeof(ShlLnkHeader))
489 return E_FAIL;
490 if (!IsEqualIID(ShlLnkHeader.clsid, CLSID_ShellLink))
491 return E_FAIL;
492
493 /* free all the old stuff */
494 ILFree(pPidl);
495 pPidl = NULL;
496 memset(&volume, 0, sizeof volume);
497 HeapFree(GetProcessHeap(), 0, sPath);
498 sPath = NULL;
499 HeapFree(GetProcessHeap(), 0, sDescription);
500 sDescription = NULL;
501 HeapFree(GetProcessHeap(), 0, sPathRel);
502 sPathRel = NULL;
503 HeapFree(GetProcessHeap(), 0, sWorkDir);
504 sWorkDir = NULL;
505 HeapFree(GetProcessHeap(), 0, sArgs);
506 sArgs = NULL;
507 HeapFree(GetProcessHeap(), 0, sIcoPath);
508 sIcoPath = NULL;
509 HeapFree(GetProcessHeap(), 0, sProduct);
510 sProduct = NULL;
511 HeapFree(GetProcessHeap(), 0, sComponent);
512 sComponent = NULL;
513
514 BOOL unicode = FALSE;
515 iShowCmd = ShlLnkHeader.nShowCommand;
516 wHotKey = ShlLnkHeader.wHotKey;
517 iIcoNdx = ShlLnkHeader.nIconIndex;
518 FileTimeToSystemTime (&ShlLnkHeader.ftCreationTime, &time1);
519 FileTimeToSystemTime (&ShlLnkHeader.ftLastAccessTime, &time2);
520 FileTimeToSystemTime (&ShlLnkHeader.ftLastWriteTime, &time3);
521 if (TRACE_ON(shell))
522 {
523 WCHAR sTemp[MAX_PATH];
524 GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time1,
525 NULL, sTemp, sizeof(sTemp) / sizeof(*sTemp));
526 TRACE("-- time1: %s\n", debugstr_w(sTemp));
527 GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time2,
528 NULL, sTemp, sizeof(sTemp) / sizeof(*sTemp));
529 TRACE("-- time2: %s\n", debugstr_w(sTemp));
530 GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time3,
531 NULL, sTemp, sizeof(sTemp) / sizeof(*sTemp));
532 TRACE("-- time3: %s\n", debugstr_w(sTemp));
533 }
534
535 /* load all the new stuff */
536 if (ShlLnkHeader.dwFlags & SLDF_HAS_ID_LIST)
537 {
538 hr = ILLoadFromStream(stm, &pPidl);
539 if (FAILED(hr))
540 return hr;
541 }
542 pdump(pPidl);
543
544 /* load the location information */
545 if (ShlLnkHeader.dwFlags & SLDF_HAS_LINK_INFO)
546 hr = Stream_LoadLocation(stm, &volume, &sPath);
547 if (FAILED(hr))
548 goto end;
549
550 if (ShlLnkHeader.dwFlags & SLDF_UNICODE)
551 unicode = TRUE;
552
553 if (ShlLnkHeader.dwFlags & SLDF_HAS_NAME)
554 {
555 hr = Stream_LoadString(stm, unicode, &sDescription);
556 TRACE("Description -> %s\n", debugstr_w(sDescription));
557 }
558 if (FAILED(hr))
559 goto end;
560
561 if (ShlLnkHeader.dwFlags & SLDF_HAS_RELPATH)
562 {
563 hr = Stream_LoadString(stm, unicode, &sPathRel);
564 TRACE("Relative Path-> %s\n", debugstr_w(sPathRel));
565 }
566 if (FAILED(hr))
567 goto end;
568
569 if (ShlLnkHeader.dwFlags & SLDF_HAS_WORKINGDIR)
570 {
571 hr = Stream_LoadString(stm, unicode, &sWorkDir);
572 PathRemoveBackslash(sWorkDir);
573 TRACE("Working Dir -> %s\n", debugstr_w(sWorkDir));
574 }
575 if (FAILED(hr))
576 goto end;
577
578 if (ShlLnkHeader.dwFlags & SLDF_HAS_ARGS)
579 {
580 hr = Stream_LoadString(stm, unicode, &sArgs);
581 TRACE("Arguments -> %s\n", debugstr_w(sArgs));
582 }
583 if (FAILED(hr))
584 goto end;
585
586 if (ShlLnkHeader.dwFlags & SLDF_HAS_ICONLOCATION)
587 {
588 hr = Stream_LoadString(stm, unicode, &sIcoPath);
589 TRACE("Icon file -> %s\n", debugstr_w(sIcoPath));
590 }
591 if (FAILED(hr))
592 goto end;
593
594 #if (NTDDI_VERSION < NTDDI_LONGHORN)
595 if (ShlLnkHeader.dwFlags & SLDF_HAS_LOGO3ID)
596 {
597 hr = Stream_LoadAdvertiseInfo(stm, &sProduct);
598 TRACE("Product -> %s\n", debugstr_w(sProduct));
599 }
600 if (FAILED(hr))
601 goto end;
602 #endif
603
604 if (ShlLnkHeader.dwFlags & SLDF_HAS_DARWINID)
605 {
606 hr = Stream_LoadAdvertiseInfo(stm, &sComponent);
607 TRACE("Component -> %s\n", debugstr_w(sComponent));
608 }
609 if (ShlLnkHeader.dwFlags & SLDF_RUNAS_USER)
610 {
611 bRunAs = TRUE;
612 }
613 else
614 {
615 bRunAs = FALSE;
616 }
617
618 if (FAILED(hr))
619 goto end;
620
621 DWORD dwZero;
622 hr = stm->Read(&dwZero, sizeof(dwZero), &dwBytesRead);
623 if (FAILED(hr) || dwZero || dwBytesRead != sizeof(dwZero))
624 ERR("Last word was not zero\n");
625
626 TRACE("OK\n");
627
628 pdump (pPidl);
629
630 return S_OK;
631
632 end:
633 return hr;
634 }
635
636 /************************************************************************
637 * Stream_WriteString
638 *
639 * Helper function for IPersistStream_Save. Writes a unicode string
640 * with terminating nul byte to a stream, preceded by the its length.
641 */
642 static HRESULT Stream_WriteString(IStream* stm, LPCWSTR str)
643 {
644 USHORT len = wcslen(str) + 1;
645 DWORD count;
646
647 HRESULT hr = stm->Write(&len, sizeof(len), &count);
648 if (FAILED(hr))
649 return hr;
650
651 len *= sizeof(WCHAR);
652
653 hr = stm->Write(str, len, &count);
654 if (FAILED(hr))
655 return hr;
656
657 return S_OK;
658 }
659
660 /************************************************************************
661 * Stream_WriteLocationInfo
662 *
663 * Writes the location info to a stream
664 *
665 * FIXME: One day we might want to write the network volume information
666 * and the final path.
667 * Figure out how Windows deals with unicode paths here.
668 */
669 static HRESULT Stream_WriteLocationInfo(IStream* stm, LPCWSTR path,
670 CShellLink::volume_info *volume)
671 {
672 LOCAL_VOLUME_INFO *vol;
673 LOCATION_INFO *loc;
674
675 TRACE("%p %s %p\n", stm, debugstr_w(path), volume);
676
677 /* figure out the size of everything */
678 DWORD label_size = WideCharToMultiByte(CP_ACP, 0, volume->label, -1,
679 NULL, 0, NULL, NULL);
680 DWORD path_size = WideCharToMultiByte(CP_ACP, 0, path, -1,
681 NULL, 0, NULL, NULL);
682 DWORD volume_info_size = sizeof(*vol) + label_size;
683 DWORD final_path_size = 1;
684 DWORD total_size = sizeof(*loc) + volume_info_size + path_size + final_path_size;
685
686 /* create pointers to everything */
687 loc = (LOCATION_INFO *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total_size);
688 vol = (LOCAL_VOLUME_INFO*) &loc[1];
689 LPSTR szLabel = (LPSTR) &vol[1];
690 LPSTR szPath = &szLabel[label_size];
691 LPSTR szFinalPath = &szPath[path_size];
692
693 /* fill in the location information header */
694 loc->dwTotalSize = total_size;
695 loc->dwHeaderSize = sizeof (*loc);
696 loc->dwFlags = 1;
697 loc->dwVolTableOfs = sizeof (*loc);
698 loc->dwLocalPathOfs = sizeof (*loc) + volume_info_size;
699 loc->dwNetworkVolTableOfs = 0;
700 loc->dwFinalPathOfs = sizeof (*loc) + volume_info_size + path_size;
701
702 /* fill in the volume information */
703 vol->dwSize = volume_info_size;
704 vol->dwType = volume->type;
705 vol->dwVolSerial = volume->serial;
706 vol->dwVolLabelOfs = sizeof (*vol);
707
708 /* copy in the strings */
709 WideCharToMultiByte(CP_ACP, 0, volume->label, -1,
710 szLabel, label_size, NULL, NULL);
711 WideCharToMultiByte(CP_ACP, 0, path, -1,
712 szPath, path_size, NULL, NULL);
713 szFinalPath[0] = 0;
714
715 ULONG count = 0;
716 HRESULT hr = stm->Write(loc, total_size, &count);
717 HeapFree(GetProcessHeap(), 0, loc);
718
719 return hr;
720 }
721
722 static EXP_DARWIN_LINK* shelllink_build_darwinid(LPCWSTR string, DWORD magic)
723 {
724 EXP_DARWIN_LINK *buffer = (EXP_DARWIN_LINK *)LocalAlloc(LMEM_ZEROINIT, sizeof * buffer);
725 buffer->dbh.cbSize = sizeof * buffer;
726 buffer->dbh.dwSignature = magic;
727 lstrcpynW(buffer->szwDarwinID, string, MAX_PATH);
728 WideCharToMultiByte(CP_ACP, 0, string, -1, buffer->szDarwinID, MAX_PATH, NULL, NULL);
729
730 return buffer;
731 }
732
733 static HRESULT Stream_WriteAdvertiseInfo(IStream* stm, LPCWSTR string, DWORD magic)
734 {
735 TRACE("%p\n", stm);
736
737 EXP_DARWIN_LINK *buffer = shelllink_build_darwinid(string, magic);
738
739 ULONG count;
740 return stm->Write(buffer, buffer->dbh.cbSize, &count);
741 }
742
743 /************************************************************************
744 * IPersistStream_Save (IPersistStream)
745 *
746 * FIXME: makes assumptions about byte order
747 */
748 HRESULT WINAPI CShellLink::Save(IStream *stm, BOOL fClearDirty)
749 {
750 TRACE("%p %p %x\n", this, stm, fClearDirty);
751
752 SHELL_LINK_HEADER ShlLnkHeader;
753 memset(&ShlLnkHeader, 0, sizeof(ShlLnkHeader));
754 ShlLnkHeader.dwSize = sizeof(ShlLnkHeader);
755 ShlLnkHeader.nShowCommand = iShowCmd;
756 ShlLnkHeader.clsid = CLSID_ShellLink;
757
758 ShlLnkHeader.wHotKey = wHotKey;
759 ShlLnkHeader.nIconIndex = iIcoNdx;
760 ShlLnkHeader.dwFlags = SLDF_UNICODE; /* strings are in unicode */
761 if (pPidl)
762 ShlLnkHeader.dwFlags |= SLDF_HAS_ID_LIST;
763 if (sPath)
764 ShlLnkHeader.dwFlags |= SLDF_HAS_LINK_INFO;
765 if (sDescription)
766 ShlLnkHeader.dwFlags |= SLDF_HAS_NAME;
767 if (sWorkDir)
768 ShlLnkHeader.dwFlags |= SLDF_HAS_WORKINGDIR;
769 if (sArgs)
770 ShlLnkHeader.dwFlags |= SLDF_HAS_ARGS;
771 if (sIcoPath)
772 ShlLnkHeader.dwFlags |= SLDF_HAS_ICONLOCATION;
773 #if (NTDDI_VERSION < NTDDI_LONGHORN)
774 if (sProduct)
775 ShlLnkHeader.dwFlags |= SLDF_HAS_LOGO3ID;
776 #endif
777 if (sComponent)
778 ShlLnkHeader.dwFlags |= SLDF_HAS_DARWINID;
779 if (bRunAs)
780 ShlLnkHeader.dwFlags |= SLDF_RUNAS_USER;
781
782 SystemTimeToFileTime (&time1, &ShlLnkHeader.ftCreationTime);
783 SystemTimeToFileTime (&time2, &ShlLnkHeader.ftLastAccessTime);
784 SystemTimeToFileTime (&time3, &ShlLnkHeader.ftLastWriteTime);
785
786 /* write the Shortcut header */
787 ULONG count;
788 HRESULT hr = stm->Write(&ShlLnkHeader, sizeof(ShlLnkHeader), &count);
789 if (FAILED(hr))
790 {
791 ERR("Write failed\n");
792 return hr;
793 }
794
795 TRACE("Writing pidl\n");
796
797 /* write the PIDL to the shortcut */
798 if (pPidl)
799 {
800 hr = ILSaveToStream(stm, pPidl);
801 if (FAILED(hr))
802 {
803 ERR("Failed to write PIDL\n");
804 return hr;
805 }
806 }
807
808 if (sPath)
809 Stream_WriteLocationInfo(stm, sPath, &volume);
810
811 if (sDescription)
812 hr = Stream_WriteString(stm, sDescription);
813
814 if (sPathRel)
815 hr = Stream_WriteString(stm, sPathRel);
816
817 if (sWorkDir)
818 hr = Stream_WriteString(stm, sWorkDir);
819
820 if (sArgs)
821 hr = Stream_WriteString(stm, sArgs);
822
823 if (sIcoPath)
824 hr = Stream_WriteString(stm, sIcoPath);
825
826 if (sProduct)
827 hr = Stream_WriteAdvertiseInfo(stm, sProduct, EXP_SZ_ICON_SIG);
828
829 if (sComponent)
830 hr = Stream_WriteAdvertiseInfo(stm, sComponent, EXP_DARWIN_ID_SIG);
831
832 /* the last field is a single zero dword */
833 DWORD zero = 0;
834 hr = stm->Write(&zero, sizeof zero, &count);
835
836 return S_OK;
837 }
838
839 /************************************************************************
840 * IPersistStream_GetSizeMax (IPersistStream)
841 */
842 HRESULT WINAPI CShellLink::GetSizeMax(ULARGE_INTEGER *pcbSize)
843 {
844 TRACE("(%p)\n", this);
845
846 return E_NOTIMPL;
847 }
848
849 static BOOL SHELL_ExistsFileW(LPCWSTR path)
850 {
851 if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(path))
852 return FALSE;
853
854 return TRUE;
855 }
856
857 /**************************************************************************
858 * ShellLink_UpdatePath
859 * update absolute path in sPath using relative path in sPathRel
860 */
861 static HRESULT ShellLink_UpdatePath(LPCWSTR sPathRel, LPCWSTR path, LPCWSTR sWorkDir, LPWSTR* psPath)
862 {
863 if (!path || !psPath)
864 return E_INVALIDARG;
865
866 if (!*psPath && sPathRel)
867 {
868 WCHAR buffer[2*MAX_PATH], abs_path[2*MAX_PATH];
869 LPWSTR final = NULL;
870
871 /* first try if [directory of link file] + [relative path] finds an existing file */
872
873 GetFullPathNameW(path, MAX_PATH * 2, buffer, &final);
874 if (!final)
875 final = buffer;
876 wcscpy(final, sPathRel);
877
878 *abs_path = '\0';
879
880 if (SHELL_ExistsFileW(buffer))
881 {
882 if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
883 wcscpy(abs_path, buffer);
884 }
885 else
886 {
887 /* try if [working directory] + [relative path] finds an existing file */
888 if (sWorkDir)
889 {
890 wcscpy(buffer, sWorkDir);
891 wcscpy(PathAddBackslashW(buffer), sPathRel);
892
893 if (SHELL_ExistsFileW(buffer))
894 if (!GetFullPathNameW(buffer, MAX_PATH, abs_path, &final))
895 wcscpy(abs_path, buffer);
896 }
897 }
898
899 /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
900 if (!*abs_path)
901 wcscpy(abs_path, sPathRel);
902
903 *psPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(abs_path) + 1) * sizeof(WCHAR));
904 if (!*psPath)
905 return E_OUTOFMEMORY;
906
907 wcscpy(*psPath, abs_path);
908 }
909
910 return S_OK;
911 }
912
913 HRESULT WINAPI CShellLink::GetPath(LPSTR pszFile, INT cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD fFlags)
914 {
915 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
916 this, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(sPath));
917
918 if (sComponent || sProduct)
919 return S_FALSE;
920
921 if (cchMaxPath)
922 pszFile[0] = 0;
923
924 if (sPath)
925 WideCharToMultiByte(CP_ACP, 0, sPath, -1,
926 pszFile, cchMaxPath, NULL, NULL);
927
928 if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
929
930 return S_OK;
931 }
932
933 HRESULT WINAPI CShellLink::GetIDList(LPITEMIDLIST * ppidl)
934 {
935 TRACE("(%p)->(ppidl=%p)\n", this, ppidl);
936
937 if (!pPidl)
938 {
939 *ppidl = NULL;
940 return S_FALSE;
941 }
942
943 *ppidl = ILClone(pPidl);
944 return S_OK;
945 }
946
947 HRESULT WINAPI CShellLink::SetIDList(LPCITEMIDLIST pidl)
948 {
949 TRACE("(%p)->(pidl=%p)\n", this, pidl);
950
951 if (pPidl)
952 ILFree(pPidl);
953
954 pPidl = ILClone(pidl);
955 if (!pPidl)
956 return E_FAIL;
957
958 bDirty = TRUE;
959
960 return S_OK;
961 }
962
963 HRESULT WINAPI CShellLink::GetDescription(LPSTR pszName, INT cchMaxName)
964 {
965 TRACE("(%p)->(%p len=%u)\n", this, pszName, cchMaxName);
966
967 if (cchMaxName)
968 pszName[0] = 0;
969
970 if (sDescription)
971 WideCharToMultiByte(CP_ACP, 0, sDescription, -1,
972 pszName, cchMaxName, NULL, NULL);
973
974 return S_OK;
975 }
976
977 HRESULT WINAPI CShellLink::SetDescription(LPCSTR pszName)
978 {
979 TRACE("(%p)->(pName=%s)\n", this, pszName);
980
981 HeapFree(GetProcessHeap(), 0, sDescription);
982 sDescription = NULL;
983
984 if (pszName)
985 {
986 sDescription = HEAP_strdupAtoW(GetProcessHeap(), 0, pszName);
987 if (!sDescription)
988 return E_OUTOFMEMORY;
989 }
990 bDirty = TRUE;
991
992 return S_OK;
993 }
994
995 HRESULT WINAPI CShellLink::GetWorkingDirectory(LPSTR pszDir, INT cchMaxPath)
996 {
997 TRACE("(%p)->(%p len=%u)\n", this, pszDir, cchMaxPath);
998
999 if (cchMaxPath)
1000 pszDir[0] = 0;
1001
1002 if (sWorkDir)
1003 WideCharToMultiByte(CP_ACP, 0, sWorkDir, -1,
1004 pszDir, cchMaxPath, NULL, NULL);
1005
1006 return S_OK;
1007 }
1008
1009 HRESULT WINAPI CShellLink::SetWorkingDirectory(LPCSTR pszDir)
1010 {
1011 TRACE("(%p)->(dir=%s)\n", this, pszDir);
1012
1013 HeapFree(GetProcessHeap(), 0, sWorkDir);
1014 sWorkDir = NULL;
1015
1016 if (pszDir)
1017 {
1018 sWorkDir = HEAP_strdupAtoW(GetProcessHeap(), 0, pszDir);
1019 if (!sWorkDir)
1020 return E_OUTOFMEMORY;
1021 }
1022 bDirty = TRUE;
1023
1024 return S_OK;
1025 }
1026
1027 HRESULT WINAPI CShellLink::GetArguments(LPSTR pszArgs, INT cchMaxPath)
1028 {
1029 TRACE("(%p)->(%p len=%u)\n", this, pszArgs, cchMaxPath);
1030
1031 if (cchMaxPath)
1032 pszArgs[0] = 0;
1033 if (sArgs)
1034 WideCharToMultiByte(CP_ACP, 0, sArgs, -1,
1035 pszArgs, cchMaxPath, NULL, NULL);
1036
1037 return S_OK;
1038 }
1039
1040 HRESULT WINAPI CShellLink::SetArguments(LPCSTR pszArgs)
1041 {
1042 TRACE("(%p)->(args=%s)\n", this, pszArgs);
1043
1044 HeapFree(GetProcessHeap(), 0, sArgs);
1045 sArgs = NULL;
1046
1047 if (pszArgs)
1048 {
1049 sArgs = HEAP_strdupAtoW(GetProcessHeap(), 0, pszArgs);
1050 if (!sArgs)
1051 return E_OUTOFMEMORY;
1052 }
1053
1054 bDirty = TRUE;
1055
1056 return S_OK;
1057 }
1058
1059 HRESULT WINAPI CShellLink::GetHotkey(WORD *pwHotkey)
1060 {
1061 TRACE("(%p)->(%p)(0x%08x)\n", this, pwHotkey, wHotKey);
1062
1063 *pwHotkey = wHotKey;
1064
1065 return S_OK;
1066 }
1067
1068 HRESULT WINAPI CShellLink::SetHotkey(WORD wHotkey)
1069 {
1070 TRACE("(%p)->(hotkey=%x)\n", this, wHotkey);
1071
1072 wHotKey = wHotkey;
1073 bDirty = TRUE;
1074
1075 return S_OK;
1076 }
1077
1078 HRESULT WINAPI CShellLink::GetShowCmd(INT *piShowCmd)
1079 {
1080 TRACE("(%p)->(%p)\n", this, piShowCmd);
1081 *piShowCmd = iShowCmd;
1082 return S_OK;
1083 }
1084
1085 HRESULT WINAPI CShellLink::SetShowCmd(INT iShowCmd)
1086 {
1087 TRACE("(%p) %d\n", this, iShowCmd);
1088
1089 this->iShowCmd = iShowCmd;
1090 bDirty = TRUE;
1091
1092 return S_OK;
1093 }
1094
1095 static HRESULT SHELL_PidlGeticonLocationA(IShellFolder* psf, LPCITEMIDLIST pidl,
1096 LPSTR pszIconPath, int cchIconPath, int* piIcon)
1097 {
1098 LPCITEMIDLIST pidlLast;
1099
1100 HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
1101
1102 if (SUCCEEDED(hr))
1103 {
1104 CComPtr<IExtractIconA> pei;
1105
1106 hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_IExtractIconA, NULL, (LPVOID*)&pei);
1107
1108 if (SUCCEEDED(hr))
1109 hr = pei->GetIconLocation(0, pszIconPath, MAX_PATH, piIcon, NULL);
1110
1111 psf->Release();
1112 }
1113
1114 return hr;
1115 }
1116
1117 HRESULT WINAPI CShellLink::GetIconLocation(LPSTR pszIconPath, INT cchIconPath, INT *piIcon)
1118 {
1119 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath, cchIconPath, piIcon);
1120
1121 pszIconPath[0] = 0;
1122 *piIcon = iIcoNdx;
1123
1124 if (sIcoPath)
1125 {
1126 WideCharToMultiByte(CP_ACP, 0, sIcoPath, -1, pszIconPath, cchIconPath, NULL, NULL);
1127 return S_OK;
1128 }
1129
1130 if (pPidl || sPath)
1131 {
1132 CComPtr<IShellFolder> pdsk;
1133
1134 HRESULT hr = SHGetDesktopFolder(&pdsk);
1135
1136 if (SUCCEEDED(hr))
1137 {
1138 /* first look for an icon using the PIDL (if present) */
1139 if (pPidl)
1140 hr = SHELL_PidlGeticonLocationA(pdsk, pPidl, pszIconPath, cchIconPath, piIcon);
1141 else
1142 hr = E_FAIL;
1143
1144 /* if we couldn't find an icon yet, look for it using the file system path */
1145 if (FAILED(hr) && sPath)
1146 {
1147 LPITEMIDLIST pidl;
1148
1149 hr = pdsk->ParseDisplayName(0, NULL, sPath, NULL, &pidl, NULL);
1150
1151 if (SUCCEEDED(hr))
1152 {
1153 hr = SHELL_PidlGeticonLocationA(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1154
1155 SHFree(pidl);
1156 }
1157 }
1158 }
1159
1160 return hr;
1161 }
1162 return S_OK;
1163 }
1164
1165 HRESULT WINAPI CShellLink::SetIconLocation(LPCSTR pszIconPath, INT iIcon)
1166 {
1167 TRACE("(%p)->(path=%s iicon=%u)\n", this, pszIconPath, iIcon);
1168
1169 HeapFree(GetProcessHeap(), 0, sIcoPath);
1170 sIcoPath = NULL;
1171
1172 if (pszIconPath)
1173 {
1174 sIcoPath = HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath);
1175 if (!sIcoPath)
1176 return E_OUTOFMEMORY;
1177 }
1178
1179 iIcoNdx = iIcon;
1180 bDirty = TRUE;
1181
1182 return S_OK;
1183 }
1184
1185 HRESULT WINAPI CShellLink::SetRelativePath(LPCSTR pszPathRel, DWORD dwReserved)
1186 {
1187 TRACE("(%p)->(path=%s %x)\n", this, pszPathRel, dwReserved);
1188
1189 HeapFree(GetProcessHeap(), 0, sPathRel);
1190 sPathRel = NULL;
1191
1192 if (pszPathRel)
1193 {
1194 sPathRel = HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel);
1195 bDirty = TRUE;
1196 }
1197
1198 return ShellLink_UpdatePath(sPathRel, sPath, sWorkDir, &sPath);
1199 }
1200
1201 HRESULT WINAPI CShellLink::Resolve(HWND hwnd, DWORD fFlags)
1202 {
1203 HRESULT hr = S_OK;
1204 BOOL bSuccess;
1205
1206 TRACE("(%p)->(hwnd=%p flags=%x)\n", this, hwnd, fFlags);
1207
1208 /*FIXME: use IResolveShellLink interface */
1209
1210 if (!sPath && pPidl)
1211 {
1212 WCHAR buffer[MAX_PATH];
1213
1214 bSuccess = SHGetPathFromIDListW(pPidl, buffer);
1215
1216 if (bSuccess && *buffer)
1217 {
1218 sPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(buffer) + 1) * sizeof(WCHAR));
1219
1220 if (!sPath)
1221 return E_OUTOFMEMORY;
1222
1223 wcscpy(sPath, buffer);
1224
1225 bDirty = TRUE;
1226 }
1227 else
1228 hr = S_OK; /* don't report an error occurred while just caching information */
1229 }
1230
1231 if (!sIcoPath && sPath)
1232 {
1233 sIcoPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(sPath) + 1) * sizeof(WCHAR));
1234
1235 if (!sIcoPath)
1236 return E_OUTOFMEMORY;
1237
1238 wcscpy(sIcoPath, sPath);
1239 iIcoNdx = 0;
1240
1241 bDirty = TRUE;
1242 }
1243
1244 return hr;
1245 }
1246
1247 HRESULT WINAPI CShellLink::SetPath(LPCSTR pszFile)
1248 {
1249 TRACE("(%p)->(path=%s)\n", this, pszFile);
1250 if (pszFile == NULL)
1251 return E_INVALIDARG;
1252
1253 LPWSTR str = HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile);
1254 if (!str)
1255 return E_OUTOFMEMORY;
1256
1257 HRESULT hr = SetPath(str);
1258 HeapFree(GetProcessHeap(), 0, str);
1259
1260 return hr;
1261 }
1262
1263 HRESULT WINAPI CShellLink::GetPath(LPWSTR pszFile, INT cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD fFlags)
1264 {
1265 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1266 this, pszFile, cchMaxPath, pfd, fFlags, debugstr_w(sPath));
1267
1268 if (sComponent || sProduct)
1269 return S_FALSE;
1270
1271 if (cchMaxPath)
1272 pszFile[0] = 0;
1273
1274 if (sPath)
1275 lstrcpynW(pszFile, sPath, cchMaxPath);
1276
1277 if (pfd) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
1278
1279 return S_OK;
1280 }
1281
1282 HRESULT WINAPI CShellLink::GetDescription(LPWSTR pszName, INT cchMaxName)
1283 {
1284 TRACE("(%p)->(%p len=%u)\n", this, pszName, cchMaxName);
1285
1286 pszName[0] = 0;
1287 if (sDescription)
1288 lstrcpynW(pszName, sDescription, cchMaxName);
1289
1290 return S_OK;
1291 }
1292
1293 HRESULT WINAPI CShellLink::SetDescription(LPCWSTR pszName)
1294 {
1295 TRACE("(%p)->(desc=%s)\n", this, debugstr_w(pszName));
1296
1297 HeapFree(GetProcessHeap(), 0, sDescription);
1298 sDescription = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
1299 (wcslen(pszName) + 1) * sizeof(WCHAR));
1300 if (!sDescription)
1301 return E_OUTOFMEMORY;
1302
1303 wcscpy(sDescription, pszName);
1304 bDirty = TRUE;
1305
1306 return S_OK;
1307 }
1308
1309 HRESULT WINAPI CShellLink::GetWorkingDirectory(LPWSTR pszDir, INT cchMaxPath)
1310 {
1311 TRACE("(%p)->(%p len %u)\n", this, pszDir, cchMaxPath);
1312
1313 if (cchMaxPath)
1314 pszDir[0] = 0;
1315 if (sWorkDir)
1316 lstrcpynW(pszDir, sWorkDir, cchMaxPath);
1317
1318 return S_OK;
1319 }
1320
1321 HRESULT WINAPI CShellLink::SetWorkingDirectory(LPCWSTR pszDir)
1322 {
1323 TRACE("(%p)->(dir=%s)\n", this, debugstr_w(pszDir));
1324
1325 HeapFree(GetProcessHeap(), 0, sWorkDir);
1326 sWorkDir = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
1327 (wcslen(pszDir) + 1) * sizeof (WCHAR));
1328 if (!sWorkDir)
1329 return E_OUTOFMEMORY;
1330 wcscpy(sWorkDir, pszDir);
1331 bDirty = TRUE;
1332
1333 return S_OK;
1334 }
1335
1336 HRESULT WINAPI CShellLink::GetArguments(LPWSTR pszArgs, INT cchMaxPath)
1337 {
1338 TRACE("(%p)->(%p len=%u)\n", this, pszArgs, cchMaxPath);
1339
1340 if (cchMaxPath)
1341 pszArgs[0] = 0;
1342 if (sArgs)
1343 lstrcpynW(pszArgs, sArgs, cchMaxPath);
1344
1345 return S_OK;
1346 }
1347
1348 HRESULT WINAPI CShellLink::SetArguments(LPCWSTR pszArgs)
1349 {
1350 TRACE("(%p)->(args=%s)\n", this, debugstr_w(pszArgs));
1351
1352 HeapFree(GetProcessHeap(), 0, sArgs);
1353 sArgs = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
1354 (wcslen(pszArgs) + 1) * sizeof (WCHAR));
1355 if (!sArgs)
1356 return E_OUTOFMEMORY;
1357
1358 wcscpy(sArgs, pszArgs);
1359 bDirty = TRUE;
1360
1361 return S_OK;
1362 }
1363
1364 static HRESULT SHELL_PidlGeticonLocationW(IShellFolder* psf, LPCITEMIDLIST pidl,
1365 LPWSTR pszIconPath, int cchIconPath, int* piIcon)
1366 {
1367 LPCITEMIDLIST pidlLast;
1368 UINT wFlags;
1369
1370 HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
1371
1372 if (SUCCEEDED(hr))
1373 {
1374 CComPtr<IExtractIconW> pei;
1375
1376 hr = psf->GetUIObjectOf(0, 1, &pidlLast, IID_IExtractIconW, NULL, (LPVOID*)&pei);
1377
1378 if (SUCCEEDED(hr))
1379 hr = pei->GetIconLocation(0, pszIconPath, MAX_PATH, piIcon, &wFlags);
1380
1381 psf->Release();
1382 }
1383
1384 return hr;
1385 }
1386
1387 HRESULT WINAPI CShellLink::GetIconLocation(LPWSTR pszIconPath, INT cchIconPath, INT *piIcon)
1388 {
1389 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath, cchIconPath, piIcon);
1390
1391 pszIconPath[0] = 0;
1392 *piIcon = iIcoNdx;
1393
1394 if (sIcoPath)
1395 {
1396 lstrcpynW(pszIconPath, sIcoPath, cchIconPath);
1397 return S_OK;
1398 }
1399
1400 if (pPidl || sPath)
1401 {
1402 CComPtr<IShellFolder> pdsk;
1403
1404 HRESULT hr = SHGetDesktopFolder(&pdsk);
1405
1406 if (SUCCEEDED(hr))
1407 {
1408 /* first look for an icon using the PIDL (if present) */
1409 if (pPidl)
1410 hr = SHELL_PidlGeticonLocationW(pdsk, pPidl, pszIconPath, cchIconPath, piIcon);
1411 else
1412 hr = E_FAIL;
1413
1414 /* if we couldn't find an icon yet, look for it using the file system path */
1415 if (FAILED(hr) && sPath)
1416 {
1417 LPITEMIDLIST pidl;
1418
1419 hr = pdsk->ParseDisplayName(0, NULL, sPath, NULL, &pidl, NULL);
1420
1421 if (SUCCEEDED(hr))
1422 {
1423 hr = SHELL_PidlGeticonLocationW(pdsk, pidl, pszIconPath, cchIconPath, piIcon);
1424
1425 SHFree(pidl);
1426 }
1427 }
1428 }
1429 return hr;
1430 }
1431 return S_OK;
1432 }
1433
1434 HRESULT WINAPI CShellLink::SetIconLocation(LPCWSTR pszIconPath, INT iIcon)
1435 {
1436 TRACE("(%p)->(path=%s iicon=%u)\n", this, debugstr_w(pszIconPath), iIcon);
1437
1438 HeapFree(GetProcessHeap(), 0, sIcoPath);
1439 sIcoPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
1440 (wcslen(pszIconPath) + 1) * sizeof (WCHAR));
1441 if (!sIcoPath)
1442 return E_OUTOFMEMORY;
1443 wcscpy(sIcoPath, pszIconPath);
1444
1445 iIcoNdx = iIcon;
1446 bDirty = TRUE;
1447
1448 return S_OK;
1449 }
1450
1451 HRESULT WINAPI CShellLink::SetRelativePath(LPCWSTR pszPathRel, DWORD dwReserved)
1452 {
1453 TRACE("(%p)->(path=%s %x)\n", this, debugstr_w(pszPathRel), dwReserved);
1454
1455 HeapFree(GetProcessHeap(), 0, sPathRel);
1456 sPathRel = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
1457 (wcslen(pszPathRel) + 1) * sizeof (WCHAR));
1458 if (!sPathRel)
1459 return E_OUTOFMEMORY;
1460 wcscpy(sPathRel, pszPathRel);
1461 bDirty = TRUE;
1462
1463 return ShellLink_UpdatePath(sPathRel, sPath, sWorkDir, &sPath);
1464 }
1465
1466 LPWSTR CShellLink::ShellLink_GetAdvertisedArg(LPCWSTR str)
1467 {
1468 if (!str)
1469 return NULL;
1470
1471 LPCWSTR p = wcschr(str, L':');
1472 if (!p)
1473 return NULL;
1474 DWORD len = p - str;
1475 LPWSTR ret = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (len + 1));
1476 if (!ret)
1477 return ret;
1478 memcpy(ret, str, sizeof(WCHAR)*len);
1479 ret[len] = 0;
1480 return ret;
1481 }
1482
1483 HRESULT CShellLink::ShellLink_SetAdvertiseInfo(LPCWSTR str)
1484 {
1485 LPCWSTR szComponent = NULL, szProduct = NULL;
1486
1487 while (str[0])
1488 {
1489 /* each segment must start with two colons */
1490 if (str[0] != ':' || str[1] != ':')
1491 return E_FAIL;
1492
1493 /* the last segment is just two colons */
1494 if (!str[2])
1495 break;
1496 str += 2;
1497
1498 /* there must be a colon straight after a guid */
1499 LPCWSTR p = wcschr(str, L':');
1500 if (!p)
1501 return E_FAIL;
1502 INT len = p - str;
1503 if (len != 38)
1504 return E_FAIL;
1505
1506 /* get the guid, and check it's validly formatted */
1507 WCHAR szGuid[39];
1508 memcpy(szGuid, str, sizeof(WCHAR)*len);
1509 szGuid[len] = 0;
1510
1511 GUID guid;
1512 HRESULT hr = CLSIDFromString(szGuid, &guid);
1513 if (hr != S_OK)
1514 return hr;
1515 str = p + 1;
1516
1517 /* match it up to a guid that we care about */
1518 if (IsEqualGUID(guid, SHELL32_AdvtShortcutComponent) && !szComponent)
1519 szComponent = str;
1520 else if (IsEqualGUID(guid, SHELL32_AdvtShortcutProduct) && !szProduct)
1521 szProduct = str;
1522 else
1523 return E_FAIL;
1524
1525 /* skip to the next field */
1526 str = wcschr(str, L':');
1527 if (!str)
1528 return E_FAIL;
1529 }
1530
1531 /* we have to have a component for an advertised shortcut */
1532 if (!szComponent)
1533 return E_FAIL;
1534
1535 sComponent = ShellLink_GetAdvertisedArg(szComponent);
1536 sProduct = ShellLink_GetAdvertisedArg(szProduct);
1537
1538 TRACE("Component = %s\n", debugstr_w(sComponent));
1539 TRACE("Product = %s\n", debugstr_w(sProduct));
1540
1541 return S_OK;
1542 }
1543
1544 static BOOL ShellLink_GetVolumeInfo(LPCWSTR path, CShellLink::volume_info *volume)
1545 {
1546 WCHAR drive[4] = { path[0], ':', '\\', 0 };
1547
1548 volume->type = GetDriveTypeW(drive);
1549 BOOL bRet = GetVolumeInformationW(drive, volume->label, _countof(volume->label), &volume->serial, NULL, NULL, NULL, 0);
1550 TRACE("ret = %d type %d serial %08x name %s\n", bRet,
1551 volume->type, volume->serial, debugstr_w(volume->label));
1552 return bRet;
1553 }
1554
1555 HRESULT WINAPI CShellLink::SetPath(LPCWSTR pszFile)
1556 {
1557 LPWSTR unquoted = NULL;
1558 HRESULT hr = S_OK;
1559
1560 TRACE("(%p)->(path=%s)\n", this, debugstr_w(pszFile));
1561
1562 if (!pszFile)
1563 return E_INVALIDARG;
1564
1565 /* quotes at the ends of the string are stripped */
1566 UINT len = wcslen(pszFile);
1567 if (pszFile[0] == '"' && pszFile[len-1] == '"')
1568 {
1569 unquoted = strdupW(pszFile);
1570 PathUnquoteSpacesW(unquoted);
1571 pszFile = unquoted;
1572 }
1573
1574 /* any other quote marks are invalid */
1575 if (wcschr(pszFile, '"'))
1576 {
1577 HeapFree(GetProcessHeap(), 0, unquoted);
1578 return S_FALSE;
1579 }
1580
1581 HeapFree(GetProcessHeap(), 0, sPath);
1582 sPath = NULL;
1583
1584 HeapFree(GetProcessHeap(), 0, sComponent);
1585 sComponent = NULL;
1586
1587 if (pPidl)
1588 ILFree(pPidl);
1589 pPidl = NULL;
1590
1591 if (S_OK != ShellLink_SetAdvertiseInfo(pszFile))
1592 {
1593 WCHAR buffer[MAX_PATH];
1594 LPWSTR fname;
1595
1596 if (*pszFile == '\0')
1597 *buffer = '\0';
1598 else if (!GetFullPathNameW(pszFile, MAX_PATH, buffer, &fname))
1599 return E_FAIL;
1600 else if(!PathFileExistsW(buffer) &&
1601 !SearchPathW(NULL, pszFile, NULL, MAX_PATH, buffer, NULL))
1602 hr = S_FALSE;
1603
1604 pPidl = SHSimpleIDListFromPathW(pszFile);
1605 ShellLink_GetVolumeInfo(buffer, &volume);
1606
1607 sPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0,
1608 (wcslen(buffer) + 1) * sizeof (WCHAR));
1609 if (!sPath)
1610 return E_OUTOFMEMORY;
1611
1612 wcscpy(sPath, buffer);
1613 }
1614
1615 bDirty = TRUE;
1616 HeapFree(GetProcessHeap(), 0, unquoted);
1617
1618 return hr;
1619 }
1620
1621 HRESULT WINAPI CShellLink::AddDataBlock(void* pDataBlock)
1622 {
1623 FIXME("\n");
1624 return E_NOTIMPL;
1625 }
1626
1627 HRESULT WINAPI CShellLink::CopyDataBlock(DWORD dwSig, void** ppDataBlock)
1628 {
1629 LPVOID block = NULL;
1630 HRESULT hr = E_FAIL;
1631
1632 TRACE("%p %08x %p\n", this, dwSig, ppDataBlock);
1633
1634 switch (dwSig)
1635 {
1636 case EXP_DARWIN_ID_SIG:
1637 if (!sComponent)
1638 break;
1639 block = shelllink_build_darwinid(sComponent, dwSig);
1640 hr = S_OK;
1641 break;
1642 case EXP_SZ_LINK_SIG:
1643 case NT_CONSOLE_PROPS_SIG:
1644 case NT_FE_CONSOLE_PROPS_SIG:
1645 case EXP_SPECIAL_FOLDER_SIG:
1646 case EXP_SZ_ICON_SIG:
1647 FIXME("valid but unhandled datablock %08x\n", dwSig);
1648 break;
1649 default:
1650 ERR("unknown datablock %08x\n", dwSig);
1651 }
1652 *ppDataBlock = block;
1653 return hr;
1654 }
1655
1656 HRESULT WINAPI CShellLink::RemoveDataBlock(DWORD dwSig)
1657 {
1658 FIXME("\n");
1659 return E_NOTIMPL;
1660 }
1661
1662 HRESULT WINAPI CShellLink::GetFlags(DWORD *pdwFlags)
1663 {
1664 DWORD flags = 0;
1665
1666 FIXME("%p %p\n", this, pdwFlags);
1667
1668 /* FIXME: add more */
1669 if (sArgs)
1670 flags |= SLDF_HAS_ARGS;
1671 if (sComponent)
1672 flags |= SLDF_HAS_DARWINID;
1673 if (sIcoPath)
1674 flags |= SLDF_HAS_ICONLOCATION;
1675 #if (NTDDI_VERSION < NTDDI_LONGHORN)
1676 if (sProduct)
1677 flags |= SLDF_HAS_LOGO3ID;
1678 #endif
1679 if (pPidl)
1680 flags |= SLDF_HAS_ID_LIST;
1681
1682 *pdwFlags = flags;
1683
1684 return S_OK;
1685 }
1686
1687 HRESULT WINAPI CShellLink::SetFlags(DWORD dwFlags)
1688 {
1689 FIXME("\n");
1690 return E_NOTIMPL;
1691 }
1692
1693 /**************************************************************************
1694 * CShellLink implementation of IShellExtInit::Initialize()
1695 *
1696 * Loads the shelllink from the dataobject the shell is pointing to.
1697 */
1698 HRESULT WINAPI CShellLink::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
1699 {
1700 TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID);
1701
1702 if (!pdtobj)
1703 return E_FAIL;
1704
1705 FORMATETC format;
1706 format.cfFormat = CF_HDROP;
1707 format.ptd = NULL;
1708 format.dwAspect = DVASPECT_CONTENT;
1709 format.lindex = -1;
1710 format.tymed = TYMED_HGLOBAL;
1711
1712 STGMEDIUM stgm;
1713 HRESULT hr = pdtobj->GetData(&format, &stgm);
1714 if (FAILED(hr))
1715 return hr;
1716
1717 UINT count = DragQueryFileW((HDROP)stgm.hGlobal, -1, NULL, 0);
1718 if (count == 1)
1719 {
1720 count = DragQueryFileW((HDROP)stgm.hGlobal, 0, NULL, 0);
1721 count++;
1722 LPWSTR path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR));
1723 if (path)
1724 {
1725 count = DragQueryFileW((HDROP)stgm.hGlobal, 0, path, count);
1726 hr = Load(path, 0);
1727 HeapFree(GetProcessHeap(), 0, path);
1728 }
1729 }
1730 ReleaseStgMedium(&stgm);
1731
1732 return S_OK;
1733 }
1734
1735 HRESULT WINAPI CShellLink::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
1736 {
1737 int id = 1;
1738
1739 TRACE("%p %p %u %u %u %u\n", this,
1740 hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
1741
1742 if (!hMenu)
1743 return E_INVALIDARG;
1744
1745 WCHAR wszOpen[20];
1746 if (!LoadStringW(shell32_hInstance, IDS_OPEN_VERB, wszOpen, _countof(wszOpen)))
1747 wszOpen[0] = L'\0';
1748
1749 MENUITEMINFOW mii;
1750 memset(&mii, 0, sizeof(mii));
1751 mii.cbSize = sizeof (mii);
1752 mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
1753 mii.dwTypeData = wszOpen;
1754 mii.cch = wcslen(mii.dwTypeData);
1755 mii.wID = idCmdFirst + id++;
1756 mii.fState = MFS_DEFAULT | MFS_ENABLED;
1757 mii.fType = MFT_STRING;
1758 if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
1759 return E_FAIL;
1760 iIdOpen = 1;
1761
1762 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id);
1763 }
1764
1765 static LPWSTR
1766 shelllink_get_msi_component_path(LPWSTR component)
1767 {
1768 DWORD Result, sz = 0;
1769
1770 Result = CommandLineFromMsiDescriptor(component, NULL, &sz);
1771 if (Result != ERROR_SUCCESS)
1772 return NULL;
1773
1774 sz++;
1775 LPWSTR path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sz * sizeof(WCHAR));
1776 Result = CommandLineFromMsiDescriptor(component, path, &sz);
1777 if (Result != ERROR_SUCCESS)
1778 {
1779 HeapFree(GetProcessHeap(), 0, path);
1780 path = NULL;
1781 }
1782
1783 TRACE("returning %s\n", debugstr_w(path));
1784
1785 return path;
1786 }
1787
1788 HRESULT WINAPI CShellLink::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
1789 {
1790 HWND hwnd = NULL; /* FIXME: get using interface set from IObjectWithSite */
1791 LPWSTR args = NULL;
1792 LPWSTR path = NULL;
1793
1794 TRACE("%p %p\n", this, lpici);
1795
1796 if (lpici->cbSize < sizeof (CMINVOKECOMMANDINFO))
1797 return E_INVALIDARG;
1798
1799 HRESULT hr = Resolve(hwnd, 0);
1800 if (FAILED(hr))
1801 {
1802 TRACE("failed to resolve component with error 0x%08x", hr);
1803 return hr;
1804 }
1805 if (sComponent)
1806 {
1807 path = shelllink_get_msi_component_path(sComponent);
1808 if (!path)
1809 return E_FAIL;
1810 }
1811 else
1812 path = strdupW(sPath);
1813
1814 if ( lpici->cbSize == sizeof(CMINVOKECOMMANDINFOEX) &&
1815 (lpici->fMask & CMIC_MASK_UNICODE) )
1816 {
1817 LPCMINVOKECOMMANDINFOEX iciex = (LPCMINVOKECOMMANDINFOEX)lpici;
1818 DWORD len = 2;
1819
1820 if (sArgs)
1821 len += wcslen(sArgs);
1822 if (iciex->lpParametersW)
1823 len += wcslen(iciex->lpParametersW);
1824
1825 args = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1826 args[0] = 0;
1827 if (sArgs)
1828 wcscat(args, sArgs);
1829 if (iciex->lpParametersW)
1830 {
1831 wcscat(args, L" ");
1832 wcscat(args, iciex->lpParametersW);
1833 }
1834 }
1835 else if (sArgs != NULL)
1836 {
1837 args = strdupW(sArgs);
1838 }
1839
1840 SHELLEXECUTEINFOW sei;
1841 memset(&sei, 0, sizeof sei);
1842 sei.cbSize = sizeof sei;
1843 sei.fMask = SEE_MASK_HASLINKNAME | SEE_MASK_UNICODE |
1844 (lpici->fMask & (SEE_MASK_NOASYNC | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI));
1845 sei.lpFile = path;
1846 sei.lpClass = sLinkPath;
1847 sei.nShow = iShowCmd;
1848 sei.lpDirectory = sWorkDir;
1849 sei.lpParameters = args;
1850 sei.lpVerb = L"open";
1851
1852 // HACK for ShellExecuteExW
1853 if (wcsstr(sPath, L".cpl"))
1854 sei.lpVerb = L"cplopen";
1855
1856 if (ShellExecuteExW(&sei))
1857 hr = S_OK;
1858 else
1859 hr = E_FAIL;
1860
1861 HeapFree(GetProcessHeap(), 0, args);
1862 HeapFree(GetProcessHeap(), 0, path);
1863
1864 return hr;
1865 }
1866
1867 HRESULT WINAPI CShellLink::GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax)
1868 {
1869 FIXME("%p %lu %u %p %p %u\n", this, idCmd, uType, pwReserved, pszName, cchMax);
1870
1871 return E_NOTIMPL;
1872 }
1873
1874 INT_PTR CALLBACK ExtendedShortcutProc(HWND hwndDlg, UINT uMsg,
1875 WPARAM wParam, LPARAM lParam)
1876 {
1877 switch(uMsg)
1878 {
1879 case WM_INITDIALOG:
1880 if (lParam)
1881 {
1882 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1883 SendMessage(hDlgCtrl, BM_SETCHECK, BST_CHECKED, 0);
1884 }
1885 return TRUE;
1886 case WM_COMMAND:
1887 {
1888 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1889 if (LOWORD(wParam) == IDOK)
1890 {
1891 if (SendMessage(hDlgCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED)
1892 EndDialog(hwndDlg, 1);
1893 else
1894 EndDialog(hwndDlg, 0);
1895 }
1896 else if (LOWORD(wParam) == IDCANCEL)
1897 {
1898 EndDialog(hwndDlg, -1);
1899 }
1900 else if (LOWORD(wParam) == 14000)
1901 {
1902 if (SendMessage(hDlgCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED)
1903 SendMessage(hDlgCtrl, BM_SETCHECK, BST_UNCHECKED, 0);
1904 else
1905 SendMessage(hDlgCtrl, BM_SETCHECK, BST_CHECKED, 0);
1906 }
1907 }
1908 }
1909 return FALSE;
1910 }
1911
1912 /**************************************************************************
1913 * SH_ShellLinkDlgProc
1914 *
1915 * dialog proc of the shortcut property dialog
1916 */
1917
1918 INT_PTR CALLBACK CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
1919 {
1920 CShellLink *pThis = (CShellLink *)GetWindowLongPtr(hwndDlg, DWLP_USER);
1921
1922 switch(uMsg)
1923 {
1924 case WM_INITDIALOG:
1925 {
1926 LPPROPSHEETPAGEW ppsp = (LPPROPSHEETPAGEW)lParam;
1927 if (ppsp == NULL)
1928 break;
1929
1930 TRACE("ShellLink_DlgProc (WM_INITDIALOG hwnd %p lParam %p ppsplParam %x)\n", hwndDlg, lParam, ppsp->lParam);
1931
1932 pThis = (CShellLink *)ppsp->lParam;
1933 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pThis);
1934
1935 TRACE("sArgs: %S sComponent: %S sDescription: %S sIcoPath: %S sPath: %S sPathRel: %S sProduct: %S sWorkDir: %S\n", pThis->sArgs, pThis->sComponent, pThis->sDescription,
1936 pThis->sIcoPath, pThis->sPath, pThis->sPathRel, pThis->sProduct, pThis->sWorkDir);
1937
1938 /* Get file information */
1939 SHFILEINFO fi;
1940 if (!SHGetFileInfoW(pThis->sLinkPath, 0, &fi, sizeof(fi), SHGFI_TYPENAME|SHGFI_ICON))
1941 {
1942 ERR("SHGetFileInfoW failed for %ls (%lu)\n", pThis->sLinkPath, GetLastError());
1943 fi.szTypeName[0] = L'\0';
1944 fi.hIcon = NULL;
1945 }
1946
1947 if (fi.hIcon) // TODO: destroy icon
1948 SendDlgItemMessageW(hwndDlg, 14000, STM_SETICON, (WPARAM)fi.hIcon, 0);
1949 else
1950 ERR("ExtractIconW failed %ls %u\n", pThis->sIcoPath, pThis->iIcoNdx);
1951
1952 /* target location */
1953 if (pThis->sWorkDir)
1954 SetDlgItemTextW(hwndDlg, 14007, PathFindFileName(pThis->sWorkDir));
1955
1956 /* target path */
1957 if (pThis->sPath)
1958 SetDlgItemTextW(hwndDlg, 14009, pThis->sPath);
1959
1960 /* working dir */
1961 if (pThis->sWorkDir)
1962 SetDlgItemTextW(hwndDlg, 14011, pThis->sWorkDir);
1963
1964 /* description */
1965 if (pThis->sDescription)
1966 SetDlgItemTextW(hwndDlg, 14019, pThis->sDescription);
1967
1968 return TRUE;
1969 }
1970 case WM_NOTIFY:
1971 {
1972 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam;
1973 if (lppsn->hdr.code == PSN_APPLY)
1974 {
1975 WCHAR wszBuf[MAX_PATH];
1976
1977 /* set working directory */
1978 GetDlgItemTextW(hwndDlg, 14011, wszBuf, MAX_PATH);
1979 pThis->SetWorkingDirectory(wszBuf);
1980 /* set link destination */
1981 GetDlgItemTextW(hwndDlg, 14009, wszBuf, MAX_PATH);
1982 if (!PathFileExistsW(wszBuf))
1983 {
1984 //FIXME load localized error msg
1985 MessageBoxW(hwndDlg, L"file not existing", wszBuf, MB_OK);
1986 SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
1987 return TRUE;
1988 }
1989
1990 WCHAR *pwszExt = PathFindExtensionW(wszBuf);
1991 if (!wcsicmp(pwszExt, L".lnk"))
1992 {
1993 // FIXME load localized error msg
1994 MessageBoxW(hwndDlg, L"You cannot create a link to a shortcut", L"Error", MB_ICONERROR);
1995 SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
1996 return TRUE;
1997 }
1998
1999 pThis->SetPath(wszBuf);
2000
2001 TRACE("This %p sLinkPath %S\n", pThis, pThis->sLinkPath);
2002 pThis->Save(pThis->sLinkPath, TRUE);
2003 SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR);
2004 return TRUE;
2005 }
2006 break;
2007 }
2008 case WM_COMMAND:
2009 switch(LOWORD(wParam))
2010 {
2011 case 14020:
2012 ///
2013 /// FIXME
2014 /// open target directory
2015 ///
2016 return TRUE;
2017 case 14021:
2018 {
2019 WCHAR wszPath[MAX_PATH] = L"";
2020
2021 if (pThis->sIcoPath)
2022 wcscpy(wszPath, pThis->sIcoPath);
2023 INT IconIndex = pThis->iIcoNdx;
2024 if (PickIconDlg(hwndDlg, wszPath, MAX_PATH, &IconIndex))
2025 {
2026 pThis->SetIconLocation(wszPath, IconIndex);
2027 ///
2028 /// FIXME redraw icon
2029 }
2030 return TRUE;
2031 }
2032
2033 case 14022:
2034 {
2035 INT_PTR result = DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_SHORTCUT_EXTENDED_PROPERTIES), hwndDlg, ExtendedShortcutProc, (LPARAM)pThis->bRunAs);
2036 if (result == 1 || result == 0)
2037 {
2038 if (pThis->bRunAs != result)
2039 {
2040 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
2041 }
2042
2043 pThis->bRunAs = result;
2044 }
2045 return TRUE;
2046 }
2047 }
2048 if(HIWORD(wParam) == EN_CHANGE)
2049 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
2050 break;
2051 default:
2052 break;
2053 }
2054 return FALSE;
2055 }
2056
2057 /**************************************************************************
2058 * ShellLink_IShellPropSheetExt interface
2059 */
2060
2061 HRESULT WINAPI CShellLink::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
2062 {
2063 HPROPSHEETPAGE hPage = SH_CreatePropertySheetPage(IDD_SHORTCUT_PROPERTIES, SH_ShellLinkDlgProc, (LPARAM)this, NULL);
2064 if (hPage == NULL)
2065 {
2066 ERR("failed to create property sheet page\n");
2067 return E_FAIL;
2068 }
2069
2070 if (!pfnAddPage(hPage, lParam))
2071 return E_FAIL;
2072
2073 return S_OK;
2074 }
2075
2076 HRESULT WINAPI CShellLink::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam)
2077 {
2078 TRACE("(%p) (uPageID %u, pfnReplacePage %p lParam %p\n", this, uPageID, pfnReplacePage, lParam);
2079 return E_NOTIMPL;
2080 }
2081
2082 HRESULT WINAPI CShellLink::SetSite(IUnknown *punk)
2083 {
2084 TRACE("%p %p\n", this, punk);
2085
2086 site = punk;
2087
2088 return S_OK;
2089 }
2090
2091 HRESULT WINAPI CShellLink::GetSite(REFIID iid, void ** ppvSite)
2092 {
2093 TRACE("%p %s %p\n", this, debugstr_guid(&iid), ppvSite);
2094
2095 if (site == NULL)
2096 return E_FAIL;
2097
2098 return site->QueryInterface(iid, ppvSite);
2099 }
2100
2101 /**************************************************************************
2102 * IShellLink_ConstructFromFile
2103 */
2104 HRESULT WINAPI IShellLink_ConstructFromFile(IUnknown *pUnkOuter, REFIID riid, LPCITEMIDLIST pidl, LPVOID *ppv)
2105 {
2106 CComPtr<IUnknown> psl;
2107
2108 HRESULT hr = CShellLink::_CreatorClass::CreateInstance(NULL, riid, (void**)&psl);
2109
2110 if (SUCCEEDED(hr))
2111 {
2112 CComPtr<IPersistFile> ppf;
2113
2114 *ppv = NULL;
2115
2116 hr = psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
2117
2118 if (SUCCEEDED(hr))
2119 {
2120 WCHAR path[MAX_PATH];
2121
2122 if (SHGetPathFromIDListW(pidl, path))
2123 hr = ppf->Load(path, 0);
2124 else
2125 hr = E_FAIL;
2126
2127 if (SUCCEEDED(hr))
2128 *ppv = psl.Detach();
2129 }
2130 }
2131
2132 return hr;
2133 }