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