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