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