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