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