xref: /reactos/dll/win32/ieframe/intshcut.c (revision 4561998a)
1 /*
2  * Copyright 2008 Damjan Jovanovic
3  *
4  * ShellLink's barely documented cousin that handles URLs.
5  *
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.
10  *
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.
15  *
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
19  */
20 
21 /*
22  * TODO:
23  * Implement the IShellLinkA/W interfaces
24  * Handle the SetURL flags
25  * Implement any other interfaces? Does any software actually use them?
26  *
27  * The installer for the Zuma Deluxe Popcap game is good for testing.
28  */
29 
30 #include <stdio.h>
31 
32 #define NONAMELESSUNION
33 
34 #include "ieframe.h"
35 
36 #include "shlobj.h"
37 #include "shobjidl.h"
38 #include "intshcut.h"
39 #include "shellapi.h"
40 #include "winreg.h"
41 #include "shlwapi.h"
42 #include "shlguid.h"
43 
44 #include "wine/debug.h"
45 
46 WINE_DEFAULT_DEBUG_CHANNEL(ieframe);
47 
48 typedef struct
49 {
50     IUniformResourceLocatorA IUniformResourceLocatorA_iface;
51     IUniformResourceLocatorW IUniformResourceLocatorW_iface;
52     IPersistFile IPersistFile_iface;
53     IPropertySetStorage IPropertySetStorage_iface;
54 
55     LONG refCount;
56 
57     IPropertySetStorage *property_set_storage;
58     WCHAR *url;
59     BOOLEAN isDirty;
60     LPOLESTR currentFile;
61 } InternetShortcut;
62 
63 /* utility functions */
64 
65 static inline InternetShortcut* impl_from_IUniformResourceLocatorA(IUniformResourceLocatorA *iface)
66 {
67     return CONTAINING_RECORD(iface, InternetShortcut, IUniformResourceLocatorA_iface);
68 }
69 
70 static inline InternetShortcut* impl_from_IUniformResourceLocatorW(IUniformResourceLocatorW *iface)
71 {
72     return CONTAINING_RECORD(iface, InternetShortcut, IUniformResourceLocatorW_iface);
73 }
74 
75 static inline InternetShortcut* impl_from_IPersistFile(IPersistFile *iface)
76 {
77     return CONTAINING_RECORD(iface, InternetShortcut, IPersistFile_iface);
78 }
79 
80 static inline InternetShortcut* impl_from_IPropertySetStorage(IPropertySetStorage *iface)
81 {
82     return CONTAINING_RECORD(iface, InternetShortcut, IPropertySetStorage_iface);
83 }
84 
85 static BOOL run_winemenubuilder( const WCHAR *args )
86 {
87     static const WCHAR menubuilder[] = {'\\','w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',0};
88     LONG len;
89     LPWSTR buffer;
90     STARTUPINFOW si;
91     PROCESS_INFORMATION pi;
92     BOOL ret;
93     WCHAR app[MAX_PATH];
94     void *redir;
95 
96     GetSystemDirectoryW( app, MAX_PATH - ARRAY_SIZE( menubuilder ));
97     strcatW( app, menubuilder );
98 
99     len = (strlenW( app ) + strlenW( args ) + 1) * sizeof(WCHAR);
100     buffer = heap_alloc( len );
101     if( !buffer )
102         return FALSE;
103 
104     strcpyW( buffer, app );
105     strcatW( buffer, args );
106 
107     TRACE("starting %s\n",debugstr_w(buffer));
108 
109     memset(&si, 0, sizeof(si));
110     si.cb = sizeof(si);
111 
112     Wow64DisableWow64FsRedirection( &redir );
113     ret = CreateProcessW( app, buffer, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi );
114     Wow64RevertWow64FsRedirection( redir );
115 
116     heap_free( buffer );
117 
118     if (ret)
119     {
120         CloseHandle( pi.hProcess );
121         CloseHandle( pi.hThread );
122     }
123 
124     return ret;
125 }
126 
127 static BOOL StartLinkProcessor( LPCOLESTR szLink )
128 {
129     static const WCHAR szFormat[] = { ' ','-','w',' ','-','u',' ','"','%','s','"',0 };
130     LONG len;
131     LPWSTR buffer;
132     BOOL ret;
133 
134     len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
135     buffer = heap_alloc( len );
136     if( !buffer )
137         return FALSE;
138 
139     sprintfW( buffer, szFormat, szLink );
140     ret = run_winemenubuilder( buffer );
141     heap_free( buffer );
142     return ret;
143 }
144 
145 /* interface functions */
146 
147 static HRESULT Unknown_QueryInterface(InternetShortcut *This, REFIID riid, PVOID *ppvObject)
148 {
149     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
150     *ppvObject = NULL;
151     if (IsEqualGUID(&IID_IUnknown, riid))
152         *ppvObject = &This->IUniformResourceLocatorA_iface;
153     else if (IsEqualGUID(&IID_IUniformResourceLocatorA, riid))
154         *ppvObject = &This->IUniformResourceLocatorA_iface;
155     else if (IsEqualGUID(&IID_IUniformResourceLocatorW, riid))
156         *ppvObject = &This->IUniformResourceLocatorW_iface;
157     else if (IsEqualGUID(&IID_IPersistFile, riid))
158         *ppvObject = &This->IPersistFile_iface;
159     else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
160         *ppvObject = &This->IPropertySetStorage_iface;
161     else if (IsEqualGUID(&IID_IShellLinkA, riid))
162     {
163         FIXME("The IShellLinkA interface is not yet supported by InternetShortcut\n");
164         return E_NOINTERFACE;
165     }
166     else if (IsEqualGUID(&IID_IShellLinkW, riid))
167     {
168         FIXME("The IShellLinkW interface is not yet supported by InternetShortcut\n");
169         return E_NOINTERFACE;
170     }
171     else
172     {
173         FIXME("Interface with GUID %s not yet implemented by InternetShortcut\n", debugstr_guid(riid));
174         return E_NOINTERFACE;
175     }
176     IUnknown_AddRef((IUnknown*)*ppvObject);
177     return S_OK;
178 }
179 
180 static ULONG Unknown_AddRef(InternetShortcut *This)
181 {
182     TRACE("(%p)\n", This);
183     return InterlockedIncrement(&This->refCount);
184 }
185 
186 static ULONG Unknown_Release(InternetShortcut *This)
187 {
188     ULONG count;
189     TRACE("(%p)\n", This);
190     count = InterlockedDecrement(&This->refCount);
191     if (count == 0)
192     {
193         CoTaskMemFree(This->url);
194         CoTaskMemFree(This->currentFile);
195         IPropertySetStorage_Release(This->property_set_storage);
196         heap_free(This);
197         unlock_module();
198     }
199     return count;
200 }
201 
202 static HRESULT WINAPI UniformResourceLocatorW_QueryInterface(IUniformResourceLocatorW *url, REFIID riid, PVOID *ppvObject)
203 {
204     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
205     TRACE("(%p, %s, %p)\n", url, debugstr_guid(riid), ppvObject);
206     return Unknown_QueryInterface(This, riid, ppvObject);
207 }
208 
209 static ULONG WINAPI UniformResourceLocatorW_AddRef(IUniformResourceLocatorW *url)
210 {
211     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
212     TRACE("(%p)\n", url);
213     return Unknown_AddRef(This);
214 }
215 
216 static ULONG WINAPI UniformResourceLocatorW_Release(IUniformResourceLocatorW *url)
217 {
218     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
219     TRACE("(%p)\n", url);
220     return Unknown_Release(This);
221 }
222 
223 static HRESULT WINAPI UniformResourceLocatorW_SetUrl(IUniformResourceLocatorW *url, LPCWSTR pcszURL, DWORD dwInFlags)
224 {
225     WCHAR *newURL = NULL;
226     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
227     TRACE("(%p, %s, 0x%x)\n", url, debugstr_w(pcszURL), dwInFlags);
228     if (dwInFlags != 0)
229         FIXME("ignoring unsupported flags 0x%x\n", dwInFlags);
230     if (pcszURL != NULL)
231     {
232         newURL = co_strdupW(pcszURL);
233         if (newURL == NULL)
234             return E_OUTOFMEMORY;
235     }
236     CoTaskMemFree(This->url);
237     This->url = newURL;
238     This->isDirty = TRUE;
239     return S_OK;
240 }
241 
242 static HRESULT WINAPI UniformResourceLocatorW_GetUrl(IUniformResourceLocatorW *url, LPWSTR *ppszURL)
243 {
244     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
245 
246     TRACE("(%p, %p)\n", url, ppszURL);
247 
248     if (!This->url) {
249         *ppszURL = NULL;
250         return S_FALSE;
251     }
252 
253     *ppszURL = co_strdupW(This->url);
254     if (!*ppszURL)
255         return E_OUTOFMEMORY;
256 
257     return S_OK;
258 }
259 
260 static HRESULT WINAPI UniformResourceLocatorW_InvokeCommand(IUniformResourceLocatorW *url, PURLINVOKECOMMANDINFOW pCommandInfo)
261 {
262     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
263     WCHAR app[64];
264     HKEY hkey;
265     static const WCHAR wszURLProtocol[] = {'U','R','L',' ','P','r','o','t','o','c','o','l',0};
266     SHELLEXECUTEINFOW sei;
267     DWORD res, type;
268     HRESULT hres;
269 
270     TRACE("%p %p\n", This, pCommandInfo );
271 
272     if (pCommandInfo->dwcbSize < sizeof (URLINVOKECOMMANDINFOW))
273         return E_INVALIDARG;
274 
275     if (pCommandInfo->dwFlags != IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB)
276     {
277         FIXME("(%p, %p): non-default verbs not implemented\n", url, pCommandInfo);
278         return E_NOTIMPL;
279     }
280 
281     hres = CoInternetParseUrl(This->url, PARSE_SCHEMA, 0, app, ARRAY_SIZE(app), NULL, 0);
282     if(FAILED(hres))
283         return E_FAIL;
284 
285     res = RegOpenKeyW(HKEY_CLASSES_ROOT, app, &hkey);
286     if(res != ERROR_SUCCESS)
287         return E_FAIL;
288 
289     res = RegQueryValueExW(hkey, wszURLProtocol, NULL, &type, NULL, NULL);
290     RegCloseKey(hkey);
291     if(res != ERROR_SUCCESS || type != REG_SZ)
292         return E_FAIL;
293 
294     memset(&sei, 0, sizeof(sei));
295     sei.cbSize = sizeof(sei);
296     sei.lpFile = This->url;
297     sei.nShow = SW_SHOW;
298 
299     if( ShellExecuteExW(&sei) )
300         return S_OK;
301     else
302         return E_FAIL;
303 }
304 
305 static HRESULT WINAPI UniformResourceLocatorA_QueryInterface(IUniformResourceLocatorA *url, REFIID riid, PVOID *ppvObject)
306 {
307     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
308     TRACE("(%p, %s, %p)\n", url, debugstr_guid(riid), ppvObject);
309     return Unknown_QueryInterface(This, riid, ppvObject);
310 }
311 
312 static ULONG WINAPI UniformResourceLocatorA_AddRef(IUniformResourceLocatorA *url)
313 {
314     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
315     TRACE("(%p)\n", url);
316     return Unknown_AddRef(This);
317 }
318 
319 static ULONG WINAPI UniformResourceLocatorA_Release(IUniformResourceLocatorA *url)
320 {
321     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
322     TRACE("(%p)\n", url);
323     return Unknown_Release(This);
324 }
325 
326 static HRESULT WINAPI UniformResourceLocatorA_SetUrl(IUniformResourceLocatorA *url, LPCSTR pcszURL, DWORD dwInFlags)
327 {
328     WCHAR *newURL = NULL;
329     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
330     TRACE("(%p, %s, 0x%x)\n", url, debugstr_a(pcszURL), dwInFlags);
331     if (dwInFlags != 0)
332         FIXME("ignoring unsupported flags 0x%x\n", dwInFlags);
333     if (pcszURL != NULL)
334     {
335         newURL = co_strdupAtoW(pcszURL);
336         if (newURL == NULL)
337             return E_OUTOFMEMORY;
338     }
339     CoTaskMemFree(This->url);
340     This->url = newURL;
341     This->isDirty = TRUE;
342     return S_OK;
343 }
344 
345 static HRESULT WINAPI UniformResourceLocatorA_GetUrl(IUniformResourceLocatorA *url, LPSTR *ppszURL)
346 {
347     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
348 
349     TRACE("(%p, %p)\n", url, ppszURL);
350 
351     if (!This->url) {
352         *ppszURL = NULL;
353         return S_FALSE;
354 
355     }
356 
357     *ppszURL = co_strdupWtoA(This->url);
358     if (!*ppszURL)
359         return E_OUTOFMEMORY;
360 
361     return S_OK;
362 }
363 
364 static HRESULT WINAPI UniformResourceLocatorA_InvokeCommand(IUniformResourceLocatorA *url, PURLINVOKECOMMANDINFOA pCommandInfo)
365 {
366     URLINVOKECOMMANDINFOW wideCommandInfo;
367     int len;
368     WCHAR *wideVerb;
369     HRESULT res;
370     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
371 
372     wideCommandInfo.dwcbSize = sizeof wideCommandInfo;
373     wideCommandInfo.dwFlags = pCommandInfo->dwFlags;
374     wideCommandInfo.hwndParent = pCommandInfo->hwndParent;
375 
376     len = MultiByteToWideChar(CP_ACP, 0, pCommandInfo->pcszVerb, -1, NULL, 0);
377     wideVerb = heap_alloc(len * sizeof(WCHAR));
378     MultiByteToWideChar(CP_ACP, 0, pCommandInfo->pcszVerb, -1, wideVerb, len);
379 
380     wideCommandInfo.pcszVerb = wideVerb;
381 
382     res = UniformResourceLocatorW_InvokeCommand(&This->IUniformResourceLocatorW_iface, &wideCommandInfo);
383     heap_free(wideVerb);
384 
385     return res;
386 }
387 
388 static HRESULT WINAPI PersistFile_QueryInterface(IPersistFile *pFile, REFIID riid, PVOID *ppvObject)
389 {
390     InternetShortcut *This = impl_from_IPersistFile(pFile);
391     TRACE("(%p, %s, %p)\n", pFile, debugstr_guid(riid), ppvObject);
392     return Unknown_QueryInterface(This, riid, ppvObject);
393 }
394 
395 static ULONG WINAPI PersistFile_AddRef(IPersistFile *pFile)
396 {
397     InternetShortcut *This = impl_from_IPersistFile(pFile);
398     TRACE("(%p)\n", pFile);
399     return Unknown_AddRef(This);
400 }
401 
402 static ULONG WINAPI PersistFile_Release(IPersistFile *pFile)
403 {
404     InternetShortcut *This = impl_from_IPersistFile(pFile);
405     TRACE("(%p)\n", pFile);
406     return Unknown_Release(This);
407 }
408 
409 static HRESULT WINAPI PersistFile_GetClassID(IPersistFile *pFile, CLSID *pClassID)
410 {
411     TRACE("(%p, %p)\n", pFile, pClassID);
412     *pClassID = CLSID_InternetShortcut;
413     return S_OK;
414 }
415 
416 static HRESULT WINAPI PersistFile_IsDirty(IPersistFile *pFile)
417 {
418     InternetShortcut *This = impl_from_IPersistFile(pFile);
419     TRACE("(%p)\n", pFile);
420     return This->isDirty ? S_OK : S_FALSE;
421 }
422 
423 /* Returns allocated profile string and a standard return code. */
424 static HRESULT get_profile_string(LPCWSTR lpAppName, LPCWSTR lpKeyName,
425                                 LPCWSTR lpFileName, WCHAR **rString )
426 {
427     DWORD r = 0;
428     DWORD len = 128;
429     WCHAR *buffer;
430 
431     *rString = NULL;
432     buffer = CoTaskMemAlloc(len * sizeof(*buffer));
433     if (!buffer)
434         return E_OUTOFMEMORY;
435 
436     r = GetPrivateProfileStringW(lpAppName, lpKeyName, NULL, buffer, len, lpFileName);
437     while (r == len-1)
438     {
439         WCHAR *realloc_buf;
440 
441         len *= 2;
442         realloc_buf = CoTaskMemRealloc(buffer, len * sizeof(*buffer));
443         if (realloc_buf == NULL)
444         {
445             CoTaskMemFree(buffer);
446             return E_OUTOFMEMORY;
447         }
448         buffer = realloc_buf;
449 
450         r = GetPrivateProfileStringW(lpAppName, lpKeyName, NULL, buffer, len, lpFileName);
451     }
452 
453     *rString = buffer;
454     return r ? S_OK : E_FAIL;
455 }
456 
457 static HRESULT WINAPI PersistFile_Load(IPersistFile *pFile, LPCOLESTR pszFileName, DWORD dwMode)
458 {
459     static const WCHAR str_header[] = {'I','n','t','e','r','n','e','t','S','h','o','r','t','c','u','t',0};
460     static const WCHAR str_URL[] = {'U','R','L',0};
461     static const WCHAR str_iconfile[] = {'i','c','o','n','f','i','l','e',0};
462     static const WCHAR str_iconindex[] = {'i','c','o','n','i','n','d','e','x',0};
463     InternetShortcut *This = impl_from_IPersistFile(pFile);
464     WCHAR *filename = NULL;
465     WCHAR *url;
466     HRESULT hr;
467     IPropertyStorage *pPropStg;
468     WCHAR *iconfile;
469     WCHAR *iconindexstring;
470 
471     TRACE("(%p, %s, 0x%x)\n", pFile, debugstr_w(pszFileName), dwMode);
472 
473     if (dwMode != 0)
474         FIXME("ignoring unimplemented mode 0x%x\n", dwMode);
475 
476     filename = co_strdupW(pszFileName);
477     if (!filename)
478         return E_OUTOFMEMORY;
479 
480     if (FAILED(hr = get_profile_string(str_header, str_URL, pszFileName, &url)))
481     {
482         CoTaskMemFree(filename);
483         return hr;
484     }
485 
486     hr = IPropertySetStorage_Open(This->property_set_storage, &FMTID_Intshcut,
487                 STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg);
488     if (FAILED(hr))
489     {
490         CoTaskMemFree(filename);
491         CoTaskMemFree(url);
492         return hr;
493     }
494 
495     CoTaskMemFree(This->currentFile);
496     This->currentFile = filename;
497     CoTaskMemFree(This->url);
498     This->url = url;
499     This->isDirty = FALSE;
500 
501     /* Now we're going to read in the iconfile and iconindex.
502        If we don't find them, that's not a failure case -- it's possible
503        that they just aren't in there. */
504 
505     if (get_profile_string(str_header, str_iconfile, pszFileName, &iconfile) == S_OK)
506     {
507         PROPSPEC ps;
508         PROPVARIANT pv;
509         ps.ulKind = PRSPEC_PROPID;
510         ps.u.propid = PID_IS_ICONFILE;
511         pv.vt = VT_LPWSTR;
512         pv.u.pwszVal = iconfile;
513         hr = IPropertyStorage_WriteMultiple(pPropStg, 1, &ps, &pv, 0);
514         if (FAILED(hr))
515             TRACE("Failed to store the iconfile to our property storage.  hr = 0x%x\n", hr);
516     }
517     CoTaskMemFree(iconfile);
518 
519     if (get_profile_string(str_header, str_iconindex, pszFileName, &iconindexstring) == S_OK)
520     {
521         int iconindex;
522         PROPSPEC ps;
523         PROPVARIANT pv;
524         iconindex = strtolW(iconindexstring, NULL, 10);
525         ps.ulKind = PRSPEC_PROPID;
526         ps.u.propid = PID_IS_ICONINDEX;
527         pv.vt = VT_I4;
528         pv.u.iVal = iconindex;
529         hr = IPropertyStorage_WriteMultiple(pPropStg, 1, &ps, &pv, 0);
530         if (FAILED(hr))
531            TRACE("Failed to store the iconindex to our property storage.  hr = 0x%x\n", hr);
532     }
533     CoTaskMemFree(iconindexstring);
534 
535     IPropertyStorage_Release(pPropStg);
536     return hr;
537 }
538 
539 static HRESULT WINAPI PersistFile_Save(IPersistFile *pFile, LPCOLESTR pszFileName, BOOL fRemember)
540 {
541     HRESULT hr = S_OK;
542     INT len;
543     CHAR *url;
544     InternetShortcut *This = impl_from_IPersistFile(pFile);
545 
546     TRACE("(%p, %s, %d)\n", pFile, debugstr_w(pszFileName), fRemember);
547 
548     if (pszFileName != NULL && fRemember)
549     {
550         LPOLESTR oldFile = This->currentFile;
551         This->currentFile = co_strdupW(pszFileName);
552         if (This->currentFile == NULL)
553         {
554             This->currentFile = oldFile;
555             return E_OUTOFMEMORY;
556         }
557         CoTaskMemFree(oldFile);
558     }
559     if (This->url == NULL)
560         return E_FAIL;
561 
562     /* Windows seems to always write:
563      *   ASCII "[InternetShortcut]" headers
564      *   ASCII names in "name=value" pairs
565      *   An ASCII (probably UTF8?) value in "URL=..."
566      */
567     len = WideCharToMultiByte(CP_UTF8, 0, This->url, -1, NULL, 0, 0, 0);
568     url = heap_alloc(len);
569     if (url != NULL)
570     {
571         HANDLE file;
572         WideCharToMultiByte(CP_UTF8, 0, This->url, -1, url, len, 0, 0);
573         file = CreateFileW(pszFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
574         if (file != INVALID_HANDLE_VALUE)
575         {
576             static const char str_header[] = "[InternetShortcut]";
577             static const char str_URL[] = "URL=";
578             static const char str_ICONFILE[] = "ICONFILE=";
579             static const char str_eol[] = "\r\n";
580             DWORD bytesWritten;
581             char *iconfile;
582             IPropertyStorage *pPropStgRead;
583             PROPSPEC ps[2];
584             PROPVARIANT pvread[2];
585             ps[0].ulKind = PRSPEC_PROPID;
586             ps[0].u.propid = PID_IS_ICONFILE;
587             ps[1].ulKind = PRSPEC_PROPID;
588             ps[1].u.propid = PID_IS_ICONINDEX;
589 
590             WriteFile(file, str_header, ARRAY_SIZE(str_header) - 1, &bytesWritten, NULL);
591             WriteFile(file, str_eol, ARRAY_SIZE(str_eol) - 1, &bytesWritten, NULL);
592             WriteFile(file, str_URL, ARRAY_SIZE(str_URL) - 1, &bytesWritten, NULL);
593             WriteFile(file, url, lstrlenA(url), &bytesWritten, NULL);
594             WriteFile(file, str_eol, ARRAY_SIZE(str_eol) - 1, &bytesWritten, NULL);
595 
596             hr = IPropertySetStorage_Open(This->property_set_storage, &FMTID_Intshcut, STGM_READ|STGM_SHARE_EXCLUSIVE, &pPropStgRead);
597             if (SUCCEEDED(hr))
598             {
599                 hr = IPropertyStorage_ReadMultiple(pPropStgRead, 2, ps, pvread);
600                 if (hr == S_FALSE)
601                 {
602                     /* None of the properties are present, that's ok */
603                     hr = S_OK;
604                     IPropertyStorage_Release(pPropStgRead);
605                 }
606                 else if (SUCCEEDED(hr))
607                 {
608                     char indexString[50];
609                     len = WideCharToMultiByte(CP_UTF8, 0, pvread[0].u.pwszVal, -1, NULL, 0, 0, 0);
610                     iconfile = heap_alloc(len);
611                     if (iconfile != NULL)
612                     {
613                         WideCharToMultiByte(CP_UTF8, 0, pvread[0].u.pwszVal, -1, iconfile, len, 0, 0);
614                         WriteFile(file, str_ICONFILE, lstrlenA(str_ICONFILE), &bytesWritten, NULL);
615                         WriteFile(file, iconfile, lstrlenA(iconfile), &bytesWritten, NULL);
616                         WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
617                     }
618 
619                     sprintf(indexString, "ICONINDEX=%d", pvread[1].u.iVal);
620                     WriteFile(file, indexString, lstrlenA(indexString), &bytesWritten, NULL);
621                     WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
622 
623                     IPropertyStorage_Release(pPropStgRead);
624                     PropVariantClear(&pvread[0]);
625                     PropVariantClear(&pvread[1]);
626                 }
627                 else
628                 {
629                     TRACE("Unable to read properties.\n");
630                 }
631             }
632             else
633             {
634                TRACE("Unable to get the IPropertyStorage.\n");
635             }
636 
637             CloseHandle(file);
638             if (pszFileName == NULL || fRemember)
639                 This->isDirty = FALSE;
640             StartLinkProcessor(pszFileName);
641         }
642         else
643             hr = E_FAIL;
644         heap_free(url);
645     }
646     else
647         hr = E_OUTOFMEMORY;
648 
649     return hr;
650 }
651 
652 static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *pFile, LPCOLESTR pszFileName)
653 {
654     FIXME("(%p, %p): stub\n", pFile, pszFileName);
655     return E_NOTIMPL;
656 }
657 
658 static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *pFile, LPOLESTR *ppszFileName)
659 {
660     HRESULT hr = S_OK;
661     InternetShortcut *This = impl_from_IPersistFile(pFile);
662     TRACE("(%p, %p)\n", pFile, ppszFileName);
663     if (This->currentFile == NULL)
664         *ppszFileName = NULL;
665     else
666     {
667         *ppszFileName = co_strdupW(This->currentFile);
668         if (*ppszFileName == NULL)
669             hr = E_OUTOFMEMORY;
670     }
671     return hr;
672 }
673 
674 static HRESULT WINAPI PropertySetStorage_QueryInterface(IPropertySetStorage *iface, REFIID riid, PVOID *ppvObject)
675 {
676     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
677     TRACE("(%p)\n", iface);
678     return Unknown_QueryInterface(This, riid, ppvObject);
679 }
680 
681 static ULONG WINAPI PropertySetStorage_AddRef(IPropertySetStorage *iface)
682 {
683     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
684     TRACE("(%p)\n", iface);
685     return Unknown_AddRef(This);
686 }
687 
688 static ULONG WINAPI PropertySetStorage_Release(IPropertySetStorage *iface)
689 {
690     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
691     TRACE("(%p)\n", iface);
692     return Unknown_Release(This);
693 }
694 
695 static HRESULT WINAPI PropertySetStorage_Create(
696         IPropertySetStorage* iface,
697         REFFMTID rfmtid,
698         const CLSID *pclsid,
699         DWORD grfFlags,
700         DWORD grfMode,
701         IPropertyStorage **ppprstg)
702 {
703     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
704     TRACE("(%s, %p, 0x%x, 0x%x, %p)\n", debugstr_guid(rfmtid), pclsid, grfFlags, grfMode, ppprstg);
705 
706     return IPropertySetStorage_Create(This->property_set_storage,
707                                       rfmtid,
708                                       pclsid,
709                                       grfFlags,
710                                       grfMode,
711                                       ppprstg);
712 }
713 
714 static HRESULT WINAPI PropertySetStorage_Open(
715         IPropertySetStorage* iface,
716         REFFMTID rfmtid,
717         DWORD grfMode,
718         IPropertyStorage **ppprstg)
719 {
720     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
721     TRACE("(%s, 0x%x, %p)\n", debugstr_guid(rfmtid), grfMode, ppprstg);
722 
723     /* Note:  The |STGM_SHARE_EXCLUSIVE is to cope with a bug in the implementation.  Should be fixed in ole32. */
724     return IPropertySetStorage_Open(This->property_set_storage,
725                                     rfmtid,
726                                     grfMode|STGM_SHARE_EXCLUSIVE,
727                                     ppprstg);
728 }
729 
730 static HRESULT WINAPI PropertySetStorage_Delete(IPropertySetStorage *iface, REFFMTID rfmtid)
731 {
732     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
733     TRACE("(%s)\n", debugstr_guid(rfmtid));
734 
735 
736     return IPropertySetStorage_Delete(This->property_set_storage,
737                                       rfmtid);
738 }
739 
740 static HRESULT WINAPI PropertySetStorage_Enum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **ppenum)
741 {
742     FIXME("(%p): stub\n", ppenum);
743     return E_NOTIMPL;
744 }
745 
746 static const IUniformResourceLocatorWVtbl uniformResourceLocatorWVtbl = {
747     UniformResourceLocatorW_QueryInterface,
748     UniformResourceLocatorW_AddRef,
749     UniformResourceLocatorW_Release,
750     UniformResourceLocatorW_SetUrl,
751     UniformResourceLocatorW_GetUrl,
752     UniformResourceLocatorW_InvokeCommand
753 };
754 
755 static const IUniformResourceLocatorAVtbl uniformResourceLocatorAVtbl = {
756     UniformResourceLocatorA_QueryInterface,
757     UniformResourceLocatorA_AddRef,
758     UniformResourceLocatorA_Release,
759     UniformResourceLocatorA_SetUrl,
760     UniformResourceLocatorA_GetUrl,
761     UniformResourceLocatorA_InvokeCommand
762 };
763 
764 static const IPersistFileVtbl persistFileVtbl = {
765     PersistFile_QueryInterface,
766     PersistFile_AddRef,
767     PersistFile_Release,
768     PersistFile_GetClassID,
769     PersistFile_IsDirty,
770     PersistFile_Load,
771     PersistFile_Save,
772     PersistFile_SaveCompleted,
773     PersistFile_GetCurFile
774 };
775 
776 static const IPropertySetStorageVtbl propertySetStorageVtbl = {
777     PropertySetStorage_QueryInterface,
778     PropertySetStorage_AddRef,
779     PropertySetStorage_Release,
780     PropertySetStorage_Create,
781     PropertySetStorage_Open,
782     PropertySetStorage_Delete,
783     PropertySetStorage_Enum
784 };
785 
786 static InternetShortcut *create_shortcut(void)
787 {
788     InternetShortcut *newshortcut;
789 
790     newshortcut = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(InternetShortcut));
791     if (newshortcut)
792     {
793         HRESULT hr;
794         IPropertyStorage *dummy;
795 
796         newshortcut->IUniformResourceLocatorA_iface.lpVtbl = &uniformResourceLocatorAVtbl;
797         newshortcut->IUniformResourceLocatorW_iface.lpVtbl = &uniformResourceLocatorWVtbl;
798         newshortcut->IPersistFile_iface.lpVtbl = &persistFileVtbl;
799         newshortcut->IPropertySetStorage_iface.lpVtbl = &propertySetStorageVtbl;
800         newshortcut->refCount = 1;
801         hr = StgCreateStorageEx(NULL, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE,
802                                 STGFMT_STORAGE, 0, NULL, NULL, &IID_IPropertySetStorage, (void **) &newshortcut->property_set_storage);
803         if (FAILED(hr))
804         {
805             TRACE("Failed to create the storage object needed for the shortcut.\n");
806             heap_free(newshortcut);
807             return NULL;
808         }
809 
810         hr = IPropertySetStorage_Create(newshortcut->property_set_storage, &FMTID_Intshcut, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &dummy);
811         if (FAILED(hr))
812         {
813             TRACE("Failed to create the property object needed for the shortcut.\n");
814             IPropertySetStorage_Release(newshortcut->property_set_storage);
815             heap_free(newshortcut);
816             return NULL;
817         }
818         IPropertyStorage_Release(dummy);
819     }
820 
821     return newshortcut;
822 }
823 
824 HRESULT WINAPI InternetShortcut_Create(IClassFactory *iface, IUnknown *outer, REFIID riid, void **ppv)
825 {
826     InternetShortcut *This;
827     HRESULT hres;
828 
829     TRACE("(%p, %s, %p)\n", outer, debugstr_guid(riid), ppv);
830 
831     *ppv = NULL;
832 
833     if(outer)
834         return CLASS_E_NOAGGREGATION;
835 
836     This = create_shortcut();
837     if(!This)
838         return E_OUTOFMEMORY;
839 
840     hres = Unknown_QueryInterface(This, riid, ppv);
841     Unknown_Release(This);
842     return hres;
843 }
844 
845 
846 /**********************************************************************
847  * OpenURL  (ieframe.@)
848  */
849 void WINAPI OpenURL(HWND hWnd, HINSTANCE hInst, LPCSTR lpcstrUrl, int nShowCmd)
850 {
851     InternetShortcut *shortcut;
852     WCHAR* urlfilepath = NULL;
853     int len;
854 
855     shortcut = create_shortcut();
856 
857     if(!shortcut)
858         return;
859 
860     len = MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, NULL, 0);
861     urlfilepath = heap_alloc(len * sizeof(WCHAR));
862     MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, urlfilepath, len);
863 
864     if(SUCCEEDED(IPersistFile_Load(&shortcut->IPersistFile_iface, urlfilepath, 0))) {
865         URLINVOKECOMMANDINFOW ici;
866 
867         memset( &ici, 0, sizeof ici );
868         ici.dwcbSize = sizeof ici;
869         ici.dwFlags = IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB;
870         ici.hwndParent = hWnd;
871 
872         if(FAILED(UniformResourceLocatorW_InvokeCommand(&shortcut->IUniformResourceLocatorW_iface, (PURLINVOKECOMMANDINFOW) &ici)))
873             TRACE("failed to open URL: %s\n", debugstr_a(lpcstrUrl));
874     }
875 
876     heap_free(urlfilepath);
877     Unknown_Release(shortcut);
878 }
879