gdiplus: Draw hotkey underlines in GdipDrawString.
[wine] / dlls / shell32 / 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 #define COBJMACROS
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winnls.h"
27 #include "winreg.h"
28 #include "objbase.h"
29 #include "shlguid.h"
30 #include "shlwapi.h"
31 #include "shobjidl.h"
32 #include "shell32_main.h"
33 #include "ver.h"
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(shell);
38
39 /**************************************************************************
40  *  IQueryAssociations
41  *
42  * DESCRIPTION
43  *  This object provides a layer of abstraction over the system registry in
44  *  order to simplify the process of parsing associations between files.
45  *  Associations in this context means the registry entries that link (for
46  *  example) the extension of a file with its description, list of
47  *  applications to open the file with, and actions that can be performed on it
48  *  (the shell displays such information in the context menu of explorer
49  *  when you right-click on a file).
50  *
51  * HELPERS
52  * You can use this object transparently by calling the helper functions
53  * AssocQueryKeyA(), AssocQueryStringA() and AssocQueryStringByKeyA(). These
54  * create an IQueryAssociations object, perform the requested actions
55  * and then dispose of the object. Alternatively, you can create an instance
56  * of the object using AssocCreate() and call the following methods on it:
57  *
58  * METHODS
59  */
60
61 typedef struct
62 {
63   IQueryAssociations IQueryAssociations_iface;
64   LONG ref;
65   HKEY hkeySource;
66   HKEY hkeyProgID;
67 } IQueryAssociationsImpl;
68
69 typedef struct
70 {
71   IApplicationAssociationRegistration IApplicationAssociationRegistration_iface;
72   LONG ref;
73 } IApplicationAssociationRegistrationImpl;
74
75
76 static inline IQueryAssociationsImpl *impl_from_IQueryAssociations(IQueryAssociations *iface)
77 {
78   return CONTAINING_RECORD(iface, IQueryAssociationsImpl, IQueryAssociations_iface);
79 }
80
81 /**************************************************************************
82  *  IQueryAssociations_QueryInterface
83  *
84  * See IUnknown_QueryInterface.
85  */
86 static HRESULT WINAPI IQueryAssociations_fnQueryInterface(
87   IQueryAssociations* iface,
88   REFIID riid,
89   LPVOID *ppvObj)
90 {
91   IQueryAssociationsImpl *This = impl_from_IQueryAssociations(iface);
92
93   TRACE("(%p,%s,%p)\n",This, debugstr_guid(riid), ppvObj);
94
95   if (ppvObj == NULL)
96       return E_POINTER;
97
98   *ppvObj = NULL;
99
100   if (IsEqualIID(riid, &IID_IUnknown) ||
101       IsEqualIID(riid, &IID_IQueryAssociations))
102   {
103     *ppvObj = This;
104
105     IQueryAssociations_AddRef((IQueryAssociations*)*ppvObj);
106     TRACE("Returning IQueryAssociations (%p)\n", *ppvObj);
107     return S_OK;
108   }
109   TRACE("Returning E_NOINTERFACE\n");
110   return E_NOINTERFACE;
111 }
112
113 /**************************************************************************
114  *  IQueryAssociations_AddRef
115  *
116  * See IUnknown_AddRef.
117  */
118 static ULONG WINAPI IQueryAssociations_fnAddRef(IQueryAssociations *iface)
119 {
120   IQueryAssociationsImpl *This = impl_from_IQueryAssociations(iface);
121   ULONG refCount = InterlockedIncrement(&This->ref);
122
123   TRACE("(%p)->(ref before=%u)\n",This, refCount - 1);
124
125   return refCount;
126 }
127
128 /**************************************************************************
129  *  IQueryAssociations_Release
130  *
131  * See IUnknown_Release.
132  */
133 static ULONG WINAPI IQueryAssociations_fnRelease(IQueryAssociations *iface)
134 {
135   IQueryAssociationsImpl *This = impl_from_IQueryAssociations(iface);
136   ULONG refCount = InterlockedDecrement(&This->ref);
137
138   TRACE("(%p)->(ref before=%u)\n",This, refCount + 1);
139
140   if (!refCount)
141   {
142     TRACE("Destroying IQueryAssociations (%p)\n", This);
143     RegCloseKey(This->hkeySource);
144     RegCloseKey(This->hkeyProgID);
145     SHFree(This);
146   }
147
148   return refCount;
149 }
150
151 /**************************************************************************
152  *  IQueryAssociations_Init
153  *
154  * Initialise an IQueryAssociations object.
155  *
156  * PARAMS
157  *  iface      [I] IQueryAssociations interface to initialise
158  *  cfFlags    [I] ASSOCF_ flags from "shlwapi.h"
159  *  pszAssoc   [I] String for the root key name, or NULL if hkeyProgid is given
160  *  hkeyProgid [I] Handle for the root key, or NULL if pszAssoc is given
161  *  hWnd       [I] Reserved, must be NULL.
162  *
163  * RETURNS
164  *  Success: S_OK. iface is initialised with the parameters given.
165  *  Failure: An HRESULT error code indicating the error.
166  */
167 static HRESULT WINAPI IQueryAssociations_fnInit(
168   IQueryAssociations *iface,
169   ASSOCF cfFlags,
170   LPCWSTR pszAssoc,
171   HKEY hkeyProgid,
172   HWND hWnd)
173 {
174     static const WCHAR szProgID[] = {'P','r','o','g','I','D',0};
175     IQueryAssociationsImpl *This = impl_from_IQueryAssociations(iface);
176     LONG ret;
177
178     TRACE("(%p)->(%d,%s,%p,%p)\n", iface,
179                                     cfFlags,
180                                     debugstr_w(pszAssoc),
181                                     hkeyProgid,
182                                     hWnd);
183     if (hWnd != NULL)
184         FIXME("hwnd != NULL not supported\n");
185     if (cfFlags != 0)
186         FIXME("unsupported flags: %x\n", cfFlags);
187     if (pszAssoc != NULL)
188     {
189         ret = RegOpenKeyExW(HKEY_CLASSES_ROOT,
190                             pszAssoc,
191                             0,
192                             KEY_READ,
193                             &This->hkeySource);
194         if (ret != ERROR_SUCCESS)
195             return E_FAIL;
196         /* if this is not a prog id */
197         if ((*pszAssoc == '.') || (*pszAssoc == '{'))
198         {
199             RegOpenKeyExW(This->hkeySource,
200                           szProgID,
201                           0,
202                           KEY_READ,
203                           &This->hkeyProgID);
204         }
205         else
206             This->hkeyProgID = This->hkeySource;
207         return S_OK;
208     }
209     else if (hkeyProgid != NULL)
210     {
211         This->hkeyProgID = hkeyProgid;
212         return S_OK;
213     }
214     else
215         return E_INVALIDARG;
216 }
217
218 static HRESULT ASSOC_GetValue(HKEY hkey, WCHAR ** pszText)
219 {
220   DWORD len;
221   LONG ret;
222
223   assert(pszText);
224   ret = RegQueryValueExW(hkey, NULL, 0, NULL, NULL, &len);
225   if (ret != ERROR_SUCCESS)
226     return HRESULT_FROM_WIN32(ret);
227   if (!len)
228     return E_FAIL;
229   *pszText = HeapAlloc(GetProcessHeap(), 0, len);
230   if (!*pszText)
231     return E_OUTOFMEMORY;
232   ret = RegQueryValueExW(hkey, NULL, 0, NULL, (LPBYTE)*pszText,
233                          &len);
234   if (ret != ERROR_SUCCESS)
235   {
236     HeapFree(GetProcessHeap(), 0, *pszText);
237     return HRESULT_FROM_WIN32(ret);
238   }
239   return S_OK;
240 }
241
242 static HRESULT ASSOC_GetCommand(IQueryAssociationsImpl *This,
243                                 LPCWSTR pszExtra, WCHAR **ppszCommand)
244 {
245   HKEY hkeyCommand;
246   HKEY hkeyFile;
247   HKEY hkeyShell;
248   HKEY hkeyVerb;
249   HRESULT hr;
250   LONG ret;
251   WCHAR * pszExtraFromReg = NULL;
252   WCHAR * pszFileType;
253   static const WCHAR commandW[] = { 'c','o','m','m','a','n','d',0 };
254   static const WCHAR shellW[] = { 's','h','e','l','l',0 };
255
256   hr = ASSOC_GetValue(This->hkeySource, &pszFileType);
257   if (FAILED(hr))
258     return hr;
259   ret = RegOpenKeyExW(HKEY_CLASSES_ROOT, pszFileType, 0, KEY_READ, &hkeyFile);
260   HeapFree(GetProcessHeap(), 0, pszFileType);
261   if (ret != ERROR_SUCCESS)
262     return HRESULT_FROM_WIN32(ret);
263
264   ret = RegOpenKeyExW(hkeyFile, shellW, 0, KEY_READ, &hkeyShell);
265   RegCloseKey(hkeyFile);
266   if (ret != ERROR_SUCCESS)
267     return HRESULT_FROM_WIN32(ret);
268
269   if (!pszExtra)
270   {
271     hr = ASSOC_GetValue(hkeyShell, &pszExtraFromReg);
272     /* if no default action */
273     if (hr == E_FAIL || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
274     {
275       DWORD rlen;
276       ret = RegQueryInfoKeyW(hkeyShell, 0, 0, 0, 0, &rlen, 0, 0, 0, 0, 0, 0);
277       if (ret != ERROR_SUCCESS)
278       {
279         RegCloseKey(hkeyShell);
280         return HRESULT_FROM_WIN32(ret);
281       }
282       rlen++;
283       pszExtraFromReg = HeapAlloc(GetProcessHeap(), 0, rlen * sizeof(WCHAR));
284       if (!pszExtraFromReg)
285       {
286         RegCloseKey(hkeyShell);
287         return E_OUTOFMEMORY;
288       }
289       ret = RegEnumKeyExW(hkeyShell, 0, pszExtraFromReg, &rlen, 0, NULL, NULL, NULL);
290       if (ret != ERROR_SUCCESS)
291       {
292         RegCloseKey(hkeyShell);
293         return HRESULT_FROM_WIN32(ret);
294       }
295     }
296     else if (FAILED(hr))
297     {
298       RegCloseKey(hkeyShell);
299       return hr;
300     }
301   }
302
303   ret = RegOpenKeyExW(hkeyShell, pszExtra ? pszExtra : pszExtraFromReg, 0,
304                       KEY_READ, &hkeyVerb);
305   HeapFree(GetProcessHeap(), 0, pszExtraFromReg);
306   RegCloseKey(hkeyShell);
307   if (ret != ERROR_SUCCESS)
308     return HRESULT_FROM_WIN32(ret);
309
310   ret = RegOpenKeyExW(hkeyVerb, commandW, 0, KEY_READ, &hkeyCommand);
311   RegCloseKey(hkeyVerb);
312   if (ret != ERROR_SUCCESS)
313     return HRESULT_FROM_WIN32(ret);
314   hr = ASSOC_GetValue(hkeyCommand, ppszCommand);
315   RegCloseKey(hkeyCommand);
316   return hr;
317 }
318
319 static HRESULT ASSOC_GetExecutable(IQueryAssociationsImpl *This,
320                                    LPCWSTR pszExtra, LPWSTR path,
321                                    DWORD pathlen, DWORD *len)
322 {
323   WCHAR *pszCommand;
324   WCHAR *pszStart;
325   WCHAR *pszEnd;
326   HRESULT hr;
327
328   assert(len);
329
330   hr = ASSOC_GetCommand(This, pszExtra, &pszCommand);
331   if (FAILED(hr))
332     return hr;
333
334   /* cleanup pszCommand */
335   if (pszCommand[0] == '"')
336   {
337     pszStart = pszCommand + 1;
338     pszEnd = strchrW(pszStart, '"');
339     if (pszEnd)
340       *pszEnd = 0;
341     *len = SearchPathW(NULL, pszStart, NULL, pathlen, path, NULL);
342   }
343   else
344   {
345     pszStart = pszCommand;
346     for (pszEnd = pszStart; (pszEnd = strchrW(pszEnd, ' ')); pszEnd++)
347     {
348       WCHAR c = *pszEnd;
349       *pszEnd = 0;
350       if ((*len = SearchPathW(NULL, pszStart, NULL, pathlen, path, NULL)))
351         break;
352       *pszEnd = c;
353     }
354     if (!pszEnd)
355       *len = SearchPathW(NULL, pszStart, NULL, pathlen, path, NULL);
356   }
357
358   HeapFree(GetProcessHeap(), 0, pszCommand);
359   if (!*len)
360     return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
361   return S_OK;
362 }
363
364 static HRESULT ASSOC_ReturnData(LPWSTR out, DWORD *outlen, LPCWSTR data,
365                                 DWORD datalen)
366 {
367   assert(outlen);
368
369   if (out)
370   {
371     if (*outlen < datalen)
372     {
373       *outlen = datalen;
374       return E_POINTER;
375     }
376     *outlen = datalen;
377     lstrcpynW(out, data, datalen);
378     return S_OK;
379   }
380   else
381   {
382     *outlen = datalen;
383     return S_FALSE;
384   }
385 }
386
387 /**************************************************************************
388  *  IQueryAssociations_GetString
389  *
390  * Get a file association string from the registry.
391  *
392  * PARAMS
393  *  iface    [I]   IQueryAssociations interface to query
394  *  cfFlags  [I]   ASSOCF_ flags from "shlwapi.h"
395  *  str      [I]   Type of string to get (ASSOCSTR enum from "shlwapi.h")
396  *  pszExtra [I]   Extra information about the string location
397  *  pszOut   [O]   Destination for the association string
398  *  pcchOut  [I/O] Length of pszOut
399  *
400  * RETURNS
401  *  Success: S_OK. pszOut contains the string, pcchOut contains its length.
402  *  Failure: An HRESULT error code indicating the error.
403  */
404 static HRESULT WINAPI IQueryAssociations_fnGetString(
405   IQueryAssociations *iface,
406   ASSOCF cfFlags,
407   ASSOCSTR str,
408   LPCWSTR pszExtra,
409   LPWSTR pszOut,
410   DWORD *pcchOut)
411 {
412   IQueryAssociationsImpl *This = impl_from_IQueryAssociations(iface);
413   const ASSOCF cfUnimplemented = ~(0);
414   DWORD len = 0;
415   HRESULT hr;
416   WCHAR path[MAX_PATH];
417
418   TRACE("(%p,0x%8x,0x%8x,%s,%p,%p)\n", This, cfFlags, str,
419         debugstr_w(pszExtra), pszOut, pcchOut);
420
421   if (cfFlags & cfUnimplemented)
422     FIXME("%08x: unimplemented flags!\n", cfFlags & cfUnimplemented);
423
424   if (!pcchOut)
425     return E_UNEXPECTED;
426
427   switch (str)
428   {
429     case ASSOCSTR_COMMAND:
430     {
431       WCHAR *command;
432       hr = ASSOC_GetCommand(This, pszExtra, &command);
433       if (SUCCEEDED(hr))
434       {
435         hr = ASSOC_ReturnData(pszOut, pcchOut, command, strlenW(command) + 1);
436         HeapFree(GetProcessHeap(), 0, command);
437       }
438       return hr;
439     }
440
441     case ASSOCSTR_EXECUTABLE:
442     {
443       hr = ASSOC_GetExecutable(This, pszExtra, path, MAX_PATH, &len);
444       if (FAILED(hr))
445         return hr;
446       len++;
447       return ASSOC_ReturnData(pszOut, pcchOut, path, len);
448     }
449
450     case ASSOCSTR_FRIENDLYDOCNAME:
451     {
452       WCHAR *pszFileType;
453       DWORD ret;
454       DWORD size;
455
456       hr = ASSOC_GetValue(This->hkeySource, &pszFileType);
457       if (FAILED(hr))
458         return hr;
459       size = 0;
460       ret = RegGetValueW(HKEY_CLASSES_ROOT, pszFileType, NULL, RRF_RT_REG_SZ, NULL, NULL, &size);
461       if (ret == ERROR_SUCCESS)
462       {
463         WCHAR *docName = HeapAlloc(GetProcessHeap(), 0, size);
464         if (docName)
465         {
466           ret = RegGetValueW(HKEY_CLASSES_ROOT, pszFileType, NULL, RRF_RT_REG_SZ, NULL, docName, &size);
467           if (ret == ERROR_SUCCESS)
468             hr = ASSOC_ReturnData(pszOut, pcchOut, docName, strlenW(docName) + 1);
469           else
470             hr = HRESULT_FROM_WIN32(ret);
471           HeapFree(GetProcessHeap(), 0, docName);
472         }
473         else
474           hr = E_OUTOFMEMORY;
475       }
476       else
477         hr = HRESULT_FROM_WIN32(ret);
478       HeapFree(GetProcessHeap(), 0, pszFileType);
479       return hr;
480     }
481
482     case ASSOCSTR_FRIENDLYAPPNAME:
483     {
484       PVOID verinfoW = NULL;
485       DWORD size, retval = 0;
486       UINT flen;
487       WCHAR *bufW;
488       static const WCHAR translationW[] = {
489         '\\','V','a','r','F','i','l','e','I','n','f','o',
490         '\\','T','r','a','n','s','l','a','t','i','o','n',0
491       };
492       static const WCHAR fileDescFmtW[] = {
493         '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
494         '\\','%','0','4','x','%','0','4','x',
495         '\\','F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0
496       };
497       WCHAR fileDescW[41];
498
499       hr = ASSOC_GetExecutable(This, pszExtra, path, MAX_PATH, &len);
500       if (FAILED(hr))
501         return hr;
502
503       retval = GetFileVersionInfoSizeW(path, &size);
504       if (!retval)
505         goto get_friendly_name_fail;
506       verinfoW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, retval);
507       if (!verinfoW)
508         return E_OUTOFMEMORY;
509       if (!GetFileVersionInfoW(path, 0, retval, verinfoW))
510         goto get_friendly_name_fail;
511       if (VerQueryValueW(verinfoW, translationW, (LPVOID *)&bufW, &flen))
512       {
513         UINT i;
514         DWORD *langCodeDesc = (DWORD *)bufW;
515         for (i = 0; i < flen / sizeof(DWORD); i++)
516         {
517           sprintfW(fileDescW, fileDescFmtW, LOWORD(langCodeDesc[i]),
518                    HIWORD(langCodeDesc[i]));
519           if (VerQueryValueW(verinfoW, fileDescW, (LPVOID *)&bufW, &flen))
520           {
521             /* Does strlenW(bufW) == 0 mean we use the filename? */
522             len = strlenW(bufW) + 1;
523             TRACE("found FileDescription: %s\n", debugstr_w(bufW));
524             hr = ASSOC_ReturnData(pszOut, pcchOut, bufW, len);
525             HeapFree(GetProcessHeap(), 0, verinfoW);
526             return hr;
527           }
528         }
529       }
530 get_friendly_name_fail:
531       PathRemoveExtensionW(path);
532       PathStripPathW(path);
533       TRACE("using filename: %s\n", debugstr_w(path));
534       hr = ASSOC_ReturnData(pszOut, pcchOut, path, strlenW(path) + 1);
535       HeapFree(GetProcessHeap(), 0, verinfoW);
536       return hr;
537     }
538
539     case ASSOCSTR_CONTENTTYPE:
540     {
541       static const WCHAR Content_TypeW[] = {'C','o','n','t','e','n','t',' ','T','y','p','e',0};
542       WCHAR *contentType;
543       DWORD ret;
544       DWORD size;
545
546       size = 0;
547       ret = RegGetValueW(This->hkeySource, NULL, Content_TypeW, RRF_RT_REG_SZ, NULL, NULL, &size);
548       if (ret != ERROR_SUCCESS)
549         return HRESULT_FROM_WIN32(ret);
550       contentType = HeapAlloc(GetProcessHeap(), 0, size);
551       if (contentType != NULL)
552       {
553         ret = RegGetValueW(This->hkeySource, NULL, Content_TypeW, RRF_RT_REG_SZ, NULL, contentType, &size);
554         if (ret == ERROR_SUCCESS)
555           hr = ASSOC_ReturnData(pszOut, pcchOut, contentType, strlenW(contentType) + 1);
556         else
557           hr = HRESULT_FROM_WIN32(ret);
558         HeapFree(GetProcessHeap(), 0, contentType);
559       }
560       else
561         hr = E_OUTOFMEMORY;
562       return hr;
563     }
564
565     case ASSOCSTR_DEFAULTICON:
566     {
567       static const WCHAR DefaultIconW[] = {'D','e','f','a','u','l','t','I','c','o','n',0};
568       WCHAR *pszFileType;
569       DWORD ret;
570       DWORD size;
571       HKEY hkeyFile;
572
573       hr = ASSOC_GetValue(This->hkeySource, &pszFileType);
574       if (FAILED(hr))
575         return hr;
576       ret = RegOpenKeyExW(HKEY_CLASSES_ROOT, pszFileType, 0, KEY_READ, &hkeyFile);
577       if (ret == ERROR_SUCCESS)
578       {
579         size = 0;
580         ret = RegGetValueW(hkeyFile, DefaultIconW, NULL, RRF_RT_REG_SZ, NULL, NULL, &size);
581         if (ret == ERROR_SUCCESS)
582         {
583           WCHAR *icon = HeapAlloc(GetProcessHeap(), 0, size);
584           if (icon)
585           {
586             ret = RegGetValueW(hkeyFile, DefaultIconW, NULL, RRF_RT_REG_SZ, NULL, icon, &size);
587             if (ret == ERROR_SUCCESS)
588               hr = ASSOC_ReturnData(pszOut, pcchOut, icon, strlenW(icon) + 1);
589             else
590               hr = HRESULT_FROM_WIN32(ret);
591             HeapFree(GetProcessHeap(), 0, icon);
592           }
593           else
594             hr = E_OUTOFMEMORY;
595         }
596         else
597           hr = HRESULT_FROM_WIN32(ret);
598         RegCloseKey(hkeyFile);
599       }
600       else
601         hr = HRESULT_FROM_WIN32(ret);
602       HeapFree(GetProcessHeap(), 0, pszFileType);
603       return hr;
604     }
605
606     default:
607       FIXME("assocstr %d unimplemented!\n", str);
608       return E_NOTIMPL;
609   }
610 }
611
612 /**************************************************************************
613  *  IQueryAssociations_GetKey
614  *
615  * Get a file association key from the registry.
616  *
617  * PARAMS
618  *  iface    [I] IQueryAssociations interface to query
619  *  cfFlags  [I] ASSOCF_ flags from "shlwapi.h"
620  *  assockey [I] Type of key to get (ASSOCKEY enum from "shlwapi.h")
621  *  pszExtra [I] Extra information about the key location
622  *  phkeyOut [O] Destination for the association key
623  *
624  * RETURNS
625  *  Success: S_OK. phkeyOut contains a handle to the key.
626  *  Failure: An HRESULT error code indicating the error.
627  */
628 static HRESULT WINAPI IQueryAssociations_fnGetKey(
629   IQueryAssociations *iface,
630   ASSOCF cfFlags,
631   ASSOCKEY assockey,
632   LPCWSTR pszExtra,
633   HKEY *phkeyOut)
634 {
635   IQueryAssociationsImpl *This = impl_from_IQueryAssociations(iface);
636
637   FIXME("(%p,0x%8x,0x%8x,%s,%p)-stub!\n", This, cfFlags, assockey,
638         debugstr_w(pszExtra), phkeyOut);
639   return E_NOTIMPL;
640 }
641
642 /**************************************************************************
643  *  IQueryAssociations_GetData
644  *
645  * Get the data for a file association key from the registry.
646  *
647  * PARAMS
648  *  iface     [I]   IQueryAssociations interface to query
649  *  cfFlags   [I]   ASSOCF_ flags from "shlwapi.h"
650  *  assocdata [I]   Type of data to get (ASSOCDATA enum from "shlwapi.h")
651  *  pszExtra  [I]   Extra information about the data location
652  *  pvOut     [O]   Destination for the association key
653  *  pcbOut    [I/O] Size of pvOut
654  *
655  * RETURNS
656  *  Success: S_OK. pszOut contains the data, pcbOut contains its length.
657  *  Failure: An HRESULT error code indicating the error.
658  */
659 static HRESULT WINAPI IQueryAssociations_fnGetData(
660   IQueryAssociations *iface,
661   ASSOCF cfFlags,
662   ASSOCDATA assocdata,
663   LPCWSTR pszExtra,
664   LPVOID pvOut,
665   DWORD *pcbOut)
666 {
667   IQueryAssociationsImpl *This = impl_from_IQueryAssociations(iface);
668
669   FIXME("(%p,0x%8x,0x%8x,%s,%p,%p)-stub!\n", This, cfFlags, assocdata,
670         debugstr_w(pszExtra), pvOut, pcbOut);
671   return E_NOTIMPL;
672 }
673
674 /**************************************************************************
675  *  IQueryAssociations_GetEnum
676  *
677  * Not yet implemented in native Win32.
678  *
679  * PARAMS
680  *  iface     [I] IQueryAssociations interface to query
681  *  cfFlags   [I] ASSOCF_ flags from "shlwapi.h"
682  *  assocenum [I] Type of enum to get (ASSOCENUM enum from "shlwapi.h")
683  *  pszExtra  [I] Extra information about the enum location
684  *  riid      [I] REFIID to look for
685  *  ppvOut    [O] Destination for the interface.
686  *
687  * RETURNS
688  *  Success: S_OK.
689  *  Failure: An HRESULT error code indicating the error.
690  *
691  * NOTES
692  *  Presumably this function returns an enumerator object.
693  */
694 static HRESULT WINAPI IQueryAssociations_fnGetEnum(
695   IQueryAssociations *iface,
696   ASSOCF cfFlags,
697   ASSOCENUM assocenum,
698   LPCWSTR pszExtra,
699   REFIID riid,
700   LPVOID *ppvOut)
701 {
702   IQueryAssociationsImpl *This = impl_from_IQueryAssociations(iface);
703
704   FIXME("(%p,0x%8x,0x%8x,%s,%s,%p)-stub!\n", This, cfFlags, assocenum,
705         debugstr_w(pszExtra), debugstr_guid(riid), ppvOut);
706   return E_NOTIMPL;
707 }
708
709 static const IQueryAssociationsVtbl IQueryAssociations_vtbl =
710 {
711   IQueryAssociations_fnQueryInterface,
712   IQueryAssociations_fnAddRef,
713   IQueryAssociations_fnRelease,
714   IQueryAssociations_fnInit,
715   IQueryAssociations_fnGetString,
716   IQueryAssociations_fnGetKey,
717   IQueryAssociations_fnGetData,
718   IQueryAssociations_fnGetEnum
719 };
720
721 /**************************************************************************
722  * IApplicationAssociationRegistration implementation
723  */
724 static inline IApplicationAssociationRegistrationImpl *impl_from_IApplicationAssociationRegistration(IApplicationAssociationRegistration *iface)
725 {
726   return CONTAINING_RECORD(iface, IApplicationAssociationRegistrationImpl, IApplicationAssociationRegistration_iface);
727 }
728
729 static HRESULT WINAPI ApplicationAssociationRegistration_QueryInterface(
730                         IApplicationAssociationRegistration* iface, REFIID riid, LPVOID *ppv)
731 {
732     IApplicationAssociationRegistrationImpl *This = impl_from_IApplicationAssociationRegistration(iface);
733
734     TRACE("(%p, %s, %p)\n",This, debugstr_guid(riid), ppv);
735
736     if (ppv == NULL)
737         return E_POINTER;
738
739     if (IsEqualGUID(&IID_IUnknown, riid) ||
740         IsEqualGUID(&IID_IApplicationAssociationRegistration, riid)) {
741         *ppv = &This->IApplicationAssociationRegistration_iface;
742         IUnknown_AddRef((IUnknown*)*ppv);
743         TRACE("returning IApplicationAssociationRegistration: %p\n", *ppv);
744         return S_OK;
745     }
746
747     *ppv = NULL;
748     FIXME("(%p)->(%s %p) interface not supported\n", This, debugstr_guid(riid), ppv);
749     return E_NOINTERFACE;
750 }
751
752 static ULONG WINAPI ApplicationAssociationRegistration_AddRef(IApplicationAssociationRegistration *iface)
753 {
754     IApplicationAssociationRegistrationImpl *This = impl_from_IApplicationAssociationRegistration(iface);
755     ULONG ref = InterlockedIncrement(&This->ref);
756
757     TRACE("(%p) ref=%d\n", This, ref);
758     return ref;
759 }
760
761 static ULONG WINAPI ApplicationAssociationRegistration_Release(IApplicationAssociationRegistration *iface)
762 {
763     IApplicationAssociationRegistrationImpl *This = impl_from_IApplicationAssociationRegistration(iface);
764     ULONG ref = InterlockedDecrement(&This->ref);
765
766     TRACE("(%p) ref=%d\n", This, ref);
767
768     if (!ref) {
769         SHFree(This);
770     }
771     return ref;
772 }
773
774 static HRESULT WINAPI ApplicationAssociationRegistration_QueryCurrentDefault(IApplicationAssociationRegistration* This, LPCWSTR query,
775                                                                              ASSOCIATIONTYPE type, ASSOCIATIONLEVEL level, LPWSTR *association)
776 {
777     FIXME("(%p)->(%s, %d, %d, %p)\n", This, debugstr_w(query), type, level, association);
778     return E_NOTIMPL;
779 }
780
781 static HRESULT WINAPI ApplicationAssociationRegistration_QueryAppIsDefault(IApplicationAssociationRegistration* This, LPCWSTR query,
782                                                                            ASSOCIATIONTYPE type, ASSOCIATIONLEVEL level, LPCWSTR appname, BOOL *is_default)
783 {
784     FIXME("(%p)->(%s, %d, %d, %s, %p)\n", This, debugstr_w(query), type, level, debugstr_w(appname), is_default);
785     return E_NOTIMPL;
786 }
787
788 static HRESULT WINAPI ApplicationAssociationRegistration_QueryAppIsDefaultAll(IApplicationAssociationRegistration* This, ASSOCIATIONLEVEL level,
789                                                                               LPCWSTR appname, BOOL *is_default)
790 {
791     FIXME("(%p)->(%d, %s, %p)\n", This, level, debugstr_w(appname), is_default);
792     return E_NOTIMPL;
793 }
794
795 static HRESULT WINAPI ApplicationAssociationRegistration_SetAppAsDefault(IApplicationAssociationRegistration* This, LPCWSTR appname,
796                                                                          LPCWSTR set, ASSOCIATIONTYPE set_type)
797 {
798     FIXME("(%p)->(%s, %s, %d)\n", This, debugstr_w(appname), debugstr_w(set), set_type);
799     return E_NOTIMPL;
800 }
801
802 static HRESULT WINAPI ApplicationAssociationRegistration_SetAppAsDefaultAll(IApplicationAssociationRegistration* This, LPCWSTR appname)
803 {
804     FIXME("(%p)->(%s)\n", This, debugstr_w(appname));
805     return E_NOTIMPL;
806 }
807
808
809 static HRESULT WINAPI ApplicationAssociationRegistration_ClearUserAssociations(IApplicationAssociationRegistration* This)
810 {
811     FIXME("(%p)\n", This);
812     return E_NOTIMPL;
813 }
814
815
816 static const IApplicationAssociationRegistrationVtbl IApplicationAssociationRegistration_vtbl =
817 {
818     ApplicationAssociationRegistration_QueryInterface,
819     ApplicationAssociationRegistration_AddRef,
820     ApplicationAssociationRegistration_Release,
821     ApplicationAssociationRegistration_QueryCurrentDefault,
822     ApplicationAssociationRegistration_QueryAppIsDefault,
823     ApplicationAssociationRegistration_QueryAppIsDefaultAll,
824     ApplicationAssociationRegistration_SetAppAsDefault,
825     ApplicationAssociationRegistration_SetAppAsDefaultAll,
826     ApplicationAssociationRegistration_ClearUserAssociations
827 };
828
829 /**************************************************************************
830  *  IQueryAssociations_Constructor [internal]
831  *
832  * Construct a new IQueryAssociations object.
833  */
834 HRESULT WINAPI QueryAssociations_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppOutput)
835 {
836     IQueryAssociationsImpl* this;
837     HRESULT ret;
838
839     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
840
841     if (!(this = SHAlloc(sizeof(*this)))) return E_OUTOFMEMORY;
842     this->IQueryAssociations_iface.lpVtbl = &IQueryAssociations_vtbl;
843     this->ref = 0;
844     this->hkeySource = 0;
845     this->hkeyProgID = 0;
846     if (FAILED(ret = IUnknown_QueryInterface((IUnknown *)this, riid, ppOutput))) SHFree( this );
847     TRACE("returning %p\n", *ppOutput);
848     return ret;
849 }
850
851 /**************************************************************************
852  * ApplicationAssociationRegistration_Constructor [internal]
853  *
854  * Construct a IApplicationAssociationRegistration object.
855  */
856 HRESULT WINAPI ApplicationAssociationRegistration_Constructor(IUnknown *outer, REFIID riid, LPVOID *ppv)
857 {
858     IApplicationAssociationRegistrationImpl *This;
859     HRESULT hr;
860
861     if (outer)
862         return CLASS_E_NOAGGREGATION;
863
864     if (!(This = SHAlloc(sizeof(*This))))
865         return E_OUTOFMEMORY;
866
867     This->IApplicationAssociationRegistration_iface.lpVtbl = &IApplicationAssociationRegistration_vtbl;
868     This->ref = 0;
869
870     hr = IUnknown_QueryInterface(&This->IApplicationAssociationRegistration_iface, riid, ppv);
871     if (FAILED(hr))
872         SHFree(This);
873
874     TRACE("returning 0x%x with %p\n", hr, *ppv);
875     return hr;
876 }