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