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
CQueryAssociations()47 CQueryAssociations::CQueryAssociations() : hkeySource(0), hkeyProgID(0)
48 {
49 }
50
~CQueryAssociations()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 */
Init(ASSOCF cfFlags,LPCWSTR pszAssoc,HKEY hkeyProgid,HWND hWnd)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 */
GetString(ASSOCF flags,ASSOCSTR str,LPCWSTR pszExtra,LPWSTR pszOut,DWORD * pcchOut)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 */
GetKey(ASSOCF cfFlags,ASSOCKEY assockey,LPCWSTR pszExtra,HKEY * phkeyOut)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 */
GetData(ASSOCF cfFlags,ASSOCDATA assocdata,LPCWSTR pszExtra,LPVOID pvOut,DWORD * pcbOut)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 */
GetEnum(ASSOCF cfFlags,ASSOCENUM assocenum,LPCWSTR pszExtra,REFIID riid,LPVOID * ppvOut)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
GetValue(HKEY hkey,const WCHAR * name,void ** data,DWORD * data_size)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
GetCommand(const WCHAR * extra,WCHAR ** command)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
GetExecutable(LPCWSTR pszExtra,LPWSTR path,DWORD pathlen,DWORD * len)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
ReturnData(void * out,DWORD * outlen,const void * data,DWORD datalen)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
ReturnString(ASSOCF flags,LPWSTR out,DWORD * outlen,LPCWSTR data,DWORD datalen)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 }
785