xref: /reactos/dll/win32/shlwapi/assoc.c (revision 299e4305)
1 /*
2  * IQueryAssociations 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 #ifdef __REACTOS__
21     #undef _WIN32_WINNT
22     #define _WIN32_WINNT _WIN32_WINNT_VISTA /* for RegGetValueW */
23 #endif
24 #include <stdarg.h>
25 #include <assert.h>
26 
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winnls.h"
30 #include "winreg.h"
31 #include "objbase.h"
32 #include "shlguid.h"
33 #include "shlobj.h"
34 #include "shlwapi.h"
35 #include "wine/unicode.h"
36 #include "wine/debug.h"
37 
38 WINE_DEFAULT_DEBUG_CHANNEL(shell);
39 
40 /* Default IQueryAssociations::Init() flags */
41 #ifdef __REACTOS__
42 #define SHLWAPI_DEF_ASSOCF (ASSOCF_INIT_BYEXENAME | ASSOCF_INIT_DEFAULTTOSTAR | \
43                             ASSOCF_INIT_DEFAULTTOFOLDER | ASSOCF_INIT_NOREMAPCLSID)
44 #else
45 #define SHLWAPI_DEF_ASSOCF (ASSOCF_INIT_BYEXENAME|ASSOCF_INIT_DEFAULTTOSTAR| \
46                             ASSOCF_INIT_DEFAULTTOFOLDER)
47 #endif
48 
49 /*************************************************************************
50  * SHLWAPI_ParamAToW
51  *
52  * Internal helper function: Convert ASCII parameter to Unicode.
53  */
54 static BOOL SHLWAPI_ParamAToW(LPCSTR lpszParam, LPWSTR lpszBuff, DWORD dwLen,
55                               LPWSTR* lpszOut)
56 {
57   if (lpszParam)
58   {
59     DWORD dwStrLen = MultiByteToWideChar(CP_ACP, 0, lpszParam, -1, NULL, 0);
60 
61     if (dwStrLen < dwLen)
62     {
63       *lpszOut = lpszBuff; /* Use Buffer, it is big enough */
64     }
65     else
66     {
67       /* Create a new buffer big enough for the string */
68       *lpszOut = HeapAlloc(GetProcessHeap(), 0,
69                                    dwStrLen * sizeof(WCHAR));
70       if (!*lpszOut)
71         return FALSE;
72     }
73     MultiByteToWideChar(CP_ACP, 0, lpszParam, -1, *lpszOut, dwStrLen);
74   }
75   else
76     *lpszOut = NULL;
77   return TRUE;
78 }
79 
80 /*************************************************************************
81  * AssocCreate  [SHLWAPI.@]
82  *
83  * Create a new IQueryAssociations object.
84  *
85  * PARAMS
86  *  clsid       [I] CLSID of object
87  *  refiid      [I] REFIID of interface
88  *  lpInterface [O] Destination for the created IQueryAssociations object
89  *
90  * RETURNS
91  *  Success: S_OK. lpInterface contains the new object.
92  *  Failure: An HRESULT error code indicating the error.
93  *
94  * NOTES
95  *  clsid  must be equal to CLSID_QueryAssociations and
96  *  refiid must be equal to IID_IQueryAssociations, IID_IUnknown or this function will fail
97  */
98 HRESULT WINAPI AssocCreate(CLSID clsid, REFIID refiid, void **lpInterface)
99 {
100   TRACE("(%s,%s,%p)\n", debugstr_guid(&clsid), debugstr_guid(refiid),
101         lpInterface);
102 
103   if (!lpInterface)
104     return E_INVALIDARG;
105 
106   *(DWORD*)lpInterface = 0;
107 
108   if (!IsEqualGUID(&clsid,  &CLSID_QueryAssociations))
109     return CLASS_E_CLASSNOTAVAILABLE;
110 
111   return SHCoCreateInstance( NULL, &clsid, NULL, refiid, lpInterface );
112 }
113 
114 
115 struct AssocPerceivedInfo
116 {
117     PCWSTR Type;
118     PERCEIVED Perceived;
119     INT FlagHardcoded;
120     INT FlagSoftcoded;
121     PCWSTR Extensions;
122 };
123 
124 static const WCHAR unspecified_exts[] = {
125     '.','l','n','k',0,
126     '.','s','e','a','r','c','h','-','m','s',0,
127     0
128 };
129 
130 static const WCHAR image_exts[] = {
131     '.','b','m','p',0,
132     '.','d','i','b',0,
133     '.','e','m','f',0,
134     '.','g','i','f',0,
135     '.','i','c','o',0,
136     '.','j','f','i','f',0,
137     '.','j','p','e',0,
138     '.','j','p','e','g',0,
139     '.','j','p','g',0,
140     '.','p','n','g',0,
141     '.','r','l','e',0,
142     '.','t','i','f',0,
143     '.','t','i','f','f',0,
144     '.','w','m','f',0,
145     0
146 };
147 
148 static const WCHAR audio_exts[] = {
149     '.','a','i','f',0,
150     '.','a','i','f','c',0,
151     '.','a','i','f','f',0,
152     '.','a','u',0,
153     '.','m','3','u',0,
154     '.','m','i','d',0,
155     '.','m','i','d','i',0,
156 #if _WIN32_WINNT > 0x602
157     '.','m','p','2',0,
158 #endif
159     '.','m','p','3',0,
160     '.','r','m','i',0,
161     '.','s','n','d',0,
162     '.','w','a','v',0,
163     '.','w','a','x',0,
164     '.','w','m','a',0,
165     0
166 };
167 
168 static const WCHAR video_exts[] = {
169     '.','a','s','f',0,
170     '.','a','s','x',0,
171     '.','a','v','i',0,
172     '.','d','v','r','-','m','s',0,
173     '.','I','V','F',0,
174     '.','m','1','v',0,
175 #if _WIN32_WINNT <= 0x602
176     '.','m','p','2',0,
177 #endif
178     '.','m','p','2','v',0,
179     '.','m','p','a',0,
180     '.','m','p','e',0,
181     '.','m','p','e','g',0,
182     '.','m','p','g',0,
183     '.','m','p','v','2',0,
184     '.','w','m',0,
185     '.','w','m','v',0,
186     '.','w','m','x',0,
187     '.','w','v','x',0,
188     0
189 };
190 
191 static const WCHAR compressed_exts[] = {
192     '.','z','i','p',0,
193     0
194 };
195 
196 static const WCHAR document_exts[] = {
197 #if _WIN32_WINNT >= 0x600
198     '.','h','t','m',0,
199     '.','h','t','m','l',0,
200 #endif
201     '.','m','h','t',0,
202     0
203 };
204 
205 static const WCHAR system_exts[] = {
206     '.','c','p','l',0,
207     0
208 };
209 
210 static const WCHAR application_exts[] = {
211     '.','b','a','s',0,
212     '.','b','a','t',0,
213     '.','c','m','d',0,
214     '.','c','o','m',0,
215     '.','e','x','e',0,
216     '.','h','t','a',0,
217     '.','m','s','i',0,
218     '.','p','i','f',0,
219     '.','r','e','g',0,
220     '.','s','c','r',0,
221     '.','v','b',0,
222     0
223 };
224 
225 const WCHAR type_text[] = {'t','e','x','t',0};
226 const WCHAR type_image[] = {'i','m','a','g','e',0};
227 const WCHAR type_audio[] = {'a','u','d','i','o',0};
228 const WCHAR type_video[] = {'v','i','d','e','o',0};
229 const WCHAR type_compressed[] = {'c','o','m','p','r','e','s','s','e','d',0};
230 const WCHAR type_document[] = {'d','o','c','u','m','e','n','t',0};
231 const WCHAR type_system[] = {'s','y','s','t','e','m',0};
232 const WCHAR type_application[] = {'a','p','p','l','i','c','a','t','i','o','n',0};
233 
234 #define HARDCODED_NATIVE_WMSDK      (PERCEIVEDFLAG_HARDCODED | PERCEIVEDFLAG_NATIVESUPPORT | PERCEIVEDFLAG_WMSDK)
235 #define HARDCODED_NATIVE_GDIPLUS    (PERCEIVEDFLAG_HARDCODED | PERCEIVEDFLAG_NATIVESUPPORT | PERCEIVEDFLAG_GDIPLUS)
236 #define HARDCODED_NATIVE_ZIPFLDR    (PERCEIVEDFLAG_HARDCODED | PERCEIVEDFLAG_NATIVESUPPORT | PERCEIVEDFLAG_ZIPFOLDER)
237 #define SOFTCODED_NATIVESUPPORT     (PERCEIVEDFLAG_SOFTCODED | PERCEIVEDFLAG_NATIVESUPPORT)
238 
239 static const struct AssocPerceivedInfo known_types[] = {
240     { NULL,             PERCEIVED_TYPE_UNSPECIFIED, PERCEIVEDFLAG_HARDCODED,  PERCEIVEDFLAG_SOFTCODED, unspecified_exts },
241     { type_text,        PERCEIVED_TYPE_TEXT,        PERCEIVEDFLAG_HARDCODED,  SOFTCODED_NATIVESUPPORT, NULL },
242     { type_image,       PERCEIVED_TYPE_IMAGE,       HARDCODED_NATIVE_GDIPLUS, PERCEIVEDFLAG_SOFTCODED, image_exts },
243     { type_audio,       PERCEIVED_TYPE_AUDIO,       HARDCODED_NATIVE_WMSDK,   PERCEIVEDFLAG_SOFTCODED, audio_exts },
244     { type_video,       PERCEIVED_TYPE_VIDEO,       HARDCODED_NATIVE_WMSDK,   PERCEIVEDFLAG_SOFTCODED, video_exts },
245     { type_compressed,  PERCEIVED_TYPE_COMPRESSED,  HARDCODED_NATIVE_ZIPFLDR, PERCEIVEDFLAG_SOFTCODED, compressed_exts },
246     { type_document,    PERCEIVED_TYPE_DOCUMENT,    PERCEIVEDFLAG_HARDCODED,  PERCEIVEDFLAG_SOFTCODED, document_exts },
247     { type_system,      PERCEIVED_TYPE_SYSTEM,      PERCEIVEDFLAG_HARDCODED,  PERCEIVEDFLAG_SOFTCODED, system_exts },
248     { type_application, PERCEIVED_TYPE_APPLICATION, PERCEIVEDFLAG_HARDCODED,  PERCEIVEDFLAG_SOFTCODED, application_exts },
249 };
250 
251 static const struct AssocPerceivedInfo* AssocFindByBuiltinExtension(LPCWSTR pszExt)
252 {
253     UINT n;
254     for (n = 0; n < sizeof(known_types) / sizeof(known_types[0]); ++n)
255     {
256         PCWSTR Ext = known_types[n].Extensions;
257         while (Ext && *Ext)
258         {
259             if (!StrCmpIW(Ext, pszExt))
260                 return &known_types[n];
261             Ext += (strlenW(Ext) + 1);
262         }
263     }
264     return NULL;
265 }
266 
267 static const struct AssocPerceivedInfo* AssocFindByType(LPCWSTR pszType)
268 {
269     UINT n;
270     for (n = 0; n < sizeof(known_types) / sizeof(known_types[0]); ++n)
271     {
272         if (known_types[n].Type)
273         {
274             if (!StrCmpIW(known_types[n].Type, pszType))
275                 return &known_types[n];
276         }
277     }
278     return NULL;
279 }
280 
281 
282 /*************************************************************************
283  * AssocGetPerceivedType  [SHLWAPI.@]
284  *
285  * Detect the type of a file by inspecting its extension
286  *
287  * PARAMS
288  *  lpszExt     [I] File extension to evaluate.
289  *  lpType      [O] Pointer to perceived type
290  *  lpFlag      [O] Pointer to perceived type flag
291  *  lppszType   [O] Address to pointer for perceived type text
292  *
293  * RETURNS
294  *  Success: S_OK. lpType and lpFlag contain the perceived type and
295  *           its information. If lppszType is not NULL, it will point
296  *           to a string with perceived type text.
297  *  Failure: An HRESULT error code indicating the error.
298  *
299  * NOTES
300  *  lppszType is optional and it can be NULL.
301  *  if lpType or lpFlag are NULL, the function will crash.
302  *  if lpszExt is NULL, an error is returned.
303  */
304 HRESULT WINAPI AssocGetPerceivedType(LPCWSTR lpszExt, PERCEIVED *lpType,
305                                      INT *lpFlag, LPWSTR *lppszType)
306 {
307     static const WCHAR PerceivedTypeKey[] = {'P','e','r','c','e','i','v','e','d','T','y','p','e',0};
308     static const WCHAR SystemFileAssociationsKey[] = {'S','y','s','t','e','m','F','i','l','e',
309         'A','s','s','o','c','i','a','t','i','o','n','s','\\','%','s',0};
310     const struct AssocPerceivedInfo *Info;
311 
312     TRACE("(%s,%p,%p,%p)\n", debugstr_w(lpszExt), lpType, lpFlag, lppszType);
313 
314     Info = AssocFindByBuiltinExtension(lpszExt);
315     if (Info)
316     {
317         *lpType = Info->Perceived;
318         *lpFlag = Info->FlagHardcoded;
319     }
320     else
321     {
322         WCHAR Buffer[100] = { 0 };
323         DWORD Size = sizeof(Buffer);
324         if (RegGetValueW(HKEY_CLASSES_ROOT, lpszExt, PerceivedTypeKey,
325                          RRF_RT_REG_SZ, NULL, Buffer, &Size) == ERROR_SUCCESS)
326         {
327             Info = AssocFindByType(Buffer);
328         }
329         if (!Info)
330         {
331             WCHAR KeyName[MAX_PATH] = { 0 };
332             snprintfW(KeyName, MAX_PATH, SystemFileAssociationsKey, lpszExt);
333             Size = sizeof(Buffer);
334             if (RegGetValueW(HKEY_CLASSES_ROOT, KeyName, PerceivedTypeKey,
335                              RRF_RT_REG_SZ, NULL, Buffer, &Size) == ERROR_SUCCESS)
336             {
337                 Info = AssocFindByType(Buffer);
338             }
339         }
340         if (Info)
341         {
342             *lpType = Info->Perceived;
343             *lpFlag = Info->FlagSoftcoded;
344         }
345     }
346 
347     if (Info)
348     {
349         if (lppszType && Info->Type)
350         {
351             return SHStrDupW(Info->Type, lppszType);
352         }
353         return Info->Type ? S_OK : E_FAIL;
354     }
355     else
356     {
357         *lpType = PERCEIVED_TYPE_UNSPECIFIED;
358         *lpFlag = 0;
359     }
360     return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
361 }
362 
363 /*************************************************************************
364  * AssocQueryKeyW  [SHLWAPI.@]
365  *
366  * See AssocQueryKeyA.
367  */
368 HRESULT WINAPI AssocQueryKeyW(ASSOCF cfFlags, ASSOCKEY assockey, LPCWSTR pszAssoc,
369                               LPCWSTR pszExtra, HKEY *phkeyOut)
370 {
371   HRESULT hRet;
372   IQueryAssociations* lpAssoc;
373 
374   TRACE("(0x%x,%d,%s,%s,%p)\n", cfFlags, assockey, debugstr_w(pszAssoc),
375         debugstr_w(pszExtra), phkeyOut);
376 
377   hRet = AssocCreate( CLSID_QueryAssociations, &IID_IQueryAssociations, (void **)&lpAssoc );
378   if (FAILED(hRet)) return hRet;
379 
380   cfFlags &= SHLWAPI_DEF_ASSOCF;
381   hRet = IQueryAssociations_Init(lpAssoc, cfFlags, pszAssoc, NULL, NULL);
382 
383   if (SUCCEEDED(hRet))
384     hRet = IQueryAssociations_GetKey(lpAssoc, cfFlags, assockey, pszExtra, phkeyOut);
385 
386   IQueryAssociations_Release(lpAssoc);
387   return hRet;
388 }
389 
390 /*************************************************************************
391  * AssocQueryKeyA  [SHLWAPI.@]
392  *
393  * Get a file association key from the registry.
394  *
395  * PARAMS
396  *  cfFlags  [I] ASSOCF_ flags from "shlwapi.h"
397  *  assockey [I] Type of key to get
398  *  pszAssoc [I] Key name to search below
399  *  pszExtra [I] Extra information about the key location
400  *  phkeyOut [O] Destination for the association key
401  *
402  * RETURNS
403  *  Success: S_OK. phkeyOut contains the key.
404  *  Failure: An HRESULT error code indicating the error.
405  */
406 HRESULT WINAPI AssocQueryKeyA(ASSOCF cfFlags, ASSOCKEY assockey, LPCSTR pszAssoc,
407                               LPCSTR pszExtra, HKEY *phkeyOut)
408 {
409   WCHAR szAssocW[MAX_PATH], *lpszAssocW = NULL;
410   WCHAR szExtraW[MAX_PATH], *lpszExtraW = NULL;
411   HRESULT hRet = E_OUTOFMEMORY;
412 
413   TRACE("(0x%x,%d,%s,%s,%p)\n", cfFlags, assockey, debugstr_a(pszAssoc),
414         debugstr_a(pszExtra), phkeyOut);
415 
416   if (SHLWAPI_ParamAToW(pszAssoc, szAssocW, MAX_PATH, &lpszAssocW) &&
417       SHLWAPI_ParamAToW(pszExtra, szExtraW, MAX_PATH, &lpszExtraW))
418   {
419     hRet = AssocQueryKeyW(cfFlags, assockey, lpszAssocW, lpszExtraW, phkeyOut);
420   }
421 
422   if (lpszAssocW != szAssocW)
423     HeapFree(GetProcessHeap(), 0, lpszAssocW);
424 
425   if (lpszExtraW != szExtraW)
426     HeapFree(GetProcessHeap(), 0, lpszExtraW);
427 
428   return hRet;
429 }
430 
431 /*************************************************************************
432  * AssocQueryStringW  [SHLWAPI.@]
433  *
434  * See AssocQueryStringA.
435  */
436 HRESULT WINAPI AssocQueryStringW(ASSOCF cfFlags, ASSOCSTR str, LPCWSTR pszAssoc,
437                                  LPCWSTR pszExtra, LPWSTR pszOut, DWORD *pcchOut)
438 {
439   HRESULT hRet;
440   IQueryAssociations* lpAssoc;
441 
442   TRACE("(0x%x,%d,%s,%s,%p,%p)\n", cfFlags, str, debugstr_w(pszAssoc),
443         debugstr_w(pszExtra), pszOut, pcchOut);
444 
445   if (!pcchOut)
446     return E_UNEXPECTED;
447 
448   hRet = AssocCreate( CLSID_QueryAssociations, &IID_IQueryAssociations, (void **)&lpAssoc );
449   if (FAILED(hRet)) return hRet;
450 
451   hRet = IQueryAssociations_Init(lpAssoc, cfFlags & SHLWAPI_DEF_ASSOCF,
452                                  pszAssoc, NULL, NULL);
453 
454   if (SUCCEEDED(hRet))
455     hRet = IQueryAssociations_GetString(lpAssoc, cfFlags, str, pszExtra,
456                                         pszOut, pcchOut);
457 
458   IQueryAssociations_Release(lpAssoc);
459   return hRet;
460 }
461 
462 /*************************************************************************
463  * AssocQueryStringA  [SHLWAPI.@]
464  *
465  * Get a file association string from the registry.
466  *
467  * PARAMS
468  *  cfFlags  [I] ASSOCF_ flags from "shlwapi.h"
469  *  str      [I] Type of string to get (ASSOCSTR enum from "shlwapi.h")
470  *  pszAssoc [I] Key name to search below
471  *  pszExtra [I] Extra information about the string location
472  *  pszOut   [O] Destination for the association string
473  *  pcchOut  [O] Length of pszOut
474  *
475  * RETURNS
476  *  Success: S_OK. pszOut contains the string, pcchOut contains its length.
477  *  Failure: An HRESULT error code indicating the error.
478  */
479 HRESULT WINAPI AssocQueryStringA(ASSOCF cfFlags, ASSOCSTR str, LPCSTR pszAssoc,
480                                  LPCSTR pszExtra, LPSTR pszOut, DWORD *pcchOut)
481 {
482   WCHAR szAssocW[MAX_PATH], *lpszAssocW = NULL;
483   WCHAR szExtraW[MAX_PATH], *lpszExtraW = NULL;
484   HRESULT hRet = E_OUTOFMEMORY;
485 
486   TRACE("(0x%x,0x%d,%s,%s,%p,%p)\n", cfFlags, str, debugstr_a(pszAssoc),
487         debugstr_a(pszExtra), pszOut, pcchOut);
488 
489   if (!pcchOut)
490     hRet = E_UNEXPECTED;
491   else if (SHLWAPI_ParamAToW(pszAssoc, szAssocW, MAX_PATH, &lpszAssocW) &&
492            SHLWAPI_ParamAToW(pszExtra, szExtraW, MAX_PATH, &lpszExtraW))
493   {
494     WCHAR szReturnW[MAX_PATH], *lpszReturnW = szReturnW;
495     DWORD dwLenOut = *pcchOut;
496 
497     if (dwLenOut >= MAX_PATH)
498       lpszReturnW = HeapAlloc(GetProcessHeap(), 0,
499                                       (dwLenOut + 1) * sizeof(WCHAR));
500     else
501       dwLenOut = sizeof(szReturnW) / sizeof(szReturnW[0]);
502 
503     if (!lpszReturnW)
504       hRet = E_OUTOFMEMORY;
505     else
506     {
507       hRet = AssocQueryStringW(cfFlags, str, lpszAssocW, lpszExtraW,
508                                lpszReturnW, &dwLenOut);
509 
510       if (SUCCEEDED(hRet))
511         dwLenOut = WideCharToMultiByte(CP_ACP, 0, lpszReturnW, -1,
512                                        pszOut, *pcchOut, NULL, NULL);
513 
514       *pcchOut = dwLenOut;
515       if (lpszReturnW != szReturnW)
516         HeapFree(GetProcessHeap(), 0, lpszReturnW);
517     }
518   }
519 
520   if (lpszAssocW != szAssocW)
521     HeapFree(GetProcessHeap(), 0, lpszAssocW);
522   if (lpszExtraW != szExtraW)
523     HeapFree(GetProcessHeap(), 0, lpszExtraW);
524   return hRet;
525 }
526 
527 /*************************************************************************
528  * AssocQueryStringByKeyW  [SHLWAPI.@]
529  *
530  * See AssocQueryStringByKeyA.
531  */
532 HRESULT WINAPI AssocQueryStringByKeyW(ASSOCF cfFlags, ASSOCSTR str, HKEY hkAssoc,
533                                       LPCWSTR pszExtra, LPWSTR pszOut,
534                                       DWORD *pcchOut)
535 {
536   HRESULT hRet;
537   IQueryAssociations* lpAssoc;
538 
539   TRACE("(0x%x,0x%d,%p,%s,%p,%p)\n", cfFlags, str, hkAssoc,
540         debugstr_w(pszExtra), pszOut, pcchOut);
541 
542   hRet = AssocCreate( CLSID_QueryAssociations, &IID_IQueryAssociations, (void **)&lpAssoc );
543   if (FAILED(hRet)) return hRet;
544 
545   cfFlags &= SHLWAPI_DEF_ASSOCF;
546   hRet = IQueryAssociations_Init(lpAssoc, cfFlags, 0, hkAssoc, NULL);
547 
548   if (SUCCEEDED(hRet))
549     hRet = IQueryAssociations_GetString(lpAssoc, cfFlags, str, pszExtra,
550                                         pszOut, pcchOut);
551 
552   IQueryAssociations_Release(lpAssoc);
553   return hRet;
554 }
555 
556 /*************************************************************************
557  * AssocQueryStringByKeyA  [SHLWAPI.@]
558  *
559  * Get a file association string from the registry, given a starting key.
560  *
561  * PARAMS
562  *  cfFlags  [I] ASSOCF_ flags from "shlwapi.h"
563  *  str      [I] Type of string to get
564  *  hkAssoc  [I] Key to search below
565  *  pszExtra [I] Extra information about the string location
566  *  pszOut   [O] Destination for the association string
567  *  pcchOut  [O] Length of pszOut
568  *
569  * RETURNS
570  *  Success: S_OK. pszOut contains the string, pcchOut contains its length.
571  *  Failure: An HRESULT error code indicating the error.
572  */
573 HRESULT WINAPI AssocQueryStringByKeyA(ASSOCF cfFlags, ASSOCSTR str, HKEY hkAssoc,
574                                       LPCSTR pszExtra, LPSTR pszOut,
575                                       DWORD *pcchOut)
576 {
577   WCHAR szExtraW[MAX_PATH], *lpszExtraW = szExtraW;
578   WCHAR szReturnW[MAX_PATH], *lpszReturnW = szReturnW;
579   HRESULT hRet = E_OUTOFMEMORY;
580 
581   TRACE("(0x%x,0x%d,%p,%s,%p,%p)\n", cfFlags, str, hkAssoc,
582         debugstr_a(pszExtra), pszOut, pcchOut);
583 
584   if (!pcchOut)
585     hRet = E_INVALIDARG;
586   else if (SHLWAPI_ParamAToW(pszExtra, szExtraW, MAX_PATH, &lpszExtraW))
587   {
588     DWORD dwLenOut = *pcchOut;
589     if (dwLenOut >= MAX_PATH)
590       lpszReturnW = HeapAlloc(GetProcessHeap(), 0,
591                                       (dwLenOut + 1) * sizeof(WCHAR));
592 
593     if (lpszReturnW)
594     {
595       hRet = AssocQueryStringByKeyW(cfFlags, str, hkAssoc, lpszExtraW,
596                                     lpszReturnW, &dwLenOut);
597 
598       if (SUCCEEDED(hRet))
599         WideCharToMultiByte(CP_ACP,0,szReturnW,-1,pszOut,dwLenOut,0,0);
600       *pcchOut = dwLenOut;
601 
602       if (lpszReturnW != szReturnW)
603         HeapFree(GetProcessHeap(), 0, lpszReturnW);
604     }
605   }
606 
607   if (lpszExtraW != szExtraW)
608     HeapFree(GetProcessHeap(), 0, lpszExtraW);
609   return hRet;
610 }
611 
612 
613 /**************************************************************************
614  *  AssocIsDangerous  (SHLWAPI.@)
615  *
616  * Determine if a file association is dangerous (potentially malware).
617  *
618  * PARAMS
619  *  lpszAssoc [I] Name of file or file extension to check.
620  *
621  * RETURNS
622  *  TRUE, if lpszAssoc may potentially be malware (executable),
623  *  FALSE, Otherwise.
624  */
625 BOOL WINAPI AssocIsDangerous(LPCWSTR lpszAssoc)
626 {
627     FIXME("%s\n", debugstr_w(lpszAssoc));
628     return FALSE;
629 }
630