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