2 * Copyright 2008 Damjan Jovanovic
4 * ShellLink's barely documented cousin that handles URLs.
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.
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.
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
23 * Implement the IShellLinkA/W interfaces
24 * Handle the SetURL flags
25 * Implement any other interfaces? Does any software actually use them?
27 * The installer for the Zuma Deluxe Popcap game is good for testing.
30 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(shdocvw
);
40 IUniformResourceLocatorA uniformResourceLocatorA
;
41 IUniformResourceLocatorW uniformResourceLocatorW
;
42 IPersistFile persistFile
;
51 /* utility functions */
53 static inline InternetShortcut
* impl_from_IUniformResourceLocatorA(IUniformResourceLocatorA
*iface
)
55 return (InternetShortcut
*)((char*)iface
- FIELD_OFFSET(InternetShortcut
, uniformResourceLocatorA
));
58 static inline InternetShortcut
* impl_from_IUniformResourceLocatorW(IUniformResourceLocatorW
*iface
)
60 return (InternetShortcut
*)((char*)iface
- FIELD_OFFSET(InternetShortcut
, uniformResourceLocatorW
));
63 static inline InternetShortcut
* impl_from_IPersistFile(IPersistFile
*iface
)
65 return (InternetShortcut
*)((char*)iface
- FIELD_OFFSET(InternetShortcut
, persistFile
));
68 static BOOL
run_winemenubuilder( const WCHAR
*args
)
70 static const WCHAR menubuilder
[] = {'\\','w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',0};
74 PROCESS_INFORMATION pi
;
78 GetSystemDirectoryW( app
, MAX_PATH
- sizeof(menubuilder
)/sizeof(WCHAR
) );
79 strcatW( app
, menubuilder
);
81 len
= (strlenW( app
) + strlenW( args
) + 1) * sizeof(WCHAR
);
82 buffer
= heap_alloc( len
);
86 strcpyW( buffer
, app
);
87 strcatW( buffer
, args
);
89 TRACE("starting %s\n",debugstr_w(buffer
));
91 memset(&si
, 0, sizeof(si
));
94 ret
= CreateProcessW( app
, buffer
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
);
100 CloseHandle( pi
.hProcess
);
101 CloseHandle( pi
.hThread
);
107 static BOOL
StartLinkProcessor( LPCOLESTR szLink
)
109 static const WCHAR szFormat
[] = { ' ','-','w',' ','-','u',' ','"','%','s','"',0 };
114 len
= sizeof(szFormat
) + lstrlenW( szLink
) * sizeof(WCHAR
);
115 buffer
= heap_alloc( len
);
119 wsprintfW( buffer
, szFormat
, szLink
);
120 ret
= run_winemenubuilder( buffer
);
125 /* interface functions */
127 static HRESULT
Unknown_QueryInterface(InternetShortcut
*This
, REFIID riid
, PVOID
*ppvObject
)
129 TRACE("(%p, %s, %p)\n", This
, debugstr_guid(riid
), ppvObject
);
131 if (IsEqualGUID(&IID_IUnknown
, riid
))
132 *ppvObject
= &This
->uniformResourceLocatorA
;
133 else if (IsEqualGUID(&IID_IUniformResourceLocatorA
, riid
))
134 *ppvObject
= &This
->uniformResourceLocatorA
;
135 else if (IsEqualGUID(&IID_IUniformResourceLocatorW
, riid
))
136 *ppvObject
= &This
->uniformResourceLocatorW
;
137 else if (IsEqualGUID(&IID_IPersistFile
, riid
))
138 *ppvObject
= &This
->persistFile
;
139 else if (IsEqualGUID(&IID_IShellLinkA
, riid
))
141 FIXME("The IShellLinkA interface is not yet supported by InternetShortcut\n");
142 return E_NOINTERFACE
;
144 else if (IsEqualGUID(&IID_IShellLinkW
, riid
))
146 FIXME("The IShellLinkW interface is not yet supported by InternetShortcut\n");
147 return E_NOINTERFACE
;
151 FIXME("Interface with GUID %s not yet implemented by InternetShortcut\n", debugstr_guid(riid
));
152 return E_NOINTERFACE
;
154 IUnknown_AddRef((IUnknown
*)*ppvObject
);
158 static ULONG
Unknown_AddRef(InternetShortcut
*This
)
160 TRACE("(%p)\n", This
);
161 return InterlockedIncrement(&This
->refCount
);
164 static ULONG
Unknown_Release(InternetShortcut
*This
)
167 TRACE("(%p)\n", This
);
168 count
= InterlockedDecrement(&This
->refCount
);
171 CoTaskMemFree(This
->url
);
172 CoTaskMemFree(This
->currentFile
);
174 SHDOCVW_UnlockModule();
179 static HRESULT WINAPI
UniformResourceLocatorW_QueryInterface(IUniformResourceLocatorW
*url
, REFIID riid
, PVOID
*ppvObject
)
181 InternetShortcut
*This
= impl_from_IUniformResourceLocatorW(url
);
182 TRACE("(%p, %s, %p)\n", url
, debugstr_guid(riid
), ppvObject
);
183 return Unknown_QueryInterface(This
, riid
, ppvObject
);
186 static ULONG WINAPI
UniformResourceLocatorW_AddRef(IUniformResourceLocatorW
*url
)
188 InternetShortcut
*This
= impl_from_IUniformResourceLocatorW(url
);
189 TRACE("(%p)\n", url
);
190 return Unknown_AddRef(This
);
193 static ULONG WINAPI
UniformResourceLocatorW_Release(IUniformResourceLocatorW
*url
)
195 InternetShortcut
*This
= impl_from_IUniformResourceLocatorW(url
);
196 TRACE("(%p)\n", url
);
197 return Unknown_Release(This
);
200 static HRESULT WINAPI
UniformResourceLocatorW_SetUrl(IUniformResourceLocatorW
*url
, LPCWSTR pcszURL
, DWORD dwInFlags
)
202 WCHAR
*newURL
= NULL
;
203 InternetShortcut
*This
= impl_from_IUniformResourceLocatorW(url
);
204 TRACE("(%p, %s, 0x%x)\n", url
, debugstr_w(pcszURL
), dwInFlags
);
206 FIXME("ignoring unsupported flags 0x%x\n", dwInFlags
);
209 newURL
= co_strdupW(pcszURL
);
211 return E_OUTOFMEMORY
;
213 CoTaskMemFree(This
->url
);
215 This
->isDirty
= TRUE
;
219 static HRESULT WINAPI
UniformResourceLocatorW_GetUrl(IUniformResourceLocatorW
*url
, LPWSTR
*ppszURL
)
222 InternetShortcut
*This
= impl_from_IUniformResourceLocatorW(url
);
223 TRACE("(%p, %p)\n", url
, ppszURL
);
224 if (This
->url
== NULL
)
228 *ppszURL
= co_strdupW(This
->url
);
229 if (*ppszURL
== NULL
)
235 static HRESULT WINAPI
UniformResourceLocatorW_InvokeCommand(IUniformResourceLocatorW
*url
, PURLINVOKECOMMANDINFOW pCommandInfo
)
237 FIXME("(%p, %p): stub\n", url
, pCommandInfo
);
241 static HRESULT WINAPI
UniformResourceLocatorA_QueryInterface(IUniformResourceLocatorA
*url
, REFIID riid
, PVOID
*ppvObject
)
243 InternetShortcut
*This
= impl_from_IUniformResourceLocatorA(url
);
244 TRACE("(%p, %s, %p)\n", url
, debugstr_guid(riid
), ppvObject
);
245 return Unknown_QueryInterface(This
, riid
, ppvObject
);
248 static ULONG WINAPI
UniformResourceLocatorA_AddRef(IUniformResourceLocatorA
*url
)
250 InternetShortcut
*This
= impl_from_IUniformResourceLocatorA(url
);
251 TRACE("(%p)\n", url
);
252 return Unknown_AddRef(This
);
255 static ULONG WINAPI
UniformResourceLocatorA_Release(IUniformResourceLocatorA
*url
)
257 InternetShortcut
*This
= impl_from_IUniformResourceLocatorA(url
);
258 TRACE("(%p)\n", url
);
259 return Unknown_Release(This
);
262 static HRESULT WINAPI
UniformResourceLocatorA_SetUrl(IUniformResourceLocatorA
*url
, LPCSTR pcszURL
, DWORD dwInFlags
)
264 WCHAR
*newURL
= NULL
;
265 InternetShortcut
*This
= impl_from_IUniformResourceLocatorA(url
);
266 TRACE("(%p, %s, 0x%x)\n", url
, debugstr_a(pcszURL
), dwInFlags
);
268 FIXME("ignoring unsupported flags 0x%x\n", dwInFlags
);
271 newURL
= co_strdupAtoW(pcszURL
);
273 return E_OUTOFMEMORY
;
275 CoTaskMemFree(This
->url
);
277 This
->isDirty
= TRUE
;
281 static HRESULT WINAPI
UniformResourceLocatorA_GetUrl(IUniformResourceLocatorA
*url
, LPSTR
*ppszURL
)
284 InternetShortcut
*This
= impl_from_IUniformResourceLocatorA(url
);
285 TRACE("(%p, %p)\n", url
, ppszURL
);
286 if (This
->url
== NULL
)
290 *ppszURL
= co_strdupWtoA(This
->url
);
291 if (*ppszURL
== NULL
)
297 static HRESULT WINAPI
UniformResourceLocatorA_InvokeCommand(IUniformResourceLocatorA
*url
, PURLINVOKECOMMANDINFOA pCommandInfo
)
299 FIXME("(%p, %p): stub\n", url
, pCommandInfo
);
303 static HRESULT WINAPI
PersistFile_QueryInterface(IPersistFile
*pFile
, REFIID riid
, PVOID
*ppvObject
)
305 InternetShortcut
*This
= impl_from_IPersistFile(pFile
);
306 TRACE("(%p, %s, %p)\n", pFile
, debugstr_guid(riid
), ppvObject
);
307 return Unknown_QueryInterface(This
, riid
, ppvObject
);
310 static ULONG WINAPI
PersistFile_AddRef(IPersistFile
*pFile
)
312 InternetShortcut
*This
= impl_from_IPersistFile(pFile
);
313 TRACE("(%p)\n", pFile
);
314 return Unknown_AddRef(This
);
317 static ULONG WINAPI
PersistFile_Release(IPersistFile
*pFile
)
319 InternetShortcut
*This
= impl_from_IPersistFile(pFile
);
320 TRACE("(%p)\n", pFile
);
321 return Unknown_Release(This
);
324 static HRESULT WINAPI
PersistFile_GetClassID(IPersistFile
*pFile
, CLSID
*pClassID
)
326 TRACE("(%p, %p)\n", pFile
, pClassID
);
327 *pClassID
= CLSID_InternetShortcut
;
331 static HRESULT WINAPI
PersistFile_IsDirty(IPersistFile
*pFile
)
333 InternetShortcut
*This
= impl_from_IPersistFile(pFile
);
334 TRACE("(%p)\n", pFile
);
335 return This
->isDirty
? S_OK
: S_FALSE
;
338 static HRESULT WINAPI
PersistFile_Load(IPersistFile
*pFile
, LPCOLESTR pszFileName
, DWORD dwMode
)
340 WCHAR str_header
[] = {'I','n','t','e','r','n','e','t','S','h','o','r','t','c','u','t',0};
341 WCHAR str_URL
[] = {'U','R','L',0};
342 WCHAR
*filename
= NULL
;
344 InternetShortcut
*This
= impl_from_IPersistFile(pFile
);
345 TRACE("(%p, %s, 0x%x)\n", pFile
, debugstr_w(pszFileName
), dwMode
);
347 FIXME("ignoring unimplemented mode 0x%x\n", dwMode
);
348 filename
= co_strdupW(pszFileName
);
349 if (filename
!= NULL
)
353 WCHAR
*url
= CoTaskMemAlloc(len
*sizeof(WCHAR
));
356 r
= GetPrivateProfileStringW(str_header
, str_URL
, NULL
, url
, len
, pszFileName
);
361 url
= CoTaskMemAlloc(len
);
364 r
= GetPrivateProfileStringW(str_header
, str_URL
, NULL
, url
, len
, pszFileName
);
368 else if (url
!= NULL
)
370 CoTaskMemFree(This
->currentFile
);
371 This
->currentFile
= filename
;
372 CoTaskMemFree(This
->url
);
374 This
->isDirty
= FALSE
;
383 CoTaskMemFree(filename
);
390 static HRESULT WINAPI
PersistFile_Save(IPersistFile
*pFile
, LPCOLESTR pszFileName
, BOOL fRemember
)
395 InternetShortcut
*This
= impl_from_IPersistFile(pFile
);
397 TRACE("(%p, %s, %d)\n", pFile
, debugstr_w(pszFileName
), fRemember
);
399 if (pszFileName
!= NULL
&& fRemember
)
401 LPOLESTR oldFile
= This
->currentFile
;
402 This
->currentFile
= co_strdupW(pszFileName
);
403 if (This
->currentFile
== NULL
)
405 This
->currentFile
= oldFile
;
406 return E_OUTOFMEMORY
;
408 CoTaskMemFree(oldFile
);
410 if (This
->url
== NULL
)
413 /* Windows seems to always write:
414 * ASCII "[InternetShortcut]" headers
415 * ASCII names in "name=value" pairs
416 * An ASCII (probably UTF8?) value in "URL=..."
418 len
= WideCharToMultiByte(CP_UTF8
, 0, This
->url
, -1, NULL
, 0, 0, 0);
419 url
= heap_alloc(len
);
423 WideCharToMultiByte(CP_UTF8
, 0, This
->url
, -1, url
, len
, 0, 0);
424 file
= CreateFileW(pszFileName
, GENERIC_WRITE
, FILE_SHARE_READ
, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
425 if (file
!= INVALID_HANDLE_VALUE
)
428 char str_header
[] = "[InternetShortcut]";
429 char str_URL
[] = "URL=";
430 char str_eol
[] = "\r\n";
432 WriteFile(file
, str_header
, lstrlenA(str_header
), &bytesWritten
, NULL
);
433 WriteFile(file
, str_eol
, lstrlenA(str_eol
), &bytesWritten
, NULL
);
434 WriteFile(file
, str_URL
, lstrlenA(str_URL
), &bytesWritten
, NULL
);
435 WriteFile(file
, url
, lstrlenA(url
), &bytesWritten
, NULL
);
436 WriteFile(file
, str_eol
, lstrlenA(str_eol
), &bytesWritten
, NULL
);
438 if (pszFileName
== NULL
|| fRemember
)
439 This
->isDirty
= FALSE
;
440 StartLinkProcessor(pszFileName
);
452 static HRESULT WINAPI
PersistFile_SaveCompleted(IPersistFile
*pFile
, LPCOLESTR pszFileName
)
454 FIXME("(%p, %p): stub\n", pFile
, pszFileName
);
458 static HRESULT WINAPI
PersistFile_GetCurFile(IPersistFile
*pFile
, LPOLESTR
*ppszFileName
)
461 InternetShortcut
*This
= impl_from_IPersistFile(pFile
);
462 TRACE("(%p, %p)\n", pFile
, ppszFileName
);
463 if (This
->currentFile
== NULL
)
464 *ppszFileName
= NULL
;
467 *ppszFileName
= co_strdupW(This
->currentFile
);
468 if (*ppszFileName
== NULL
)
476 static const IUniformResourceLocatorWVtbl uniformResourceLocatorWVtbl
= {
477 UniformResourceLocatorW_QueryInterface
,
478 UniformResourceLocatorW_AddRef
,
479 UniformResourceLocatorW_Release
,
480 UniformResourceLocatorW_SetUrl
,
481 UniformResourceLocatorW_GetUrl
,
482 UniformResourceLocatorW_InvokeCommand
485 static const IUniformResourceLocatorAVtbl uniformResourceLocatorAVtbl
= {
486 UniformResourceLocatorA_QueryInterface
,
487 UniformResourceLocatorA_AddRef
,
488 UniformResourceLocatorA_Release
,
489 UniformResourceLocatorA_SetUrl
,
490 UniformResourceLocatorA_GetUrl
,
491 UniformResourceLocatorA_InvokeCommand
494 static const IPersistFileVtbl persistFileVtbl
= {
495 PersistFile_QueryInterface
,
498 PersistFile_GetClassID
,
502 PersistFile_SaveCompleted
,
503 PersistFile_GetCurFile
506 HRESULT
InternetShortcut_Create(IUnknown
*pOuter
, REFIID riid
, void **ppv
)
508 InternetShortcut
*This
;
511 TRACE("(%p, %s, %p)\n", pOuter
, debugstr_guid(riid
), ppv
);
516 return CLASS_E_NOAGGREGATION
;
518 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(InternetShortcut
));
521 This
->uniformResourceLocatorA
.lpVtbl
= &uniformResourceLocatorAVtbl
;
522 This
->uniformResourceLocatorW
.lpVtbl
= &uniformResourceLocatorWVtbl
;
523 This
->persistFile
.lpVtbl
= &persistFileVtbl
;
525 hr
= Unknown_QueryInterface(This
, riid
, ppv
);
527 SHDOCVW_LockModule();
533 return E_OUTOFMEMORY
;