shdocvw: Convert coclass registration to the IRegistrar mechanism.
[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 (hr == S_FALSE)
614                 {
615                     /* None of the properties are present, that's ok */
616                     hr = S_OK;
617                     IPropertyStorage_Release(pPropStgRead);
618                 }
619                 else if SUCCEEDED(hr)
620                 {
621                     char indexString[50];
622                     len = WideCharToMultiByte(CP_UTF8, 0, pvread[0].u.pwszVal, -1, NULL, 0, 0, 0);
623                     iconfile = heap_alloc(len);
624                     if (iconfile != NULL)
625                     {
626                         WideCharToMultiByte(CP_UTF8, 0, pvread[0].u.pwszVal, -1, iconfile, len, 0, 0);
627                         WriteFile(file, str_ICONFILE, lstrlenA(str_ICONFILE), &bytesWritten, NULL);
628                         WriteFile(file, iconfile, lstrlenA(iconfile), &bytesWritten, NULL);
629                         WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
630                     }
631
632                     sprintf(indexString, "ICONINDEX=%d", pvread[1].u.iVal);
633                     WriteFile(file, indexString, lstrlenA(indexString), &bytesWritten, NULL);
634                     WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
635
636                     IPropertyStorage_Release(pPropStgRead);
637                     PropVariantClear(&pvread[0]);
638                     PropVariantClear(&pvread[1]);
639                 }
640                 else
641                 {
642                     TRACE("Unable to read properties.\n");
643                 }
644             }
645             else
646             {
647                TRACE("Unable to get the IPropertyStorage.\n");
648             }
649
650             CloseHandle(file);
651             if (pszFileName == NULL || fRemember)
652                 This->isDirty = FALSE;
653             StartLinkProcessor(pszFileName);
654         }
655         else
656             hr = E_FAIL;
657         heap_free(url);
658     }
659     else
660         hr = E_OUTOFMEMORY;
661
662     return hr;
663 }
664
665 static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *pFile, LPCOLESTR pszFileName)
666 {
667     FIXME("(%p, %p): stub\n", pFile, pszFileName);
668     return E_NOTIMPL;
669 }
670
671 static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *pFile, LPOLESTR *ppszFileName)
672 {
673     HRESULT hr = S_OK;
674     InternetShortcut *This = impl_from_IPersistFile(pFile);
675     TRACE("(%p, %p)\n", pFile, ppszFileName);
676     if (This->currentFile == NULL)
677         *ppszFileName = NULL;
678     else
679     {
680         *ppszFileName = co_strdupW(This->currentFile);
681         if (*ppszFileName == NULL)
682             hr = E_OUTOFMEMORY;
683     }
684     return hr;
685 }
686
687 static HRESULT WINAPI PropertySetStorage_QueryInterface(IPropertySetStorage *iface, REFIID riid, PVOID *ppvObject)
688 {
689     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
690     TRACE("(%p)\n", iface);
691     return Unknown_QueryInterface(This, riid, ppvObject);
692 }
693
694 static ULONG WINAPI PropertySetStorage_AddRef(IPropertySetStorage *iface)
695 {
696     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
697     TRACE("(%p)\n", iface);
698     return Unknown_AddRef(This);
699 }
700
701 static ULONG WINAPI PropertySetStorage_Release(IPropertySetStorage *iface)
702 {
703     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
704     TRACE("(%p)\n", iface);
705     return Unknown_Release(This);
706 }
707
708 static HRESULT WINAPI PropertySetStorage_Create(
709         IPropertySetStorage* iface,
710         REFFMTID rfmtid,
711         const CLSID *pclsid,
712         DWORD grfFlags,
713         DWORD grfMode,
714         IPropertyStorage **ppprstg)
715 {
716     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
717     TRACE("(%s, %p, 0x%x, 0x%x, %p)\n", debugstr_guid(rfmtid), pclsid, grfFlags, grfMode, ppprstg);
718
719     return IPropertySetStorage_Create(This->property_set_storage,
720                                       rfmtid,
721                                       pclsid,
722                                       grfFlags,
723                                       grfMode,
724                                       ppprstg);
725 }
726
727 static HRESULT WINAPI PropertySetStorage_Open(
728         IPropertySetStorage* iface,
729         REFFMTID rfmtid,
730         DWORD grfMode,
731         IPropertyStorage **ppprstg)
732 {
733     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
734     TRACE("(%s, 0x%x, %p)\n", debugstr_guid(rfmtid), grfMode, ppprstg);
735
736     /* Note:  The |STGM_SHARE_EXCLUSIVE is to cope with a bug in the implementation.  Should be fixed in ole32. */
737     return IPropertySetStorage_Open(This->property_set_storage,
738                                     rfmtid,
739                                     grfMode|STGM_SHARE_EXCLUSIVE,
740                                     ppprstg);
741 }
742
743 static HRESULT WINAPI PropertySetStorage_Delete(IPropertySetStorage *iface, REFFMTID rfmtid)
744 {
745     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
746     TRACE("(%s)\n", debugstr_guid(rfmtid));
747
748
749     return IPropertySetStorage_Delete(This->property_set_storage,
750                                       rfmtid);
751 }
752
753 static HRESULT WINAPI PropertySetStorage_Enum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **ppenum)
754 {
755     FIXME("(%p): stub\n", ppenum);
756     return E_NOTIMPL;
757 }
758
759 static const IUniformResourceLocatorWVtbl uniformResourceLocatorWVtbl = {
760     UniformResourceLocatorW_QueryInterface,
761     UniformResourceLocatorW_AddRef,
762     UniformResourceLocatorW_Release,
763     UniformResourceLocatorW_SetUrl,
764     UniformResourceLocatorW_GetUrl,
765     UniformResourceLocatorW_InvokeCommand
766 };
767
768 static const IUniformResourceLocatorAVtbl uniformResourceLocatorAVtbl = {
769     UniformResourceLocatorA_QueryInterface,
770     UniformResourceLocatorA_AddRef,
771     UniformResourceLocatorA_Release,
772     UniformResourceLocatorA_SetUrl,
773     UniformResourceLocatorA_GetUrl,
774     UniformResourceLocatorA_InvokeCommand
775 };
776
777 static const IPersistFileVtbl persistFileVtbl = {
778     PersistFile_QueryInterface,
779     PersistFile_AddRef,
780     PersistFile_Release,
781     PersistFile_GetClassID,
782     PersistFile_IsDirty,
783     PersistFile_Load,
784     PersistFile_Save,
785     PersistFile_SaveCompleted,
786     PersistFile_GetCurFile
787 };
788
789 static const IPropertySetStorageVtbl propertySetStorageVtbl = {
790     PropertySetStorage_QueryInterface,
791     PropertySetStorage_AddRef,
792     PropertySetStorage_Release,
793     PropertySetStorage_Create,
794     PropertySetStorage_Open,
795     PropertySetStorage_Delete,
796     PropertySetStorage_Enum
797 };
798
799 static InternetShortcut *create_shortcut(void)
800 {
801     InternetShortcut *newshortcut;
802
803     newshortcut = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(InternetShortcut));
804     if (newshortcut)
805     {
806         HRESULT hr;
807         IPropertyStorage *dummy;
808
809         newshortcut->uniformResourceLocatorA.lpVtbl = &uniformResourceLocatorAVtbl;
810         newshortcut->uniformResourceLocatorW.lpVtbl = &uniformResourceLocatorWVtbl;
811         newshortcut->persistFile.lpVtbl = &persistFileVtbl;
812         newshortcut->IPropertySetStorage_iface.lpVtbl = &propertySetStorageVtbl;
813         newshortcut->refCount = 0;
814         hr = StgCreateStorageEx(NULL, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, STGFMT_STORAGE, 0, NULL, NULL, &IID_IPropertySetStorage, (void **) &newshortcut->property_set_storage);
815         if FAILED(hr)
816         {
817             TRACE("Failed to create the storage object needed for the shortcut.\n");
818             heap_free(newshortcut);
819             return NULL;
820         }
821
822         hr = IPropertySetStorage_Create(newshortcut->property_set_storage, &FMTID_Intshcut, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &dummy);
823         if FAILED(hr)
824         {
825             TRACE("Failed to create the property object needed for the shortcut.\n");
826             IPropertySetStorage_Release(newshortcut->property_set_storage);
827             heap_free(newshortcut);
828             return NULL;
829         }
830         IPropertySetStorage_Release(dummy);
831     }
832
833     return newshortcut;
834 }
835
836 HRESULT InternetShortcut_Create(IUnknown *pOuter, REFIID riid, void **ppv)
837 {
838     InternetShortcut *This;
839     HRESULT hr;
840
841     TRACE("(%p, %s, %p)\n", pOuter, debugstr_guid(riid), ppv);
842
843     *ppv = NULL;
844
845     if(pOuter)
846         return CLASS_E_NOAGGREGATION;
847
848     This = create_shortcut();
849     if (This)
850     {
851         hr = Unknown_QueryInterface(This, riid, ppv);
852         if (SUCCEEDED(hr))
853             SHDOCVW_LockModule();
854         else
855             heap_free(This);
856         return hr;
857     }
858     else
859         return E_OUTOFMEMORY;
860 }
861
862
863 /**********************************************************************
864  * OpenURL  (SHDOCVW.@)
865  */
866 void WINAPI OpenURL(HWND hWnd, HINSTANCE hInst, LPCSTR lpcstrUrl, int nShowCmd)
867 {
868     InternetShortcut *shortcut;
869     WCHAR* urlfilepath = NULL;
870     shortcut = create_shortcut();
871
872     if (shortcut)
873     {
874         int len;
875
876         len = MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, NULL, 0);
877         urlfilepath = heap_alloc(len * sizeof(WCHAR));
878         MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, urlfilepath, len);
879
880         if(SUCCEEDED(IPersistFile_Load(&shortcut->persistFile, urlfilepath, 0)))
881         {
882             URLINVOKECOMMANDINFOW ici;
883
884             memset( &ici, 0, sizeof ici );
885             ici.dwcbSize = sizeof ici;
886             ici.dwFlags = IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB;
887             ici.hwndParent = hWnd;
888
889             if FAILED(UniformResourceLocatorW_InvokeCommand(&shortcut->uniformResourceLocatorW, (PURLINVOKECOMMANDINFOW) &ici))
890                     TRACE("failed to open URL: %s\n", debugstr_a(lpcstrUrl));
891         }
892
893         heap_free(shortcut);
894         heap_free(urlfilepath);
895     }
896 }