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