2 * Copyright 2008 Damjan Jovanovic
4 * ShellLink's barely documented cousin that handles URLs.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 * Implement the IShellLinkA/W interfaces
24 * Handle the SetURL flags
25 * Implement any other interfaces? Does any software actually use them?
27 * The installer for the Zuma Deluxe Popcap game is good for testing.
33 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(shdocvw);
46 IUniformResourceLocatorA uniformResourceLocatorA;
47 IUniformResourceLocatorW uniformResourceLocatorW;
48 IPersistFile persistFile;
49 IPropertySetStorage IPropertySetStorage_iface;
53 IPropertySetStorage *property_set_storage;
59 /* utility functions */
61 static inline InternetShortcut* impl_from_IUniformResourceLocatorA(IUniformResourceLocatorA *iface)
63 return CONTAINING_RECORD(iface, InternetShortcut, uniformResourceLocatorA);
66 static inline InternetShortcut* impl_from_IUniformResourceLocatorW(IUniformResourceLocatorW *iface)
68 return CONTAINING_RECORD(iface, InternetShortcut, uniformResourceLocatorW);
71 static inline InternetShortcut* impl_from_IPersistFile(IPersistFile *iface)
73 return CONTAINING_RECORD(iface, InternetShortcut, persistFile);
76 static inline InternetShortcut* impl_from_IPropertySetStorage(IPropertySetStorage *iface)
78 return (InternetShortcut*)((char*)iface - FIELD_OFFSET(InternetShortcut, IPropertySetStorage_iface));
81 static BOOL run_winemenubuilder( const WCHAR *args )
83 static const WCHAR menubuilder[] = {'\\','w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',0};
87 PROCESS_INFORMATION pi;
92 GetSystemDirectoryW( app, MAX_PATH - sizeof(menubuilder)/sizeof(WCHAR) );
93 strcatW( app, menubuilder );
95 len = (strlenW( app ) + strlenW( args ) + 1) * sizeof(WCHAR);
96 buffer = heap_alloc( len );
100 strcpyW( buffer, app );
101 strcatW( buffer, args );
103 TRACE("starting %s\n",debugstr_w(buffer));
105 memset(&si, 0, sizeof(si));
108 Wow64DisableWow64FsRedirection( &redir );
109 ret = CreateProcessW( app, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
110 Wow64RevertWow64FsRedirection( redir );
116 CloseHandle( pi.hProcess );
117 CloseHandle( pi.hThread );
123 static BOOL StartLinkProcessor( LPCOLESTR szLink )
125 static const WCHAR szFormat[] = { ' ','-','w',' ','-','u',' ','"','%','s','"',0 };
130 len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
131 buffer = heap_alloc( len );
135 wsprintfW( buffer, szFormat, szLink );
136 ret = run_winemenubuilder( buffer );
141 /* interface functions */
143 static HRESULT Unknown_QueryInterface(InternetShortcut *This, REFIID riid, PVOID *ppvObject)
145 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
147 if (IsEqualGUID(&IID_IUnknown, riid))
148 *ppvObject = &This->uniformResourceLocatorA;
149 else if (IsEqualGUID(&IID_IUniformResourceLocatorA, riid))
150 *ppvObject = &This->uniformResourceLocatorA;
151 else if (IsEqualGUID(&IID_IUniformResourceLocatorW, riid))
152 *ppvObject = &This->uniformResourceLocatorW;
153 else if (IsEqualGUID(&IID_IPersistFile, riid))
154 *ppvObject = &This->persistFile;
155 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
156 *ppvObject = &This->IPropertySetStorage_iface;
157 else if (IsEqualGUID(&IID_IShellLinkA, riid))
159 FIXME("The IShellLinkA interface is not yet supported by InternetShortcut\n");
160 return E_NOINTERFACE;
162 else if (IsEqualGUID(&IID_IShellLinkW, riid))
164 FIXME("The IShellLinkW interface is not yet supported by InternetShortcut\n");
165 return E_NOINTERFACE;
169 FIXME("Interface with GUID %s not yet implemented by InternetShortcut\n", debugstr_guid(riid));
170 return E_NOINTERFACE;
172 IUnknown_AddRef((IUnknown*)*ppvObject);
176 static ULONG Unknown_AddRef(InternetShortcut *This)
178 TRACE("(%p)\n", This);
179 return InterlockedIncrement(&This->refCount);
182 static ULONG Unknown_Release(InternetShortcut *This)
185 TRACE("(%p)\n", This);
186 count = InterlockedDecrement(&This->refCount);
189 CoTaskMemFree(This->url);
190 CoTaskMemFree(This->currentFile);
191 IPropertySetStorage_Release(This->property_set_storage);
193 SHDOCVW_UnlockModule();
198 static HRESULT WINAPI UniformResourceLocatorW_QueryInterface(IUniformResourceLocatorW *url, REFIID riid, PVOID *ppvObject)
200 InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
201 TRACE("(%p, %s, %p)\n", url, debugstr_guid(riid), ppvObject);
202 return Unknown_QueryInterface(This, riid, ppvObject);
205 static ULONG WINAPI UniformResourceLocatorW_AddRef(IUniformResourceLocatorW *url)
207 InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
208 TRACE("(%p)\n", url);
209 return Unknown_AddRef(This);
212 static ULONG WINAPI UniformResourceLocatorW_Release(IUniformResourceLocatorW *url)
214 InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
215 TRACE("(%p)\n", url);
216 return Unknown_Release(This);
219 static HRESULT WINAPI UniformResourceLocatorW_SetUrl(IUniformResourceLocatorW *url, LPCWSTR pcszURL, DWORD dwInFlags)
221 WCHAR *newURL = NULL;
222 InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
223 TRACE("(%p, %s, 0x%x)\n", url, debugstr_w(pcszURL), dwInFlags);
225 FIXME("ignoring unsupported flags 0x%x\n", dwInFlags);
228 newURL = co_strdupW(pcszURL);
230 return E_OUTOFMEMORY;
232 CoTaskMemFree(This->url);
234 This->isDirty = TRUE;
238 static HRESULT WINAPI UniformResourceLocatorW_GetUrl(IUniformResourceLocatorW *url, LPWSTR *ppszURL)
241 InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
242 TRACE("(%p, %p)\n", url, ppszURL);
243 if (This->url == NULL)
247 *ppszURL = co_strdupW(This->url);
248 if (*ppszURL == NULL)
254 static HRESULT WINAPI UniformResourceLocatorW_InvokeCommand(IUniformResourceLocatorW *url, PURLINVOKECOMMANDINFOW pCommandInfo)
256 InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
259 static const WCHAR wszURLProtocol[] = {'U','R','L',' ','P','r','o','t','o','c','o','l',0};
260 SHELLEXECUTEINFOW sei;
264 TRACE("%p %p\n", This, pCommandInfo );
266 if (pCommandInfo->dwcbSize < sizeof (URLINVOKECOMMANDINFOW))
269 if (pCommandInfo->dwFlags != IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB)
271 FIXME("(%p, %p): non-default verbs not implemented\n", url, pCommandInfo);
275 hres = CoInternetParseUrl(This->url, PARSE_SCHEMA, 0, app, sizeof(app)/sizeof(WCHAR), NULL, 0);
279 res = RegOpenKeyW(HKEY_CLASSES_ROOT, app, &hkey);
280 if(res != ERROR_SUCCESS)
283 res = RegQueryValueExW(hkey, wszURLProtocol, NULL, &type, NULL, NULL);
285 if(res != ERROR_SUCCESS || type != REG_SZ)
288 memset(&sei, 0, sizeof(sei));
289 sei.cbSize = sizeof(sei);
290 sei.lpFile = This->url;
293 if( ShellExecuteExW(&sei) )
299 static HRESULT WINAPI UniformResourceLocatorA_QueryInterface(IUniformResourceLocatorA *url, REFIID riid, PVOID *ppvObject)
301 InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
302 TRACE("(%p, %s, %p)\n", url, debugstr_guid(riid), ppvObject);
303 return Unknown_QueryInterface(This, riid, ppvObject);
306 static ULONG WINAPI UniformResourceLocatorA_AddRef(IUniformResourceLocatorA *url)
308 InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
309 TRACE("(%p)\n", url);
310 return Unknown_AddRef(This);
313 static ULONG WINAPI UniformResourceLocatorA_Release(IUniformResourceLocatorA *url)
315 InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
316 TRACE("(%p)\n", url);
317 return Unknown_Release(This);
320 static HRESULT WINAPI UniformResourceLocatorA_SetUrl(IUniformResourceLocatorA *url, LPCSTR pcszURL, DWORD dwInFlags)
322 WCHAR *newURL = NULL;
323 InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
324 TRACE("(%p, %s, 0x%x)\n", url, debugstr_a(pcszURL), dwInFlags);
326 FIXME("ignoring unsupported flags 0x%x\n", dwInFlags);
329 newURL = co_strdupAtoW(pcszURL);
331 return E_OUTOFMEMORY;
333 CoTaskMemFree(This->url);
335 This->isDirty = TRUE;
339 static HRESULT WINAPI UniformResourceLocatorA_GetUrl(IUniformResourceLocatorA *url, LPSTR *ppszURL)
342 InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
343 TRACE("(%p, %p)\n", url, ppszURL);
344 if (This->url == NULL)
348 *ppszURL = co_strdupWtoA(This->url);
349 if (*ppszURL == NULL)
355 static HRESULT WINAPI UniformResourceLocatorA_InvokeCommand(IUniformResourceLocatorA *url, PURLINVOKECOMMANDINFOA pCommandInfo)
357 URLINVOKECOMMANDINFOW wideCommandInfo;
361 InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
363 wideCommandInfo.dwcbSize = sizeof wideCommandInfo;
364 wideCommandInfo.dwFlags = pCommandInfo->dwFlags;
365 wideCommandInfo.hwndParent = pCommandInfo->hwndParent;
367 len = MultiByteToWideChar(CP_ACP, 0, pCommandInfo->pcszVerb, -1, NULL, 0);
368 wideVerb = heap_alloc(len * sizeof(WCHAR));
369 MultiByteToWideChar(CP_ACP, 0, pCommandInfo->pcszVerb, -1, wideVerb, len);
371 wideCommandInfo.pcszVerb = wideVerb;
373 res = UniformResourceLocatorW_InvokeCommand(&This->uniformResourceLocatorW, &wideCommandInfo);
379 static HRESULT WINAPI PersistFile_QueryInterface(IPersistFile *pFile, REFIID riid, PVOID *ppvObject)
381 InternetShortcut *This = impl_from_IPersistFile(pFile);
382 TRACE("(%p, %s, %p)\n", pFile, debugstr_guid(riid), ppvObject);
383 return Unknown_QueryInterface(This, riid, ppvObject);
386 static ULONG WINAPI PersistFile_AddRef(IPersistFile *pFile)
388 InternetShortcut *This = impl_from_IPersistFile(pFile);
389 TRACE("(%p)\n", pFile);
390 return Unknown_AddRef(This);
393 static ULONG WINAPI PersistFile_Release(IPersistFile *pFile)
395 InternetShortcut *This = impl_from_IPersistFile(pFile);
396 TRACE("(%p)\n", pFile);
397 return Unknown_Release(This);
400 static HRESULT WINAPI PersistFile_GetClassID(IPersistFile *pFile, CLSID *pClassID)
402 TRACE("(%p, %p)\n", pFile, pClassID);
403 *pClassID = CLSID_InternetShortcut;
407 static HRESULT WINAPI PersistFile_IsDirty(IPersistFile *pFile)
409 InternetShortcut *This = impl_from_IPersistFile(pFile);
410 TRACE("(%p)\n", pFile);
411 return This->isDirty ? S_OK : S_FALSE;
414 static HRESULT WINAPI PersistFile_Load(IPersistFile *pFile, LPCOLESTR pszFileName, DWORD dwMode)
416 WCHAR str_header[] = {'I','n','t','e','r','n','e','t','S','h','o','r','t','c','u','t',0};
417 WCHAR str_URL[] = {'U','R','L',0};
418 WCHAR *filename = NULL;
420 InternetShortcut *This = impl_from_IPersistFile(pFile);
421 TRACE("(%p, %s, 0x%x)\n", pFile, debugstr_w(pszFileName), dwMode);
423 FIXME("ignoring unimplemented mode 0x%x\n", dwMode);
424 filename = co_strdupW(pszFileName);
425 if (filename != NULL)
429 WCHAR *url = CoTaskMemAlloc(len*sizeof(WCHAR));
432 r = GetPrivateProfileStringW(str_header, str_URL, NULL, url, len, pszFileName);
437 url = CoTaskMemAlloc(len*sizeof(WCHAR));
440 r = GetPrivateProfileStringW(str_header, str_URL, NULL, url, len, pszFileName);
444 else if (url != NULL)
446 CoTaskMemFree(This->currentFile);
447 This->currentFile = filename;
448 CoTaskMemFree(This->url);
450 This->isDirty = FALSE;
459 CoTaskMemFree(filename);
466 static HRESULT WINAPI PersistFile_Save(IPersistFile *pFile, LPCOLESTR pszFileName, BOOL fRemember)
471 InternetShortcut *This = impl_from_IPersistFile(pFile);
473 TRACE("(%p, %s, %d)\n", pFile, debugstr_w(pszFileName), fRemember);
475 if (pszFileName != NULL && fRemember)
477 LPOLESTR oldFile = This->currentFile;
478 This->currentFile = co_strdupW(pszFileName);
479 if (This->currentFile == NULL)
481 This->currentFile = oldFile;
482 return E_OUTOFMEMORY;
484 CoTaskMemFree(oldFile);
486 if (This->url == NULL)
489 /* Windows seems to always write:
490 * ASCII "[InternetShortcut]" headers
491 * ASCII names in "name=value" pairs
492 * An ASCII (probably UTF8?) value in "URL=..."
494 len = WideCharToMultiByte(CP_UTF8, 0, This->url, -1, NULL, 0, 0, 0);
495 url = heap_alloc(len);
499 WideCharToMultiByte(CP_UTF8, 0, This->url, -1, url, len, 0, 0);
500 file = CreateFileW(pszFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
501 if (file != INVALID_HANDLE_VALUE)
504 char str_header[] = "[InternetShortcut]";
505 char str_URL[] = "URL=";
506 char str_eol[] = "\r\n";
508 WriteFile(file, str_header, lstrlenA(str_header), &bytesWritten, NULL);
509 WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
510 WriteFile(file, str_URL, lstrlenA(str_URL), &bytesWritten, NULL);
511 WriteFile(file, url, lstrlenA(url), &bytesWritten, NULL);
512 WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
514 if (pszFileName == NULL || fRemember)
515 This->isDirty = FALSE;
516 StartLinkProcessor(pszFileName);
528 static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *pFile, LPCOLESTR pszFileName)
530 FIXME("(%p, %p): stub\n", pFile, pszFileName);
534 static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *pFile, LPOLESTR *ppszFileName)
537 InternetShortcut *This = impl_from_IPersistFile(pFile);
538 TRACE("(%p, %p)\n", pFile, ppszFileName);
539 if (This->currentFile == NULL)
540 *ppszFileName = NULL;
543 *ppszFileName = co_strdupW(This->currentFile);
544 if (*ppszFileName == NULL)
550 static HRESULT WINAPI PropertySetStorage_QueryInterface(IPropertySetStorage *iface, REFIID riid, PVOID *ppvObject)
552 InternetShortcut *This = impl_from_IPropertySetStorage(iface);
553 TRACE("(%p)\n", iface);
554 return Unknown_QueryInterface(This, riid, ppvObject);
557 static ULONG WINAPI PropertySetStorage_AddRef(IPropertySetStorage *iface)
559 InternetShortcut *This = impl_from_IPropertySetStorage(iface);
560 TRACE("(%p)\n", iface);
561 return Unknown_AddRef(This);
564 static ULONG WINAPI PropertySetStorage_Release(IPropertySetStorage *iface)
566 InternetShortcut *This = impl_from_IPropertySetStorage(iface);
567 TRACE("(%p)\n", iface);
568 return Unknown_Release(This);
571 static HRESULT WINAPI PropertySetStorage_Create(
572 IPropertySetStorage* iface,
577 IPropertyStorage **ppprstg)
579 InternetShortcut *This = impl_from_IPropertySetStorage(iface);
580 TRACE("(%s, %p, 0x%x, 0x%x, %p)\n", debugstr_guid(rfmtid), pclsid, grfFlags, grfMode, ppprstg);
582 return IPropertySetStorage_Create(This->property_set_storage,
590 static HRESULT WINAPI PropertySetStorage_Open(
591 IPropertySetStorage* iface,
594 IPropertyStorage **ppprstg)
596 InternetShortcut *This = impl_from_IPropertySetStorage(iface);
597 TRACE("(%s, 0x%x, %p)\n", debugstr_guid(rfmtid), grfMode, ppprstg);
599 /* Note: The |STGM_SHARE_EXCLUSIVE is to cope with a bug in the implementation. Should be fixed in ole32. */
600 return IPropertySetStorage_Open(This->property_set_storage,
602 grfMode|STGM_SHARE_EXCLUSIVE,
606 static HRESULT WINAPI PropertySetStorage_Delete(IPropertySetStorage *iface, REFFMTID rfmtid)
608 InternetShortcut *This = impl_from_IPropertySetStorage(iface);
609 TRACE("(%s)\n", debugstr_guid(rfmtid));
612 return IPropertySetStorage_Delete(This->property_set_storage,
616 static HRESULT WINAPI PropertySetStorage_Enum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **ppenum)
618 FIXME("(%p): stub\n", ppenum);
622 static const IUniformResourceLocatorWVtbl uniformResourceLocatorWVtbl = {
623 UniformResourceLocatorW_QueryInterface,
624 UniformResourceLocatorW_AddRef,
625 UniformResourceLocatorW_Release,
626 UniformResourceLocatorW_SetUrl,
627 UniformResourceLocatorW_GetUrl,
628 UniformResourceLocatorW_InvokeCommand
631 static const IUniformResourceLocatorAVtbl uniformResourceLocatorAVtbl = {
632 UniformResourceLocatorA_QueryInterface,
633 UniformResourceLocatorA_AddRef,
634 UniformResourceLocatorA_Release,
635 UniformResourceLocatorA_SetUrl,
636 UniformResourceLocatorA_GetUrl,
637 UniformResourceLocatorA_InvokeCommand
640 static const IPersistFileVtbl persistFileVtbl = {
641 PersistFile_QueryInterface,
644 PersistFile_GetClassID,
648 PersistFile_SaveCompleted,
649 PersistFile_GetCurFile
652 static const IPropertySetStorageVtbl propertySetStorageVtbl = {
653 PropertySetStorage_QueryInterface,
654 PropertySetStorage_AddRef,
655 PropertySetStorage_Release,
656 PropertySetStorage_Create,
657 PropertySetStorage_Open,
658 PropertySetStorage_Delete,
659 PropertySetStorage_Enum
662 static InternetShortcut *create_shortcut(void)
664 InternetShortcut *newshortcut;
666 newshortcut = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(InternetShortcut));
670 IPropertyStorage *dummy;
672 newshortcut->uniformResourceLocatorA.lpVtbl = &uniformResourceLocatorAVtbl;
673 newshortcut->uniformResourceLocatorW.lpVtbl = &uniformResourceLocatorWVtbl;
674 newshortcut->persistFile.lpVtbl = &persistFileVtbl;
675 newshortcut->IPropertySetStorage_iface.lpVtbl = &propertySetStorageVtbl;
676 newshortcut->refCount = 0;
677 hr = StgCreateStorageEx(NULL, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, STGFMT_STORAGE, 0, NULL, NULL, &IID_IPropertySetStorage, (void **) &newshortcut->property_set_storage);
680 TRACE("Failed to create the storage object needed for the shortcut.\n");
681 heap_free(newshortcut);
685 hr = IPropertySetStorage_Create(newshortcut->property_set_storage, &FMTID_Intshcut, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &dummy);
688 TRACE("Failed to create the property object needed for the shortcut.\n");
689 IPropertySetStorage_Release(newshortcut->property_set_storage);
690 heap_free(newshortcut);
693 IPropertySetStorage_Release(dummy);
699 HRESULT InternetShortcut_Create(IUnknown *pOuter, REFIID riid, void **ppv)
701 InternetShortcut *This;
704 TRACE("(%p, %s, %p)\n", pOuter, debugstr_guid(riid), ppv);
709 return CLASS_E_NOAGGREGATION;
711 This = create_shortcut();
714 hr = Unknown_QueryInterface(This, riid, ppv);
716 SHDOCVW_LockModule();
722 return E_OUTOFMEMORY;
726 /**********************************************************************
727 * OpenURL (SHDOCVW.@)
729 void WINAPI OpenURL(HWND hWnd, HINSTANCE hInst, LPCSTR lpcstrUrl, int nShowCmd)
731 InternetShortcut *shortcut;
732 WCHAR* urlfilepath = NULL;
733 shortcut = create_shortcut();
739 len = MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, NULL, 0);
740 urlfilepath = heap_alloc(len * sizeof(WCHAR));
741 MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, urlfilepath, len);
743 if(SUCCEEDED(IPersistFile_Load(&shortcut->persistFile, urlfilepath, 0)))
745 URLINVOKECOMMANDINFOW ici;
747 memset( &ici, 0, sizeof ici );
748 ici.dwcbSize = sizeof ici;
749 ici.dwFlags = IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB;
750 ici.hwndParent = hWnd;
752 if FAILED(UniformResourceLocatorW_InvokeCommand(&shortcut->uniformResourceLocatorW, (PURLINVOKECOMMANDINFOW) &ici))
753 TRACE("failed to open URL: %s\n", debugstr_a(lpcstrUrl));
757 heap_free(urlfilepath);