3 * Copyright 1997 Marcus Meissner
4 * Copyright 1998 Juergen Schmied
5 * Copyright 2005 Mike McCormack
6 * Copyright 2009 Andrew Hill
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.
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.
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
23 * Nearly complete information about the binary formats
24 * of .lnk files available at http://www.wotsit.org
26 * You can use winedump to examine the contents of a link file:
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.
38 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
40 #define SHLINK_LOCAL 0
41 #define SHLINK_REMOTE 1
42 #define MAX_PROPERTY_SHEET_PAGE 32
44 /* link file formats */
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 */
72 DWORD dwNetworkVolTableOfs
;
76 struct LOCAL_VOLUME_INFO
88 WCHAR label
[12]; /* assume 8.3 */
93 /* IShellLink Implementation */
95 static HRESULT
ShellLink_UpdatePath(LPCWSTR sPathRel
, LPCWSTR path
, LPCWSTR sWorkDir
, LPWSTR
* psPath
);
97 /* strdup on the process heap */
98 static LPWSTR __inline
HEAP_strdupAtoW(HANDLE heap
, DWORD flags
, LPCSTR str
)
105 len
= MultiByteToWideChar(CP_ACP
, 0, str
, -1, NULL
, 0);
106 p
= (LPWSTR
)HeapAlloc(heap
, flags
, len
* sizeof(WCHAR
));
109 MultiByteToWideChar(CP_ACP
, 0, str
, -1, p
, len
);
113 static LPWSTR __inline
strdupW(LPCWSTR src
)
116 if (!src
) return NULL
;
117 dest
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(src
) + 1) * sizeof(WCHAR
));
123 CShellLink::CShellLink()
127 memset(&time1
, 0, sizeof(time1
));
128 memset(&time2
, 0, sizeof(time2
));
129 memset(&time3
, 0, sizeof(time3
));
130 iShowCmd
= SW_SHOWNORMAL
;
140 memset(&volume
, 0, sizeof(volume
));
147 CShellLink::~CShellLink()
149 TRACE("-- destroying IShellLink(%p)\n", this);
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
);
162 HRESULT WINAPI
CShellLink::GetClassID(CLSID
*pclsid
)
164 TRACE("%p %p\n", this, pclsid
);
168 *pclsid
= CLSID_ShellLink
;
172 HRESULT WINAPI
CShellLink::IsDirty()
174 TRACE("(%p)\n", this);
182 HRESULT WINAPI
CShellLink::Load(LPCOLESTR pszFileName
, DWORD dwMode
)
184 TRACE("(%p, %s, %x)\n", this, debugstr_w(pszFileName
), dwMode
);
187 dwMode
= STGM_READ
| STGM_SHARE_DENY_WRITE
;
189 CComPtr
<IStream
> stm
;
190 HRESULT hr
= SHCreateStreamOnFileW(pszFileName
, dwMode
, &stm
);
193 HeapFree(GetProcessHeap(), 0, sLinkPath
);
194 sLinkPath
= strdupW(pszFileName
);
196 ShellLink_UpdatePath(sPathRel
, pszFileName
, sWorkDir
, &sPath
);
199 TRACE("-- returning hr %08x\n", hr
);
203 HRESULT WINAPI
CShellLink::Save(LPCOLESTR pszFileName
, BOOL fRemember
)
205 TRACE("(%p)->(%s)\n", this, debugstr_w(pszFileName
));
210 CComPtr
<IStream
> stm
;
211 HRESULT hr
= SHCreateStreamOnFileW(pszFileName
, STGM_READWRITE
| STGM_CREATE
| STGM_SHARE_EXCLUSIVE
, &stm
);
214 hr
= Save(stm
, FALSE
);
219 HeapFree(GetProcessHeap(), 0, sLinkPath
);
221 sLinkPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(pszFileName
) + 1) * sizeof(WCHAR
));
223 wcscpy(sLinkPath
, pszFileName
);
229 DeleteFileW(pszFileName
);
230 WARN("Failed to create shortcut %s\n", debugstr_w(pszFileName
));
237 HRESULT WINAPI
CShellLink::SaveCompleted(LPCOLESTR pszFileName
)
239 FIXME("(%p)->(%s)\n", this, debugstr_w(pszFileName
));
243 HRESULT WINAPI
CShellLink::GetCurFile(LPOLESTR
*ppszFileName
)
245 *ppszFileName
= NULL
;
249 /* IPersistFile::GetCurFile called before IPersistFile::Save */
253 *ppszFileName
= (LPOLESTR
)CoTaskMemAlloc((wcslen(sLinkPath
) + 1) * sizeof(WCHAR
));
257 return E_OUTOFMEMORY
;
260 /* copy last saved filename */
261 wcscpy(*ppszFileName
, sLinkPath
);
266 /************************************************************************
267 * IPersistStream_IsDirty (IPersistStream)
270 static HRESULT
Stream_LoadString(IStream
* stm
, BOOL unicode
, LPWSTR
*pstr
)
276 HRESULT hr
= stm
->Read(&len
, sizeof(len
), &count
);
277 if (FAILED(hr
) || count
!= sizeof(len
))
281 len
*= sizeof (WCHAR
);
283 TRACE("reading %d\n", len
);
284 LPSTR temp
= (LPSTR
)HeapAlloc(GetProcessHeap(), 0, len
+ sizeof(WCHAR
));
286 return E_OUTOFMEMORY
;
288 hr
= stm
->Read(temp
, len
, &count
);
289 if(FAILED(hr
) || count
!= len
)
291 HeapFree(GetProcessHeap(), 0, temp
);
295 TRACE("read %s\n", debugstr_an(temp
, len
));
297 /* convert to unicode if necessary */
301 count
= MultiByteToWideChar(CP_ACP
, 0, temp
, len
, NULL
, 0);
302 str
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (count
+ 1) * sizeof (WCHAR
));
305 HeapFree(GetProcessHeap(), 0, temp
);
306 return E_OUTOFMEMORY
;
308 MultiByteToWideChar(CP_ACP
, 0, temp
, len
, str
, count
);
309 HeapFree(GetProcessHeap(), 0, temp
);
323 static HRESULT
Stream_ReadChunk(IStream
* stm
, LPVOID
*data
)
328 unsigned char data
[1];
335 HRESULT hr
= stm
->Read(&size
, sizeof(size
), &count
);
336 if (FAILED(hr
) || count
!= sizeof(size
))
339 chunk
= (sized_chunk
*)HeapAlloc(GetProcessHeap(), 0, size
);
341 return E_OUTOFMEMORY
;
344 hr
= stm
->Read(chunk
->data
, size
- sizeof(size
), &count
);
345 if (FAILED(hr
) || count
!= (size
- sizeof(size
)))
347 HeapFree(GetProcessHeap(), 0, chunk
);
351 TRACE("Read %d bytes\n", chunk
->size
);
358 static BOOL
Stream_LoadVolume(LOCAL_VOLUME_INFO
*vol
, CShellLink::volume_info
*volume
)
360 volume
->serial
= vol
->dwVolSerial
;
361 volume
->type
= vol
->dwType
;
363 if (!vol
->dwVolLabelOfs
)
365 if (vol
->dwSize
<= vol
->dwVolLabelOfs
)
367 INT len
= vol
->dwSize
- vol
->dwVolLabelOfs
;
369 LPSTR label
= (LPSTR
)vol
;
370 label
+= vol
->dwVolLabelOfs
;
371 MultiByteToWideChar(CP_ACP
, 0, label
, len
, volume
->label
, _countof(volume
->label
));
376 static LPWSTR
Stream_LoadPath(LPCSTR p
, DWORD maxlen
)
380 while (p
[len
] && len
< maxlen
)
383 UINT wlen
= MultiByteToWideChar(CP_ACP
, 0, p
, len
, NULL
, 0);
384 LPWSTR path
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wlen
+ 1) * sizeof(WCHAR
));
387 MultiByteToWideChar(CP_ACP
, 0, p
, len
, path
, wlen
);
393 static HRESULT
Stream_LoadLocation(IStream
*stm
,
394 CShellLink::volume_info
*volume
, LPWSTR
*path
)
397 HRESULT hr
= Stream_ReadChunk(stm
, (LPVOID
*) &p
);
401 LOCATION_INFO
*loc
= (LOCATION_INFO
*) p
;
402 if (loc
->dwTotalSize
< sizeof(LOCATION_INFO
))
404 HeapFree(GetProcessHeap(), 0, p
);
408 /* if there's valid local volume information, load it */
409 if (loc
->dwVolTableOfs
&&
410 ((loc
->dwVolTableOfs
+ sizeof(LOCAL_VOLUME_INFO
)) <= loc
->dwTotalSize
))
412 LOCAL_VOLUME_INFO
*volume_info
;
414 volume_info
= (LOCAL_VOLUME_INFO
*) &p
[loc
->dwVolTableOfs
];
415 Stream_LoadVolume(volume_info
, volume
);
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
);
423 TRACE("type %d serial %08x name %s path %s\n", volume
->type
,
424 volume
->serial
, debugstr_w(volume
->label
), debugstr_w(*path
));
426 HeapFree(GetProcessHeap(), 0, p
);
431 * The format of the advertised shortcut info seems to be:
436 * 0 Length of the block (4 bytes, usually 0x314)
438 * 8 string data in ASCII
439 * 8+0x104 string data in UNICODE
441 * In the original Win32 implementation the buffers are not initialized
442 * to zero, so data trailing the string is random garbage.
444 static HRESULT
Stream_LoadAdvertiseInfo(IStream
* stm
, LPWSTR
*str
)
449 EXP_DARWIN_LINK buffer
;
450 HRESULT hr
= stm
->Read(&buffer
.dbh
.cbSize
, sizeof (DWORD
), &count
);
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
)
458 ERR("Ooops. This structure is not as expected...\n");
462 hr
= stm
->Read(&buffer
.dbh
.dwSignature
, size
, &count
);
469 TRACE("magic %08x string = %s\n", buffer
.dbh
.dwSignature
, debugstr_w(buffer
.szwDarwinID
));
471 if ((buffer
.dbh
.dwSignature
& 0xffff0000) != 0xa0000000)
473 ERR("Unknown magic number %08x in advertised shortcut\n", buffer
.dbh
.dwSignature
);
477 *str
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
478 (wcslen(buffer
.szwDarwinID
) + 1) * sizeof(WCHAR
));
479 wcscpy(*str
, buffer
.szwDarwinID
);
484 /************************************************************************
485 * IPersistStream_Load (IPersistStream)
487 HRESULT WINAPI
CShellLink::Load(IStream
*stm
)
489 TRACE("%p %p\n", this, stm
);
492 return STG_E_INVALIDPOINTER
;
495 ULONG dwBytesRead
= 0;
496 HRESULT hr
= stm
->Read(&hdr
, sizeof(hdr
), &dwBytesRead
);
500 if (dwBytesRead
!= sizeof(hdr
))
502 if (hdr
.dwSize
!= sizeof(hdr
))
504 if (!IsEqualIID(hdr
.MagicGuid
, CLSID_ShellLink
))
507 /* free all the old stuff */
510 memset(&volume
, 0, sizeof volume
);
511 HeapFree(GetProcessHeap(), 0, sPath
);
513 HeapFree(GetProcessHeap(), 0, sDescription
);
515 HeapFree(GetProcessHeap(), 0, sPathRel
);
517 HeapFree(GetProcessHeap(), 0, sWorkDir
);
519 HeapFree(GetProcessHeap(), 0, sArgs
);
521 HeapFree(GetProcessHeap(), 0, sIcoPath
);
523 HeapFree(GetProcessHeap(), 0, sProduct
);
525 HeapFree(GetProcessHeap(), 0, sComponent
);
528 BOOL unicode
= FALSE
;
529 iShowCmd
= hdr
.fStartup
;
530 wHotKey
= (WORD
)hdr
.wHotKey
;
532 FileTimeToSystemTime (&hdr
.Time1
, &time1
);
533 FileTimeToSystemTime (&hdr
.Time2
, &time2
);
534 FileTimeToSystemTime (&hdr
.Time3
, &time3
);
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
));
549 /* load all the new stuff */
550 if (hdr
.dwFlags
& SLDF_HAS_ID_LIST
)
552 hr
= ILLoadFromStream(stm
, &pPidl
);
558 /* load the location information */
559 if (hdr
.dwFlags
& SLDF_HAS_LINK_INFO
)
560 hr
= Stream_LoadLocation(stm
, &volume
, &sPath
);
564 if (hdr
.dwFlags
& SLDF_UNICODE
)
567 if (hdr
.dwFlags
& SLDF_HAS_NAME
)
569 hr
= Stream_LoadString(stm
, unicode
, &sDescription
);
570 TRACE("Description -> %s\n", debugstr_w(sDescription
));
575 if (hdr
.dwFlags
& SLDF_HAS_RELPATH
)
577 hr
= Stream_LoadString(stm
, unicode
, &sPathRel
);
578 TRACE("Relative Path-> %s\n", debugstr_w(sPathRel
));
583 if (hdr
.dwFlags
& SLDF_HAS_WORKINGDIR
)
585 hr
= Stream_LoadString(stm
, unicode
, &sWorkDir
);
586 PathRemoveBackslash(sWorkDir
);
587 TRACE("Working Dir -> %s\n", debugstr_w(sWorkDir
));
592 if (hdr
.dwFlags
& SLDF_HAS_ARGS
)
594 hr
= Stream_LoadString(stm
, unicode
, &sArgs
);
595 TRACE("Arguments -> %s\n", debugstr_w(sArgs
));
600 if (hdr
.dwFlags
& SLDF_HAS_ICONLOCATION
)
602 hr
= Stream_LoadString(stm
, unicode
, &sIcoPath
);
603 TRACE("Icon file -> %s\n", debugstr_w(sIcoPath
));
608 #if (NTDDI_VERSION < NTDDI_LONGHORN)
609 if (hdr
.dwFlags
& SLDF_HAS_LOGO3ID
)
611 hr
= Stream_LoadAdvertiseInfo(stm
, &sProduct
);
612 TRACE("Product -> %s\n", debugstr_w(sProduct
));
618 if (hdr
.dwFlags
& SLDF_HAS_DARWINID
)
620 hr
= Stream_LoadAdvertiseInfo(stm
, &sComponent
);
621 TRACE("Component -> %s\n", debugstr_w(sComponent
));
623 if (hdr
.dwFlags
& SLDF_RUNAS_USER
)
636 hr
= stm
->Read(&dwZero
, sizeof(dwZero
), &dwBytesRead
);
637 if (FAILED(hr
) || dwZero
|| dwBytesRead
!= sizeof(dwZero
))
638 ERR("Last word was not zero\n");
650 /************************************************************************
653 * Helper function for IPersistStream_Save. Writes a unicode string
654 * with terminating nul byte to a stream, preceded by the its length.
656 static HRESULT
Stream_WriteString(IStream
* stm
, LPCWSTR str
)
658 USHORT len
= wcslen(str
) + 1;
661 HRESULT hr
= stm
->Write(&len
, sizeof(len
), &count
);
665 len
*= sizeof(WCHAR
);
667 hr
= stm
->Write(str
, len
, &count
);
674 /************************************************************************
675 * Stream_WriteLocationInfo
677 * Writes the location info to a stream
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.
683 static HRESULT
Stream_WriteLocationInfo(IStream
* stm
, LPCWSTR path
,
684 CShellLink::volume_info
*volume
)
686 LOCAL_VOLUME_INFO
*vol
;
689 TRACE("%p %s %p\n", stm
, debugstr_w(path
), volume
);
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
;
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
];
707 /* fill in the location information header */
708 loc
->dwTotalSize
= total_size
;
709 loc
->dwHeaderSize
= sizeof (*loc
);
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
;
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
);
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
);
730 HRESULT hr
= stm
->Write(loc
, total_size
, &count
);
731 HeapFree(GetProcessHeap(), 0, loc
);
736 static EXP_DARWIN_LINK
* shelllink_build_darwinid(LPCWSTR string
, DWORD magic
)
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
);
747 static HRESULT
Stream_WriteAdvertiseInfo(IStream
* stm
, LPCWSTR string
, DWORD magic
)
751 EXP_DARWIN_LINK
*buffer
= shelllink_build_darwinid(string
, magic
);
754 return stm
->Write(buffer
, buffer
->dbh
.cbSize
, &count
);
757 /************************************************************************
758 * IPersistStream_Save (IPersistStream)
760 * FIXME: makes assumptions about byte order
762 HRESULT WINAPI
CShellLink::Save(IStream
*stm
, BOOL fClearDirty
)
764 TRACE("%p %p %x\n", this, stm
, fClearDirty
);
767 memset(&header
, 0, sizeof(header
));
768 header
.dwSize
= sizeof(header
);
769 header
.fStartup
= iShowCmd
;
770 header
.MagicGuid
= CLSID_ShellLink
;
772 header
.wHotKey
= wHotKey
;
773 header
.nIcon
= iIcoNdx
;
774 header
.dwFlags
= SLDF_UNICODE
; /* strings are in unicode */
776 header
.dwFlags
|= SLDF_HAS_ID_LIST
;
778 header
.dwFlags
|= SLDF_HAS_LINK_INFO
;
780 header
.dwFlags
|= SLDF_HAS_NAME
;
782 header
.dwFlags
|= SLDF_HAS_WORKINGDIR
;
784 header
.dwFlags
|= SLDF_HAS_ARGS
;
786 header
.dwFlags
|= SLDF_HAS_ICONLOCATION
;
787 #if (NTDDI_VERSION < NTDDI_LONGHORN)
789 header
.dwFlags
|= SLDF_HAS_LOGO3ID
;
792 header
.dwFlags
|= SLDF_HAS_DARWINID
;
794 header
.dwFlags
|= SLDF_RUNAS_USER
;
796 SystemTimeToFileTime (&time1
, &header
.Time1
);
797 SystemTimeToFileTime (&time2
, &header
.Time2
);
798 SystemTimeToFileTime (&time3
, &header
.Time3
);
800 /* write the Shortcut header */
802 HRESULT hr
= stm
->Write(&header
, sizeof(header
), &count
);
805 ERR("Write failed\n");
809 TRACE("Writing pidl\n");
811 /* write the PIDL to the shortcut */
814 hr
= ILSaveToStream(stm
, pPidl
);
817 ERR("Failed to write PIDL\n");
823 Stream_WriteLocationInfo(stm
, sPath
, &volume
);
826 hr
= Stream_WriteString(stm
, sDescription
);
829 hr
= Stream_WriteString(stm
, sPathRel
);
832 hr
= Stream_WriteString(stm
, sWorkDir
);
835 hr
= Stream_WriteString(stm
, sArgs
);
838 hr
= Stream_WriteString(stm
, sIcoPath
);
841 hr
= Stream_WriteAdvertiseInfo(stm
, sProduct
, EXP_SZ_ICON_SIG
);
844 hr
= Stream_WriteAdvertiseInfo(stm
, sComponent
, EXP_DARWIN_ID_SIG
);
846 /* the last field is a single zero dword */
848 hr
= stm
->Write(&zero
, sizeof zero
, &count
);
853 /************************************************************************
854 * IPersistStream_GetSizeMax (IPersistStream)
856 HRESULT WINAPI
CShellLink::GetSizeMax(ULARGE_INTEGER
*pcbSize
)
858 TRACE("(%p)\n", this);
863 static BOOL
SHELL_ExistsFileW(LPCWSTR path
)
865 if (INVALID_FILE_ATTRIBUTES
== GetFileAttributesW(path
))
871 /**************************************************************************
872 * ShellLink_UpdatePath
873 * update absolute path in sPath using relative path in sPathRel
875 static HRESULT
ShellLink_UpdatePath(LPCWSTR sPathRel
, LPCWSTR path
, LPCWSTR sWorkDir
, LPWSTR
* psPath
)
877 if (!path
|| !psPath
)
880 if (!*psPath
&& sPathRel
)
882 WCHAR buffer
[2*MAX_PATH
], abs_path
[2*MAX_PATH
];
885 /* first try if [directory of link file] + [relative path] finds an existing file */
887 GetFullPathNameW(path
, MAX_PATH
* 2, buffer
, &final
);
890 wcscpy(final
, sPathRel
);
894 if (SHELL_ExistsFileW(buffer
))
896 if (!GetFullPathNameW(buffer
, MAX_PATH
, abs_path
, &final
))
897 wcscpy(abs_path
, buffer
);
901 /* try if [working directory] + [relative path] finds an existing file */
904 wcscpy(buffer
, sWorkDir
);
905 wcscpy(PathAddBackslashW(buffer
), sPathRel
);
907 if (SHELL_ExistsFileW(buffer
))
908 if (!GetFullPathNameW(buffer
, MAX_PATH
, abs_path
, &final
))
909 wcscpy(abs_path
, buffer
);
913 /* FIXME: This is even not enough - not all shell links can be resolved using this algorithm. */
915 wcscpy(abs_path
, sPathRel
);
917 *psPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(abs_path
) + 1) * sizeof(WCHAR
));
919 return E_OUTOFMEMORY
;
921 wcscpy(*psPath
, abs_path
);
927 HRESULT WINAPI
CShellLink::GetPath(LPSTR pszFile
, INT cchMaxPath
, WIN32_FIND_DATAA
*pfd
, DWORD fFlags
)
929 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
930 this, pszFile
, cchMaxPath
, pfd
, fFlags
, debugstr_w(sPath
));
932 if (sComponent
|| sProduct
)
939 WideCharToMultiByte(CP_ACP
, 0, sPath
, -1,
940 pszFile
, cchMaxPath
, NULL
, NULL
);
942 if (pfd
) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
947 HRESULT WINAPI
CShellLink::GetIDList(LPITEMIDLIST
* ppidl
)
949 TRACE("(%p)->(ppidl=%p)\n", this, ppidl
);
957 *ppidl
= ILClone(pPidl
);
961 HRESULT WINAPI
CShellLink::SetIDList(LPCITEMIDLIST pidl
)
963 TRACE("(%p)->(pidl=%p)\n", this, pidl
);
968 pPidl
= ILClone(pidl
);
977 HRESULT WINAPI
CShellLink::GetDescription(LPSTR pszName
, INT cchMaxName
)
979 TRACE("(%p)->(%p len=%u)\n", this, pszName
, cchMaxName
);
985 WideCharToMultiByte(CP_ACP
, 0, sDescription
, -1,
986 pszName
, cchMaxName
, NULL
, NULL
);
991 HRESULT WINAPI
CShellLink::SetDescription(LPCSTR pszName
)
993 TRACE("(%p)->(pName=%s)\n", this, pszName
);
995 HeapFree(GetProcessHeap(), 0, sDescription
);
1000 sDescription
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszName
);
1002 return E_OUTOFMEMORY
;
1009 HRESULT WINAPI
CShellLink::GetWorkingDirectory(LPSTR pszDir
, INT cchMaxPath
)
1011 TRACE("(%p)->(%p len=%u)\n", this, pszDir
, cchMaxPath
);
1017 WideCharToMultiByte(CP_ACP
, 0, sWorkDir
, -1,
1018 pszDir
, cchMaxPath
, NULL
, NULL
);
1023 HRESULT WINAPI
CShellLink::SetWorkingDirectory(LPCSTR pszDir
)
1025 TRACE("(%p)->(dir=%s)\n", this, pszDir
);
1027 HeapFree(GetProcessHeap(), 0, sWorkDir
);
1032 sWorkDir
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszDir
);
1034 return E_OUTOFMEMORY
;
1041 HRESULT WINAPI
CShellLink::GetArguments(LPSTR pszArgs
, INT cchMaxPath
)
1043 TRACE("(%p)->(%p len=%u)\n", this, pszArgs
, cchMaxPath
);
1048 WideCharToMultiByte(CP_ACP
, 0, sArgs
, -1,
1049 pszArgs
, cchMaxPath
, NULL
, NULL
);
1054 HRESULT WINAPI
CShellLink::SetArguments(LPCSTR pszArgs
)
1056 TRACE("(%p)->(args=%s)\n", this, pszArgs
);
1058 HeapFree(GetProcessHeap(), 0, sArgs
);
1063 sArgs
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszArgs
);
1065 return E_OUTOFMEMORY
;
1073 HRESULT WINAPI
CShellLink::GetHotkey(WORD
*pwHotkey
)
1075 TRACE("(%p)->(%p)(0x%08x)\n", this, pwHotkey
, wHotKey
);
1077 *pwHotkey
= wHotKey
;
1082 HRESULT WINAPI
CShellLink::SetHotkey(WORD wHotkey
)
1084 TRACE("(%p)->(hotkey=%x)\n", this, wHotkey
);
1092 HRESULT WINAPI
CShellLink::GetShowCmd(INT
*piShowCmd
)
1094 TRACE("(%p)->(%p)\n", this, piShowCmd
);
1095 *piShowCmd
= iShowCmd
;
1099 HRESULT WINAPI
CShellLink::SetShowCmd(INT iShowCmd
)
1101 TRACE("(%p) %d\n", this, iShowCmd
);
1103 this->iShowCmd
= iShowCmd
;
1109 static HRESULT
SHELL_PidlGeticonLocationA(IShellFolder
* psf
, LPCITEMIDLIST pidl
,
1110 LPSTR pszIconPath
, int cchIconPath
, int* piIcon
)
1112 LPCITEMIDLIST pidlLast
;
1114 HRESULT hr
= SHBindToParent(pidl
, IID_IShellFolder
, (LPVOID
*)&psf
, &pidlLast
);
1118 CComPtr
<IExtractIconA
> pei
;
1120 hr
= psf
->GetUIObjectOf(0, 1, &pidlLast
, IID_IExtractIconA
, NULL
, (LPVOID
*)&pei
);
1123 hr
= pei
->GetIconLocation(0, pszIconPath
, MAX_PATH
, piIcon
, NULL
);
1131 HRESULT WINAPI
CShellLink::GetIconLocation(LPSTR pszIconPath
, INT cchIconPath
, INT
*piIcon
)
1133 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath
, cchIconPath
, piIcon
);
1140 WideCharToMultiByte(CP_ACP
, 0, sIcoPath
, -1, pszIconPath
, cchIconPath
, NULL
, NULL
);
1146 CComPtr
<IShellFolder
> pdsk
;
1148 HRESULT hr
= SHGetDesktopFolder(&pdsk
);
1152 /* first look for an icon using the PIDL (if present) */
1154 hr
= SHELL_PidlGeticonLocationA(pdsk
, pPidl
, pszIconPath
, cchIconPath
, piIcon
);
1158 /* if we couldn't find an icon yet, look for it using the file system path */
1159 if (FAILED(hr
) && sPath
)
1163 hr
= pdsk
->ParseDisplayName(0, NULL
, sPath
, NULL
, &pidl
, NULL
);
1167 hr
= SHELL_PidlGeticonLocationA(pdsk
, pidl
, pszIconPath
, cchIconPath
, piIcon
);
1179 HRESULT WINAPI
CShellLink::SetIconLocation(LPCSTR pszIconPath
, INT iIcon
)
1181 TRACE("(%p)->(path=%s iicon=%u)\n", this, pszIconPath
, iIcon
);
1183 HeapFree(GetProcessHeap(), 0, sIcoPath
);
1188 sIcoPath
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszIconPath
);
1190 return E_OUTOFMEMORY
;
1199 HRESULT WINAPI
CShellLink::SetRelativePath(LPCSTR pszPathRel
, DWORD dwReserved
)
1201 TRACE("(%p)->(path=%s %x)\n", this, pszPathRel
, dwReserved
);
1203 HeapFree(GetProcessHeap(), 0, sPathRel
);
1208 sPathRel
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszPathRel
);
1212 return ShellLink_UpdatePath(sPathRel
, sPath
, sWorkDir
, &sPath
);
1215 HRESULT WINAPI
CShellLink::Resolve(HWND hwnd
, DWORD fFlags
)
1220 TRACE("(%p)->(hwnd=%p flags=%x)\n", this, hwnd
, fFlags
);
1222 /*FIXME: use IResolveShellLink interface */
1224 if (!sPath
&& pPidl
)
1226 WCHAR buffer
[MAX_PATH
];
1228 bSuccess
= SHGetPathFromIDListW(pPidl
, buffer
);
1230 if (bSuccess
&& *buffer
)
1232 sPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(buffer
) + 1) * sizeof(WCHAR
));
1235 return E_OUTOFMEMORY
;
1237 wcscpy(sPath
, buffer
);
1242 hr
= S_OK
; /* don't report an error occurred while just caching information */
1245 if (!sIcoPath
&& sPath
)
1247 sIcoPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(sPath
) + 1) * sizeof(WCHAR
));
1250 return E_OUTOFMEMORY
;
1252 wcscpy(sIcoPath
, sPath
);
1261 HRESULT WINAPI
CShellLink::SetPath(LPCSTR pszFile
)
1263 TRACE("(%p)->(path=%s)\n", this, pszFile
);
1264 if (pszFile
== NULL
)
1265 return E_INVALIDARG
;
1267 LPWSTR str
= HEAP_strdupAtoW(GetProcessHeap(), 0, pszFile
);
1269 return E_OUTOFMEMORY
;
1271 HRESULT hr
= SetPath(str
);
1272 HeapFree(GetProcessHeap(), 0, str
);
1277 HRESULT WINAPI
CShellLink::GetPath(LPWSTR pszFile
, INT cchMaxPath
, WIN32_FIND_DATAW
*pfd
, DWORD fFlags
)
1279 TRACE("(%p)->(pfile=%p len=%u find_data=%p flags=%u)(%s)\n",
1280 this, pszFile
, cchMaxPath
, pfd
, fFlags
, debugstr_w(sPath
));
1282 if (sComponent
|| sProduct
)
1289 lstrcpynW(pszFile
, sPath
, cchMaxPath
);
1291 if (pfd
) FIXME("(%p): WIN32_FIND_DATA is not yet filled.\n", this);
1296 HRESULT WINAPI
CShellLink::GetDescription(LPWSTR pszName
, INT cchMaxName
)
1298 TRACE("(%p)->(%p len=%u)\n", this, pszName
, cchMaxName
);
1302 lstrcpynW(pszName
, sDescription
, cchMaxName
);
1307 HRESULT WINAPI
CShellLink::SetDescription(LPCWSTR pszName
)
1309 TRACE("(%p)->(desc=%s)\n", this, debugstr_w(pszName
));
1311 HeapFree(GetProcessHeap(), 0, sDescription
);
1312 sDescription
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1313 (wcslen(pszName
) + 1) * sizeof(WCHAR
));
1315 return E_OUTOFMEMORY
;
1317 wcscpy(sDescription
, pszName
);
1323 HRESULT WINAPI
CShellLink::GetWorkingDirectory(LPWSTR pszDir
, INT cchMaxPath
)
1325 TRACE("(%p)->(%p len %u)\n", this, pszDir
, cchMaxPath
);
1330 lstrcpynW(pszDir
, sWorkDir
, cchMaxPath
);
1335 HRESULT WINAPI
CShellLink::SetWorkingDirectory(LPCWSTR pszDir
)
1337 TRACE("(%p)->(dir=%s)\n", this, debugstr_w(pszDir
));
1339 HeapFree(GetProcessHeap(), 0, sWorkDir
);
1340 sWorkDir
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1341 (wcslen(pszDir
) + 1) * sizeof (WCHAR
));
1343 return E_OUTOFMEMORY
;
1344 wcscpy(sWorkDir
, pszDir
);
1350 HRESULT WINAPI
CShellLink::GetArguments(LPWSTR pszArgs
, INT cchMaxPath
)
1352 TRACE("(%p)->(%p len=%u)\n", this, pszArgs
, cchMaxPath
);
1357 lstrcpynW(pszArgs
, sArgs
, cchMaxPath
);
1362 HRESULT WINAPI
CShellLink::SetArguments(LPCWSTR pszArgs
)
1364 TRACE("(%p)->(args=%s)\n", this, debugstr_w(pszArgs
));
1366 HeapFree(GetProcessHeap(), 0, sArgs
);
1367 sArgs
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1368 (wcslen(pszArgs
) + 1) * sizeof (WCHAR
));
1370 return E_OUTOFMEMORY
;
1372 wcscpy(sArgs
, pszArgs
);
1378 static HRESULT
SHELL_PidlGeticonLocationW(IShellFolder
* psf
, LPCITEMIDLIST pidl
,
1379 LPWSTR pszIconPath
, int cchIconPath
, int* piIcon
)
1381 LPCITEMIDLIST pidlLast
;
1384 HRESULT hr
= SHBindToParent(pidl
, IID_IShellFolder
, (LPVOID
*)&psf
, &pidlLast
);
1388 CComPtr
<IExtractIconW
> pei
;
1390 hr
= psf
->GetUIObjectOf(0, 1, &pidlLast
, IID_IExtractIconW
, NULL
, (LPVOID
*)&pei
);
1393 hr
= pei
->GetIconLocation(0, pszIconPath
, MAX_PATH
, piIcon
, &wFlags
);
1401 HRESULT WINAPI
CShellLink::GetIconLocation(LPWSTR pszIconPath
, INT cchIconPath
, INT
*piIcon
)
1403 TRACE("(%p)->(%p len=%u iicon=%p)\n", this, pszIconPath
, cchIconPath
, piIcon
);
1410 lstrcpynW(pszIconPath
, sIcoPath
, cchIconPath
);
1416 CComPtr
<IShellFolder
> pdsk
;
1418 HRESULT hr
= SHGetDesktopFolder(&pdsk
);
1422 /* first look for an icon using the PIDL (if present) */
1424 hr
= SHELL_PidlGeticonLocationW(pdsk
, pPidl
, pszIconPath
, cchIconPath
, piIcon
);
1428 /* if we couldn't find an icon yet, look for it using the file system path */
1429 if (FAILED(hr
) && sPath
)
1433 hr
= pdsk
->ParseDisplayName(0, NULL
, sPath
, NULL
, &pidl
, NULL
);
1437 hr
= SHELL_PidlGeticonLocationW(pdsk
, pidl
, pszIconPath
, cchIconPath
, piIcon
);
1448 HRESULT WINAPI
CShellLink::SetIconLocation(LPCWSTR pszIconPath
, INT iIcon
)
1450 TRACE("(%p)->(path=%s iicon=%u)\n", this, debugstr_w(pszIconPath
), iIcon
);
1452 HeapFree(GetProcessHeap(), 0, sIcoPath
);
1453 sIcoPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1454 (wcslen(pszIconPath
) + 1) * sizeof (WCHAR
));
1456 return E_OUTOFMEMORY
;
1457 wcscpy(sIcoPath
, pszIconPath
);
1465 HRESULT WINAPI
CShellLink::SetRelativePath(LPCWSTR pszPathRel
, DWORD dwReserved
)
1467 TRACE("(%p)->(path=%s %x)\n", this, debugstr_w(pszPathRel
), dwReserved
);
1469 HeapFree(GetProcessHeap(), 0, sPathRel
);
1470 sPathRel
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1471 (wcslen(pszPathRel
) + 1) * sizeof (WCHAR
));
1473 return E_OUTOFMEMORY
;
1474 wcscpy(sPathRel
, pszPathRel
);
1477 return ShellLink_UpdatePath(sPathRel
, sPath
, sWorkDir
, &sPath
);
1480 LPWSTR
CShellLink::ShellLink_GetAdvertisedArg(LPCWSTR str
)
1485 LPCWSTR p
= wcschr(str
, L
':');
1488 DWORD len
= p
- str
;
1489 LPWSTR ret
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) * (len
+ 1));
1492 memcpy(ret
, str
, sizeof(WCHAR
)*len
);
1497 HRESULT
CShellLink::ShellLink_SetAdvertiseInfo(LPCWSTR str
)
1499 LPCWSTR szComponent
= NULL
, szProduct
= NULL
;
1503 /* each segment must start with two colons */
1504 if (str
[0] != ':' || str
[1] != ':')
1507 /* the last segment is just two colons */
1512 /* there must be a colon straight after a guid */
1513 LPCWSTR p
= wcschr(str
, L
':');
1520 /* get the guid, and check it's validly formatted */
1522 memcpy(szGuid
, str
, sizeof(WCHAR
)*len
);
1526 HRESULT hr
= CLSIDFromString(szGuid
, &guid
);
1531 /* match it up to a guid that we care about */
1532 if (IsEqualGUID(guid
, SHELL32_AdvtShortcutComponent
) && !szComponent
)
1534 else if (IsEqualGUID(guid
, SHELL32_AdvtShortcutProduct
) && !szProduct
)
1539 /* skip to the next field */
1540 str
= wcschr(str
, L
':');
1545 /* we have to have a component for an advertised shortcut */
1549 sComponent
= ShellLink_GetAdvertisedArg(szComponent
);
1550 sProduct
= ShellLink_GetAdvertisedArg(szProduct
);
1552 TRACE("Component = %s\n", debugstr_w(sComponent
));
1553 TRACE("Product = %s\n", debugstr_w(sProduct
));
1558 static BOOL
ShellLink_GetVolumeInfo(LPCWSTR path
, CShellLink::volume_info
*volume
)
1560 WCHAR drive
[4] = { path
[0], ':', '\\', 0 };
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
));
1569 HRESULT WINAPI
CShellLink::SetPath(LPCWSTR pszFile
)
1571 LPWSTR unquoted
= NULL
;
1574 TRACE("(%p)->(path=%s)\n", this, debugstr_w(pszFile
));
1577 return E_INVALIDARG
;
1579 /* quotes at the ends of the string are stripped */
1580 UINT len
= wcslen(pszFile
);
1581 if (pszFile
[0] == '"' && pszFile
[len
-1] == '"')
1583 unquoted
= strdupW(pszFile
);
1584 PathUnquoteSpacesW(unquoted
);
1588 /* any other quote marks are invalid */
1589 if (wcschr(pszFile
, '"'))
1591 HeapFree(GetProcessHeap(), 0, unquoted
);
1595 HeapFree(GetProcessHeap(), 0, sPath
);
1598 HeapFree(GetProcessHeap(), 0, sComponent
);
1605 if (S_OK
!= ShellLink_SetAdvertiseInfo(pszFile
))
1607 WCHAR buffer
[MAX_PATH
];
1610 if (*pszFile
== '\0')
1612 else if (!GetFullPathNameW(pszFile
, MAX_PATH
, buffer
, &fname
))
1614 else if(!PathFileExistsW(buffer
) &&
1615 !SearchPathW(NULL
, pszFile
, NULL
, MAX_PATH
, buffer
, NULL
))
1618 pPidl
= SHSimpleIDListFromPathW(pszFile
);
1619 ShellLink_GetVolumeInfo(buffer
, &volume
);
1621 sPath
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0,
1622 (wcslen(buffer
) + 1) * sizeof (WCHAR
));
1624 return E_OUTOFMEMORY
;
1626 wcscpy(sPath
, buffer
);
1630 HeapFree(GetProcessHeap(), 0, unquoted
);
1635 HRESULT WINAPI
CShellLink::AddDataBlock(void* pDataBlock
)
1641 HRESULT WINAPI
CShellLink::CopyDataBlock(DWORD dwSig
, void** ppDataBlock
)
1643 LPVOID block
= NULL
;
1644 HRESULT hr
= E_FAIL
;
1646 TRACE("%p %08x %p\n", this, dwSig
, ppDataBlock
);
1650 case EXP_DARWIN_ID_SIG
:
1653 block
= shelllink_build_darwinid(sComponent
, dwSig
);
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
);
1664 ERR("unknown datablock %08x\n", dwSig
);
1666 *ppDataBlock
= block
;
1670 HRESULT WINAPI
CShellLink::RemoveDataBlock(DWORD dwSig
)
1676 HRESULT WINAPI
CShellLink::GetFlags(DWORD
*pdwFlags
)
1680 FIXME("%p %p\n", this, pdwFlags
);
1682 /* FIXME: add more */
1684 flags
|= SLDF_HAS_ARGS
;
1686 flags
|= SLDF_HAS_DARWINID
;
1688 flags
|= SLDF_HAS_ICONLOCATION
;
1689 #if (NTDDI_VERSION < NTDDI_LONGHORN)
1691 flags
|= SLDF_HAS_LOGO3ID
;
1694 flags
|= SLDF_HAS_ID_LIST
;
1701 HRESULT WINAPI
CShellLink::SetFlags(DWORD dwFlags
)
1707 /**************************************************************************
1708 * CShellLink implementation of IShellExtInit::Initialize()
1710 * Loads the shelllink from the dataobject the shell is pointing to.
1712 HRESULT WINAPI
CShellLink::Initialize(LPCITEMIDLIST pidlFolder
, IDataObject
*pdtobj
, HKEY hkeyProgID
)
1714 TRACE("%p %p %p %p\n", this, pidlFolder
, pdtobj
, hkeyProgID
);
1720 format
.cfFormat
= CF_HDROP
;
1722 format
.dwAspect
= DVASPECT_CONTENT
;
1724 format
.tymed
= TYMED_HGLOBAL
;
1727 HRESULT hr
= pdtobj
->GetData(&format
, &stgm
);
1731 UINT count
= DragQueryFileW((HDROP
)stgm
.hGlobal
, -1, NULL
, 0);
1734 count
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0, NULL
, 0);
1736 LPWSTR path
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, count
* sizeof(WCHAR
));
1739 count
= DragQueryFileW((HDROP
)stgm
.hGlobal
, 0, path
, count
);
1741 HeapFree(GetProcessHeap(), 0, path
);
1744 ReleaseStgMedium(&stgm
);
1749 HRESULT WINAPI
CShellLink::QueryContextMenu(HMENU hMenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
)
1753 TRACE("%p %p %u %u %u %u\n", this,
1754 hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
1757 return E_INVALIDARG
;
1760 if (!LoadStringW(shell32_hInstance
, IDS_OPEN_VERB
, wszOpen
, _countof(wszOpen
)))
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
))
1776 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, id
);
1780 shelllink_get_msi_component_path(LPWSTR component
)
1782 DWORD Result
, sz
= 0;
1784 Result
= CommandLineFromMsiDescriptor(component
, NULL
, &sz
);
1785 if (Result
!= ERROR_SUCCESS
)
1789 LPWSTR path
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, sz
* sizeof(WCHAR
));
1790 Result
= CommandLineFromMsiDescriptor(component
, path
, &sz
);
1791 if (Result
!= ERROR_SUCCESS
)
1793 HeapFree(GetProcessHeap(), 0, path
);
1797 TRACE("returning %s\n", debugstr_w(path
));
1802 HRESULT WINAPI
CShellLink::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
1804 HWND hwnd
= NULL
; /* FIXME: get using interface set from IObjectWithSite */
1808 TRACE("%p %p\n", this, lpici
);
1810 if (lpici
->cbSize
< sizeof (CMINVOKECOMMANDINFO
))
1811 return E_INVALIDARG
;
1813 HRESULT hr
= Resolve(hwnd
, 0);
1816 TRACE("failed to resolve component with error 0x%08x", hr
);
1821 path
= shelllink_get_msi_component_path(sComponent
);
1826 path
= strdupW(sPath
);
1828 if (lpici
->cbSize
== sizeof (CMINVOKECOMMANDINFOEX
) &&
1829 (lpici
->fMask
& CMIC_MASK_UNICODE
))
1831 LPCMINVOKECOMMANDINFOEX iciex
= (LPCMINVOKECOMMANDINFOEX
) lpici
;
1835 len
+= wcslen(sArgs
);
1836 if (iciex
->lpParametersW
)
1837 len
+= wcslen(iciex
->lpParametersW
);
1839 args
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
1842 wcscat(args
, sArgs
);
1843 if (iciex
->lpParametersW
)
1846 wcscat(args
, iciex
->lpParametersW
);
1849 else if (sArgs
!= NULL
)
1851 args
= strdupW(sArgs
);
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
));
1859 sei
.nShow
= iShowCmd
;
1860 sei
.lpDirectory
= sWorkDir
;
1861 sei
.lpParameters
= args
;
1862 sei
.lpVerb
= L
"open";
1864 // HACK for ShellExecuteExW
1865 if (wcsstr(sPath
, L
".cpl"))
1866 sei
.lpVerb
= L
"cplopen";
1868 if (ShellExecuteExW(&sei
))
1873 HeapFree(GetProcessHeap(), 0, args
);
1874 HeapFree(GetProcessHeap(), 0, path
);
1879 HRESULT WINAPI
CShellLink::GetCommandString(UINT_PTR idCmd
, UINT uType
, UINT
* pwReserved
, LPSTR pszName
, UINT cchMax
)
1881 FIXME("%p %lu %u %p %p %u\n", this, idCmd
, uType
, pwReserved
, pszName
, cchMax
);
1886 INT_PTR CALLBACK
ExtendedShortcutProc(HWND hwndDlg
, UINT uMsg
,
1887 WPARAM wParam
, LPARAM lParam
)
1894 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14000);
1895 SendMessage(hDlgCtrl
, BM_SETCHECK
, BST_CHECKED
, 0);
1900 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14000);
1901 if (LOWORD(wParam
) == IDOK
)
1903 if (SendMessage(hDlgCtrl
, BM_GETCHECK
, 0, 0) == BST_CHECKED
)
1904 EndDialog(hwndDlg
, 1);
1906 EndDialog(hwndDlg
, 0);
1908 else if (LOWORD(wParam
) == IDCANCEL
)
1910 EndDialog(hwndDlg
, -1);
1912 else if (LOWORD(wParam
) == 14000)
1914 if (SendMessage(hDlgCtrl
, BM_GETCHECK
, 0, 0) == BST_CHECKED
)
1915 SendMessage(hDlgCtrl
, BM_SETCHECK
, BST_UNCHECKED
, 0);
1917 SendMessage(hDlgCtrl
, BM_SETCHECK
, BST_CHECKED
, 0);
1924 /**************************************************************************
1925 * SH_ShellLinkDlgProc
1927 * dialog proc of the shortcut property dialog
1930 INT_PTR CALLBACK
CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1932 CShellLink
*pThis
= (CShellLink
*)GetWindowLongPtr(hwndDlg
, DWLP_USER
);
1938 LPPROPSHEETPAGEW ppsp
= (LPPROPSHEETPAGEW
)lParam
;
1942 TRACE("ShellLink_DlgProc (WM_INITDIALOG hwnd %p lParam %p ppsplParam %x)\n", hwndDlg
, lParam
, ppsp
->lParam
);
1944 pThis
= (CShellLink
*)ppsp
->lParam
;
1945 SetWindowLongPtr(hwndDlg
, DWLP_USER
, (LONG_PTR
)pThis
);
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
);
1950 /* Get file information */
1952 if (!SHGetFileInfoW(pThis
->sLinkPath
, 0, &fi
, sizeof(fi
), SHGFI_TYPENAME
|SHGFI_ICON
))
1954 ERR("SHGetFileInfoW failed for %ls (%lu)\n", pThis
->sLinkPath
, GetLastError());
1955 fi
.szTypeName
[0] = L
'\0';
1959 if (fi
.hIcon
) // TODO: destroy icon
1960 SendDlgItemMessageW(hwndDlg
, 14000, STM_SETICON
, (WPARAM
)fi
.hIcon
, 0);
1962 ERR("ExtractIconW failed %ls %u\n", pThis
->sIcoPath
, pThis
->iIcoNdx
);
1964 /* target location */
1965 if (pThis
->sWorkDir
)
1966 SetDlgItemTextW(hwndDlg
, 14007, PathFindFileName(pThis
->sWorkDir
));
1970 SetDlgItemTextW(hwndDlg
, 14009, pThis
->sPath
);
1973 if (pThis
->sWorkDir
)
1974 SetDlgItemTextW(hwndDlg
, 14011, pThis
->sWorkDir
);
1977 if (pThis
->sDescription
)
1978 SetDlgItemTextW(hwndDlg
, 14019, pThis
->sDescription
);
1984 LPPSHNOTIFY lppsn
= (LPPSHNOTIFY
)lParam
;
1985 if (lppsn
->hdr
.code
== PSN_APPLY
)
1987 WCHAR wszBuf
[MAX_PATH
];
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
))
1996 //FIXME load localized error msg
1997 MessageBoxW(hwndDlg
, L
"file not existing", wszBuf
, MB_OK
);
1998 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_INVALID_NOCHANGEPAGE
);
2002 WCHAR
*pwszExt
= PathFindExtensionW(wszBuf
);
2003 if (!wcsicmp(pwszExt
, L
".lnk"))
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
);
2011 pThis
->SetPath(wszBuf
);
2013 TRACE("This %p sLinkPath %S\n", pThis
, pThis
->sLinkPath
);
2014 pThis
->Save(pThis
->sLinkPath
, TRUE
);
2015 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_NOERROR
);
2021 switch(LOWORD(wParam
))
2026 /// open target directory
2031 WCHAR wszPath
[MAX_PATH
] = L
"";
2033 if (pThis
->sIcoPath
)
2034 wcscpy(wszPath
, pThis
->sIcoPath
);
2035 INT IconIndex
= pThis
->iIcoNdx
;
2036 if (PickIconDlg(hwndDlg
, wszPath
, MAX_PATH
, &IconIndex
))
2038 pThis
->SetIconLocation(wszPath
, IconIndex
);
2040 /// FIXME redraw icon
2047 INT_PTR result
= DialogBoxParamW(shell32_hInstance
, MAKEINTRESOURCEW(IDD_SHORTCUT_EXTENDED_PROPERTIES
), hwndDlg
, ExtendedShortcutProc
, (LPARAM
)pThis
->bRunAs
);
2048 if (result
== 1 || result
== 0)
2050 if (pThis
->bRunAs
!= result
)
2052 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
2055 pThis
->bRunAs
= result
;
2060 if(HIWORD(wParam
) == EN_CHANGE
)
2061 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
2069 /**************************************************************************
2070 * ShellLink_IShellPropSheetExt interface
2073 HRESULT WINAPI
CShellLink::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
)
2075 HPROPSHEETPAGE hPage
= SH_CreatePropertySheetPage(IDD_SHORTCUT_PROPERTIES
, SH_ShellLinkDlgProc
, (LPARAM
)this, NULL
);
2078 ERR("failed to create property sheet page\n");
2082 if (!pfnAddPage(hPage
, lParam
))
2088 HRESULT WINAPI
CShellLink::ReplacePage(UINT uPageID
, LPFNADDPROPSHEETPAGE pfnReplacePage
, LPARAM lParam
)
2090 TRACE("(%p) (uPageID %u, pfnReplacePage %p lParam %p\n", this, uPageID
, pfnReplacePage
, lParam
);
2094 HRESULT WINAPI
CShellLink::SetSite(IUnknown
*punk
)
2096 TRACE("%p %p\n", this, punk
);
2103 HRESULT WINAPI
CShellLink::GetSite(REFIID iid
, void ** ppvSite
)
2105 TRACE("%p %s %p\n", this, debugstr_guid(&iid
), ppvSite
);
2110 return site
->QueryInterface(iid
, ppvSite
);
2113 /**************************************************************************
2114 * IShellLink_ConstructFromFile
2116 HRESULT WINAPI
IShellLink_ConstructFromFile(IUnknown
*pUnkOuter
, REFIID riid
, LPCITEMIDLIST pidl
, LPVOID
*ppv
)
2118 CComPtr
<IUnknown
> psl
;
2120 HRESULT hr
= CShellLink::_CreatorClass::CreateInstance(NULL
, riid
, (void**)&psl
);
2124 CComPtr
<IPersistFile
> ppf
;
2128 hr
= psl
->QueryInterface(IID_IPersistFile
, (LPVOID
*)&ppf
);
2132 WCHAR path
[MAX_PATH
];
2134 if (SHGetPathFromIDListW(pidl
, path
))
2135 hr
= ppf
->Load(path
, 0);
2140 *ppv
= psl
.Detach();