[SHELL] IPersistFolder::Initialize takes a PCIDLIST_ABSOLUTE. CORE-16385
[reactos.git] / 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 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 }