1 /* 2 * IQueryAssociations object and helper functions 3 * 4 * Copyright 2002 Jon Griffiths 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 #include "precomp.h" 22 23 WINE_DEFAULT_DEBUG_CHANNEL(shell); 24 25 EXTERN_C HRESULT SHELL32_AssocGetFSDirectoryDescription(PWSTR Buf, UINT cchBuf) 26 { 27 static WCHAR cache[33] = {}; 28 if (!*cache) 29 LoadStringW(shell32_hInstance, IDS_DIRECTORY, cache, _countof(cache)); 30 return StringCchCopyW(Buf, cchBuf, cache); 31 } 32 33 static HRESULT GetExtensionDefaultDescription(PCWSTR DotExt, PWSTR Buf, UINT cchBuf) 34 { 35 static WCHAR fmt[33] = {}; 36 if (!*fmt) 37 LoadStringW(shell32_hInstance, IDS_ANY_FILE, fmt, _countof(fmt)); 38 return StringCchPrintfW(Buf, cchBuf, fmt, DotExt); 39 } 40 41 static HRESULT SHELL32_AssocGetExtensionDescription(PCWSTR DotExt, PWSTR Buf, UINT cchBuf) 42 { 43 HRESULT hr; 44 if (!DotExt[0] || (!DotExt[1] && DotExt[0] == '.')) 45 { 46 if (SUCCEEDED(hr = GetExtensionDefaultDescription(L"", Buf, cchBuf))) 47 StrTrimW(Buf, L" -"); // Remove the empty %s so we are left with "File" 48 return hr; 49 } 50 HKEY hKey; 51 if (SUCCEEDED(hr = HCR_GetProgIdKeyOfExtension(DotExt, &hKey, TRUE))) 52 { 53 DWORD err = RegLoadMUIStringW(hKey, L"FriendlyTypeName", Buf, cchBuf, NULL, 0, NULL); 54 if (err && hr == S_OK) // ProgId default value fallback (but not if we only have a .ext key) 55 { 56 DWORD cb = cchBuf * sizeof(*Buf); 57 err = RegGetValueW(hKey, NULL, NULL, RRF_RT_REG_SZ, NULL, Buf, &cb); 58 } 59 RegCloseKey(hKey); 60 if (!err) 61 return err; 62 } 63 // No information in the registry, default to "UPPERCASEEXT File" 64 WCHAR ext[MAX_PATH + 33]; 65 if (LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE, ++DotExt, -1, ext, _countof(ext))) 66 DotExt = ext; 67 return GetExtensionDefaultDescription(DotExt, Buf, cchBuf); 68 } 69 70 EXTERN_C HRESULT SHELL32_AssocGetFileDescription(PCWSTR Name, PWSTR Buf, UINT cchBuf) 71 { 72 return SHELL32_AssocGetExtensionDescription(PathFindExtensionW(Name), Buf, cchBuf); 73 } 74 75 /************************************************************************** 76 * IQueryAssociations 77 * 78 * DESCRIPTION 79 * This object provides a layer of abstraction over the system registry in 80 * order to simplify the process of parsing associations between files. 81 * Associations in this context means the registry entries that link (for 82 * example) the extension of a file with its description, list of 83 * applications to open the file with, and actions that can be performed on it 84 * (the shell displays such information in the context menu of explorer 85 * when you right-click on a file). 86 * 87 * HELPERS 88 * You can use this object transparently by calling the helper functions 89 * AssocQueryKeyA(), AssocQueryStringA() and AssocQueryStringByKeyA(). These 90 * create an IQueryAssociations object, perform the requested actions 91 * and then dispose of the object. Alternatively, you can create an instance 92 * of the object using AssocCreate() and call the following methods on it: 93 * 94 * METHODS 95 */ 96 97 CQueryAssociations::CQueryAssociations() : hkeySource(0), hkeyProgID(0) 98 { 99 } 100 101 CQueryAssociations::~CQueryAssociations() 102 { 103 } 104 105 /************************************************************************** 106 * IQueryAssociations_Init 107 * 108 * Initialise an IQueryAssociations object. 109 * 110 * PARAMS 111 * cfFlags [I] ASSOCF_ flags from "shlwapi.h" 112 * pszAssoc [I] String for the root key name, or NULL if hkeyProgid is given 113 * hkeyProgid [I] Handle for the root key, or NULL if pszAssoc is given 114 * hWnd [I] Reserved, must be NULL. 115 * 116 * RETURNS 117 * Success: S_OK. iface is initialised with the parameters given. 118 * Failure: An HRESULT error code indicating the error. 119 */ 120 HRESULT STDMETHODCALLTYPE CQueryAssociations::Init( 121 ASSOCF cfFlags, 122 LPCWSTR pszAssoc, 123 HKEY hkeyProgid, 124 HWND hWnd) 125 { 126 TRACE("(%p)->(%d,%s,%p,%p)\n", this, 127 cfFlags, 128 debugstr_w(pszAssoc), 129 hkeyProgid, 130 hWnd); 131 132 if (hWnd != NULL) 133 { 134 FIXME("hwnd != NULL not supported\n"); 135 } 136 137 if (cfFlags != 0) 138 { 139 FIXME("unsupported flags: %x\n", cfFlags); 140 } 141 142 RegCloseKey(this->hkeySource); 143 RegCloseKey(this->hkeyProgID); 144 this->hkeySource = this->hkeyProgID = NULL; 145 if (pszAssoc != NULL) 146 { 147 WCHAR *progId; 148 HRESULT hr; 149 LPCWSTR pchDotExt; 150 151 if (StrChrW(pszAssoc, L'\\')) 152 { 153 pchDotExt = PathFindExtensionW(pszAssoc); 154 if (pchDotExt && *pchDotExt) 155 pszAssoc = pchDotExt; 156 } 157 158 LONG ret = RegOpenKeyExW(HKEY_CLASSES_ROOT, 159 pszAssoc, 160 0, 161 KEY_READ, 162 &this->hkeySource); 163 if (ret) 164 { 165 return S_OK; 166 } 167 168 /* if this is a progid */ 169 if (*pszAssoc != '.' && *pszAssoc != '{') 170 { 171 this->hkeyProgID = this->hkeySource; 172 return S_OK; 173 } 174 175 /* if it's not a progid, it's a file extension or clsid */ 176 if (*pszAssoc == '.') 177 { 178 /* for a file extension, the progid is the default value */ 179 hr = this->GetValue(this->hkeySource, NULL, (void**)&progId, NULL); 180 if (FAILED(hr)) 181 return S_OK; 182 } 183 else /* if (*pszAssoc == '{') */ 184 { 185 HKEY progIdKey; 186 /* for a clsid, the progid is the default value of the ProgID subkey */ 187 ret = RegOpenKeyExW(this->hkeySource, L"ProgID", 0, KEY_READ, &progIdKey); 188 if (ret != ERROR_SUCCESS) 189 return S_OK; 190 hr = this->GetValue(progIdKey, NULL, (void**)&progId, NULL); 191 if (FAILED(hr)) 192 return S_OK; 193 RegCloseKey(progIdKey); 194 } 195 196 /* open the actual progid key, the one with the shell subkey */ 197 ret = RegOpenKeyExW(HKEY_CLASSES_ROOT, 198 progId, 199 0, 200 KEY_READ, 201 &this->hkeyProgID); 202 HeapFree(GetProcessHeap(), 0, progId); 203 204 return S_OK; 205 } 206 else if (hkeyProgid != NULL) 207 { 208 /* reopen the key so we don't end up closing a key owned by the caller */ 209 RegOpenKeyExW(hkeyProgid, NULL, 0, KEY_READ, &this->hkeyProgID); 210 this->hkeySource = this->hkeyProgID; 211 return S_OK; 212 } 213 else 214 return E_INVALIDARG; 215 } 216 217 /************************************************************************** 218 * IQueryAssociations_GetString 219 * 220 * Get a file association string from the registry. 221 * 222 * PARAMS 223 * cfFlags [I] ASSOCF_ flags from "shlwapi.h" 224 * str [I] Type of string to get (ASSOCSTR enum from "shlwapi.h") 225 * pszExtra [I] Extra information about the string location 226 * pszOut [O] Destination for the association string 227 * pcchOut [I/O] Length of pszOut 228 * 229 * RETURNS 230 * Success: S_OK. pszOut contains the string, pcchOut contains its length. 231 * Failure: An HRESULT error code indicating the error. 232 */ 233 HRESULT STDMETHODCALLTYPE CQueryAssociations::GetString( 234 ASSOCF flags, 235 ASSOCSTR str, 236 LPCWSTR pszExtra, 237 LPWSTR pszOut, 238 DWORD *pcchOut) 239 { 240 const ASSOCF unimplemented_flags = ~ASSOCF_NOTRUNCATE; 241 DWORD len = 0; 242 HRESULT hr; 243 WCHAR path[MAX_PATH]; 244 245 TRACE("(%p)->(0x%08x, %u, %s, %p, %p)\n", this, flags, str, debugstr_w(pszExtra), pszOut, pcchOut); 246 if (flags & unimplemented_flags) 247 { 248 FIXME("%08x: unimplemented flags\n", flags & unimplemented_flags); 249 } 250 251 if (!pcchOut) 252 { 253 return E_UNEXPECTED; 254 } 255 256 if (!this->hkeySource && !this->hkeyProgID) 257 { 258 return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION); 259 } 260 261 switch (str) 262 { 263 case ASSOCSTR_COMMAND: 264 { 265 WCHAR *command; 266 hr = this->GetCommand(pszExtra, &command); 267 if (SUCCEEDED(hr)) 268 { 269 hr = this->ReturnString(flags, pszOut, pcchOut, command, strlenW(command) + 1); 270 HeapFree(GetProcessHeap(), 0, command); 271 } 272 return hr; 273 } 274 case ASSOCSTR_EXECUTABLE: 275 { 276 hr = this->GetExecutable(pszExtra, path, MAX_PATH, &len); 277 if (FAILED_UNEXPECTEDLY(hr)) 278 { 279 return hr; 280 } 281 len++; 282 return this->ReturnString(flags, pszOut, pcchOut, path, len); 283 } 284 case ASSOCSTR_FRIENDLYDOCNAME: 285 { 286 WCHAR *pszFileType; 287 288 hr = this->GetValue(this->hkeySource, NULL, (void**)&pszFileType, NULL); 289 if (FAILED(hr)) 290 { 291 return hr; 292 } 293 DWORD size = 0; 294 DWORD ret = RegGetValueW(HKEY_CLASSES_ROOT, pszFileType, NULL, RRF_RT_REG_SZ, NULL, NULL, &size); 295 if (ret == ERROR_SUCCESS) 296 { 297 WCHAR *docName = static_cast<WCHAR *>(HeapAlloc(GetProcessHeap(), 0, size)); 298 if (docName) 299 { 300 ret = RegGetValueW(HKEY_CLASSES_ROOT, pszFileType, NULL, RRF_RT_REG_SZ, NULL, docName, &size); 301 if (ret == ERROR_SUCCESS) 302 { 303 hr = this->ReturnString(flags, pszOut, pcchOut, docName, strlenW(docName) + 1); 304 } 305 else 306 { 307 hr = HRESULT_FROM_WIN32(ret); 308 } 309 HeapFree(GetProcessHeap(), 0, docName); 310 } 311 else 312 { 313 hr = E_OUTOFMEMORY; 314 } 315 } 316 else 317 { 318 hr = HRESULT_FROM_WIN32(ret); 319 } 320 HeapFree(GetProcessHeap(), 0, pszFileType); 321 return hr; 322 } 323 case ASSOCSTR_FRIENDLYAPPNAME: 324 { 325 PVOID verinfoW = NULL; 326 DWORD size, retval = 0; 327 UINT flen; 328 WCHAR *bufW; 329 WCHAR fileDescW[41]; 330 331 hr = this->GetExecutable(pszExtra, path, MAX_PATH, &len); 332 if (FAILED(hr)) 333 { 334 return hr; 335 } 336 retval = GetFileVersionInfoSizeW(path, &size); 337 if (!retval) 338 { 339 goto get_friendly_name_fail; 340 } 341 verinfoW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, retval); 342 if (!verinfoW) 343 { 344 return E_OUTOFMEMORY; 345 } 346 if (!GetFileVersionInfoW(path, 0, retval, verinfoW)) 347 { 348 goto get_friendly_name_fail; 349 } 350 if (VerQueryValueW(verinfoW, L"\\VarFileInfo\\Translation", (LPVOID *)&bufW, &flen)) 351 { 352 UINT i; 353 DWORD *langCodeDesc = (DWORD *)bufW; 354 for (i = 0; i < flen / sizeof(DWORD); i++) 355 { 356 sprintfW(fileDescW, L"\\StringFileInfo\\%04x%04x\\FileDescription", 357 LOWORD(langCodeDesc[i]), HIWORD(langCodeDesc[i])); 358 if (VerQueryValueW(verinfoW, fileDescW, (LPVOID *)&bufW, &flen)) 359 { 360 /* Does strlenW(bufW) == 0 mean we use the filename? */ 361 len = strlenW(bufW) + 1; 362 TRACE("found FileDescription: %s\n", debugstr_w(bufW)); 363 hr = this->ReturnString(flags, pszOut, pcchOut, bufW, len); 364 HeapFree(GetProcessHeap(), 0, verinfoW); 365 return hr; 366 } 367 } 368 } 369 get_friendly_name_fail: 370 PathRemoveExtensionW(path); 371 PathStripPathW(path); 372 TRACE("using filename: %s\n", debugstr_w(path)); 373 hr = this->ReturnString(flags, pszOut, pcchOut, path, strlenW(path) + 1); 374 HeapFree(GetProcessHeap(), 0, verinfoW); 375 return hr; 376 } 377 case ASSOCSTR_CONTENTTYPE: 378 { 379 DWORD size = 0; 380 DWORD ret = RegGetValueW(this->hkeySource, NULL, L"Content Type", RRF_RT_REG_SZ, NULL, NULL, &size); 381 if (ret != ERROR_SUCCESS) 382 { 383 return HRESULT_FROM_WIN32(ret); 384 } 385 WCHAR *contentType = static_cast<WCHAR *>(HeapAlloc(GetProcessHeap(), 0, size)); 386 if (contentType != NULL) 387 { 388 ret = RegGetValueW(this->hkeySource, NULL, L"Content Type", RRF_RT_REG_SZ, NULL, contentType, &size); 389 if (ret == ERROR_SUCCESS) 390 { 391 hr = this->ReturnString(flags, pszOut, pcchOut, contentType, strlenW(contentType) + 1); 392 } 393 else 394 { 395 hr = HRESULT_FROM_WIN32(ret); 396 } 397 HeapFree(GetProcessHeap(), 0, contentType); 398 } 399 else 400 { 401 hr = E_OUTOFMEMORY; 402 } 403 return hr; 404 } 405 case ASSOCSTR_DEFAULTICON: 406 { 407 DWORD ret; 408 DWORD size = 0; 409 ret = RegGetValueW(this->hkeyProgID, L"DefaultIcon", NULL, RRF_RT_REG_SZ, NULL, NULL, &size); 410 if (ret == ERROR_SUCCESS) 411 { 412 WCHAR *icon = static_cast<WCHAR *>(HeapAlloc(GetProcessHeap(), 0, size)); 413 if (icon) 414 { 415 ret = RegGetValueW(this->hkeyProgID, L"DefaultIcon", NULL, RRF_RT_REG_SZ, NULL, icon, &size); 416 if (ret == ERROR_SUCCESS) 417 { 418 hr = this->ReturnString(flags, pszOut, pcchOut, icon, strlenW(icon) + 1); 419 } 420 else 421 { 422 hr = HRESULT_FROM_WIN32(ret); 423 } 424 HeapFree(GetProcessHeap(), 0, icon); 425 } 426 else 427 { 428 hr = HRESULT_FROM_WIN32(ret); 429 } 430 } 431 else 432 { 433 hr = HRESULT_FROM_WIN32(ret); 434 } 435 return hr; 436 } 437 case ASSOCSTR_SHELLEXTENSION: 438 { 439 WCHAR keypath[ARRAY_SIZE(L"ShellEx\\") + 39], guid[39]; 440 CLSID clsid; 441 HKEY hkey; 442 443 hr = CLSIDFromString(pszExtra, &clsid); 444 if (FAILED(hr)) 445 { 446 return hr; 447 } 448 strcpyW(keypath, L"ShellEx\\"); 449 strcatW(keypath, pszExtra); 450 LONG ret = RegOpenKeyExW(this->hkeySource, keypath, 0, KEY_READ, &hkey); 451 if (ret) 452 { 453 return HRESULT_FROM_WIN32(ret); 454 } 455 DWORD size = sizeof(guid); 456 ret = RegGetValueW(hkey, NULL, NULL, RRF_RT_REG_SZ, NULL, guid, &size); 457 RegCloseKey(hkey); 458 if (ret) 459 { 460 return HRESULT_FROM_WIN32(ret); 461 } 462 return this->ReturnString(flags, pszOut, pcchOut, guid, size / sizeof(WCHAR)); 463 } 464 465 default: 466 { 467 FIXME("assocstr %d unimplemented!\n", str); 468 return E_NOTIMPL; 469 } 470 } 471 } 472 473 /************************************************************************** 474 * IQueryAssociations_GetKey 475 * 476 * Get a file association key from the registry. 477 * 478 * PARAMS 479 * cfFlags [I] ASSOCF_ flags from "shlwapi.h" 480 * assockey [I] Type of key to get (ASSOCKEY enum from "shlwapi.h") 481 * pszExtra [I] Extra information about the key location 482 * phkeyOut [O] Destination for the association key 483 * 484 * RETURNS 485 * Success: S_OK. phkeyOut contains a handle to the key. 486 * Failure: An HRESULT error code indicating the error. 487 */ 488 HRESULT STDMETHODCALLTYPE CQueryAssociations::GetKey( 489 ASSOCF cfFlags, 490 ASSOCKEY assockey, 491 LPCWSTR pszExtra, 492 HKEY *phkeyOut) 493 { 494 FIXME("(%p,0x%8x,0x%8x,%s,%p)-stub!\n", this, cfFlags, assockey, 495 debugstr_w(pszExtra), phkeyOut); 496 return E_NOTIMPL; 497 } 498 499 /************************************************************************** 500 * IQueryAssociations_GetData 501 * 502 * Get the data for a file association key from the registry. 503 * 504 * PARAMS 505 * cfFlags [I] ASSOCF_ flags from "shlwapi.h" 506 * assocdata [I] Type of data to get (ASSOCDATA enum from "shlwapi.h") 507 * pszExtra [I] Extra information about the data location 508 * pvOut [O] Destination for the association key 509 * pcbOut [I/O] Size of pvOut 510 * 511 * RETURNS 512 * Success: S_OK. pszOut contains the data, pcbOut contains its length. 513 * Failure: An HRESULT error code indicating the error. 514 */ 515 HRESULT STDMETHODCALLTYPE CQueryAssociations::GetData(ASSOCF cfFlags, ASSOCDATA assocdata, LPCWSTR pszExtra, LPVOID pvOut, DWORD *pcbOut) 516 { 517 TRACE("(%p,0x%8x,0x%8x,%s,%p,%p)\n", this, cfFlags, assocdata, 518 debugstr_w(pszExtra), pvOut, pcbOut); 519 520 if(cfFlags) 521 { 522 FIXME("Unsupported flags: %x\n", cfFlags); 523 } 524 525 switch(assocdata) 526 { 527 case ASSOCDATA_EDITFLAGS: 528 { 529 if(!this->hkeyProgID) 530 { 531 return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION); 532 } 533 534 void *data; 535 DWORD size; 536 HRESULT hres = this->GetValue(this->hkeyProgID, L"EditFlags", &data, &size); 537 if(FAILED(hres)) 538 { 539 return hres; 540 } 541 542 if (!pcbOut) 543 { 544 HeapFree(GetProcessHeap(), 0, data); 545 return hres; 546 } 547 548 hres = this->ReturnData(pvOut, pcbOut, data, size); 549 HeapFree(GetProcessHeap(), 0, data); 550 return hres; 551 } 552 default: 553 { 554 FIXME("Unsupported ASSOCDATA value: %d\n", assocdata); 555 return E_NOTIMPL; 556 } 557 } 558 } 559 560 /************************************************************************** 561 * IQueryAssociations_GetEnum 562 * 563 * Not yet implemented in native Win32. 564 * 565 * PARAMS 566 * cfFlags [I] ASSOCF_ flags from "shlwapi.h" 567 * assocenum [I] Type of enum to get (ASSOCENUM enum from "shlwapi.h") 568 * pszExtra [I] Extra information about the enum location 569 * riid [I] REFIID to look for 570 * ppvOut [O] Destination for the interface. 571 * 572 * RETURNS 573 * Success: S_OK. 574 * Failure: An HRESULT error code indicating the error. 575 * 576 * NOTES 577 * Presumably this function returns an enumerator object. 578 */ 579 HRESULT STDMETHODCALLTYPE CQueryAssociations::GetEnum( 580 ASSOCF cfFlags, 581 ASSOCENUM assocenum, 582 LPCWSTR pszExtra, 583 REFIID riid, 584 LPVOID *ppvOut) 585 { 586 return E_NOTIMPL; 587 } 588 589 HRESULT CQueryAssociations::GetValue(HKEY hkey, const WCHAR *name, void **data, DWORD *data_size) 590 { 591 DWORD size; 592 LONG ret; 593 594 ret = RegQueryValueExW(hkey, name, 0, NULL, NULL, &size); 595 if (ret != ERROR_SUCCESS) 596 return HRESULT_FROM_WIN32(ret); 597 598 if (!size) 599 return E_FAIL; 600 601 *data = HeapAlloc(GetProcessHeap(), 0, size); 602 if (!*data) 603 return E_OUTOFMEMORY; 604 605 ret = RegQueryValueExW(hkey, name, 0, NULL, (LPBYTE)*data, &size); 606 if (ret != ERROR_SUCCESS) 607 { 608 HeapFree(GetProcessHeap(), 0, *data); 609 return HRESULT_FROM_WIN32(ret); 610 } 611 612 if (data_size) 613 *data_size = size; 614 615 return S_OK; 616 } 617 618 HRESULT CQueryAssociations::GetCommand(const WCHAR *extra, WCHAR **command) 619 { 620 HKEY hkeyCommand; 621 HKEY hkeyShell; 622 HKEY hkeyVerb; 623 HRESULT hr; 624 LONG ret; 625 WCHAR *extra_from_reg = NULL; 626 WCHAR *filetype; 627 628 /* When looking for file extension it's possible to have a default value 629 that points to another key that contains 'shell/<verb>/command' subtree. */ 630 hr = this->GetValue(this->hkeySource, NULL, (void**)&filetype, NULL); 631 if (hr == S_OK) 632 { 633 HKEY hkeyFile; 634 635 ret = RegOpenKeyExW(HKEY_CLASSES_ROOT, filetype, 0, KEY_READ, &hkeyFile); 636 HeapFree(GetProcessHeap(), 0, filetype); 637 638 if (ret == ERROR_SUCCESS) 639 { 640 ret = RegOpenKeyExW(hkeyFile, L"shell", 0, KEY_READ, &hkeyShell); 641 RegCloseKey(hkeyFile); 642 } 643 else 644 { 645 ret = RegOpenKeyExW(this->hkeySource, L"shell", 0, KEY_READ, &hkeyShell); 646 } 647 } 648 else 649 { 650 ret = RegOpenKeyExW(this->hkeySource, L"shell", 0, KEY_READ, &hkeyShell); 651 } 652 653 if (ret) 654 { 655 return HRESULT_FROM_WIN32(ret); 656 } 657 658 if (!extra) 659 { 660 /* check for default verb */ 661 hr = this->GetValue(hkeyShell, NULL, (void**)&extra_from_reg, NULL); 662 if (FAILED(hr)) 663 hr = this->GetValue(hkeyShell, L"open", (void**)&extra_from_reg, NULL); 664 if (FAILED(hr)) 665 { 666 /* no default verb, try first subkey */ 667 DWORD max_subkey_len; 668 669 ret = RegQueryInfoKeyW(hkeyShell, NULL, NULL, NULL, NULL, &max_subkey_len, NULL, NULL, NULL, NULL, NULL, NULL); 670 if (ret) 671 { 672 RegCloseKey(hkeyShell); 673 return HRESULT_FROM_WIN32(ret); 674 } 675 676 max_subkey_len++; 677 extra_from_reg = static_cast<WCHAR*>(HeapAlloc(GetProcessHeap(), 0, max_subkey_len * sizeof(WCHAR))); 678 if (!extra_from_reg) 679 { 680 RegCloseKey(hkeyShell); 681 return E_OUTOFMEMORY; 682 } 683 684 ret = RegEnumKeyExW(hkeyShell, 0, extra_from_reg, &max_subkey_len, NULL, NULL, NULL, NULL); 685 if (ret) 686 { 687 HeapFree(GetProcessHeap(), 0, extra_from_reg); 688 RegCloseKey(hkeyShell); 689 return HRESULT_FROM_WIN32(ret); 690 } 691 } 692 extra = extra_from_reg; 693 } 694 695 /* open verb subkey */ 696 ret = RegOpenKeyExW(hkeyShell, extra, 0, KEY_READ, &hkeyVerb); 697 HeapFree(GetProcessHeap(), 0, extra_from_reg); 698 RegCloseKey(hkeyShell); 699 if (ret) 700 { 701 return HRESULT_FROM_WIN32(ret); 702 } 703 /* open command subkey */ 704 ret = RegOpenKeyExW(hkeyVerb, L"command", 0, KEY_READ, &hkeyCommand); 705 RegCloseKey(hkeyVerb); 706 if (ret) 707 { 708 return HRESULT_FROM_WIN32(ret); 709 } 710 hr = this->GetValue(hkeyCommand, NULL, (void**)command, NULL); 711 RegCloseKey(hkeyCommand); 712 return hr; 713 } 714 715 HRESULT CQueryAssociations::GetExecutable(LPCWSTR pszExtra, LPWSTR path, DWORD pathlen, DWORD *len) 716 { 717 WCHAR *pszCommand; 718 WCHAR *pszStart; 719 WCHAR *pszEnd; 720 721 HRESULT hr = this->GetCommand(pszExtra, &pszCommand); 722 if (FAILED_UNEXPECTEDLY(hr)) 723 { 724 return hr; 725 } 726 727 DWORD expLen = ExpandEnvironmentStringsW(pszCommand, NULL, 0); 728 if (expLen > 0) 729 { 730 expLen++; 731 WCHAR *buf = static_cast<WCHAR *>(HeapAlloc(GetProcessHeap(), 0, expLen * sizeof(WCHAR))); 732 ExpandEnvironmentStringsW(pszCommand, buf, expLen); 733 HeapFree(GetProcessHeap(), 0, pszCommand); 734 pszCommand = buf; 735 } 736 737 /* cleanup pszCommand */ 738 if (pszCommand[0] == '"') 739 { 740 pszStart = pszCommand + 1; 741 pszEnd = strchrW(pszStart, '"'); 742 if (pszEnd) 743 { 744 *pszEnd = 0; 745 } 746 *len = SearchPathW(NULL, pszStart, NULL, pathlen, path, NULL); 747 } 748 else 749 { 750 pszStart = pszCommand; 751 for (pszEnd = pszStart; (pszEnd = strchrW(pszEnd, ' ')); pszEnd++) 752 { 753 WCHAR c = *pszEnd; 754 *pszEnd = 0; 755 if ((*len = SearchPathW(NULL, pszStart, NULL, pathlen, path, NULL))) 756 { 757 break; 758 } 759 *pszEnd = c; 760 } 761 if (!pszEnd) 762 { 763 *len = SearchPathW(NULL, pszStart, NULL, pathlen, path, NULL); 764 } 765 } 766 767 HeapFree(GetProcessHeap(), 0, pszCommand); 768 if (!*len) 769 { 770 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 771 } 772 return S_OK; 773 } 774 775 HRESULT CQueryAssociations::ReturnData(void *out, DWORD *outlen, const void *data, DWORD datalen) 776 { 777 if (out) 778 { 779 if (*outlen < datalen) 780 { 781 *outlen = datalen; 782 return E_POINTER; 783 } 784 *outlen = datalen; 785 memcpy(out, data, datalen); 786 return S_OK; 787 } 788 else 789 { 790 *outlen = datalen; 791 return S_FALSE; 792 } 793 } 794 795 HRESULT CQueryAssociations::ReturnString(ASSOCF flags, LPWSTR out, DWORD *outlen, LPCWSTR data, DWORD datalen) 796 { 797 HRESULT hr = S_OK; 798 DWORD len; 799 800 TRACE("flags=0x%08x, data=%s\n", flags, debugstr_w(data)); 801 802 if (!out) 803 { 804 *outlen = datalen; 805 return S_FALSE; 806 } 807 808 if (*outlen < datalen) 809 { 810 if (flags & ASSOCF_NOTRUNCATE) 811 { 812 len = 0; 813 if (*outlen > 0) out[0] = 0; 814 hr = E_POINTER; 815 } 816 else 817 { 818 len = min(*outlen, datalen); 819 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 820 } 821 *outlen = datalen; 822 } 823 else 824 { 825 *outlen = len = datalen; 826 } 827 828 if (len) 829 { 830 memcpy(out, data, len*sizeof(WCHAR)); 831 } 832 833 return hr; 834 } 835