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