comctl32/trackbar: Fix tic count calculation.
[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   }
324   else
325   {
326     pszStart = pszCommand;
327     pszEnd = strchrW(pszStart, ' ');
328   }
329   if (pszEnd)
330     *pszEnd = 0;
331
332   *len = SearchPathW(NULL, pszStart, NULL, pathlen, path, NULL);
333   HeapFree(GetProcessHeap(), 0, pszCommand);
334   if (!*len)
335     return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
336   return S_OK;
337 }
338
339 static HRESULT ASSOC_ReturnData(LPWSTR out, DWORD *outlen, LPCWSTR data,
340                                 DWORD datalen)
341 {
342   assert(outlen);
343
344   if (out)
345   {
346     if (*outlen < datalen)
347     {
348       *outlen = datalen;
349       return E_POINTER;
350     }
351     *outlen = datalen;
352     lstrcpynW(out, data, datalen);
353     return S_OK;
354   }
355   else
356   {
357     *outlen = datalen;
358     return S_FALSE;
359   }
360 }
361
362 /**************************************************************************
363  *  IQueryAssociations_GetString {SHLWAPI}
364  *
365  * Get a file association string from the registry.
366  *
367  * PARAMS
368  *  iface    [I]   IQueryAssociations interface to query
369  *  cfFlags  [I]   ASSOCF_ flags from "shlwapi.h"
370  *  str      [I]   Type of string to get (ASSOCSTR enum from "shlwapi.h")
371  *  pszExtra [I]   Extra information about the string location
372  *  pszOut   [O]   Destination for the association string
373  *  pcchOut  [I/O] Length of pszOut
374  *
375  * RETURNS
376  *  Success: S_OK. pszOut contains the string, pcchOut contains its length.
377  *  Failure: An HRESULT error code indicating the error.
378  */
379 static HRESULT WINAPI IQueryAssociations_fnGetString(
380   IQueryAssociations *iface,
381   ASSOCF cfFlags,
382   ASSOCSTR str,
383   LPCWSTR pszExtra,
384   LPWSTR pszOut,
385   DWORD *pcchOut)
386 {
387   IQueryAssociationsImpl *This = (IQueryAssociationsImpl *)iface;
388   const ASSOCF cfUnimplemented = ~(0);
389   DWORD len = 0;
390   HRESULT hr;
391   WCHAR path[MAX_PATH];
392
393   TRACE("(%p,0x%8x,0x%8x,%s,%p,%p)\n", This, cfFlags, str,
394         debugstr_w(pszExtra), pszOut, pcchOut);
395
396   if (cfFlags & cfUnimplemented)
397     FIXME("%08x: unimplemented flags!\n", cfFlags & cfUnimplemented);
398
399   if (!pcchOut)
400     return E_UNEXPECTED;
401
402   switch (str)
403   {
404     case ASSOCSTR_COMMAND:
405     {
406       WCHAR *command;
407       hr = ASSOC_GetCommand(This, pszExtra, &command);
408       if (SUCCEEDED(hr))
409       {
410         hr = ASSOC_ReturnData(pszOut, pcchOut, command, strlenW(command) + 1);
411         HeapFree(GetProcessHeap(), 0, command);
412       }
413       return hr;
414     }
415
416     case ASSOCSTR_EXECUTABLE:
417     {
418       hr = ASSOC_GetExecutable(This, pszExtra, path, MAX_PATH, &len);
419       if (FAILED(hr))
420         return hr;
421       len++;
422       return ASSOC_ReturnData(pszOut, pcchOut, path, len);
423     }
424
425     case ASSOCSTR_FRIENDLYDOCNAME:
426     {
427       WCHAR *pszFileType;
428       DWORD ret;
429       DWORD size;
430
431       hr = ASSOC_GetValue(This->hkeySource, &pszFileType);
432       if (FAILED(hr))
433         return hr;
434       size = 0;
435       ret = RegGetValueW(HKEY_CLASSES_ROOT, pszFileType, NULL, RRF_RT_REG_SZ, NULL, NULL, &size);
436       if (ret == ERROR_SUCCESS)
437       {
438         WCHAR *docName = HeapAlloc(GetProcessHeap(), 0, size);
439         if (docName)
440         {
441           ret = RegGetValueW(HKEY_CLASSES_ROOT, pszFileType, NULL, RRF_RT_REG_SZ, NULL, docName, &size);
442           if (ret == ERROR_SUCCESS)
443             hr = ASSOC_ReturnData(pszOut, pcchOut, docName, strlenW(docName) + 1);
444           else
445             hr = HRESULT_FROM_WIN32(ret);
446           HeapFree(GetProcessHeap(), 0, docName);
447         }
448         else
449           hr = E_OUTOFMEMORY;
450       }
451       else
452         hr = HRESULT_FROM_WIN32(ret);
453       HeapFree(GetProcessHeap(), 0, pszFileType);
454       return hr;
455     }
456
457     case ASSOCSTR_FRIENDLYAPPNAME:
458     {
459       PVOID verinfoW = NULL;
460       DWORD size, retval = 0;
461       UINT flen;
462       WCHAR *bufW;
463       static const WCHAR translationW[] = {
464         '\\','V','a','r','F','i','l','e','I','n','f','o',
465         '\\','T','r','a','n','s','l','a','t','i','o','n',0
466       };
467       static const WCHAR fileDescFmtW[] = {
468         '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
469         '\\','%','0','4','x','%','0','4','x',
470         '\\','F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0
471       };
472       WCHAR fileDescW[41];
473
474       hr = ASSOC_GetExecutable(This, pszExtra, path, MAX_PATH, &len);
475       if (FAILED(hr))
476         return hr;
477
478       retval = GetFileVersionInfoSizeW(path, &size);
479       if (!retval)
480         goto get_friendly_name_fail;
481       verinfoW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, retval);
482       if (!verinfoW)
483         return E_OUTOFMEMORY;
484       if (!GetFileVersionInfoW(path, 0, retval, verinfoW))
485         goto get_friendly_name_fail;
486       if (VerQueryValueW(verinfoW, translationW, (LPVOID *)&bufW, &flen))
487       {
488         UINT i;
489         DWORD *langCodeDesc = (DWORD *)bufW;
490         for (i = 0; i < flen / sizeof(DWORD); i++)
491         {
492           sprintfW(fileDescW, fileDescFmtW, LOWORD(langCodeDesc[i]),
493                    HIWORD(langCodeDesc[i]));
494           if (VerQueryValueW(verinfoW, fileDescW, (LPVOID *)&bufW, &flen))
495           {
496             /* Does strlenW(bufW) == 0 mean we use the filename? */
497             len = strlenW(bufW) + 1;
498             TRACE("found FileDescription: %s\n", debugstr_w(bufW));
499             return ASSOC_ReturnData(pszOut, pcchOut, bufW, len);
500           }
501         }
502       }
503 get_friendly_name_fail:
504       PathRemoveExtensionW(path);
505       PathStripPathW(path);
506       TRACE("using filename: %s\n", debugstr_w(path));
507       return ASSOC_ReturnData(pszOut, pcchOut, path, strlenW(path) + 1);
508     }
509
510     case ASSOCSTR_CONTENTTYPE:
511     {
512       static const WCHAR Content_TypeW[] = {'C','o','n','t','e','n','t',' ','T','y','p','e',0};
513       WCHAR *contentType;
514       DWORD ret;
515       DWORD size;
516
517       size = 0;
518       ret = RegGetValueW(This->hkeySource, NULL, Content_TypeW, RRF_RT_REG_SZ, NULL, NULL, &size);
519       if (ret != ERROR_SUCCESS)
520         return HRESULT_FROM_WIN32(ret);
521       contentType = HeapAlloc(GetProcessHeap(), 0, size);
522       if (contentType != NULL)
523       {
524         ret = RegGetValueW(This->hkeySource, NULL, Content_TypeW, RRF_RT_REG_SZ, NULL, contentType, &size);
525         if (ret == ERROR_SUCCESS)
526           hr = ASSOC_ReturnData(pszOut, pcchOut, contentType, strlenW(contentType) + 1);
527         else
528           hr = HRESULT_FROM_WIN32(ret);
529         HeapFree(GetProcessHeap(), 0, contentType);
530       }
531       else
532         hr = E_OUTOFMEMORY;
533       return hr;
534     }
535
536     case ASSOCSTR_DEFAULTICON:
537     {
538       static const WCHAR DefaultIconW[] = {'D','e','f','a','u','l','t','I','c','o','n',0};
539       WCHAR *pszFileType;
540       DWORD ret;
541       DWORD size;
542       HKEY hkeyFile;
543
544       hr = ASSOC_GetValue(This->hkeySource, &pszFileType);
545       if (FAILED(hr))
546         return hr;
547       ret = RegOpenKeyExW(HKEY_CLASSES_ROOT, pszFileType, 0, KEY_READ, &hkeyFile);
548       if (ret == ERROR_SUCCESS)
549       {
550         size = 0;
551         ret = RegGetValueW(hkeyFile, DefaultIconW, NULL, RRF_RT_REG_SZ, NULL, NULL, &size);
552         if (ret == ERROR_SUCCESS)
553         {
554           WCHAR *icon = HeapAlloc(GetProcessHeap(), 0, size);
555           if (icon)
556           {
557             ret = RegGetValueW(hkeyFile, DefaultIconW, NULL, RRF_RT_REG_SZ, NULL, icon, &size);
558             if (ret == ERROR_SUCCESS)
559               hr = ASSOC_ReturnData(pszOut, pcchOut, icon, strlenW(icon) + 1);
560             else
561               hr = HRESULT_FROM_WIN32(ret);
562             HeapFree(GetProcessHeap(), 0, icon);
563           }
564           else
565             hr = E_OUTOFMEMORY;
566         }
567         else
568           hr = HRESULT_FROM_WIN32(ret);
569         RegCloseKey(hkeyFile);
570       }
571       else
572         hr = HRESULT_FROM_WIN32(ret);
573       HeapFree(GetProcessHeap(), 0, pszFileType);
574       return hr;
575     }
576
577     default:
578       FIXME("assocstr %d unimplemented!\n", str);
579       return E_NOTIMPL;
580   }
581 }
582
583 /**************************************************************************
584  *  IQueryAssociations_GetKey {SHLWAPI}
585  *
586  * Get a file association key from the registry.
587  *
588  * PARAMS
589  *  iface    [I] IQueryAssociations interface to query
590  *  cfFlags  [I] ASSOCF_ flags from "shlwapi.h"
591  *  assockey [I] Type of key to get (ASSOCKEY enum from "shlwapi.h")
592  *  pszExtra [I] Extra information about the key location
593  *  phkeyOut [O] Destination for the association key
594  *
595  * RETURNS
596  *  Success: S_OK. phkeyOut contains a handle to the key.
597  *  Failure: An HRESULT error code indicating the error.
598  */
599 static HRESULT WINAPI IQueryAssociations_fnGetKey(
600   IQueryAssociations *iface,
601   ASSOCF cfFlags,
602   ASSOCKEY assockey,
603   LPCWSTR pszExtra,
604   HKEY *phkeyOut)
605 {
606   IQueryAssociationsImpl *This = (IQueryAssociationsImpl *)iface;
607
608   FIXME("(%p,0x%8x,0x%8x,%s,%p)-stub!\n", This, cfFlags, assockey,
609         debugstr_w(pszExtra), phkeyOut);
610   return E_NOTIMPL;
611 }
612
613 /**************************************************************************
614  *  IQueryAssociations_GetData {SHLWAPI}
615  *
616  * Get the data for a file association key from the registry.
617  *
618  * PARAMS
619  *  iface     [I]   IQueryAssociations interface to query
620  *  cfFlags   [I]   ASSOCF_ flags from "shlwapi.h"
621  *  assocdata [I]   Type of data to get (ASSOCDATA enum from "shlwapi.h")
622  *  pszExtra  [I]   Extra information about the data location
623  *  pvOut     [O]   Destination for the association key
624  *  pcbOut    [I/O] Size of pvOut
625  *
626  * RETURNS
627  *  Success: S_OK. pszOut contains the data, pcbOut contains its length.
628  *  Failure: An HRESULT error code indicating the error.
629  */
630 static HRESULT WINAPI IQueryAssociations_fnGetData(
631   IQueryAssociations *iface,
632   ASSOCF cfFlags,
633   ASSOCDATA assocdata,
634   LPCWSTR pszExtra,
635   LPVOID pvOut,
636   DWORD *pcbOut)
637 {
638   IQueryAssociationsImpl *This = (IQueryAssociationsImpl *)iface;
639
640   FIXME("(%p,0x%8x,0x%8x,%s,%p,%p)-stub!\n", This, cfFlags, assocdata,
641         debugstr_w(pszExtra), pvOut, pcbOut);
642   return E_NOTIMPL;
643 }
644
645 /**************************************************************************
646  *  IQueryAssociations_GetEnum {SHLWAPI}
647  *
648  * Not yet implemented in native Win32.
649  *
650  * PARAMS
651  *  iface     [I] IQueryAssociations interface to query
652  *  cfFlags   [I] ASSOCF_ flags from "shlwapi.h"
653  *  assocenum [I] Type of enum to get (ASSOCENUM enum from "shlwapi.h")
654  *  pszExtra  [I] Extra information about the enum location
655  *  riid      [I] REFIID to look for
656  *  ppvOut    [O] Destination for the interface.
657  *
658  * RETURNS
659  *  Success: S_OK.
660  *  Failure: An HRESULT error code indicating the error.
661  *
662  * NOTES
663  *  Presumably this function returns an enumerator object.
664  */
665 static HRESULT WINAPI IQueryAssociations_fnGetEnum(
666   IQueryAssociations *iface,
667   ASSOCF cfFlags,
668   ASSOCENUM assocenum,
669   LPCWSTR pszExtra,
670   REFIID riid,
671   LPVOID *ppvOut)
672 {
673   IQueryAssociationsImpl *This = (IQueryAssociationsImpl *)iface;
674
675   FIXME("(%p,0x%8x,0x%8x,%s,%s,%p)-stub!\n", This, cfFlags, assocenum,
676         debugstr_w(pszExtra), debugstr_guid(riid), ppvOut);
677   return E_NOTIMPL;
678 }
679
680 static const IQueryAssociationsVtbl IQueryAssociations_vtbl =
681 {
682   IQueryAssociations_fnQueryInterface,
683   IQueryAssociations_fnAddRef,
684   IQueryAssociations_fnRelease,
685   IQueryAssociations_fnInit,
686   IQueryAssociations_fnGetString,
687   IQueryAssociations_fnGetKey,
688   IQueryAssociations_fnGetData,
689   IQueryAssociations_fnGetEnum
690 };
691
692 /**************************************************************************
693  *  IQueryAssociations_Constructor [internal]
694  *
695  * Construct a new IQueryAssociations object.
696  */
697 HRESULT WINAPI QueryAssociations_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppOutput)
698 {
699     IQueryAssociationsImpl* this;
700     HRESULT ret;
701
702     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
703
704     if (!(this = SHAlloc(sizeof(*this)))) return E_OUTOFMEMORY;
705     this->lpVtbl = &IQueryAssociations_vtbl;
706     this->ref = 0;
707     this->hkeySource = 0;
708     this->hkeyProgID = 0;
709     if (FAILED(ret = IUnknown_QueryInterface((IUnknown *)this, riid, ppOutput))) SHFree( this );
710     TRACE("returning %p\n", *ppOutput);
711     return ret;
712 }