xref: /reactos/dll/win32/ieframe/intshcut.c (revision c2c66aff)
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 /* Returns allocated profile string and a standard return code. */
408 static HRESULT 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     *rString = NULL;
416     buffer = CoTaskMemAlloc(len * sizeof(*buffer));
417     if (!buffer)
418         return E_OUTOFMEMORY;
419 
420     r = GetPrivateProfileStringW(lpAppName, lpKeyName, NULL, buffer, len, lpFileName);
421     while (r == len-1)
422     {
423         WCHAR *realloc_buf;
424 
425         len *= 2;
426         realloc_buf = CoTaskMemRealloc(buffer, len * sizeof(*buffer));
427         if (realloc_buf == NULL)
428         {
429             CoTaskMemFree(buffer);
430             return E_OUTOFMEMORY;
431         }
432         buffer = realloc_buf;
433 
434         r = GetPrivateProfileStringW(lpAppName, lpKeyName, NULL, buffer, len, lpFileName);
435     }
436 
437     *rString = buffer;
438     return r ? S_OK : E_FAIL;
439 }
440 
441 static HRESULT WINAPI PersistFile_Load(IPersistFile *pFile, LPCOLESTR pszFileName, DWORD dwMode)
442 {
443     InternetShortcut *This = impl_from_IPersistFile(pFile);
444     static WCHAR str_header[] = {'I','n','t','e','r','n','e','t','S','h','o','r','t','c','u','t',0};
445     static WCHAR str_URL[] = {'U','R','L',0};
446     static WCHAR str_iconfile[] = {'i','c','o','n','f','i','l','e',0};
447     static WCHAR str_iconindex[] = {'i','c','o','n','i','n','d','e','x',0};
448     WCHAR *filename = NULL;
449     WCHAR *url;
450     HRESULT hr;
451     IPropertyStorage *pPropStg;
452     WCHAR *iconfile;
453     WCHAR *iconindexstring;
454 
455     TRACE("(%p, %s, 0x%x)\n", pFile, debugstr_w(pszFileName), dwMode);
456 
457     if (dwMode != 0)
458         FIXME("ignoring unimplemented mode 0x%x\n", dwMode);
459 
460     filename = co_strdupW(pszFileName);
461     if (!filename)
462         return E_OUTOFMEMORY;
463 
464     if (FAILED(hr = get_profile_string(str_header, str_URL, pszFileName, &url)))
465     {
466         CoTaskMemFree(filename);
467         return hr;
468     }
469 
470     hr = IPropertySetStorage_Open(This->property_set_storage, &FMTID_Intshcut,
471                 STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg);
472     if (FAILED(hr))
473     {
474         CoTaskMemFree(filename);
475         CoTaskMemFree(url);
476         return hr;
477     }
478 
479     CoTaskMemFree(This->currentFile);
480     This->currentFile = filename;
481     CoTaskMemFree(This->url);
482     This->url = url;
483     This->isDirty = FALSE;
484 
485     /* Now we're going to read in the iconfile and iconindex.
486        If we don't find them, that's not a failure case -- it's possible
487        that they just aren't in there. */
488 
489     if (get_profile_string(str_header, str_iconfile, pszFileName, &iconfile) == S_OK)
490     {
491         PROPSPEC ps;
492         PROPVARIANT pv;
493         ps.ulKind = PRSPEC_PROPID;
494         ps.u.propid = PID_IS_ICONFILE;
495         pv.vt = VT_LPWSTR;
496         pv.u.pwszVal = iconfile;
497         hr = IPropertyStorage_WriteMultiple(pPropStg, 1, &ps, &pv, 0);
498         if (FAILED(hr))
499             TRACE("Failed to store the iconfile to our property storage.  hr = 0x%x\n", hr);
500     }
501     CoTaskMemFree(iconfile);
502 
503     if (get_profile_string(str_header, str_iconindex, pszFileName, &iconindexstring) == S_OK)
504     {
505         int iconindex;
506         PROPSPEC ps;
507         PROPVARIANT pv;
508         iconindex = strtolW(iconindexstring, NULL, 10);
509         ps.ulKind = PRSPEC_PROPID;
510         ps.u.propid = PID_IS_ICONINDEX;
511         pv.vt = VT_I4;
512         pv.u.iVal = iconindex;
513         hr = IPropertyStorage_WriteMultiple(pPropStg, 1, &ps, &pv, 0);
514         if (FAILED(hr))
515            TRACE("Failed to store the iconindex to our property storage.  hr = 0x%x\n", hr);
516     }
517     CoTaskMemFree(iconindexstring);
518 
519     IPropertyStorage_Release(pPropStg);
520     return hr;
521 }
522 
523 static HRESULT WINAPI PersistFile_Save(IPersistFile *pFile, LPCOLESTR pszFileName, BOOL fRemember)
524 {
525     HRESULT hr = S_OK;
526     INT len;
527     CHAR *url;
528     InternetShortcut *This = impl_from_IPersistFile(pFile);
529 
530     TRACE("(%p, %s, %d)\n", pFile, debugstr_w(pszFileName), fRemember);
531 
532     if (pszFileName != NULL && fRemember)
533     {
534         LPOLESTR oldFile = This->currentFile;
535         This->currentFile = co_strdupW(pszFileName);
536         if (This->currentFile == NULL)
537         {
538             This->currentFile = oldFile;
539             return E_OUTOFMEMORY;
540         }
541         CoTaskMemFree(oldFile);
542     }
543     if (This->url == NULL)
544         return E_FAIL;
545 
546     /* Windows seems to always write:
547      *   ASCII "[InternetShortcut]" headers
548      *   ASCII names in "name=value" pairs
549      *   An ASCII (probably UTF8?) value in "URL=..."
550      */
551     len = WideCharToMultiByte(CP_UTF8, 0, This->url, -1, NULL, 0, 0, 0);
552     url = heap_alloc(len);
553     if (url != NULL)
554     {
555         HANDLE file;
556         WideCharToMultiByte(CP_UTF8, 0, This->url, -1, url, len, 0, 0);
557         file = CreateFileW(pszFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
558         if (file != INVALID_HANDLE_VALUE)
559         {
560             DWORD bytesWritten;
561             char *iconfile;
562             char str_header[] = "[InternetShortcut]";
563             char str_URL[] = "URL=";
564             char str_ICONFILE[] = "ICONFILE=";
565             char str_eol[] = "\r\n";
566             IPropertyStorage *pPropStgRead;
567             PROPSPEC ps[2];
568             PROPVARIANT pvread[2];
569             ps[0].ulKind = PRSPEC_PROPID;
570             ps[0].u.propid = PID_IS_ICONFILE;
571             ps[1].ulKind = PRSPEC_PROPID;
572             ps[1].u.propid = PID_IS_ICONINDEX;
573 
574             WriteFile(file, str_header, lstrlenA(str_header), &bytesWritten, NULL);
575             WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
576             WriteFile(file, str_URL, lstrlenA(str_URL), &bytesWritten, NULL);
577             WriteFile(file, url, lstrlenA(url), &bytesWritten, NULL);
578             WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
579 
580             hr = IPropertySetStorage_Open(This->property_set_storage, &FMTID_Intshcut, STGM_READ|STGM_SHARE_EXCLUSIVE, &pPropStgRead);
581             if (SUCCEEDED(hr))
582             {
583                 hr = IPropertyStorage_ReadMultiple(pPropStgRead, 2, ps, pvread);
584                 if (hr == S_FALSE)
585                 {
586                     /* None of the properties are present, that's ok */
587                     hr = S_OK;
588                     IPropertyStorage_Release(pPropStgRead);
589                 }
590                 else if (SUCCEEDED(hr))
591                 {
592                     char indexString[50];
593                     len = WideCharToMultiByte(CP_UTF8, 0, pvread[0].u.pwszVal, -1, NULL, 0, 0, 0);
594                     iconfile = heap_alloc(len);
595                     if (iconfile != NULL)
596                     {
597                         WideCharToMultiByte(CP_UTF8, 0, pvread[0].u.pwszVal, -1, iconfile, len, 0, 0);
598                         WriteFile(file, str_ICONFILE, lstrlenA(str_ICONFILE), &bytesWritten, NULL);
599                         WriteFile(file, iconfile, lstrlenA(iconfile), &bytesWritten, NULL);
600                         WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
601                     }
602 
603                     sprintf(indexString, "ICONINDEX=%d", pvread[1].u.iVal);
604                     WriteFile(file, indexString, lstrlenA(indexString), &bytesWritten, NULL);
605                     WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
606 
607                     IPropertyStorage_Release(pPropStgRead);
608                     PropVariantClear(&pvread[0]);
609                     PropVariantClear(&pvread[1]);
610                 }
611                 else
612                 {
613                     TRACE("Unable to read properties.\n");
614                 }
615             }
616             else
617             {
618                TRACE("Unable to get the IPropertyStorage.\n");
619             }
620 
621             CloseHandle(file);
622             if (pszFileName == NULL || fRemember)
623                 This->isDirty = FALSE;
624             StartLinkProcessor(pszFileName);
625         }
626         else
627             hr = E_FAIL;
628         heap_free(url);
629     }
630     else
631         hr = E_OUTOFMEMORY;
632 
633     return hr;
634 }
635 
636 static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *pFile, LPCOLESTR pszFileName)
637 {
638     FIXME("(%p, %p): stub\n", pFile, pszFileName);
639     return E_NOTIMPL;
640 }
641 
642 static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *pFile, LPOLESTR *ppszFileName)
643 {
644     HRESULT hr = S_OK;
645     InternetShortcut *This = impl_from_IPersistFile(pFile);
646     TRACE("(%p, %p)\n", pFile, ppszFileName);
647     if (This->currentFile == NULL)
648         *ppszFileName = NULL;
649     else
650     {
651         *ppszFileName = co_strdupW(This->currentFile);
652         if (*ppszFileName == NULL)
653             hr = E_OUTOFMEMORY;
654     }
655     return hr;
656 }
657 
658 static HRESULT WINAPI PropertySetStorage_QueryInterface(IPropertySetStorage *iface, REFIID riid, PVOID *ppvObject)
659 {
660     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
661     TRACE("(%p)\n", iface);
662     return Unknown_QueryInterface(This, riid, ppvObject);
663 }
664 
665 static ULONG WINAPI PropertySetStorage_AddRef(IPropertySetStorage *iface)
666 {
667     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
668     TRACE("(%p)\n", iface);
669     return Unknown_AddRef(This);
670 }
671 
672 static ULONG WINAPI PropertySetStorage_Release(IPropertySetStorage *iface)
673 {
674     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
675     TRACE("(%p)\n", iface);
676     return Unknown_Release(This);
677 }
678 
679 static HRESULT WINAPI PropertySetStorage_Create(
680         IPropertySetStorage* iface,
681         REFFMTID rfmtid,
682         const CLSID *pclsid,
683         DWORD grfFlags,
684         DWORD grfMode,
685         IPropertyStorage **ppprstg)
686 {
687     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
688     TRACE("(%s, %p, 0x%x, 0x%x, %p)\n", debugstr_guid(rfmtid), pclsid, grfFlags, grfMode, ppprstg);
689 
690     return IPropertySetStorage_Create(This->property_set_storage,
691                                       rfmtid,
692                                       pclsid,
693                                       grfFlags,
694                                       grfMode,
695                                       ppprstg);
696 }
697 
698 static HRESULT WINAPI PropertySetStorage_Open(
699         IPropertySetStorage* iface,
700         REFFMTID rfmtid,
701         DWORD grfMode,
702         IPropertyStorage **ppprstg)
703 {
704     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
705     TRACE("(%s, 0x%x, %p)\n", debugstr_guid(rfmtid), grfMode, ppprstg);
706 
707     /* Note:  The |STGM_SHARE_EXCLUSIVE is to cope with a bug in the implementation.  Should be fixed in ole32. */
708     return IPropertySetStorage_Open(This->property_set_storage,
709                                     rfmtid,
710                                     grfMode|STGM_SHARE_EXCLUSIVE,
711                                     ppprstg);
712 }
713 
714 static HRESULT WINAPI PropertySetStorage_Delete(IPropertySetStorage *iface, REFFMTID rfmtid)
715 {
716     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
717     TRACE("(%s)\n", debugstr_guid(rfmtid));
718 
719 
720     return IPropertySetStorage_Delete(This->property_set_storage,
721                                       rfmtid);
722 }
723 
724 static HRESULT WINAPI PropertySetStorage_Enum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **ppenum)
725 {
726     FIXME("(%p): stub\n", ppenum);
727     return E_NOTIMPL;
728 }
729 
730 static const IUniformResourceLocatorWVtbl uniformResourceLocatorWVtbl = {
731     UniformResourceLocatorW_QueryInterface,
732     UniformResourceLocatorW_AddRef,
733     UniformResourceLocatorW_Release,
734     UniformResourceLocatorW_SetUrl,
735     UniformResourceLocatorW_GetUrl,
736     UniformResourceLocatorW_InvokeCommand
737 };
738 
739 static const IUniformResourceLocatorAVtbl uniformResourceLocatorAVtbl = {
740     UniformResourceLocatorA_QueryInterface,
741     UniformResourceLocatorA_AddRef,
742     UniformResourceLocatorA_Release,
743     UniformResourceLocatorA_SetUrl,
744     UniformResourceLocatorA_GetUrl,
745     UniformResourceLocatorA_InvokeCommand
746 };
747 
748 static const IPersistFileVtbl persistFileVtbl = {
749     PersistFile_QueryInterface,
750     PersistFile_AddRef,
751     PersistFile_Release,
752     PersistFile_GetClassID,
753     PersistFile_IsDirty,
754     PersistFile_Load,
755     PersistFile_Save,
756     PersistFile_SaveCompleted,
757     PersistFile_GetCurFile
758 };
759 
760 static const IPropertySetStorageVtbl propertySetStorageVtbl = {
761     PropertySetStorage_QueryInterface,
762     PropertySetStorage_AddRef,
763     PropertySetStorage_Release,
764     PropertySetStorage_Create,
765     PropertySetStorage_Open,
766     PropertySetStorage_Delete,
767     PropertySetStorage_Enum
768 };
769 
770 static InternetShortcut *create_shortcut(void)
771 {
772     InternetShortcut *newshortcut;
773 
774     newshortcut = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(InternetShortcut));
775     if (newshortcut)
776     {
777         HRESULT hr;
778         IPropertyStorage *dummy;
779 
780         newshortcut->IUniformResourceLocatorA_iface.lpVtbl = &uniformResourceLocatorAVtbl;
781         newshortcut->IUniformResourceLocatorW_iface.lpVtbl = &uniformResourceLocatorWVtbl;
782         newshortcut->IPersistFile_iface.lpVtbl = &persistFileVtbl;
783         newshortcut->IPropertySetStorage_iface.lpVtbl = &propertySetStorageVtbl;
784         newshortcut->refCount = 1;
785         hr = StgCreateStorageEx(NULL, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE,
786                                 STGFMT_STORAGE, 0, NULL, NULL, &IID_IPropertySetStorage, (void **) &newshortcut->property_set_storage);
787         if (FAILED(hr))
788         {
789             TRACE("Failed to create the storage object needed for the shortcut.\n");
790             heap_free(newshortcut);
791             return NULL;
792         }
793 
794         hr = IPropertySetStorage_Create(newshortcut->property_set_storage, &FMTID_Intshcut, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &dummy);
795         if (FAILED(hr))
796         {
797             TRACE("Failed to create the property object needed for the shortcut.\n");
798             IPropertySetStorage_Release(newshortcut->property_set_storage);
799             heap_free(newshortcut);
800             return NULL;
801         }
802         IPropertyStorage_Release(dummy);
803     }
804 
805     return newshortcut;
806 }
807 
808 HRESULT WINAPI InternetShortcut_Create(IClassFactory *iface, IUnknown *outer, REFIID riid, void **ppv)
809 {
810     InternetShortcut *This;
811     HRESULT hres;
812 
813     TRACE("(%p, %s, %p)\n", outer, debugstr_guid(riid), ppv);
814 
815     *ppv = NULL;
816 
817     if(outer)
818         return CLASS_E_NOAGGREGATION;
819 
820     This = create_shortcut();
821     if(!This)
822         return E_OUTOFMEMORY;
823 
824     hres = Unknown_QueryInterface(This, riid, ppv);
825     Unknown_Release(This);
826     return hres;
827 }
828 
829 
830 /**********************************************************************
831  * OpenURL  (ieframe.@)
832  */
833 void WINAPI OpenURL(HWND hWnd, HINSTANCE hInst, LPCSTR lpcstrUrl, int nShowCmd)
834 {
835     InternetShortcut *shortcut;
836     WCHAR* urlfilepath = NULL;
837     int len;
838 
839     shortcut = create_shortcut();
840 
841     if(!shortcut)
842         return;
843 
844     len = MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, NULL, 0);
845     urlfilepath = heap_alloc(len * sizeof(WCHAR));
846     MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, urlfilepath, len);
847 
848     if(SUCCEEDED(IPersistFile_Load(&shortcut->IPersistFile_iface, urlfilepath, 0))) {
849         URLINVOKECOMMANDINFOW ici;
850 
851         memset( &ici, 0, sizeof ici );
852         ici.dwcbSize = sizeof ici;
853         ici.dwFlags = IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB;
854         ici.hwndParent = hWnd;
855 
856         if(FAILED(UniformResourceLocatorW_InvokeCommand(&shortcut->IUniformResourceLocatorW_iface, (PURLINVOKECOMMANDINFOW) &ici)))
857             TRACE("failed to open URL: %s\n", debugstr_a(lpcstrUrl));
858     }
859 
860     heap_free(urlfilepath);
861     Unknown_Release(shortcut);
862 }
863