| /* |
| * Copyright 2008 Damjan Jovanovic |
| * |
| * ShellLink's barely documented cousin that handles URLs. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| /* |
| * TODO: |
| * Implement the IShellLinkA/W interfaces |
| * Handle the SetURL flags |
| * Implement any other interfaces? Does any software actually use them? |
| * |
| * The installer for the Zuma Deluxe Popcap game is good for testing. |
| */ |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| |
| #define NONAMELESSUNION |
| #include "wine/debug.h" |
| #include "shdocvw.h" |
| #include "objidl.h" |
| #include "shobjidl.h" |
| #include "intshcut.h" |
| #include "shellapi.h" |
| #include "winreg.h" |
| #include "shlwapi.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(shdocvw); |
| |
| typedef struct |
| { |
| IUniformResourceLocatorA IUniformResourceLocatorA_iface; |
| IUniformResourceLocatorW IUniformResourceLocatorW_iface; |
| IPersistFile IPersistFile_iface; |
| IPropertySetStorage IPropertySetStorage_iface; |
| |
| LONG refCount; |
| |
| IPropertySetStorage *property_set_storage; |
| WCHAR *url; |
| BOOLEAN isDirty; |
| LPOLESTR currentFile; |
| } InternetShortcut; |
| |
| /* utility functions */ |
| |
| static inline InternetShortcut* impl_from_IUniformResourceLocatorA(IUniformResourceLocatorA *iface) |
| { |
| return CONTAINING_RECORD(iface, InternetShortcut, IUniformResourceLocatorA_iface); |
| } |
| |
| static inline InternetShortcut* impl_from_IUniformResourceLocatorW(IUniformResourceLocatorW *iface) |
| { |
| return CONTAINING_RECORD(iface, InternetShortcut, IUniformResourceLocatorW_iface); |
| } |
| |
| static inline InternetShortcut* impl_from_IPersistFile(IPersistFile *iface) |
| { |
| return CONTAINING_RECORD(iface, InternetShortcut, IPersistFile_iface); |
| } |
| |
| static inline InternetShortcut* impl_from_IPropertySetStorage(IPropertySetStorage *iface) |
| { |
| return (InternetShortcut*)((char*)iface - FIELD_OFFSET(InternetShortcut, IPropertySetStorage_iface)); |
| } |
| |
| static BOOL run_winemenubuilder( const WCHAR *args ) |
| { |
| static const WCHAR menubuilder[] = {'\\','w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',0}; |
| LONG len; |
| LPWSTR buffer; |
| STARTUPINFOW si; |
| PROCESS_INFORMATION pi; |
| BOOL ret; |
| WCHAR app[MAX_PATH]; |
| void *redir; |
| |
| GetSystemDirectoryW( app, MAX_PATH - sizeof(menubuilder)/sizeof(WCHAR) ); |
| strcatW( app, menubuilder ); |
| |
| len = (strlenW( app ) + strlenW( args ) + 1) * sizeof(WCHAR); |
| buffer = heap_alloc( len ); |
| if( !buffer ) |
| return FALSE; |
| |
| strcpyW( buffer, app ); |
| strcatW( buffer, args ); |
| |
| TRACE("starting %s\n",debugstr_w(buffer)); |
| |
| memset(&si, 0, sizeof(si)); |
| si.cb = sizeof(si); |
| |
| Wow64DisableWow64FsRedirection( &redir ); |
| ret = CreateProcessW( app, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ); |
| Wow64RevertWow64FsRedirection( redir ); |
| |
| heap_free( buffer ); |
| |
| if (ret) |
| { |
| CloseHandle( pi.hProcess ); |
| CloseHandle( pi.hThread ); |
| } |
| |
| return ret; |
| } |
| |
| static BOOL StartLinkProcessor( LPCOLESTR szLink ) |
| { |
| static const WCHAR szFormat[] = { ' ','-','w',' ','-','u',' ','"','%','s','"',0 }; |
| LONG len; |
| LPWSTR buffer; |
| BOOL ret; |
| |
| len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR); |
| buffer = heap_alloc( len ); |
| if( !buffer ) |
| return FALSE; |
| |
| wsprintfW( buffer, szFormat, szLink ); |
| ret = run_winemenubuilder( buffer ); |
| heap_free( buffer ); |
| return ret; |
| } |
| |
| /* interface functions */ |
| |
| static HRESULT Unknown_QueryInterface(InternetShortcut *This, REFIID riid, PVOID *ppvObject) |
| { |
| TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject); |
| *ppvObject = NULL; |
| if (IsEqualGUID(&IID_IUnknown, riid)) |
| *ppvObject = &This->IUniformResourceLocatorA_iface; |
| else if (IsEqualGUID(&IID_IUniformResourceLocatorA, riid)) |
| *ppvObject = &This->IUniformResourceLocatorA_iface; |
| else if (IsEqualGUID(&IID_IUniformResourceLocatorW, riid)) |
| *ppvObject = &This->IUniformResourceLocatorW_iface; |
| else if (IsEqualGUID(&IID_IPersistFile, riid)) |
| *ppvObject = &This->IPersistFile_iface; |
| else if (IsEqualGUID(&IID_IPropertySetStorage, riid)) |
| *ppvObject = &This->IPropertySetStorage_iface; |
| else if (IsEqualGUID(&IID_IShellLinkA, riid)) |
| { |
| FIXME("The IShellLinkA interface is not yet supported by InternetShortcut\n"); |
| return E_NOINTERFACE; |
| } |
| else if (IsEqualGUID(&IID_IShellLinkW, riid)) |
| { |
| FIXME("The IShellLinkW interface is not yet supported by InternetShortcut\n"); |
| return E_NOINTERFACE; |
| } |
| else |
| { |
| FIXME("Interface with GUID %s not yet implemented by InternetShortcut\n", debugstr_guid(riid)); |
| return E_NOINTERFACE; |
| } |
| IUnknown_AddRef((IUnknown*)*ppvObject); |
| return S_OK; |
| } |
| |
| static ULONG Unknown_AddRef(InternetShortcut *This) |
| { |
| TRACE("(%p)\n", This); |
| return InterlockedIncrement(&This->refCount); |
| } |
| |
| static ULONG Unknown_Release(InternetShortcut *This) |
| { |
| ULONG count; |
| TRACE("(%p)\n", This); |
| count = InterlockedDecrement(&This->refCount); |
| if (count == 0) |
| { |
| CoTaskMemFree(This->url); |
| CoTaskMemFree(This->currentFile); |
| IPropertySetStorage_Release(This->property_set_storage); |
| heap_free(This); |
| SHDOCVW_UnlockModule(); |
| } |
| return count; |
| } |
| |
| static HRESULT WINAPI UniformResourceLocatorW_QueryInterface(IUniformResourceLocatorW *url, REFIID riid, PVOID *ppvObject) |
| { |
| InternetShortcut *This = impl_from_IUniformResourceLocatorW(url); |
| TRACE("(%p, %s, %p)\n", url, debugstr_guid(riid), ppvObject); |
| return Unknown_QueryInterface(This, riid, ppvObject); |
| } |
| |
| static ULONG WINAPI UniformResourceLocatorW_AddRef(IUniformResourceLocatorW *url) |
| { |
| InternetShortcut *This = impl_from_IUniformResourceLocatorW(url); |
| TRACE("(%p)\n", url); |
| return Unknown_AddRef(This); |
| } |
| |
| static ULONG WINAPI UniformResourceLocatorW_Release(IUniformResourceLocatorW *url) |
| { |
| InternetShortcut *This = impl_from_IUniformResourceLocatorW(url); |
| TRACE("(%p)\n", url); |
| return Unknown_Release(This); |
| } |
| |
| static HRESULT WINAPI UniformResourceLocatorW_SetUrl(IUniformResourceLocatorW *url, LPCWSTR pcszURL, DWORD dwInFlags) |
| { |
| WCHAR *newURL = NULL; |
| InternetShortcut *This = impl_from_IUniformResourceLocatorW(url); |
| TRACE("(%p, %s, 0x%x)\n", url, debugstr_w(pcszURL), dwInFlags); |
| if (dwInFlags != 0) |
| FIXME("ignoring unsupported flags 0x%x\n", dwInFlags); |
| if (pcszURL != NULL) |
| { |
| newURL = co_strdupW(pcszURL); |
| if (newURL == NULL) |
| return E_OUTOFMEMORY; |
| } |
| CoTaskMemFree(This->url); |
| This->url = newURL; |
| This->isDirty = TRUE; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI UniformResourceLocatorW_GetUrl(IUniformResourceLocatorW *url, LPWSTR *ppszURL) |
| { |
| InternetShortcut *This = impl_from_IUniformResourceLocatorW(url); |
| |
| TRACE("(%p, %p)\n", url, ppszURL); |
| |
| if (!This->url) { |
| *ppszURL = NULL; |
| return S_FALSE; |
| } |
| |
| *ppszURL = co_strdupW(This->url); |
| if (!*ppszURL) |
| return E_OUTOFMEMORY; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI UniformResourceLocatorW_InvokeCommand(IUniformResourceLocatorW *url, PURLINVOKECOMMANDINFOW pCommandInfo) |
| { |
| InternetShortcut *This = impl_from_IUniformResourceLocatorW(url); |
| WCHAR app[64]; |
| HKEY hkey; |
| static const WCHAR wszURLProtocol[] = {'U','R','L',' ','P','r','o','t','o','c','o','l',0}; |
| SHELLEXECUTEINFOW sei; |
| DWORD res, type; |
| HRESULT hres; |
| |
| TRACE("%p %p\n", This, pCommandInfo ); |
| |
| if (pCommandInfo->dwcbSize < sizeof (URLINVOKECOMMANDINFOW)) |
| return E_INVALIDARG; |
| |
| if (pCommandInfo->dwFlags != IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB) |
| { |
| FIXME("(%p, %p): non-default verbs not implemented\n", url, pCommandInfo); |
| return E_NOTIMPL; |
| } |
| |
| hres = CoInternetParseUrl(This->url, PARSE_SCHEMA, 0, app, sizeof(app)/sizeof(WCHAR), NULL, 0); |
| if(FAILED(hres)) |
| return E_FAIL; |
| |
| res = RegOpenKeyW(HKEY_CLASSES_ROOT, app, &hkey); |
| if(res != ERROR_SUCCESS) |
| return E_FAIL; |
| |
| res = RegQueryValueExW(hkey, wszURLProtocol, NULL, &type, NULL, NULL); |
| RegCloseKey(hkey); |
| if(res != ERROR_SUCCESS || type != REG_SZ) |
| return E_FAIL; |
| |
| memset(&sei, 0, sizeof(sei)); |
| sei.cbSize = sizeof(sei); |
| sei.lpFile = This->url; |
| sei.nShow = SW_SHOW; |
| |
| if( ShellExecuteExW(&sei) ) |
| return S_OK; |
| else |
| return E_FAIL; |
| } |
| |
| static HRESULT WINAPI UniformResourceLocatorA_QueryInterface(IUniformResourceLocatorA *url, REFIID riid, PVOID *ppvObject) |
| { |
| InternetShortcut *This = impl_from_IUniformResourceLocatorA(url); |
| TRACE("(%p, %s, %p)\n", url, debugstr_guid(riid), ppvObject); |
| return Unknown_QueryInterface(This, riid, ppvObject); |
| } |
| |
| static ULONG WINAPI UniformResourceLocatorA_AddRef(IUniformResourceLocatorA *url) |
| { |
| InternetShortcut *This = impl_from_IUniformResourceLocatorA(url); |
| TRACE("(%p)\n", url); |
| return Unknown_AddRef(This); |
| } |
| |
| static ULONG WINAPI UniformResourceLocatorA_Release(IUniformResourceLocatorA *url) |
| { |
| InternetShortcut *This = impl_from_IUniformResourceLocatorA(url); |
| TRACE("(%p)\n", url); |
| return Unknown_Release(This); |
| } |
| |
| static HRESULT WINAPI UniformResourceLocatorA_SetUrl(IUniformResourceLocatorA *url, LPCSTR pcszURL, DWORD dwInFlags) |
| { |
| WCHAR *newURL = NULL; |
| InternetShortcut *This = impl_from_IUniformResourceLocatorA(url); |
| TRACE("(%p, %s, 0x%x)\n", url, debugstr_a(pcszURL), dwInFlags); |
| if (dwInFlags != 0) |
| FIXME("ignoring unsupported flags 0x%x\n", dwInFlags); |
| if (pcszURL != NULL) |
| { |
| newURL = co_strdupAtoW(pcszURL); |
| if (newURL == NULL) |
| return E_OUTOFMEMORY; |
| } |
| CoTaskMemFree(This->url); |
| This->url = newURL; |
| This->isDirty = TRUE; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI UniformResourceLocatorA_GetUrl(IUniformResourceLocatorA *url, LPSTR *ppszURL) |
| { |
| InternetShortcut *This = impl_from_IUniformResourceLocatorA(url); |
| |
| TRACE("(%p, %p)\n", url, ppszURL); |
| |
| if (!This->url) { |
| *ppszURL = NULL; |
| return S_FALSE; |
| |
| } |
| |
| *ppszURL = co_strdupWtoA(This->url); |
| if (!*ppszURL) |
| return E_OUTOFMEMORY; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI UniformResourceLocatorA_InvokeCommand(IUniformResourceLocatorA *url, PURLINVOKECOMMANDINFOA pCommandInfo) |
| { |
| URLINVOKECOMMANDINFOW wideCommandInfo; |
| int len; |
| WCHAR *wideVerb; |
| HRESULT res; |
| InternetShortcut *This = impl_from_IUniformResourceLocatorA(url); |
| |
| wideCommandInfo.dwcbSize = sizeof wideCommandInfo; |
| wideCommandInfo.dwFlags = pCommandInfo->dwFlags; |
| wideCommandInfo.hwndParent = pCommandInfo->hwndParent; |
| |
| len = MultiByteToWideChar(CP_ACP, 0, pCommandInfo->pcszVerb, -1, NULL, 0); |
| wideVerb = heap_alloc(len * sizeof(WCHAR)); |
| MultiByteToWideChar(CP_ACP, 0, pCommandInfo->pcszVerb, -1, wideVerb, len); |
| |
| wideCommandInfo.pcszVerb = wideVerb; |
| |
| res = UniformResourceLocatorW_InvokeCommand(&This->IUniformResourceLocatorW_iface, &wideCommandInfo); |
| heap_free(wideVerb); |
| |
| return res; |
| } |
| |
| static HRESULT WINAPI PersistFile_QueryInterface(IPersistFile *pFile, REFIID riid, PVOID *ppvObject) |
| { |
| InternetShortcut *This = impl_from_IPersistFile(pFile); |
| TRACE("(%p, %s, %p)\n", pFile, debugstr_guid(riid), ppvObject); |
| return Unknown_QueryInterface(This, riid, ppvObject); |
| } |
| |
| static ULONG WINAPI PersistFile_AddRef(IPersistFile *pFile) |
| { |
| InternetShortcut *This = impl_from_IPersistFile(pFile); |
| TRACE("(%p)\n", pFile); |
| return Unknown_AddRef(This); |
| } |
| |
| static ULONG WINAPI PersistFile_Release(IPersistFile *pFile) |
| { |
| InternetShortcut *This = impl_from_IPersistFile(pFile); |
| TRACE("(%p)\n", pFile); |
| return Unknown_Release(This); |
| } |
| |
| static HRESULT WINAPI PersistFile_GetClassID(IPersistFile *pFile, CLSID *pClassID) |
| { |
| TRACE("(%p, %p)\n", pFile, pClassID); |
| *pClassID = CLSID_InternetShortcut; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI PersistFile_IsDirty(IPersistFile *pFile) |
| { |
| InternetShortcut *This = impl_from_IPersistFile(pFile); |
| TRACE("(%p)\n", pFile); |
| return This->isDirty ? S_OK : S_FALSE; |
| } |
| |
| /* A helper function: Allocate and fill rString. Return number of bytes read. */ |
| static DWORD get_profile_string(LPCWSTR lpAppName, LPCWSTR lpKeyName, |
| LPCWSTR lpFileName, WCHAR **rString ) |
| { |
| DWORD r = 0; |
| DWORD len = 128; |
| WCHAR *buffer; |
| |
| buffer = CoTaskMemAlloc(len * sizeof(*buffer)); |
| if (buffer != NULL) |
| { |
| r = GetPrivateProfileStringW(lpAppName, lpKeyName, NULL, buffer, len, lpFileName); |
| while (r == len-1) |
| { |
| WCHAR *realloc_buf; |
| |
| len *= 2; |
| realloc_buf = CoTaskMemRealloc(buffer, len * sizeof(*buffer)); |
| if (realloc_buf == NULL) |
| { |
| CoTaskMemFree(buffer); |
| *rString = NULL; |
| return 0; |
| } |
| buffer = realloc_buf; |
| |
| r = GetPrivateProfileStringW(lpAppName, lpKeyName, NULL, buffer, len, lpFileName); |
| } |
| } |
| |
| *rString = buffer; |
| return r; |
| } |
| |
| static HRESULT WINAPI PersistFile_Load(IPersistFile *pFile, LPCOLESTR pszFileName, DWORD dwMode) |
| { |
| WCHAR str_header[] = {'I','n','t','e','r','n','e','t','S','h','o','r','t','c','u','t',0}; |
| WCHAR str_URL[] = {'U','R','L',0}; |
| WCHAR str_iconfile[] = {'i','c','o','n','f','i','l','e',0}; |
| WCHAR str_iconindex[] = {'i','c','o','n','i','n','d','e','x',0}; |
| WCHAR *filename = NULL; |
| HRESULT hr; |
| InternetShortcut *This = impl_from_IPersistFile(pFile); |
| TRACE("(%p, %s, 0x%x)\n", pFile, debugstr_w(pszFileName), dwMode); |
| if (dwMode != 0) |
| FIXME("ignoring unimplemented mode 0x%x\n", dwMode); |
| filename = co_strdupW(pszFileName); |
| if (filename != NULL) |
| { |
| DWORD r; |
| WCHAR *url; |
| |
| r = get_profile_string(str_header, str_URL, pszFileName, &url); |
| |
| if (url == NULL) |
| { |
| hr = E_OUTOFMEMORY; |
| CoTaskMemFree(filename); |
| } |
| else if (r == 0) |
| { |
| hr = E_FAIL; |
| CoTaskMemFree(filename); |
| } |
| else |
| { |
| hr = S_OK; |
| CoTaskMemFree(This->currentFile); |
| This->currentFile = filename; |
| CoTaskMemFree(This->url); |
| This->url = url; |
| This->isDirty = FALSE; |
| } |
| |
| /* Now we're going to read in the iconfile and iconindex. |
| If we don't find them, that's not a failure case -- it's possible |
| that they just aren't in there. */ |
| if (SUCCEEDED(hr)) |
| { |
| IPropertyStorage *pPropStg; |
| WCHAR *iconfile; |
| WCHAR *iconindexstring; |
| hr = IPropertySetStorage_Open(This->property_set_storage, &FMTID_Intshcut, |
| STGM_READWRITE | STGM_SHARE_EXCLUSIVE, |
| &pPropStg); |
| |
| r = get_profile_string(str_header, str_iconfile, pszFileName, &iconfile); |
| if (iconfile != NULL) |
| { |
| PROPSPEC ps; |
| PROPVARIANT pv; |
| ps.ulKind = PRSPEC_PROPID; |
| ps.u.propid = PID_IS_ICONFILE; |
| pv.vt = VT_LPWSTR; |
| pv.u.pwszVal = iconfile; |
| hr = IPropertyStorage_WriteMultiple(pPropStg, 1, &ps, &pv, 0); |
| if (FAILED(hr)) |
| { |
| TRACE("Failed to store the iconfile to our property storage. hr = 0x%x\n", hr); |
| } |
| |
| CoTaskMemFree(iconfile); |
| } |
| |
| r = get_profile_string(str_header, str_iconindex, pszFileName, &iconindexstring); |
| |
| if (iconindexstring != NULL) |
| { |
| int iconindex; |
| PROPSPEC ps; |
| PROPVARIANT pv; |
| char *iconindexastring = co_strdupWtoA(iconindexstring); |
| sscanf(iconindexastring, "%d", &iconindex); |
| CoTaskMemFree(iconindexastring); |
| ps.ulKind = PRSPEC_PROPID; |
| ps.u.propid = PID_IS_ICONINDEX; |
| pv.vt = VT_I4; |
| pv.u.iVal = iconindex; |
| hr = IPropertyStorage_WriteMultiple(pPropStg, 1, &ps, &pv, 0); |
| if (FAILED(hr)) |
| { |
| TRACE("Failed to store the iconindex to our property storage. hr = 0x%x\n", hr); |
| } |
| |
| CoTaskMemFree(iconindexstring); |
| } |
| |
| IPropertyStorage_Release(pPropStg); |
| } |
| else |
| hr = E_OUTOFMEMORY; |
| } |
| else |
| hr = E_OUTOFMEMORY; |
| return hr; |
| } |
| |
| static HRESULT WINAPI PersistFile_Save(IPersistFile *pFile, LPCOLESTR pszFileName, BOOL fRemember) |
| { |
| HRESULT hr = S_OK; |
| INT len; |
| CHAR *url; |
| InternetShortcut *This = impl_from_IPersistFile(pFile); |
| |
| TRACE("(%p, %s, %d)\n", pFile, debugstr_w(pszFileName), fRemember); |
| |
| if (pszFileName != NULL && fRemember) |
| { |
| LPOLESTR oldFile = This->currentFile; |
| This->currentFile = co_strdupW(pszFileName); |
| if (This->currentFile == NULL) |
| { |
| This->currentFile = oldFile; |
| return E_OUTOFMEMORY; |
| } |
| CoTaskMemFree(oldFile); |
| } |
| if (This->url == NULL) |
| return E_FAIL; |
| |
| /* Windows seems to always write: |
| * ASCII "[InternetShortcut]" headers |
| * ASCII names in "name=value" pairs |
| * An ASCII (probably UTF8?) value in "URL=..." |
| */ |
| len = WideCharToMultiByte(CP_UTF8, 0, This->url, -1, NULL, 0, 0, 0); |
| url = heap_alloc(len); |
| if (url != NULL) |
| { |
| HANDLE file; |
| WideCharToMultiByte(CP_UTF8, 0, This->url, -1, url, len, 0, 0); |
| file = CreateFileW(pszFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
| if (file != INVALID_HANDLE_VALUE) |
| { |
| DWORD bytesWritten; |
| char *iconfile; |
| char str_header[] = "[InternetShortcut]"; |
| char str_URL[] = "URL="; |
| char str_ICONFILE[] = "ICONFILE="; |
| char str_eol[] = "\r\n"; |
| IPropertyStorage *pPropStgRead; |
| PROPSPEC ps[2]; |
| PROPVARIANT pvread[2]; |
| ps[0].ulKind = PRSPEC_PROPID; |
| ps[0].u.propid = PID_IS_ICONFILE; |
| ps[1].ulKind = PRSPEC_PROPID; |
| ps[1].u.propid = PID_IS_ICONINDEX; |
| |
| WriteFile(file, str_header, lstrlenA(str_header), &bytesWritten, NULL); |
| WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL); |
| WriteFile(file, str_URL, lstrlenA(str_URL), &bytesWritten, NULL); |
| WriteFile(file, url, lstrlenA(url), &bytesWritten, NULL); |
| WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL); |
| |
| hr = IPropertySetStorage_Open(This->property_set_storage, &FMTID_Intshcut, STGM_READ|STGM_SHARE_EXCLUSIVE, &pPropStgRead); |
| if SUCCEEDED(hr) |
| { |
| hr = IPropertyStorage_ReadMultiple(pPropStgRead, 2, ps, pvread); |
| if (hr == S_FALSE) |
| { |
| /* None of the properties are present, that's ok */ |
| hr = S_OK; |
| IPropertyStorage_Release(pPropStgRead); |
| } |
| else if SUCCEEDED(hr) |
| { |
| char indexString[50]; |
| len = WideCharToMultiByte(CP_UTF8, 0, pvread[0].u.pwszVal, -1, NULL, 0, 0, 0); |
| iconfile = heap_alloc(len); |
| if (iconfile != NULL) |
| { |
| WideCharToMultiByte(CP_UTF8, 0, pvread[0].u.pwszVal, -1, iconfile, len, 0, 0); |
| WriteFile(file, str_ICONFILE, lstrlenA(str_ICONFILE), &bytesWritten, NULL); |
| WriteFile(file, iconfile, lstrlenA(iconfile), &bytesWritten, NULL); |
| WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL); |
| } |
| |
| sprintf(indexString, "ICONINDEX=%d", pvread[1].u.iVal); |
| WriteFile(file, indexString, lstrlenA(indexString), &bytesWritten, NULL); |
| WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL); |
| |
| IPropertyStorage_Release(pPropStgRead); |
| PropVariantClear(&pvread[0]); |
| PropVariantClear(&pvread[1]); |
| } |
| else |
| { |
| TRACE("Unable to read properties.\n"); |
| } |
| } |
| else |
| { |
| TRACE("Unable to get the IPropertyStorage.\n"); |
| } |
| |
| CloseHandle(file); |
| if (pszFileName == NULL || fRemember) |
| This->isDirty = FALSE; |
| StartLinkProcessor(pszFileName); |
| } |
| else |
| hr = E_FAIL; |
| heap_free(url); |
| } |
| else |
| hr = E_OUTOFMEMORY; |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *pFile, LPCOLESTR pszFileName) |
| { |
| FIXME("(%p, %p): stub\n", pFile, pszFileName); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *pFile, LPOLESTR *ppszFileName) |
| { |
| HRESULT hr = S_OK; |
| InternetShortcut *This = impl_from_IPersistFile(pFile); |
| TRACE("(%p, %p)\n", pFile, ppszFileName); |
| if (This->currentFile == NULL) |
| *ppszFileName = NULL; |
| else |
| { |
| *ppszFileName = co_strdupW(This->currentFile); |
| if (*ppszFileName == NULL) |
| hr = E_OUTOFMEMORY; |
| } |
| return hr; |
| } |
| |
| static HRESULT WINAPI PropertySetStorage_QueryInterface(IPropertySetStorage *iface, REFIID riid, PVOID *ppvObject) |
| { |
| InternetShortcut *This = impl_from_IPropertySetStorage(iface); |
| TRACE("(%p)\n", iface); |
| return Unknown_QueryInterface(This, riid, ppvObject); |
| } |
| |
| static ULONG WINAPI PropertySetStorage_AddRef(IPropertySetStorage *iface) |
| { |
| InternetShortcut *This = impl_from_IPropertySetStorage(iface); |
| TRACE("(%p)\n", iface); |
| return Unknown_AddRef(This); |
| } |
| |
| static ULONG WINAPI PropertySetStorage_Release(IPropertySetStorage *iface) |
| { |
| InternetShortcut *This = impl_from_IPropertySetStorage(iface); |
| TRACE("(%p)\n", iface); |
| return Unknown_Release(This); |
| } |
| |
| static HRESULT WINAPI PropertySetStorage_Create( |
| IPropertySetStorage* iface, |
| REFFMTID rfmtid, |
| const CLSID *pclsid, |
| DWORD grfFlags, |
| DWORD grfMode, |
| IPropertyStorage **ppprstg) |
| { |
| InternetShortcut *This = impl_from_IPropertySetStorage(iface); |
| TRACE("(%s, %p, 0x%x, 0x%x, %p)\n", debugstr_guid(rfmtid), pclsid, grfFlags, grfMode, ppprstg); |
| |
| return IPropertySetStorage_Create(This->property_set_storage, |
| rfmtid, |
| pclsid, |
| grfFlags, |
| grfMode, |
| ppprstg); |
| } |
| |
| static HRESULT WINAPI PropertySetStorage_Open( |
| IPropertySetStorage* iface, |
| REFFMTID rfmtid, |
| DWORD grfMode, |
| IPropertyStorage **ppprstg) |
| { |
| InternetShortcut *This = impl_from_IPropertySetStorage(iface); |
| TRACE("(%s, 0x%x, %p)\n", debugstr_guid(rfmtid), grfMode, ppprstg); |
| |
| /* Note: The |STGM_SHARE_EXCLUSIVE is to cope with a bug in the implementation. Should be fixed in ole32. */ |
| return IPropertySetStorage_Open(This->property_set_storage, |
| rfmtid, |
| grfMode|STGM_SHARE_EXCLUSIVE, |
| ppprstg); |
| } |
| |
| static HRESULT WINAPI PropertySetStorage_Delete(IPropertySetStorage *iface, REFFMTID rfmtid) |
| { |
| InternetShortcut *This = impl_from_IPropertySetStorage(iface); |
| TRACE("(%s)\n", debugstr_guid(rfmtid)); |
| |
| |
| return IPropertySetStorage_Delete(This->property_set_storage, |
| rfmtid); |
| } |
| |
| static HRESULT WINAPI PropertySetStorage_Enum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **ppenum) |
| { |
| FIXME("(%p): stub\n", ppenum); |
| return E_NOTIMPL; |
| } |
| |
| static const IUniformResourceLocatorWVtbl uniformResourceLocatorWVtbl = { |
| UniformResourceLocatorW_QueryInterface, |
| UniformResourceLocatorW_AddRef, |
| UniformResourceLocatorW_Release, |
| UniformResourceLocatorW_SetUrl, |
| UniformResourceLocatorW_GetUrl, |
| UniformResourceLocatorW_InvokeCommand |
| }; |
| |
| static const IUniformResourceLocatorAVtbl uniformResourceLocatorAVtbl = { |
| UniformResourceLocatorA_QueryInterface, |
| UniformResourceLocatorA_AddRef, |
| UniformResourceLocatorA_Release, |
| UniformResourceLocatorA_SetUrl, |
| UniformResourceLocatorA_GetUrl, |
| UniformResourceLocatorA_InvokeCommand |
| }; |
| |
| static const IPersistFileVtbl persistFileVtbl = { |
| PersistFile_QueryInterface, |
| PersistFile_AddRef, |
| PersistFile_Release, |
| PersistFile_GetClassID, |
| PersistFile_IsDirty, |
| PersistFile_Load, |
| PersistFile_Save, |
| PersistFile_SaveCompleted, |
| PersistFile_GetCurFile |
| }; |
| |
| static const IPropertySetStorageVtbl propertySetStorageVtbl = { |
| PropertySetStorage_QueryInterface, |
| PropertySetStorage_AddRef, |
| PropertySetStorage_Release, |
| PropertySetStorage_Create, |
| PropertySetStorage_Open, |
| PropertySetStorage_Delete, |
| PropertySetStorage_Enum |
| }; |
| |
| static InternetShortcut *create_shortcut(void) |
| { |
| InternetShortcut *newshortcut; |
| |
| newshortcut = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(InternetShortcut)); |
| if (newshortcut) |
| { |
| HRESULT hr; |
| IPropertyStorage *dummy; |
| |
| newshortcut->IUniformResourceLocatorA_iface.lpVtbl = &uniformResourceLocatorAVtbl; |
| newshortcut->IUniformResourceLocatorW_iface.lpVtbl = &uniformResourceLocatorWVtbl; |
| newshortcut->IPersistFile_iface.lpVtbl = &persistFileVtbl; |
| newshortcut->IPropertySetStorage_iface.lpVtbl = &propertySetStorageVtbl; |
| newshortcut->refCount = 0; |
| hr = StgCreateStorageEx(NULL, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, STGFMT_STORAGE, 0, NULL, NULL, &IID_IPropertySetStorage, (void **) &newshortcut->property_set_storage); |
| if FAILED(hr) |
| { |
| TRACE("Failed to create the storage object needed for the shortcut.\n"); |
| heap_free(newshortcut); |
| return NULL; |
| } |
| |
| hr = IPropertySetStorage_Create(newshortcut->property_set_storage, &FMTID_Intshcut, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &dummy); |
| if FAILED(hr) |
| { |
| TRACE("Failed to create the property object needed for the shortcut.\n"); |
| IPropertySetStorage_Release(newshortcut->property_set_storage); |
| heap_free(newshortcut); |
| return NULL; |
| } |
| IPropertySetStorage_Release(dummy); |
| } |
| |
| return newshortcut; |
| } |
| |
| HRESULT InternetShortcut_Create(IUnknown *pOuter, REFIID riid, void **ppv) |
| { |
| InternetShortcut *This; |
| HRESULT hr; |
| |
| TRACE("(%p, %s, %p)\n", pOuter, debugstr_guid(riid), ppv); |
| |
| *ppv = NULL; |
| |
| if(pOuter) |
| return CLASS_E_NOAGGREGATION; |
| |
| This = create_shortcut(); |
| if (This) |
| { |
| hr = Unknown_QueryInterface(This, riid, ppv); |
| if (SUCCEEDED(hr)) |
| SHDOCVW_LockModule(); |
| else |
| heap_free(This); |
| return hr; |
| } |
| else |
| return E_OUTOFMEMORY; |
| } |
| |
| |
| /********************************************************************** |
| * OpenURL (SHDOCVW.@) |
| */ |
| void WINAPI OpenURL(HWND hWnd, HINSTANCE hInst, LPCSTR lpcstrUrl, int nShowCmd) |
| { |
| InternetShortcut *shortcut; |
| WCHAR* urlfilepath = NULL; |
| shortcut = create_shortcut(); |
| |
| if (shortcut) |
| { |
| int len; |
| |
| len = MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, NULL, 0); |
| urlfilepath = heap_alloc(len * sizeof(WCHAR)); |
| MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, urlfilepath, len); |
| |
| if(SUCCEEDED(IPersistFile_Load(&shortcut->IPersistFile_iface, urlfilepath, 0))) |
| { |
| URLINVOKECOMMANDINFOW ici; |
| |
| memset( &ici, 0, sizeof ici ); |
| ici.dwcbSize = sizeof ici; |
| ici.dwFlags = IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB; |
| ici.hwndParent = hWnd; |
| |
| if(FAILED(UniformResourceLocatorW_InvokeCommand(&shortcut->IUniformResourceLocatorW_iface, (PURLINVOKECOMMANDINFOW) &ici))) |
| TRACE("failed to open URL: %s\n", debugstr_a(lpcstrUrl)); |
| } |
| |
| heap_free(shortcut); |
| heap_free(urlfilepath); |
| } |
| } |