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