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