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