xref: /reactos/dll/win32/shlwapi/ordinal.c (revision 565bf9e3)
1 /*
2  * SHLWAPI ordinal functions
3  *
4  * Copyright 1997 Marcus Meissner
5  *           1998 Jürgen Schmied
6  *           2001-2003 Jon Griffiths
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 #include "config.h"
24 #include "wine/port.h"
25 
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
29 
30 #define COBJMACROS
31 
32 #include "windef.h"
33 #include "winbase.h"
34 #include "winnls.h"
35 #include "winreg.h"
36 #include "wingdi.h"
37 #include "winuser.h"
38 #include "winver.h"
39 #include "winnetwk.h"
40 #include "mmsystem.h"
41 #include "objbase.h"
42 #include "exdisp.h"
43 #include "shdeprecated.h"
44 #include "shlobj.h"
45 #include "shlwapi.h"
46 #include "shellapi.h"
47 #include "commdlg.h"
48 #include "mlang.h"
49 #include "mshtmhst.h"
50 #ifdef __REACTOS__
51     #include <shlwapi_undoc.h>
52 #endif
53 #include "wine/unicode.h"
54 #include "wine/debug.h"
55 
56 
57 WINE_DEFAULT_DEBUG_CHANNEL(shell);
58 
59 /* DLL handles for late bound calls */
60 extern HINSTANCE shlwapi_hInstance;
61 extern DWORD SHLWAPI_ThreadRef_index;
62 
63 HRESULT WINAPI IUnknown_QueryService(IUnknown*,REFGUID,REFIID,LPVOID*);
64 HRESULT WINAPI SHInvokeCommand(HWND,IShellFolder*,LPCITEMIDLIST,DWORD);
65 BOOL    WINAPI SHAboutInfoW(LPWSTR,DWORD);
66 
67 /*
68  NOTES: Most functions exported by ordinal seem to be superfluous.
69  The reason for these functions to be there is to provide a wrapper
70  for unicode functions to provide these functions on systems without
71  unicode functions eg. win95/win98. Since we have such functions we just
72  call these. If running Wine with native DLLs, some late bound calls may
73  fail. However, it is better to implement the functions in the forward DLL
74  and recommend the builtin rather than reimplementing the calls here!
75 */
76 
77 /*************************************************************************
78  * @   [SHLWAPI.11]
79  *
80  * Copy a sharable memory handle from one process to another.
81  *
82  * PARAMS
83  * hShared     [I] Shared memory handle to duplicate
84  * dwSrcProcId [I] ID of the process owning hShared
85  * dwDstProcId [I] ID of the process wanting the duplicated handle
86  * dwAccess    [I] Desired DuplicateHandle() access
87  * dwOptions   [I] Desired DuplicateHandle() options
88  *
89  * RETURNS
90  * Success: A handle suitable for use by the dwDstProcId process.
91  * Failure: A NULL handle.
92  *
93  */
94 HANDLE WINAPI SHMapHandle(HANDLE hShared, DWORD dwSrcProcId, DWORD dwDstProcId,
95                           DWORD dwAccess, DWORD dwOptions)
96 {
97   HANDLE hDst, hSrc;
98   DWORD dwMyProcId = GetCurrentProcessId();
99   HANDLE hRet = NULL;
100 
101   TRACE("(%p,%d,%d,%08x,%08x)\n", hShared, dwDstProcId, dwSrcProcId,
102         dwAccess, dwOptions);
103 
104   if (!hShared)
105   {
106     TRACE("Returning handle NULL\n");
107     return NULL;
108   }
109 
110   /* Get dest process handle */
111   if (dwDstProcId == dwMyProcId)
112     hDst = GetCurrentProcess();
113   else
114     hDst = OpenProcess(PROCESS_DUP_HANDLE, 0, dwDstProcId);
115 
116   if (hDst)
117   {
118     /* Get src process handle */
119     if (dwSrcProcId == dwMyProcId)
120       hSrc = GetCurrentProcess();
121     else
122       hSrc = OpenProcess(PROCESS_DUP_HANDLE, 0, dwSrcProcId);
123 
124     if (hSrc)
125     {
126       /* Make handle available to dest process */
127       if (!DuplicateHandle(hSrc, hShared, hDst, &hRet,
128                            dwAccess, 0, dwOptions | DUPLICATE_SAME_ACCESS))
129         hRet = NULL;
130 
131       if (dwSrcProcId != dwMyProcId)
132         CloseHandle(hSrc);
133     }
134 
135     if (dwDstProcId != dwMyProcId)
136       CloseHandle(hDst);
137   }
138 
139   TRACE("Returning handle %p\n", hRet);
140   return hRet;
141 }
142 
143 /*************************************************************************
144  * @  [SHLWAPI.7]
145  *
146  * Create a block of sharable memory and initialise it with data.
147  *
148  * PARAMS
149  * lpvData  [I] Pointer to data to write
150  * dwSize   [I] Size of data
151  * dwProcId [I] ID of process owning data
152  *
153  * RETURNS
154  * Success: A shared memory handle
155  * Failure: NULL
156  *
157  * NOTES
158  * Ordinals 7-11 provide a set of calls to create shared memory between a
159  * group of processes. The shared memory is treated opaquely in that its size
160  * is not exposed to clients who map it. This is accomplished by storing
161  * the size of the map as the first DWORD of mapped data, and then offsetting
162  * the view pointer returned by this size.
163  *
164  */
165 HANDLE WINAPI SHAllocShared(LPCVOID lpvData, DWORD dwSize, DWORD dwProcId)
166 {
167   HANDLE hMap;
168   LPVOID pMapped;
169   HANDLE hRet = NULL;
170 
171   TRACE("(%p,%d,%d)\n", lpvData, dwSize, dwProcId);
172 
173   /* Create file mapping of the correct length */
174   hMap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, FILE_MAP_READ, 0,
175                             dwSize + sizeof(dwSize), NULL);
176   if (!hMap)
177     return hRet;
178 
179   /* Get a view in our process address space */
180   pMapped = MapViewOfFile(hMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
181 
182   if (pMapped)
183   {
184     /* Write size of data, followed by the data, to the view */
185     *((DWORD*)pMapped) = dwSize;
186     if (lpvData)
187       memcpy((char *) pMapped + sizeof(dwSize), lpvData, dwSize);
188 
189     /* Release view. All further views mapped will be opaque */
190     UnmapViewOfFile(pMapped);
191     hRet = SHMapHandle(hMap, GetCurrentProcessId(), dwProcId,
192                        FILE_MAP_ALL_ACCESS, DUPLICATE_SAME_ACCESS);
193   }
194 
195   CloseHandle(hMap);
196   return hRet;
197 }
198 
199 #ifdef __REACTOS__
200 /*************************************************************************
201  * @ [SHLWAPI.510]
202  *
203  * Get a pointer to a block of shared memory from a shared memory handle,
204  * with specified access rights.
205  *
206  * PARAMS
207  * hShared  [I] Shared memory handle
208  * dwProcId [I] ID of process owning hShared
209  * bWriteAccess [I] TRUE to get a writable block,
210  *                  FALSE to get a read-only block
211  *
212  * RETURNS
213  * Success: A pointer to the shared memory
214  * Failure: NULL
215  */
216 LPVOID WINAPI
217 SHLockSharedEx(HANDLE hShared, DWORD dwProcId, BOOL bWriteAccess)
218 {
219   HANDLE hDup;
220   LPVOID pMapped;
221   DWORD dwAccess;
222 
223   TRACE("(%p %d %d)\n", hShared, dwProcId, bWriteAccess);
224 
225   /* Get handle to shared memory for current process */
226   hDup = SHMapHandle(hShared, dwProcId, GetCurrentProcessId(), FILE_MAP_ALL_ACCESS, 0);
227   if (hDup == NULL)
228     return NULL;
229 
230   /* Get View */
231   dwAccess = (FILE_MAP_READ | (bWriteAccess ? FILE_MAP_WRITE : 0));
232   pMapped = MapViewOfFile(hDup, dwAccess, 0, 0, 0);
233   CloseHandle(hDup);
234 
235   if (pMapped)
236     return (char *) pMapped + sizeof(DWORD); /* Hide size */
237   return NULL;
238 }
239 
240 #endif
241 /*************************************************************************
242  * @ [SHLWAPI.8]
243  *
244  * Get a pointer to a block of shared memory from a shared memory handle.
245  *
246  * PARAMS
247  * hShared  [I] Shared memory handle
248  * dwProcId [I] ID of process owning hShared
249  *
250  * RETURNS
251  * Success: A pointer to the shared memory
252  * Failure: NULL
253  *
254  */
255 PVOID WINAPI SHLockShared(HANDLE hShared, DWORD dwProcId)
256 {
257 #ifdef __REACTOS__
258     return SHLockSharedEx(hShared, dwProcId, TRUE);
259 #else
260   HANDLE hDup;
261   LPVOID pMapped;
262 
263   TRACE("(%p %d)\n", hShared, dwProcId);
264 
265   /* Get handle to shared memory for current process */
266   hDup = SHMapHandle(hShared, dwProcId, GetCurrentProcessId(), FILE_MAP_ALL_ACCESS, 0);
267 
268   /* Get View */
269   pMapped = MapViewOfFile(hDup, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
270   CloseHandle(hDup);
271 
272   if (pMapped)
273     return (char *) pMapped + sizeof(DWORD); /* Hide size */
274   return NULL;
275 #endif
276 }
277 
278 /*************************************************************************
279  * @ [SHLWAPI.9]
280  *
281  * Release a pointer to a block of shared memory.
282  *
283  * PARAMS
284  * lpView [I] Shared memory pointer
285  *
286  * RETURNS
287  * Success: TRUE
288  * Failure: FALSE
289  *
290  */
291 BOOL WINAPI SHUnlockShared(LPVOID lpView)
292 {
293   TRACE("(%p)\n", lpView);
294   return UnmapViewOfFile((char *) lpView - sizeof(DWORD)); /* Include size */
295 }
296 
297 /*************************************************************************
298  * @ [SHLWAPI.10]
299  *
300  * Destroy a block of sharable memory.
301  *
302  * PARAMS
303  * hShared  [I] Shared memory handle
304  * dwProcId [I] ID of process owning hShared
305  *
306  * RETURNS
307  * Success: TRUE
308  * Failure: FALSE
309  *
310  */
311 BOOL WINAPI SHFreeShared(HANDLE hShared, DWORD dwProcId)
312 {
313   HANDLE hClose;
314 
315   TRACE("(%p %d)\n", hShared, dwProcId);
316 
317   if (!hShared)
318     return TRUE;
319 
320   /* Get a copy of the handle for our process, closing the source handle */
321   hClose = SHMapHandle(hShared, dwProcId, GetCurrentProcessId(),
322                        FILE_MAP_ALL_ACCESS,DUPLICATE_CLOSE_SOURCE);
323   /* Close local copy */
324   return CloseHandle(hClose);
325 }
326 
327 /*************************************************************************
328  *      @	[SHLWAPI.13]
329  *
330  * Create and register a clipboard enumerator for a web browser.
331  *
332  * PARAMS
333  *  lpBC      [I] Binding context
334  *  lpUnknown [I] An object exposing the IWebBrowserApp interface
335  *
336  * RETURNS
337  *  Success: S_OK.
338  *  Failure: An HRESULT error code.
339  *
340  * NOTES
341  *  The enumerator is stored as a property of the web browser. If it does not
342  *  yet exist, it is created and set before being registered.
343  */
344 HRESULT WINAPI RegisterDefaultAcceptHeaders(LPBC lpBC, IUnknown *lpUnknown)
345 {
346   static const WCHAR szProperty[] = { '{','D','0','F','C','A','4','2','0',
347       '-','D','3','F','5','-','1','1','C','F', '-','B','2','1','1','-','0',
348       '0','A','A','0','0','4','A','E','8','3','7','}','\0' };
349   BSTR property;
350   IEnumFORMATETC* pIEnumFormatEtc = NULL;
351   VARIANTARG var;
352   HRESULT hr;
353   IWebBrowserApp* pBrowser;
354 
355   TRACE("(%p, %p)\n", lpBC, lpUnknown);
356 
357   hr = IUnknown_QueryService(lpUnknown, &IID_IWebBrowserApp, &IID_IWebBrowserApp, (void**)&pBrowser);
358   if (FAILED(hr))
359     return hr;
360 
361   V_VT(&var) = VT_EMPTY;
362 
363   /* The property we get is the browsers clipboard enumerator */
364   property = SysAllocString(szProperty);
365   hr = IWebBrowserApp_GetProperty(pBrowser, property, &var);
366   SysFreeString(property);
367   if (FAILED(hr)) goto exit;
368 
369   if (V_VT(&var) == VT_EMPTY)
370   {
371     /* Iterate through accepted documents and RegisterClipBoardFormatA() them */
372     char szKeyBuff[128], szValueBuff[128];
373     DWORD dwKeySize, dwValueSize, dwRet = 0, dwCount = 0, dwNumValues, dwType;
374     FORMATETC* formatList, *format;
375     HKEY hDocs;
376 
377     TRACE("Registering formats and creating IEnumFORMATETC instance\n");
378 
379     if (!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\Current"
380                      "Version\\Internet Settings\\Accepted Documents", &hDocs))
381     {
382       hr = E_FAIL;
383       goto exit;
384     }
385 
386     /* Get count of values in key */
387     while (!dwRet)
388     {
389       dwKeySize = sizeof(szKeyBuff);
390       dwRet = RegEnumValueA(hDocs,dwCount,szKeyBuff,&dwKeySize,0,&dwType,0,0);
391       dwCount++;
392     }
393 
394     dwNumValues = dwCount;
395 
396     /* Note: dwCount = number of items + 1; The extra item is the end node */
397     format = formatList = HeapAlloc(GetProcessHeap(), 0, dwCount * sizeof(FORMATETC));
398     if (!formatList)
399     {
400       RegCloseKey(hDocs);
401       hr = E_OUTOFMEMORY;
402       goto exit;
403     }
404 
405     if (dwNumValues > 1)
406     {
407       dwRet = 0;
408       dwCount = 0;
409 
410       dwNumValues--;
411 
412       /* Register clipboard formats for the values and populate format list */
413       while(!dwRet && dwCount < dwNumValues)
414       {
415         dwKeySize = sizeof(szKeyBuff);
416         dwValueSize = sizeof(szValueBuff);
417         dwRet = RegEnumValueA(hDocs, dwCount, szKeyBuff, &dwKeySize, 0, &dwType,
418                               (PBYTE)szValueBuff, &dwValueSize);
419         if (!dwRet)
420         {
421           HeapFree(GetProcessHeap(), 0, formatList);
422           RegCloseKey(hDocs);
423           hr = E_FAIL;
424           goto exit;
425         }
426 
427         format->cfFormat = RegisterClipboardFormatA(szValueBuff);
428         format->ptd = NULL;
429         format->dwAspect = 1;
430         format->lindex = 4;
431         format->tymed = -1;
432 
433         format++;
434         dwCount++;
435       }
436     }
437 
438     RegCloseKey(hDocs);
439 
440     /* Terminate the (maybe empty) list, last entry has a cfFormat of 0 */
441     format->cfFormat = 0;
442     format->ptd = NULL;
443     format->dwAspect = 1;
444     format->lindex = 4;
445     format->tymed = -1;
446 
447     /* Create a clipboard enumerator */
448     hr = CreateFormatEnumerator(dwNumValues, formatList, &pIEnumFormatEtc);
449     HeapFree(GetProcessHeap(), 0, formatList);
450     if (FAILED(hr)) goto exit;
451 
452     /* Set our enumerator as the browsers property */
453     V_VT(&var) = VT_UNKNOWN;
454     V_UNKNOWN(&var) = (IUnknown*)pIEnumFormatEtc;
455 
456     property = SysAllocString(szProperty);
457     hr = IWebBrowserApp_PutProperty(pBrowser, property, var);
458     SysFreeString(property);
459     if (FAILED(hr))
460     {
461        IEnumFORMATETC_Release(pIEnumFormatEtc);
462        goto exit;
463     }
464   }
465 
466   if (V_VT(&var) == VT_UNKNOWN)
467   {
468     /* Our variant is holding the clipboard enumerator */
469     IUnknown* pIUnknown = V_UNKNOWN(&var);
470     IEnumFORMATETC* pClone = NULL;
471 
472     TRACE("Retrieved IEnumFORMATETC property\n");
473 
474     /* Get an IEnumFormatEtc interface from the variants value */
475     pIEnumFormatEtc = NULL;
476     hr = IUnknown_QueryInterface(pIUnknown, &IID_IEnumFORMATETC, (void**)&pIEnumFormatEtc);
477     if (hr == S_OK && pIEnumFormatEtc)
478     {
479       /* Clone and register the enumerator */
480       hr = IEnumFORMATETC_Clone(pIEnumFormatEtc, &pClone);
481       if (hr == S_OK && pClone)
482       {
483         RegisterFormatEnumerator(lpBC, pClone, 0);
484 
485         IEnumFORMATETC_Release(pClone);
486       }
487 
488       IUnknown_Release(pIUnknown);
489     }
490     IUnknown_Release(V_UNKNOWN(&var));
491   }
492 
493 exit:
494   IWebBrowserApp_Release(pBrowser);
495   return hr;
496 }
497 
498 /*************************************************************************
499  *      @	[SHLWAPI.15]
500  *
501  * Get Explorers "AcceptLanguage" setting.
502  *
503  * PARAMS
504  *  langbuf [O] Destination for language string
505  *  buflen  [I] Length of langbuf in characters
506  *          [0] Success: used length of langbuf
507  *
508  * RETURNS
509  *  Success: S_OK.   langbuf is set to the language string found.
510  *  Failure: E_FAIL, If any arguments are invalid, error occurred, or Explorer
511  *           does not contain the setting.
512  *           E_NOT_SUFFICIENT_BUFFER, If the buffer is not big enough
513  */
514 HRESULT WINAPI GetAcceptLanguagesW( LPWSTR langbuf, LPDWORD buflen)
515 {
516     static const WCHAR szkeyW[] = {
517 	'S','o','f','t','w','a','r','e','\\',
518 	'M','i','c','r','o','s','o','f','t','\\',
519 	'I','n','t','e','r','n','e','t',' ','E','x','p','l','o','r','e','r','\\',
520 	'I','n','t','e','r','n','a','t','i','o','n','a','l',0};
521     static const WCHAR valueW[] = {
522 	'A','c','c','e','p','t','L','a','n','g','u','a','g','e',0};
523     DWORD mystrlen, mytype;
524     DWORD len;
525     HKEY mykey;
526     LCID mylcid;
527     WCHAR *mystr;
528     LONG lres;
529 
530     TRACE("(%p, %p) *%p: %d\n", langbuf, buflen, buflen, buflen ? *buflen : -1);
531 
532     if(!langbuf || !buflen || !*buflen)
533 	return E_FAIL;
534 
535     mystrlen = (*buflen > 20) ? *buflen : 20 ;
536     len = mystrlen * sizeof(WCHAR);
537     mystr = HeapAlloc(GetProcessHeap(), 0, len);
538     mystr[0] = 0;
539     RegOpenKeyW(HKEY_CURRENT_USER, szkeyW, &mykey);
540     lres = RegQueryValueExW(mykey, valueW, 0, &mytype, (PBYTE)mystr, &len);
541     RegCloseKey(mykey);
542     len = lstrlenW(mystr);
543 
544     if (!lres && (*buflen > len)) {
545         lstrcpyW(langbuf, mystr);
546         *buflen = len;
547         HeapFree(GetProcessHeap(), 0, mystr);
548         return S_OK;
549     }
550 
551     /* Did not find a value in the registry or the user buffer is too small */
552     mylcid = GetUserDefaultLCID();
553     LcidToRfc1766W(mylcid, mystr, mystrlen);
554     len = lstrlenW(mystr);
555 
556     memcpy( langbuf, mystr, min(*buflen, len+1)*sizeof(WCHAR) );
557     HeapFree(GetProcessHeap(), 0, mystr);
558 
559     if (*buflen > len) {
560         *buflen = len;
561         return S_OK;
562     }
563 
564     *buflen = 0;
565     return E_NOT_SUFFICIENT_BUFFER;
566 }
567 
568 /*************************************************************************
569  *      @	[SHLWAPI.14]
570  *
571  * Ascii version of GetAcceptLanguagesW.
572  */
573 HRESULT WINAPI GetAcceptLanguagesA( LPSTR langbuf, LPDWORD buflen)
574 {
575     WCHAR *langbufW;
576     DWORD buflenW, convlen;
577     HRESULT retval;
578 
579     TRACE("(%p, %p) *%p: %d\n", langbuf, buflen, buflen, buflen ? *buflen : -1);
580 
581     if(!langbuf || !buflen || !*buflen) return E_FAIL;
582 
583     buflenW = *buflen;
584     langbufW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * buflenW);
585     retval = GetAcceptLanguagesW(langbufW, &buflenW);
586 
587     if (retval == S_OK)
588     {
589         convlen = WideCharToMultiByte(CP_ACP, 0, langbufW, -1, langbuf, *buflen, NULL, NULL);
590         convlen--;  /* do not count the terminating 0 */
591     }
592     else  /* copy partial string anyway */
593     {
594         convlen = WideCharToMultiByte(CP_ACP, 0, langbufW, *buflen, langbuf, *buflen, NULL, NULL);
595         if (convlen < *buflen)
596         {
597             langbuf[convlen] = 0;
598             convlen--;  /* do not count the terminating 0 */
599         }
600         else
601         {
602             convlen = *buflen;
603         }
604     }
605     *buflen = buflenW ? convlen : 0;
606 
607     HeapFree(GetProcessHeap(), 0, langbufW);
608     return retval;
609 }
610 
611 /*************************************************************************
612  *      @	[SHLWAPI.23]
613  *
614  * Convert a GUID to a string.
615  *
616  * PARAMS
617  *  guid     [I] GUID to convert
618  *  lpszDest [O] Destination for string
619  *  cchMax   [I] Length of output buffer
620  *
621  * RETURNS
622  *  The length of the string created.
623  */
624 INT WINAPI SHStringFromGUIDA(REFGUID guid, LPSTR lpszDest, INT cchMax)
625 {
626   char xguid[40];
627   INT iLen;
628 
629   TRACE("(%s,%p,%d)\n", debugstr_guid(guid), lpszDest, cchMax);
630 
631   sprintf(xguid, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
632           guid->Data1, guid->Data2, guid->Data3,
633           guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
634           guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
635 
636   iLen = strlen(xguid) + 1;
637 
638   if (iLen > cchMax)
639     return 0;
640   memcpy(lpszDest, xguid, iLen);
641   return iLen;
642 }
643 
644 /*************************************************************************
645  *      @	[SHLWAPI.24]
646  *
647  * Convert a GUID to a string.
648  *
649  * PARAMS
650  *  guid [I] GUID to convert
651  *  str  [O] Destination for string
652  *  cmax [I] Length of output buffer
653  *
654  * RETURNS
655  *  The length of the string created.
656  */
657 INT WINAPI SHStringFromGUIDW(REFGUID guid, LPWSTR lpszDest, INT cchMax)
658 {
659   WCHAR xguid[40];
660   INT iLen;
661   static const WCHAR wszFormat[] = {'{','%','0','8','l','X','-','%','0','4','X','-','%','0','4','X','-',
662       '%','0','2','X','%','0','2','X','-','%','0','2','X','%','0','2','X','%','0','2','X','%','0','2',
663       'X','%','0','2','X','%','0','2','X','}',0};
664 
665   TRACE("(%s,%p,%d)\n", debugstr_guid(guid), lpszDest, cchMax);
666 
667   sprintfW(xguid, wszFormat, guid->Data1, guid->Data2, guid->Data3,
668           guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
669           guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
670 
671   iLen = strlenW(xguid) + 1;
672 
673   if (iLen > cchMax)
674     return 0;
675   memcpy(lpszDest, xguid, iLen*sizeof(WCHAR));
676   return iLen;
677 }
678 
679 /*************************************************************************
680  *      @	[SHLWAPI.30]
681  *
682  * Determine if a Unicode character is a blank.
683  *
684  * PARAMS
685  *  wc [I] Character to check.
686  *
687  * RETURNS
688  *  TRUE, if wc is a blank,
689  *  FALSE otherwise.
690  *
691  */
692 BOOL WINAPI IsCharBlankW(WCHAR wc)
693 {
694     WORD CharType;
695 
696     return GetStringTypeW(CT_CTYPE1, &wc, 1, &CharType) && (CharType & C1_BLANK);
697 }
698 
699 /*************************************************************************
700  *      @	[SHLWAPI.31]
701  *
702  * Determine if a Unicode character is punctuation.
703  *
704  * PARAMS
705  *  wc [I] Character to check.
706  *
707  * RETURNS
708  *  TRUE, if wc is punctuation,
709  *  FALSE otherwise.
710  */
711 BOOL WINAPI IsCharPunctW(WCHAR wc)
712 {
713     WORD CharType;
714 
715     return GetStringTypeW(CT_CTYPE1, &wc, 1, &CharType) && (CharType & C1_PUNCT);
716 }
717 
718 /*************************************************************************
719  *      @	[SHLWAPI.32]
720  *
721  * Determine if a Unicode character is a control character.
722  *
723  * PARAMS
724  *  wc [I] Character to check.
725  *
726  * RETURNS
727  *  TRUE, if wc is a control character,
728  *  FALSE otherwise.
729  */
730 BOOL WINAPI IsCharCntrlW(WCHAR wc)
731 {
732     WORD CharType;
733 
734     return GetStringTypeW(CT_CTYPE1, &wc, 1, &CharType) && (CharType & C1_CNTRL);
735 }
736 
737 /*************************************************************************
738  *      @	[SHLWAPI.33]
739  *
740  * Determine if a Unicode character is a digit.
741  *
742  * PARAMS
743  *  wc [I] Character to check.
744  *
745  * RETURNS
746  *  TRUE, if wc is a digit,
747  *  FALSE otherwise.
748  */
749 BOOL WINAPI IsCharDigitW(WCHAR wc)
750 {
751     WORD CharType;
752 
753     return GetStringTypeW(CT_CTYPE1, &wc, 1, &CharType) && (CharType & C1_DIGIT);
754 }
755 
756 /*************************************************************************
757  *      @	[SHLWAPI.34]
758  *
759  * Determine if a Unicode character is a hex digit.
760  *
761  * PARAMS
762  *  wc [I] Character to check.
763  *
764  * RETURNS
765  *  TRUE, if wc is a hex digit,
766  *  FALSE otherwise.
767  */
768 BOOL WINAPI IsCharXDigitW(WCHAR wc)
769 {
770     WORD CharType;
771 
772     return GetStringTypeW(CT_CTYPE1, &wc, 1, &CharType) && (CharType & C1_XDIGIT);
773 }
774 
775 /*************************************************************************
776  *      @	[SHLWAPI.35]
777  *
778  */
779 BOOL WINAPI GetStringType3ExW(LPWSTR src, INT count, LPWORD type)
780 {
781     return GetStringTypeW(CT_CTYPE3, src, count, type);
782 }
783 
784 /*************************************************************************
785  *      @	[SHLWAPI.151]
786  *
787  * Compare two Ascii strings up to a given length.
788  *
789  * PARAMS
790  *  lpszSrc [I] Source string
791  *  lpszCmp [I] String to compare to lpszSrc
792  *  len     [I] Maximum length
793  *
794  * RETURNS
795  *  A number greater than, less than or equal to 0 depending on whether
796  *  lpszSrc is greater than, less than or equal to lpszCmp.
797  */
798 DWORD WINAPI StrCmpNCA(LPCSTR lpszSrc, LPCSTR lpszCmp, INT len)
799 {
800     return StrCmpNA(lpszSrc, lpszCmp, len);
801 }
802 
803 /*************************************************************************
804  *      @	[SHLWAPI.152]
805  *
806  * Unicode version of StrCmpNCA.
807  */
808 DWORD WINAPI StrCmpNCW(LPCWSTR lpszSrc, LPCWSTR lpszCmp, INT len)
809 {
810     return StrCmpNW(lpszSrc, lpszCmp, len);
811 }
812 
813 /*************************************************************************
814  *      @	[SHLWAPI.153]
815  *
816  * Compare two Ascii strings up to a given length, ignoring case.
817  *
818  * PARAMS
819  *  lpszSrc [I] Source string
820  *  lpszCmp [I] String to compare to lpszSrc
821  *  len     [I] Maximum length
822  *
823  * RETURNS
824  *  A number greater than, less than or equal to 0 depending on whether
825  *  lpszSrc is greater than, less than or equal to lpszCmp.
826  */
827 DWORD WINAPI StrCmpNICA(LPCSTR lpszSrc, LPCSTR lpszCmp, DWORD len)
828 {
829     return StrCmpNIA(lpszSrc, lpszCmp, len);
830 }
831 
832 /*************************************************************************
833  *      @	[SHLWAPI.154]
834  *
835  * Unicode version of StrCmpNICA.
836  */
837 DWORD WINAPI StrCmpNICW(LPCWSTR lpszSrc, LPCWSTR lpszCmp, DWORD len)
838 {
839     return StrCmpNIW(lpszSrc, lpszCmp, len);
840 }
841 
842 /*************************************************************************
843  *      @	[SHLWAPI.155]
844  *
845  * Compare two Ascii strings.
846  *
847  * PARAMS
848  *  lpszSrc [I] Source string
849  *  lpszCmp [I] String to compare to lpszSrc
850  *
851  * RETURNS
852  *  A number greater than, less than or equal to 0 depending on whether
853  *  lpszSrc is greater than, less than or equal to lpszCmp.
854  */
855 DWORD WINAPI StrCmpCA(LPCSTR lpszSrc, LPCSTR lpszCmp)
856 {
857     return lstrcmpA(lpszSrc, lpszCmp);
858 }
859 
860 /*************************************************************************
861  *      @	[SHLWAPI.156]
862  *
863  * Unicode version of StrCmpCA.
864  */
865 DWORD WINAPI StrCmpCW(LPCWSTR lpszSrc, LPCWSTR lpszCmp)
866 {
867     return lstrcmpW(lpszSrc, lpszCmp);
868 }
869 
870 /*************************************************************************
871  *      @	[SHLWAPI.157]
872  *
873  * Compare two Ascii strings, ignoring case.
874  *
875  * PARAMS
876  *  lpszSrc [I] Source string
877  *  lpszCmp [I] String to compare to lpszSrc
878  *
879  * RETURNS
880  *  A number greater than, less than or equal to 0 depending on whether
881  *  lpszSrc is greater than, less than or equal to lpszCmp.
882  */
883 DWORD WINAPI StrCmpICA(LPCSTR lpszSrc, LPCSTR lpszCmp)
884 {
885     return lstrcmpiA(lpszSrc, lpszCmp);
886 }
887 
888 /*************************************************************************
889  *      @	[SHLWAPI.158]
890  *
891  * Unicode version of StrCmpICA.
892  */
893 DWORD WINAPI StrCmpICW(LPCWSTR lpszSrc, LPCWSTR lpszCmp)
894 {
895     return lstrcmpiW(lpszSrc, lpszCmp);
896 }
897 
898 /*************************************************************************
899  *      @	[SHLWAPI.160]
900  *
901  * Get an identification string for the OS and explorer.
902  *
903  * PARAMS
904  *  lpszDest  [O] Destination for Id string
905  *  dwDestLen [I] Length of lpszDest
906  *
907  * RETURNS
908  *  TRUE,  If the string was created successfully
909  *  FALSE, Otherwise
910  */
911 BOOL WINAPI SHAboutInfoA(LPSTR lpszDest, DWORD dwDestLen)
912 {
913   WCHAR buff[2084];
914 
915   TRACE("(%p,%d)\n", lpszDest, dwDestLen);
916 
917   if (lpszDest && SHAboutInfoW(buff, dwDestLen))
918   {
919     WideCharToMultiByte(CP_ACP, 0, buff, -1, lpszDest, dwDestLen, NULL, NULL);
920     return TRUE;
921   }
922   return FALSE;
923 }
924 
925 /*************************************************************************
926  *      @	[SHLWAPI.161]
927  *
928  * Unicode version of SHAboutInfoA.
929  */
930 BOOL WINAPI SHAboutInfoW(LPWSTR lpszDest, DWORD dwDestLen)
931 {
932   static const WCHAR szIEKey[] = { 'S','O','F','T','W','A','R','E','\\',
933     'M','i','c','r','o','s','o','f','t','\\','I','n','t','e','r','n','e','t',
934     ' ','E','x','p','l','o','r','e','r','\0' };
935   static const WCHAR szWinNtKey[] = { 'S','O','F','T','W','A','R','E','\\',
936     'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s',' ',
937     'N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\0' };
938   static const WCHAR szWinKey[] = { 'S','O','F','T','W','A','R','E','\\',
939     'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
940     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\0' };
941   static const WCHAR szRegKey[] = { 'S','O','F','T','W','A','R','E','\\',
942     'M','i','c','r','o','s','o','f','t','\\','I','n','t','e','r','n','e','t',
943     ' ','E','x','p','l','o','r','e','r','\\',
944     'R','e','g','i','s','t','r','a','t','i','o','n','\0' };
945   static const WCHAR szVersion[] = { 'V','e','r','s','i','o','n','\0' };
946   static const WCHAR szCustomized[] = { 'C','u','s','t','o','m','i','z','e','d',
947     'V','e','r','s','i','o','n','\0' };
948   static const WCHAR szOwner[] = { 'R','e','g','i','s','t','e','r','e','d',
949     'O','w','n','e','r','\0' };
950   static const WCHAR szOrg[] = { 'R','e','g','i','s','t','e','r','e','d',
951     'O','r','g','a','n','i','z','a','t','i','o','n','\0' };
952   static const WCHAR szProduct[] = { 'P','r','o','d','u','c','t','I','d','\0' };
953   static const WCHAR szUpdate[] = { 'I','E','A','K',
954     'U','p','d','a','t','e','U','r','l','\0' };
955   static const WCHAR szHelp[] = { 'I','E','A','K',
956     'H','e','l','p','S','t','r','i','n','g','\0' };
957   WCHAR buff[2084];
958   HKEY hReg;
959   DWORD dwType, dwLen;
960 
961   TRACE("(%p,%d)\n", lpszDest, dwDestLen);
962 
963   if (!lpszDest)
964     return FALSE;
965 
966   *lpszDest = '\0';
967 
968   /* Try the NT key first, followed by 95/98 key */
969   if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szWinNtKey, 0, KEY_READ, &hReg) &&
970       RegOpenKeyExW(HKEY_LOCAL_MACHINE, szWinKey, 0, KEY_READ, &hReg))
971     return FALSE;
972 
973   /* OS Version */
974   buff[0] = '\0';
975   dwLen = 30;
976   if (!SHGetValueW(HKEY_LOCAL_MACHINE, szIEKey, szVersion, &dwType, buff, &dwLen))
977   {
978     DWORD dwStrLen = strlenW(buff);
979     dwLen = 30 - dwStrLen;
980     SHGetValueW(HKEY_LOCAL_MACHINE, szIEKey,
981                 szCustomized, &dwType, buff+dwStrLen, &dwLen);
982   }
983   StrCatBuffW(lpszDest, buff, dwDestLen);
984 
985   /* ~Registered Owner */
986   buff[0] = '~';
987   dwLen = 256;
988   if (SHGetValueW(hReg, szOwner, 0, &dwType, buff+1, &dwLen))
989     buff[1] = '\0';
990   StrCatBuffW(lpszDest, buff, dwDestLen);
991 
992   /* ~Registered Organization */
993   dwLen = 256;
994   if (SHGetValueW(hReg, szOrg, 0, &dwType, buff+1, &dwLen))
995     buff[1] = '\0';
996   StrCatBuffW(lpszDest, buff, dwDestLen);
997 
998   /* FIXME: Not sure where this number comes from  */
999   buff[0] = '~';
1000   buff[1] = '0';
1001   buff[2] = '\0';
1002   StrCatBuffW(lpszDest, buff, dwDestLen);
1003 
1004   /* ~Product Id */
1005   dwLen = 256;
1006   if (SHGetValueW(HKEY_LOCAL_MACHINE, szRegKey, szProduct, &dwType, buff+1, &dwLen))
1007     buff[1] = '\0';
1008   StrCatBuffW(lpszDest, buff, dwDestLen);
1009 
1010   /* ~IE Update Url */
1011   dwLen = 2048;
1012   if(SHGetValueW(HKEY_LOCAL_MACHINE, szWinKey, szUpdate, &dwType, buff+1, &dwLen))
1013     buff[1] = '\0';
1014   StrCatBuffW(lpszDest, buff, dwDestLen);
1015 
1016   /* ~IE Help String */
1017   dwLen = 256;
1018   if(SHGetValueW(hReg, szHelp, 0, &dwType, buff+1, &dwLen))
1019     buff[1] = '\0';
1020   StrCatBuffW(lpszDest, buff, dwDestLen);
1021 
1022   RegCloseKey(hReg);
1023   return TRUE;
1024 }
1025 
1026 /*************************************************************************
1027  *      @	[SHLWAPI.163]
1028  *
1029  * Call IOleCommandTarget_QueryStatus() on an object.
1030  *
1031  * PARAMS
1032  *  lpUnknown     [I] Object supporting the IOleCommandTarget interface
1033  *  pguidCmdGroup [I] GUID for the command group
1034  *  cCmds         [I]
1035  *  prgCmds       [O] Commands
1036  *  pCmdText      [O] Command text
1037  *
1038  * RETURNS
1039  *  Success: S_OK.
1040  *  Failure: E_FAIL, if lpUnknown is NULL.
1041  *           E_NOINTERFACE, if lpUnknown does not support IOleCommandTarget.
1042  *           Otherwise, an error code from IOleCommandTarget_QueryStatus().
1043  */
1044 HRESULT WINAPI IUnknown_QueryStatus(IUnknown* lpUnknown, REFGUID pguidCmdGroup,
1045                            ULONG cCmds, OLECMD *prgCmds, OLECMDTEXT* pCmdText)
1046 {
1047   HRESULT hRet = E_FAIL;
1048 
1049   TRACE("(%p,%p,%d,%p,%p)\n",lpUnknown, pguidCmdGroup, cCmds, prgCmds, pCmdText);
1050 
1051   if (lpUnknown)
1052   {
1053     IOleCommandTarget* lpOle;
1054 
1055     hRet = IUnknown_QueryInterface(lpUnknown, &IID_IOleCommandTarget,
1056                                    (void**)&lpOle);
1057 
1058     if (SUCCEEDED(hRet) && lpOle)
1059     {
1060       hRet = IOleCommandTarget_QueryStatus(lpOle, pguidCmdGroup, cCmds,
1061                                            prgCmds, pCmdText);
1062       IOleCommandTarget_Release(lpOle);
1063     }
1064   }
1065   return hRet;
1066 }
1067 
1068 /*************************************************************************
1069  *      @		[SHLWAPI.164]
1070  *
1071  * Call IOleCommandTarget_Exec() on an object.
1072  *
1073  * PARAMS
1074  *  lpUnknown     [I] Object supporting the IOleCommandTarget interface
1075  *  pguidCmdGroup [I] GUID for the command group
1076  *
1077  * RETURNS
1078  *  Success: S_OK.
1079  *  Failure: E_FAIL, if lpUnknown is NULL.
1080  *           E_NOINTERFACE, if lpUnknown does not support IOleCommandTarget.
1081  *           Otherwise, an error code from IOleCommandTarget_Exec().
1082  */
1083 HRESULT WINAPI IUnknown_Exec(IUnknown* lpUnknown, REFGUID pguidCmdGroup,
1084                            DWORD nCmdID, DWORD nCmdexecopt, VARIANT* pvaIn,
1085                            VARIANT* pvaOut)
1086 {
1087   HRESULT hRet = E_FAIL;
1088 
1089   TRACE("(%p,%p,%d,%d,%p,%p)\n",lpUnknown, pguidCmdGroup, nCmdID,
1090         nCmdexecopt, pvaIn, pvaOut);
1091 
1092   if (lpUnknown)
1093   {
1094     IOleCommandTarget* lpOle;
1095 
1096     hRet = IUnknown_QueryInterface(lpUnknown, &IID_IOleCommandTarget,
1097                                    (void**)&lpOle);
1098     if (SUCCEEDED(hRet) && lpOle)
1099     {
1100       hRet = IOleCommandTarget_Exec(lpOle, pguidCmdGroup, nCmdID,
1101                                     nCmdexecopt, pvaIn, pvaOut);
1102       IOleCommandTarget_Release(lpOle);
1103     }
1104   }
1105   return hRet;
1106 }
1107 
1108 /*************************************************************************
1109  *      @	[SHLWAPI.165]
1110  *
1111  * Retrieve, modify, and re-set a value from a window.
1112  *
1113  * PARAMS
1114  *  hWnd   [I] Window to get value from
1115  *  offset [I] Offset of value
1116  *  mask   [I] Mask for flags
1117  *  flags  [I] Bits to set in window value
1118  *
1119  * RETURNS
1120  *  The new value as it was set, or 0 if any parameter is invalid.
1121  *
1122  * NOTES
1123  *  Only bits specified in mask are affected - set if present in flags and
1124  *  reset otherwise.
1125  */
1126 LONG WINAPI SHSetWindowBits(HWND hwnd, INT offset, UINT mask, UINT flags)
1127 {
1128   LONG ret = GetWindowLongW(hwnd, offset);
1129   LONG new_flags = (flags & mask) | (ret & ~mask);
1130 
1131   TRACE("%p %d %x %x\n", hwnd, offset, mask, flags);
1132 
1133   if (new_flags != ret)
1134     ret = SetWindowLongW(hwnd, offset, new_flags);
1135   return ret;
1136 }
1137 
1138 /*************************************************************************
1139  *      @	[SHLWAPI.167]
1140  *
1141  * Change a window's parent.
1142  *
1143  * PARAMS
1144  *  hWnd       [I] Window to change parent of
1145  *  hWndParent [I] New parent window
1146  *
1147  * RETURNS
1148  *  The old parent of hWnd.
1149  *
1150  * NOTES
1151  *  If hWndParent is NULL (desktop), the window style is changed to WS_POPUP.
1152  *  If hWndParent is NOT NULL then we set the WS_CHILD style.
1153  */
1154 HWND WINAPI SHSetParentHwnd(HWND hWnd, HWND hWndParent)
1155 {
1156   TRACE("%p, %p\n", hWnd, hWndParent);
1157 
1158   if(GetParent(hWnd) == hWndParent)
1159     return NULL;
1160 
1161   if(hWndParent)
1162     SHSetWindowBits(hWnd, GWL_STYLE, WS_CHILD | WS_POPUP, WS_CHILD);
1163   else
1164     SHSetWindowBits(hWnd, GWL_STYLE, WS_CHILD | WS_POPUP, WS_POPUP);
1165 
1166   return hWndParent ? SetParent(hWnd, hWndParent) : NULL;
1167 }
1168 
1169 /*************************************************************************
1170  *      @       [SHLWAPI.168]
1171  *
1172  * Locate and advise a connection point in an IConnectionPointContainer object.
1173  *
1174  * PARAMS
1175  *  lpUnkSink   [I] Sink for the connection point advise call
1176  *  riid        [I] REFIID of connection point to advise
1177  *  fConnect    [I] TRUE = Connection being establisted, FALSE = broken
1178  *  lpUnknown   [I] Object supporting the IConnectionPointContainer interface
1179  *  lpCookie    [O] Pointer to connection point cookie
1180  *  lppCP       [O] Destination for the IConnectionPoint found
1181  *
1182  * RETURNS
1183  *  Success: S_OK. If lppCP is non-NULL, it is filled with the IConnectionPoint
1184  *           that was advised. The caller is responsible for releasing it.
1185  *  Failure: E_FAIL, if any arguments are invalid.
1186  *           E_NOINTERFACE, if lpUnknown isn't an IConnectionPointContainer,
1187  *           Or an HRESULT error code if any call fails.
1188  */
1189 HRESULT WINAPI ConnectToConnectionPoint(IUnknown* lpUnkSink, REFIID riid, BOOL fConnect,
1190                            IUnknown* lpUnknown, LPDWORD lpCookie,
1191                            IConnectionPoint **lppCP)
1192 {
1193   HRESULT hRet;
1194   IConnectionPointContainer* lpContainer;
1195   IConnectionPoint *lpCP;
1196 
1197   if(!lpUnknown || (fConnect && !lpUnkSink))
1198     return E_FAIL;
1199 
1200   if(lppCP)
1201     *lppCP = NULL;
1202 
1203   hRet = IUnknown_QueryInterface(lpUnknown, &IID_IConnectionPointContainer,
1204                                  (void**)&lpContainer);
1205   if (SUCCEEDED(hRet))
1206   {
1207     hRet = IConnectionPointContainer_FindConnectionPoint(lpContainer, riid, &lpCP);
1208 
1209     if (SUCCEEDED(hRet))
1210     {
1211       if(!fConnect)
1212         hRet = IConnectionPoint_Unadvise(lpCP, *lpCookie);
1213       else
1214         hRet = IConnectionPoint_Advise(lpCP, lpUnkSink, lpCookie);
1215 
1216       if (FAILED(hRet))
1217         *lpCookie = 0;
1218 
1219       if (lppCP && SUCCEEDED(hRet))
1220         *lppCP = lpCP; /* Caller keeps the interface */
1221       else
1222         IConnectionPoint_Release(lpCP); /* Release it */
1223     }
1224 
1225     IConnectionPointContainer_Release(lpContainer);
1226   }
1227   return hRet;
1228 }
1229 
1230 /*************************************************************************
1231  *	@	[SHLWAPI.169]
1232  *
1233  * Release an interface and zero a supplied pointer.
1234  *
1235  * PARAMS
1236  *  lpUnknown [I] Object to release
1237  *
1238  * RETURNS
1239  *  Nothing.
1240  */
1241 void WINAPI IUnknown_AtomicRelease(IUnknown ** lpUnknown)
1242 {
1243     TRACE("(%p)\n", lpUnknown);
1244 
1245     if(!lpUnknown || !*lpUnknown) return;
1246 
1247     TRACE("doing Release\n");
1248 
1249     IUnknown_Release(*lpUnknown);
1250     *lpUnknown = NULL;
1251 }
1252 
1253 /*************************************************************************
1254  *      @	[SHLWAPI.170]
1255  *
1256  * Skip '//' if present in a string.
1257  *
1258  * PARAMS
1259  *  lpszSrc [I] String to check for '//'
1260  *
1261  * RETURNS
1262  *  Success: The next character after the '//' or the string if not present
1263  *  Failure: NULL, if lpszStr is NULL.
1264  */
1265 LPCSTR WINAPI PathSkipLeadingSlashesA(LPCSTR lpszSrc)
1266 {
1267   if (lpszSrc && lpszSrc[0] == '/' && lpszSrc[1] == '/')
1268     lpszSrc += 2;
1269   return lpszSrc;
1270 }
1271 
1272 /*************************************************************************
1273  *      @		[SHLWAPI.171]
1274  *
1275  * Check if two interfaces come from the same object.
1276  *
1277  * PARAMS
1278  *   lpInt1 [I] Interface to check against lpInt2.
1279  *   lpInt2 [I] Interface to check against lpInt1.
1280  *
1281  * RETURNS
1282  *   TRUE, If the interfaces come from the same object.
1283  *   FALSE Otherwise.
1284  */
1285 BOOL WINAPI SHIsSameObject(IUnknown* lpInt1, IUnknown* lpInt2)
1286 {
1287   IUnknown *lpUnknown1, *lpUnknown2;
1288   BOOL ret;
1289 
1290   TRACE("(%p %p)\n", lpInt1, lpInt2);
1291 
1292   if (!lpInt1 || !lpInt2)
1293     return FALSE;
1294 
1295   if (lpInt1 == lpInt2)
1296     return TRUE;
1297 
1298   if (IUnknown_QueryInterface(lpInt1, &IID_IUnknown, (void**)&lpUnknown1) != S_OK)
1299     return FALSE;
1300 
1301   if (IUnknown_QueryInterface(lpInt2, &IID_IUnknown, (void**)&lpUnknown2) != S_OK)
1302   {
1303     IUnknown_Release(lpUnknown1);
1304     return FALSE;
1305   }
1306 
1307   ret = lpUnknown1 == lpUnknown2;
1308 
1309   IUnknown_Release(lpUnknown1);
1310   IUnknown_Release(lpUnknown2);
1311 
1312   return ret;
1313 }
1314 
1315 /*************************************************************************
1316  *      @	[SHLWAPI.172]
1317  *
1318  * Get the window handle of an object.
1319  *
1320  * PARAMS
1321  *  lpUnknown [I] Object to get the window handle of
1322  *  lphWnd    [O] Destination for window handle
1323  *
1324  * RETURNS
1325  *  Success: S_OK. lphWnd contains the objects window handle.
1326  *  Failure: An HRESULT error code.
1327  *
1328  * NOTES
1329  *  lpUnknown is expected to support one of the following interfaces:
1330  *  IOleWindow(), IInternetSecurityMgrSite(), or IShellView().
1331  */
1332 HRESULT WINAPI IUnknown_GetWindow(IUnknown *lpUnknown, HWND *lphWnd)
1333 {
1334   IUnknown *lpOle;
1335   HRESULT hRet = E_FAIL;
1336 
1337   TRACE("(%p,%p)\n", lpUnknown, lphWnd);
1338 
1339   if (!lpUnknown)
1340     return hRet;
1341 
1342   hRet = IUnknown_QueryInterface(lpUnknown, &IID_IOleWindow, (void**)&lpOle);
1343 
1344   if (FAILED(hRet))
1345   {
1346     hRet = IUnknown_QueryInterface(lpUnknown,&IID_IShellView, (void**)&lpOle);
1347 
1348     if (FAILED(hRet))
1349     {
1350       hRet = IUnknown_QueryInterface(lpUnknown, &IID_IInternetSecurityMgrSite,
1351                                       (void**)&lpOle);
1352     }
1353   }
1354 
1355   if (SUCCEEDED(hRet))
1356   {
1357     /* Laziness here - Since GetWindow() is the first method for the above 3
1358      * interfaces, we use the same call for them all.
1359      */
1360     hRet = IOleWindow_GetWindow((IOleWindow*)lpOle, lphWnd);
1361     IUnknown_Release(lpOle);
1362     if (lphWnd)
1363       TRACE("Returning HWND=%p\n", *lphWnd);
1364   }
1365 
1366   return hRet;
1367 }
1368 
1369 /*************************************************************************
1370  *      @	[SHLWAPI.173]
1371  *
1372  * Call a SetOwner method of IShellService from specified object.
1373  *
1374  * PARAMS
1375  *  iface [I] Object that supports IShellService
1376  *  pUnk  [I] Argument for the SetOwner call
1377  *
1378  * RETURNS
1379  *  Corresponding return value from last call or E_FAIL for null input
1380  */
1381 HRESULT WINAPI IUnknown_SetOwner(IUnknown *iface, IUnknown *pUnk)
1382 {
1383   IShellService *service;
1384   HRESULT hr;
1385 
1386   TRACE("(%p, %p)\n", iface, pUnk);
1387 
1388   if (!iface) return E_FAIL;
1389 
1390   hr = IUnknown_QueryInterface(iface, &IID_IShellService, (void**)&service);
1391   if (hr == S_OK)
1392   {
1393     hr = IShellService_SetOwner(service, pUnk);
1394     IShellService_Release(service);
1395   }
1396 
1397   return hr;
1398 }
1399 
1400 /*************************************************************************
1401  *      @	[SHLWAPI.174]
1402  *
1403  * Call either IObjectWithSite_SetSite() or IInternetSecurityManager_SetSecuritySite() on
1404  * an object.
1405  *
1406  */
1407 HRESULT WINAPI IUnknown_SetSite(
1408         IUnknown *obj,        /* [in]   OLE object     */
1409         IUnknown *site)       /* [in]   Site interface */
1410 {
1411     HRESULT hr;
1412     IObjectWithSite *iobjwithsite;
1413     IInternetSecurityManager *isecmgr;
1414 
1415     if (!obj) return E_FAIL;
1416 
1417     hr = IUnknown_QueryInterface(obj, &IID_IObjectWithSite, (LPVOID *)&iobjwithsite);
1418     TRACE("IID_IObjectWithSite QI ret=%08x, %p\n", hr, iobjwithsite);
1419     if (SUCCEEDED(hr))
1420     {
1421 	hr = IObjectWithSite_SetSite(iobjwithsite, site);
1422 	TRACE("done IObjectWithSite_SetSite ret=%08x\n", hr);
1423 	IObjectWithSite_Release(iobjwithsite);
1424     }
1425     else
1426     {
1427 	hr = IUnknown_QueryInterface(obj, &IID_IInternetSecurityManager, (LPVOID *)&isecmgr);
1428 	TRACE("IID_IInternetSecurityManager QI ret=%08x, %p\n", hr, isecmgr);
1429 	if (FAILED(hr)) return hr;
1430 
1431 	hr = IInternetSecurityManager_SetSecuritySite(isecmgr, (IInternetSecurityMgrSite *)site);
1432 	TRACE("done IInternetSecurityManager_SetSecuritySite ret=%08x\n", hr);
1433 	IInternetSecurityManager_Release(isecmgr);
1434     }
1435     return hr;
1436 }
1437 
1438 /*************************************************************************
1439  *      @	[SHLWAPI.175]
1440  *
1441  * Call IPersist_GetClassID() on an object.
1442  *
1443  * PARAMS
1444  *  lpUnknown [I] Object supporting the IPersist interface
1445  *  clsid     [O] Destination for Class Id
1446  *
1447  * RETURNS
1448  *  Success: S_OK. lpClassId contains the Class Id requested.
1449  *  Failure: E_FAIL, If lpUnknown is NULL,
1450  *           E_NOINTERFACE If lpUnknown does not support IPersist,
1451  *           Or an HRESULT error code.
1452  */
1453 HRESULT WINAPI IUnknown_GetClassID(IUnknown *lpUnknown, CLSID *clsid)
1454 {
1455     IPersist *persist;
1456     HRESULT hr;
1457 
1458     TRACE("(%p, %p)\n", lpUnknown, clsid);
1459 
1460     if (!lpUnknown)
1461     {
1462         memset(clsid, 0, sizeof(*clsid));
1463         return E_FAIL;
1464     }
1465 
1466     hr = IUnknown_QueryInterface(lpUnknown, &IID_IPersist, (void**)&persist);
1467     if (hr != S_OK)
1468     {
1469         hr = IUnknown_QueryInterface(lpUnknown, &IID_IPersistFolder, (void**)&persist);
1470         if (hr != S_OK)
1471             return hr;
1472     }
1473 
1474     hr = IPersist_GetClassID(persist, clsid);
1475     IPersist_Release(persist);
1476     return hr;
1477 }
1478 
1479 /*************************************************************************
1480  *      @	[SHLWAPI.176]
1481  *
1482  * Retrieve a Service Interface from an object.
1483  *
1484  * PARAMS
1485  *  lpUnknown [I] Object to get an IServiceProvider interface from
1486  *  sid       [I] Service ID for IServiceProvider_QueryService() call
1487  *  riid      [I] Function requested for QueryService call
1488  *  lppOut    [O] Destination for the service interface pointer
1489  *
1490  * RETURNS
1491  *  Success: S_OK. lppOut contains an object providing the requested service
1492  *  Failure: An HRESULT error code
1493  *
1494  * NOTES
1495  *  lpUnknown is expected to support the IServiceProvider interface.
1496  */
1497 HRESULT WINAPI IUnknown_QueryService(IUnknown* lpUnknown, REFGUID sid, REFIID riid,
1498                            LPVOID *lppOut)
1499 {
1500   IServiceProvider* pService = NULL;
1501   HRESULT hRet;
1502 
1503   if (!lppOut)
1504     return E_FAIL;
1505 
1506   *lppOut = NULL;
1507 
1508   if (!lpUnknown)
1509     return E_FAIL;
1510 
1511   hRet = IUnknown_QueryInterface(lpUnknown, &IID_IServiceProvider,
1512                                  (LPVOID*)&pService);
1513 
1514   if (hRet == S_OK && pService)
1515   {
1516     TRACE("QueryInterface returned (IServiceProvider*)%p\n", pService);
1517 
1518     /* Get a Service interface from the object */
1519     hRet = IServiceProvider_QueryService(pService, sid, riid, lppOut);
1520 
1521     TRACE("(IServiceProvider*)%p returned (IUnknown*)%p\n", pService, *lppOut);
1522 
1523     IServiceProvider_Release(pService);
1524   }
1525   return hRet;
1526 }
1527 
1528 /*************************************************************************
1529  *      @	[SHLWAPI.484]
1530  *
1531  * Calls IOleCommandTarget::Exec() for specified service object.
1532  *
1533  * PARAMS
1534  *  lpUnknown [I] Object to get an IServiceProvider interface from
1535  *  service   [I] Service ID for IServiceProvider_QueryService() call
1536  *  group     [I] Group ID for IOleCommandTarget::Exec() call
1537  *  cmdId     [I] Command ID for IOleCommandTarget::Exec() call
1538  *  cmdOpt    [I] Options flags for command
1539  *  pIn       [I] Input arguments for command
1540  *  pOut      [O] Output arguments for command
1541  *
1542  * RETURNS
1543  *  Success: S_OK. lppOut contains an object providing the requested service
1544  *  Failure: An HRESULT error code
1545  *
1546  * NOTES
1547  *  lpUnknown is expected to support the IServiceProvider interface.
1548  */
1549 HRESULT WINAPI IUnknown_QueryServiceExec(IUnknown *lpUnknown, REFIID service,
1550     const GUID *group, DWORD cmdId, DWORD cmdOpt, VARIANT *pIn, VARIANT *pOut)
1551 {
1552     IOleCommandTarget *target;
1553     HRESULT hr;
1554 
1555     TRACE("%p %s %s %d %08x %p %p\n", lpUnknown, debugstr_guid(service),
1556         debugstr_guid(group), cmdId, cmdOpt, pIn, pOut);
1557 
1558     hr = IUnknown_QueryService(lpUnknown, service, &IID_IOleCommandTarget, (void**)&target);
1559     if (hr == S_OK)
1560     {
1561         hr = IOleCommandTarget_Exec(target, group, cmdId, cmdOpt, pIn, pOut);
1562         IOleCommandTarget_Release(target);
1563     }
1564 
1565     TRACE("<-- hr=0x%08x\n", hr);
1566 
1567     return hr;
1568 }
1569 
1570 /*************************************************************************
1571  *      @	[SHLWAPI.514]
1572  *
1573  * Calls IProfferService methods to proffer/revoke specified service.
1574  *
1575  * PARAMS
1576  *  lpUnknown [I]  Object to get an IServiceProvider interface from
1577  *  service   [I]  Service ID for IProfferService::Proffer/Revoke calls
1578  *  pService  [I]  Service to proffer. If NULL ::Revoke is called
1579  *  pCookie   [IO] Group ID for IOleCommandTarget::Exec() call
1580  *
1581  * RETURNS
1582  *  Success: S_OK. IProffer method returns S_OK
1583  *  Failure: An HRESULT error code
1584  *
1585  * NOTES
1586  *  lpUnknown is expected to support the IServiceProvider interface.
1587  */
1588 HRESULT WINAPI IUnknown_ProfferService(IUnknown *lpUnknown, REFGUID service, IServiceProvider *pService, DWORD *pCookie)
1589 {
1590     IProfferService *proffer;
1591     HRESULT hr;
1592 
1593     TRACE("%p %s %p %p\n", lpUnknown, debugstr_guid(service), pService, pCookie);
1594 
1595     hr = IUnknown_QueryService(lpUnknown, &IID_IProfferService, &IID_IProfferService, (void**)&proffer);
1596     if (hr == S_OK)
1597     {
1598         if (pService)
1599             hr = IProfferService_ProfferService(proffer, service, pService, pCookie);
1600         else
1601         {
1602             hr = IProfferService_RevokeService(proffer, *pCookie);
1603             *pCookie = 0;
1604         }
1605 
1606         IProfferService_Release(proffer);
1607     }
1608 
1609     return hr;
1610 }
1611 
1612 /*************************************************************************
1613  *      @	[SHLWAPI.479]
1614  *
1615  * Call an object's UIActivateIO method.
1616  *
1617  * PARAMS
1618  *  unknown  [I] Object to call the UIActivateIO method on
1619  *  activate [I] Parameter for UIActivateIO call
1620  *  msg      [I] Parameter for UIActivateIO call
1621  *
1622  * RETURNS
1623  *  Success: Value of UI_ActivateIO call
1624  *  Failure: An HRESULT error code
1625  *
1626  * NOTES
1627  *  unknown is expected to support the IInputObject interface.
1628  */
1629 HRESULT WINAPI IUnknown_UIActivateIO(IUnknown *unknown, BOOL activate, LPMSG msg)
1630 {
1631     IInputObject* object = NULL;
1632     HRESULT ret;
1633 
1634     if (!unknown)
1635         return E_FAIL;
1636 
1637     /* Get an IInputObject interface from the object */
1638     ret = IUnknown_QueryInterface(unknown, &IID_IInputObject, (LPVOID*) &object);
1639 
1640     if (ret == S_OK)
1641     {
1642         ret = IInputObject_UIActivateIO(object, activate, msg);
1643         IInputObject_Release(object);
1644     }
1645 
1646     return ret;
1647 }
1648 
1649 /*************************************************************************
1650  *      @	[SHLWAPI.177]
1651  *
1652  * Loads a popup menu.
1653  *
1654  * PARAMS
1655  *  hInst  [I] Instance handle
1656  *  szName [I] Menu name
1657  *
1658  * RETURNS
1659  *  Success: TRUE.
1660  *  Failure: FALSE.
1661  */
1662 BOOL WINAPI SHLoadMenuPopup(HINSTANCE hInst, LPCWSTR szName)
1663 {
1664   HMENU hMenu;
1665 
1666   TRACE("%p %s\n", hInst, debugstr_w(szName));
1667 
1668   if ((hMenu = LoadMenuW(hInst, szName)))
1669   {
1670     if (GetSubMenu(hMenu, 0))
1671       RemoveMenu(hMenu, 0, MF_BYPOSITION);
1672 
1673     DestroyMenu(hMenu);
1674     return TRUE;
1675   }
1676   return FALSE;
1677 }
1678 
1679 typedef struct _enumWndData
1680 {
1681   UINT   uiMsgId;
1682   WPARAM wParam;
1683   LPARAM lParam;
1684   LRESULT (WINAPI *pfnPost)(HWND,UINT,WPARAM,LPARAM);
1685 } enumWndData;
1686 
1687 /* Callback for SHLWAPI_178 */
1688 static BOOL CALLBACK SHLWAPI_EnumChildProc(HWND hWnd, LPARAM lParam)
1689 {
1690   enumWndData *data = (enumWndData *)lParam;
1691 
1692   TRACE("(%p,%p)\n", hWnd, data);
1693   data->pfnPost(hWnd, data->uiMsgId, data->wParam, data->lParam);
1694   return TRUE;
1695 }
1696 
1697 /*************************************************************************
1698  * @  [SHLWAPI.178]
1699  *
1700  * Send or post a message to every child of a window.
1701  *
1702  * PARAMS
1703  *  hWnd    [I] Window whose children will get the messages
1704  *  uiMsgId [I] Message Id
1705  *  wParam  [I] WPARAM of message
1706  *  lParam  [I] LPARAM of message
1707  *  bSend   [I] TRUE = Use SendMessageA(), FALSE = Use PostMessageA()
1708  *
1709  * RETURNS
1710  *  Nothing.
1711  *
1712  * NOTES
1713  *  The appropriate ASCII or Unicode function is called for the window.
1714  */
1715 void WINAPI SHPropagateMessage(HWND hWnd, UINT uiMsgId, WPARAM wParam, LPARAM lParam, BOOL bSend)
1716 {
1717   enumWndData data;
1718 
1719   TRACE("(%p,%u,%ld,%ld,%d)\n", hWnd, uiMsgId, wParam, lParam, bSend);
1720 
1721   if(hWnd)
1722   {
1723     data.uiMsgId = uiMsgId;
1724     data.wParam  = wParam;
1725     data.lParam  = lParam;
1726 
1727     if (bSend)
1728       data.pfnPost = IsWindowUnicode(hWnd) ? (void*)SendMessageW : (void*)SendMessageA;
1729     else
1730       data.pfnPost = IsWindowUnicode(hWnd) ? (void*)PostMessageW : (void*)PostMessageA;
1731 
1732     EnumChildWindows(hWnd, SHLWAPI_EnumChildProc, (LPARAM)&data);
1733   }
1734 }
1735 
1736 /*************************************************************************
1737  *      @	[SHLWAPI.180]
1738  *
1739  * Remove all sub-menus from a menu.
1740  *
1741  * PARAMS
1742  *  hMenu [I] Menu to remove sub-menus from
1743  *
1744  * RETURNS
1745  *  Success: 0.  All sub-menus under hMenu are removed
1746  *  Failure: -1, if any parameter is invalid
1747  */
1748 DWORD WINAPI SHRemoveAllSubMenus(HMENU hMenu)
1749 {
1750   int iItemCount = GetMenuItemCount(hMenu) - 1;
1751 
1752   TRACE("%p\n", hMenu);
1753 
1754   while (iItemCount >= 0)
1755   {
1756     HMENU hSubMenu = GetSubMenu(hMenu, iItemCount);
1757     if (hSubMenu)
1758       RemoveMenu(hMenu, iItemCount, MF_BYPOSITION);
1759     iItemCount--;
1760   }
1761   return iItemCount;
1762 }
1763 
1764 /*************************************************************************
1765  *      @	[SHLWAPI.181]
1766  *
1767  * Enable or disable a menu item.
1768  *
1769  * PARAMS
1770  *  hMenu   [I] Menu holding menu item
1771  *  uID     [I] ID of menu item to enable/disable
1772  *  bEnable [I] Whether to enable (TRUE) or disable (FALSE) the item.
1773  *
1774  * RETURNS
1775  *  The return code from EnableMenuItem.
1776  */
1777 UINT WINAPI SHEnableMenuItem(HMENU hMenu, UINT wItemID, BOOL bEnable)
1778 {
1779   TRACE("%p, %u, %d\n", hMenu, wItemID, bEnable);
1780   return EnableMenuItem(hMenu, wItemID, bEnable ? MF_ENABLED : MF_GRAYED);
1781 }
1782 
1783 /*************************************************************************
1784  * @	[SHLWAPI.182]
1785  *
1786  * Check or uncheck a menu item.
1787  *
1788  * PARAMS
1789  *  hMenu  [I] Menu holding menu item
1790  *  uID    [I] ID of menu item to check/uncheck
1791  *  bCheck [I] Whether to check (TRUE) or uncheck (FALSE) the item.
1792  *
1793  * RETURNS
1794  *  The return code from CheckMenuItem.
1795  */
1796 DWORD WINAPI SHCheckMenuItem(HMENU hMenu, UINT uID, BOOL bCheck)
1797 {
1798   TRACE("%p, %u, %d\n", hMenu, uID, bCheck);
1799   return CheckMenuItem(hMenu, uID, bCheck ? MF_CHECKED : MF_UNCHECKED);
1800 }
1801 
1802 /*************************************************************************
1803  *      @	[SHLWAPI.183]
1804  *
1805  * Register a window class if it isn't already.
1806  *
1807  * PARAMS
1808  *  lpWndClass [I] Window class to register
1809  *
1810  * RETURNS
1811  *  The result of the RegisterClassA call.
1812  */
1813 DWORD WINAPI SHRegisterClassA(WNDCLASSA *wndclass)
1814 {
1815   WNDCLASSA wca;
1816   if (GetClassInfoA(wndclass->hInstance, wndclass->lpszClassName, &wca))
1817     return TRUE;
1818   return (DWORD)RegisterClassA(wndclass);
1819 }
1820 
1821 /*************************************************************************
1822  *      @	[SHLWAPI.186]
1823  */
1824 BOOL WINAPI SHSimulateDrop(IDropTarget *pDrop, IDataObject *pDataObj,
1825                            DWORD grfKeyState, PPOINTL lpPt, DWORD* pdwEffect)
1826 {
1827   DWORD dwEffect = DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY;
1828   POINTL pt = { 0, 0 };
1829 
1830   TRACE("%p %p 0x%08x %p %p\n", pDrop, pDataObj, grfKeyState, lpPt, pdwEffect);
1831 
1832   if (!lpPt)
1833     lpPt = &pt;
1834 
1835   if (!pdwEffect)
1836     pdwEffect = &dwEffect;
1837 
1838   IDropTarget_DragEnter(pDrop, pDataObj, grfKeyState, *lpPt, pdwEffect);
1839 
1840   if (*pdwEffect != DROPEFFECT_NONE)
1841     return IDropTarget_Drop(pDrop, pDataObj, grfKeyState, *lpPt, pdwEffect);
1842 
1843   IDropTarget_DragLeave(pDrop);
1844   return TRUE;
1845 }
1846 
1847 /*************************************************************************
1848  *      @	[SHLWAPI.187]
1849  *
1850  * Call IPersistPropertyBag_Load() on an object.
1851  *
1852  * PARAMS
1853  *  lpUnknown [I] Object supporting the IPersistPropertyBag interface
1854  *  lpPropBag [O] Destination for loaded IPropertyBag
1855  *
1856  * RETURNS
1857  *  Success: S_OK.
1858  *  Failure: An HRESULT error code, or E_FAIL if lpUnknown is NULL.
1859  */
1860 DWORD WINAPI SHLoadFromPropertyBag(IUnknown *lpUnknown, IPropertyBag* lpPropBag)
1861 {
1862   IPersistPropertyBag* lpPPBag;
1863   HRESULT hRet = E_FAIL;
1864 
1865   TRACE("(%p,%p)\n", lpUnknown, lpPropBag);
1866 
1867   if (lpUnknown)
1868   {
1869     hRet = IUnknown_QueryInterface(lpUnknown, &IID_IPersistPropertyBag,
1870                                    (void**)&lpPPBag);
1871     if (SUCCEEDED(hRet) && lpPPBag)
1872     {
1873       hRet = IPersistPropertyBag_Load(lpPPBag, lpPropBag, NULL);
1874       IPersistPropertyBag_Release(lpPPBag);
1875     }
1876   }
1877   return hRet;
1878 }
1879 
1880 /*************************************************************************
1881  * @  [SHLWAPI.188]
1882  *
1883  * Call IOleControlSite_TranslateAccelerator()  on an object.
1884  *
1885  * PARAMS
1886  *  lpUnknown   [I] Object supporting the IOleControlSite interface.
1887  *  lpMsg       [I] Key message to be processed.
1888  *  dwModifiers [I] Flags containing the state of the modifier keys.
1889  *
1890  * RETURNS
1891  *  Success: S_OK.
1892  *  Failure: An HRESULT error code, or E_INVALIDARG if lpUnknown is NULL.
1893  */
1894 HRESULT WINAPI IUnknown_TranslateAcceleratorOCS(IUnknown *lpUnknown, LPMSG lpMsg, DWORD dwModifiers)
1895 {
1896   IOleControlSite* lpCSite = NULL;
1897   HRESULT hRet = E_INVALIDARG;
1898 
1899   TRACE("(%p,%p,0x%08x)\n", lpUnknown, lpMsg, dwModifiers);
1900   if (lpUnknown)
1901   {
1902     hRet = IUnknown_QueryInterface(lpUnknown, &IID_IOleControlSite,
1903                                    (void**)&lpCSite);
1904     if (SUCCEEDED(hRet) && lpCSite)
1905     {
1906       hRet = IOleControlSite_TranslateAccelerator(lpCSite, lpMsg, dwModifiers);
1907       IOleControlSite_Release(lpCSite);
1908     }
1909   }
1910   return hRet;
1911 }
1912 
1913 
1914 /*************************************************************************
1915  * @  [SHLWAPI.189]
1916  *
1917  * Call IOleControlSite_OnFocus() on an object.
1918  *
1919  * PARAMS
1920  *  lpUnknown [I] Object supporting the IOleControlSite interface.
1921  *  fGotFocus [I] Whether focus was gained (TRUE) or lost (FALSE).
1922  *
1923  * RETURNS
1924  *  Success: S_OK.
1925  *  Failure: An HRESULT error code, or E_FAIL if lpUnknown is NULL.
1926  */
1927 HRESULT WINAPI IUnknown_OnFocusOCS(IUnknown *lpUnknown, BOOL fGotFocus)
1928 {
1929   IOleControlSite* lpCSite = NULL;
1930   HRESULT hRet = E_FAIL;
1931 
1932   TRACE("(%p, %d)\n", lpUnknown, fGotFocus);
1933   if (lpUnknown)
1934   {
1935     hRet = IUnknown_QueryInterface(lpUnknown, &IID_IOleControlSite,
1936                                    (void**)&lpCSite);
1937     if (SUCCEEDED(hRet) && lpCSite)
1938     {
1939       hRet = IOleControlSite_OnFocus(lpCSite, fGotFocus);
1940       IOleControlSite_Release(lpCSite);
1941     }
1942   }
1943   return hRet;
1944 }
1945 
1946 /*************************************************************************
1947  * @    [SHLWAPI.190]
1948  */
1949 HRESULT WINAPI IUnknown_HandleIRestrict(LPUNKNOWN lpUnknown, PVOID lpArg1,
1950                                         PVOID lpArg2, PVOID lpArg3, PVOID lpArg4)
1951 {
1952   /* FIXME: {D12F26B2-D90A-11D0-830D-00AA005B4383} - What object does this represent? */
1953   static const DWORD service_id[] = { 0xd12f26b2, 0x11d0d90a, 0xaa000d83, 0x83435b00 };
1954   /* FIXME: {D12F26B1-D90A-11D0-830D-00AA005B4383} - Also Unknown/undocumented */
1955   static const DWORD function_id[] = { 0xd12f26b1, 0x11d0d90a, 0xaa000d83, 0x83435b00 };
1956   HRESULT hRet = E_INVALIDARG;
1957   LPUNKNOWN lpUnkInner = NULL; /* FIXME: Real type is unknown */
1958 
1959   TRACE("(%p,%p,%p,%p,%p)\n", lpUnknown, lpArg1, lpArg2, lpArg3, lpArg4);
1960 
1961   if (lpUnknown && lpArg4)
1962   {
1963      hRet = IUnknown_QueryService(lpUnknown, (REFGUID)service_id,
1964                                   (REFGUID)function_id, (void**)&lpUnkInner);
1965 
1966      if (SUCCEEDED(hRet) && lpUnkInner)
1967      {
1968        /* FIXME: The type of service object requested is unknown, however
1969 	* testing shows that its first method is called with 4 parameters.
1970 	* Fake this by using IParseDisplayName_ParseDisplayName since the
1971 	* signature and position in the vtable matches our unknown object type.
1972 	*/
1973        hRet = IParseDisplayName_ParseDisplayName((LPPARSEDISPLAYNAME)lpUnkInner,
1974                                                  lpArg1, lpArg2, lpArg3, lpArg4);
1975        IUnknown_Release(lpUnkInner);
1976      }
1977   }
1978   return hRet;
1979 }
1980 
1981 /*************************************************************************
1982  * @    [SHLWAPI.192]
1983  *
1984  * Get a sub-menu from a menu item.
1985  *
1986  * PARAMS
1987  *  hMenu [I] Menu to get sub-menu from
1988  *  uID   [I] ID of menu item containing sub-menu
1989  *
1990  * RETURNS
1991  *  The sub-menu of the item, or a NULL handle if any parameters are invalid.
1992  */
1993 HMENU WINAPI SHGetMenuFromID(HMENU hMenu, UINT uID)
1994 {
1995   MENUITEMINFOW mi;
1996 
1997   TRACE("(%p,%u)\n", hMenu, uID);
1998 
1999   mi.cbSize = sizeof(mi);
2000   mi.fMask = MIIM_SUBMENU;
2001 
2002   if (!GetMenuItemInfoW(hMenu, uID, FALSE, &mi))
2003     return NULL;
2004 
2005   return mi.hSubMenu;
2006 }
2007 
2008 /*************************************************************************
2009  *      @	[SHLWAPI.193]
2010  *
2011  * Get the color depth of the primary display.
2012  *
2013  * PARAMS
2014  *  None.
2015  *
2016  * RETURNS
2017  *  The color depth of the primary display.
2018  */
2019 DWORD WINAPI SHGetCurColorRes(void)
2020 {
2021     HDC hdc;
2022     DWORD ret;
2023 
2024     TRACE("()\n");
2025 
2026     hdc = GetDC(0);
2027     ret = GetDeviceCaps(hdc, BITSPIXEL) * GetDeviceCaps(hdc, PLANES);
2028     ReleaseDC(0, hdc);
2029     return ret;
2030 }
2031 
2032 /*************************************************************************
2033  *      @	[SHLWAPI.194]
2034  *
2035  * Wait for a message to arrive, with a timeout.
2036  *
2037  * PARAMS
2038  *  hand      [I] Handle to query
2039  *  dwTimeout [I] Timeout in ticks or INFINITE to never timeout
2040  *
2041  * RETURNS
2042  *  STATUS_TIMEOUT if no message is received before dwTimeout ticks passes.
2043  *  Otherwise returns the value from MsgWaitForMultipleObjectsEx when a
2044  *  message is available.
2045  */
2046 DWORD WINAPI SHWaitForSendMessageThread(HANDLE hand, DWORD dwTimeout)
2047 {
2048   DWORD dwEndTicks = GetTickCount() + dwTimeout;
2049   DWORD dwRet;
2050 
2051   while ((dwRet = MsgWaitForMultipleObjectsEx(1, &hand, dwTimeout, QS_SENDMESSAGE, 0)) == 1)
2052   {
2053     MSG msg;
2054 
2055     PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE);
2056 
2057     if (dwTimeout != INFINITE)
2058     {
2059         if ((int)(dwTimeout = dwEndTicks - GetTickCount()) <= 0)
2060             return WAIT_TIMEOUT;
2061     }
2062   }
2063 
2064   return dwRet;
2065 }
2066 
2067 /*************************************************************************
2068  *      @       [SHLWAPI.195]
2069  *
2070  * Determine if a shell folder can be expanded.
2071  *
2072  * PARAMS
2073  *  lpFolder [I] Parent folder containing the object to test.
2074  *  pidl     [I] Id of the object to test.
2075  *
2076  * RETURNS
2077  *  Success: S_OK, if the object is expandable, S_FALSE otherwise.
2078  *  Failure: E_INVALIDARG, if any argument is invalid.
2079  *
2080  * NOTES
2081  *  If the object to be tested does not expose the IQueryInfo() interface it
2082  *  will not be identified as an expandable folder.
2083  */
2084 HRESULT WINAPI SHIsExpandableFolder(LPSHELLFOLDER lpFolder, LPCITEMIDLIST pidl)
2085 {
2086   HRESULT hRet = E_INVALIDARG;
2087   IQueryInfo *lpInfo;
2088 
2089   if (lpFolder && pidl)
2090   {
2091     hRet = IShellFolder_GetUIObjectOf(lpFolder, NULL, 1, &pidl, &IID_IQueryInfo,
2092                                       NULL, (void**)&lpInfo);
2093     if (FAILED(hRet))
2094       hRet = S_FALSE; /* Doesn't expose IQueryInfo */
2095     else
2096     {
2097       DWORD dwFlags = 0;
2098 
2099       /* MSDN states of IQueryInfo_GetInfoFlags() that "This method is not
2100        * currently used". Really? You wouldn't be holding out on me would you?
2101        */
2102       hRet = IQueryInfo_GetInfoFlags(lpInfo, &dwFlags);
2103 
2104       if (SUCCEEDED(hRet))
2105       {
2106         /* 0x2 is an undocumented flag apparently indicating expandability */
2107         hRet = dwFlags & 0x2 ? S_OK : S_FALSE;
2108       }
2109 
2110       IQueryInfo_Release(lpInfo);
2111     }
2112   }
2113   return hRet;
2114 }
2115 
2116 /*************************************************************************
2117  *      @       [SHLWAPI.197]
2118  *
2119  * Blank out a region of text by drawing the background only.
2120  *
2121  * PARAMS
2122  *  hDC   [I] Device context to draw in
2123  *  pRect [I] Area to draw in
2124  *  cRef  [I] Color to draw in
2125  *
2126  * RETURNS
2127  *  Nothing.
2128  */
2129 DWORD WINAPI SHFillRectClr(HDC hDC, LPCRECT pRect, COLORREF cRef)
2130 {
2131     COLORREF cOldColor = SetBkColor(hDC, cRef);
2132     ExtTextOutA(hDC, 0, 0, ETO_OPAQUE, pRect, 0, 0, 0);
2133     SetBkColor(hDC, cOldColor);
2134     return 0;
2135 }
2136 
2137 /*************************************************************************
2138  *      @	[SHLWAPI.198]
2139  *
2140  * Return the value associated with a key in a map.
2141  *
2142  * PARAMS
2143  *  lpKeys   [I] A list of keys of length iLen
2144  *  lpValues [I] A list of values associated with lpKeys, of length iLen
2145  *  iLen     [I] Length of both lpKeys and lpValues
2146  *  iKey     [I] The key value to look up in lpKeys
2147  *
2148  * RETURNS
2149  *  The value in lpValues associated with iKey, or -1 if iKey is not
2150  *  found in lpKeys.
2151  *
2152  * NOTES
2153  *  - If two elements in the map share the same key, this function returns
2154  *    the value closest to the start of the map
2155  *  - The native version of this function crashes if lpKeys or lpValues is NULL.
2156  */
2157 int WINAPI SHSearchMapInt(const int *lpKeys, const int *lpValues, int iLen, int iKey)
2158 {
2159   if (lpKeys && lpValues)
2160   {
2161     int i = 0;
2162 
2163     while (i < iLen)
2164     {
2165       if (lpKeys[i] == iKey)
2166         return lpValues[i]; /* Found */
2167       i++;
2168     }
2169   }
2170   return -1; /* Not found */
2171 }
2172 
2173 
2174 /*************************************************************************
2175  *      @	[SHLWAPI.199]
2176  *
2177  * Copy an interface pointer
2178  *
2179  * PARAMS
2180  *   lppDest   [O] Destination for copy
2181  *   lpUnknown [I] Source for copy
2182  *
2183  * RETURNS
2184  *  Nothing.
2185  */
2186 VOID WINAPI IUnknown_Set(IUnknown **lppDest, IUnknown *lpUnknown)
2187 {
2188   TRACE("(%p,%p)\n", lppDest, lpUnknown);
2189 
2190   IUnknown_AtomicRelease(lppDest);
2191 
2192   if (lpUnknown)
2193   {
2194     IUnknown_AddRef(lpUnknown);
2195     *lppDest = lpUnknown;
2196   }
2197 }
2198 
2199 /*************************************************************************
2200  *      @	[SHLWAPI.200]
2201  *
2202  */
2203 HRESULT WINAPI MayQSForward(IUnknown* lpUnknown, PVOID lpReserved,
2204                             REFGUID riidCmdGrp, ULONG cCmds,
2205                             OLECMD *prgCmds, OLECMDTEXT* pCmdText)
2206 {
2207   FIXME("(%p,%p,%p,%d,%p,%p) - stub\n",
2208         lpUnknown, lpReserved, riidCmdGrp, cCmds, prgCmds, pCmdText);
2209 
2210   /* FIXME: Calls IsQSForward & IUnknown_QueryStatus */
2211   return DRAGDROP_E_NOTREGISTERED;
2212 }
2213 
2214 /*************************************************************************
2215  *      @	[SHLWAPI.201]
2216  *
2217  */
2218 HRESULT WINAPI MayExecForward(IUnknown* lpUnknown, INT iUnk, REFGUID pguidCmdGroup,
2219                            DWORD nCmdID, DWORD nCmdexecopt, VARIANT* pvaIn,
2220                            VARIANT* pvaOut)
2221 {
2222   FIXME("(%p,%d,%p,%d,%d,%p,%p) - stub!\n", lpUnknown, iUnk, pguidCmdGroup,
2223         nCmdID, nCmdexecopt, pvaIn, pvaOut);
2224   return DRAGDROP_E_NOTREGISTERED;
2225 }
2226 
2227 /*************************************************************************
2228  *      @	[SHLWAPI.202]
2229  *
2230  */
2231 HRESULT WINAPI IsQSForward(REFGUID pguidCmdGroup,ULONG cCmds, OLECMD *prgCmds)
2232 {
2233   FIXME("(%p,%d,%p) - stub!\n", pguidCmdGroup, cCmds, prgCmds);
2234   return DRAGDROP_E_NOTREGISTERED;
2235 }
2236 
2237 /*************************************************************************
2238  * @	[SHLWAPI.204]
2239  *
2240  * Determine if a window is not a child of another window.
2241  *
2242  * PARAMS
2243  * hParent [I] Suspected parent window
2244  * hChild  [I] Suspected child window
2245  *
2246  * RETURNS
2247  * TRUE:  If hChild is a child window of hParent
2248  * FALSE: If hChild is not a child window of hParent, or they are equal
2249  */
2250 BOOL WINAPI SHIsChildOrSelf(HWND hParent, HWND hChild)
2251 {
2252   TRACE("(%p,%p)\n", hParent, hChild);
2253 
2254   if (!hParent || !hChild)
2255     return TRUE;
2256   else if(hParent == hChild)
2257     return FALSE;
2258   return !IsChild(hParent, hChild);
2259 }
2260 
2261 /*************************************************************************
2262  *    FDSA functions.  Manage a dynamic array of fixed size memory blocks.
2263  */
2264 
2265 typedef struct
2266 {
2267     DWORD num_items;       /* Number of elements inserted */
2268     void *mem;             /* Ptr to array */
2269     DWORD blocks_alloced;  /* Number of elements allocated */
2270     BYTE inc;              /* Number of elements to grow by when we need to expand */
2271     BYTE block_size;       /* Size in bytes of an element */
2272     BYTE flags;            /* Flags */
2273 } FDSA_info;
2274 
2275 #define FDSA_FLAG_INTERNAL_ALLOC 0x01 /* When set we have allocated mem internally */
2276 
2277 /*************************************************************************
2278  *      @	[SHLWAPI.208]
2279  *
2280  * Initialize an FDSA array.
2281  */
2282 BOOL WINAPI FDSA_Initialize(DWORD block_size, DWORD inc, FDSA_info *info, void *mem,
2283                             DWORD init_blocks)
2284 {
2285     TRACE("(0x%08x 0x%08x %p %p 0x%08x)\n", block_size, inc, info, mem, init_blocks);
2286 
2287     if(inc == 0)
2288         inc = 1;
2289 
2290     if(mem)
2291         memset(mem, 0, block_size * init_blocks);
2292 
2293     info->num_items = 0;
2294     info->inc = inc;
2295     info->mem = mem;
2296     info->blocks_alloced = init_blocks;
2297     info->block_size = block_size;
2298     info->flags = 0;
2299 
2300     return TRUE;
2301 }
2302 
2303 /*************************************************************************
2304  *      @	[SHLWAPI.209]
2305  *
2306  * Destroy an FDSA array
2307  */
2308 BOOL WINAPI FDSA_Destroy(FDSA_info *info)
2309 {
2310     TRACE("(%p)\n", info);
2311 
2312     if(info->flags & FDSA_FLAG_INTERNAL_ALLOC)
2313     {
2314         HeapFree(GetProcessHeap(), 0, info->mem);
2315         return FALSE;
2316     }
2317 
2318     return TRUE;
2319 }
2320 
2321 /*************************************************************************
2322  *      @	[SHLWAPI.210]
2323  *
2324  * Insert element into an FDSA array
2325  */
2326 DWORD WINAPI FDSA_InsertItem(FDSA_info *info, DWORD where, const void *block)
2327 {
2328     TRACE("(%p 0x%08x %p)\n", info, where, block);
2329     if(where > info->num_items)
2330         where = info->num_items;
2331 
2332     if(info->num_items >= info->blocks_alloced)
2333     {
2334         DWORD size = (info->blocks_alloced + info->inc) * info->block_size;
2335         if(info->flags & 0x1)
2336             info->mem = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, info->mem, size);
2337         else
2338         {
2339             void *old_mem = info->mem;
2340             info->mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
2341             memcpy(info->mem, old_mem, info->blocks_alloced * info->block_size);
2342         }
2343         info->blocks_alloced += info->inc;
2344         info->flags |= 0x1;
2345     }
2346 
2347     if(where < info->num_items)
2348     {
2349         memmove((char*)info->mem + (where + 1) * info->block_size,
2350                 (char*)info->mem + where * info->block_size,
2351                 (info->num_items - where) * info->block_size);
2352     }
2353     memcpy((char*)info->mem + where * info->block_size, block, info->block_size);
2354 
2355     info->num_items++;
2356     return where;
2357 }
2358 
2359 /*************************************************************************
2360  *      @	[SHLWAPI.211]
2361  *
2362  * Delete an element from an FDSA array.
2363  */
2364 BOOL WINAPI FDSA_DeleteItem(FDSA_info *info, DWORD where)
2365 {
2366     TRACE("(%p 0x%08x)\n", info, where);
2367 
2368     if(where >= info->num_items)
2369         return FALSE;
2370 
2371     if(where < info->num_items - 1)
2372     {
2373         memmove((char*)info->mem + where * info->block_size,
2374                 (char*)info->mem + (where + 1) * info->block_size,
2375                 (info->num_items - where - 1) * info->block_size);
2376     }
2377     memset((char*)info->mem + (info->num_items - 1) * info->block_size,
2378            0, info->block_size);
2379     info->num_items--;
2380     return TRUE;
2381 }
2382 
2383 /*************************************************************************
2384  *      @	[SHLWAPI.219]
2385  *
2386  * Call IUnknown_QueryInterface() on a table of objects.
2387  *
2388  * RETURNS
2389  *  Success: S_OK.
2390  *  Failure: E_POINTER or E_NOINTERFACE.
2391  */
2392 HRESULT WINAPI QISearch(
2393 	void *base,         /* [in]   Table of interfaces */
2394 	const QITAB *table, /* [in]   Array of REFIIDs and indexes into the table */
2395 	REFIID riid,        /* [in]   REFIID to get interface for */
2396 	void **ppv)         /* [out]  Destination for interface pointer */
2397 {
2398 	HRESULT ret;
2399 	IUnknown *a_vtbl;
2400 	const QITAB *xmove;
2401 
2402 	TRACE("(%p %p %s %p)\n", base, table, debugstr_guid(riid), ppv);
2403 	if (ppv) {
2404 	    xmove = table;
2405 	    while (xmove->piid) {
2406 		TRACE("trying (offset %d) %s\n", xmove->dwOffset, debugstr_guid(xmove->piid));
2407 		if (IsEqualIID(riid, xmove->piid)) {
2408 		    a_vtbl = (IUnknown*)(xmove->dwOffset + (LPBYTE)base);
2409 		    TRACE("matched, returning (%p)\n", a_vtbl);
2410                     *ppv = a_vtbl;
2411 		    IUnknown_AddRef(a_vtbl);
2412 		    return S_OK;
2413 		}
2414 		xmove++;
2415 	    }
2416 
2417 	    if (IsEqualIID(riid, &IID_IUnknown)) {
2418 		a_vtbl = (IUnknown*)(table->dwOffset + (LPBYTE)base);
2419 		TRACE("returning first for IUnknown (%p)\n", a_vtbl);
2420                 *ppv = a_vtbl;
2421 		IUnknown_AddRef(a_vtbl);
2422 		return S_OK;
2423 	    }
2424 	    *ppv = 0;
2425 	    ret = E_NOINTERFACE;
2426 	} else
2427 	    ret = E_POINTER;
2428 
2429 	TRACE("-- 0x%08x\n", ret);
2430 	return ret;
2431 }
2432 
2433 /*************************************************************************
2434  * @ [SHLWAPI.220]
2435  *
2436  * Set the Font for a window and the "PropDlgFont" property of the parent window.
2437  *
2438  * PARAMS
2439  *  hWnd [I] Parent Window to set the property
2440  *  id   [I] Index of child Window to set the Font
2441  *
2442  * RETURNS
2443  *  Success: S_OK
2444  *
2445  */
2446 HRESULT WINAPI SHSetDefaultDialogFont(HWND hWnd, INT id)
2447 {
2448     FIXME("(%p, %d) stub\n", hWnd, id);
2449     return S_OK;
2450 }
2451 
2452 /*************************************************************************
2453  *      @	[SHLWAPI.221]
2454  *
2455  * Remove the "PropDlgFont" property from a window.
2456  *
2457  * PARAMS
2458  *  hWnd [I] Window to remove the property from
2459  *
2460  * RETURNS
2461  *  A handle to the removed property, or NULL if it did not exist.
2462  */
2463 HANDLE WINAPI SHRemoveDefaultDialogFont(HWND hWnd)
2464 {
2465   HANDLE hProp;
2466 
2467   TRACE("(%p)\n", hWnd);
2468 
2469   hProp = GetPropA(hWnd, "PropDlgFont");
2470 
2471   if(hProp)
2472   {
2473     DeleteObject(hProp);
2474     hProp = RemovePropA(hWnd, "PropDlgFont");
2475   }
2476   return hProp;
2477 }
2478 
2479 /*************************************************************************
2480  *      @	[SHLWAPI.236]
2481  *
2482  * Load the in-process server of a given GUID.
2483  *
2484  * PARAMS
2485  *  refiid [I] GUID of the server to load.
2486  *
2487  * RETURNS
2488  *  Success: A handle to the loaded server dll.
2489  *  Failure: A NULL handle.
2490  */
2491 HMODULE WINAPI SHPinDllOfCLSID(REFIID refiid)
2492 {
2493     HKEY newkey;
2494     DWORD type, count;
2495     CHAR value[MAX_PATH], string[MAX_PATH];
2496 
2497     strcpy(string, "CLSID\\");
2498     SHStringFromGUIDA(refiid, string + 6, sizeof(string)/sizeof(char) - 6);
2499     strcat(string, "\\InProcServer32");
2500 
2501     count = MAX_PATH;
2502     RegOpenKeyExA(HKEY_CLASSES_ROOT, string, 0, 1, &newkey);
2503     RegQueryValueExA(newkey, 0, 0, &type, (PBYTE)value, &count);
2504     RegCloseKey(newkey);
2505     return LoadLibraryExA(value, 0, 0);
2506 }
2507 
2508 /*************************************************************************
2509  *      @	[SHLWAPI.237]
2510  *
2511  * Unicode version of SHLWAPI_183.
2512  */
2513 DWORD WINAPI SHRegisterClassW(WNDCLASSW * lpWndClass)
2514 {
2515 	WNDCLASSW WndClass;
2516 
2517 	TRACE("(%p %s)\n",lpWndClass->hInstance, debugstr_w(lpWndClass->lpszClassName));
2518 
2519 	if (GetClassInfoW(lpWndClass->hInstance, lpWndClass->lpszClassName, &WndClass))
2520 		return TRUE;
2521 	return RegisterClassW(lpWndClass);
2522 }
2523 
2524 /*************************************************************************
2525  *      @	[SHLWAPI.238]
2526  *
2527  * Unregister a list of classes.
2528  *
2529  * PARAMS
2530  *  hInst      [I] Application instance that registered the classes
2531  *  lppClasses [I] List of class names
2532  *  iCount     [I] Number of names in lppClasses
2533  *
2534  * RETURNS
2535  *  Nothing.
2536  */
2537 void WINAPI SHUnregisterClassesA(HINSTANCE hInst, LPCSTR *lppClasses, INT iCount)
2538 {
2539   WNDCLASSA WndClass;
2540 
2541   TRACE("(%p,%p,%d)\n", hInst, lppClasses, iCount);
2542 
2543   while (iCount > 0)
2544   {
2545     if (GetClassInfoA(hInst, *lppClasses, &WndClass))
2546       UnregisterClassA(*lppClasses, hInst);
2547     lppClasses++;
2548     iCount--;
2549   }
2550 }
2551 
2552 /*************************************************************************
2553  *      @	[SHLWAPI.239]
2554  *
2555  * Unicode version of SHUnregisterClassesA.
2556  */
2557 void WINAPI SHUnregisterClassesW(HINSTANCE hInst, LPCWSTR *lppClasses, INT iCount)
2558 {
2559   WNDCLASSW WndClass;
2560 
2561   TRACE("(%p,%p,%d)\n", hInst, lppClasses, iCount);
2562 
2563   while (iCount > 0)
2564   {
2565     if (GetClassInfoW(hInst, *lppClasses, &WndClass))
2566       UnregisterClassW(*lppClasses, hInst);
2567     lppClasses++;
2568     iCount--;
2569   }
2570 }
2571 
2572 /*************************************************************************
2573  *      @	[SHLWAPI.240]
2574  *
2575  * Call The correct (Ascii/Unicode) default window procedure for a window.
2576  *
2577  * PARAMS
2578  *  hWnd     [I] Window to call the default procedure for
2579  *  uMessage [I] Message ID
2580  *  wParam   [I] WPARAM of message
2581  *  lParam   [I] LPARAM of message
2582  *
2583  * RETURNS
2584  *  The result of calling DefWindowProcA() or DefWindowProcW().
2585  */
2586 LRESULT CALLBACK SHDefWindowProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
2587 {
2588 	if (IsWindowUnicode(hWnd))
2589 		return DefWindowProcW(hWnd, uMessage, wParam, lParam);
2590 	return DefWindowProcA(hWnd, uMessage, wParam, lParam);
2591 }
2592 
2593 /*************************************************************************
2594  *      @       [SHLWAPI.256]
2595  */
2596 HRESULT WINAPI IUnknown_GetSite(LPUNKNOWN lpUnknown, REFIID iid, PVOID *lppSite)
2597 {
2598   HRESULT hRet = E_INVALIDARG;
2599   LPOBJECTWITHSITE lpSite = NULL;
2600 
2601   TRACE("(%p,%s,%p)\n", lpUnknown, debugstr_guid(iid), lppSite);
2602 
2603   if (lpUnknown && iid && lppSite)
2604   {
2605     hRet = IUnknown_QueryInterface(lpUnknown, &IID_IObjectWithSite,
2606                                    (void**)&lpSite);
2607     if (SUCCEEDED(hRet) && lpSite)
2608     {
2609       hRet = IObjectWithSite_GetSite(lpSite, iid, lppSite);
2610       IObjectWithSite_Release(lpSite);
2611     }
2612   }
2613   return hRet;
2614 }
2615 
2616 /*************************************************************************
2617  *      @	[SHLWAPI.257]
2618  *
2619  * Create a worker window using CreateWindowExA().
2620  *
2621  * PARAMS
2622  *  wndProc    [I] Window procedure
2623  *  hWndParent [I] Parent window
2624  *  dwExStyle  [I] Extra style flags
2625  *  dwStyle    [I] Style flags
2626  *  hMenu      [I] Window menu
2627  *  wnd_extra  [I] Window extra bytes value
2628  *
2629  * RETURNS
2630  *  Success: The window handle of the newly created window.
2631  *  Failure: 0.
2632  */
2633 HWND WINAPI SHCreateWorkerWindowA(WNDPROC wndProc, HWND hWndParent, DWORD dwExStyle,
2634                                   DWORD dwStyle, HMENU hMenu, LONG_PTR wnd_extra)
2635 {
2636   static const char szClass[] = "WorkerA";
2637   WNDCLASSA wc;
2638   HWND hWnd;
2639 
2640   TRACE("(%p, %p, 0x%08x, 0x%08x, %p, 0x%08lx)\n",
2641          wndProc, hWndParent, dwExStyle, dwStyle, hMenu, wnd_extra);
2642 
2643   /* Create Window class */
2644   wc.style         = 0;
2645   wc.lpfnWndProc   = DefWindowProcA;
2646   wc.cbClsExtra    = 0;
2647   wc.cbWndExtra    = sizeof(LONG_PTR);
2648   wc.hInstance     = shlwapi_hInstance;
2649   wc.hIcon         = NULL;
2650   wc.hCursor       = LoadCursorA(NULL, (LPSTR)IDC_ARROW);
2651   wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
2652   wc.lpszMenuName  = NULL;
2653   wc.lpszClassName = szClass;
2654 
2655   SHRegisterClassA(&wc);
2656 
2657   hWnd = CreateWindowExA(dwExStyle, szClass, 0, dwStyle, 0, 0, 0, 0,
2658                          hWndParent, hMenu, shlwapi_hInstance, 0);
2659   if (hWnd)
2660   {
2661     SetWindowLongPtrW(hWnd, 0, wnd_extra);
2662     if (wndProc) SetWindowLongPtrA(hWnd, GWLP_WNDPROC, (LONG_PTR)wndProc);
2663   }
2664 
2665   return hWnd;
2666 }
2667 
2668 typedef struct tagPOLICYDATA
2669 {
2670   DWORD policy;        /* flags value passed to SHRestricted */
2671   LPCWSTR appstr;      /* application str such as "Explorer" */
2672   LPCWSTR keystr;      /* name of the actual registry key / policy */
2673 } POLICYDATA, *LPPOLICYDATA;
2674 
2675 #define SHELL_NO_POLICY 0xffffffff
2676 
2677 /* default shell policy registry key */
2678 static const WCHAR strRegistryPolicyW[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o',
2679                                       's','o','f','t','\\','W','i','n','d','o','w','s','\\',
2680                                       'C','u','r','r','e','n','t','V','e','r','s','i','o','n',
2681                                       '\\','P','o','l','i','c','i','e','s',0};
2682 
2683 /*************************************************************************
2684  * @                          [SHLWAPI.271]
2685  *
2686  * Retrieve a policy value from the registry.
2687  *
2688  * PARAMS
2689  *  lpSubKey   [I]   registry key name
2690  *  lpSubName  [I]   subname of registry key
2691  *  lpValue    [I]   value name of registry value
2692  *
2693  * RETURNS
2694  *  the value associated with the registry key or 0 if not found
2695  */
2696 DWORD WINAPI SHGetRestriction(LPCWSTR lpSubKey, LPCWSTR lpSubName, LPCWSTR lpValue)
2697 {
2698 	DWORD retval, datsize = sizeof(retval);
2699 	HKEY hKey;
2700 
2701 	if (!lpSubKey)
2702 	  lpSubKey = strRegistryPolicyW;
2703 
2704 	retval = RegOpenKeyW(HKEY_LOCAL_MACHINE, lpSubKey, &hKey);
2705         if (retval != ERROR_SUCCESS)
2706 	  retval = RegOpenKeyW(HKEY_CURRENT_USER, lpSubKey, &hKey);
2707 	if (retval != ERROR_SUCCESS)
2708 	  return 0;
2709 
2710         SHGetValueW(hKey, lpSubName, lpValue, NULL, &retval, &datsize);
2711 	RegCloseKey(hKey);
2712 	return retval;
2713 }
2714 
2715 /*************************************************************************
2716  * @                         [SHLWAPI.266]
2717  *
2718  * Helper function to retrieve the possibly cached value for a specific policy
2719  *
2720  * PARAMS
2721  *  policy     [I]   The policy to look for
2722  *  initial    [I]   Main registry key to open, if NULL use default
2723  *  polTable   [I]   Table of known policies, 0 terminated
2724  *  polArr     [I]   Cache array of policy values
2725  *
2726  * RETURNS
2727  *  The retrieved policy value or 0 if not successful
2728  *
2729  * NOTES
2730  *  This function is used by the native SHRestricted function to search for the
2731  *  policy and cache it once retrieved. The current Wine implementation uses a
2732  *  different POLICYDATA structure and implements a similar algorithm adapted to
2733  *  that structure.
2734  */
2735 DWORD WINAPI SHRestrictionLookup(
2736 	DWORD policy,
2737 	LPCWSTR initial,
2738 	LPPOLICYDATA polTable,
2739 	LPDWORD polArr)
2740 {
2741 	TRACE("(0x%08x %s %p %p)\n", policy, debugstr_w(initial), polTable, polArr);
2742 
2743 	if (!polTable || !polArr)
2744 	  return 0;
2745 
2746 	for (;polTable->policy; polTable++, polArr++)
2747 	{
2748 	  if (policy == polTable->policy)
2749 	  {
2750 	    /* we have a known policy */
2751 
2752 	    /* check if this policy has been cached */
2753             if (*polArr == SHELL_NO_POLICY)
2754 	      *polArr = SHGetRestriction(initial, polTable->appstr, polTable->keystr);
2755 	    return *polArr;
2756 	  }
2757 	}
2758 	/* we don't know this policy, return 0 */
2759 	TRACE("unknown policy: (%08x)\n", policy);
2760 	return 0;
2761 }
2762 
2763 /*************************************************************************
2764  *      @	[SHLWAPI.267]
2765  *
2766  * Get an interface from an object.
2767  *
2768  * RETURNS
2769  *  Success: S_OK. ppv contains the requested interface.
2770  *  Failure: An HRESULT error code.
2771  *
2772  * NOTES
2773  *   This QueryInterface asks the inner object for an interface. In case
2774  *   of aggregation this request would be forwarded by the inner to the
2775  *   outer object. This function asks the inner object directly for the
2776  *   interface circumventing the forwarding to the outer object.
2777  */
2778 HRESULT WINAPI SHWeakQueryInterface(
2779 	IUnknown * pUnk,   /* [in] Outer object */
2780 	IUnknown * pInner, /* [in] Inner object */
2781 	IID * riid, /* [in] Interface GUID to query for */
2782 	LPVOID* ppv) /* [out] Destination for queried interface */
2783 {
2784 	HRESULT hret = E_NOINTERFACE;
2785 	TRACE("(pUnk=%p pInner=%p\n\tIID:  %s %p)\n",pUnk,pInner,debugstr_guid(riid), ppv);
2786 
2787 	*ppv = NULL;
2788 	if(pUnk && pInner) {
2789             hret = IUnknown_QueryInterface(pInner, riid, ppv);
2790 	    if (SUCCEEDED(hret)) IUnknown_Release(pUnk);
2791 	}
2792 	TRACE("-- 0x%08x\n", hret);
2793 	return hret;
2794 }
2795 
2796 /*************************************************************************
2797  *      @	[SHLWAPI.268]
2798  *
2799  * Move a reference from one interface to another.
2800  *
2801  * PARAMS
2802  *   lpDest     [O] Destination to receive the reference
2803  *   lppUnknown [O] Source to give up the reference to lpDest
2804  *
2805  * RETURNS
2806  *  Nothing.
2807  */
2808 VOID WINAPI SHWeakReleaseInterface(IUnknown *lpDest, IUnknown **lppUnknown)
2809 {
2810   TRACE("(%p,%p)\n", lpDest, lppUnknown);
2811 
2812   if (*lppUnknown)
2813   {
2814     /* Copy Reference*/
2815     IUnknown_AddRef(lpDest);
2816     IUnknown_AtomicRelease(lppUnknown); /* Release existing interface */
2817   }
2818 }
2819 
2820 /*************************************************************************
2821  *      @	[SHLWAPI.269]
2822  *
2823  * Convert an ASCII string of a CLSID into a CLSID.
2824  *
2825  * PARAMS
2826  *  idstr [I] String representing a CLSID in registry format
2827  *  id    [O] Destination for the converted CLSID
2828  *
2829  * RETURNS
2830  *  Success: TRUE. id contains the converted CLSID.
2831  *  Failure: FALSE.
2832  */
2833 BOOL WINAPI GUIDFromStringA(LPCSTR idstr, CLSID *id)
2834 {
2835   WCHAR wClsid[40];
2836   MultiByteToWideChar(CP_ACP, 0, idstr, -1, wClsid, sizeof(wClsid)/sizeof(WCHAR));
2837   return SUCCEEDED(CLSIDFromString(wClsid, id));
2838 }
2839 
2840 /*************************************************************************
2841  *      @	[SHLWAPI.270]
2842  *
2843  * Unicode version of GUIDFromStringA.
2844  */
2845 BOOL WINAPI GUIDFromStringW(LPCWSTR idstr, CLSID *id)
2846 {
2847     return SUCCEEDED(CLSIDFromString((LPCOLESTR)idstr, id));
2848 }
2849 
2850 /*************************************************************************
2851  *      @	[SHLWAPI.276]
2852  *
2853  * Determine if the browser is integrated into the shell, and set a registry
2854  * key accordingly.
2855  *
2856  * PARAMS
2857  *  None.
2858  *
2859  * RETURNS
2860  *  1, If the browser is not integrated.
2861  *  2, If the browser is integrated.
2862  *
2863  * NOTES
2864  *  The key "HKLM\Software\Microsoft\Internet Explorer\IntegratedBrowser" is
2865  *  either set to TRUE, or removed depending on whether the browser is deemed
2866  *  to be integrated.
2867  */
2868 DWORD WINAPI WhichPlatform(void)
2869 {
2870   static const char szIntegratedBrowser[] = "IntegratedBrowser";
2871   static DWORD dwState = 0;
2872   HKEY hKey;
2873   DWORD dwRet, dwData, dwSize;
2874   HMODULE hshell32;
2875 
2876   if (dwState)
2877     return dwState;
2878 
2879   /* If shell32 exports DllGetVersion(), the browser is integrated */
2880   dwState = 1;
2881   hshell32 = LoadLibraryA("shell32.dll");
2882   if (hshell32)
2883   {
2884     FARPROC pDllGetVersion;
2885     pDllGetVersion = GetProcAddress(hshell32, "DllGetVersion");
2886     dwState = pDllGetVersion ? 2 : 1;
2887     FreeLibrary(hshell32);
2888   }
2889 
2890   /* Set or delete the key accordingly */
2891   dwRet = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
2892                         "Software\\Microsoft\\Internet Explorer", 0,
2893                          KEY_ALL_ACCESS, &hKey);
2894   if (!dwRet)
2895   {
2896     dwRet = RegQueryValueExA(hKey, szIntegratedBrowser, 0, 0,
2897                              (LPBYTE)&dwData, &dwSize);
2898 
2899     if (!dwRet && dwState == 1)
2900     {
2901       /* Value exists but browser is not integrated */
2902       RegDeleteValueA(hKey, szIntegratedBrowser);
2903     }
2904     else if (dwRet && dwState == 2)
2905     {
2906       /* Browser is integrated but value does not exist */
2907       dwData = TRUE;
2908       RegSetValueExA(hKey, szIntegratedBrowser, 0, REG_DWORD,
2909                      (LPBYTE)&dwData, sizeof(dwData));
2910     }
2911     RegCloseKey(hKey);
2912   }
2913   return dwState;
2914 }
2915 
2916 /*************************************************************************
2917  *      @	[SHLWAPI.278]
2918  *
2919  * Unicode version of SHCreateWorkerWindowA.
2920  */
2921 HWND WINAPI SHCreateWorkerWindowW(WNDPROC wndProc, HWND hWndParent, DWORD dwExStyle,
2922                                   DWORD dwStyle, HMENU hMenu, LONG_PTR wnd_extra)
2923 {
2924   static const WCHAR szClass[] = { 'W', 'o', 'r', 'k', 'e', 'r', 'W', 0 };
2925   WNDCLASSW wc;
2926   HWND hWnd;
2927 
2928   TRACE("(%p, %p, 0x%08x, 0x%08x, %p, 0x%08lx)\n",
2929          wndProc, hWndParent, dwExStyle, dwStyle, hMenu, wnd_extra);
2930 
2931   /* If our OS is natively ANSI, use the ANSI version */
2932   if (GetVersion() & 0x80000000)  /* not NT */
2933   {
2934     TRACE("fallback to ANSI, ver 0x%08x\n", GetVersion());
2935     return SHCreateWorkerWindowA(wndProc, hWndParent, dwExStyle, dwStyle, hMenu, wnd_extra);
2936   }
2937 
2938   /* Create Window class */
2939   wc.style         = 0;
2940   wc.lpfnWndProc   = DefWindowProcW;
2941   wc.cbClsExtra    = 0;
2942   wc.cbWndExtra    = sizeof(LONG_PTR);
2943   wc.hInstance     = shlwapi_hInstance;
2944   wc.hIcon         = NULL;
2945   wc.hCursor       = LoadCursorW(NULL, (LPWSTR)IDC_ARROW);
2946   wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
2947   wc.lpszMenuName  = NULL;
2948   wc.lpszClassName = szClass;
2949 
2950   SHRegisterClassW(&wc);
2951 
2952   hWnd = CreateWindowExW(dwExStyle, szClass, 0, dwStyle, 0, 0, 0, 0,
2953                          hWndParent, hMenu, shlwapi_hInstance, 0);
2954   if (hWnd)
2955   {
2956     SetWindowLongPtrW(hWnd, 0, wnd_extra);
2957     if (wndProc) SetWindowLongPtrW(hWnd, GWLP_WNDPROC, (LONG_PTR)wndProc);
2958   }
2959 
2960   return hWnd;
2961 }
2962 
2963 /*************************************************************************
2964  *      @	[SHLWAPI.279]
2965  *
2966  * Get and show a context menu from a shell folder.
2967  *
2968  * PARAMS
2969  *  hWnd           [I] Window displaying the shell folder
2970  *  lpFolder       [I] IShellFolder interface
2971  *  lpApidl        [I] Id for the particular folder desired
2972  *
2973  * RETURNS
2974  *  Success: S_OK.
2975  *  Failure: An HRESULT error code indicating the error.
2976  */
2977 HRESULT WINAPI SHInvokeDefaultCommand(HWND hWnd, IShellFolder* lpFolder, LPCITEMIDLIST lpApidl)
2978 {
2979     TRACE("%p %p %p\n", hWnd, lpFolder, lpApidl);
2980     return SHInvokeCommand(hWnd, lpFolder, lpApidl, 0);
2981 }
2982 
2983 /*************************************************************************
2984  *      @	[SHLWAPI.281]
2985  *
2986  * _SHPackDispParamsV
2987  */
2988 HRESULT WINAPI SHPackDispParamsV(DISPPARAMS *params, VARIANTARG *args, UINT cnt, __ms_va_list valist)
2989 {
2990   VARIANTARG *iter;
2991 
2992   TRACE("(%p %p %u ...)\n", params, args, cnt);
2993 
2994   params->rgvarg = args;
2995   params->rgdispidNamedArgs = NULL;
2996   params->cArgs = cnt;
2997   params->cNamedArgs = 0;
2998 
2999   iter = args+cnt;
3000 
3001   while(iter-- > args) {
3002     V_VT(iter) = va_arg(valist, enum VARENUM);
3003 
3004     TRACE("vt=%d\n", V_VT(iter));
3005 
3006     if(V_VT(iter) & VT_BYREF) {
3007       V_BYREF(iter) = va_arg(valist, LPVOID);
3008     } else {
3009       switch(V_VT(iter)) {
3010       case VT_I4:
3011         V_I4(iter) = va_arg(valist, LONG);
3012         break;
3013       case VT_BSTR:
3014         V_BSTR(iter) = va_arg(valist, BSTR);
3015         break;
3016       case VT_DISPATCH:
3017         V_DISPATCH(iter) = va_arg(valist, IDispatch*);
3018         break;
3019       case VT_BOOL:
3020         V_BOOL(iter) = va_arg(valist, int);
3021         break;
3022       case VT_UNKNOWN:
3023         V_UNKNOWN(iter) = va_arg(valist, IUnknown*);
3024         break;
3025       default:
3026         V_VT(iter) = VT_I4;
3027         V_I4(iter) = va_arg(valist, LONG);
3028       }
3029     }
3030   }
3031 
3032   return S_OK;
3033 }
3034 
3035 /*************************************************************************
3036  *      @       [SHLWAPI.282]
3037  *
3038  * SHPackDispParams
3039  */
3040 HRESULT WINAPIV SHPackDispParams(DISPPARAMS *params, VARIANTARG *args, UINT cnt, ...)
3041 {
3042   __ms_va_list valist;
3043   HRESULT hres;
3044 
3045   __ms_va_start(valist, cnt);
3046   hres = SHPackDispParamsV(params, args, cnt, valist);
3047   __ms_va_end(valist);
3048   return hres;
3049 }
3050 
3051 /*************************************************************************
3052  *      SHLWAPI_InvokeByIID
3053  *
3054  *   This helper function calls IDispatch::Invoke for each sink
3055  * which implements given iid or IDispatch.
3056  *
3057  */
3058 static HRESULT SHLWAPI_InvokeByIID(
3059         IConnectionPoint* iCP,
3060         REFIID iid,
3061         DISPID dispId,
3062         DISPPARAMS* dispParams)
3063 {
3064   IEnumConnections *enumerator;
3065   CONNECTDATA rgcd;
3066   static DISPPARAMS empty = {NULL, NULL, 0, 0};
3067   DISPPARAMS* params = dispParams;
3068 
3069   HRESULT result = IConnectionPoint_EnumConnections(iCP, &enumerator);
3070   if (FAILED(result))
3071     return result;
3072 
3073   /* Invoke is never happening with an NULL dispParams */
3074   if (!params)
3075     params = &empty;
3076 
3077   while(IEnumConnections_Next(enumerator, 1, &rgcd, NULL)==S_OK)
3078   {
3079     IDispatch *dispIface;
3080     if ((iid && SUCCEEDED(IUnknown_QueryInterface(rgcd.pUnk, iid, (LPVOID*)&dispIface))) ||
3081         SUCCEEDED(IUnknown_QueryInterface(rgcd.pUnk, &IID_IDispatch, (LPVOID*)&dispIface)))
3082     {
3083       IDispatch_Invoke(dispIface, dispId, &IID_NULL, 0, DISPATCH_METHOD, params, NULL, NULL, NULL);
3084       IDispatch_Release(dispIface);
3085     }
3086     IUnknown_Release(rgcd.pUnk);
3087   }
3088 
3089   IEnumConnections_Release(enumerator);
3090 
3091   return S_OK;
3092 }
3093 
3094 /*************************************************************************
3095  *  IConnectionPoint_InvokeWithCancel   [SHLWAPI.283]
3096  */
3097 HRESULT WINAPI IConnectionPoint_InvokeWithCancel( IConnectionPoint* iCP,
3098                                                   DISPID dispId, DISPPARAMS* dispParams,
3099                                                   DWORD unknown1, DWORD unknown2 )
3100 {
3101     IID iid;
3102     HRESULT result;
3103 
3104     FIXME("(%p)->(0x%x %p %x %x) partial stub\n", iCP, dispId, dispParams, unknown1, unknown2);
3105 
3106     result = IConnectionPoint_GetConnectionInterface(iCP, &iid);
3107     if (SUCCEEDED(result))
3108         result = SHLWAPI_InvokeByIID(iCP, &iid, dispId, dispParams);
3109     else
3110         result = SHLWAPI_InvokeByIID(iCP, NULL, dispId, dispParams);
3111 
3112     return result;
3113 }
3114 
3115 
3116 /*************************************************************************
3117  *      @	[SHLWAPI.284]
3118  *
3119  *  IConnectionPoint_SimpleInvoke
3120  */
3121 HRESULT WINAPI IConnectionPoint_SimpleInvoke(
3122         IConnectionPoint* iCP,
3123         DISPID dispId,
3124         DISPPARAMS* dispParams)
3125 {
3126   IID iid;
3127   HRESULT result;
3128 
3129   TRACE("(%p)->(0x%x %p)\n",iCP,dispId,dispParams);
3130 
3131   result = IConnectionPoint_GetConnectionInterface(iCP, &iid);
3132   if (SUCCEEDED(result))
3133     result = SHLWAPI_InvokeByIID(iCP, &iid, dispId, dispParams);
3134   else
3135     result = SHLWAPI_InvokeByIID(iCP, NULL, dispId, dispParams);
3136 
3137   return result;
3138 }
3139 
3140 /*************************************************************************
3141  *      @	[SHLWAPI.285]
3142  *
3143  * Notify an IConnectionPoint object of changes.
3144  *
3145  * PARAMS
3146  *  lpCP   [I] Object to notify
3147  *  dispID [I]
3148  *
3149  * RETURNS
3150  *  Success: S_OK.
3151  *  Failure: E_NOINTERFACE, if lpCP is NULL or does not support the
3152  *           IConnectionPoint interface.
3153  */
3154 HRESULT WINAPI IConnectionPoint_OnChanged(IConnectionPoint* lpCP, DISPID dispID)
3155 {
3156   IEnumConnections *lpEnum;
3157   HRESULT hRet = E_NOINTERFACE;
3158 
3159   TRACE("(%p,0x%8X)\n", lpCP, dispID);
3160 
3161   /* Get an enumerator for the connections */
3162   if (lpCP)
3163     hRet = IConnectionPoint_EnumConnections(lpCP, &lpEnum);
3164 
3165   if (SUCCEEDED(hRet))
3166   {
3167     IPropertyNotifySink *lpSink;
3168     CONNECTDATA connData;
3169     ULONG ulFetched;
3170 
3171     /* Call OnChanged() for every notify sink in the connection point */
3172     while (IEnumConnections_Next(lpEnum, 1, &connData, &ulFetched) == S_OK)
3173     {
3174       if (SUCCEEDED(IUnknown_QueryInterface(connData.pUnk, &IID_IPropertyNotifySink, (void**)&lpSink)) &&
3175           lpSink)
3176       {
3177         IPropertyNotifySink_OnChanged(lpSink, dispID);
3178         IPropertyNotifySink_Release(lpSink);
3179       }
3180       IUnknown_Release(connData.pUnk);
3181     }
3182 
3183     IEnumConnections_Release(lpEnum);
3184   }
3185   return hRet;
3186 }
3187 
3188 /*************************************************************************
3189  *      @	[SHLWAPI.286]
3190  *
3191  *  IUnknown_CPContainerInvokeParam
3192  */
3193 HRESULT WINAPIV IUnknown_CPContainerInvokeParam(
3194         IUnknown *container,
3195         REFIID riid,
3196         DISPID dispId,
3197         VARIANTARG* buffer,
3198         DWORD cParams, ...)
3199 {
3200   HRESULT result;
3201   IConnectionPoint *iCP;
3202   IConnectionPointContainer *iCPC;
3203   DISPPARAMS dispParams = {buffer, NULL, cParams, 0};
3204   __ms_va_list valist;
3205 
3206   if (!container)
3207     return E_NOINTERFACE;
3208 
3209   result = IUnknown_QueryInterface(container, &IID_IConnectionPointContainer,(LPVOID*) &iCPC);
3210   if (FAILED(result))
3211       return result;
3212 
3213   result = IConnectionPointContainer_FindConnectionPoint(iCPC, riid, &iCP);
3214   IConnectionPointContainer_Release(iCPC);
3215   if(FAILED(result))
3216       return result;
3217 
3218   __ms_va_start(valist, cParams);
3219   SHPackDispParamsV(&dispParams, buffer, cParams, valist);
3220   __ms_va_end(valist);
3221 
3222   result = SHLWAPI_InvokeByIID(iCP, riid, dispId, &dispParams);
3223   IConnectionPoint_Release(iCP);
3224 
3225   return result;
3226 }
3227 
3228 /*************************************************************************
3229  *      @	[SHLWAPI.287]
3230  *
3231  * Notify an IConnectionPointContainer object of changes.
3232  *
3233  * PARAMS
3234  *  lpUnknown [I] Object to notify
3235  *  dispID    [I]
3236  *
3237  * RETURNS
3238  *  Success: S_OK.
3239  *  Failure: E_NOINTERFACE, if lpUnknown is NULL or does not support the
3240  *           IConnectionPointContainer interface.
3241  */
3242 HRESULT WINAPI IUnknown_CPContainerOnChanged(IUnknown *lpUnknown, DISPID dispID)
3243 {
3244   IConnectionPointContainer* lpCPC = NULL;
3245   HRESULT hRet = E_NOINTERFACE;
3246 
3247   TRACE("(%p,0x%8X)\n", lpUnknown, dispID);
3248 
3249   if (lpUnknown)
3250     hRet = IUnknown_QueryInterface(lpUnknown, &IID_IConnectionPointContainer, (void**)&lpCPC);
3251 
3252   if (SUCCEEDED(hRet))
3253   {
3254     IConnectionPoint* lpCP;
3255 
3256     hRet = IConnectionPointContainer_FindConnectionPoint(lpCPC, &IID_IPropertyNotifySink, &lpCP);
3257     IConnectionPointContainer_Release(lpCPC);
3258 
3259     hRet = IConnectionPoint_OnChanged(lpCP, dispID);
3260     IConnectionPoint_Release(lpCP);
3261   }
3262   return hRet;
3263 }
3264 
3265 /*************************************************************************
3266  *      @	[SHLWAPI.289]
3267  *
3268  * See PlaySoundW.
3269  */
3270 BOOL WINAPI PlaySoundWrapW(LPCWSTR pszSound, HMODULE hmod, DWORD fdwSound)
3271 {
3272     return PlaySoundW(pszSound, hmod, fdwSound);
3273 }
3274 
3275 #ifndef __REACTOS__ /* See propbag.cpp */
3276 /*************************************************************************
3277  *      @	[SHLWAPI.294]
3278  *
3279  * Retrieve a key value from an INI file.  See GetPrivateProfileString for
3280  * more information.
3281  *
3282  * PARAMS
3283  *  appName   [I] The section in the INI file that contains the key
3284  *  keyName   [I] The key to be retrieved
3285  *  out       [O] The buffer into which the key's value will be copied
3286  *  outLen    [I] The length of the `out' buffer
3287  *  filename  [I] The location of the INI file
3288  *
3289  * RETURNS
3290  *  Length of string copied into `out'.
3291  */
3292 DWORD WINAPI SHGetIniStringW(LPCWSTR appName, LPCWSTR keyName, LPWSTR out,
3293         DWORD outLen, LPCWSTR filename)
3294 {
3295     INT ret;
3296     WCHAR *buf;
3297 
3298     TRACE("(%s,%s,%p,%08x,%s)\n", debugstr_w(appName), debugstr_w(keyName),
3299         out, outLen, debugstr_w(filename));
3300 
3301     if(outLen == 0)
3302         return 0;
3303 
3304     buf = HeapAlloc(GetProcessHeap(), 0, outLen * sizeof(WCHAR));
3305     if(!buf){
3306         *out = 0;
3307         return 0;
3308     }
3309 
3310     ret = GetPrivateProfileStringW(appName, keyName, NULL, buf, outLen, filename);
3311     if(ret)
3312         strcpyW(out, buf);
3313     else
3314         *out = 0;
3315 
3316     HeapFree(GetProcessHeap(), 0, buf);
3317 
3318     return strlenW(out);
3319 }
3320 #endif
3321 
3322 #ifndef __REACTOS__ /* See propbag.cpp */
3323 /*************************************************************************
3324  *      @	[SHLWAPI.295]
3325  *
3326  * Set a key value in an INI file.  See WritePrivateProfileString for
3327  * more information.
3328  *
3329  * PARAMS
3330  *  appName   [I] The section in the INI file that contains the key
3331  *  keyName   [I] The key to be set
3332  *  str       [O] The value of the key
3333  *  filename  [I] The location of the INI file
3334  *
3335  * RETURNS
3336  *   Success: TRUE
3337  *   Failure: FALSE
3338  */
3339 BOOL WINAPI SHSetIniStringW(LPCWSTR appName, LPCWSTR keyName, LPCWSTR str,
3340         LPCWSTR filename)
3341 {
3342     TRACE("(%s, %p, %s, %s)\n", debugstr_w(appName), keyName, debugstr_w(str),
3343             debugstr_w(filename));
3344 
3345     return WritePrivateProfileStringW(appName, keyName, str, filename);
3346 }
3347 #endif
3348 
3349 /*************************************************************************
3350  *      @	[SHLWAPI.313]
3351  *
3352  * See SHGetFileInfoW.
3353  */
3354 DWORD WINAPI SHGetFileInfoWrapW(LPCWSTR path, DWORD dwFileAttributes,
3355                          SHFILEINFOW *psfi, UINT sizeofpsfi, UINT flags)
3356 {
3357     return SHGetFileInfoW(path, dwFileAttributes, psfi, sizeofpsfi, flags);
3358 }
3359 
3360 /*************************************************************************
3361  *      @	[SHLWAPI.318]
3362  *
3363  * See DragQueryFileW.
3364  */
3365 UINT WINAPI DragQueryFileWrapW(HDROP hDrop, UINT lFile, LPWSTR lpszFile, UINT lLength)
3366 {
3367     return DragQueryFileW(hDrop, lFile, lpszFile, lLength);
3368 }
3369 
3370 /*************************************************************************
3371  *      @	[SHLWAPI.333]
3372  *
3373  * See SHBrowseForFolderW.
3374  */
3375 LPITEMIDLIST WINAPI SHBrowseForFolderWrapW(LPBROWSEINFOW lpBi)
3376 {
3377     return SHBrowseForFolderW(lpBi);
3378 }
3379 
3380 /*************************************************************************
3381  *      @	[SHLWAPI.334]
3382  *
3383  * See SHGetPathFromIDListW.
3384  */
3385 BOOL WINAPI SHGetPathFromIDListWrapW(LPCITEMIDLIST pidl,LPWSTR pszPath)
3386 {
3387     return SHGetPathFromIDListW(pidl, pszPath);
3388 }
3389 
3390 /*************************************************************************
3391  *      @	[SHLWAPI.335]
3392  *
3393  * See ShellExecuteExW.
3394  */
3395 BOOL WINAPI ShellExecuteExWrapW(LPSHELLEXECUTEINFOW lpExecInfo)
3396 {
3397     return ShellExecuteExW(lpExecInfo);
3398 }
3399 
3400 /*************************************************************************
3401  *      @	[SHLWAPI.336]
3402  *
3403  * See SHFileOperationW.
3404  */
3405 INT WINAPI SHFileOperationWrapW(LPSHFILEOPSTRUCTW lpFileOp)
3406 {
3407     return SHFileOperationW(lpFileOp);
3408 }
3409 
3410 /*************************************************************************
3411  *      @	[SHLWAPI.342]
3412  *
3413  */
3414 PVOID WINAPI SHInterlockedCompareExchange( PVOID *dest, PVOID xchg, PVOID compare )
3415 {
3416     return InterlockedCompareExchangePointer( dest, xchg, compare );
3417 }
3418 
3419 /*************************************************************************
3420  *      @	[SHLWAPI.350]
3421  *
3422  * See GetFileVersionInfoSizeW.
3423  */
3424 DWORD WINAPI GetFileVersionInfoSizeWrapW( LPCWSTR filename, LPDWORD handle )
3425 {
3426     return GetFileVersionInfoSizeW( filename, handle );
3427 }
3428 
3429 /*************************************************************************
3430  *      @	[SHLWAPI.351]
3431  *
3432  * See GetFileVersionInfoW.
3433  */
3434 BOOL  WINAPI GetFileVersionInfoWrapW( LPCWSTR filename, DWORD handle,
3435                                       DWORD datasize, LPVOID data )
3436 {
3437     return GetFileVersionInfoW( filename, handle, datasize, data );
3438 }
3439 
3440 /*************************************************************************
3441  *      @	[SHLWAPI.352]
3442  *
3443  * See VerQueryValueW.
3444  */
3445 WORD WINAPI VerQueryValueWrapW( LPVOID pBlock, LPCWSTR lpSubBlock,
3446                                 LPVOID *lplpBuffer, UINT *puLen )
3447 {
3448     return VerQueryValueW( pBlock, lpSubBlock, lplpBuffer, puLen );
3449 }
3450 
3451 #define IsIface(type) SUCCEEDED((hRet = IUnknown_QueryInterface(lpUnknown, &IID_##type, (void**)&lpObj)))
3452 #define IShellBrowser_EnableModeless IShellBrowser_EnableModelessSB
3453 #define EnableModeless(type) type##_EnableModeless((type*)lpObj, bModeless)
3454 
3455 /*************************************************************************
3456  *      @	[SHLWAPI.355]
3457  *
3458  * Change the modality of a shell object.
3459  *
3460  * PARAMS
3461  *  lpUnknown [I] Object to make modeless
3462  *  bModeless [I] TRUE=Make modeless, FALSE=Make modal
3463  *
3464  * RETURNS
3465  *  Success: S_OK. The modality lpUnknown is changed.
3466  *  Failure: An HRESULT error code indicating the error.
3467  *
3468  * NOTES
3469  *  lpUnknown must support the IOleInPlaceFrame interface, the
3470  *  IInternetSecurityMgrSite interface, the IShellBrowser interface
3471  *  the IDocHostUIHandler interface, or the IOleInPlaceActiveObject interface,
3472  *  or this call will fail.
3473  */
3474 HRESULT WINAPI IUnknown_EnableModeless(IUnknown *lpUnknown, BOOL bModeless)
3475 {
3476   IUnknown *lpObj;
3477   HRESULT hRet;
3478 
3479   TRACE("(%p,%d)\n", lpUnknown, bModeless);
3480 
3481   if (!lpUnknown)
3482     return E_FAIL;
3483 
3484   if (IsIface(IOleInPlaceActiveObject))
3485     EnableModeless(IOleInPlaceActiveObject);
3486   else if (IsIface(IOleInPlaceFrame))
3487     EnableModeless(IOleInPlaceFrame);
3488   else if (IsIface(IShellBrowser))
3489     EnableModeless(IShellBrowser);
3490   else if (IsIface(IInternetSecurityMgrSite))
3491     EnableModeless(IInternetSecurityMgrSite);
3492   else if (IsIface(IDocHostUIHandler))
3493     EnableModeless(IDocHostUIHandler);
3494   else
3495     return hRet;
3496 
3497   IUnknown_Release(lpObj);
3498   return S_OK;
3499 }
3500 
3501 /*************************************************************************
3502  *      @	[SHLWAPI.357]
3503  *
3504  * See SHGetNewLinkInfoW.
3505  */
3506 BOOL WINAPI SHGetNewLinkInfoWrapW(LPCWSTR pszLinkTo, LPCWSTR pszDir, LPWSTR pszName,
3507                         BOOL *pfMustCopy, UINT uFlags)
3508 {
3509     return SHGetNewLinkInfoW(pszLinkTo, pszDir, pszName, pfMustCopy, uFlags);
3510 }
3511 
3512 /*************************************************************************
3513  *      @	[SHLWAPI.358]
3514  *
3515  * See SHDefExtractIconW.
3516  */
3517 UINT WINAPI SHDefExtractIconWrapW(LPCWSTR pszIconFile, int iIndex, UINT uFlags, HICON* phiconLarge,
3518                          HICON* phiconSmall, UINT nIconSize)
3519 {
3520     return SHDefExtractIconW(pszIconFile, iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
3521 }
3522 
3523 /*************************************************************************
3524  *      @	[SHLWAPI.363]
3525  *
3526  * Get and show a context menu from a shell folder.
3527  *
3528  * PARAMS
3529  *  hWnd           [I] Window displaying the shell folder
3530  *  lpFolder       [I] IShellFolder interface
3531  *  lpApidl        [I] Id for the particular folder desired
3532  *  dwCommandId    [I] The command ID to invoke (0=invoke default)
3533  *
3534  * RETURNS
3535  *  Success: S_OK. If bInvokeDefault is TRUE, the default menu action was
3536  *           executed.
3537  *  Failure: An HRESULT error code indicating the error.
3538  */
3539 HRESULT WINAPI SHInvokeCommand(HWND hWnd, IShellFolder* lpFolder, LPCITEMIDLIST lpApidl, DWORD dwCommandId)
3540 {
3541   IContextMenu *iContext;
3542   HRESULT hRet;
3543 
3544   TRACE("(%p, %p, %p, %u)\n", hWnd, lpFolder, lpApidl, dwCommandId);
3545 
3546   if (!lpFolder)
3547     return E_FAIL;
3548 
3549   /* Get the context menu from the shell folder */
3550   hRet = IShellFolder_GetUIObjectOf(lpFolder, hWnd, 1, &lpApidl,
3551                                     &IID_IContextMenu, 0, (void**)&iContext);
3552   if (SUCCEEDED(hRet))
3553   {
3554     HMENU hMenu;
3555     if ((hMenu = CreatePopupMenu()))
3556     {
3557       HRESULT hQuery;
3558 
3559       /* Add the context menu entries to the popup */
3560       hQuery = IContextMenu_QueryContextMenu(iContext, hMenu, 0, 1, 0x7FFF,
3561                                              dwCommandId ? CMF_NORMAL : CMF_DEFAULTONLY);
3562 
3563       if (SUCCEEDED(hQuery))
3564       {
3565         if (!dwCommandId)
3566           dwCommandId = GetMenuDefaultItem(hMenu, 0, 0);
3567         if (dwCommandId != (UINT)-1)
3568         {
3569           CMINVOKECOMMANDINFO cmIci;
3570           /* Invoke the default item */
3571           memset(&cmIci,0,sizeof(cmIci));
3572           cmIci.cbSize = sizeof(cmIci);
3573           cmIci.fMask = CMIC_MASK_ASYNCOK;
3574           cmIci.hwnd = hWnd;
3575 #ifdef __REACTOS__ /* r75561 */
3576           cmIci.lpVerb = MAKEINTRESOURCEA(dwCommandId - 1);
3577 #else
3578           cmIci.lpVerb = MAKEINTRESOURCEA(dwCommandId);
3579 #endif
3580           cmIci.nShow = SW_SHOWNORMAL;
3581 
3582           hRet = IContextMenu_InvokeCommand(iContext, &cmIci);
3583         }
3584       }
3585       DestroyMenu(hMenu);
3586     }
3587     IContextMenu_Release(iContext);
3588   }
3589   return hRet;
3590 }
3591 
3592 /*************************************************************************
3593  *      @	[SHLWAPI.370]
3594  *
3595  * See ExtractIconW.
3596  */
3597 HICON WINAPI ExtractIconWrapW(HINSTANCE hInstance, LPCWSTR lpszExeFileName,
3598                          UINT nIconIndex)
3599 {
3600     return ExtractIconW(hInstance, lpszExeFileName, nIconIndex);
3601 }
3602 
3603 /*************************************************************************
3604  *      @	[SHLWAPI.377]
3605  *
3606  * Load a library from the directory of a particular process.
3607  *
3608  * PARAMS
3609  *  new_mod        [I] Library name
3610  *  inst_hwnd      [I] Module whose directory is to be used
3611  *  dwCrossCodePage [I] Should be FALSE (currently ignored)
3612  *
3613  * RETURNS
3614  *  Success: A handle to the loaded module
3615  *  Failure: A NULL handle.
3616  */
3617 HMODULE WINAPI MLLoadLibraryA(LPCSTR new_mod, HMODULE inst_hwnd, DWORD dwCrossCodePage)
3618 {
3619   /* FIXME: Native appears to do DPA_Create and a DPA_InsertPtr for
3620    *        each call here.
3621    * FIXME: Native shows calls to:
3622    *  SHRegGetUSValue for "Software\Microsoft\Internet Explorer\International"
3623    *                      CheckVersion
3624    *  RegOpenKeyExA for "HKLM\Software\Microsoft\Internet Explorer"
3625    *  RegQueryValueExA for "LPKInstalled"
3626    *  RegCloseKey
3627    *  RegOpenKeyExA for "HKCU\Software\Microsoft\Internet Explorer\International"
3628    *  RegQueryValueExA for "ResourceLocale"
3629    *  RegCloseKey
3630    *  RegOpenKeyExA for "HKLM\Software\Microsoft\Active Setup\Installed Components\{guid}"
3631    *  RegQueryValueExA for "Locale"
3632    *  RegCloseKey
3633    *  and then tests the Locale ("en" for me).
3634    *     code below
3635    *  after the code then a DPA_Create (first time) and DPA_InsertPtr are done.
3636    */
3637     CHAR mod_path[2*MAX_PATH];
3638     LPSTR ptr;
3639     DWORD len;
3640 
3641     FIXME("(%s,%p,%d) semi-stub!\n", debugstr_a(new_mod), inst_hwnd, dwCrossCodePage);
3642     len = GetModuleFileNameA(inst_hwnd, mod_path, sizeof(mod_path));
3643     if (!len || len >= sizeof(mod_path)) return NULL;
3644 
3645     ptr = strrchr(mod_path, '\\');
3646     if (ptr) {
3647 	strcpy(ptr+1, new_mod);
3648 	TRACE("loading %s\n", debugstr_a(mod_path));
3649 	return LoadLibraryA(mod_path);
3650     }
3651     return NULL;
3652 }
3653 
3654 /*************************************************************************
3655  *      @	[SHLWAPI.378]
3656  *
3657  * Unicode version of MLLoadLibraryA.
3658  */
3659 HMODULE WINAPI MLLoadLibraryW(LPCWSTR new_mod, HMODULE inst_hwnd, DWORD dwCrossCodePage)
3660 {
3661     WCHAR mod_path[2*MAX_PATH];
3662     LPWSTR ptr;
3663     DWORD len;
3664 
3665     FIXME("(%s,%p,%d) semi-stub!\n", debugstr_w(new_mod), inst_hwnd, dwCrossCodePage);
3666     len = GetModuleFileNameW(inst_hwnd, mod_path, sizeof(mod_path) / sizeof(WCHAR));
3667     if (!len || len >= sizeof(mod_path) / sizeof(WCHAR)) return NULL;
3668 
3669     ptr = strrchrW(mod_path, '\\');
3670     if (ptr) {
3671 	strcpyW(ptr+1, new_mod);
3672 	TRACE("loading %s\n", debugstr_w(mod_path));
3673 	return LoadLibraryW(mod_path);
3674     }
3675     return NULL;
3676 }
3677 
3678 /*************************************************************************
3679  * ColorAdjustLuma      [SHLWAPI.@]
3680  *
3681  * Adjust the luminosity of a color
3682  *
3683  * PARAMS
3684  *  cRGB         [I] RGB value to convert
3685  *  dwLuma       [I] Luma adjustment
3686  *  bUnknown     [I] Unknown
3687  *
3688  * RETURNS
3689  *  The adjusted RGB color.
3690  */
3691 COLORREF WINAPI ColorAdjustLuma(COLORREF cRGB, int dwLuma, BOOL bUnknown)
3692 {
3693   TRACE("(0x%8x,%d,%d)\n", cRGB, dwLuma, bUnknown);
3694 
3695   if (dwLuma)
3696   {
3697     WORD wH, wL, wS;
3698 
3699     ColorRGBToHLS(cRGB, &wH, &wL, &wS);
3700 
3701     FIXME("Ignoring luma adjustment\n");
3702 
3703     /* FIXME: The adjustment is not linear */
3704 
3705     cRGB = ColorHLSToRGB(wH, wL, wS);
3706   }
3707   return cRGB;
3708 }
3709 
3710 /*************************************************************************
3711  *      @	[SHLWAPI.389]
3712  *
3713  * See GetSaveFileNameW.
3714  */
3715 BOOL WINAPI GetSaveFileNameWrapW(LPOPENFILENAMEW ofn)
3716 {
3717     return GetSaveFileNameW(ofn);
3718 }
3719 
3720 /*************************************************************************
3721  *      @	[SHLWAPI.390]
3722  *
3723  * See WNetRestoreConnectionW.
3724  */
3725 DWORD WINAPI WNetRestoreConnectionWrapW(HWND hwndOwner, LPWSTR lpszDevice)
3726 {
3727     return WNetRestoreConnectionW(hwndOwner, lpszDevice);
3728 }
3729 
3730 /*************************************************************************
3731  *      @	[SHLWAPI.391]
3732  *
3733  * See WNetGetLastErrorW.
3734  */
3735 DWORD WINAPI WNetGetLastErrorWrapW(LPDWORD lpError, LPWSTR lpErrorBuf, DWORD nErrorBufSize,
3736                          LPWSTR lpNameBuf, DWORD nNameBufSize)
3737 {
3738     return WNetGetLastErrorW(lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize);
3739 }
3740 
3741 /*************************************************************************
3742  *      @	[SHLWAPI.401]
3743  *
3744  * See PageSetupDlgW.
3745  */
3746 BOOL WINAPI PageSetupDlgWrapW(LPPAGESETUPDLGW pagedlg)
3747 {
3748     return PageSetupDlgW(pagedlg);
3749 }
3750 
3751 /*************************************************************************
3752  *      @	[SHLWAPI.402]
3753  *
3754  * See PrintDlgW.
3755  */
3756 BOOL WINAPI PrintDlgWrapW(LPPRINTDLGW printdlg)
3757 {
3758     return PrintDlgW(printdlg);
3759 }
3760 
3761 /*************************************************************************
3762  *      @	[SHLWAPI.403]
3763  *
3764  * See GetOpenFileNameW.
3765  */
3766 BOOL WINAPI GetOpenFileNameWrapW(LPOPENFILENAMEW ofn)
3767 {
3768     return GetOpenFileNameW(ofn);
3769 }
3770 
3771 /*************************************************************************
3772  *      @	[SHLWAPI.404]
3773  */
3774 HRESULT WINAPI SHIShellFolder_EnumObjects(LPSHELLFOLDER lpFolder, HWND hwnd, SHCONTF flags, IEnumIDList **ppenum)
3775 {
3776     /* Windows attempts to get an IPersist interface and, if that fails, an
3777      * IPersistFolder interface on the folder passed-in here.  If one of those
3778      * interfaces is available, it then calls GetClassID on the folder... and
3779      * then calls IShellFolder_EnumObjects no matter what, even crashing if
3780      * lpFolder isn't actually an IShellFolder object.  The purpose of getting
3781      * the ClassID is unknown, so we don't do it here.
3782      *
3783      * For discussion and detailed tests, see:
3784      * "shlwapi: Be less strict on which type of IShellFolder can be enumerated"
3785      * wine-devel mailing list, 3 Jun 2010
3786      */
3787 
3788     return IShellFolder_EnumObjects(lpFolder, hwnd, flags, ppenum);
3789 }
3790 
3791 /* INTERNAL: Map from HLS color space to RGB */
3792 static WORD ConvertHue(int wHue, WORD wMid1, WORD wMid2)
3793 {
3794   wHue = wHue > 240 ? wHue - 240 : wHue < 0 ? wHue + 240 : wHue;
3795 
3796   if (wHue > 160)
3797     return wMid1;
3798   else if (wHue > 120)
3799     wHue = 160 - wHue;
3800   else if (wHue > 40)
3801     return wMid2;
3802 
3803   return ((wHue * (wMid2 - wMid1) + 20) / 40) + wMid1;
3804 }
3805 
3806 /* Convert to RGB and scale into RGB range (0..255) */
3807 #define GET_RGB(h) (ConvertHue(h, wMid1, wMid2) * 255 + 120) / 240
3808 
3809 /*************************************************************************
3810  *      ColorHLSToRGB	[SHLWAPI.@]
3811  *
3812  * Convert from hls color space into an rgb COLORREF.
3813  *
3814  * PARAMS
3815  *  wHue        [I] Hue amount
3816  *  wLuminosity [I] Luminosity amount
3817  *  wSaturation [I] Saturation amount
3818  *
3819  * RETURNS
3820  *  A COLORREF representing the converted color.
3821  *
3822  * NOTES
3823  *  Input hls values are constrained to the range (0..240).
3824  */
3825 COLORREF WINAPI ColorHLSToRGB(WORD wHue, WORD wLuminosity, WORD wSaturation)
3826 {
3827   WORD wRed;
3828 
3829   if (wSaturation)
3830   {
3831     WORD wGreen, wBlue, wMid1, wMid2;
3832 
3833     if (wLuminosity > 120)
3834       wMid2 = wSaturation + wLuminosity - (wSaturation * wLuminosity + 120) / 240;
3835     else
3836       wMid2 = ((wSaturation + 240) * wLuminosity + 120) / 240;
3837 
3838     wMid1 = wLuminosity * 2 - wMid2;
3839 
3840     wRed   = GET_RGB(wHue + 80);
3841     wGreen = GET_RGB(wHue);
3842     wBlue  = GET_RGB(wHue - 80);
3843 
3844     return RGB(wRed, wGreen, wBlue);
3845   }
3846 
3847   wRed = wLuminosity * 255 / 240;
3848   return RGB(wRed, wRed, wRed);
3849 }
3850 
3851 /*************************************************************************
3852  *      @	[SHLWAPI.413]
3853  *
3854  * Get the current docking status of the system.
3855  *
3856  * PARAMS
3857  *  dwFlags [I] DOCKINFO_ flags from "winbase.h", unused
3858  *
3859  * RETURNS
3860  *  One of DOCKINFO_UNDOCKED, DOCKINFO_UNDOCKED, or 0 if the system is not
3861  *  a notebook.
3862  */
3863 DWORD WINAPI SHGetMachineInfo(DWORD dwFlags)
3864 {
3865   HW_PROFILE_INFOA hwInfo;
3866 
3867   TRACE("(0x%08x)\n", dwFlags);
3868 
3869   GetCurrentHwProfileA(&hwInfo);
3870   switch (hwInfo.dwDockInfo & (DOCKINFO_DOCKED|DOCKINFO_UNDOCKED))
3871   {
3872   case DOCKINFO_DOCKED:
3873   case DOCKINFO_UNDOCKED:
3874     return hwInfo.dwDockInfo & (DOCKINFO_DOCKED|DOCKINFO_UNDOCKED);
3875   default:
3876     return 0;
3877   }
3878 }
3879 
3880 /*************************************************************************
3881  * @    [SHLWAPI.416]
3882  *
3883  */
3884 DWORD WINAPI SHWinHelpOnDemandW(HWND hwnd, LPCWSTR helpfile, DWORD flags1, VOID *ptr1, DWORD flags2)
3885 {
3886 
3887     FIXME("(%p, %s, 0x%x, %p, %d)\n", hwnd, debugstr_w(helpfile), flags1, ptr1, flags2);
3888     return 0;
3889 }
3890 
3891 /*************************************************************************
3892  * @    [SHLWAPI.417]
3893  *
3894  */
3895 DWORD WINAPI SHWinHelpOnDemandA(HWND hwnd, LPCSTR helpfile, DWORD flags1, VOID *ptr1, DWORD flags2)
3896 {
3897 
3898     FIXME("(%p, %s, 0x%x, %p, %d)\n", hwnd, debugstr_a(helpfile), flags1, ptr1, flags2);
3899     return 0;
3900 }
3901 
3902 /*************************************************************************
3903  *      @	[SHLWAPI.418]
3904  *
3905  * Function seems to do FreeLibrary plus other things.
3906  *
3907  * FIXME native shows the following calls:
3908  *   RtlEnterCriticalSection
3909  *   LocalFree
3910  *   GetProcAddress(Comctl32??, 150L)
3911  *   DPA_DeletePtr
3912  *   RtlLeaveCriticalSection
3913  *  followed by the FreeLibrary.
3914  *  The above code may be related to .377 above.
3915  */
3916 BOOL WINAPI MLFreeLibrary(HMODULE hModule)
3917 {
3918 	FIXME("(%p) semi-stub\n", hModule);
3919 	return FreeLibrary(hModule);
3920 }
3921 
3922 /*************************************************************************
3923  *      @	[SHLWAPI.419]
3924  */
3925 BOOL WINAPI SHFlushSFCacheWrap(void) {
3926   FIXME(": stub\n");
3927   return TRUE;
3928 }
3929 
3930 /*************************************************************************
3931  *      @      [SHLWAPI.429]
3932  * FIXME I have no idea what this function does or what its arguments are.
3933  */
3934 BOOL WINAPI MLIsMLHInstance(HINSTANCE hInst)
3935 {
3936        FIXME("(%p) stub\n", hInst);
3937        return FALSE;
3938 }
3939 
3940 
3941 /*************************************************************************
3942  *      @	[SHLWAPI.430]
3943  */
3944 DWORD WINAPI MLSetMLHInstance(HINSTANCE hInst, HANDLE hHeap)
3945 {
3946 	FIXME("(%p,%p) stub\n", hInst, hHeap);
3947 	return E_FAIL;   /* This is what is used if shlwapi not loaded */
3948 }
3949 
3950 /*************************************************************************
3951  *      @	[SHLWAPI.431]
3952  */
3953 DWORD WINAPI MLClearMLHInstance(DWORD x)
3954 {
3955 	FIXME("(0x%08x)stub\n", x);
3956 	return 0xabba1247;
3957 }
3958 
3959 /*************************************************************************
3960  * @ [SHLWAPI.432]
3961  *
3962  * See SHSendMessageBroadcastW
3963  *
3964  */
3965 DWORD WINAPI SHSendMessageBroadcastA(UINT uMsg, WPARAM wParam, LPARAM lParam)
3966 {
3967     return SendMessageTimeoutA(HWND_BROADCAST, uMsg, wParam, lParam,
3968                                SMTO_ABORTIFHUNG, 2000, NULL);
3969 }
3970 
3971 /*************************************************************************
3972  * @ [SHLWAPI.433]
3973  *
3974  * A wrapper for sending Broadcast Messages to all top level Windows
3975  *
3976  */
3977 DWORD WINAPI SHSendMessageBroadcastW(UINT uMsg, WPARAM wParam, LPARAM lParam)
3978 {
3979     return SendMessageTimeoutW(HWND_BROADCAST, uMsg, wParam, lParam,
3980                                SMTO_ABORTIFHUNG, 2000, NULL);
3981 }
3982 
3983 /*************************************************************************
3984  *      @	[SHLWAPI.436]
3985  *
3986  * Convert a Unicode string CLSID into a CLSID.
3987  *
3988  * PARAMS
3989  *  idstr      [I]   string containing a CLSID in text form
3990  *  id         [O]   CLSID extracted from the string
3991  *
3992  * RETURNS
3993  *  S_OK on success or E_INVALIDARG on failure
3994  */
3995 HRESULT WINAPI CLSIDFromStringWrap(LPCWSTR idstr, CLSID *id)
3996 {
3997     return CLSIDFromString((LPCOLESTR)idstr, id);
3998 }
3999 
4000 /*************************************************************************
4001  *      @	[SHLWAPI.437]
4002  *
4003  * Determine if the OS supports a given feature.
4004  *
4005  * PARAMS
4006  *  dwFeature [I] Feature requested (undocumented)
4007  *
4008  * RETURNS
4009  *  TRUE  If the feature is available.
4010  *  FALSE If the feature is not available.
4011  */
4012 BOOL WINAPI IsOS(DWORD feature)
4013 {
4014     OSVERSIONINFOA osvi;
4015     DWORD platform, majorv, minorv;
4016 
4017     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
4018     if(!GetVersionExA(&osvi))  {
4019         ERR("GetVersionEx failed\n");
4020         return FALSE;
4021     }
4022 
4023     majorv = osvi.dwMajorVersion;
4024     minorv = osvi.dwMinorVersion;
4025     platform = osvi.dwPlatformId;
4026 
4027 #define ISOS_RETURN(x) \
4028     TRACE("(0x%x) ret=%d\n",feature,(x)); \
4029     return (x);
4030 
4031     switch(feature)  {
4032     case OS_WIN32SORGREATER:
4033         ISOS_RETURN(platform == VER_PLATFORM_WIN32s
4034                  || platform == VER_PLATFORM_WIN32_WINDOWS)
4035     case OS_NT:
4036         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4037     case OS_WIN95ORGREATER:
4038         ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS)
4039     case OS_NT4ORGREATER:
4040         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 4)
4041     case OS_WIN2000ORGREATER_ALT:
4042     case OS_WIN2000ORGREATER:
4043         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5)
4044     case OS_WIN98ORGREATER:
4045         ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS && minorv >= 10)
4046     case OS_WIN98_GOLD:
4047         ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS && minorv == 10)
4048     case OS_WIN2000PRO:
4049         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5)
4050     case OS_WIN2000SERVER:
4051         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && (minorv == 0 || minorv == 1))
4052     case OS_WIN2000ADVSERVER:
4053         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && (minorv == 0 || minorv == 1))
4054     case OS_WIN2000DATACENTER:
4055         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && (minorv == 0 || minorv == 1))
4056     case OS_WIN2000TERMINAL:
4057         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && (minorv == 0 || minorv == 1))
4058     case OS_EMBEDDED:
4059         FIXME("(OS_EMBEDDED) What should we return here?\n");
4060         return FALSE;
4061     case OS_TERMINALCLIENT:
4062         FIXME("(OS_TERMINALCLIENT) What should we return here?\n");
4063         return FALSE;
4064     case OS_TERMINALREMOTEADMIN:
4065         FIXME("(OS_TERMINALREMOTEADMIN) What should we return here?\n");
4066         return FALSE;
4067     case OS_WIN95_GOLD:
4068         ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS && minorv == 0)
4069     case OS_MEORGREATER:
4070         ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS && minorv >= 90)
4071     case OS_XPORGREATER:
4072         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5 && minorv >= 1)
4073     case OS_HOME:
4074         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5 && minorv >= 1)
4075     case OS_PROFESSIONAL:
4076         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4077     case OS_DATACENTER:
4078         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4079     case OS_ADVSERVER:
4080         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5)
4081     case OS_SERVER:
4082         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4083     case OS_TERMINALSERVER:
4084         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4085     case OS_PERSONALTERMINALSERVER:
4086         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && minorv >= 1 && majorv >= 5)
4087     case OS_FASTUSERSWITCHING:
4088         FIXME("(OS_FASTUSERSWITCHING) What should we return here?\n");
4089         return TRUE;
4090     case OS_WELCOMELOGONUI:
4091         FIXME("(OS_WELCOMELOGONUI) What should we return here?\n");
4092         return FALSE;
4093     case OS_DOMAINMEMBER:
4094         FIXME("(OS_DOMAINMEMBER) What should we return here?\n");
4095         return TRUE;
4096     case OS_ANYSERVER:
4097         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4098     case OS_WOW6432:
4099         {
4100             BOOL is_wow64;
4101             IsWow64Process(GetCurrentProcess(), &is_wow64);
4102             return is_wow64;
4103         }
4104     case OS_WEBSERVER:
4105         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4106     case OS_SMALLBUSINESSSERVER:
4107         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4108     case OS_TABLETPC:
4109         FIXME("(OS_TABLETPC) What should we return here?\n");
4110         return FALSE;
4111     case OS_SERVERADMINUI:
4112         FIXME("(OS_SERVERADMINUI) What should we return here?\n");
4113         return FALSE;
4114     case OS_MEDIACENTER:
4115         FIXME("(OS_MEDIACENTER) What should we return here?\n");
4116         return FALSE;
4117     case OS_APPLIANCE:
4118         FIXME("(OS_APPLIANCE) What should we return here?\n");
4119         return FALSE;
4120     case 0x25: /*OS_VISTAORGREATER*/
4121         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 6)
4122     }
4123 
4124 #undef ISOS_RETURN
4125 
4126     WARN("(0x%x) unknown parameter\n",feature);
4127 
4128     return FALSE;
4129 }
4130 
4131 #ifdef __REACTOS__
4132 /*************************************************************************
4133  * @  [SHLWAPI.438]
4134  */
4135 HRESULT WINAPI SHLoadRegUIStringA(HKEY hkey, LPCSTR value, LPSTR buf, DWORD size)
4136 {
4137     WCHAR valueW[MAX_PATH], bufferW[MAX_PATH];
4138     DWORD dwSize = ARRAY_SIZE(bufferW) * sizeof(CHAR);
4139     HRESULT hr;
4140 
4141     MultiByteToWideChar(CP_ACP, 0, value, -1, valueW, ARRAY_SIZE(valueW));
4142     valueW[ARRAY_SIZE(valueW) - 1] = UNICODE_NULL; /* Avoid buffer overrun */
4143 
4144     if (RegQueryValueExW(hkey, valueW, NULL, NULL, (LPBYTE)bufferW, &dwSize) != ERROR_SUCCESS)
4145         return E_FAIL;
4146 
4147     hr = SHLoadIndirectString(bufferW, bufferW, ARRAY_SIZE(bufferW), NULL);
4148     if (FAILED(hr))
4149         return hr;
4150 
4151     WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, size, NULL, NULL);
4152     if (size > 0)
4153         buf[size - 1] = ANSI_NULL; /* Avoid buffer overrun */
4154     return S_OK;
4155 }
4156 #endif
4157 
4158 /*************************************************************************
4159  * @  [SHLWAPI.439]
4160  */
4161 HRESULT WINAPI SHLoadRegUIStringW(HKEY hkey, LPCWSTR value, LPWSTR buf, DWORD size)
4162 {
4163     DWORD type, sz = size * sizeof(WCHAR);
4164 
4165     if(RegQueryValueExW(hkey, value, NULL, &type, (LPBYTE)buf, &sz) != ERROR_SUCCESS)
4166         return E_FAIL;
4167 
4168     return SHLoadIndirectString(buf, buf, size, NULL);
4169 }
4170 
4171 /*************************************************************************
4172  * @  [SHLWAPI.478]
4173  *
4174  * Call IInputObject_TranslateAcceleratorIO() on an object.
4175  *
4176  * PARAMS
4177  *  lpUnknown [I] Object supporting the IInputObject interface.
4178  *  lpMsg     [I] Key message to be processed.
4179  *
4180  * RETURNS
4181  *  Success: S_OK.
4182  *  Failure: An HRESULT error code, or E_INVALIDARG if lpUnknown is NULL.
4183  */
4184 HRESULT WINAPI IUnknown_TranslateAcceleratorIO(IUnknown *lpUnknown, LPMSG lpMsg)
4185 {
4186   IInputObject* lpInput = NULL;
4187   HRESULT hRet = E_INVALIDARG;
4188 
4189   TRACE("(%p,%p)\n", lpUnknown, lpMsg);
4190   if (lpUnknown)
4191   {
4192     hRet = IUnknown_QueryInterface(lpUnknown, &IID_IInputObject,
4193                                    (void**)&lpInput);
4194     if (SUCCEEDED(hRet) && lpInput)
4195     {
4196       hRet = IInputObject_TranslateAcceleratorIO(lpInput, lpMsg);
4197       IInputObject_Release(lpInput);
4198     }
4199   }
4200   return hRet;
4201 }
4202 
4203 /*************************************************************************
4204  * @  [SHLWAPI.481]
4205  *
4206  * Call IInputObject_HasFocusIO() on an object.
4207  *
4208  * PARAMS
4209  *  lpUnknown [I] Object supporting the IInputObject interface.
4210  *
4211  * RETURNS
4212  *  Success: S_OK, if lpUnknown is an IInputObject object and has the focus,
4213  *           or S_FALSE otherwise.
4214  *  Failure: An HRESULT error code, or E_INVALIDARG if lpUnknown is NULL.
4215  */
4216 HRESULT WINAPI IUnknown_HasFocusIO(IUnknown *lpUnknown)
4217 {
4218   IInputObject* lpInput = NULL;
4219   HRESULT hRet = E_INVALIDARG;
4220 
4221   TRACE("(%p)\n", lpUnknown);
4222   if (lpUnknown)
4223   {
4224     hRet = IUnknown_QueryInterface(lpUnknown, &IID_IInputObject,
4225                                    (void**)&lpInput);
4226     if (SUCCEEDED(hRet) && lpInput)
4227     {
4228       hRet = IInputObject_HasFocusIO(lpInput);
4229       IInputObject_Release(lpInput);
4230     }
4231   }
4232   return hRet;
4233 }
4234 
4235 /*************************************************************************
4236  *      ColorRGBToHLS	[SHLWAPI.@]
4237  *
4238  * Convert an rgb COLORREF into the hls color space.
4239  *
4240  * PARAMS
4241  *  cRGB         [I] Source rgb value
4242  *  pwHue        [O] Destination for converted hue
4243  *  pwLuminance  [O] Destination for converted luminance
4244  *  pwSaturation [O] Destination for converted saturation
4245  *
4246  * RETURNS
4247  *  Nothing. pwHue, pwLuminance and pwSaturation are set to the converted
4248  *  values.
4249  *
4250  * NOTES
4251  *  Output HLS values are constrained to the range (0..240).
4252  *  For Achromatic conversions, Hue is set to 160.
4253  */
4254 VOID WINAPI ColorRGBToHLS(COLORREF cRGB, LPWORD pwHue,
4255 			  LPWORD pwLuminance, LPWORD pwSaturation)
4256 {
4257   int wR, wG, wB, wMax, wMin, wHue, wLuminosity, wSaturation;
4258 
4259   TRACE("(%08x,%p,%p,%p)\n", cRGB, pwHue, pwLuminance, pwSaturation);
4260 
4261   wR = GetRValue(cRGB);
4262   wG = GetGValue(cRGB);
4263   wB = GetBValue(cRGB);
4264 
4265   wMax = max(wR, max(wG, wB));
4266   wMin = min(wR, min(wG, wB));
4267 
4268   /* Luminosity */
4269   wLuminosity = ((wMax + wMin) * 240 + 255) / 510;
4270 
4271   if (wMax == wMin)
4272   {
4273     /* Achromatic case */
4274     wSaturation = 0;
4275     /* Hue is now unrepresentable, but this is what native returns... */
4276     wHue = 160;
4277   }
4278   else
4279   {
4280     /* Chromatic case */
4281     int wDelta = wMax - wMin, wRNorm, wGNorm, wBNorm;
4282 
4283     /* Saturation */
4284     if (wLuminosity <= 120)
4285       wSaturation = ((wMax + wMin)/2 + wDelta * 240) / (wMax + wMin);
4286     else
4287       wSaturation = ((510 - wMax - wMin)/2 + wDelta * 240) / (510 - wMax - wMin);
4288 
4289     /* Hue */
4290     wRNorm = (wDelta/2 + wMax * 40 - wR * 40) / wDelta;
4291     wGNorm = (wDelta/2 + wMax * 40 - wG * 40) / wDelta;
4292     wBNorm = (wDelta/2 + wMax * 40 - wB * 40) / wDelta;
4293 
4294     if (wR == wMax)
4295       wHue = wBNorm - wGNorm;
4296     else if (wG == wMax)
4297       wHue = 80 + wRNorm - wBNorm;
4298     else
4299       wHue = 160 + wGNorm - wRNorm;
4300     if (wHue < 0)
4301       wHue += 240;
4302     else if (wHue > 240)
4303       wHue -= 240;
4304   }
4305   if (pwHue)
4306     *pwHue = wHue;
4307   if (pwLuminance)
4308     *pwLuminance = wLuminosity;
4309   if (pwSaturation)
4310     *pwSaturation = wSaturation;
4311 }
4312 
4313 /*************************************************************************
4314  *      SHCreateShellPalette	[SHLWAPI.@]
4315  */
4316 HPALETTE WINAPI SHCreateShellPalette(HDC hdc)
4317 {
4318 	FIXME("stub\n");
4319 	return CreateHalftonePalette(hdc);
4320 }
4321 
4322 /*************************************************************************
4323  *	SHGetInverseCMAP (SHLWAPI.@)
4324  *
4325  * Get an inverse color map table.
4326  *
4327  * PARAMS
4328  *  lpCmap  [O] Destination for color map
4329  *  dwSize  [I] Size of memory pointed to by lpCmap
4330  *
4331  * RETURNS
4332  *  Success: S_OK.
4333  *  Failure: E_POINTER,    If lpCmap is invalid.
4334  *           E_INVALIDARG, If dwFlags is invalid
4335  *           E_OUTOFMEMORY, If there is no memory available
4336  *
4337  * NOTES
4338  *  dwSize may only be CMAP_PTR_SIZE (4) or CMAP_SIZE (8192).
4339  *  If dwSize = CMAP_PTR_SIZE, *lpCmap is set to the address of this DLL's
4340  *  internal CMap.
4341  *  If dwSize = CMAP_SIZE, lpCmap is filled with a copy of the data from
4342  *  this DLL's internal CMap.
4343  */
4344 HRESULT WINAPI SHGetInverseCMAP(LPDWORD dest, DWORD dwSize)
4345 {
4346     if (dwSize == 4) {
4347 	FIXME(" - returning bogus address for SHGetInverseCMAP\n");
4348 	*dest = (DWORD)0xabba1249;
4349 	return 0;
4350     }
4351     FIXME("(%p, %#x) stub\n", dest, dwSize);
4352     return 0;
4353 }
4354 
4355 /*************************************************************************
4356  *      SHIsLowMemoryMachine	[SHLWAPI.@]
4357  *
4358  * Determine if the current computer has low memory.
4359  *
4360  * PARAMS
4361  *  dwType [I] Zero.
4362  *
4363  * RETURNS
4364  *  TRUE if the users machine has 16 Megabytes of memory or less,
4365  *  FALSE otherwise.
4366  */
4367 BOOL WINAPI SHIsLowMemoryMachine(DWORD dwType)
4368 {
4369 #ifdef __REACTOS__
4370     MEMORYSTATUS status;
4371     static int is_low = -1;
4372     TRACE("(0x%08x)\n", dwType);
4373     if (dwType == 0 && is_low == -1)
4374     {
4375         GlobalMemoryStatus(&status);
4376         is_low = (status.dwTotalPhys <= 0x1000000);
4377     }
4378     return is_low;
4379 #else
4380   FIXME("(0x%08x) stub\n", dwType);
4381   return FALSE;
4382 #endif
4383 }
4384 
4385 /*************************************************************************
4386  *      GetMenuPosFromID	[SHLWAPI.@]
4387  *
4388  * Return the position of a menu item from its Id.
4389  *
4390  * PARAMS
4391  *   hMenu [I] Menu containing the item
4392  *   wID   [I] Id of the menu item
4393  *
4394  * RETURNS
4395  *  Success: The index of the menu item in hMenu.
4396  *  Failure: -1, If the item is not found.
4397  */
4398 INT WINAPI GetMenuPosFromID(HMENU hMenu, UINT wID)
4399 {
4400     MENUITEMINFOW mi;
4401     INT nCount = GetMenuItemCount(hMenu), nIter = 0;
4402 
4403     TRACE("%p %u\n", hMenu, wID);
4404 
4405     while (nIter < nCount)
4406     {
4407         mi.cbSize = sizeof(mi);
4408         mi.fMask = MIIM_ID;
4409         if (GetMenuItemInfoW(hMenu, nIter, TRUE, &mi) && mi.wID == wID)
4410         {
4411             TRACE("ret %d\n", nIter);
4412             return nIter;
4413         }
4414         nIter++;
4415     }
4416 
4417     return -1;
4418 }
4419 
4420 /*************************************************************************
4421  *      @	[SHLWAPI.179]
4422  *
4423  * Same as SHLWAPI.GetMenuPosFromID
4424  */
4425 DWORD WINAPI SHMenuIndexFromID(HMENU hMenu, UINT uID)
4426 {
4427     TRACE("%p %u\n", hMenu, uID);
4428     return GetMenuPosFromID(hMenu, uID);
4429 }
4430 
4431 
4432 /*************************************************************************
4433  *      @	[SHLWAPI.448]
4434  */
4435 VOID WINAPI FixSlashesAndColonW(LPWSTR lpwstr)
4436 {
4437     while (*lpwstr)
4438     {
4439         if (*lpwstr == '/')
4440             *lpwstr = '\\';
4441         lpwstr++;
4442     }
4443 }
4444 
4445 
4446 /*************************************************************************
4447  *      @	[SHLWAPI.461]
4448  */
4449 DWORD WINAPI SHGetAppCompatFlags(DWORD dwUnknown)
4450 {
4451   FIXME("(0x%08x) stub\n", dwUnknown);
4452   return 0;
4453 }
4454 
4455 
4456 /*************************************************************************
4457  *      @	[SHLWAPI.549]
4458  */
4459 HRESULT WINAPI SHCoCreateInstanceAC(REFCLSID rclsid, LPUNKNOWN pUnkOuter,
4460                                     DWORD dwClsContext, REFIID iid, LPVOID *ppv)
4461 {
4462     return CoCreateInstance(rclsid, pUnkOuter, dwClsContext, iid, ppv);
4463 }
4464 
4465 /*************************************************************************
4466  * SHSkipJunction	[SHLWAPI.@]
4467  *
4468  * Determine if a bind context can be bound to an object
4469  *
4470  * PARAMS
4471  *  pbc    [I] Bind context to check
4472  *  pclsid [I] CLSID of object to be bound to
4473  *
4474  * RETURNS
4475  *  TRUE: If it is safe to bind
4476  *  FALSE: If pbc is invalid or binding would not be safe
4477  *
4478  */
4479 BOOL WINAPI SHSkipJunction(IBindCtx *pbc, const CLSID *pclsid)
4480 {
4481   static WCHAR szSkipBinding[] = { 'S','k','i','p',' ',
4482     'B','i','n','d','i','n','g',' ','C','L','S','I','D','\0' };
4483   BOOL bRet = FALSE;
4484 
4485   if (pbc)
4486   {
4487     IUnknown* lpUnk;
4488 
4489     if (SUCCEEDED(IBindCtx_GetObjectParam(pbc, szSkipBinding, &lpUnk)))
4490     {
4491       CLSID clsid;
4492 
4493       if (SUCCEEDED(IUnknown_GetClassID(lpUnk, &clsid)) &&
4494           IsEqualGUID(pclsid, &clsid))
4495         bRet = TRUE;
4496 
4497       IUnknown_Release(lpUnk);
4498     }
4499   }
4500   return bRet;
4501 }
4502 
4503 /***********************************************************************
4504  *		SHGetShellKey (SHLWAPI.491)
4505  */
4506 HKEY WINAPI SHGetShellKey(DWORD flags, LPCWSTR sub_key, BOOL create)
4507 {
4508 #ifndef __REACTOS__
4509     enum _shellkey_flags {
4510         SHKEY_Root_HKCU = 0x1,
4511         SHKEY_Root_HKLM = 0x2,
4512         SHKEY_Key_Explorer  = 0x00,
4513         SHKEY_Key_Shell = 0x10,
4514         SHKEY_Key_ShellNoRoam = 0x20,
4515         SHKEY_Key_Classes = 0x30,
4516         SHKEY_Subkey_Default = 0x0000,
4517         SHKEY_Subkey_ResourceName = 0x1000,
4518         SHKEY_Subkey_Handlers = 0x2000,
4519         SHKEY_Subkey_Associations = 0x3000,
4520         SHKEY_Subkey_Volatile = 0x4000,
4521         SHKEY_Subkey_MUICache = 0x5000,
4522         SHKEY_Subkey_FileExts = 0x6000
4523     };
4524 #endif
4525 
4526     static const WCHAR explorerW[] = {'S','o','f','t','w','a','r','e','\\',
4527         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
4528         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4529         'E','x','p','l','o','r','e','r','\\'};
4530     static const WCHAR shellW[] = {'S','o','f','t','w','a','r','e','\\',
4531         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
4532         'S','h','e','l','l','\\'};
4533     static const WCHAR shell_no_roamW[] = {'S','o','f','t','w','a','r','e','\\',
4534         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
4535         'S','h','e','l','l','N','o','R','o','a','m','\\'};
4536     static const WCHAR classesW[] = {'S','o','f','t','w','a','r','e','\\',
4537         'C','l','a','s','s','e','s','\\'};
4538 
4539     static const WCHAR localized_resource_nameW[] = {'L','o','c','a','l','i','z','e','d',
4540         'R','e','s','o','u','r','c','e','N','a','m','e','\\'};
4541     static const WCHAR handlersW[] = {'H','a','n','d','l','e','r','s','\\'};
4542     static const WCHAR associationsW[] = {'A','s','s','o','c','i','a','t','i','o','n','s','\\'};
4543     static const WCHAR volatileW[] = {'V','o','l','a','t','i','l','e','\\'};
4544     static const WCHAR mui_cacheW[] = {'M','U','I','C','a','c','h','e','\\'};
4545     static const WCHAR file_extsW[] = {'F','i','l','e','E','x','t','s','\\'};
4546 
4547     WCHAR *path;
4548     const WCHAR *key, *subkey;
4549     int size_key, size_subkey, size_user;
4550     HKEY hkey = NULL;
4551 
4552     TRACE("(0x%08x, %s, %d)\n", flags, debugstr_w(sub_key), create);
4553 
4554     /* For compatibility with Vista+ */
4555     if(flags == 0x1ffff)
4556         flags = 0x21;
4557 
4558     switch(flags&0xff0) {
4559     case SHKEY_Key_Explorer:
4560         key = explorerW;
4561         size_key = sizeof(explorerW);
4562         break;
4563     case SHKEY_Key_Shell:
4564         key = shellW;
4565         size_key = sizeof(shellW);
4566         break;
4567     case SHKEY_Key_ShellNoRoam:
4568         key = shell_no_roamW;
4569         size_key = sizeof(shell_no_roamW);
4570         break;
4571     case SHKEY_Key_Classes:
4572         key = classesW;
4573         size_key = sizeof(classesW);
4574         break;
4575     default:
4576         FIXME("unsupported flags (0x%08x)\n", flags);
4577         return NULL;
4578     }
4579 
4580     switch(flags&0xff000) {
4581     case SHKEY_Subkey_Default:
4582         subkey = NULL;
4583         size_subkey = 0;
4584         break;
4585     case SHKEY_Subkey_ResourceName:
4586         subkey = localized_resource_nameW;
4587         size_subkey = sizeof(localized_resource_nameW);
4588         break;
4589     case SHKEY_Subkey_Handlers:
4590         subkey = handlersW;
4591         size_subkey = sizeof(handlersW);
4592         break;
4593     case SHKEY_Subkey_Associations:
4594         subkey = associationsW;
4595         size_subkey = sizeof(associationsW);
4596         break;
4597     case SHKEY_Subkey_Volatile:
4598         subkey = volatileW;
4599         size_subkey = sizeof(volatileW);
4600         break;
4601     case SHKEY_Subkey_MUICache:
4602         subkey = mui_cacheW;
4603         size_subkey = sizeof(mui_cacheW);
4604         break;
4605     case SHKEY_Subkey_FileExts:
4606         subkey = file_extsW;
4607         size_subkey = sizeof(file_extsW);
4608         break;
4609     default:
4610         FIXME("unsupported flags (0x%08x)\n", flags);
4611         return NULL;
4612     }
4613 
4614     if(sub_key)
4615         size_user = lstrlenW(sub_key)*sizeof(WCHAR);
4616     else
4617         size_user = 0;
4618 
4619     path = HeapAlloc(GetProcessHeap(), 0, size_key+size_subkey+size_user+sizeof(WCHAR));
4620     if(!path) {
4621         ERR("Out of memory\n");
4622         return NULL;
4623     }
4624 
4625     memcpy(path, key, size_key);
4626     if(subkey)
4627         memcpy(path+size_key/sizeof(WCHAR), subkey, size_subkey);
4628     if(sub_key)
4629         memcpy(path+(size_key+size_subkey)/sizeof(WCHAR), sub_key, size_user);
4630     path[(size_key+size_subkey+size_user)/sizeof(WCHAR)] = '\0';
4631 
4632     if(create)
4633         RegCreateKeyExW((flags&0xf)==SHKEY_Root_HKLM?HKEY_LOCAL_MACHINE:HKEY_CURRENT_USER,
4634                 path, 0, NULL, 0, MAXIMUM_ALLOWED, NULL, &hkey, NULL);
4635     else
4636         RegOpenKeyExW((flags&0xf)==SHKEY_Root_HKLM?HKEY_LOCAL_MACHINE:HKEY_CURRENT_USER,
4637                 path, 0, MAXIMUM_ALLOWED, &hkey);
4638 
4639     HeapFree(GetProcessHeap(), 0, path);
4640     return hkey;
4641 }
4642 
4643 /***********************************************************************
4644  *		SHQueueUserWorkItem (SHLWAPI.@)
4645  */
4646 BOOL WINAPI SHQueueUserWorkItem(LPTHREAD_START_ROUTINE pfnCallback,
4647         LPVOID pContext, LONG lPriority, DWORD_PTR dwTag,
4648         DWORD_PTR *pdwId, LPCSTR pszModule, DWORD dwFlags)
4649 {
4650     TRACE("(%p, %p, %d, %lx, %p, %s, %08x)\n", pfnCallback, pContext,
4651           lPriority, dwTag, pdwId, debugstr_a(pszModule), dwFlags);
4652 
4653     if(lPriority || dwTag || pdwId || pszModule || dwFlags)
4654         FIXME("Unsupported arguments\n");
4655 
4656     return QueueUserWorkItem(pfnCallback, pContext, 0);
4657 }
4658 
4659 /***********************************************************************
4660  *		SHSetTimerQueueTimer (SHLWAPI.263)
4661  */
4662 HANDLE WINAPI SHSetTimerQueueTimer(HANDLE hQueue,
4663         WAITORTIMERCALLBACK pfnCallback, LPVOID pContext, DWORD dwDueTime,
4664         DWORD dwPeriod, LPCSTR lpszLibrary, DWORD dwFlags)
4665 {
4666     HANDLE hNewTimer;
4667 
4668     /* SHSetTimerQueueTimer flags -> CreateTimerQueueTimer flags */
4669     if (dwFlags & TPS_LONGEXECTIME) {
4670         dwFlags &= ~TPS_LONGEXECTIME;
4671         dwFlags |= WT_EXECUTELONGFUNCTION;
4672     }
4673     if (dwFlags & TPS_EXECUTEIO) {
4674         dwFlags &= ~TPS_EXECUTEIO;
4675         dwFlags |= WT_EXECUTEINIOTHREAD;
4676     }
4677 
4678     if (!CreateTimerQueueTimer(&hNewTimer, hQueue, pfnCallback, pContext,
4679                                dwDueTime, dwPeriod, dwFlags))
4680         return NULL;
4681 
4682     return hNewTimer;
4683 }
4684 
4685 /***********************************************************************
4686  *		IUnknown_OnFocusChangeIS (SHLWAPI.@)
4687  */
4688 HRESULT WINAPI IUnknown_OnFocusChangeIS(LPUNKNOWN lpUnknown, LPUNKNOWN pFocusObject, BOOL bFocus)
4689 {
4690     IInputObjectSite *pIOS = NULL;
4691     HRESULT hRet = E_INVALIDARG;
4692 
4693     TRACE("(%p, %p, %s)\n", lpUnknown, pFocusObject, bFocus ? "TRUE" : "FALSE");
4694 
4695     if (lpUnknown)
4696     {
4697         hRet = IUnknown_QueryInterface(lpUnknown, &IID_IInputObjectSite,
4698                                        (void **)&pIOS);
4699         if (SUCCEEDED(hRet) && pIOS)
4700         {
4701             hRet = IInputObjectSite_OnFocusChangeIS(pIOS, pFocusObject, bFocus);
4702             IInputObjectSite_Release(pIOS);
4703         }
4704     }
4705     return hRet;
4706 }
4707 
4708 /***********************************************************************
4709  *		SKAllocValueW (SHLWAPI.519)
4710  */
4711 HRESULT WINAPI SKAllocValueW(DWORD flags, LPCWSTR subkey, LPCWSTR value, DWORD *type,
4712         LPVOID *data, DWORD *count)
4713 {
4714     DWORD ret, size;
4715     HKEY hkey;
4716 
4717     TRACE("(0x%x, %s, %s, %p, %p, %p)\n", flags, debugstr_w(subkey),
4718         debugstr_w(value), type, data, count);
4719 
4720     hkey = SHGetShellKey(flags, subkey, FALSE);
4721     if (!hkey)
4722         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
4723 
4724     ret = SHQueryValueExW(hkey, value, NULL, type, NULL, &size);
4725     if (ret) {
4726         RegCloseKey(hkey);
4727         return HRESULT_FROM_WIN32(ret);
4728     }
4729 
4730     size += 2;
4731     *data = LocalAlloc(0, size);
4732     if (!*data) {
4733         RegCloseKey(hkey);
4734         return E_OUTOFMEMORY;
4735     }
4736 
4737     ret = SHQueryValueExW(hkey, value, NULL, type, *data, &size);
4738     if (count)
4739         *count = size;
4740 
4741     RegCloseKey(hkey);
4742     return HRESULT_FROM_WIN32(ret);
4743 }
4744 
4745 /***********************************************************************
4746  *		SKDeleteValueW (SHLWAPI.518)
4747  */
4748 HRESULT WINAPI SKDeleteValueW(DWORD flags, LPCWSTR subkey, LPCWSTR value)
4749 {
4750     DWORD ret;
4751     HKEY hkey;
4752 
4753     TRACE("(0x%x, %s %s)\n", flags, debugstr_w(subkey), debugstr_w(value));
4754 
4755     hkey = SHGetShellKey(flags, subkey, FALSE);
4756     if (!hkey)
4757         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
4758 
4759     ret = RegDeleteValueW(hkey, value);
4760 
4761     RegCloseKey(hkey);
4762     return HRESULT_FROM_WIN32(ret);
4763 }
4764 
4765 /***********************************************************************
4766  *		SKGetValueW (SHLWAPI.516)
4767  */
4768 HRESULT WINAPI SKGetValueW(DWORD flags, LPCWSTR subkey, LPCWSTR value, DWORD *type,
4769     void *data, DWORD *count)
4770 {
4771     DWORD ret;
4772     HKEY hkey;
4773 
4774     TRACE("(0x%x, %s, %s, %p, %p, %p)\n", flags, debugstr_w(subkey),
4775         debugstr_w(value), type, data, count);
4776 
4777     hkey = SHGetShellKey(flags, subkey, FALSE);
4778     if (!hkey)
4779         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
4780 
4781     ret = SHQueryValueExW(hkey, value, NULL, type, data, count);
4782 
4783     RegCloseKey(hkey);
4784     return HRESULT_FROM_WIN32(ret);
4785 }
4786 
4787 /***********************************************************************
4788  *		SKSetValueW (SHLWAPI.516)
4789  */
4790 HRESULT WINAPI SKSetValueW(DWORD flags, LPCWSTR subkey, LPCWSTR value,
4791         DWORD type, void *data, DWORD count)
4792 {
4793     DWORD ret;
4794     HKEY hkey;
4795 
4796     TRACE("(0x%x, %s, %s, %x, %p, %d)\n", flags, debugstr_w(subkey),
4797             debugstr_w(value), type, data, count);
4798 
4799     hkey = SHGetShellKey(flags, subkey, TRUE);
4800     if (!hkey)
4801         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
4802 
4803     ret = RegSetValueExW(hkey, value, 0, type, data, count);
4804 
4805     RegCloseKey(hkey);
4806     return HRESULT_FROM_WIN32(ret);
4807 }
4808 
4809 typedef HRESULT (WINAPI *DllGetVersion_func)(DLLVERSIONINFO *);
4810 
4811 /***********************************************************************
4812  *              GetUIVersion (SHLWAPI.452)
4813  */
4814 DWORD WINAPI GetUIVersion(void)
4815 {
4816     static DWORD version;
4817 
4818     if (!version)
4819     {
4820         DllGetVersion_func pDllGetVersion;
4821         HMODULE dll = LoadLibraryA("shell32.dll");
4822         if (!dll) return 0;
4823 
4824         pDllGetVersion = (DllGetVersion_func)GetProcAddress(dll, "DllGetVersion");
4825         if (pDllGetVersion)
4826         {
4827             DLLVERSIONINFO dvi;
4828             dvi.cbSize = sizeof(DLLVERSIONINFO);
4829             if (pDllGetVersion(&dvi) == S_OK) version = dvi.dwMajorVersion;
4830         }
4831         FreeLibrary( dll );
4832         if (!version) version = 3;  /* old shell dlls don't have DllGetVersion */
4833     }
4834     return version;
4835 }
4836 
4837 /***********************************************************************
4838  *              ShellMessageBoxWrapW [SHLWAPI.388]
4839  *
4840  * See shell32.ShellMessageBoxW
4841  *
4842 #ifndef __REACTOS__
4843  *
4844  * NOTE:
4845  * shlwapi.ShellMessageBoxWrapW is a duplicate of shell32.ShellMessageBoxW
4846  * because we can't forward to it in the .spec file since it's exported by
4847  * ordinal. If you change the implementation here please update the code in
4848  * shell32 as well.
4849  *
4850 #else // __REACTOS__
4851  *
4852  * From Vista+ onwards, all the implementation of ShellMessageBoxA/W that
4853  * were existing in shell32 has been completely moved to shlwapi, so that
4854  * shell32.ShellMessageBoxA and shell32.ShellMessageBoxW are redirections
4855  * to the corresponding shlwapi functions.
4856  *
4857  * For Win2003 compatibility, if you change the implementation here please
4858  * update the code of ShellMessageBoxA in shell32 as well.
4859  *
4860 #endif
4861  */
4862 INT WINAPIV ShellMessageBoxWrapW(HINSTANCE hInstance, HWND hWnd, LPCWSTR lpText,
4863                                  LPCWSTR lpCaption, UINT uType, ...)
4864 {
4865     WCHAR *szText = NULL, szTitle[100];
4866     LPCWSTR pszText, pszTitle = szTitle;
4867     LPWSTR pszTemp;
4868     __ms_va_list args;
4869     int ret;
4870 
4871     __ms_va_start(args, uType);
4872 
4873     TRACE("(%p,%p,%p,%p,%08x)\n", hInstance, hWnd, lpText, lpCaption, uType);
4874 
4875     if (IS_INTRESOURCE(lpCaption))
4876         LoadStringW(hInstance, LOWORD(lpCaption), szTitle, ARRAY_SIZE(szTitle));
4877     else
4878         pszTitle = lpCaption;
4879 
4880     if (IS_INTRESOURCE(lpText))
4881     {
4882         const WCHAR *ptr;
4883         UINT len = LoadStringW(hInstance, LOWORD(lpText), (LPWSTR)&ptr, 0);
4884 
4885         if (len)
4886         {
4887             szText = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4888             if (szText) LoadStringW(hInstance, LOWORD(lpText), szText, len + 1);
4889         }
4890         pszText = szText;
4891         if (!pszText) {
4892             WARN("Failed to load id %d\n", LOWORD(lpText));
4893             __ms_va_end(args);
4894             return 0;
4895         }
4896     }
4897     else
4898         pszText = lpText;
4899 
4900     FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
4901                    pszText, 0, 0, (LPWSTR)&pszTemp, 0, &args);
4902 
4903     __ms_va_end(args);
4904 
4905 #ifdef __REACTOS__
4906     uType |= MB_SETFOREGROUND;
4907 #endif
4908     ret = MessageBoxW(hWnd, pszTemp, pszTitle, uType);
4909 
4910     HeapFree(GetProcessHeap(), 0, szText);
4911     LocalFree(pszTemp);
4912     return ret;
4913 }
4914 
4915 /***********************************************************************
4916  *              ZoneComputePaneSize [SHLWAPI.382]
4917  */
4918 UINT WINAPI ZoneComputePaneSize(HWND hwnd)
4919 {
4920     FIXME("\n");
4921     return 0x95;
4922 }
4923 
4924 /***********************************************************************
4925  *              SHChangeNotifyWrap [SHLWAPI.394]
4926  */
4927 void WINAPI SHChangeNotifyWrap(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2)
4928 {
4929     SHChangeNotify(wEventId, uFlags, dwItem1, dwItem2);
4930 }
4931 
4932 typedef struct SHELL_USER_SID {   /* according to MSDN this should be in shlobj.h... */
4933     SID_IDENTIFIER_AUTHORITY sidAuthority;
4934     DWORD                    dwUserGroupID;
4935     DWORD                    dwUserID;
4936 } SHELL_USER_SID, *PSHELL_USER_SID;
4937 
4938 typedef struct SHELL_USER_PERMISSION { /* ...and this should be in shlwapi.h */
4939     SHELL_USER_SID susID;
4940     DWORD          dwAccessType;
4941     BOOL           fInherit;
4942     DWORD          dwAccessMask;
4943     DWORD          dwInheritMask;
4944     DWORD          dwInheritAccessMask;
4945 } SHELL_USER_PERMISSION, *PSHELL_USER_PERMISSION;
4946 
4947 /***********************************************************************
4948  *             GetShellSecurityDescriptor [SHLWAPI.475]
4949  *
4950  * prepares SECURITY_DESCRIPTOR from a set of ACEs
4951  *
4952  * PARAMS
4953  *  apUserPerm [I] array of pointers to SHELL_USER_PERMISSION structures,
4954  *                 each of which describes permissions to apply
4955  *  cUserPerm  [I] number of entries in apUserPerm array
4956  *
4957  * RETURNS
4958  *  success: pointer to SECURITY_DESCRIPTOR
4959  *  failure: NULL
4960  *
4961  * NOTES
4962  *  Call should free returned descriptor with LocalFree
4963  */
4964 PSECURITY_DESCRIPTOR WINAPI GetShellSecurityDescriptor(const PSHELL_USER_PERMISSION *apUserPerm, int cUserPerm)
4965 {
4966     PSID *sidlist;
4967     PSID  cur_user = NULL;
4968     BYTE  tuUser[2000];
4969     DWORD acl_size;
4970     int   sid_count, i;
4971     PSECURITY_DESCRIPTOR psd = NULL;
4972 
4973     TRACE("%p %d\n", apUserPerm, cUserPerm);
4974 
4975     if (apUserPerm == NULL || cUserPerm <= 0)
4976         return NULL;
4977 
4978     sidlist = HeapAlloc(GetProcessHeap(), 0, cUserPerm * sizeof(PSID));
4979     if (!sidlist)
4980         return NULL;
4981 
4982     acl_size = sizeof(ACL);
4983 
4984     for(sid_count = 0; sid_count < cUserPerm; sid_count++)
4985     {
4986         static SHELL_USER_SID null_sid = {{SECURITY_NULL_SID_AUTHORITY}, 0, 0};
4987         PSHELL_USER_PERMISSION perm = apUserPerm[sid_count];
4988         PSHELL_USER_SID sid = &perm->susID;
4989         PSID pSid;
4990         BOOL ret = TRUE;
4991 
4992         if (!memcmp((void*)sid, (void*)&null_sid, sizeof(SHELL_USER_SID)))
4993         {  /* current user's SID */
4994             if (!cur_user)
4995             {
4996                 HANDLE Token;
4997                 DWORD bufsize = sizeof(tuUser);
4998 
4999                 ret = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &Token);
5000                 if (ret)
5001                 {
5002                     ret = GetTokenInformation(Token, TokenUser, (void*)tuUser, bufsize, &bufsize );
5003                     if (ret)
5004                         cur_user = ((PTOKEN_USER)tuUser)->User.Sid;
5005                     CloseHandle(Token);
5006                 }
5007             }
5008             pSid = cur_user;
5009         } else if (sid->dwUserID==0) /* one sub-authority */
5010             ret = AllocateAndInitializeSid(&sid->sidAuthority, 1, sid->dwUserGroupID, 0,
5011                     0, 0, 0, 0, 0, 0, &pSid);
5012         else
5013             ret = AllocateAndInitializeSid(&sid->sidAuthority, 2, sid->dwUserGroupID, sid->dwUserID,
5014                     0, 0, 0, 0, 0, 0, &pSid);
5015         if (!ret)
5016             goto free_sids;
5017 
5018         sidlist[sid_count] = pSid;
5019         /* increment acl_size (1 ACE for non-inheritable and 2 ACEs for inheritable records */
5020         acl_size += (sizeof(ACCESS_ALLOWED_ACE)-sizeof(DWORD) + GetLengthSid(pSid)) * (perm->fInherit ? 2 : 1);
5021     }
5022 
5023     psd = LocalAlloc(0, sizeof(SECURITY_DESCRIPTOR) + acl_size);
5024 
5025     if (psd != NULL)
5026     {
5027         PACL pAcl = (PACL)(((BYTE*)psd)+sizeof(SECURITY_DESCRIPTOR));
5028 
5029         if (!InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION))
5030             goto error;
5031 
5032         if (!InitializeAcl(pAcl, acl_size, ACL_REVISION))
5033             goto error;
5034 
5035         for(i = 0; i < sid_count; i++)
5036         {
5037             PSHELL_USER_PERMISSION sup = apUserPerm[i];
5038             PSID sid = sidlist[i];
5039 
5040             switch(sup->dwAccessType)
5041             {
5042                 case ACCESS_ALLOWED_ACE_TYPE:
5043                     if (!AddAccessAllowedAce(pAcl, ACL_REVISION, sup->dwAccessMask, sid))
5044                         goto error;
5045                     if (sup->fInherit && !AddAccessAllowedAceEx(pAcl, ACL_REVISION,
5046                                 (BYTE)sup->dwInheritMask, sup->dwInheritAccessMask, sid))
5047                         goto error;
5048                     break;
5049                 case ACCESS_DENIED_ACE_TYPE:
5050                     if (!AddAccessDeniedAce(pAcl, ACL_REVISION, sup->dwAccessMask, sid))
5051                         goto error;
5052                     if (sup->fInherit && !AddAccessDeniedAceEx(pAcl, ACL_REVISION,
5053                                 (BYTE)sup->dwInheritMask, sup->dwInheritAccessMask, sid))
5054                         goto error;
5055                     break;
5056                 default:
5057                     goto error;
5058             }
5059         }
5060 
5061         if (!SetSecurityDescriptorDacl(psd, TRUE, pAcl, FALSE))
5062             goto error;
5063     }
5064     goto free_sids;
5065 
5066 error:
5067     LocalFree(psd);
5068     psd = NULL;
5069 free_sids:
5070     for(i = 0; i < sid_count; i++)
5071     {
5072         if (!cur_user || sidlist[i] != cur_user)
5073             FreeSid(sidlist[i]);
5074     }
5075     HeapFree(GetProcessHeap(), 0, sidlist);
5076 
5077     return psd;
5078 }
5079 
5080 #ifndef __REACTOS__ /* See propbag.cpp */
5081 /***********************************************************************
5082  *             SHCreatePropertyBagOnRegKey [SHLWAPI.471]
5083  *
5084  * Creates a property bag from a registry key
5085  *
5086  * PARAMS
5087  *  hKey       [I] Handle to the desired registry key
5088  *  subkey     [I] Name of desired subkey, or NULL to open hKey directly
5089  *  grfMode    [I] Optional flags
5090  *  riid       [I] IID of requested property bag interface
5091  *  ppv        [O] Address to receive pointer to the new interface
5092  *
5093  * RETURNS
5094  *  success: 0
5095  *  failure: error code
5096  *
5097  */
5098 HRESULT WINAPI SHCreatePropertyBagOnRegKey (HKEY hKey, LPCWSTR subkey,
5099     DWORD grfMode, REFIID riid, void **ppv)
5100 {
5101     FIXME("%p %s %d %s %p STUB\n", hKey, debugstr_w(subkey), grfMode,
5102           debugstr_guid(riid), ppv);
5103 
5104     return E_NOTIMPL;
5105 }
5106 #endif
5107 
5108 #ifndef __REACTOS__ /* See propbag.cpp */
5109 /***********************************************************************
5110  *             SHGetViewStatePropertyBag [SHLWAPI.515]
5111  *
5112  * Retrieves a property bag in which the view state information of a folder
5113  * can be stored.
5114  *
5115  * PARAMS
5116  *  pidl        [I] PIDL of the folder requested
5117  *  bag_name    [I] Name of the property bag requested
5118  *  flags       [I] Optional flags
5119  *  riid        [I] IID of requested property bag interface
5120  *  ppv         [O] Address to receive pointer to the new interface
5121  *
5122  * RETURNS
5123  *  success: S_OK
5124  *  failure: error code
5125  *
5126  */
5127 HRESULT WINAPI SHGetViewStatePropertyBag(LPCITEMIDLIST pidl, LPWSTR bag_name,
5128     DWORD flags, REFIID riid, void **ppv)
5129 {
5130     FIXME("%p %s %d %s %p STUB\n", pidl, debugstr_w(bag_name), flags,
5131           debugstr_guid(riid), ppv);
5132 
5133     return E_NOTIMPL;
5134 }
5135 #endif
5136 
5137 /***********************************************************************
5138  *             SHFormatDateTimeW [SHLWAPI.354]
5139  *
5140  * Produces a string representation of a time.
5141  *
5142  * PARAMS
5143  *  fileTime   [I] Pointer to FILETIME structure specifying the time
5144  *  flags      [I] Flags specifying the desired output
5145  *  buf        [O] Pointer to buffer for output
5146  *  size       [I] Number of characters that can be contained in buffer
5147  *
5148  * RETURNS
5149  *  success: number of characters written to the buffer
5150  *  failure: 0
5151  *
5152  */
5153 INT WINAPI SHFormatDateTimeW(const FILETIME UNALIGNED *fileTime, DWORD *flags,
5154     LPWSTR buf, UINT size)
5155 {
5156 #define SHFORMATDT_UNSUPPORTED_FLAGS (FDTF_RELATIVE | FDTF_LTRDATE | FDTF_RTLDATE | FDTF_NOAUTOREADINGORDER)
5157     DWORD fmt_flags = flags ? *flags : FDTF_DEFAULT;
5158     SYSTEMTIME st;
5159     FILETIME ft;
5160     INT ret = 0;
5161 
5162     TRACE("%p %p %p %u\n", fileTime, flags, buf, size);
5163 
5164     if (!buf || !size)
5165         return 0;
5166 
5167     if (fmt_flags & SHFORMATDT_UNSUPPORTED_FLAGS)
5168         FIXME("ignoring some flags - 0x%08x\n", fmt_flags & SHFORMATDT_UNSUPPORTED_FLAGS);
5169 
5170     FileTimeToLocalFileTime(fileTime, &ft);
5171     FileTimeToSystemTime(&ft, &st);
5172 
5173     /* first of all date */
5174     if (fmt_flags & (FDTF_LONGDATE | FDTF_SHORTDATE))
5175     {
5176         static const WCHAR sep1[] = {',',' ',0};
5177         static const WCHAR sep2[] = {' ',0};
5178 
5179         DWORD date = fmt_flags & FDTF_LONGDATE ? DATE_LONGDATE : DATE_SHORTDATE;
5180         ret = GetDateFormatW(LOCALE_USER_DEFAULT, date, &st, NULL, buf, size);
5181         if (ret >= size) return ret;
5182 
5183         /* add separator */
5184         if (ret < size && (fmt_flags & (FDTF_LONGTIME | FDTF_SHORTTIME)))
5185         {
5186             if ((fmt_flags & FDTF_LONGDATE) && (ret < size + 2))
5187             {
5188                 lstrcatW(&buf[ret-1], sep1);
5189                 ret += 2;
5190             }
5191             else
5192             {
5193                 lstrcatW(&buf[ret-1], sep2);
5194                 ret++;
5195             }
5196         }
5197     }
5198     /* time part */
5199     if (fmt_flags & (FDTF_LONGTIME | FDTF_SHORTTIME))
5200     {
5201         DWORD time = fmt_flags & FDTF_LONGTIME ? 0 : TIME_NOSECONDS;
5202 
5203         if (ret) ret--;
5204         ret += GetTimeFormatW(LOCALE_USER_DEFAULT, time, &st, NULL, &buf[ret], size - ret);
5205     }
5206 
5207     return ret;
5208 
5209 #undef SHFORMATDT_UNSUPPORTED_FLAGS
5210 }
5211 
5212 /***********************************************************************
5213  *             SHFormatDateTimeA [SHLWAPI.353]
5214  *
5215  * See SHFormatDateTimeW.
5216  *
5217  */
5218 INT WINAPI SHFormatDateTimeA(const FILETIME UNALIGNED *fileTime, DWORD *flags,
5219     LPSTR buf, UINT size)
5220 {
5221     WCHAR *bufW;
5222     INT retval;
5223 
5224     if (!buf || !size)
5225         return 0;
5226 
5227     bufW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * size);
5228     retval = SHFormatDateTimeW(fileTime, flags, bufW, size);
5229 
5230     if (retval != 0)
5231         WideCharToMultiByte(CP_ACP, 0, bufW, -1, buf, size, NULL, NULL);
5232 
5233     HeapFree(GetProcessHeap(), 0, bufW);
5234     return retval;
5235 }
5236 
5237 /***********************************************************************
5238  *             ZoneCheckUrlExW [SHLWAPI.231]
5239  *
5240  * Checks the details of the security zone for the supplied site. (?)
5241  *
5242  * PARAMS
5243  *
5244  *  szURL   [I] Pointer to the URL to check
5245  *
5246  *  Other parameters currently unknown.
5247  *
5248  * RETURNS
5249  *  unknown
5250  */
5251 
5252 INT WINAPI ZoneCheckUrlExW(LPWSTR szURL, PVOID pUnknown, DWORD dwUnknown2,
5253     DWORD dwUnknown3, DWORD dwUnknown4, DWORD dwUnknown5, DWORD dwUnknown6,
5254     DWORD dwUnknown7)
5255 {
5256     FIXME("(%s,%p,%x,%x,%x,%x,%x,%x) STUB\n", debugstr_w(szURL), pUnknown, dwUnknown2,
5257         dwUnknown3, dwUnknown4, dwUnknown5, dwUnknown6, dwUnknown7);
5258 
5259     return 0;
5260 }
5261 
5262 /***********************************************************************
5263  *             SHVerbExistsNA [SHLWAPI.196]
5264  *
5265  *
5266  * PARAMS
5267  *
5268  *  verb [I] a string, often appears to be an extension.
5269  *
5270  *  Other parameters currently unknown.
5271  *
5272  * RETURNS
5273  *  unknown
5274  */
5275 INT WINAPI SHVerbExistsNA(LPSTR verb, PVOID pUnknown, PVOID pUnknown2, DWORD dwUnknown3)
5276 {
5277     FIXME("(%s, %p, %p, %i) STUB\n",verb, pUnknown, pUnknown2, dwUnknown3);
5278     return 0;
5279 }
5280 
5281 /*************************************************************************
5282  *      @	[SHLWAPI.538]
5283  *
5284  *  Undocumented:  Implementation guessed at via Name and behavior
5285  *
5286  * PARAMS
5287  *  lpUnknown [I] Object to get an IServiceProvider interface from
5288  *  riid      [I] Function requested for QueryService call
5289  *  lppOut    [O] Destination for the service interface pointer
5290  *
5291  * RETURNS
5292  *  Success: S_OK. lppOut contains an object providing the requested service
5293  *  Failure: An HRESULT error code
5294  *
5295  * NOTES
5296  *  lpUnknown is expected to support the IServiceProvider interface.
5297  */
5298 HRESULT WINAPI IUnknown_QueryServiceForWebBrowserApp(IUnknown* lpUnknown,
5299         REFGUID riid, LPVOID *lppOut)
5300 {
5301     FIXME("%p %s %p semi-STUB\n", lpUnknown, debugstr_guid(riid), lppOut);
5302     return IUnknown_QueryService(lpUnknown,&IID_IWebBrowserApp,riid,lppOut);
5303 }
5304 
5305 #ifdef __REACTOS__
5306 HRESULT VariantChangeTypeForRead(_Inout_ VARIANTARG *pvarg, _In_ VARTYPE vt)
5307 {
5308     HRESULT hr;
5309     VARIANTARG vargTemp;
5310     VARIANT variTemp;
5311 
5312     if (V_VT(pvarg) == vt || vt == VT_EMPTY)
5313         return S_OK;
5314 
5315     vargTemp = *pvarg;
5316 
5317     if (V_VT(&vargTemp) != VT_BSTR || vt <= VT_NULL)
5318         goto DoDefault;
5319 
5320     if (vt == VT_I1 || vt == VT_I2 || vt == VT_I4)
5321     {
5322         if (!StrToIntExW(V_BSTR(&vargTemp), STIF_SUPPORT_HEX, &V_I4(&variTemp)))
5323             goto DoDefault;
5324 
5325         V_VT(&variTemp) = VT_INT;
5326         VariantInit(pvarg);
5327         hr = VariantChangeType(pvarg, &variTemp, 0, vt);
5328         VariantClear(&vargTemp);
5329         return hr;
5330     }
5331 
5332     if (vt <= VT_DECIMAL)
5333         goto DoDefault;
5334 
5335     if (vt == VT_UI1 || vt == VT_UI2 || vt == VT_UI4)
5336     {
5337         if (!StrToIntExW(V_BSTR(&vargTemp), STIF_SUPPORT_HEX, (LPINT)&V_UI4(&variTemp)))
5338             goto DoDefault;
5339 
5340         V_VT(&variTemp) = VT_UINT;
5341         VariantInit(pvarg);
5342         hr = VariantChangeType(pvarg, &variTemp, 0, vt);
5343         VariantClear(&vargTemp);
5344         return hr;
5345     }
5346 
5347     if (vt == VT_INT || vt == VT_UINT)
5348     {
5349         if (!StrToIntExW(V_BSTR(&vargTemp), STIF_SUPPORT_HEX, &V_INT(&variTemp)))
5350             goto DoDefault;
5351 
5352         V_VT(&variTemp) = VT_UINT;
5353         VariantInit(pvarg);
5354         hr = VariantChangeType(pvarg, &variTemp, 0, vt);
5355         VariantClear(&vargTemp);
5356         return hr;
5357     }
5358 
5359 DoDefault:
5360     VariantInit(pvarg);
5361     hr = VariantChangeType(pvarg, &vargTemp, 0, vt);
5362     VariantClear(&vargTemp);
5363     return hr;
5364 }
5365 
5366 BOOL
5367 VariantArrayToBuffer(
5368     _In_ const VARIANT *pvarIn,
5369     _Out_writes_(cbSize) LPVOID pvDest,
5370     _In_ SIZE_T cbSize)
5371 {
5372     LPVOID pvData;
5373     LONG LowerBound, UpperBound;
5374     LPSAFEARRAY pArray;
5375 
5376     /* Only supports byte array */
5377     if (!pvarIn || V_VT(pvarIn) != (VT_UI1 | VT_ARRAY))
5378         return FALSE;
5379 
5380     /* Boundary check and access */
5381     pArray = V_ARRAY(pvarIn);
5382     if (SafeArrayGetDim(pArray) == 1 &&
5383         SUCCEEDED(SafeArrayGetLBound(pArray, 1, &LowerBound)) &&
5384         SUCCEEDED(SafeArrayGetUBound(pArray, 1, &UpperBound)) &&
5385         ((LONG)cbSize <= UpperBound - LowerBound + 1) &&
5386         SUCCEEDED(SafeArrayAccessData(pArray, &pvData)))
5387     {
5388         CopyMemory(pvDest, pvData, cbSize);
5389         SafeArrayUnaccessData(pArray);
5390         return TRUE; /* Success */
5391     }
5392 
5393     return FALSE; /* Failure */
5394 }
5395 
5396 /**************************************************************************
5397  *  SHPropertyBag_ReadType (SHLWAPI.493)
5398  *
5399  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readtype.htm
5400  */
5401 HRESULT WINAPI
5402 SHPropertyBag_ReadType(IPropertyBag *ppb, LPCWSTR pszPropName, VARIANTARG *pvarg, VARTYPE vt)
5403 {
5404     HRESULT hr;
5405 
5406     VariantInit(pvarg);
5407     V_VT(pvarg) = vt;
5408 
5409     hr = IPropertyBag_Read(ppb, pszPropName, pvarg, NULL);
5410     if (FAILED(hr))
5411     {
5412         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
5413         VariantInit(pvarg);
5414         return hr;
5415     }
5416 
5417     return VariantChangeTypeForRead(pvarg, vt);
5418 }
5419 
5420 /**************************************************************************
5421  *  SHPropertyBag_ReadBOOL (SHLWAPI.534)
5422  *
5423  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readbool.htm
5424  */
5425 HRESULT WINAPI SHPropertyBag_ReadBOOL(IPropertyBag *ppb, LPCWSTR pszPropName, BOOL *pbValue)
5426 {
5427     HRESULT hr;
5428     VARIANTARG varg;
5429 
5430     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pbValue);
5431 
5432     if (!ppb || !pszPropName || !pbValue)
5433     {
5434         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pbValue);
5435         return E_INVALIDARG;
5436     }
5437 
5438     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_BOOL);
5439     if (SUCCEEDED(hr))
5440         *pbValue = (V_BOOL(&varg) == VARIANT_TRUE);
5441 
5442     return hr;
5443 }
5444 
5445 /**************************************************************************
5446  *  SHPropertyBag_ReadBOOLOld (SHLWAPI.498)
5447  *
5448  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readboolold.htm
5449  */
5450 BOOL WINAPI SHPropertyBag_ReadBOOLOld(IPropertyBag *ppb, LPCWSTR pszPropName, BOOL bDefValue)
5451 {
5452     VARIANTARG varg;
5453     HRESULT hr;
5454 
5455     TRACE("%p %s %d\n", ppb, debugstr_w(pszPropName), bDefValue);
5456 
5457     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_BOOL);
5458     if (FAILED(hr))
5459         return bDefValue;
5460 
5461     return V_BOOL(&varg) == VARIANT_TRUE;
5462 }
5463 
5464 /**************************************************************************
5465  *  SHPropertyBag_ReadSHORT (SHLWAPI.527)
5466  *
5467  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readshort.htm
5468  */
5469 HRESULT WINAPI SHPropertyBag_ReadSHORT(IPropertyBag *ppb, LPCWSTR pszPropName, SHORT *psValue)
5470 {
5471     HRESULT hr;
5472     VARIANTARG varg;
5473 
5474     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), psValue);
5475 
5476     if (!ppb || !pszPropName || !psValue)
5477     {
5478         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), psValue);
5479         return E_INVALIDARG;
5480     }
5481 
5482     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_UI2);
5483     if (SUCCEEDED(hr))
5484         *psValue = V_UI2(&varg);
5485 
5486     return hr;
5487 }
5488 #endif
5489 
5490 /**************************************************************************
5491  *  SHPropertyBag_ReadLONG (SHLWAPI.496)
5492  *
5493  * This function asks a property bag to read a named property as a LONG.
5494  *
5495  * PARAMS
5496  *  ppb: a IPropertyBag interface
5497  *  pszPropName:  Unicode string that names the property
5498  *  pValue: address to receive the property value as a 32-bit signed integer
5499  *
5500  * RETURNS
5501  *  HRESULT codes
5502 #ifdef __REACTOS__
5503  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readlong.htm
5504 #endif
5505  */
5506 HRESULT WINAPI SHPropertyBag_ReadLONG(IPropertyBag *ppb, LPCWSTR pszPropName, LPLONG pValue)
5507 {
5508 #ifdef __REACTOS__
5509     HRESULT hr;
5510     VARIANTARG varg;
5511 
5512     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pValue);
5513 
5514     if (!ppb || !pszPropName || !pValue)
5515     {
5516         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pValue);
5517         return E_INVALIDARG;
5518     }
5519 
5520     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_I4);
5521     if (SUCCEEDED(hr))
5522         *pValue = V_I4(&varg);
5523 #else
5524     VARIANT var;
5525     HRESULT hr;
5526     TRACE("%p %s %p\n", ppb,debugstr_w(pszPropName),pValue);
5527     if (!pszPropName || !ppb || !pValue)
5528         return E_INVALIDARG;
5529     V_VT(&var) = VT_I4;
5530     hr = IPropertyBag_Read(ppb, pszPropName, &var, NULL);
5531     if (SUCCEEDED(hr))
5532     {
5533         if (V_VT(&var) == VT_I4)
5534             *pValue = V_I4(&var);
5535         else
5536             hr = DISP_E_BADVARTYPE;
5537     }
5538 #endif
5539     return hr;
5540 }
5541 
5542 #ifdef __REACTOS__
5543 /**************************************************************************
5544  *  SHPropertyBag_ReadDWORD (SHLWAPI.507)
5545  *
5546  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readdword.htm
5547  */
5548 HRESULT WINAPI SHPropertyBag_ReadDWORD(IPropertyBag *ppb, LPCWSTR pszPropName, DWORD *pdwValue)
5549 {
5550     HRESULT hr;
5551     VARIANTARG varg;
5552 
5553     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pdwValue);
5554 
5555     if (!ppb || !pszPropName || !pdwValue)
5556     {
5557         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pdwValue);
5558         return E_INVALIDARG;
5559     }
5560 
5561     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_UI4);
5562     if (SUCCEEDED(hr))
5563         *pdwValue = V_UI4(&varg);
5564 
5565     return hr;
5566 }
5567 
5568 /**************************************************************************
5569  *  SHPropertyBag_ReadBSTR (SHLWAPI.520)
5570  *
5571  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readbstr.htm
5572  */
5573 HRESULT WINAPI SHPropertyBag_ReadBSTR(IPropertyBag *ppb, LPCWSTR pszPropName, BSTR *pbstr)
5574 {
5575     HRESULT hr;
5576     VARIANTARG varg;
5577 
5578     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pbstr);
5579 
5580     if (!ppb || !pszPropName || !pbstr)
5581     {
5582         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pbstr);
5583         return E_INVALIDARG;
5584     }
5585 
5586     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_BSTR);
5587     if (FAILED(hr))
5588         *pbstr = NULL;
5589     else
5590         *pbstr = V_BSTR(&varg);
5591 
5592     return hr;
5593 }
5594 
5595 /**************************************************************************
5596  *  SHPropertyBag_ReadStr (SHLWAPI.494)
5597  *
5598  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readstr.htm
5599  */
5600 HRESULT WINAPI SHPropertyBag_ReadStr(IPropertyBag *ppb, LPCWSTR pszPropName, LPWSTR pszDst, int cchMax)
5601 {
5602     HRESULT hr;
5603     VARIANTARG varg;
5604 
5605     TRACE("%p %s %p %d\n", ppb, debugstr_w(pszPropName), pszDst, cchMax);
5606 
5607     if (!ppb || !pszPropName || !pszDst)
5608     {
5609         ERR("%p %s %p %d\n", ppb, debugstr_w(pszPropName), pszDst, cchMax);
5610         return E_INVALIDARG;
5611     }
5612 
5613     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_BSTR);
5614     if (FAILED(hr))
5615         return E_FAIL;
5616 
5617     StrCpyNW(pszDst, V_BSTR(&varg), cchMax);
5618     VariantClear(&varg);
5619     return hr;
5620 }
5621 
5622 /**************************************************************************
5623  *  SHPropertyBag_ReadPOINTL (SHLWAPI.521)
5624  *
5625  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readpointl.htm
5626  */
5627 HRESULT WINAPI SHPropertyBag_ReadPOINTL(IPropertyBag *ppb, LPCWSTR pszPropName, POINTL *pptl)
5628 {
5629     HRESULT hr;
5630     int cch, cch2;
5631     WCHAR *pch, szBuff[MAX_PATH];
5632 
5633     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pptl);
5634 
5635     if (!ppb || !pszPropName || !pptl)
5636     {
5637         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pptl);
5638         return E_INVALIDARG;
5639     }
5640 
5641     StrCpyNW(szBuff, pszPropName, _countof(szBuff));
5642 
5643     cch = lstrlenW(szBuff);
5644     cch2 = _countof(szBuff) - cch;
5645     if (cch2 < _countof(L".x"))
5646     {
5647         ERR("%s is too long\n", debugstr_w(pszPropName));
5648         return E_FAIL;
5649     }
5650 
5651     pch = &szBuff[cch];
5652 
5653     StrCpyNW(pch, L".x", cch2);
5654     hr = SHPropertyBag_ReadLONG(ppb, szBuff, &pptl->x);
5655     if (FAILED(hr))
5656         return hr;
5657 
5658     StrCpyNW(pch, L".y", cch2);
5659     return SHPropertyBag_ReadLONG(ppb, szBuff, &pptl->y);
5660 }
5661 
5662 /**************************************************************************
5663  *  SHPropertyBag_ReadPOINTS (SHLWAPI.525)
5664  *
5665  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readpoints.htm
5666  */
5667 HRESULT WINAPI SHPropertyBag_ReadPOINTS(IPropertyBag *ppb, LPCWSTR pszPropName, POINTS *ppts)
5668 {
5669     HRESULT hr;
5670     POINTL ptl;
5671 
5672     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), ppts);
5673 
5674     if (!ppb || !pszPropName || !ppts)
5675     {
5676         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), ppts);
5677         return E_INVALIDARG;
5678     }
5679 
5680     hr = SHPropertyBag_ReadPOINTL(ppb, pszPropName, &ptl);
5681     if (FAILED(hr))
5682         return hr;
5683 
5684     ppts->x = ptl.x;
5685     ppts->y = ptl.y;
5686     return hr;
5687 }
5688 
5689 /**************************************************************************
5690  *  SHPropertyBag_ReadRECTL (SHLWAPI.523)
5691  *
5692  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readrectl.htm
5693  */
5694 HRESULT WINAPI SHPropertyBag_ReadRECTL(IPropertyBag *ppb, LPCWSTR pszPropName, RECTL *prcl)
5695 {
5696     HRESULT hr;
5697     int cch, cch2;
5698     WCHAR *pch, szBuff[MAX_PATH];
5699 
5700     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), prcl);
5701 
5702     if (!ppb || !pszPropName || !prcl)
5703     {
5704         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), prcl);
5705         return E_INVALIDARG;
5706     }
5707 
5708     StrCpyNW(szBuff, pszPropName, _countof(szBuff));
5709 
5710     cch = lstrlenW(szBuff);
5711     cch2 = _countof(szBuff) - cch;
5712     if (cch2 < _countof(L".bottom"))
5713     {
5714         ERR("%s is too long\n", debugstr_w(pszPropName));
5715         return E_FAIL;
5716     }
5717 
5718     pch = &szBuff[cch];
5719 
5720     StrCpyNW(pch, L".left", cch2);
5721     hr = SHPropertyBag_ReadLONG(ppb, szBuff, &prcl->left);
5722     if (FAILED(hr))
5723         return hr;
5724 
5725     StrCpyNW(pch, L".top", cch2);
5726     hr = SHPropertyBag_ReadLONG(ppb, szBuff, &prcl->top);
5727     if (FAILED(hr))
5728         return hr;
5729 
5730     StrCpyNW(pch, L".right", cch2);
5731     hr = SHPropertyBag_ReadLONG(ppb, szBuff, &prcl->right);
5732     if (FAILED(hr))
5733         return hr;
5734 
5735     StrCpyNW(pch, L".bottom", cch2);
5736     return SHPropertyBag_ReadLONG(ppb, szBuff, &prcl->bottom);
5737 }
5738 
5739 /**************************************************************************
5740  *  SHPropertyBag_ReadGUID (SHLWAPI.505)
5741  *
5742  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readguid.htm
5743  */
5744 HRESULT WINAPI SHPropertyBag_ReadGUID(IPropertyBag *ppb, LPCWSTR pszPropName, GUID *pguid)
5745 {
5746     HRESULT hr;
5747     BOOL bRet;
5748     VARIANT vari;
5749 
5750     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pguid);
5751 
5752     if (!ppb || !pszPropName || !pguid)
5753     {
5754         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pguid);
5755         return E_INVALIDARG;
5756     }
5757 
5758     hr = SHPropertyBag_ReadType(ppb, pszPropName, &vari, VT_EMPTY);
5759     if (FAILED(hr))
5760     {
5761         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pguid);
5762         return hr;
5763     }
5764 
5765     if (V_VT(&vari) == (VT_UI1 | VT_ARRAY)) /* Byte Array */
5766         bRet = VariantArrayToBuffer(&vari, pguid, sizeof(*pguid));
5767     else if (V_VT(&vari) == VT_BSTR)
5768         bRet = GUIDFromStringW(V_BSTR(&vari), pguid);
5769     else
5770 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
5771         bRet = FALSE;
5772 #else
5773         bRet = TRUE; /* This is by design in WinXP/Win2k3. */
5774 #endif
5775 
5776     if (!bRet)
5777         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pguid);
5778 
5779     VariantClear(&vari);
5780     return (bRet ? S_OK : E_FAIL);
5781 }
5782 
5783 /**************************************************************************
5784  *  SHPropertyBag_ReadStream (SHLWAPI.531)
5785  *
5786  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readstream.htm
5787  */
5788 HRESULT WINAPI SHPropertyBag_ReadStream(IPropertyBag *ppb, LPCWSTR pszPropName, IStream **ppStream)
5789 {
5790     HRESULT hr;
5791     VARIANT vari;
5792 
5793     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), ppStream);
5794 
5795     if (!ppb || !pszPropName || !ppStream)
5796     {
5797         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), ppStream);
5798         return E_INVALIDARG;
5799     }
5800 
5801     hr = SHPropertyBag_ReadType(ppb, pszPropName, &vari, VT_UNKNOWN);
5802     if (FAILED(hr))
5803         return hr;
5804 
5805     hr = IUnknown_QueryInterface(V_UNKNOWN(&vari), &IID_IStream, (void **)ppStream);
5806     IUnknown_Release(V_UNKNOWN(&vari));
5807 
5808     return hr;
5809 }
5810 
5811 /**************************************************************************
5812  *  SHPropertyBag_Delete (SHLWAPI.535)
5813  *
5814  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/delete.htm
5815  */
5816 HRESULT WINAPI SHPropertyBag_Delete(IPropertyBag *ppb, LPCWSTR pszPropName)
5817 {
5818     VARIANT vari;
5819 
5820     TRACE("%p %s\n", ppb, debugstr_w(pszPropName));
5821 
5822     if (!ppb || !pszPropName)
5823     {
5824         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
5825         return E_INVALIDARG;
5826     }
5827 
5828     V_VT(&vari) = VT_EMPTY;
5829     return IPropertyBag_Write(ppb, pszPropName, &vari);
5830 }
5831 
5832 /**************************************************************************
5833  *  SHPropertyBag_WriteBOOL (SHLWAPI.499)
5834  *
5835  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writebool.htm
5836  */
5837 HRESULT WINAPI SHPropertyBag_WriteBOOL(IPropertyBag *ppb, LPCWSTR pszPropName, BOOL bValue)
5838 {
5839     VARIANT vari;
5840 
5841     TRACE("%p %s %d\n", ppb, debugstr_w(pszPropName), bValue);
5842 
5843     if (!ppb || !pszPropName)
5844     {
5845         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
5846         return E_INVALIDARG;
5847     }
5848 
5849     V_VT(&vari) = VT_BOOL;
5850     V_BOOL(&vari) = (bValue ? VARIANT_TRUE : VARIANT_FALSE); /* NOTE: VARIANT_TRUE is (SHORT)-1 */
5851     return IPropertyBag_Write(ppb, pszPropName, &vari);
5852 }
5853 
5854 /**************************************************************************
5855  *  SHPropertyBag_WriteSHORT (SHLWAPI.528)
5856  *
5857  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writeshort.htm
5858  */
5859 HRESULT WINAPI SHPropertyBag_WriteSHORT(IPropertyBag *ppb, LPCWSTR pszPropName, SHORT sValue)
5860 {
5861     VARIANT vari;
5862 
5863     TRACE("%p %s %d\n", ppb, debugstr_w(pszPropName), sValue);
5864 
5865     if (!ppb || !pszPropName)
5866     {
5867         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
5868         return E_INVALIDARG;
5869     }
5870 
5871     V_VT(&vari) = VT_UI2;
5872     V_UI2(&vari) = sValue;
5873     return IPropertyBag_Write(ppb, pszPropName, &vari);
5874 }
5875 
5876 /**************************************************************************
5877  *  SHPropertyBag_WriteLONG (SHLWAPI.497)
5878  *
5879  * This function asks a property bag to write a named property as a LONG.
5880  *
5881  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writelong.htm
5882  */
5883 HRESULT WINAPI SHPropertyBag_WriteLONG(IPropertyBag *ppb, LPCWSTR pszPropName, LONG lValue)
5884 {
5885     VARIANT vari;
5886 
5887     TRACE("%p %s %ld\n", ppb, debugstr_w(pszPropName), lValue);
5888 
5889     if (!ppb || !pszPropName)
5890     {
5891         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
5892         return E_INVALIDARG;
5893     }
5894 
5895     V_VT(&vari) = VT_I4;
5896     V_I4(&vari) = lValue;
5897     return IPropertyBag_Write(ppb, pszPropName, &vari);
5898 }
5899 
5900 /**************************************************************************
5901  *  SHPropertyBag_WriteDWORD (SHLWAPI.508)
5902  *
5903  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writedword.htm
5904  */
5905 HRESULT WINAPI SHPropertyBag_WriteDWORD(IPropertyBag *ppb, LPCWSTR pszPropName, DWORD dwValue)
5906 {
5907     VARIANT vari;
5908 
5909     TRACE("%p %s %lu\n", ppb, debugstr_w(pszPropName), dwValue);
5910 
5911     if (!ppb || !pszPropName)
5912     {
5913         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
5914         return E_INVALIDARG;
5915     }
5916 
5917     V_VT(&vari) = VT_UI4;
5918     V_UI4(&vari) = dwValue;
5919     return IPropertyBag_Write(ppb, pszPropName, &vari);
5920 }
5921 
5922 /**************************************************************************
5923  *  SHPropertyBag_WriteStr (SHLWAPI.495)
5924  *
5925  * This function asks a property bag to write a string as the value of a named property.
5926  *
5927  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writestr.htm
5928  */
5929 HRESULT WINAPI SHPropertyBag_WriteStr(IPropertyBag *ppb, LPCWSTR pszPropName, LPCWSTR pszValue)
5930 {
5931     HRESULT hr;
5932     VARIANT vari;
5933 
5934     TRACE("%p %s %s\n", ppb, debugstr_w(pszPropName), debugstr_w(pszValue));
5935 
5936     if (!ppb || !pszPropName)
5937     {
5938         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
5939         return E_INVALIDARG;
5940     }
5941 
5942     V_BSTR(&vari) = SysAllocString(pszValue);
5943     if (!V_BSTR(&vari))
5944         return E_OUTOFMEMORY;
5945 
5946     V_VT(&vari) = VT_BSTR;
5947     hr = IPropertyBag_Write(ppb, pszPropName, &vari);
5948 
5949     SysFreeString(V_BSTR(&vari));
5950     return hr;
5951 }
5952 
5953 /**************************************************************************
5954  *  SHPropertyBag_WriteGUID (SHLWAPI.506)
5955  *
5956  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writeguid.htm
5957  */
5958 HRESULT WINAPI SHPropertyBag_WriteGUID(IPropertyBag *ppb, LPCWSTR pszPropName, const GUID *pguid)
5959 {
5960     WCHAR szBuff[64];
5961 
5962     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pguid);
5963 
5964     if (!ppb || !pszPropName || !pguid)
5965     {
5966         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pguid);
5967         return E_INVALIDARG;
5968     }
5969 
5970     SHStringFromGUIDW(pguid, szBuff, _countof(szBuff));
5971     return SHPropertyBag_WriteStr(ppb, pszPropName, szBuff);
5972 }
5973 
5974 /**************************************************************************
5975  *  SHPropertyBag_WriteStream (SHLWAPI.532)
5976  *
5977  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writestream.htm
5978  */
5979 HRESULT WINAPI SHPropertyBag_WriteStream(IPropertyBag *ppb, LPCWSTR pszPropName, IStream *pStream)
5980 {
5981     VARIANT vari;
5982 
5983     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pStream);
5984 
5985     if (!ppb || !pszPropName || !pStream)
5986     {
5987         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pStream);
5988         return E_INVALIDARG;
5989     }
5990 
5991     V_VT(&vari) = VT_UNKNOWN;
5992     V_UNKNOWN(&vari) = (IUnknown*)pStream;
5993     return IPropertyBag_Write(ppb, pszPropName, &vari);
5994 }
5995 
5996 /**************************************************************************
5997  *  SHPropertyBag_WritePOINTL (SHLWAPI.522)
5998  *
5999  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writepointl.htm
6000  */
6001 HRESULT WINAPI SHPropertyBag_WritePOINTL(IPropertyBag *ppb, LPCWSTR pszPropName, const POINTL *pptl)
6002 {
6003     HRESULT hr;
6004     int cch, cch2;
6005     WCHAR *pch, szBuff[MAX_PATH];
6006 
6007     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pptl);
6008 
6009     if (!ppb || !pszPropName || !pptl)
6010     {
6011         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pptl);
6012         return E_INVALIDARG;
6013     }
6014 
6015     StrCpyNW(szBuff, pszPropName, _countof(szBuff));
6016 
6017     cch = lstrlenW(szBuff);
6018     cch2 = _countof(szBuff) - cch;
6019     if (cch2 < _countof(L".x"))
6020     {
6021         ERR("%s is too long\n", debugstr_w(pszPropName));
6022         return E_FAIL;
6023     }
6024 
6025     pch = &szBuff[cch];
6026 
6027     StrCpyNW(pch, L".x", cch2);
6028     hr = SHPropertyBag_WriteLONG(ppb, szBuff, pptl->x);
6029     if (FAILED(hr))
6030         return hr;
6031 
6032     StrCpyNW(pch, L".y", cch2);
6033     hr = SHPropertyBag_WriteLONG(ppb, szBuff, pptl->y);
6034     if (FAILED(hr))
6035     {
6036         StrCpyNW(pch, L".x", cch2);
6037         return SHPropertyBag_Delete(ppb, szBuff);
6038     }
6039 
6040     return hr;
6041 }
6042 
6043 /**************************************************************************
6044  *  SHPropertyBag_WritePOINTS (SHLWAPI.526)
6045  *
6046  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writepoints.htm
6047  */
6048 HRESULT WINAPI SHPropertyBag_WritePOINTS(IPropertyBag *ppb, LPCWSTR pszPropName, const POINTS *ppts)
6049 {
6050     POINTL pt;
6051 
6052     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), ppts);
6053 
6054     if (!ppb || !pszPropName || !ppts)
6055     {
6056         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), ppts);
6057         return E_INVALIDARG;
6058     }
6059 
6060     pt.x = ppts->x;
6061     pt.y = ppts->y;
6062     return SHPropertyBag_WritePOINTL(ppb, pszPropName, &pt);
6063 }
6064 
6065 /**************************************************************************
6066  *  SHPropertyBag_WriteRECTL (SHLWAPI.524)
6067  *
6068  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writerectl.htm
6069  */
6070 HRESULT WINAPI SHPropertyBag_WriteRECTL(IPropertyBag *ppb, LPCWSTR pszPropName, const RECTL *prcl)
6071 {
6072     HRESULT hr;
6073     int cch, cch2;
6074     WCHAR *pch, szBuff[MAX_PATH];
6075 
6076     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), prcl);
6077 
6078     if (!ppb || !pszPropName || !prcl)
6079     {
6080         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), prcl);
6081         return E_INVALIDARG;
6082     }
6083 
6084     StrCpyNW(szBuff, pszPropName, _countof(szBuff));
6085 
6086     cch = lstrlenW(szBuff);
6087     cch2 = _countof(szBuff) - cch;
6088     if (cch2 < _countof(L".bottom"))
6089     {
6090         ERR("%s is too long\n", debugstr_w(pszPropName));
6091         return E_FAIL;
6092     }
6093 
6094     pch = &szBuff[cch];
6095 
6096     StrCpyNW(pch, L".left", cch2);
6097     hr = SHPropertyBag_WriteLONG(ppb, szBuff, prcl->left);
6098     if (SUCCEEDED(hr))
6099     {
6100         StrCpyNW(pch, L".top", cch2);
6101         hr = SHPropertyBag_WriteLONG(ppb, szBuff, prcl->top);
6102         if (SUCCEEDED(hr))
6103         {
6104             StrCpyNW(pch, L".right", cch2);
6105             hr = SHPropertyBag_WriteLONG(ppb, szBuff, prcl->right);
6106             if (SUCCEEDED(hr))
6107             {
6108                 StrCpyNW(pch, L".bottom", cch2);
6109                 hr = SHPropertyBag_WriteLONG(ppb, szBuff, prcl->bottom);
6110                 if (SUCCEEDED(hr))
6111                     return hr; /* All successful */
6112 
6113                 StrCpyNW(pch, L".right", cch2);
6114                 hr = SHPropertyBag_Delete(ppb, szBuff);
6115                 if (SUCCEEDED(hr))
6116                     return hr;
6117             }
6118 
6119             StrCpyNW(pch, L".top", cch2);
6120             hr = SHPropertyBag_Delete(ppb, szBuff);
6121             if (SUCCEEDED(hr))
6122                 return hr;
6123         }
6124 
6125         StrCpyNW(pch, L".left", cch2);
6126         hr = SHPropertyBag_Delete(ppb, szBuff);
6127         if (SUCCEEDED(hr))
6128             return hr;
6129     }
6130 
6131     return hr;
6132 }
6133 #endif
6134 
6135 /* return flags for SHGetObjectCompatFlags, names derived from registry value names */
6136 #define OBJCOMPAT_OTNEEDSSFCACHE           0x00000001
6137 #define OBJCOMPAT_NO_WEBVIEW               0x00000002
6138 #define OBJCOMPAT_UNBINDABLE               0x00000004
6139 #define OBJCOMPAT_PINDLL                   0x00000008
6140 #define OBJCOMPAT_NEEDSFILESYSANCESTOR     0x00000010
6141 #define OBJCOMPAT_NOTAFILESYSTEM           0x00000020
6142 #define OBJCOMPAT_CTXMENU_NOVERBS          0x00000040
6143 #define OBJCOMPAT_CTXMENU_LIMITEDQI        0x00000080
6144 #define OBJCOMPAT_COCREATESHELLFOLDERONLY  0x00000100
6145 #define OBJCOMPAT_NEEDSSTORAGEANCESTOR     0x00000200
6146 #define OBJCOMPAT_NOLEGACYWEBVIEW          0x00000400
6147 #define OBJCOMPAT_CTXMENU_XPQCMFLAGS       0x00001000
6148 #define OBJCOMPAT_NOIPROPERTYSTORE         0x00002000
6149 
6150 /* a search table for compatibility flags */
6151 struct objcompat_entry {
6152     const WCHAR name[30];
6153     DWORD value;
6154 };
6155 
6156 /* expected to be sorted by name */
6157 static const struct objcompat_entry objcompat_table[] = {
6158     { {'C','O','C','R','E','A','T','E','S','H','E','L','L','F','O','L','D','E','R','O','N','L','Y',0},
6159       OBJCOMPAT_COCREATESHELLFOLDERONLY },
6160     { {'C','T','X','M','E','N','U','_','L','I','M','I','T','E','D','Q','I',0},
6161       OBJCOMPAT_CTXMENU_LIMITEDQI },
6162     { {'C','T','X','M','E','N','U','_','N','O','V','E','R','B','S',0},
6163       OBJCOMPAT_CTXMENU_LIMITEDQI },
6164     { {'C','T','X','M','E','N','U','_','X','P','Q','C','M','F','L','A','G','S',0},
6165       OBJCOMPAT_CTXMENU_XPQCMFLAGS },
6166     { {'N','E','E','D','S','F','I','L','E','S','Y','S','A','N','C','E','S','T','O','R',0},
6167       OBJCOMPAT_NEEDSFILESYSANCESTOR },
6168     { {'N','E','E','D','S','S','T','O','R','A','G','E','A','N','C','E','S','T','O','R',0},
6169       OBJCOMPAT_NEEDSSTORAGEANCESTOR },
6170     { {'N','O','I','P','R','O','P','E','R','T','Y','S','T','O','R','E',0},
6171       OBJCOMPAT_NOIPROPERTYSTORE },
6172     { {'N','O','L','E','G','A','C','Y','W','E','B','V','I','E','W',0},
6173       OBJCOMPAT_NOLEGACYWEBVIEW },
6174     { {'N','O','T','A','F','I','L','E','S','Y','S','T','E','M',0},
6175       OBJCOMPAT_NOTAFILESYSTEM },
6176     { {'N','O','_','W','E','B','V','I','E','W',0},
6177       OBJCOMPAT_NO_WEBVIEW },
6178     { {'O','T','N','E','E','D','S','S','F','C','A','C','H','E',0},
6179       OBJCOMPAT_OTNEEDSSFCACHE },
6180     { {'P','I','N','D','L','L',0},
6181       OBJCOMPAT_PINDLL },
6182     { {'U','N','B','I','N','D','A','B','L','E',0},
6183       OBJCOMPAT_UNBINDABLE }
6184 };
6185 
6186 /**************************************************************************
6187  *  SHGetObjectCompatFlags (SHLWAPI.476)
6188  *
6189  * Function returns an integer representation of compatibility flags stored
6190  * in registry for CLSID under ShellCompatibility subkey.
6191  *
6192  * PARAMS
6193  *  pUnk:  pointer to object IUnknown interface, idetifies CLSID
6194  *  clsid: pointer to CLSID to retrieve data for
6195  *
6196  * RETURNS
6197  *  0 on failure, flags set on success
6198  */
6199 DWORD WINAPI SHGetObjectCompatFlags(IUnknown *pUnk, const CLSID *clsid)
6200 {
6201     static const WCHAR compatpathW[] =
6202         {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
6203          'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
6204          'S','h','e','l','l','C','o','m','p','a','t','i','b','i','l','i','t','y','\\',
6205          'O','b','j','e','c','t','s','\\','%','s',0};
6206     WCHAR strW[sizeof(compatpathW)/sizeof(WCHAR) + 38 /* { CLSID } */];
6207     DWORD ret, length = sizeof(strW)/sizeof(WCHAR);
6208     OLECHAR *clsid_str;
6209     HKEY key;
6210     INT i;
6211 
6212     TRACE("%p %s\n", pUnk, debugstr_guid(clsid));
6213 
6214     if (!pUnk && !clsid) return 0;
6215 
6216     if (pUnk && !clsid)
6217     {
6218         FIXME("iface not handled\n");
6219         return 0;
6220     }
6221 
6222     StringFromCLSID(clsid, &clsid_str);
6223     sprintfW(strW, compatpathW, clsid_str);
6224     CoTaskMemFree(clsid_str);
6225 
6226     ret = RegOpenKeyW(HKEY_LOCAL_MACHINE, strW, &key);
6227     if (ret != ERROR_SUCCESS) return 0;
6228 
6229     /* now collect flag values */
6230     ret = 0;
6231     for (i = 0; RegEnumValueW(key, i, strW, &length, NULL, NULL, NULL, NULL) == ERROR_SUCCESS; i++)
6232     {
6233         INT left, right, res, x;
6234 
6235         /* search in table */
6236         left  = 0;
6237         right = sizeof(objcompat_table) / sizeof(struct objcompat_entry) - 1;
6238 
6239         while (right >= left) {
6240             x = (left + right) / 2;
6241             res = strcmpW(strW, objcompat_table[x].name);
6242             if (res == 0)
6243             {
6244                 ret |= objcompat_table[x].value;
6245                 break;
6246             }
6247             else if (res < 0)
6248                 right = x - 1;
6249             else
6250                 left = x + 1;
6251         }
6252 
6253         length = sizeof(strW)/sizeof(WCHAR);
6254     }
6255 
6256     return ret;
6257 }
6258