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