gdi32: Add support for drawing diagonal patterned lines.
[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 IUniformResourceLocatorA_iface;
48     IUniformResourceLocatorW IUniformResourceLocatorW_iface;
49     IPersistFile IPersistFile_iface;
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, IUniformResourceLocatorA_iface);
65 }
66
67 static inline InternetShortcut* impl_from_IUniformResourceLocatorW(IUniformResourceLocatorW *iface)
68 {
69     return CONTAINING_RECORD(iface, InternetShortcut, IUniformResourceLocatorW_iface);
70 }
71
72 static inline InternetShortcut* impl_from_IPersistFile(IPersistFile *iface)
73 {
74     return CONTAINING_RECORD(iface, InternetShortcut, IPersistFile_iface);
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->IUniformResourceLocatorA_iface;
150     else if (IsEqualGUID(&IID_IUniformResourceLocatorA, riid))
151         *ppvObject = &This->IUniformResourceLocatorA_iface;
152     else if (IsEqualGUID(&IID_IUniformResourceLocatorW, riid))
153         *ppvObject = &This->IUniformResourceLocatorW_iface;
154     else if (IsEqualGUID(&IID_IPersistFile, riid))
155         *ppvObject = &This->IPersistFile_iface;
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     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
242
243     TRACE("(%p, %p)\n", url, ppszURL);
244
245     if (!This->url) {
246         *ppszURL = NULL;
247         return S_FALSE;
248     }
249
250     *ppszURL = co_strdupW(This->url);
251     if (!*ppszURL)
252         return E_OUTOFMEMORY;
253
254     return S_OK;
255 }
256
257 static HRESULT WINAPI UniformResourceLocatorW_InvokeCommand(IUniformResourceLocatorW *url, PURLINVOKECOMMANDINFOW pCommandInfo)
258 {
259     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
260     WCHAR app[64];
261     HKEY hkey;
262     static const WCHAR wszURLProtocol[] = {'U','R','L',' ','P','r','o','t','o','c','o','l',0};
263     SHELLEXECUTEINFOW sei;
264     DWORD res, type;
265     HRESULT hres;
266
267     TRACE("%p %p\n", This, pCommandInfo );
268
269     if (pCommandInfo->dwcbSize < sizeof (URLINVOKECOMMANDINFOW))
270         return E_INVALIDARG;
271
272     if (pCommandInfo->dwFlags != IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB)
273     {
274         FIXME("(%p, %p): non-default verbs not implemented\n", url, pCommandInfo);
275         return E_NOTIMPL;
276     }
277
278     hres = CoInternetParseUrl(This->url, PARSE_SCHEMA, 0, app, sizeof(app)/sizeof(WCHAR), NULL, 0);
279     if(FAILED(hres))
280         return E_FAIL;
281
282     res = RegOpenKeyW(HKEY_CLASSES_ROOT, app, &hkey);
283     if(res != ERROR_SUCCESS)
284         return E_FAIL;
285
286     res = RegQueryValueExW(hkey, wszURLProtocol, NULL, &type, NULL, NULL);
287     RegCloseKey(hkey);
288     if(res != ERROR_SUCCESS || type != REG_SZ)
289         return E_FAIL;
290
291     memset(&sei, 0, sizeof(sei));
292     sei.cbSize = sizeof(sei);
293     sei.lpFile = This->url;
294     sei.nShow = SW_SHOW;
295
296     if( ShellExecuteExW(&sei) )
297         return S_OK;
298     else
299         return E_FAIL;
300 }
301
302 static HRESULT WINAPI UniformResourceLocatorA_QueryInterface(IUniformResourceLocatorA *url, REFIID riid, PVOID *ppvObject)
303 {
304     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
305     TRACE("(%p, %s, %p)\n", url, debugstr_guid(riid), ppvObject);
306     return Unknown_QueryInterface(This, riid, ppvObject);
307 }
308
309 static ULONG WINAPI UniformResourceLocatorA_AddRef(IUniformResourceLocatorA *url)
310 {
311     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
312     TRACE("(%p)\n", url);
313     return Unknown_AddRef(This);
314 }
315
316 static ULONG WINAPI UniformResourceLocatorA_Release(IUniformResourceLocatorA *url)
317 {
318     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
319     TRACE("(%p)\n", url);
320     return Unknown_Release(This);
321 }
322
323 static HRESULT WINAPI UniformResourceLocatorA_SetUrl(IUniformResourceLocatorA *url, LPCSTR pcszURL, DWORD dwInFlags)
324 {
325     WCHAR *newURL = NULL;
326     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
327     TRACE("(%p, %s, 0x%x)\n", url, debugstr_a(pcszURL), dwInFlags);
328     if (dwInFlags != 0)
329         FIXME("ignoring unsupported flags 0x%x\n", dwInFlags);
330     if (pcszURL != NULL)
331     {
332         newURL = co_strdupAtoW(pcszURL);
333         if (newURL == NULL)
334             return E_OUTOFMEMORY;
335     }
336     CoTaskMemFree(This->url);
337     This->url = newURL;
338     This->isDirty = TRUE;
339     return S_OK;
340 }
341
342 static HRESULT WINAPI UniformResourceLocatorA_GetUrl(IUniformResourceLocatorA *url, LPSTR *ppszURL)
343 {
344     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
345
346     TRACE("(%p, %p)\n", url, ppszURL);
347
348     if (!This->url) {
349         *ppszURL = NULL;
350         return S_FALSE;
351
352     }
353
354     *ppszURL = co_strdupWtoA(This->url);
355     if (!*ppszURL)
356         return E_OUTOFMEMORY;
357
358     return S_OK;
359 }
360
361 static HRESULT WINAPI UniformResourceLocatorA_InvokeCommand(IUniformResourceLocatorA *url, PURLINVOKECOMMANDINFOA pCommandInfo)
362 {
363     URLINVOKECOMMANDINFOW wideCommandInfo;
364     int len;
365     WCHAR *wideVerb;
366     HRESULT res;
367     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
368
369     wideCommandInfo.dwcbSize = sizeof wideCommandInfo;
370     wideCommandInfo.dwFlags = pCommandInfo->dwFlags;
371     wideCommandInfo.hwndParent = pCommandInfo->hwndParent;
372
373     len = MultiByteToWideChar(CP_ACP, 0, pCommandInfo->pcszVerb, -1, NULL, 0);
374     wideVerb = heap_alloc(len * sizeof(WCHAR));
375     MultiByteToWideChar(CP_ACP, 0, pCommandInfo->pcszVerb, -1, wideVerb, len);
376
377     wideCommandInfo.pcszVerb = wideVerb;
378
379     res = UniformResourceLocatorW_InvokeCommand(&This->IUniformResourceLocatorW_iface, &wideCommandInfo);
380     heap_free(wideVerb);
381
382     return res;
383 }
384
385 static HRESULT WINAPI PersistFile_QueryInterface(IPersistFile *pFile, REFIID riid, PVOID *ppvObject)
386 {
387     InternetShortcut *This = impl_from_IPersistFile(pFile);
388     TRACE("(%p, %s, %p)\n", pFile, debugstr_guid(riid), ppvObject);
389     return Unknown_QueryInterface(This, riid, ppvObject);
390 }
391
392 static ULONG WINAPI PersistFile_AddRef(IPersistFile *pFile)
393 {
394     InternetShortcut *This = impl_from_IPersistFile(pFile);
395     TRACE("(%p)\n", pFile);
396     return Unknown_AddRef(This);
397 }
398
399 static ULONG WINAPI PersistFile_Release(IPersistFile *pFile)
400 {
401     InternetShortcut *This = impl_from_IPersistFile(pFile);
402     TRACE("(%p)\n", pFile);
403     return Unknown_Release(This);
404 }
405
406 static HRESULT WINAPI PersistFile_GetClassID(IPersistFile *pFile, CLSID *pClassID)
407 {
408     TRACE("(%p, %p)\n", pFile, pClassID);
409     *pClassID = CLSID_InternetShortcut;
410     return S_OK;
411 }
412
413 static HRESULT WINAPI PersistFile_IsDirty(IPersistFile *pFile)
414 {
415     InternetShortcut *This = impl_from_IPersistFile(pFile);
416     TRACE("(%p)\n", pFile);
417     return This->isDirty ? S_OK : S_FALSE;
418 }
419
420 /* A helper function:  Allocate and fill rString.  Return number of bytes read. */
421 static DWORD get_profile_string(LPCWSTR lpAppName, LPCWSTR lpKeyName,
422                                 LPCWSTR lpFileName, WCHAR **rString )
423 {
424     DWORD r = 0;
425     DWORD len = 128;
426     WCHAR *buffer;
427
428     buffer = CoTaskMemAlloc(len * sizeof(*buffer));
429     if (buffer != NULL)
430     {
431         r = GetPrivateProfileStringW(lpAppName, lpKeyName, NULL, buffer, len, lpFileName);
432         while (r == len-1)
433         {
434             WCHAR *realloc_buf;
435
436             len *= 2;
437             realloc_buf = CoTaskMemRealloc(buffer, len * sizeof(*buffer));
438             if (realloc_buf == NULL)
439             {
440                 CoTaskMemFree(buffer);
441                 *rString = NULL;
442                 return 0;
443             }
444             buffer = realloc_buf;
445
446             r = GetPrivateProfileStringW(lpAppName, lpKeyName, NULL, buffer, len, lpFileName);
447         }
448     }
449
450     *rString = buffer;
451     return r;
452 }
453
454 static HRESULT WINAPI PersistFile_Load(IPersistFile *pFile, LPCOLESTR pszFileName, DWORD dwMode)
455 {
456     WCHAR str_header[] = {'I','n','t','e','r','n','e','t','S','h','o','r','t','c','u','t',0};
457     WCHAR str_URL[] = {'U','R','L',0};
458     WCHAR str_iconfile[] = {'i','c','o','n','f','i','l','e',0};
459     WCHAR str_iconindex[] = {'i','c','o','n','i','n','d','e','x',0};
460     WCHAR *filename = NULL;
461     HRESULT hr;
462     InternetShortcut *This = impl_from_IPersistFile(pFile);
463     TRACE("(%p, %s, 0x%x)\n", pFile, debugstr_w(pszFileName), dwMode);
464     if (dwMode != 0)
465         FIXME("ignoring unimplemented mode 0x%x\n", dwMode);
466     filename = co_strdupW(pszFileName);
467     if (filename != NULL)
468     {
469         DWORD r;
470         WCHAR *url;
471
472         r = get_profile_string(str_header, str_URL, pszFileName, &url);
473
474         if (url == NULL)
475         {
476             hr = E_OUTOFMEMORY;
477             CoTaskMemFree(filename);
478         }
479         else if (r == 0)
480         {
481             hr = E_FAIL;
482             CoTaskMemFree(filename);
483         }
484         else
485         {
486             hr = S_OK;
487             CoTaskMemFree(This->currentFile);
488             This->currentFile = filename;
489             CoTaskMemFree(This->url);
490             This->url = url;
491             This->isDirty = FALSE;
492         }
493
494         /* Now we're going to read in the iconfile and iconindex.
495            If we don't find them, that's not a failure case -- it's possible
496            that they just aren't in there. */
497         if (SUCCEEDED(hr))
498         {
499             IPropertyStorage *pPropStg;
500             WCHAR *iconfile;
501             WCHAR *iconindexstring;
502             hr = IPropertySetStorage_Open(This->property_set_storage, &FMTID_Intshcut,
503                                           STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
504                                           &pPropStg);
505
506             r = get_profile_string(str_header, str_iconfile, pszFileName, &iconfile);
507             if (iconfile != NULL)
508             {
509                 PROPSPEC ps;
510                 PROPVARIANT pv;
511                 ps.ulKind = PRSPEC_PROPID;
512                 ps.u.propid = PID_IS_ICONFILE;
513                 pv.vt = VT_LPWSTR;
514                 pv.u.pwszVal = iconfile;
515                 hr = IPropertyStorage_WriteMultiple(pPropStg, 1, &ps, &pv, 0);
516                 if (FAILED(hr))
517                 {
518                     TRACE("Failed to store the iconfile to our property storage.  hr = 0x%x\n", hr);
519                 }
520
521                 CoTaskMemFree(iconfile);
522             }
523
524             r = get_profile_string(str_header, str_iconindex, pszFileName, &iconindexstring);
525
526             if (iconindexstring != NULL)
527             {
528                 int iconindex;
529                 PROPSPEC ps;
530                 PROPVARIANT pv;
531                 char *iconindexastring = co_strdupWtoA(iconindexstring);
532                 sscanf(iconindexastring, "%d", &iconindex);
533                 CoTaskMemFree(iconindexastring);
534                 ps.ulKind = PRSPEC_PROPID;
535                 ps.u.propid = PID_IS_ICONINDEX;
536                 pv.vt = VT_I4;
537                 pv.u.iVal = iconindex;
538                 hr = IPropertyStorage_WriteMultiple(pPropStg, 1, &ps, &pv, 0);
539                 if (FAILED(hr))
540                 {
541                     TRACE("Failed to store the iconindex to our property storage.  hr = 0x%x\n", hr);
542                 }
543
544                 CoTaskMemFree(iconindexstring);
545             }
546
547             IPropertyStorage_Release(pPropStg);
548         }
549         else
550             hr = E_OUTOFMEMORY;
551     }
552     else
553         hr = E_OUTOFMEMORY;
554     return hr;
555 }
556
557 static HRESULT WINAPI PersistFile_Save(IPersistFile *pFile, LPCOLESTR pszFileName, BOOL fRemember)
558 {
559     HRESULT hr = S_OK;
560     INT len;
561     CHAR *url;
562     InternetShortcut *This = impl_from_IPersistFile(pFile);
563
564     TRACE("(%p, %s, %d)\n", pFile, debugstr_w(pszFileName), fRemember);
565
566     if (pszFileName != NULL && fRemember)
567     {
568         LPOLESTR oldFile = This->currentFile;
569         This->currentFile = co_strdupW(pszFileName);
570         if (This->currentFile == NULL)
571         {
572             This->currentFile = oldFile;
573             return E_OUTOFMEMORY;
574         }
575         CoTaskMemFree(oldFile);
576     }
577     if (This->url == NULL)
578         return E_FAIL;
579
580     /* Windows seems to always write:
581      *   ASCII "[InternetShortcut]" headers
582      *   ASCII names in "name=value" pairs
583      *   An ASCII (probably UTF8?) value in "URL=..."
584      */
585     len = WideCharToMultiByte(CP_UTF8, 0, This->url, -1, NULL, 0, 0, 0);
586     url = heap_alloc(len);
587     if (url != NULL)
588     {
589         HANDLE file;
590         WideCharToMultiByte(CP_UTF8, 0, This->url, -1, url, len, 0, 0);
591         file = CreateFileW(pszFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
592         if (file != INVALID_HANDLE_VALUE)
593         {
594             DWORD bytesWritten;
595             char *iconfile;
596             char str_header[] = "[InternetShortcut]";
597             char str_URL[] = "URL=";
598             char str_ICONFILE[] = "ICONFILE=";
599             char str_eol[] = "\r\n";
600             IPropertyStorage *pPropStgRead;
601             PROPSPEC ps[2];
602             PROPVARIANT pvread[2];
603             ps[0].ulKind = PRSPEC_PROPID;
604             ps[0].u.propid = PID_IS_ICONFILE;
605             ps[1].ulKind = PRSPEC_PROPID;
606             ps[1].u.propid = PID_IS_ICONINDEX;
607
608             WriteFile(file, str_header, lstrlenA(str_header), &bytesWritten, NULL);
609             WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
610             WriteFile(file, str_URL, lstrlenA(str_URL), &bytesWritten, NULL);
611             WriteFile(file, url, lstrlenA(url), &bytesWritten, NULL);
612             WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
613
614             hr = IPropertySetStorage_Open(This->property_set_storage, &FMTID_Intshcut, STGM_READ|STGM_SHARE_EXCLUSIVE, &pPropStgRead);
615             if SUCCEEDED(hr)
616             {
617                 hr = IPropertyStorage_ReadMultiple(pPropStgRead, 2, ps, pvread);
618                 if (hr == S_FALSE)
619                 {
620                     /* None of the properties are present, that's ok */
621                     hr = S_OK;
622                     IPropertyStorage_Release(pPropStgRead);
623                 }
624                 else if SUCCEEDED(hr)
625                 {
626                     char indexString[50];
627                     len = WideCharToMultiByte(CP_UTF8, 0, pvread[0].u.pwszVal, -1, NULL, 0, 0, 0);
628                     iconfile = heap_alloc(len);
629                     if (iconfile != NULL)
630                     {
631                         WideCharToMultiByte(CP_UTF8, 0, pvread[0].u.pwszVal, -1, iconfile, len, 0, 0);
632                         WriteFile(file, str_ICONFILE, lstrlenA(str_ICONFILE), &bytesWritten, NULL);
633                         WriteFile(file, iconfile, lstrlenA(iconfile), &bytesWritten, NULL);
634                         WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
635                     }
636
637                     sprintf(indexString, "ICONINDEX=%d", pvread[1].u.iVal);
638                     WriteFile(file, indexString, lstrlenA(indexString), &bytesWritten, NULL);
639                     WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
640
641                     IPropertyStorage_Release(pPropStgRead);
642                     PropVariantClear(&pvread[0]);
643                     PropVariantClear(&pvread[1]);
644                 }
645                 else
646                 {
647                     TRACE("Unable to read properties.\n");
648                 }
649             }
650             else
651             {
652                TRACE("Unable to get the IPropertyStorage.\n");
653             }
654
655             CloseHandle(file);
656             if (pszFileName == NULL || fRemember)
657                 This->isDirty = FALSE;
658             StartLinkProcessor(pszFileName);
659         }
660         else
661             hr = E_FAIL;
662         heap_free(url);
663     }
664     else
665         hr = E_OUTOFMEMORY;
666
667     return hr;
668 }
669
670 static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *pFile, LPCOLESTR pszFileName)
671 {
672     FIXME("(%p, %p): stub\n", pFile, pszFileName);
673     return E_NOTIMPL;
674 }
675
676 static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *pFile, LPOLESTR *ppszFileName)
677 {
678     HRESULT hr = S_OK;
679     InternetShortcut *This = impl_from_IPersistFile(pFile);
680     TRACE("(%p, %p)\n", pFile, ppszFileName);
681     if (This->currentFile == NULL)
682         *ppszFileName = NULL;
683     else
684     {
685         *ppszFileName = co_strdupW(This->currentFile);
686         if (*ppszFileName == NULL)
687             hr = E_OUTOFMEMORY;
688     }
689     return hr;
690 }
691
692 static HRESULT WINAPI PropertySetStorage_QueryInterface(IPropertySetStorage *iface, REFIID riid, PVOID *ppvObject)
693 {
694     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
695     TRACE("(%p)\n", iface);
696     return Unknown_QueryInterface(This, riid, ppvObject);
697 }
698
699 static ULONG WINAPI PropertySetStorage_AddRef(IPropertySetStorage *iface)
700 {
701     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
702     TRACE("(%p)\n", iface);
703     return Unknown_AddRef(This);
704 }
705
706 static ULONG WINAPI PropertySetStorage_Release(IPropertySetStorage *iface)
707 {
708     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
709     TRACE("(%p)\n", iface);
710     return Unknown_Release(This);
711 }
712
713 static HRESULT WINAPI PropertySetStorage_Create(
714         IPropertySetStorage* iface,
715         REFFMTID rfmtid,
716         const CLSID *pclsid,
717         DWORD grfFlags,
718         DWORD grfMode,
719         IPropertyStorage **ppprstg)
720 {
721     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
722     TRACE("(%s, %p, 0x%x, 0x%x, %p)\n", debugstr_guid(rfmtid), pclsid, grfFlags, grfMode, ppprstg);
723
724     return IPropertySetStorage_Create(This->property_set_storage,
725                                       rfmtid,
726                                       pclsid,
727                                       grfFlags,
728                                       grfMode,
729                                       ppprstg);
730 }
731
732 static HRESULT WINAPI PropertySetStorage_Open(
733         IPropertySetStorage* iface,
734         REFFMTID rfmtid,
735         DWORD grfMode,
736         IPropertyStorage **ppprstg)
737 {
738     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
739     TRACE("(%s, 0x%x, %p)\n", debugstr_guid(rfmtid), grfMode, ppprstg);
740
741     /* Note:  The |STGM_SHARE_EXCLUSIVE is to cope with a bug in the implementation.  Should be fixed in ole32. */
742     return IPropertySetStorage_Open(This->property_set_storage,
743                                     rfmtid,
744                                     grfMode|STGM_SHARE_EXCLUSIVE,
745                                     ppprstg);
746 }
747
748 static HRESULT WINAPI PropertySetStorage_Delete(IPropertySetStorage *iface, REFFMTID rfmtid)
749 {
750     InternetShortcut *This = impl_from_IPropertySetStorage(iface);
751     TRACE("(%s)\n", debugstr_guid(rfmtid));
752
753
754     return IPropertySetStorage_Delete(This->property_set_storage,
755                                       rfmtid);
756 }
757
758 static HRESULT WINAPI PropertySetStorage_Enum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **ppenum)
759 {
760     FIXME("(%p): stub\n", ppenum);
761     return E_NOTIMPL;
762 }
763
764 static const IUniformResourceLocatorWVtbl uniformResourceLocatorWVtbl = {
765     UniformResourceLocatorW_QueryInterface,
766     UniformResourceLocatorW_AddRef,
767     UniformResourceLocatorW_Release,
768     UniformResourceLocatorW_SetUrl,
769     UniformResourceLocatorW_GetUrl,
770     UniformResourceLocatorW_InvokeCommand
771 };
772
773 static const IUniformResourceLocatorAVtbl uniformResourceLocatorAVtbl = {
774     UniformResourceLocatorA_QueryInterface,
775     UniformResourceLocatorA_AddRef,
776     UniformResourceLocatorA_Release,
777     UniformResourceLocatorA_SetUrl,
778     UniformResourceLocatorA_GetUrl,
779     UniformResourceLocatorA_InvokeCommand
780 };
781
782 static const IPersistFileVtbl persistFileVtbl = {
783     PersistFile_QueryInterface,
784     PersistFile_AddRef,
785     PersistFile_Release,
786     PersistFile_GetClassID,
787     PersistFile_IsDirty,
788     PersistFile_Load,
789     PersistFile_Save,
790     PersistFile_SaveCompleted,
791     PersistFile_GetCurFile
792 };
793
794 static const IPropertySetStorageVtbl propertySetStorageVtbl = {
795     PropertySetStorage_QueryInterface,
796     PropertySetStorage_AddRef,
797     PropertySetStorage_Release,
798     PropertySetStorage_Create,
799     PropertySetStorage_Open,
800     PropertySetStorage_Delete,
801     PropertySetStorage_Enum
802 };
803
804 static InternetShortcut *create_shortcut(void)
805 {
806     InternetShortcut *newshortcut;
807
808     newshortcut = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(InternetShortcut));
809     if (newshortcut)
810     {
811         HRESULT hr;
812         IPropertyStorage *dummy;
813
814         newshortcut->IUniformResourceLocatorA_iface.lpVtbl = &uniformResourceLocatorAVtbl;
815         newshortcut->IUniformResourceLocatorW_iface.lpVtbl = &uniformResourceLocatorWVtbl;
816         newshortcut->IPersistFile_iface.lpVtbl = &persistFileVtbl;
817         newshortcut->IPropertySetStorage_iface.lpVtbl = &propertySetStorageVtbl;
818         newshortcut->refCount = 0;
819         hr = StgCreateStorageEx(NULL, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, STGFMT_STORAGE, 0, NULL, NULL, &IID_IPropertySetStorage, (void **) &newshortcut->property_set_storage);
820         if FAILED(hr)
821         {
822             TRACE("Failed to create the storage object needed for the shortcut.\n");
823             heap_free(newshortcut);
824             return NULL;
825         }
826
827         hr = IPropertySetStorage_Create(newshortcut->property_set_storage, &FMTID_Intshcut, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &dummy);
828         if FAILED(hr)
829         {
830             TRACE("Failed to create the property object needed for the shortcut.\n");
831             IPropertySetStorage_Release(newshortcut->property_set_storage);
832             heap_free(newshortcut);
833             return NULL;
834         }
835         IPropertySetStorage_Release(dummy);
836     }
837
838     return newshortcut;
839 }
840
841 HRESULT InternetShortcut_Create(IUnknown *pOuter, REFIID riid, void **ppv)
842 {
843     InternetShortcut *This;
844     HRESULT hr;
845
846     TRACE("(%p, %s, %p)\n", pOuter, debugstr_guid(riid), ppv);
847
848     *ppv = NULL;
849
850     if(pOuter)
851         return CLASS_E_NOAGGREGATION;
852
853     This = create_shortcut();
854     if (This)
855     {
856         hr = Unknown_QueryInterface(This, riid, ppv);
857         if (SUCCEEDED(hr))
858             SHDOCVW_LockModule();
859         else
860             heap_free(This);
861         return hr;
862     }
863     else
864         return E_OUTOFMEMORY;
865 }
866
867
868 /**********************************************************************
869  * OpenURL  (SHDOCVW.@)
870  */
871 void WINAPI OpenURL(HWND hWnd, HINSTANCE hInst, LPCSTR lpcstrUrl, int nShowCmd)
872 {
873     InternetShortcut *shortcut;
874     WCHAR* urlfilepath = NULL;
875     shortcut = create_shortcut();
876
877     if (shortcut)
878     {
879         int len;
880
881         len = MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, NULL, 0);
882         urlfilepath = heap_alloc(len * sizeof(WCHAR));
883         MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, urlfilepath, len);
884
885         if(SUCCEEDED(IPersistFile_Load(&shortcut->IPersistFile_iface, urlfilepath, 0)))
886         {
887             URLINVOKECOMMANDINFOW ici;
888
889             memset( &ici, 0, sizeof ici );
890             ici.dwcbSize = sizeof ici;
891             ici.dwFlags = IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB;
892             ici.hwndParent = hWnd;
893
894             if(FAILED(UniformResourceLocatorW_InvokeCommand(&shortcut->IUniformResourceLocatorW_iface, (PURLINVOKECOMMANDINFOW) &ici)))
895                     TRACE("failed to open URL: %s\n", debugstr_a(lpcstrUrl));
896         }
897
898         heap_free(shortcut);
899         heap_free(urlfilepath);
900     }
901 }