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