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