windowscodecs: Implement Commit for BMP encoder.
[wine] / dlls / shlwapi / assoc.c
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 #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 "shlwapi.h"
30 #include "ver.h"
31 #include "wine/unicode.h"
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(shell);
35
36 /**************************************************************************
37  *  IQueryAssociations {SHLWAPI}
38  *
39  * DESCRIPTION
40  *  This object provides a layer of abstraction over the system registry in
41  *  order to simplify the process of parsing associations between files.
42  *  Associations in this context means the registry entries that link (for
43  *  example) the extension of a file with its description, list of
44  *  applications to open the file with, and actions that can be performed on it
45  *  (the shell displays such information in the context menu of explorer
46  *  when you right-click on a file).
47  *
48  * HELPERS
49  * You can use this object transparently by calling the helper functions
50  * AssocQueryKeyA(), AssocQueryStringA() and AssocQueryStringByKeyA(). These
51  * create an IQueryAssociations object, perform the requested actions
52  * and then dispose of the object. Alternatively, you can create an instance
53  * of the object using AssocCreate() and call the following methods on it:
54  *
55  * METHODS
56  */
57
58 /* Default IQueryAssociations::Init() flags */
59 #define SHLWAPI_DEF_ASSOCF (ASSOCF_INIT_BYEXENAME|ASSOCF_INIT_DEFAULTTOSTAR| \
60                             ASSOCF_INIT_DEFAULTTOFOLDER)
61
62 typedef struct
63 {
64   const IQueryAssociationsVtbl *lpVtbl;
65   LONG ref;
66   HKEY hkeySource;
67   HKEY hkeyProgID;
68 } IQueryAssociationsImpl;
69
70 static const IQueryAssociationsVtbl IQueryAssociations_vtbl;
71
72 /**************************************************************************
73  *  IQueryAssociations_Constructor [internal]
74  *
75  * Construct a new IQueryAssociations object.
76  */
77 static IQueryAssociations* IQueryAssociations_Constructor(void)
78 {
79   IQueryAssociationsImpl* iface;
80
81   iface = HeapAlloc(GetProcessHeap(),0,sizeof(IQueryAssociationsImpl));
82   iface->lpVtbl = &IQueryAssociations_vtbl;
83   iface->ref = 1;
84   iface->hkeySource = NULL;
85   iface->hkeyProgID = NULL;
86
87   TRACE("Returning IQueryAssociations* %p\n", iface);
88   return (IQueryAssociations*)iface;
89 }
90
91 /*************************************************************************
92  * SHLWAPI_ParamAToW
93  *
94  * Internal helper function: Convert ASCII parameter to Unicode.
95  */
96 static BOOL SHLWAPI_ParamAToW(LPCSTR lpszParam, LPWSTR lpszBuff, DWORD dwLen,
97                               LPWSTR* lpszOut)
98 {
99   if (lpszParam)
100   {
101     DWORD dwStrLen = MultiByteToWideChar(CP_ACP, 0, lpszParam, -1, NULL, 0);
102
103     if (dwStrLen < dwLen)
104     {
105       *lpszOut = lpszBuff; /* Use Buffer, it is big enough */
106     }
107     else
108     {
109       /* Create a new buffer big enough for the string */
110       *lpszOut = HeapAlloc(GetProcessHeap(), 0,
111                                    dwStrLen * sizeof(WCHAR));
112       if (!*lpszOut)
113         return FALSE;
114     }
115     MultiByteToWideChar(CP_ACP, 0, lpszParam, -1, *lpszOut, dwStrLen);
116   }
117   else
118     *lpszOut = NULL;
119   return TRUE;
120 }
121
122 /*************************************************************************
123  * AssocCreate  [SHLWAPI.@]
124  *
125  * Create a new IQueryAssociations object.
126  *
127  * PARAMS
128  *  clsid       [I] CLSID of object
129  *  refiid      [I] REFIID of interface
130  *  lpInterface [O] Destination for the created IQueryAssociations object
131  *
132  * RETURNS
133  *  Success: S_OK. lpInterface contains the new object.
134  *  Failure: An HRESULT error code indicating the error.
135  *
136  * NOTES
137  *  clsid  must be equal to CLSID_QueryAssociations and
138  *  refiid must be equal to IID_IQueryAssociations, IID_IUnknown or this function will fail
139  */
140 HRESULT WINAPI AssocCreate(CLSID clsid, REFIID refiid, void **lpInterface)
141 {
142   HRESULT hRet;
143   IQueryAssociations* lpAssoc;
144
145   TRACE("(%s,%s,%p)\n", debugstr_guid(&clsid), debugstr_guid(refiid),
146         lpInterface);
147
148   if (!lpInterface)
149     return E_INVALIDARG;
150
151   *(DWORD*)lpInterface = 0;
152
153   if (!IsEqualGUID(&clsid,  &CLSID_QueryAssociations))
154     return CLASS_E_CLASSNOTAVAILABLE;
155
156   lpAssoc = IQueryAssociations_Constructor();
157
158   if (!lpAssoc)
159     return E_OUTOFMEMORY;
160
161   hRet = IQueryAssociations_QueryInterface(lpAssoc, refiid, lpInterface);
162   IQueryAssociations_Release(lpAssoc);
163
164   if(hRet == E_NOINTERFACE)
165     return CLASS_E_CLASSNOTAVAILABLE;
166
167   return hRet;
168 }
169
170 /*************************************************************************
171  * AssocQueryKeyW  [SHLWAPI.@]
172  *
173  * See AssocQueryKeyA.
174  */
175 HRESULT WINAPI AssocQueryKeyW(ASSOCF cfFlags, ASSOCKEY assockey, LPCWSTR pszAssoc,
176                               LPCWSTR pszExtra, HKEY *phkeyOut)
177 {
178   HRESULT hRet;
179   IQueryAssociations* lpAssoc;
180
181   TRACE("(0x%8x,0x%8x,%s,%s,%p)\n", cfFlags, assockey, debugstr_w(pszAssoc),
182         debugstr_w(pszExtra), phkeyOut);
183
184   lpAssoc = IQueryAssociations_Constructor();
185
186   if (!lpAssoc)
187     return E_OUTOFMEMORY;
188
189   cfFlags &= SHLWAPI_DEF_ASSOCF;
190   hRet = IQueryAssociations_Init(lpAssoc, cfFlags, pszAssoc, NULL, NULL);
191
192   if (SUCCEEDED(hRet))
193     hRet = IQueryAssociations_GetKey(lpAssoc, cfFlags, assockey, pszExtra, phkeyOut);
194
195   IQueryAssociations_Release(lpAssoc);
196   return hRet;
197 }
198
199 /*************************************************************************
200  * AssocQueryKeyA  [SHLWAPI.@]
201  *
202  * Get a file association key from the registry.
203  *
204  * PARAMS
205  *  cfFlags  [I] ASSOCF_ flags from "shlwapi.h"
206  *  assockey [I] Type of key to get
207  *  pszAssoc [I] Key name to search below
208  *  pszExtra [I] Extra information about the key location
209  *  phkeyOut [O] Destination for the association key
210  *
211  * RETURNS
212  *  Success: S_OK. phkeyOut contains the key.
213  *  Failure: An HRESULT error code indicating the error.
214  */
215 HRESULT WINAPI AssocQueryKeyA(ASSOCF cfFlags, ASSOCKEY assockey, LPCSTR pszAssoc,
216                               LPCSTR pszExtra, HKEY *phkeyOut)
217 {
218   WCHAR szAssocW[MAX_PATH], *lpszAssocW = NULL;
219   WCHAR szExtraW[MAX_PATH], *lpszExtraW = NULL;
220   HRESULT hRet = E_OUTOFMEMORY;
221
222   TRACE("(0x%8x,0x%8x,%s,%s,%p)\n", cfFlags, assockey, debugstr_a(pszAssoc),
223         debugstr_a(pszExtra), phkeyOut);
224
225   if (SHLWAPI_ParamAToW(pszAssoc, szAssocW, MAX_PATH, &lpszAssocW) &&
226       SHLWAPI_ParamAToW(pszExtra, szExtraW, MAX_PATH, &lpszExtraW))
227   {
228     hRet = AssocQueryKeyW(cfFlags, assockey, lpszAssocW, lpszExtraW, phkeyOut);
229   }
230
231   if (lpszAssocW != szAssocW)
232     HeapFree(GetProcessHeap(), 0, lpszAssocW);
233
234   if (lpszExtraW != szExtraW)
235     HeapFree(GetProcessHeap(), 0, lpszExtraW);
236
237   return hRet;
238 }
239
240 /*************************************************************************
241  * AssocQueryStringW  [SHLWAPI.@]
242  *
243  * See AssocQueryStringA.
244  */
245 HRESULT WINAPI AssocQueryStringW(ASSOCF cfFlags, ASSOCSTR str, LPCWSTR pszAssoc,
246                                  LPCWSTR pszExtra, LPWSTR pszOut, DWORD *pcchOut)
247 {
248   HRESULT hRet;
249   IQueryAssociations* lpAssoc;
250
251   TRACE("(0x%8x,0x%8x,%s,%s,%p,%p)\n", cfFlags, str, debugstr_w(pszAssoc),
252         debugstr_w(pszExtra), pszOut, pcchOut);
253
254   if (!pcchOut)
255     return E_UNEXPECTED;
256
257   lpAssoc = IQueryAssociations_Constructor();
258
259   if (!lpAssoc)
260     return E_OUTOFMEMORY;
261
262   hRet = IQueryAssociations_Init(lpAssoc, cfFlags & SHLWAPI_DEF_ASSOCF,
263                                  pszAssoc, NULL, NULL);
264
265   if (SUCCEEDED(hRet))
266     hRet = IQueryAssociations_GetString(lpAssoc, cfFlags, str, pszExtra,
267                                         pszOut, pcchOut);
268
269   IQueryAssociations_Release(lpAssoc);
270   return hRet;
271 }
272
273 /*************************************************************************
274  * AssocQueryStringA  [SHLWAPI.@]
275  *
276  * Get a file association string from the registry.
277  *
278  * PARAMS
279  *  cfFlags  [I] ASSOCF_ flags from "shlwapi.h"
280  *  str      [I] Type of string to get (ASSOCSTR enum from "shlwapi.h")
281  *  pszAssoc [I] Key name to search below
282  *  pszExtra [I] Extra information about the string location
283  *  pszOut   [O] Destination for the association string
284  *  pcchOut  [O] Length of pszOut
285  *
286  * RETURNS
287  *  Success: S_OK. pszOut contains the string, pcchOut contains its length.
288  *  Failure: An HRESULT error code indicating the error.
289  */
290 HRESULT WINAPI AssocQueryStringA(ASSOCF cfFlags, ASSOCSTR str, LPCSTR pszAssoc,
291                                  LPCSTR pszExtra, LPSTR pszOut, DWORD *pcchOut)
292 {
293   WCHAR szAssocW[MAX_PATH], *lpszAssocW = NULL;
294   WCHAR szExtraW[MAX_PATH], *lpszExtraW = NULL;
295   HRESULT hRet = E_OUTOFMEMORY;
296
297   TRACE("(0x%8x,0x%8x,%s,%s,%p,%p)\n", cfFlags, str, debugstr_a(pszAssoc),
298         debugstr_a(pszExtra), pszOut, pcchOut);
299
300   if (!pcchOut)
301     hRet = E_UNEXPECTED;
302   else if (SHLWAPI_ParamAToW(pszAssoc, szAssocW, MAX_PATH, &lpszAssocW) &&
303            SHLWAPI_ParamAToW(pszExtra, szExtraW, MAX_PATH, &lpszExtraW))
304   {
305     WCHAR szReturnW[MAX_PATH], *lpszReturnW = szReturnW;
306     DWORD dwLenOut = *pcchOut;
307
308     if (dwLenOut >= MAX_PATH)
309       lpszReturnW = HeapAlloc(GetProcessHeap(), 0,
310                                       (dwLenOut + 1) * sizeof(WCHAR));
311     else
312       dwLenOut = sizeof(szReturnW) / sizeof(szReturnW[0]);
313
314     if (!lpszReturnW)
315       hRet = E_OUTOFMEMORY;
316     else
317     {
318       hRet = AssocQueryStringW(cfFlags, str, lpszAssocW, lpszExtraW,
319                                lpszReturnW, &dwLenOut);
320
321       if (SUCCEEDED(hRet))
322         dwLenOut = WideCharToMultiByte(CP_ACP, 0, lpszReturnW, -1,
323                                        pszOut, *pcchOut, NULL, NULL);
324
325       *pcchOut = dwLenOut;
326       if (lpszReturnW != szReturnW)
327         HeapFree(GetProcessHeap(), 0, lpszReturnW);
328     }
329   }
330
331   if (lpszAssocW != szAssocW)
332     HeapFree(GetProcessHeap(), 0, lpszAssocW);
333   if (lpszExtraW != szExtraW)
334     HeapFree(GetProcessHeap(), 0, lpszExtraW);
335   return hRet;
336 }
337
338 /*************************************************************************
339  * AssocQueryStringByKeyW  [SHLWAPI.@]
340  *
341  * See AssocQueryStringByKeyA.
342  */
343 HRESULT WINAPI AssocQueryStringByKeyW(ASSOCF cfFlags, ASSOCSTR str, HKEY hkAssoc,
344                                       LPCWSTR pszExtra, LPWSTR pszOut,
345                                       DWORD *pcchOut)
346 {
347   HRESULT hRet;
348   IQueryAssociations* lpAssoc;
349
350   TRACE("(0x%8x,0x%8x,%p,%s,%p,%p)\n", cfFlags, str, hkAssoc,
351         debugstr_w(pszExtra), pszOut, pcchOut);
352
353   lpAssoc = IQueryAssociations_Constructor();
354
355   if (!lpAssoc)
356     return E_OUTOFMEMORY;
357
358   cfFlags &= SHLWAPI_DEF_ASSOCF;
359   hRet = IQueryAssociations_Init(lpAssoc, cfFlags, 0, hkAssoc, NULL);
360
361   if (SUCCEEDED(hRet))
362     hRet = IQueryAssociations_GetString(lpAssoc, cfFlags, str, pszExtra,
363                                         pszOut, pcchOut);
364
365   IQueryAssociations_Release(lpAssoc);
366   return hRet;
367 }
368
369 /*************************************************************************
370  * AssocQueryStringByKeyA  [SHLWAPI.@]
371  *
372  * Get a file association string from the registry, given a starting key.
373  *
374  * PARAMS
375  *  cfFlags  [I] ASSOCF_ flags from "shlwapi.h"
376  *  str      [I] Type of string to get
377  *  hkAssoc  [I] Key to search below
378  *  pszExtra [I] Extra information about the string location
379  *  pszOut   [O] Destination for the association string
380  *  pcchOut  [O] Length of pszOut
381  *
382  * RETURNS
383  *  Success: S_OK. pszOut contains the string, pcchOut contains its length.
384  *  Failure: An HRESULT error code indicating the error.
385  */
386 HRESULT WINAPI AssocQueryStringByKeyA(ASSOCF cfFlags, ASSOCSTR str, HKEY hkAssoc,
387                                       LPCSTR pszExtra, LPSTR pszOut,
388                                       DWORD *pcchOut)
389 {
390   WCHAR szExtraW[MAX_PATH], *lpszExtraW = szExtraW;
391   WCHAR szReturnW[MAX_PATH], *lpszReturnW = szReturnW;
392   HRESULT hRet = E_OUTOFMEMORY;
393
394   TRACE("(0x%8x,0x%8x,%p,%s,%p,%p)\n", cfFlags, str, hkAssoc,
395         debugstr_a(pszExtra), pszOut, pcchOut);
396
397   if (!pcchOut)
398     hRet = E_INVALIDARG;
399   else if (SHLWAPI_ParamAToW(pszExtra, szExtraW, MAX_PATH, &lpszExtraW))
400   {
401     DWORD dwLenOut = *pcchOut;
402     if (dwLenOut >= MAX_PATH)
403       lpszReturnW = HeapAlloc(GetProcessHeap(), 0,
404                                       (dwLenOut + 1) * sizeof(WCHAR));
405
406     if (lpszReturnW)
407     {
408       hRet = AssocQueryStringByKeyW(cfFlags, str, hkAssoc, lpszExtraW,
409                                     lpszReturnW, &dwLenOut);
410
411       if (SUCCEEDED(hRet))
412         WideCharToMultiByte(CP_ACP,0,szReturnW,-1,pszOut,dwLenOut,0,0);
413       *pcchOut = dwLenOut;
414
415       if (lpszReturnW != szReturnW)
416         HeapFree(GetProcessHeap(), 0, lpszReturnW);
417     }
418   }
419
420   if (lpszExtraW != szExtraW)
421     HeapFree(GetProcessHeap(), 0, lpszExtraW);
422   return hRet;
423 }
424
425
426 /**************************************************************************
427  *  AssocIsDangerous  (SHLWAPI.@)
428  *  
429  * Determine if a file association is dangerous (potentially malware).
430  *
431  * PARAMS
432  *  lpszAssoc [I] Name of file or file extension to check.
433  *
434  * RETURNS
435  *  TRUE, if lpszAssoc may potentially be malware (executable),
436  *  FALSE, Otherwise.
437  */
438 BOOL WINAPI AssocIsDangerous(LPCWSTR lpszAssoc)
439 {
440     FIXME("%s\n", debugstr_w(lpszAssoc));
441     return FALSE;
442 }
443
444 /**************************************************************************
445  *  IQueryAssociations_QueryInterface {SHLWAPI}
446  *
447  * See IUnknown_QueryInterface.
448  */
449 static HRESULT WINAPI IQueryAssociations_fnQueryInterface(
450   IQueryAssociations* iface,
451   REFIID riid,
452   LPVOID *ppvObj)
453 {
454   IQueryAssociationsImpl *This = (IQueryAssociationsImpl *)iface;
455
456   TRACE("(%p,%s,%p)\n",This, debugstr_guid(riid), ppvObj);
457
458   *ppvObj = NULL;
459
460   if (IsEqualIID(riid, &IID_IUnknown) ||
461       IsEqualIID(riid, &IID_IQueryAssociations))
462   {
463     *ppvObj = This;
464
465     IQueryAssociations_AddRef((IQueryAssociations*)*ppvObj);
466     TRACE("Returning IQueryAssociations (%p)\n", *ppvObj);
467     return S_OK;
468   }
469   TRACE("Returning E_NOINTERFACE\n");
470   return E_NOINTERFACE;
471 }
472
473 /**************************************************************************
474  *  IQueryAssociations_AddRef {SHLWAPI}
475  *
476  * See IUnknown_AddRef.
477  */
478 static ULONG WINAPI IQueryAssociations_fnAddRef(IQueryAssociations *iface)
479 {
480   IQueryAssociationsImpl *This = (IQueryAssociationsImpl *)iface;
481   ULONG refCount = InterlockedIncrement(&This->ref);
482   
483   TRACE("(%p)->(ref before=%u)\n",This, refCount - 1);
484
485   return refCount;
486 }
487
488 /**************************************************************************
489  *  IQueryAssociations_Release {SHLWAPI}
490  *
491  * See IUnknown_Release.
492  */
493 static ULONG WINAPI IQueryAssociations_fnRelease(IQueryAssociations *iface)
494 {
495   IQueryAssociationsImpl *This = (IQueryAssociationsImpl *)iface;
496   ULONG refCount = InterlockedDecrement(&This->ref);
497
498   TRACE("(%p)->(ref before=%u)\n",This, refCount + 1);
499
500   if (!refCount)
501   {
502     TRACE("Destroying IQueryAssociations (%p)\n", This);
503     RegCloseKey(This->hkeySource);
504     RegCloseKey(This->hkeyProgID);
505     HeapFree(GetProcessHeap(), 0, This);
506   }
507   
508   return refCount;
509 }
510
511 /**************************************************************************
512  *  IQueryAssociations_Init {SHLWAPI}
513  *
514  * Initialise an IQueryAssociations object.
515  *
516  * PARAMS
517  *  iface      [I] IQueryAssociations interface to initialise
518  *  cfFlags    [I] ASSOCF_ flags from "shlwapi.h"
519  *  pszAssoc   [I] String for the root key name, or NULL if hkeyProgid is given
520  *  hkeyProgid [I] Handle for the root key, or NULL if pszAssoc is given
521  *  hWnd       [I] Reserved, must be NULL.
522  *
523  * RETURNS
524  *  Success: S_OK. iface is initialised with the parameters given.
525  *  Failure: An HRESULT error code indicating the error.
526  */
527 static HRESULT WINAPI IQueryAssociations_fnInit(
528   IQueryAssociations *iface,
529   ASSOCF cfFlags,
530   LPCWSTR pszAssoc,
531   HKEY hkeyProgid,
532   HWND hWnd)
533 {
534     static const WCHAR szProgID[] = {'P','r','o','g','I','D',0};
535     IQueryAssociationsImpl *This = (IQueryAssociationsImpl *)iface;
536     LONG ret;
537
538     TRACE("(%p)->(%d,%s,%p,%p)\n", iface,
539                                     cfFlags,
540                                     debugstr_w(pszAssoc),
541                                     hkeyProgid,
542                                     hWnd);
543     if (hWnd != NULL)
544         FIXME("hwnd != NULL not supported\n");
545     if (cfFlags != 0)
546         FIXME("unsupported flags: %x\n", cfFlags);
547     if (pszAssoc != NULL)
548     {
549         ret = RegOpenKeyExW(HKEY_CLASSES_ROOT,
550                             pszAssoc,
551                             0,
552                             KEY_READ,
553                             &This->hkeySource);
554         if (ret != ERROR_SUCCESS)
555             return E_FAIL;
556         /* if this is not a prog id */
557         if ((*pszAssoc == '.') || (*pszAssoc == '{'))
558         {
559             RegOpenKeyExW(This->hkeySource,
560                           szProgID,
561                           0,
562                           KEY_READ,
563                           &This->hkeyProgID);
564         }
565         else
566             This->hkeyProgID = This->hkeySource;
567         return S_OK;
568     }
569     else if (hkeyProgid != NULL)
570     {
571         This->hkeyProgID = hkeyProgid;
572         return S_OK;
573     }
574     else
575         return E_INVALIDARG;
576 }
577
578 static HRESULT ASSOC_GetValue(HKEY hkey, WCHAR ** pszText)
579 {
580   DWORD len;
581   LONG ret;
582
583   assert(pszText);
584   ret = RegQueryValueExW(hkey, NULL, 0, NULL, NULL, &len);
585   if (ret != ERROR_SUCCESS)
586     return HRESULT_FROM_WIN32(ret);
587   if (!len)
588     return E_FAIL;
589   *pszText = HeapAlloc(GetProcessHeap(), 0, len);
590   if (!*pszText)
591     return E_OUTOFMEMORY;
592   ret = RegQueryValueExW(hkey, NULL, 0, NULL, (LPBYTE)*pszText,
593                          &len);
594   if (ret != ERROR_SUCCESS)
595   {
596     HeapFree(GetProcessHeap(), 0, *pszText);
597     return HRESULT_FROM_WIN32(ret);
598   }
599   return S_OK;
600 }
601
602 static HRESULT ASSOC_GetCommand(IQueryAssociationsImpl *This,
603                                 LPCWSTR pszExtra, WCHAR **ppszCommand)
604 {
605   HKEY hkeyCommand;
606   HKEY hkeyFile;
607   HKEY hkeyShell;
608   HKEY hkeyVerb;
609   HRESULT hr;
610   LONG ret;
611   WCHAR * pszExtraFromReg = NULL;
612   WCHAR * pszFileType;
613   static const WCHAR commandW[] = { 'c','o','m','m','a','n','d',0 };
614   static const WCHAR shellW[] = { 's','h','e','l','l',0 };
615
616   hr = ASSOC_GetValue(This->hkeySource, &pszFileType);
617   if (FAILED(hr))
618     return hr;
619   ret = RegOpenKeyExW(HKEY_CLASSES_ROOT, pszFileType, 0, KEY_READ, &hkeyFile);
620   HeapFree(GetProcessHeap(), 0, pszFileType);
621   if (ret != ERROR_SUCCESS)
622     return HRESULT_FROM_WIN32(ret);
623
624   ret = RegOpenKeyExW(hkeyFile, shellW, 0, KEY_READ, &hkeyShell);
625   RegCloseKey(hkeyFile);
626   if (ret != ERROR_SUCCESS)
627     return HRESULT_FROM_WIN32(ret);
628
629   if (!pszExtra)
630   {
631     hr = ASSOC_GetValue(hkeyShell, &pszExtraFromReg);
632     /* if no default action */
633     if (hr == E_FAIL || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
634     {
635       DWORD rlen;
636       ret = RegQueryInfoKeyW(hkeyShell, 0, 0, 0, 0, &rlen, 0, 0, 0, 0, 0, 0);
637       if (ret != ERROR_SUCCESS)
638       {
639         RegCloseKey(hkeyShell);
640         return HRESULT_FROM_WIN32(ret);
641       }
642       rlen++;
643       pszExtraFromReg = HeapAlloc(GetProcessHeap(), 0, rlen * sizeof(WCHAR));
644       if (!pszExtraFromReg)
645       {
646         RegCloseKey(hkeyShell);
647         return E_OUTOFMEMORY;
648       }
649       ret = RegEnumKeyExW(hkeyShell, 0, pszExtraFromReg, &rlen, 0, NULL, NULL, NULL);
650       if (ret != ERROR_SUCCESS)
651       {
652         RegCloseKey(hkeyShell);
653         return HRESULT_FROM_WIN32(ret);
654       }
655     }
656     else if (FAILED(hr))
657     {
658       RegCloseKey(hkeyShell);
659       return hr;
660     }
661   }
662
663   ret = RegOpenKeyExW(hkeyShell, pszExtra ? pszExtra : pszExtraFromReg, 0,
664                       KEY_READ, &hkeyVerb);
665   HeapFree(GetProcessHeap(), 0, pszExtraFromReg);
666   RegCloseKey(hkeyShell);
667   if (ret != ERROR_SUCCESS)
668     return HRESULT_FROM_WIN32(ret);
669
670   ret = RegOpenKeyExW(hkeyVerb, commandW, 0, KEY_READ, &hkeyCommand);
671   RegCloseKey(hkeyVerb);
672   if (ret != ERROR_SUCCESS)
673     return HRESULT_FROM_WIN32(ret);
674   hr = ASSOC_GetValue(hkeyCommand, ppszCommand);
675   RegCloseKey(hkeyCommand);
676   return hr;
677 }
678
679 static HRESULT ASSOC_GetExecutable(IQueryAssociationsImpl *This,
680                                    LPCWSTR pszExtra, LPWSTR path,
681                                    DWORD pathlen, DWORD *len)
682 {
683   WCHAR *pszCommand;
684   WCHAR *pszStart;
685   WCHAR *pszEnd;
686   HRESULT hr;
687
688   assert(len);
689
690   hr = ASSOC_GetCommand(This, pszExtra, &pszCommand);
691   if (FAILED(hr))
692     return hr;
693
694   /* cleanup pszCommand */
695   if (pszCommand[0] == '"')
696   {
697     pszStart = pszCommand + 1;
698     pszEnd = strchrW(pszStart, '"');
699   }
700   else
701   {
702     pszStart = pszCommand;
703     pszEnd = strchrW(pszStart, ' ');
704   }
705   if (pszEnd)
706     *pszEnd = 0;
707
708   *len = SearchPathW(NULL, pszStart, NULL, pathlen, path, NULL);
709   HeapFree(GetProcessHeap(), 0, pszCommand);
710   if (!*len)
711     return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
712   return S_OK;
713 }
714
715 static HRESULT ASSOC_ReturnData(LPWSTR out, DWORD *outlen, LPCWSTR data,
716                                 DWORD datalen)
717 {
718   assert(outlen);
719
720   if (out)
721   {
722     if (*outlen < datalen)
723     {
724       *outlen = datalen;
725       return E_POINTER;
726     }
727     *outlen = datalen;
728     lstrcpynW(out, data, datalen);
729     return S_OK;
730   }
731   else
732   {
733     *outlen = datalen;
734     return S_FALSE;
735   }
736 }
737
738 /**************************************************************************
739  *  IQueryAssociations_GetString {SHLWAPI}
740  *
741  * Get a file association string from the registry.
742  *
743  * PARAMS
744  *  iface    [I]   IQueryAssociations interface to query
745  *  cfFlags  [I]   ASSOCF_ flags from "shlwapi.h"
746  *  str      [I]   Type of string to get (ASSOCSTR enum from "shlwapi.h")
747  *  pszExtra [I]   Extra information about the string location
748  *  pszOut   [O]   Destination for the association string
749  *  pcchOut  [I/O] Length of pszOut
750  *
751  * RETURNS
752  *  Success: S_OK. pszOut contains the string, pcchOut contains its length.
753  *  Failure: An HRESULT error code indicating the error.
754  */
755 static HRESULT WINAPI IQueryAssociations_fnGetString(
756   IQueryAssociations *iface,
757   ASSOCF cfFlags,
758   ASSOCSTR str,
759   LPCWSTR pszExtra,
760   LPWSTR pszOut,
761   DWORD *pcchOut)
762 {
763   IQueryAssociationsImpl *This = (IQueryAssociationsImpl *)iface;
764   const ASSOCF cfUnimplemented = ~(0);
765   DWORD len = 0;
766   HRESULT hr;
767   WCHAR path[MAX_PATH];
768
769   TRACE("(%p,0x%8x,0x%8x,%s,%p,%p)\n", This, cfFlags, str,
770         debugstr_w(pszExtra), pszOut, pcchOut);
771
772   if (cfFlags & cfUnimplemented)
773     FIXME("%08x: unimplemented flags!\n", cfFlags & cfUnimplemented);
774
775   if (!pcchOut)
776     return E_UNEXPECTED;
777
778   switch (str)
779   {
780     case ASSOCSTR_COMMAND:
781     {
782       WCHAR *command;
783       hr = ASSOC_GetCommand(This, pszExtra, &command);
784       if (SUCCEEDED(hr))
785       {
786         hr = ASSOC_ReturnData(pszOut, pcchOut, command, strlenW(command) + 1);
787         HeapFree(GetProcessHeap(), 0, command);
788       }
789       return hr;
790     }
791
792     case ASSOCSTR_EXECUTABLE:
793     {
794       hr = ASSOC_GetExecutable(This, pszExtra, path, MAX_PATH, &len);
795       if (FAILED(hr))
796         return hr;
797       len++;
798       return ASSOC_ReturnData(pszOut, pcchOut, path, len);
799     }
800
801     case ASSOCSTR_FRIENDLYDOCNAME:
802     {
803       WCHAR *pszFileType;
804       DWORD ret;
805       DWORD size;
806
807       hr = ASSOC_GetValue(This->hkeySource, &pszFileType);
808       if (FAILED(hr))
809         return hr;
810       size = 0;
811       ret = RegGetValueW(HKEY_CLASSES_ROOT, pszFileType, NULL, RRF_RT_REG_SZ, NULL, NULL, &size);
812       if (ret == ERROR_SUCCESS)
813       {
814         WCHAR *docName = HeapAlloc(GetProcessHeap(), 0, size);
815         if (docName)
816         {
817           ret = RegGetValueW(HKEY_CLASSES_ROOT, pszFileType, NULL, RRF_RT_REG_SZ, NULL, docName, &size);
818           if (ret == ERROR_SUCCESS)
819             hr = ASSOC_ReturnData(pszOut, pcchOut, docName, strlenW(docName) + 1);
820           else
821             hr = HRESULT_FROM_WIN32(ret);
822           HeapFree(GetProcessHeap(), 0, docName);
823         }
824         else
825           hr = E_OUTOFMEMORY;
826       }
827       else
828         hr = HRESULT_FROM_WIN32(ret);
829       HeapFree(GetProcessHeap(), 0, pszFileType);
830       return hr;
831     }
832
833     case ASSOCSTR_FRIENDLYAPPNAME:
834     {
835       PVOID verinfoW = NULL;
836       DWORD size, retval = 0;
837       UINT flen;
838       WCHAR *bufW;
839       static const WCHAR translationW[] = {
840         '\\','V','a','r','F','i','l','e','I','n','f','o',
841         '\\','T','r','a','n','s','l','a','t','i','o','n',0
842       };
843       static const WCHAR fileDescFmtW[] = {
844         '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
845         '\\','%','0','4','x','%','0','4','x',
846         '\\','F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0
847       };
848       WCHAR fileDescW[41];
849
850       hr = ASSOC_GetExecutable(This, pszExtra, path, MAX_PATH, &len);
851       if (FAILED(hr))
852         return hr;
853
854       retval = GetFileVersionInfoSizeW(path, &size);
855       if (!retval)
856         goto get_friendly_name_fail;
857       verinfoW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, retval);
858       if (!verinfoW)
859         return E_OUTOFMEMORY;
860       if (!GetFileVersionInfoW(path, 0, retval, verinfoW))
861         goto get_friendly_name_fail;
862       if (VerQueryValueW(verinfoW, translationW, (LPVOID *)&bufW, &flen))
863       {
864         UINT i;
865         DWORD *langCodeDesc = (DWORD *)bufW;
866         for (i = 0; i < flen / sizeof(DWORD); i++)
867         {
868           sprintfW(fileDescW, fileDescFmtW, LOWORD(langCodeDesc[i]),
869                    HIWORD(langCodeDesc[i]));
870           if (VerQueryValueW(verinfoW, fileDescW, (LPVOID *)&bufW, &flen))
871           {
872             /* Does strlenW(bufW) == 0 mean we use the filename? */
873             len = strlenW(bufW) + 1;
874             TRACE("found FileDescription: %s\n", debugstr_w(bufW));
875             return ASSOC_ReturnData(pszOut, pcchOut, bufW, len);
876           }
877         }
878       }
879 get_friendly_name_fail:
880       PathRemoveExtensionW(path);
881       PathStripPathW(path);
882       TRACE("using filename: %s\n", debugstr_w(path));
883       return ASSOC_ReturnData(pszOut, pcchOut, path, strlenW(path) + 1);
884     }
885
886     case ASSOCSTR_CONTENTTYPE:
887     {
888       static const WCHAR Content_TypeW[] = {'C','o','n','t','e','n','t',' ','T','y','p','e',0};
889       WCHAR *contentType;
890       DWORD ret;
891       DWORD size;
892
893       size = 0;
894       ret = RegGetValueW(This->hkeySource, NULL, Content_TypeW, RRF_RT_REG_SZ, NULL, NULL, &size);
895       if (ret != ERROR_SUCCESS)
896         return HRESULT_FROM_WIN32(ret);
897       contentType = HeapAlloc(GetProcessHeap(), 0, size);
898       if (contentType != NULL)
899       {
900         ret = RegGetValueW(This->hkeySource, NULL, Content_TypeW, RRF_RT_REG_SZ, NULL, contentType, &size);
901         if (ret == ERROR_SUCCESS)
902           hr = ASSOC_ReturnData(pszOut, pcchOut, contentType, strlenW(contentType) + 1);
903         else
904           hr = HRESULT_FROM_WIN32(ret);
905         HeapFree(GetProcessHeap(), 0, contentType);
906       }
907       else
908         hr = E_OUTOFMEMORY;
909       return hr;
910     }
911
912     case ASSOCSTR_DEFAULTICON:
913     {
914       static const WCHAR DefaultIconW[] = {'D','e','f','a','u','l','t','I','c','o','n',0};
915       WCHAR *pszFileType;
916       DWORD ret;
917       DWORD size;
918       HKEY hkeyFile;
919
920       hr = ASSOC_GetValue(This->hkeySource, &pszFileType);
921       if (FAILED(hr))
922         return hr;
923       ret = RegOpenKeyExW(HKEY_CLASSES_ROOT, pszFileType, 0, KEY_READ, &hkeyFile);
924       if (ret == ERROR_SUCCESS)
925       {
926         size = 0;
927         ret = RegGetValueW(hkeyFile, DefaultIconW, NULL, RRF_RT_REG_SZ, NULL, NULL, &size);
928         if (ret == ERROR_SUCCESS)
929         {
930           WCHAR *icon = HeapAlloc(GetProcessHeap(), 0, size);
931           if (icon)
932           {
933             ret = RegGetValueW(hkeyFile, DefaultIconW, NULL, RRF_RT_REG_SZ, NULL, icon, &size);
934             if (ret == ERROR_SUCCESS)
935               hr = ASSOC_ReturnData(pszOut, pcchOut, icon, strlenW(icon) + 1);
936             else
937               hr = HRESULT_FROM_WIN32(ret);
938             HeapFree(GetProcessHeap(), 0, icon);
939           }
940           else
941             hr = E_OUTOFMEMORY;
942         }
943         else
944           hr = HRESULT_FROM_WIN32(ret);
945         RegCloseKey(hkeyFile);
946       }
947       else
948         hr = HRESULT_FROM_WIN32(ret);
949       HeapFree(GetProcessHeap(), 0, pszFileType);
950       return hr;
951     }
952
953     default:
954       FIXME("assocstr %d unimplemented!\n", str);
955       return E_NOTIMPL;
956   }
957 }
958
959 /**************************************************************************
960  *  IQueryAssociations_GetKey {SHLWAPI}
961  *
962  * Get a file association key from the registry.
963  *
964  * PARAMS
965  *  iface    [I] IQueryAssociations interface to query
966  *  cfFlags  [I] ASSOCF_ flags from "shlwapi.h"
967  *  assockey [I] Type of key to get (ASSOCKEY enum from "shlwapi.h")
968  *  pszExtra [I] Extra information about the key location
969  *  phkeyOut [O] Destination for the association key
970  *
971  * RETURNS
972  *  Success: S_OK. phkeyOut contains a handle to the key.
973  *  Failure: An HRESULT error code indicating the error.
974  */
975 static HRESULT WINAPI IQueryAssociations_fnGetKey(
976   IQueryAssociations *iface,
977   ASSOCF cfFlags,
978   ASSOCKEY assockey,
979   LPCWSTR pszExtra,
980   HKEY *phkeyOut)
981 {
982   IQueryAssociationsImpl *This = (IQueryAssociationsImpl *)iface;
983
984   FIXME("(%p,0x%8x,0x%8x,%s,%p)-stub!\n", This, cfFlags, assockey,
985         debugstr_w(pszExtra), phkeyOut);
986   return E_NOTIMPL;
987 }
988
989 /**************************************************************************
990  *  IQueryAssociations_GetData {SHLWAPI}
991  *
992  * Get the data for a file association key from the registry.
993  *
994  * PARAMS
995  *  iface     [I]   IQueryAssociations interface to query
996  *  cfFlags   [I]   ASSOCF_ flags from "shlwapi.h"
997  *  assocdata [I]   Type of data to get (ASSOCDATA enum from "shlwapi.h")
998  *  pszExtra  [I]   Extra information about the data location
999  *  pvOut     [O]   Destination for the association key
1000  *  pcbOut    [I/O] Size of pvOut
1001  *
1002  * RETURNS
1003  *  Success: S_OK. pszOut contains the data, pcbOut contains its length.
1004  *  Failure: An HRESULT error code indicating the error.
1005  */
1006 static HRESULT WINAPI IQueryAssociations_fnGetData(
1007   IQueryAssociations *iface,
1008   ASSOCF cfFlags,
1009   ASSOCDATA assocdata,
1010   LPCWSTR pszExtra,
1011   LPVOID pvOut,
1012   DWORD *pcbOut)
1013 {
1014   IQueryAssociationsImpl *This = (IQueryAssociationsImpl *)iface;
1015
1016   FIXME("(%p,0x%8x,0x%8x,%s,%p,%p)-stub!\n", This, cfFlags, assocdata,
1017         debugstr_w(pszExtra), pvOut, pcbOut);
1018   return E_NOTIMPL;
1019 }
1020
1021 /**************************************************************************
1022  *  IQueryAssociations_GetEnum {SHLWAPI}
1023  *
1024  * Not yet implemented in native Win32.
1025  *
1026  * PARAMS
1027  *  iface     [I] IQueryAssociations interface to query
1028  *  cfFlags   [I] ASSOCF_ flags from "shlwapi.h"
1029  *  assocenum [I] Type of enum to get (ASSOCENUM enum from "shlwapi.h")
1030  *  pszExtra  [I] Extra information about the enum location
1031  *  riid      [I] REFIID to look for
1032  *  ppvOut    [O] Destination for the interface.
1033  *
1034  * RETURNS
1035  *  Success: S_OK.
1036  *  Failure: An HRESULT error code indicating the error.
1037  *
1038  * NOTES
1039  *  Presumably this function returns an enumerator object.
1040  */
1041 static HRESULT WINAPI IQueryAssociations_fnGetEnum(
1042   IQueryAssociations *iface,
1043   ASSOCF cfFlags,
1044   ASSOCENUM assocenum,
1045   LPCWSTR pszExtra,
1046   REFIID riid,
1047   LPVOID *ppvOut)
1048 {
1049   IQueryAssociationsImpl *This = (IQueryAssociationsImpl *)iface;
1050
1051   FIXME("(%p,0x%8x,0x%8x,%s,%s,%p)-stub!\n", This, cfFlags, assocenum,
1052         debugstr_w(pszExtra), debugstr_guid(riid), ppvOut);
1053   return E_NOTIMPL;
1054 }
1055
1056 static const IQueryAssociationsVtbl IQueryAssociations_vtbl =
1057 {
1058   IQueryAssociations_fnQueryInterface,
1059   IQueryAssociations_fnAddRef,
1060   IQueryAssociations_fnRelease,
1061   IQueryAssociations_fnInit,
1062   IQueryAssociations_fnGetString,
1063   IQueryAssociations_fnGetKey,
1064   IQueryAssociations_fnGetData,
1065   IQueryAssociations_fnGetEnum
1066 };