xref: /reactos/dll/win32/shlwapi/ordinal.c (revision de5c4720)
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 #ifdef __REACTOS__
2444  *  VOID
2445 #else
2446  *  Success: S_OK
2447 #endif
2448  *
2449  */
2450 #ifdef __REACTOS__
2451 VOID WINAPI SHSetDefaultDialogFont(HWND hWnd, INT id)
2452 #else
2453 HRESULT WINAPI SHSetDefaultDialogFont(HWND hWnd, INT id)
2454 #endif
2455 {
2456 #ifdef __REACTOS__
2457     HFONT hOldFont, hNewFont;
2458     LOGFONTW lfOldFont, lfNewFont;
2459     HWND hwndItem;
2460 
2461     TRACE("(%p, %d)\n", hWnd, id);
2462 
2463     hOldFont = (HFONT)SendMessageW(hWnd, WM_GETFONT, 0, 0);
2464     GetObjectW(hOldFont, sizeof(lfOldFont), &lfOldFont);
2465     SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(lfNewFont), &lfNewFont, 0);
2466 
2467     if (lfOldFont.lfCharSet == lfNewFont.lfCharSet)
2468         return;
2469 
2470     hNewFont = GetPropW(hWnd, L"PropDlgFont");
2471     if (!hNewFont)
2472     {
2473         /* Create the icon-title font of the same height */
2474         lfNewFont.lfHeight = lfOldFont.lfHeight;
2475         hNewFont = CreateFontIndirectW(&lfNewFont);
2476 
2477         /* If creating the font is failed, then keep the old font */
2478         if (!hNewFont)
2479             hNewFont = hOldFont;
2480 
2481         /* Set "PropDlgFont" property if the font is changed */
2482         if (hOldFont != hNewFont)
2483             SetPropW(hWnd, L"PropDlgFont", hNewFont);
2484     }
2485 
2486     hwndItem = GetDlgItem(hWnd, id);
2487     SendMessageW(hwndItem, WM_SETFONT, (WPARAM)hNewFont, 0);
2488 #else
2489     FIXME("(%p, %d) stub\n", hWnd, id);
2490     return S_OK;
2491 #endif
2492 }
2493 
2494 /*************************************************************************
2495  *      @	[SHLWAPI.221]
2496  *
2497  * Remove the "PropDlgFont" property from a window.
2498  *
2499  * PARAMS
2500  *  hWnd [I] Window to remove the property from
2501  *
2502  * RETURNS
2503  *  A handle to the removed property, or NULL if it did not exist.
2504  */
2505 HANDLE WINAPI SHRemoveDefaultDialogFont(HWND hWnd)
2506 {
2507   HANDLE hProp;
2508 
2509   TRACE("(%p)\n", hWnd);
2510 
2511   hProp = GetPropA(hWnd, "PropDlgFont");
2512 
2513   if(hProp)
2514   {
2515     DeleteObject(hProp);
2516     hProp = RemovePropA(hWnd, "PropDlgFont");
2517   }
2518   return hProp;
2519 }
2520 
2521 /*************************************************************************
2522  *      @	[SHLWAPI.236]
2523  *
2524  * Load the in-process server of a given GUID.
2525  *
2526  * PARAMS
2527  *  refiid [I] GUID of the server to load.
2528  *
2529  * RETURNS
2530  *  Success: A handle to the loaded server dll.
2531  *  Failure: A NULL handle.
2532  */
2533 HMODULE WINAPI SHPinDllOfCLSID(REFIID refiid)
2534 {
2535     HKEY newkey;
2536     DWORD type, count;
2537     CHAR value[MAX_PATH], string[MAX_PATH];
2538 
2539     strcpy(string, "CLSID\\");
2540     SHStringFromGUIDA(refiid, string + 6, sizeof(string)/sizeof(char) - 6);
2541     strcat(string, "\\InProcServer32");
2542 
2543     count = MAX_PATH;
2544     RegOpenKeyExA(HKEY_CLASSES_ROOT, string, 0, 1, &newkey);
2545     RegQueryValueExA(newkey, 0, 0, &type, (PBYTE)value, &count);
2546     RegCloseKey(newkey);
2547     return LoadLibraryExA(value, 0, 0);
2548 }
2549 
2550 /*************************************************************************
2551  *      @	[SHLWAPI.237]
2552  *
2553  * Unicode version of SHLWAPI_183.
2554  */
2555 DWORD WINAPI SHRegisterClassW(WNDCLASSW * lpWndClass)
2556 {
2557 	WNDCLASSW WndClass;
2558 
2559 	TRACE("(%p %s)\n",lpWndClass->hInstance, debugstr_w(lpWndClass->lpszClassName));
2560 
2561 	if (GetClassInfoW(lpWndClass->hInstance, lpWndClass->lpszClassName, &WndClass))
2562 		return TRUE;
2563 	return RegisterClassW(lpWndClass);
2564 }
2565 
2566 /*************************************************************************
2567  *      @	[SHLWAPI.238]
2568  *
2569  * Unregister a list of classes.
2570  *
2571  * PARAMS
2572  *  hInst      [I] Application instance that registered the classes
2573  *  lppClasses [I] List of class names
2574  *  iCount     [I] Number of names in lppClasses
2575  *
2576  * RETURNS
2577  *  Nothing.
2578  */
2579 void WINAPI SHUnregisterClassesA(HINSTANCE hInst, LPCSTR *lppClasses, INT iCount)
2580 {
2581   WNDCLASSA WndClass;
2582 
2583   TRACE("(%p,%p,%d)\n", hInst, lppClasses, iCount);
2584 
2585   while (iCount > 0)
2586   {
2587     if (GetClassInfoA(hInst, *lppClasses, &WndClass))
2588       UnregisterClassA(*lppClasses, hInst);
2589     lppClasses++;
2590     iCount--;
2591   }
2592 }
2593 
2594 /*************************************************************************
2595  *      @	[SHLWAPI.239]
2596  *
2597  * Unicode version of SHUnregisterClassesA.
2598  */
2599 void WINAPI SHUnregisterClassesW(HINSTANCE hInst, LPCWSTR *lppClasses, INT iCount)
2600 {
2601   WNDCLASSW WndClass;
2602 
2603   TRACE("(%p,%p,%d)\n", hInst, lppClasses, iCount);
2604 
2605   while (iCount > 0)
2606   {
2607     if (GetClassInfoW(hInst, *lppClasses, &WndClass))
2608       UnregisterClassW(*lppClasses, hInst);
2609     lppClasses++;
2610     iCount--;
2611   }
2612 }
2613 
2614 /*************************************************************************
2615  *      @	[SHLWAPI.240]
2616  *
2617  * Call The correct (Ascii/Unicode) default window procedure for a window.
2618  *
2619  * PARAMS
2620  *  hWnd     [I] Window to call the default procedure for
2621  *  uMessage [I] Message ID
2622  *  wParam   [I] WPARAM of message
2623  *  lParam   [I] LPARAM of message
2624  *
2625  * RETURNS
2626  *  The result of calling DefWindowProcA() or DefWindowProcW().
2627  */
2628 LRESULT CALLBACK SHDefWindowProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
2629 {
2630 	if (IsWindowUnicode(hWnd))
2631 		return DefWindowProcW(hWnd, uMessage, wParam, lParam);
2632 	return DefWindowProcA(hWnd, uMessage, wParam, lParam);
2633 }
2634 
2635 /*************************************************************************
2636  *      @       [SHLWAPI.256]
2637  */
2638 HRESULT WINAPI IUnknown_GetSite(LPUNKNOWN lpUnknown, REFIID iid, PVOID *lppSite)
2639 {
2640   HRESULT hRet = E_INVALIDARG;
2641   LPOBJECTWITHSITE lpSite = NULL;
2642 
2643   TRACE("(%p,%s,%p)\n", lpUnknown, debugstr_guid(iid), lppSite);
2644 
2645   if (lpUnknown && iid && lppSite)
2646   {
2647     hRet = IUnknown_QueryInterface(lpUnknown, &IID_IObjectWithSite,
2648                                    (void**)&lpSite);
2649     if (SUCCEEDED(hRet) && lpSite)
2650     {
2651       hRet = IObjectWithSite_GetSite(lpSite, iid, lppSite);
2652       IObjectWithSite_Release(lpSite);
2653     }
2654   }
2655   return hRet;
2656 }
2657 
2658 /*************************************************************************
2659  *      @	[SHLWAPI.257]
2660  *
2661  * Create a worker window using CreateWindowExA().
2662  *
2663  * PARAMS
2664  *  wndProc    [I] Window procedure
2665  *  hWndParent [I] Parent window
2666  *  dwExStyle  [I] Extra style flags
2667  *  dwStyle    [I] Style flags
2668  *  hMenu      [I] Window menu
2669  *  wnd_extra  [I] Window extra bytes value
2670  *
2671  * RETURNS
2672  *  Success: The window handle of the newly created window.
2673  *  Failure: 0.
2674  */
2675 HWND WINAPI SHCreateWorkerWindowA(WNDPROC wndProc, HWND hWndParent, DWORD dwExStyle,
2676                                   DWORD dwStyle, HMENU hMenu, LONG_PTR wnd_extra)
2677 {
2678   static const char szClass[] = "WorkerA";
2679   WNDCLASSA wc;
2680   HWND hWnd;
2681 
2682   TRACE("(%p, %p, 0x%08x, 0x%08x, %p, 0x%08lx)\n",
2683          wndProc, hWndParent, dwExStyle, dwStyle, hMenu, wnd_extra);
2684 
2685   /* Create Window class */
2686   wc.style         = 0;
2687   wc.lpfnWndProc   = DefWindowProcA;
2688   wc.cbClsExtra    = 0;
2689   wc.cbWndExtra    = sizeof(LONG_PTR);
2690   wc.hInstance     = shlwapi_hInstance;
2691   wc.hIcon         = NULL;
2692   wc.hCursor       = LoadCursorA(NULL, (LPSTR)IDC_ARROW);
2693   wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
2694   wc.lpszMenuName  = NULL;
2695   wc.lpszClassName = szClass;
2696 
2697   SHRegisterClassA(&wc);
2698 
2699   hWnd = CreateWindowExA(dwExStyle, szClass, 0, dwStyle, 0, 0, 0, 0,
2700                          hWndParent, hMenu, shlwapi_hInstance, 0);
2701   if (hWnd)
2702   {
2703     SetWindowLongPtrW(hWnd, 0, wnd_extra);
2704     if (wndProc) SetWindowLongPtrA(hWnd, GWLP_WNDPROC, (LONG_PTR)wndProc);
2705   }
2706 
2707   return hWnd;
2708 }
2709 
2710 #ifndef __REACTOS__ /* The followings are defined in <shlwapi_undoc.h> */
2711 typedef struct tagPOLICYDATA
2712 {
2713   DWORD policy;        /* flags value passed to SHRestricted */
2714   LPCWSTR appstr;      /* application str such as "Explorer" */
2715   LPCWSTR keystr;      /* name of the actual registry key / policy */
2716 } POLICYDATA, *LPPOLICYDATA;
2717 
2718 #define SHELL_NO_POLICY 0xffffffff
2719 
2720 /* default shell policy registry key */
2721 static const WCHAR strRegistryPolicyW[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o',
2722                                       's','o','f','t','\\','W','i','n','d','o','w','s','\\',
2723                                       'C','u','r','r','e','n','t','V','e','r','s','i','o','n',
2724                                       '\\','P','o','l','i','c','i','e','s',0};
2725 #endif /* ndef __REACTOS__ */
2726 
2727 /*************************************************************************
2728  * @                          [SHLWAPI.271]
2729  *
2730  * Retrieve a policy value from the registry.
2731  *
2732  * PARAMS
2733  *  lpSubKey   [I]   registry key name
2734  *  lpSubName  [I]   subname of registry key
2735  *  lpValue    [I]   value name of registry value
2736  *
2737  * RETURNS
2738  *  the value associated with the registry key or 0 if not found
2739  */
2740 DWORD WINAPI SHGetRestriction(LPCWSTR lpSubKey, LPCWSTR lpSubName, LPCWSTR lpValue)
2741 {
2742 #ifdef __REACTOS__
2743     WCHAR szPath[MAX_PATH];
2744     DWORD dwSize, dwValue = 0;
2745 
2746     TRACE("(%s, %s, %s)\n", debugstr_w(lpSubKey), debugstr_w(lpSubName), debugstr_w(lpValue));
2747 
2748     if (!lpSubKey)
2749         lpSubKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies";
2750 
2751     PathCombineW(szPath, lpSubKey, lpSubName);
2752 
2753     dwSize = sizeof(dwValue);
2754     if (SHGetValueW(HKEY_LOCAL_MACHINE, szPath, lpValue, NULL, &dwValue, &dwSize) == ERROR_SUCCESS)
2755         return dwValue;
2756 
2757     dwSize = sizeof(dwValue);
2758     SHGetValueW(HKEY_CURRENT_USER, szPath, lpValue, NULL, &dwValue, &dwSize);
2759     return dwValue;
2760 #else
2761 	DWORD retval, datsize = sizeof(retval);
2762 	HKEY hKey;
2763 
2764 	if (!lpSubKey)
2765 	  lpSubKey = strRegistryPolicyW;
2766 
2767 	retval = RegOpenKeyW(HKEY_LOCAL_MACHINE, lpSubKey, &hKey);
2768         if (retval != ERROR_SUCCESS)
2769 	  retval = RegOpenKeyW(HKEY_CURRENT_USER, lpSubKey, &hKey);
2770 	if (retval != ERROR_SUCCESS)
2771 	  return 0;
2772 
2773         SHGetValueW(hKey, lpSubName, lpValue, NULL, &retval, &datsize);
2774 	RegCloseKey(hKey);
2775 	return retval;
2776 #endif
2777 }
2778 
2779 /*************************************************************************
2780  * @                         [SHLWAPI.266]
2781  *
2782  * Helper function to retrieve the possibly cached value for a specific policy
2783  *
2784  * PARAMS
2785  *  policy     [I]   The policy to look for
2786  *  initial    [I]   Main registry key to open, if NULL use default
2787  *  polTable   [I]   Table of known policies, 0 terminated
2788  *  polArr     [I]   Cache array of policy values
2789  *
2790  * RETURNS
2791  *  The retrieved policy value or 0 if not successful
2792  *
2793  * NOTES
2794  *  This function is used by the native SHRestricted function to search for the
2795  *  policy and cache it once retrieved. The current Wine implementation uses a
2796  *  different POLICYDATA structure and implements a similar algorithm adapted to
2797  *  that structure.
2798  */
2799 #ifdef __REACTOS__
2800 DWORD WINAPI
2801 SHRestrictionLookup(
2802     _In_ DWORD policy,
2803     _In_ LPCWSTR initial,
2804     _In_ const POLICYDATA *polTable,
2805     _Inout_ LPDWORD polArr)
2806 #else
2807 DWORD WINAPI SHRestrictionLookup(
2808 	DWORD policy,
2809 	LPCWSTR initial,
2810 	LPPOLICYDATA polTable,
2811 	LPDWORD polArr)
2812 #endif
2813 {
2814 	TRACE("(0x%08x %s %p %p)\n", policy, debugstr_w(initial), polTable, polArr);
2815 
2816 #ifndef __REACTOS__
2817 	if (!polTable || !polArr)
2818 	  return 0;
2819 #endif
2820 
2821 #ifndef __REACTOS__
2822 	for (;polTable->appstr; polTable++, polArr++)
2823 #else
2824 	for (;polTable->policy; polTable++, polArr++)
2825 #endif
2826 	{
2827 	  if (policy == polTable->policy)
2828 	  {
2829 	    /* we have a known policy */
2830 
2831 	    /* check if this policy has been cached */
2832             if (*polArr == SHELL_NO_POLICY)
2833 	      *polArr = SHGetRestriction(initial, polTable->appstr, polTable->keystr);
2834 	    return *polArr;
2835 	  }
2836 	}
2837 	/* we don't know this policy, return 0 */
2838 	TRACE("unknown policy: (%08x)\n", policy);
2839 	return 0;
2840 }
2841 
2842 /*************************************************************************
2843  *      @	[SHLWAPI.267]
2844  *
2845  * Get an interface from an object.
2846  *
2847  * RETURNS
2848  *  Success: S_OK. ppv contains the requested interface.
2849  *  Failure: An HRESULT error code.
2850  *
2851  * NOTES
2852  *   This QueryInterface asks the inner object for an interface. In case
2853  *   of aggregation this request would be forwarded by the inner to the
2854  *   outer object. This function asks the inner object directly for the
2855  *   interface circumventing the forwarding to the outer object.
2856  */
2857 HRESULT WINAPI SHWeakQueryInterface(
2858 	IUnknown * pUnk,   /* [in] Outer object */
2859 	IUnknown * pInner, /* [in] Inner object */
2860 	IID * riid, /* [in] Interface GUID to query for */
2861 	LPVOID* ppv) /* [out] Destination for queried interface */
2862 {
2863 	HRESULT hret = E_NOINTERFACE;
2864 	TRACE("(pUnk=%p pInner=%p\n\tIID:  %s %p)\n",pUnk,pInner,debugstr_guid(riid), ppv);
2865 
2866 	*ppv = NULL;
2867 	if(pUnk && pInner) {
2868             hret = IUnknown_QueryInterface(pInner, riid, ppv);
2869 	    if (SUCCEEDED(hret)) IUnknown_Release(pUnk);
2870 	}
2871 	TRACE("-- 0x%08x\n", hret);
2872 	return hret;
2873 }
2874 
2875 /*************************************************************************
2876  *      @	[SHLWAPI.268]
2877  *
2878  * Move a reference from one interface to another.
2879  *
2880  * PARAMS
2881  *   lpDest     [O] Destination to receive the reference
2882  *   lppUnknown [O] Source to give up the reference to lpDest
2883  *
2884  * RETURNS
2885  *  Nothing.
2886  */
2887 VOID WINAPI SHWeakReleaseInterface(IUnknown *lpDest, IUnknown **lppUnknown)
2888 {
2889   TRACE("(%p,%p)\n", lpDest, lppUnknown);
2890 
2891   if (*lppUnknown)
2892   {
2893     /* Copy Reference*/
2894     IUnknown_AddRef(lpDest);
2895     IUnknown_AtomicRelease(lppUnknown); /* Release existing interface */
2896   }
2897 }
2898 
2899 /*************************************************************************
2900  *      @	[SHLWAPI.269]
2901  *
2902  * Convert an ASCII string of a CLSID into a CLSID.
2903  *
2904  * PARAMS
2905  *  idstr [I] String representing a CLSID in registry format
2906  *  id    [O] Destination for the converted CLSID
2907  *
2908  * RETURNS
2909  *  Success: TRUE. id contains the converted CLSID.
2910  *  Failure: FALSE.
2911  */
2912 BOOL WINAPI GUIDFromStringA(LPCSTR idstr, CLSID *id)
2913 {
2914   WCHAR wClsid[40];
2915   MultiByteToWideChar(CP_ACP, 0, idstr, -1, wClsid, sizeof(wClsid)/sizeof(WCHAR));
2916   return SUCCEEDED(CLSIDFromString(wClsid, id));
2917 }
2918 
2919 /*************************************************************************
2920  *      @	[SHLWAPI.270]
2921  *
2922  * Unicode version of GUIDFromStringA.
2923  */
2924 BOOL WINAPI GUIDFromStringW(LPCWSTR idstr, CLSID *id)
2925 {
2926     return SUCCEEDED(CLSIDFromString((LPCOLESTR)idstr, id));
2927 }
2928 
2929 /*************************************************************************
2930  *      @	[SHLWAPI.276]
2931  *
2932  * Determine if the browser is integrated into the shell, and set a registry
2933  * key accordingly.
2934  *
2935  * PARAMS
2936  *  None.
2937  *
2938  * RETURNS
2939  *  1, If the browser is not integrated.
2940  *  2, If the browser is integrated.
2941  *
2942  * NOTES
2943  *  The key "HKLM\Software\Microsoft\Internet Explorer\IntegratedBrowser" is
2944  *  either set to TRUE, or removed depending on whether the browser is deemed
2945  *  to be integrated.
2946  */
2947 DWORD WINAPI WhichPlatform(void)
2948 {
2949   static const char szIntegratedBrowser[] = "IntegratedBrowser";
2950   static DWORD dwState = 0;
2951   HKEY hKey;
2952   DWORD dwRet, dwData, dwSize;
2953   HMODULE hshell32;
2954 
2955   if (dwState)
2956     return dwState;
2957 
2958   /* If shell32 exports DllGetVersion(), the browser is integrated */
2959   dwState = 1;
2960   hshell32 = LoadLibraryA("shell32.dll");
2961   if (hshell32)
2962   {
2963     FARPROC pDllGetVersion;
2964     pDllGetVersion = GetProcAddress(hshell32, "DllGetVersion");
2965     dwState = pDllGetVersion ? 2 : 1;
2966     FreeLibrary(hshell32);
2967   }
2968 
2969   /* Set or delete the key accordingly */
2970   dwRet = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
2971                         "Software\\Microsoft\\Internet Explorer", 0,
2972                          KEY_ALL_ACCESS, &hKey);
2973   if (!dwRet)
2974   {
2975     dwRet = RegQueryValueExA(hKey, szIntegratedBrowser, 0, 0,
2976                              (LPBYTE)&dwData, &dwSize);
2977 
2978     if (!dwRet && dwState == 1)
2979     {
2980       /* Value exists but browser is not integrated */
2981       RegDeleteValueA(hKey, szIntegratedBrowser);
2982     }
2983     else if (dwRet && dwState == 2)
2984     {
2985       /* Browser is integrated but value does not exist */
2986       dwData = TRUE;
2987       RegSetValueExA(hKey, szIntegratedBrowser, 0, REG_DWORD,
2988                      (LPBYTE)&dwData, sizeof(dwData));
2989     }
2990     RegCloseKey(hKey);
2991   }
2992   return dwState;
2993 }
2994 
2995 /*************************************************************************
2996  *      @	[SHLWAPI.278]
2997  *
2998  * Unicode version of SHCreateWorkerWindowA.
2999  */
3000 HWND WINAPI SHCreateWorkerWindowW(WNDPROC wndProc, HWND hWndParent, DWORD dwExStyle,
3001                                   DWORD dwStyle, HMENU hMenu, LONG_PTR wnd_extra)
3002 {
3003   static const WCHAR szClass[] = { 'W', 'o', 'r', 'k', 'e', 'r', 'W', 0 };
3004   WNDCLASSW wc;
3005   HWND hWnd;
3006 
3007   TRACE("(%p, %p, 0x%08x, 0x%08x, %p, 0x%08lx)\n",
3008          wndProc, hWndParent, dwExStyle, dwStyle, hMenu, wnd_extra);
3009 
3010   /* If our OS is natively ANSI, use the ANSI version */
3011   if (GetVersion() & 0x80000000)  /* not NT */
3012   {
3013     TRACE("fallback to ANSI, ver 0x%08x\n", GetVersion());
3014     return SHCreateWorkerWindowA(wndProc, hWndParent, dwExStyle, dwStyle, hMenu, wnd_extra);
3015   }
3016 
3017   /* Create Window class */
3018   wc.style         = 0;
3019   wc.lpfnWndProc   = DefWindowProcW;
3020   wc.cbClsExtra    = 0;
3021   wc.cbWndExtra    = sizeof(LONG_PTR);
3022   wc.hInstance     = shlwapi_hInstance;
3023   wc.hIcon         = NULL;
3024   wc.hCursor       = LoadCursorW(NULL, (LPWSTR)IDC_ARROW);
3025   wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
3026   wc.lpszMenuName  = NULL;
3027   wc.lpszClassName = szClass;
3028 
3029   SHRegisterClassW(&wc);
3030 
3031   hWnd = CreateWindowExW(dwExStyle, szClass, 0, dwStyle, 0, 0, 0, 0,
3032                          hWndParent, hMenu, shlwapi_hInstance, 0);
3033   if (hWnd)
3034   {
3035     SetWindowLongPtrW(hWnd, 0, wnd_extra);
3036     if (wndProc) SetWindowLongPtrW(hWnd, GWLP_WNDPROC, (LONG_PTR)wndProc);
3037   }
3038 
3039   return hWnd;
3040 }
3041 
3042 /*************************************************************************
3043  *      @	[SHLWAPI.279]
3044  *
3045  * Get and show a context menu from a shell folder.
3046  *
3047  * PARAMS
3048  *  hWnd           [I] Window displaying the shell folder
3049  *  lpFolder       [I] IShellFolder interface
3050  *  lpApidl        [I] Id for the particular folder desired
3051  *
3052  * RETURNS
3053  *  Success: S_OK.
3054  *  Failure: An HRESULT error code indicating the error.
3055  */
3056 HRESULT WINAPI SHInvokeDefaultCommand(HWND hWnd, IShellFolder* lpFolder, LPCITEMIDLIST lpApidl)
3057 {
3058     TRACE("%p %p %p\n", hWnd, lpFolder, lpApidl);
3059     return SHInvokeCommand(hWnd, lpFolder, lpApidl, 0);
3060 }
3061 
3062 /*************************************************************************
3063  *      @	[SHLWAPI.281]
3064  *
3065  * _SHPackDispParamsV
3066  */
3067 HRESULT WINAPI SHPackDispParamsV(DISPPARAMS *params, VARIANTARG *args, UINT cnt, __ms_va_list valist)
3068 {
3069   VARIANTARG *iter;
3070 
3071   TRACE("(%p %p %u ...)\n", params, args, cnt);
3072 
3073   params->rgvarg = args;
3074   params->rgdispidNamedArgs = NULL;
3075   params->cArgs = cnt;
3076   params->cNamedArgs = 0;
3077 
3078   iter = args+cnt;
3079 
3080   while(iter-- > args) {
3081     V_VT(iter) = va_arg(valist, enum VARENUM);
3082 
3083     TRACE("vt=%d\n", V_VT(iter));
3084 
3085     if(V_VT(iter) & VT_BYREF) {
3086       V_BYREF(iter) = va_arg(valist, LPVOID);
3087     } else {
3088       switch(V_VT(iter)) {
3089       case VT_I4:
3090         V_I4(iter) = va_arg(valist, LONG);
3091         break;
3092       case VT_BSTR:
3093         V_BSTR(iter) = va_arg(valist, BSTR);
3094         break;
3095       case VT_DISPATCH:
3096         V_DISPATCH(iter) = va_arg(valist, IDispatch*);
3097         break;
3098       case VT_BOOL:
3099         V_BOOL(iter) = va_arg(valist, int);
3100         break;
3101       case VT_UNKNOWN:
3102         V_UNKNOWN(iter) = va_arg(valist, IUnknown*);
3103         break;
3104       default:
3105         V_VT(iter) = VT_I4;
3106         V_I4(iter) = va_arg(valist, LONG);
3107       }
3108     }
3109   }
3110 
3111   return S_OK;
3112 }
3113 
3114 /*************************************************************************
3115  *      @       [SHLWAPI.282]
3116  *
3117  * SHPackDispParams
3118  */
3119 HRESULT WINAPIV SHPackDispParams(DISPPARAMS *params, VARIANTARG *args, UINT cnt, ...)
3120 {
3121   __ms_va_list valist;
3122   HRESULT hres;
3123 
3124   __ms_va_start(valist, cnt);
3125   hres = SHPackDispParamsV(params, args, cnt, valist);
3126   __ms_va_end(valist);
3127   return hres;
3128 }
3129 
3130 /*************************************************************************
3131  *      SHLWAPI_InvokeByIID
3132  *
3133  *   This helper function calls IDispatch::Invoke for each sink
3134  * which implements given iid or IDispatch.
3135  *
3136  */
3137 static HRESULT SHLWAPI_InvokeByIID(
3138         IConnectionPoint* iCP,
3139         REFIID iid,
3140         DISPID dispId,
3141         DISPPARAMS* dispParams)
3142 {
3143   IEnumConnections *enumerator;
3144   CONNECTDATA rgcd;
3145   static DISPPARAMS empty = {NULL, NULL, 0, 0};
3146   DISPPARAMS* params = dispParams;
3147 
3148   HRESULT result = IConnectionPoint_EnumConnections(iCP, &enumerator);
3149   if (FAILED(result))
3150     return result;
3151 
3152   /* Invoke is never happening with an NULL dispParams */
3153   if (!params)
3154     params = &empty;
3155 
3156   while(IEnumConnections_Next(enumerator, 1, &rgcd, NULL)==S_OK)
3157   {
3158     IDispatch *dispIface;
3159     if ((iid && SUCCEEDED(IUnknown_QueryInterface(rgcd.pUnk, iid, (LPVOID*)&dispIface))) ||
3160         SUCCEEDED(IUnknown_QueryInterface(rgcd.pUnk, &IID_IDispatch, (LPVOID*)&dispIface)))
3161     {
3162       IDispatch_Invoke(dispIface, dispId, &IID_NULL, 0, DISPATCH_METHOD, params, NULL, NULL, NULL);
3163       IDispatch_Release(dispIface);
3164     }
3165     IUnknown_Release(rgcd.pUnk);
3166   }
3167 
3168   IEnumConnections_Release(enumerator);
3169 
3170   return S_OK;
3171 }
3172 
3173 /*************************************************************************
3174  *  IConnectionPoint_InvokeWithCancel   [SHLWAPI.283]
3175  */
3176 HRESULT WINAPI IConnectionPoint_InvokeWithCancel( IConnectionPoint* iCP,
3177                                                   DISPID dispId, DISPPARAMS* dispParams,
3178                                                   DWORD unknown1, DWORD unknown2 )
3179 {
3180     IID iid;
3181     HRESULT result;
3182 
3183     FIXME("(%p)->(0x%x %p %x %x) partial stub\n", iCP, dispId, dispParams, unknown1, unknown2);
3184 
3185     result = IConnectionPoint_GetConnectionInterface(iCP, &iid);
3186     if (SUCCEEDED(result))
3187         result = SHLWAPI_InvokeByIID(iCP, &iid, dispId, dispParams);
3188     else
3189         result = SHLWAPI_InvokeByIID(iCP, NULL, dispId, dispParams);
3190 
3191     return result;
3192 }
3193 
3194 
3195 /*************************************************************************
3196  *      @	[SHLWAPI.284]
3197  *
3198  *  IConnectionPoint_SimpleInvoke
3199  */
3200 HRESULT WINAPI IConnectionPoint_SimpleInvoke(
3201         IConnectionPoint* iCP,
3202         DISPID dispId,
3203         DISPPARAMS* dispParams)
3204 {
3205   IID iid;
3206   HRESULT result;
3207 
3208   TRACE("(%p)->(0x%x %p)\n",iCP,dispId,dispParams);
3209 
3210   result = IConnectionPoint_GetConnectionInterface(iCP, &iid);
3211   if (SUCCEEDED(result))
3212     result = SHLWAPI_InvokeByIID(iCP, &iid, dispId, dispParams);
3213   else
3214     result = SHLWAPI_InvokeByIID(iCP, NULL, dispId, dispParams);
3215 
3216   return result;
3217 }
3218 
3219 /*************************************************************************
3220  *      @	[SHLWAPI.285]
3221  *
3222  * Notify an IConnectionPoint object of changes.
3223  *
3224  * PARAMS
3225  *  lpCP   [I] Object to notify
3226  *  dispID [I]
3227  *
3228  * RETURNS
3229  *  Success: S_OK.
3230  *  Failure: E_NOINTERFACE, if lpCP is NULL or does not support the
3231  *           IConnectionPoint interface.
3232  */
3233 HRESULT WINAPI IConnectionPoint_OnChanged(IConnectionPoint* lpCP, DISPID dispID)
3234 {
3235   IEnumConnections *lpEnum;
3236   HRESULT hRet = E_NOINTERFACE;
3237 
3238   TRACE("(%p,0x%8X)\n", lpCP, dispID);
3239 
3240   /* Get an enumerator for the connections */
3241   if (lpCP)
3242     hRet = IConnectionPoint_EnumConnections(lpCP, &lpEnum);
3243 
3244   if (SUCCEEDED(hRet))
3245   {
3246     IPropertyNotifySink *lpSink;
3247     CONNECTDATA connData;
3248     ULONG ulFetched;
3249 
3250     /* Call OnChanged() for every notify sink in the connection point */
3251     while (IEnumConnections_Next(lpEnum, 1, &connData, &ulFetched) == S_OK)
3252     {
3253       if (SUCCEEDED(IUnknown_QueryInterface(connData.pUnk, &IID_IPropertyNotifySink, (void**)&lpSink)) &&
3254           lpSink)
3255       {
3256         IPropertyNotifySink_OnChanged(lpSink, dispID);
3257         IPropertyNotifySink_Release(lpSink);
3258       }
3259       IUnknown_Release(connData.pUnk);
3260     }
3261 
3262     IEnumConnections_Release(lpEnum);
3263   }
3264   return hRet;
3265 }
3266 
3267 /*************************************************************************
3268  *      @	[SHLWAPI.286]
3269  *
3270  *  IUnknown_CPContainerInvokeParam
3271  */
3272 HRESULT WINAPIV IUnknown_CPContainerInvokeParam(
3273         IUnknown *container,
3274         REFIID riid,
3275         DISPID dispId,
3276         VARIANTARG* buffer,
3277         DWORD cParams, ...)
3278 {
3279   HRESULT result;
3280   IConnectionPoint *iCP;
3281   IConnectionPointContainer *iCPC;
3282   DISPPARAMS dispParams = {buffer, NULL, cParams, 0};
3283   __ms_va_list valist;
3284 
3285   if (!container)
3286     return E_NOINTERFACE;
3287 
3288   result = IUnknown_QueryInterface(container, &IID_IConnectionPointContainer,(LPVOID*) &iCPC);
3289   if (FAILED(result))
3290       return result;
3291 
3292   result = IConnectionPointContainer_FindConnectionPoint(iCPC, riid, &iCP);
3293   IConnectionPointContainer_Release(iCPC);
3294   if(FAILED(result))
3295       return result;
3296 
3297   __ms_va_start(valist, cParams);
3298   SHPackDispParamsV(&dispParams, buffer, cParams, valist);
3299   __ms_va_end(valist);
3300 
3301   result = SHLWAPI_InvokeByIID(iCP, riid, dispId, &dispParams);
3302   IConnectionPoint_Release(iCP);
3303 
3304   return result;
3305 }
3306 
3307 /*************************************************************************
3308  *      @	[SHLWAPI.287]
3309  *
3310  * Notify an IConnectionPointContainer object of changes.
3311  *
3312  * PARAMS
3313  *  lpUnknown [I] Object to notify
3314  *  dispID    [I]
3315  *
3316  * RETURNS
3317  *  Success: S_OK.
3318  *  Failure: E_NOINTERFACE, if lpUnknown is NULL or does not support the
3319  *           IConnectionPointContainer interface.
3320  */
3321 HRESULT WINAPI IUnknown_CPContainerOnChanged(IUnknown *lpUnknown, DISPID dispID)
3322 {
3323   IConnectionPointContainer* lpCPC = NULL;
3324   HRESULT hRet = E_NOINTERFACE;
3325 
3326   TRACE("(%p,0x%8X)\n", lpUnknown, dispID);
3327 
3328   if (lpUnknown)
3329     hRet = IUnknown_QueryInterface(lpUnknown, &IID_IConnectionPointContainer, (void**)&lpCPC);
3330 
3331   if (SUCCEEDED(hRet))
3332   {
3333     IConnectionPoint* lpCP;
3334 
3335     hRet = IConnectionPointContainer_FindConnectionPoint(lpCPC, &IID_IPropertyNotifySink, &lpCP);
3336     IConnectionPointContainer_Release(lpCPC);
3337 
3338     hRet = IConnectionPoint_OnChanged(lpCP, dispID);
3339     IConnectionPoint_Release(lpCP);
3340   }
3341   return hRet;
3342 }
3343 
3344 /*************************************************************************
3345  *      @	[SHLWAPI.289]
3346  *
3347  * See PlaySoundW.
3348  */
3349 BOOL WINAPI PlaySoundWrapW(LPCWSTR pszSound, HMODULE hmod, DWORD fdwSound)
3350 {
3351     return PlaySoundW(pszSound, hmod, fdwSound);
3352 }
3353 
3354 #ifndef __REACTOS__ /* See propbag.cpp */
3355 /*************************************************************************
3356  *      @	[SHLWAPI.294]
3357  *
3358  * Retrieve a key value from an INI file.  See GetPrivateProfileString for
3359  * more information.
3360  *
3361  * PARAMS
3362  *  appName   [I] The section in the INI file that contains the key
3363  *  keyName   [I] The key to be retrieved
3364  *  out       [O] The buffer into which the key's value will be copied
3365  *  outLen    [I] The length of the `out' buffer
3366  *  filename  [I] The location of the INI file
3367  *
3368  * RETURNS
3369  *  Length of string copied into `out'.
3370  */
3371 DWORD WINAPI SHGetIniStringW(LPCWSTR appName, LPCWSTR keyName, LPWSTR out,
3372         DWORD outLen, LPCWSTR filename)
3373 {
3374     INT ret;
3375     WCHAR *buf;
3376 
3377     TRACE("(%s,%s,%p,%08x,%s)\n", debugstr_w(appName), debugstr_w(keyName),
3378         out, outLen, debugstr_w(filename));
3379 
3380     if(outLen == 0)
3381         return 0;
3382 
3383     buf = HeapAlloc(GetProcessHeap(), 0, outLen * sizeof(WCHAR));
3384     if(!buf){
3385         *out = 0;
3386         return 0;
3387     }
3388 
3389     ret = GetPrivateProfileStringW(appName, keyName, NULL, buf, outLen, filename);
3390     if(ret)
3391         strcpyW(out, buf);
3392     else
3393         *out = 0;
3394 
3395     HeapFree(GetProcessHeap(), 0, buf);
3396 
3397     return strlenW(out);
3398 }
3399 #endif
3400 
3401 #ifndef __REACTOS__ /* See propbag.cpp */
3402 /*************************************************************************
3403  *      @	[SHLWAPI.295]
3404  *
3405  * Set a key value in an INI file.  See WritePrivateProfileString for
3406  * more information.
3407  *
3408  * PARAMS
3409  *  appName   [I] The section in the INI file that contains the key
3410  *  keyName   [I] The key to be set
3411  *  str       [O] The value of the key
3412  *  filename  [I] The location of the INI file
3413  *
3414  * RETURNS
3415  *   Success: TRUE
3416  *   Failure: FALSE
3417  */
3418 BOOL WINAPI SHSetIniStringW(LPCWSTR appName, LPCWSTR keyName, LPCWSTR str,
3419         LPCWSTR filename)
3420 {
3421     TRACE("(%s, %p, %s, %s)\n", debugstr_w(appName), keyName, debugstr_w(str),
3422             debugstr_w(filename));
3423 
3424     return WritePrivateProfileStringW(appName, keyName, str, filename);
3425 }
3426 #endif
3427 
3428 /*************************************************************************
3429  *      @	[SHLWAPI.313]
3430  *
3431  * See SHGetFileInfoW.
3432  */
3433 DWORD WINAPI SHGetFileInfoWrapW(LPCWSTR path, DWORD dwFileAttributes,
3434                          SHFILEINFOW *psfi, UINT sizeofpsfi, UINT flags)
3435 {
3436     return SHGetFileInfoW(path, dwFileAttributes, psfi, sizeofpsfi, flags);
3437 }
3438 
3439 /*************************************************************************
3440  *      @	[SHLWAPI.318]
3441  *
3442  * See DragQueryFileW.
3443  */
3444 UINT WINAPI DragQueryFileWrapW(HDROP hDrop, UINT lFile, LPWSTR lpszFile, UINT lLength)
3445 {
3446     return DragQueryFileW(hDrop, lFile, lpszFile, lLength);
3447 }
3448 
3449 /*************************************************************************
3450  *      @	[SHLWAPI.333]
3451  *
3452  * See SHBrowseForFolderW.
3453  */
3454 LPITEMIDLIST WINAPI SHBrowseForFolderWrapW(LPBROWSEINFOW lpBi)
3455 {
3456     return SHBrowseForFolderW(lpBi);
3457 }
3458 
3459 /*************************************************************************
3460  *      @	[SHLWAPI.334]
3461  *
3462  * See SHGetPathFromIDListW.
3463  */
3464 BOOL WINAPI SHGetPathFromIDListWrapW(LPCITEMIDLIST pidl,LPWSTR pszPath)
3465 {
3466     return SHGetPathFromIDListW(pidl, pszPath);
3467 }
3468 
3469 /*************************************************************************
3470  *      @	[SHLWAPI.335]
3471  *
3472  * See ShellExecuteExW.
3473  */
3474 BOOL WINAPI ShellExecuteExWrapW(LPSHELLEXECUTEINFOW lpExecInfo)
3475 {
3476     return ShellExecuteExW(lpExecInfo);
3477 }
3478 
3479 /*************************************************************************
3480  *      @	[SHLWAPI.336]
3481  *
3482  * See SHFileOperationW.
3483  */
3484 INT WINAPI SHFileOperationWrapW(LPSHFILEOPSTRUCTW lpFileOp)
3485 {
3486     return SHFileOperationW(lpFileOp);
3487 }
3488 
3489 /*************************************************************************
3490  *      @	[SHLWAPI.342]
3491  *
3492  */
3493 PVOID WINAPI SHInterlockedCompareExchange( PVOID *dest, PVOID xchg, PVOID compare )
3494 {
3495     return InterlockedCompareExchangePointer( dest, xchg, compare );
3496 }
3497 
3498 /*************************************************************************
3499  *      @	[SHLWAPI.350]
3500  *
3501  * See GetFileVersionInfoSizeW.
3502  */
3503 DWORD WINAPI GetFileVersionInfoSizeWrapW( LPCWSTR filename, LPDWORD handle )
3504 {
3505     return GetFileVersionInfoSizeW( filename, handle );
3506 }
3507 
3508 /*************************************************************************
3509  *      @	[SHLWAPI.351]
3510  *
3511  * See GetFileVersionInfoW.
3512  */
3513 BOOL  WINAPI GetFileVersionInfoWrapW( LPCWSTR filename, DWORD handle,
3514                                       DWORD datasize, LPVOID data )
3515 {
3516     return GetFileVersionInfoW( filename, handle, datasize, data );
3517 }
3518 
3519 /*************************************************************************
3520  *      @	[SHLWAPI.352]
3521  *
3522  * See VerQueryValueW.
3523  */
3524 WORD WINAPI VerQueryValueWrapW( LPVOID pBlock, LPCWSTR lpSubBlock,
3525                                 LPVOID *lplpBuffer, UINT *puLen )
3526 {
3527     return VerQueryValueW( pBlock, lpSubBlock, lplpBuffer, puLen );
3528 }
3529 
3530 #define IsIface(type) SUCCEEDED((hRet = IUnknown_QueryInterface(lpUnknown, &IID_##type, (void**)&lpObj)))
3531 #define IShellBrowser_EnableModeless IShellBrowser_EnableModelessSB
3532 #define EnableModeless(type) type##_EnableModeless((type*)lpObj, bModeless)
3533 
3534 /*************************************************************************
3535  *      @	[SHLWAPI.355]
3536  *
3537  * Change the modality of a shell object.
3538  *
3539  * PARAMS
3540  *  lpUnknown [I] Object to make modeless
3541  *  bModeless [I] TRUE=Make modeless, FALSE=Make modal
3542  *
3543  * RETURNS
3544  *  Success: S_OK. The modality lpUnknown is changed.
3545  *  Failure: An HRESULT error code indicating the error.
3546  *
3547  * NOTES
3548  *  lpUnknown must support the IOleInPlaceFrame interface, the
3549  *  IInternetSecurityMgrSite interface, the IShellBrowser interface
3550  *  the IDocHostUIHandler interface, or the IOleInPlaceActiveObject interface,
3551  *  or this call will fail.
3552  */
3553 HRESULT WINAPI IUnknown_EnableModeless(IUnknown *lpUnknown, BOOL bModeless)
3554 {
3555   IUnknown *lpObj;
3556   HRESULT hRet;
3557 
3558   TRACE("(%p,%d)\n", lpUnknown, bModeless);
3559 
3560   if (!lpUnknown)
3561     return E_FAIL;
3562 
3563   if (IsIface(IOleInPlaceActiveObject))
3564     EnableModeless(IOleInPlaceActiveObject);
3565   else if (IsIface(IOleInPlaceFrame))
3566     EnableModeless(IOleInPlaceFrame);
3567   else if (IsIface(IShellBrowser))
3568     EnableModeless(IShellBrowser);
3569   else if (IsIface(IInternetSecurityMgrSite))
3570     EnableModeless(IInternetSecurityMgrSite);
3571   else if (IsIface(IDocHostUIHandler))
3572     EnableModeless(IDocHostUIHandler);
3573   else
3574     return hRet;
3575 
3576   IUnknown_Release(lpObj);
3577   return S_OK;
3578 }
3579 
3580 /*************************************************************************
3581  *      @	[SHLWAPI.357]
3582  *
3583  * See SHGetNewLinkInfoW.
3584  */
3585 BOOL WINAPI SHGetNewLinkInfoWrapW(LPCWSTR pszLinkTo, LPCWSTR pszDir, LPWSTR pszName,
3586                         BOOL *pfMustCopy, UINT uFlags)
3587 {
3588     return SHGetNewLinkInfoW(pszLinkTo, pszDir, pszName, pfMustCopy, uFlags);
3589 }
3590 
3591 /*************************************************************************
3592  *      @	[SHLWAPI.358]
3593  *
3594  * See SHDefExtractIconW.
3595  */
3596 UINT WINAPI SHDefExtractIconWrapW(LPCWSTR pszIconFile, int iIndex, UINT uFlags, HICON* phiconLarge,
3597                          HICON* phiconSmall, UINT nIconSize)
3598 {
3599     return SHDefExtractIconW(pszIconFile, iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
3600 }
3601 
3602 /*************************************************************************
3603  *      @	[SHLWAPI.363]
3604  *
3605  * Get and show a context menu from a shell folder.
3606  *
3607  * PARAMS
3608  *  hWnd           [I] Window displaying the shell folder
3609  *  lpFolder       [I] IShellFolder interface
3610  *  lpApidl        [I] Id for the particular folder desired
3611  *  dwCommandId    [I] The command ID to invoke (0=invoke default)
3612  *
3613  * RETURNS
3614  *  Success: S_OK. If bInvokeDefault is TRUE, the default menu action was
3615  *           executed.
3616  *  Failure: An HRESULT error code indicating the error.
3617  */
3618 HRESULT WINAPI SHInvokeCommand(HWND hWnd, IShellFolder* lpFolder, LPCITEMIDLIST lpApidl, DWORD dwCommandId)
3619 {
3620   IContextMenu *iContext;
3621   HRESULT hRet;
3622 
3623   TRACE("(%p, %p, %p, %u)\n", hWnd, lpFolder, lpApidl, dwCommandId);
3624 
3625   if (!lpFolder)
3626     return E_FAIL;
3627 
3628   /* Get the context menu from the shell folder */
3629   hRet = IShellFolder_GetUIObjectOf(lpFolder, hWnd, 1, &lpApidl,
3630                                     &IID_IContextMenu, 0, (void**)&iContext);
3631   if (SUCCEEDED(hRet))
3632   {
3633     HMENU hMenu;
3634     if ((hMenu = CreatePopupMenu()))
3635     {
3636       HRESULT hQuery;
3637 
3638       /* Add the context menu entries to the popup */
3639       hQuery = IContextMenu_QueryContextMenu(iContext, hMenu, 0, 1, 0x7FFF,
3640                                              dwCommandId ? CMF_NORMAL : CMF_DEFAULTONLY);
3641 
3642       if (SUCCEEDED(hQuery))
3643       {
3644         if (!dwCommandId)
3645           dwCommandId = GetMenuDefaultItem(hMenu, 0, 0);
3646         if (dwCommandId != (UINT)-1)
3647         {
3648           CMINVOKECOMMANDINFO cmIci;
3649           /* Invoke the default item */
3650           memset(&cmIci,0,sizeof(cmIci));
3651           cmIci.cbSize = sizeof(cmIci);
3652           cmIci.fMask = CMIC_MASK_ASYNCOK;
3653           cmIci.hwnd = hWnd;
3654 #ifdef __REACTOS__ /* r75561 */
3655           cmIci.lpVerb = MAKEINTRESOURCEA(dwCommandId - 1);
3656 #else
3657           cmIci.lpVerb = MAKEINTRESOURCEA(dwCommandId);
3658 #endif
3659           cmIci.nShow = SW_SHOWNORMAL;
3660 
3661           hRet = IContextMenu_InvokeCommand(iContext, &cmIci);
3662         }
3663       }
3664       DestroyMenu(hMenu);
3665     }
3666     IContextMenu_Release(iContext);
3667   }
3668   return hRet;
3669 }
3670 
3671 /*************************************************************************
3672  *      @	[SHLWAPI.370]
3673  *
3674  * See ExtractIconW.
3675  */
3676 HICON WINAPI ExtractIconWrapW(HINSTANCE hInstance, LPCWSTR lpszExeFileName,
3677                          UINT nIconIndex)
3678 {
3679     return ExtractIconW(hInstance, lpszExeFileName, nIconIndex);
3680 }
3681 
3682 /*************************************************************************
3683  *      @	[SHLWAPI.377]
3684  *
3685  * Load a library from the directory of a particular process.
3686  *
3687  * PARAMS
3688  *  new_mod        [I] Library name
3689  *  inst_hwnd      [I] Module whose directory is to be used
3690  *  dwCrossCodePage [I] Should be FALSE (currently ignored)
3691  *
3692  * RETURNS
3693  *  Success: A handle to the loaded module
3694  *  Failure: A NULL handle.
3695  */
3696 HMODULE WINAPI MLLoadLibraryA(LPCSTR new_mod, HMODULE inst_hwnd, DWORD dwCrossCodePage)
3697 {
3698   /* FIXME: Native appears to do DPA_Create and a DPA_InsertPtr for
3699    *        each call here.
3700    * FIXME: Native shows calls to:
3701    *  SHRegGetUSValue for "Software\Microsoft\Internet Explorer\International"
3702    *                      CheckVersion
3703    *  RegOpenKeyExA for "HKLM\Software\Microsoft\Internet Explorer"
3704    *  RegQueryValueExA for "LPKInstalled"
3705    *  RegCloseKey
3706    *  RegOpenKeyExA for "HKCU\Software\Microsoft\Internet Explorer\International"
3707    *  RegQueryValueExA for "ResourceLocale"
3708    *  RegCloseKey
3709    *  RegOpenKeyExA for "HKLM\Software\Microsoft\Active Setup\Installed Components\{guid}"
3710    *  RegQueryValueExA for "Locale"
3711    *  RegCloseKey
3712    *  and then tests the Locale ("en" for me).
3713    *     code below
3714    *  after the code then a DPA_Create (first time) and DPA_InsertPtr are done.
3715    */
3716     CHAR mod_path[2*MAX_PATH];
3717     LPSTR ptr;
3718     DWORD len;
3719 
3720     FIXME("(%s,%p,%d) semi-stub!\n", debugstr_a(new_mod), inst_hwnd, dwCrossCodePage);
3721     len = GetModuleFileNameA(inst_hwnd, mod_path, sizeof(mod_path));
3722     if (!len || len >= sizeof(mod_path)) return NULL;
3723 
3724     ptr = strrchr(mod_path, '\\');
3725     if (ptr) {
3726 	strcpy(ptr+1, new_mod);
3727 	TRACE("loading %s\n", debugstr_a(mod_path));
3728 	return LoadLibraryA(mod_path);
3729     }
3730     return NULL;
3731 }
3732 
3733 /*************************************************************************
3734  *      @	[SHLWAPI.378]
3735  *
3736  * Unicode version of MLLoadLibraryA.
3737  */
3738 HMODULE WINAPI MLLoadLibraryW(LPCWSTR new_mod, HMODULE inst_hwnd, DWORD dwCrossCodePage)
3739 {
3740     WCHAR mod_path[2*MAX_PATH];
3741     LPWSTR ptr;
3742     DWORD len;
3743 
3744     FIXME("(%s,%p,%d) semi-stub!\n", debugstr_w(new_mod), inst_hwnd, dwCrossCodePage);
3745     len = GetModuleFileNameW(inst_hwnd, mod_path, sizeof(mod_path) / sizeof(WCHAR));
3746     if (!len || len >= sizeof(mod_path) / sizeof(WCHAR)) return NULL;
3747 
3748     ptr = strrchrW(mod_path, '\\');
3749     if (ptr) {
3750 	strcpyW(ptr+1, new_mod);
3751 	TRACE("loading %s\n", debugstr_w(mod_path));
3752 	return LoadLibraryW(mod_path);
3753     }
3754     return NULL;
3755 }
3756 
3757 /*************************************************************************
3758  * ColorAdjustLuma      [SHLWAPI.@]
3759  *
3760  * Adjust the luminosity of a color
3761  *
3762  * PARAMS
3763  *  cRGB         [I] RGB value to convert
3764  *  dwLuma       [I] Luma adjustment
3765  *  bUnknown     [I] Unknown
3766  *
3767  * RETURNS
3768  *  The adjusted RGB color.
3769  */
3770 COLORREF WINAPI ColorAdjustLuma(COLORREF cRGB, int dwLuma, BOOL bUnknown)
3771 {
3772   TRACE("(0x%8x,%d,%d)\n", cRGB, dwLuma, bUnknown);
3773 
3774   if (dwLuma)
3775   {
3776     WORD wH, wL, wS;
3777 
3778     ColorRGBToHLS(cRGB, &wH, &wL, &wS);
3779 
3780     FIXME("Ignoring luma adjustment\n");
3781 
3782     /* FIXME: The adjustment is not linear */
3783 
3784     cRGB = ColorHLSToRGB(wH, wL, wS);
3785   }
3786   return cRGB;
3787 }
3788 
3789 /*************************************************************************
3790  *      @	[SHLWAPI.389]
3791  *
3792  * See GetSaveFileNameW.
3793  */
3794 BOOL WINAPI GetSaveFileNameWrapW(LPOPENFILENAMEW ofn)
3795 {
3796     return GetSaveFileNameW(ofn);
3797 }
3798 
3799 /*************************************************************************
3800  *      @	[SHLWAPI.390]
3801  *
3802  * See WNetRestoreConnectionW.
3803  */
3804 DWORD WINAPI WNetRestoreConnectionWrapW(HWND hwndOwner, LPWSTR lpszDevice)
3805 {
3806     return WNetRestoreConnectionW(hwndOwner, lpszDevice);
3807 }
3808 
3809 /*************************************************************************
3810  *      @	[SHLWAPI.391]
3811  *
3812  * See WNetGetLastErrorW.
3813  */
3814 DWORD WINAPI WNetGetLastErrorWrapW(LPDWORD lpError, LPWSTR lpErrorBuf, DWORD nErrorBufSize,
3815                          LPWSTR lpNameBuf, DWORD nNameBufSize)
3816 {
3817     return WNetGetLastErrorW(lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize);
3818 }
3819 
3820 /*************************************************************************
3821  *      @	[SHLWAPI.401]
3822  *
3823  * See PageSetupDlgW.
3824  */
3825 BOOL WINAPI PageSetupDlgWrapW(LPPAGESETUPDLGW pagedlg)
3826 {
3827     return PageSetupDlgW(pagedlg);
3828 }
3829 
3830 /*************************************************************************
3831  *      @	[SHLWAPI.402]
3832  *
3833  * See PrintDlgW.
3834  */
3835 BOOL WINAPI PrintDlgWrapW(LPPRINTDLGW printdlg)
3836 {
3837     return PrintDlgW(printdlg);
3838 }
3839 
3840 /*************************************************************************
3841  *      @	[SHLWAPI.403]
3842  *
3843  * See GetOpenFileNameW.
3844  */
3845 BOOL WINAPI GetOpenFileNameWrapW(LPOPENFILENAMEW ofn)
3846 {
3847     return GetOpenFileNameW(ofn);
3848 }
3849 
3850 /*************************************************************************
3851  *      @	[SHLWAPI.404]
3852  */
3853 HRESULT WINAPI SHIShellFolder_EnumObjects(LPSHELLFOLDER lpFolder, HWND hwnd, SHCONTF flags, IEnumIDList **ppenum)
3854 {
3855     /* Windows attempts to get an IPersist interface and, if that fails, an
3856      * IPersistFolder interface on the folder passed-in here.  If one of those
3857      * interfaces is available, it then calls GetClassID on the folder... and
3858      * then calls IShellFolder_EnumObjects no matter what, even crashing if
3859      * lpFolder isn't actually an IShellFolder object.  The purpose of getting
3860      * the ClassID is unknown, so we don't do it here.
3861      *
3862      * For discussion and detailed tests, see:
3863      * "shlwapi: Be less strict on which type of IShellFolder can be enumerated"
3864      * wine-devel mailing list, 3 Jun 2010
3865      */
3866 
3867     return IShellFolder_EnumObjects(lpFolder, hwnd, flags, ppenum);
3868 }
3869 
3870 /* INTERNAL: Map from HLS color space to RGB */
3871 static WORD ConvertHue(int wHue, WORD wMid1, WORD wMid2)
3872 {
3873   wHue = wHue > 240 ? wHue - 240 : wHue < 0 ? wHue + 240 : wHue;
3874 
3875   if (wHue > 160)
3876     return wMid1;
3877   else if (wHue > 120)
3878     wHue = 160 - wHue;
3879   else if (wHue > 40)
3880     return wMid2;
3881 
3882   return ((wHue * (wMid2 - wMid1) + 20) / 40) + wMid1;
3883 }
3884 
3885 /* Convert to RGB and scale into RGB range (0..255) */
3886 #define GET_RGB(h) (ConvertHue(h, wMid1, wMid2) * 255 + 120) / 240
3887 
3888 /*************************************************************************
3889  *      ColorHLSToRGB	[SHLWAPI.@]
3890  *
3891  * Convert from hls color space into an rgb COLORREF.
3892  *
3893  * PARAMS
3894  *  wHue        [I] Hue amount
3895  *  wLuminosity [I] Luminosity amount
3896  *  wSaturation [I] Saturation amount
3897  *
3898  * RETURNS
3899  *  A COLORREF representing the converted color.
3900  *
3901  * NOTES
3902  *  Input hls values are constrained to the range (0..240).
3903  */
3904 COLORREF WINAPI ColorHLSToRGB(WORD wHue, WORD wLuminosity, WORD wSaturation)
3905 {
3906   WORD wRed;
3907 
3908   if (wSaturation)
3909   {
3910     WORD wGreen, wBlue, wMid1, wMid2;
3911 
3912     if (wLuminosity > 120)
3913       wMid2 = wSaturation + wLuminosity - (wSaturation * wLuminosity + 120) / 240;
3914     else
3915       wMid2 = ((wSaturation + 240) * wLuminosity + 120) / 240;
3916 
3917     wMid1 = wLuminosity * 2 - wMid2;
3918 
3919     wRed   = GET_RGB(wHue + 80);
3920     wGreen = GET_RGB(wHue);
3921     wBlue  = GET_RGB(wHue - 80);
3922 
3923     return RGB(wRed, wGreen, wBlue);
3924   }
3925 
3926   wRed = wLuminosity * 255 / 240;
3927   return RGB(wRed, wRed, wRed);
3928 }
3929 
3930 /*************************************************************************
3931  *      @	[SHLWAPI.413]
3932  *
3933  * Get the current docking status of the system.
3934  *
3935  * PARAMS
3936  *  dwFlags [I] DOCKINFO_ flags from "winbase.h", unused
3937  *
3938  * RETURNS
3939  *  One of DOCKINFO_UNDOCKED, DOCKINFO_UNDOCKED, or 0 if the system is not
3940  *  a notebook.
3941  */
3942 DWORD WINAPI SHGetMachineInfo(DWORD dwFlags)
3943 {
3944   HW_PROFILE_INFOA hwInfo;
3945 
3946   TRACE("(0x%08x)\n", dwFlags);
3947 
3948   GetCurrentHwProfileA(&hwInfo);
3949   switch (hwInfo.dwDockInfo & (DOCKINFO_DOCKED|DOCKINFO_UNDOCKED))
3950   {
3951   case DOCKINFO_DOCKED:
3952   case DOCKINFO_UNDOCKED:
3953     return hwInfo.dwDockInfo & (DOCKINFO_DOCKED|DOCKINFO_UNDOCKED);
3954   default:
3955     return 0;
3956   }
3957 }
3958 
3959 /*************************************************************************
3960  * @    [SHLWAPI.416]
3961  *
3962  */
3963 DWORD WINAPI SHWinHelpOnDemandW(HWND hwnd, LPCWSTR helpfile, DWORD flags1, VOID *ptr1, DWORD flags2)
3964 {
3965 
3966     FIXME("(%p, %s, 0x%x, %p, %d)\n", hwnd, debugstr_w(helpfile), flags1, ptr1, flags2);
3967     return 0;
3968 }
3969 
3970 /*************************************************************************
3971  * @    [SHLWAPI.417]
3972  *
3973  */
3974 DWORD WINAPI SHWinHelpOnDemandA(HWND hwnd, LPCSTR helpfile, DWORD flags1, VOID *ptr1, DWORD flags2)
3975 {
3976 
3977     FIXME("(%p, %s, 0x%x, %p, %d)\n", hwnd, debugstr_a(helpfile), flags1, ptr1, flags2);
3978     return 0;
3979 }
3980 
3981 /*************************************************************************
3982  *      @	[SHLWAPI.418]
3983  *
3984  * Function seems to do FreeLibrary plus other things.
3985  *
3986  * FIXME native shows the following calls:
3987  *   RtlEnterCriticalSection
3988  *   LocalFree
3989  *   GetProcAddress(Comctl32??, 150L)
3990  *   DPA_DeletePtr
3991  *   RtlLeaveCriticalSection
3992  *  followed by the FreeLibrary.
3993  *  The above code may be related to .377 above.
3994  */
3995 BOOL WINAPI MLFreeLibrary(HMODULE hModule)
3996 {
3997 	FIXME("(%p) semi-stub\n", hModule);
3998 	return FreeLibrary(hModule);
3999 }
4000 
4001 /*************************************************************************
4002  *      @	[SHLWAPI.419]
4003  */
4004 BOOL WINAPI SHFlushSFCacheWrap(void) {
4005   FIXME(": stub\n");
4006   return TRUE;
4007 }
4008 
4009 /*************************************************************************
4010  *      @      [SHLWAPI.429]
4011  * FIXME I have no idea what this function does or what its arguments are.
4012  */
4013 BOOL WINAPI MLIsMLHInstance(HINSTANCE hInst)
4014 {
4015        FIXME("(%p) stub\n", hInst);
4016        return FALSE;
4017 }
4018 
4019 
4020 /*************************************************************************
4021  *      @	[SHLWAPI.430]
4022  */
4023 DWORD WINAPI MLSetMLHInstance(HINSTANCE hInst, HANDLE hHeap)
4024 {
4025 	FIXME("(%p,%p) stub\n", hInst, hHeap);
4026 	return E_FAIL;   /* This is what is used if shlwapi not loaded */
4027 }
4028 
4029 /*************************************************************************
4030  *      @	[SHLWAPI.431]
4031  */
4032 DWORD WINAPI MLClearMLHInstance(DWORD x)
4033 {
4034 	FIXME("(0x%08x)stub\n", x);
4035 	return 0xabba1247;
4036 }
4037 
4038 /*************************************************************************
4039  * @ [SHLWAPI.432]
4040  *
4041  * See SHSendMessageBroadcastW
4042  *
4043  */
4044 DWORD WINAPI SHSendMessageBroadcastA(UINT uMsg, WPARAM wParam, LPARAM lParam)
4045 {
4046     return SendMessageTimeoutA(HWND_BROADCAST, uMsg, wParam, lParam,
4047                                SMTO_ABORTIFHUNG, 2000, NULL);
4048 }
4049 
4050 /*************************************************************************
4051  * @ [SHLWAPI.433]
4052  *
4053  * A wrapper for sending Broadcast Messages to all top level Windows
4054  *
4055  */
4056 DWORD WINAPI SHSendMessageBroadcastW(UINT uMsg, WPARAM wParam, LPARAM lParam)
4057 {
4058     return SendMessageTimeoutW(HWND_BROADCAST, uMsg, wParam, lParam,
4059                                SMTO_ABORTIFHUNG, 2000, NULL);
4060 }
4061 
4062 /*************************************************************************
4063  *      @	[SHLWAPI.436]
4064  *
4065  * Convert a Unicode string CLSID into a CLSID.
4066  *
4067  * PARAMS
4068  *  idstr      [I]   string containing a CLSID in text form
4069  *  id         [O]   CLSID extracted from the string
4070  *
4071  * RETURNS
4072  *  S_OK on success or E_INVALIDARG on failure
4073  */
4074 HRESULT WINAPI CLSIDFromStringWrap(LPCWSTR idstr, CLSID *id)
4075 {
4076     return CLSIDFromString((LPCOLESTR)idstr, id);
4077 }
4078 
4079 /*************************************************************************
4080  *      @	[SHLWAPI.437]
4081  *
4082  * Determine if the OS supports a given feature.
4083  *
4084  * PARAMS
4085  *  dwFeature [I] Feature requested (undocumented)
4086  *
4087  * RETURNS
4088  *  TRUE  If the feature is available.
4089  *  FALSE If the feature is not available.
4090  */
4091 BOOL WINAPI IsOS(DWORD feature)
4092 {
4093     OSVERSIONINFOA osvi;
4094     DWORD platform, majorv, minorv;
4095 
4096     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
4097     if(!GetVersionExA(&osvi))  {
4098         ERR("GetVersionEx failed\n");
4099         return FALSE;
4100     }
4101 
4102     majorv = osvi.dwMajorVersion;
4103     minorv = osvi.dwMinorVersion;
4104     platform = osvi.dwPlatformId;
4105 
4106 #define ISOS_RETURN(x) \
4107     TRACE("(0x%x) ret=%d\n",feature,(x)); \
4108     return (x);
4109 
4110     switch(feature)  {
4111     case OS_WIN32SORGREATER:
4112         ISOS_RETURN(platform == VER_PLATFORM_WIN32s
4113                  || platform == VER_PLATFORM_WIN32_WINDOWS)
4114     case OS_NT:
4115         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4116     case OS_WIN95ORGREATER:
4117         ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS)
4118     case OS_NT4ORGREATER:
4119         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 4)
4120     case OS_WIN2000ORGREATER_ALT:
4121     case OS_WIN2000ORGREATER:
4122         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5)
4123     case OS_WIN98ORGREATER:
4124         ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS && minorv >= 10)
4125     case OS_WIN98_GOLD:
4126         ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS && minorv == 10)
4127     case OS_WIN2000PRO:
4128         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5)
4129     case OS_WIN2000SERVER:
4130         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && (minorv == 0 || minorv == 1))
4131     case OS_WIN2000ADVSERVER:
4132         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && (minorv == 0 || minorv == 1))
4133     case OS_WIN2000DATACENTER:
4134         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && (minorv == 0 || minorv == 1))
4135     case OS_WIN2000TERMINAL:
4136         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && (minorv == 0 || minorv == 1))
4137     case OS_EMBEDDED:
4138         FIXME("(OS_EMBEDDED) What should we return here?\n");
4139         return FALSE;
4140     case OS_TERMINALCLIENT:
4141         FIXME("(OS_TERMINALCLIENT) What should we return here?\n");
4142         return FALSE;
4143     case OS_TERMINALREMOTEADMIN:
4144         FIXME("(OS_TERMINALREMOTEADMIN) What should we return here?\n");
4145         return FALSE;
4146     case OS_WIN95_GOLD:
4147         ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS && minorv == 0)
4148     case OS_MEORGREATER:
4149         ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS && minorv >= 90)
4150     case OS_XPORGREATER:
4151         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5 && minorv >= 1)
4152     case OS_HOME:
4153         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5 && minorv >= 1)
4154     case OS_PROFESSIONAL:
4155         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4156     case OS_DATACENTER:
4157         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4158     case OS_ADVSERVER:
4159         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5)
4160     case OS_SERVER:
4161         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4162     case OS_TERMINALSERVER:
4163         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4164     case OS_PERSONALTERMINALSERVER:
4165         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && minorv >= 1 && majorv >= 5)
4166     case OS_FASTUSERSWITCHING:
4167         FIXME("(OS_FASTUSERSWITCHING) What should we return here?\n");
4168         return TRUE;
4169     case OS_WELCOMELOGONUI:
4170         FIXME("(OS_WELCOMELOGONUI) What should we return here?\n");
4171         return FALSE;
4172     case OS_DOMAINMEMBER:
4173         FIXME("(OS_DOMAINMEMBER) What should we return here?\n");
4174         return TRUE;
4175     case OS_ANYSERVER:
4176         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4177     case OS_WOW6432:
4178         {
4179             BOOL is_wow64;
4180             IsWow64Process(GetCurrentProcess(), &is_wow64);
4181             return is_wow64;
4182         }
4183     case OS_WEBSERVER:
4184         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4185     case OS_SMALLBUSINESSSERVER:
4186         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT)
4187     case OS_TABLETPC:
4188         FIXME("(OS_TABLETPC) What should we return here?\n");
4189         return FALSE;
4190     case OS_SERVERADMINUI:
4191         FIXME("(OS_SERVERADMINUI) What should we return here?\n");
4192         return FALSE;
4193     case OS_MEDIACENTER:
4194         FIXME("(OS_MEDIACENTER) What should we return here?\n");
4195         return FALSE;
4196     case OS_APPLIANCE:
4197         FIXME("(OS_APPLIANCE) What should we return here?\n");
4198         return FALSE;
4199     case 0x25: /*OS_VISTAORGREATER*/
4200         ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 6)
4201     }
4202 
4203 #undef ISOS_RETURN
4204 
4205     WARN("(0x%x) unknown parameter\n",feature);
4206 
4207     return FALSE;
4208 }
4209 
4210 #ifdef __REACTOS__
4211 /*************************************************************************
4212  * @  [SHLWAPI.438]
4213  */
4214 HRESULT WINAPI SHLoadRegUIStringA(HKEY hkey, LPCSTR value, LPSTR buf, DWORD size)
4215 {
4216     WCHAR valueW[MAX_PATH], bufferW[MAX_PATH];
4217     DWORD dwSize = ARRAY_SIZE(bufferW) * sizeof(CHAR);
4218     HRESULT hr;
4219 
4220     MultiByteToWideChar(CP_ACP, 0, value, -1, valueW, ARRAY_SIZE(valueW));
4221     valueW[ARRAY_SIZE(valueW) - 1] = UNICODE_NULL; /* Avoid buffer overrun */
4222 
4223     if (RegQueryValueExW(hkey, valueW, NULL, NULL, (LPBYTE)bufferW, &dwSize) != ERROR_SUCCESS)
4224         return E_FAIL;
4225 
4226     hr = SHLoadIndirectString(bufferW, bufferW, ARRAY_SIZE(bufferW), NULL);
4227     if (FAILED(hr))
4228         return hr;
4229 
4230     WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, size, NULL, NULL);
4231     if (size > 0)
4232         buf[size - 1] = ANSI_NULL; /* Avoid buffer overrun */
4233     return S_OK;
4234 }
4235 #endif
4236 
4237 /*************************************************************************
4238  * @  [SHLWAPI.439]
4239  */
4240 HRESULT WINAPI SHLoadRegUIStringW(HKEY hkey, LPCWSTR value, LPWSTR buf, DWORD size)
4241 {
4242     DWORD type, sz = size * sizeof(WCHAR);
4243 
4244     if(RegQueryValueExW(hkey, value, NULL, &type, (LPBYTE)buf, &sz) != ERROR_SUCCESS)
4245         return E_FAIL;
4246 
4247     return SHLoadIndirectString(buf, buf, size, NULL);
4248 }
4249 
4250 /*************************************************************************
4251  * @  [SHLWAPI.478]
4252  *
4253  * Call IInputObject_TranslateAcceleratorIO() on an object.
4254  *
4255  * PARAMS
4256  *  lpUnknown [I] Object supporting the IInputObject interface.
4257  *  lpMsg     [I] Key message to be processed.
4258  *
4259  * RETURNS
4260  *  Success: S_OK.
4261  *  Failure: An HRESULT error code, or E_INVALIDARG if lpUnknown is NULL.
4262  */
4263 HRESULT WINAPI IUnknown_TranslateAcceleratorIO(IUnknown *lpUnknown, LPMSG lpMsg)
4264 {
4265   IInputObject* lpInput = NULL;
4266   HRESULT hRet = E_INVALIDARG;
4267 
4268   TRACE("(%p,%p)\n", lpUnknown, lpMsg);
4269   if (lpUnknown)
4270   {
4271     hRet = IUnknown_QueryInterface(lpUnknown, &IID_IInputObject,
4272                                    (void**)&lpInput);
4273     if (SUCCEEDED(hRet) && lpInput)
4274     {
4275       hRet = IInputObject_TranslateAcceleratorIO(lpInput, lpMsg);
4276       IInputObject_Release(lpInput);
4277     }
4278   }
4279   return hRet;
4280 }
4281 
4282 /*************************************************************************
4283  * @  [SHLWAPI.481]
4284  *
4285  * Call IInputObject_HasFocusIO() on an object.
4286  *
4287  * PARAMS
4288  *  lpUnknown [I] Object supporting the IInputObject interface.
4289  *
4290  * RETURNS
4291  *  Success: S_OK, if lpUnknown is an IInputObject object and has the focus,
4292  *           or S_FALSE otherwise.
4293  *  Failure: An HRESULT error code, or E_INVALIDARG if lpUnknown is NULL.
4294  */
4295 HRESULT WINAPI IUnknown_HasFocusIO(IUnknown *lpUnknown)
4296 {
4297   IInputObject* lpInput = NULL;
4298   HRESULT hRet = E_INVALIDARG;
4299 
4300   TRACE("(%p)\n", lpUnknown);
4301   if (lpUnknown)
4302   {
4303     hRet = IUnknown_QueryInterface(lpUnknown, &IID_IInputObject,
4304                                    (void**)&lpInput);
4305     if (SUCCEEDED(hRet) && lpInput)
4306     {
4307       hRet = IInputObject_HasFocusIO(lpInput);
4308       IInputObject_Release(lpInput);
4309     }
4310   }
4311   return hRet;
4312 }
4313 
4314 /*************************************************************************
4315  *      ColorRGBToHLS	[SHLWAPI.@]
4316  *
4317  * Convert an rgb COLORREF into the hls color space.
4318  *
4319  * PARAMS
4320  *  cRGB         [I] Source rgb value
4321  *  pwHue        [O] Destination for converted hue
4322  *  pwLuminance  [O] Destination for converted luminance
4323  *  pwSaturation [O] Destination for converted saturation
4324  *
4325  * RETURNS
4326  *  Nothing. pwHue, pwLuminance and pwSaturation are set to the converted
4327  *  values.
4328  *
4329  * NOTES
4330  *  Output HLS values are constrained to the range (0..240).
4331  *  For Achromatic conversions, Hue is set to 160.
4332  */
4333 VOID WINAPI ColorRGBToHLS(COLORREF cRGB, LPWORD pwHue,
4334 			  LPWORD pwLuminance, LPWORD pwSaturation)
4335 {
4336   int wR, wG, wB, wMax, wMin, wHue, wLuminosity, wSaturation;
4337 
4338   TRACE("(%08x,%p,%p,%p)\n", cRGB, pwHue, pwLuminance, pwSaturation);
4339 
4340   wR = GetRValue(cRGB);
4341   wG = GetGValue(cRGB);
4342   wB = GetBValue(cRGB);
4343 
4344   wMax = max(wR, max(wG, wB));
4345   wMin = min(wR, min(wG, wB));
4346 
4347   /* Luminosity */
4348   wLuminosity = ((wMax + wMin) * 240 + 255) / 510;
4349 
4350   if (wMax == wMin)
4351   {
4352     /* Achromatic case */
4353     wSaturation = 0;
4354     /* Hue is now unrepresentable, but this is what native returns... */
4355     wHue = 160;
4356   }
4357   else
4358   {
4359     /* Chromatic case */
4360     int wDelta = wMax - wMin, wRNorm, wGNorm, wBNorm;
4361 
4362     /* Saturation */
4363     if (wLuminosity <= 120)
4364       wSaturation = ((wMax + wMin)/2 + wDelta * 240) / (wMax + wMin);
4365     else
4366       wSaturation = ((510 - wMax - wMin)/2 + wDelta * 240) / (510 - wMax - wMin);
4367 
4368     /* Hue */
4369     wRNorm = (wDelta/2 + wMax * 40 - wR * 40) / wDelta;
4370     wGNorm = (wDelta/2 + wMax * 40 - wG * 40) / wDelta;
4371     wBNorm = (wDelta/2 + wMax * 40 - wB * 40) / wDelta;
4372 
4373     if (wR == wMax)
4374       wHue = wBNorm - wGNorm;
4375     else if (wG == wMax)
4376       wHue = 80 + wRNorm - wBNorm;
4377     else
4378       wHue = 160 + wGNorm - wRNorm;
4379     if (wHue < 0)
4380       wHue += 240;
4381     else if (wHue > 240)
4382       wHue -= 240;
4383   }
4384   if (pwHue)
4385     *pwHue = wHue;
4386   if (pwLuminance)
4387     *pwLuminance = wLuminosity;
4388   if (pwSaturation)
4389     *pwSaturation = wSaturation;
4390 }
4391 
4392 /*************************************************************************
4393  *      SHCreateShellPalette	[SHLWAPI.@]
4394  */
4395 HPALETTE WINAPI SHCreateShellPalette(HDC hdc)
4396 {
4397 	FIXME("stub\n");
4398 	return CreateHalftonePalette(hdc);
4399 }
4400 
4401 /*************************************************************************
4402  *	SHGetInverseCMAP (SHLWAPI.@)
4403  *
4404  * Get an inverse color map table.
4405  *
4406  * PARAMS
4407  *  lpCmap  [O] Destination for color map
4408  *  dwSize  [I] Size of memory pointed to by lpCmap
4409  *
4410  * RETURNS
4411  *  Success: S_OK.
4412  *  Failure: E_POINTER,    If lpCmap is invalid.
4413  *           E_INVALIDARG, If dwFlags is invalid
4414  *           E_OUTOFMEMORY, If there is no memory available
4415  *
4416  * NOTES
4417  *  dwSize may only be CMAP_PTR_SIZE (4) or CMAP_SIZE (8192).
4418  *  If dwSize = CMAP_PTR_SIZE, *lpCmap is set to the address of this DLL's
4419  *  internal CMap.
4420  *  If dwSize = CMAP_SIZE, lpCmap is filled with a copy of the data from
4421  *  this DLL's internal CMap.
4422  */
4423 HRESULT WINAPI SHGetInverseCMAP(LPDWORD dest, DWORD dwSize)
4424 {
4425     if (dwSize == 4) {
4426 	FIXME(" - returning bogus address for SHGetInverseCMAP\n");
4427 	*dest = (DWORD)0xabba1249;
4428 	return 0;
4429     }
4430     FIXME("(%p, %#x) stub\n", dest, dwSize);
4431     return 0;
4432 }
4433 
4434 /*************************************************************************
4435  *      SHIsLowMemoryMachine	[SHLWAPI.@]
4436  *
4437  * Determine if the current computer has low memory.
4438  *
4439  * PARAMS
4440  *  dwType [I] Zero.
4441  *
4442  * RETURNS
4443  *  TRUE if the users machine has 16 Megabytes of memory or less,
4444  *  FALSE otherwise.
4445  */
4446 BOOL WINAPI SHIsLowMemoryMachine(DWORD dwType)
4447 {
4448 #ifdef __REACTOS__
4449     MEMORYSTATUS status;
4450     static int is_low = -1;
4451     TRACE("(0x%08x)\n", dwType);
4452     if (dwType == 0 && is_low == -1)
4453     {
4454         GlobalMemoryStatus(&status);
4455         is_low = (status.dwTotalPhys <= 0x1000000);
4456     }
4457     return is_low;
4458 #else
4459   FIXME("(0x%08x) stub\n", dwType);
4460   return FALSE;
4461 #endif
4462 }
4463 
4464 /*************************************************************************
4465  *      GetMenuPosFromID	[SHLWAPI.@]
4466  *
4467  * Return the position of a menu item from its Id.
4468  *
4469  * PARAMS
4470  *   hMenu [I] Menu containing the item
4471  *   wID   [I] Id of the menu item
4472  *
4473  * RETURNS
4474  *  Success: The index of the menu item in hMenu.
4475  *  Failure: -1, If the item is not found.
4476  */
4477 INT WINAPI GetMenuPosFromID(HMENU hMenu, UINT wID)
4478 {
4479     MENUITEMINFOW mi;
4480     INT nCount = GetMenuItemCount(hMenu), nIter = 0;
4481 
4482     TRACE("%p %u\n", hMenu, wID);
4483 
4484     while (nIter < nCount)
4485     {
4486         mi.cbSize = sizeof(mi);
4487         mi.fMask = MIIM_ID;
4488         if (GetMenuItemInfoW(hMenu, nIter, TRUE, &mi) && mi.wID == wID)
4489         {
4490             TRACE("ret %d\n", nIter);
4491             return nIter;
4492         }
4493         nIter++;
4494     }
4495 
4496     return -1;
4497 }
4498 
4499 /*************************************************************************
4500  *      @	[SHLWAPI.179]
4501  *
4502  * Same as SHLWAPI.GetMenuPosFromID
4503  */
4504 DWORD WINAPI SHMenuIndexFromID(HMENU hMenu, UINT uID)
4505 {
4506     TRACE("%p %u\n", hMenu, uID);
4507     return GetMenuPosFromID(hMenu, uID);
4508 }
4509 
4510 
4511 /*************************************************************************
4512  *      @	[SHLWAPI.448]
4513  */
4514 VOID WINAPI FixSlashesAndColonW(LPWSTR lpwstr)
4515 {
4516     while (*lpwstr)
4517     {
4518         if (*lpwstr == '/')
4519             *lpwstr = '\\';
4520         lpwstr++;
4521     }
4522 }
4523 
4524 
4525 /*************************************************************************
4526  *      @	[SHLWAPI.461]
4527  */
4528 DWORD WINAPI SHGetAppCompatFlags(DWORD dwUnknown)
4529 {
4530   FIXME("(0x%08x) stub\n", dwUnknown);
4531   return 0;
4532 }
4533 
4534 
4535 /*************************************************************************
4536  *      @	[SHLWAPI.549]
4537  */
4538 HRESULT WINAPI SHCoCreateInstanceAC(REFCLSID rclsid, LPUNKNOWN pUnkOuter,
4539                                     DWORD dwClsContext, REFIID iid, LPVOID *ppv)
4540 {
4541     return CoCreateInstance(rclsid, pUnkOuter, dwClsContext, iid, ppv);
4542 }
4543 
4544 /*************************************************************************
4545  * SHSkipJunction	[SHLWAPI.@]
4546  *
4547  * Determine if a bind context can be bound to an object
4548  *
4549  * PARAMS
4550  *  pbc    [I] Bind context to check
4551  *  pclsid [I] CLSID of object to be bound to
4552  *
4553  * RETURNS
4554  *  TRUE: If it is safe to bind
4555  *  FALSE: If pbc is invalid or binding would not be safe
4556  *
4557  */
4558 BOOL WINAPI SHSkipJunction(IBindCtx *pbc, const CLSID *pclsid)
4559 {
4560   static WCHAR szSkipBinding[] = { 'S','k','i','p',' ',
4561     'B','i','n','d','i','n','g',' ','C','L','S','I','D','\0' };
4562   BOOL bRet = FALSE;
4563 
4564   if (pbc)
4565   {
4566     IUnknown* lpUnk;
4567 
4568     if (SUCCEEDED(IBindCtx_GetObjectParam(pbc, szSkipBinding, &lpUnk)))
4569     {
4570       CLSID clsid;
4571 
4572       if (SUCCEEDED(IUnknown_GetClassID(lpUnk, &clsid)) &&
4573           IsEqualGUID(pclsid, &clsid))
4574         bRet = TRUE;
4575 
4576       IUnknown_Release(lpUnk);
4577     }
4578   }
4579   return bRet;
4580 }
4581 
4582 /***********************************************************************
4583  *		SHGetShellKey (SHLWAPI.491)
4584  */
4585 HKEY WINAPI SHGetShellKey(DWORD flags, LPCWSTR sub_key, BOOL create)
4586 {
4587 #ifndef __REACTOS__
4588     enum _shellkey_flags {
4589         SHKEY_Root_HKCU = 0x1,
4590         SHKEY_Root_HKLM = 0x2,
4591         SHKEY_Key_Explorer  = 0x00,
4592         SHKEY_Key_Shell = 0x10,
4593         SHKEY_Key_ShellNoRoam = 0x20,
4594         SHKEY_Key_Classes = 0x30,
4595         SHKEY_Subkey_Default = 0x0000,
4596         SHKEY_Subkey_ResourceName = 0x1000,
4597         SHKEY_Subkey_Handlers = 0x2000,
4598         SHKEY_Subkey_Associations = 0x3000,
4599         SHKEY_Subkey_Volatile = 0x4000,
4600         SHKEY_Subkey_MUICache = 0x5000,
4601         SHKEY_Subkey_FileExts = 0x6000
4602     };
4603 #endif
4604 
4605     static const WCHAR explorerW[] = {'S','o','f','t','w','a','r','e','\\',
4606         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
4607         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4608         'E','x','p','l','o','r','e','r','\\'};
4609     static const WCHAR shellW[] = {'S','o','f','t','w','a','r','e','\\',
4610         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
4611         'S','h','e','l','l','\\'};
4612     static const WCHAR shell_no_roamW[] = {'S','o','f','t','w','a','r','e','\\',
4613         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
4614         'S','h','e','l','l','N','o','R','o','a','m','\\'};
4615     static const WCHAR classesW[] = {'S','o','f','t','w','a','r','e','\\',
4616         'C','l','a','s','s','e','s','\\'};
4617 
4618     static const WCHAR localized_resource_nameW[] = {'L','o','c','a','l','i','z','e','d',
4619         'R','e','s','o','u','r','c','e','N','a','m','e','\\'};
4620     static const WCHAR handlersW[] = {'H','a','n','d','l','e','r','s','\\'};
4621     static const WCHAR associationsW[] = {'A','s','s','o','c','i','a','t','i','o','n','s','\\'};
4622     static const WCHAR volatileW[] = {'V','o','l','a','t','i','l','e','\\'};
4623     static const WCHAR mui_cacheW[] = {'M','U','I','C','a','c','h','e','\\'};
4624     static const WCHAR file_extsW[] = {'F','i','l','e','E','x','t','s','\\'};
4625 
4626     WCHAR *path;
4627     const WCHAR *key, *subkey;
4628     int size_key, size_subkey, size_user;
4629     HKEY hkey = NULL;
4630 
4631     TRACE("(0x%08x, %s, %d)\n", flags, debugstr_w(sub_key), create);
4632 
4633     /* For compatibility with Vista+ */
4634     if(flags == 0x1ffff)
4635         flags = 0x21;
4636 
4637     switch(flags&0xff0) {
4638     case SHKEY_Key_Explorer:
4639         key = explorerW;
4640         size_key = sizeof(explorerW);
4641         break;
4642     case SHKEY_Key_Shell:
4643         key = shellW;
4644         size_key = sizeof(shellW);
4645         break;
4646     case SHKEY_Key_ShellNoRoam:
4647         key = shell_no_roamW;
4648         size_key = sizeof(shell_no_roamW);
4649         break;
4650     case SHKEY_Key_Classes:
4651         key = classesW;
4652         size_key = sizeof(classesW);
4653         break;
4654     default:
4655         FIXME("unsupported flags (0x%08x)\n", flags);
4656         return NULL;
4657     }
4658 
4659     switch(flags&0xff000) {
4660     case SHKEY_Subkey_Default:
4661         subkey = NULL;
4662         size_subkey = 0;
4663         break;
4664     case SHKEY_Subkey_ResourceName:
4665         subkey = localized_resource_nameW;
4666         size_subkey = sizeof(localized_resource_nameW);
4667         break;
4668     case SHKEY_Subkey_Handlers:
4669         subkey = handlersW;
4670         size_subkey = sizeof(handlersW);
4671         break;
4672     case SHKEY_Subkey_Associations:
4673         subkey = associationsW;
4674         size_subkey = sizeof(associationsW);
4675         break;
4676     case SHKEY_Subkey_Volatile:
4677         subkey = volatileW;
4678         size_subkey = sizeof(volatileW);
4679         break;
4680     case SHKEY_Subkey_MUICache:
4681         subkey = mui_cacheW;
4682         size_subkey = sizeof(mui_cacheW);
4683         break;
4684     case SHKEY_Subkey_FileExts:
4685         subkey = file_extsW;
4686         size_subkey = sizeof(file_extsW);
4687         break;
4688     default:
4689         FIXME("unsupported flags (0x%08x)\n", flags);
4690         return NULL;
4691     }
4692 
4693     if(sub_key)
4694         size_user = lstrlenW(sub_key)*sizeof(WCHAR);
4695     else
4696         size_user = 0;
4697 
4698     path = HeapAlloc(GetProcessHeap(), 0, size_key+size_subkey+size_user+sizeof(WCHAR));
4699     if(!path) {
4700         ERR("Out of memory\n");
4701         return NULL;
4702     }
4703 
4704     memcpy(path, key, size_key);
4705     if(subkey)
4706         memcpy(path+size_key/sizeof(WCHAR), subkey, size_subkey);
4707     if(sub_key)
4708         memcpy(path+(size_key+size_subkey)/sizeof(WCHAR), sub_key, size_user);
4709     path[(size_key+size_subkey+size_user)/sizeof(WCHAR)] = '\0';
4710 
4711     if(create)
4712         RegCreateKeyExW((flags&0xf)==SHKEY_Root_HKLM?HKEY_LOCAL_MACHINE:HKEY_CURRENT_USER,
4713                 path, 0, NULL, 0, MAXIMUM_ALLOWED, NULL, &hkey, NULL);
4714     else
4715         RegOpenKeyExW((flags&0xf)==SHKEY_Root_HKLM?HKEY_LOCAL_MACHINE:HKEY_CURRENT_USER,
4716                 path, 0, MAXIMUM_ALLOWED, &hkey);
4717 
4718     HeapFree(GetProcessHeap(), 0, path);
4719     return hkey;
4720 }
4721 
4722 /***********************************************************************
4723  *		SHQueueUserWorkItem (SHLWAPI.@)
4724  */
4725 BOOL WINAPI SHQueueUserWorkItem(LPTHREAD_START_ROUTINE pfnCallback,
4726         LPVOID pContext, LONG lPriority, DWORD_PTR dwTag,
4727         DWORD_PTR *pdwId, LPCSTR pszModule, DWORD dwFlags)
4728 {
4729     TRACE("(%p, %p, %d, %lx, %p, %s, %08x)\n", pfnCallback, pContext,
4730           lPriority, dwTag, pdwId, debugstr_a(pszModule), dwFlags);
4731 
4732     if(lPriority || dwTag || pdwId || pszModule || dwFlags)
4733         FIXME("Unsupported arguments\n");
4734 
4735     return QueueUserWorkItem(pfnCallback, pContext, 0);
4736 }
4737 
4738 /***********************************************************************
4739  *		SHSetTimerQueueTimer (SHLWAPI.263)
4740  */
4741 HANDLE WINAPI SHSetTimerQueueTimer(HANDLE hQueue,
4742         WAITORTIMERCALLBACK pfnCallback, LPVOID pContext, DWORD dwDueTime,
4743         DWORD dwPeriod, LPCSTR lpszLibrary, DWORD dwFlags)
4744 {
4745     HANDLE hNewTimer;
4746 
4747     /* SHSetTimerQueueTimer flags -> CreateTimerQueueTimer flags */
4748     if (dwFlags & TPS_LONGEXECTIME) {
4749         dwFlags &= ~TPS_LONGEXECTIME;
4750         dwFlags |= WT_EXECUTELONGFUNCTION;
4751     }
4752     if (dwFlags & TPS_EXECUTEIO) {
4753         dwFlags &= ~TPS_EXECUTEIO;
4754         dwFlags |= WT_EXECUTEINIOTHREAD;
4755     }
4756 
4757     if (!CreateTimerQueueTimer(&hNewTimer, hQueue, pfnCallback, pContext,
4758                                dwDueTime, dwPeriod, dwFlags))
4759         return NULL;
4760 
4761     return hNewTimer;
4762 }
4763 
4764 /***********************************************************************
4765  *		IUnknown_OnFocusChangeIS (SHLWAPI.@)
4766  */
4767 HRESULT WINAPI IUnknown_OnFocusChangeIS(LPUNKNOWN lpUnknown, LPUNKNOWN pFocusObject, BOOL bFocus)
4768 {
4769     IInputObjectSite *pIOS = NULL;
4770     HRESULT hRet = E_INVALIDARG;
4771 
4772     TRACE("(%p, %p, %s)\n", lpUnknown, pFocusObject, bFocus ? "TRUE" : "FALSE");
4773 
4774     if (lpUnknown)
4775     {
4776         hRet = IUnknown_QueryInterface(lpUnknown, &IID_IInputObjectSite,
4777                                        (void **)&pIOS);
4778         if (SUCCEEDED(hRet) && pIOS)
4779         {
4780             hRet = IInputObjectSite_OnFocusChangeIS(pIOS, pFocusObject, bFocus);
4781             IInputObjectSite_Release(pIOS);
4782         }
4783     }
4784     return hRet;
4785 }
4786 
4787 /***********************************************************************
4788  *		SKAllocValueW (SHLWAPI.519)
4789  */
4790 HRESULT WINAPI SKAllocValueW(DWORD flags, LPCWSTR subkey, LPCWSTR value, DWORD *type,
4791         LPVOID *data, DWORD *count)
4792 {
4793     DWORD ret, size;
4794     HKEY hkey;
4795 
4796     TRACE("(0x%x, %s, %s, %p, %p, %p)\n", flags, debugstr_w(subkey),
4797         debugstr_w(value), type, data, count);
4798 
4799     hkey = SHGetShellKey(flags, subkey, FALSE);
4800     if (!hkey)
4801         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
4802 
4803     ret = SHQueryValueExW(hkey, value, NULL, type, NULL, &size);
4804     if (ret) {
4805         RegCloseKey(hkey);
4806         return HRESULT_FROM_WIN32(ret);
4807     }
4808 
4809     size += 2;
4810     *data = LocalAlloc(0, size);
4811     if (!*data) {
4812         RegCloseKey(hkey);
4813         return E_OUTOFMEMORY;
4814     }
4815 
4816     ret = SHQueryValueExW(hkey, value, NULL, type, *data, &size);
4817     if (count)
4818         *count = size;
4819 
4820     RegCloseKey(hkey);
4821     return HRESULT_FROM_WIN32(ret);
4822 }
4823 
4824 /***********************************************************************
4825  *		SKDeleteValueW (SHLWAPI.518)
4826  */
4827 HRESULT WINAPI SKDeleteValueW(DWORD flags, LPCWSTR subkey, LPCWSTR value)
4828 {
4829     DWORD ret;
4830     HKEY hkey;
4831 
4832     TRACE("(0x%x, %s %s)\n", flags, debugstr_w(subkey), debugstr_w(value));
4833 
4834     hkey = SHGetShellKey(flags, subkey, FALSE);
4835     if (!hkey)
4836         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
4837 
4838     ret = RegDeleteValueW(hkey, value);
4839 
4840     RegCloseKey(hkey);
4841     return HRESULT_FROM_WIN32(ret);
4842 }
4843 
4844 /***********************************************************************
4845  *		SKGetValueW (SHLWAPI.516)
4846  */
4847 HRESULT WINAPI SKGetValueW(DWORD flags, LPCWSTR subkey, LPCWSTR value, DWORD *type,
4848     void *data, DWORD *count)
4849 {
4850     DWORD ret;
4851     HKEY hkey;
4852 
4853     TRACE("(0x%x, %s, %s, %p, %p, %p)\n", flags, debugstr_w(subkey),
4854         debugstr_w(value), type, data, count);
4855 
4856     hkey = SHGetShellKey(flags, subkey, FALSE);
4857     if (!hkey)
4858         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
4859 
4860     ret = SHQueryValueExW(hkey, value, NULL, type, data, count);
4861 
4862     RegCloseKey(hkey);
4863     return HRESULT_FROM_WIN32(ret);
4864 }
4865 
4866 /***********************************************************************
4867  *		SKSetValueW (SHLWAPI.516)
4868  */
4869 HRESULT WINAPI SKSetValueW(DWORD flags, LPCWSTR subkey, LPCWSTR value,
4870         DWORD type, void *data, DWORD count)
4871 {
4872     DWORD ret;
4873     HKEY hkey;
4874 
4875     TRACE("(0x%x, %s, %s, %x, %p, %d)\n", flags, debugstr_w(subkey),
4876             debugstr_w(value), type, data, count);
4877 
4878     hkey = SHGetShellKey(flags, subkey, TRUE);
4879     if (!hkey)
4880         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
4881 
4882     ret = RegSetValueExW(hkey, value, 0, type, data, count);
4883 
4884     RegCloseKey(hkey);
4885     return HRESULT_FROM_WIN32(ret);
4886 }
4887 
4888 typedef HRESULT (WINAPI *DllGetVersion_func)(DLLVERSIONINFO *);
4889 
4890 /***********************************************************************
4891  *              GetUIVersion (SHLWAPI.452)
4892  */
4893 DWORD WINAPI GetUIVersion(void)
4894 {
4895     static DWORD version;
4896 
4897     if (!version)
4898     {
4899         DllGetVersion_func pDllGetVersion;
4900         HMODULE dll = LoadLibraryA("shell32.dll");
4901         if (!dll) return 0;
4902 
4903         pDllGetVersion = (DllGetVersion_func)GetProcAddress(dll, "DllGetVersion");
4904         if (pDllGetVersion)
4905         {
4906             DLLVERSIONINFO dvi;
4907             dvi.cbSize = sizeof(DLLVERSIONINFO);
4908             if (pDllGetVersion(&dvi) == S_OK) version = dvi.dwMajorVersion;
4909         }
4910         FreeLibrary( dll );
4911         if (!version) version = 3;  /* old shell dlls don't have DllGetVersion */
4912     }
4913     return version;
4914 }
4915 
4916 /***********************************************************************
4917  *              ShellMessageBoxWrapW [SHLWAPI.388]
4918  *
4919  * See shell32.ShellMessageBoxW
4920  *
4921 #ifndef __REACTOS__
4922  *
4923  * NOTE:
4924  * shlwapi.ShellMessageBoxWrapW is a duplicate of shell32.ShellMessageBoxW
4925  * because we can't forward to it in the .spec file since it's exported by
4926  * ordinal. If you change the implementation here please update the code in
4927  * shell32 as well.
4928  *
4929 #else // __REACTOS__
4930  *
4931  * From Vista+ onwards, all the implementation of ShellMessageBoxA/W that
4932  * were existing in shell32 has been completely moved to shlwapi, so that
4933  * shell32.ShellMessageBoxA and shell32.ShellMessageBoxW are redirections
4934  * to the corresponding shlwapi functions.
4935  *
4936  * For Win2003 compatibility, if you change the implementation here please
4937  * update the code of ShellMessageBoxA in shell32 as well.
4938  *
4939 #endif
4940  */
4941 INT WINAPIV ShellMessageBoxWrapW(HINSTANCE hInstance, HWND hWnd, LPCWSTR lpText,
4942                                  LPCWSTR lpCaption, UINT uType, ...)
4943 {
4944     WCHAR *szText = NULL, szTitle[100];
4945     LPCWSTR pszText, pszTitle = szTitle;
4946     LPWSTR pszTemp;
4947     __ms_va_list args;
4948     int ret;
4949 
4950     __ms_va_start(args, uType);
4951 
4952     TRACE("(%p,%p,%p,%p,%08x)\n", hInstance, hWnd, lpText, lpCaption, uType);
4953 
4954     if (IS_INTRESOURCE(lpCaption))
4955         LoadStringW(hInstance, LOWORD(lpCaption), szTitle, ARRAY_SIZE(szTitle));
4956     else
4957         pszTitle = lpCaption;
4958 
4959     if (IS_INTRESOURCE(lpText))
4960     {
4961         const WCHAR *ptr;
4962         UINT len = LoadStringW(hInstance, LOWORD(lpText), (LPWSTR)&ptr, 0);
4963 
4964         if (len)
4965         {
4966             szText = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
4967             if (szText) LoadStringW(hInstance, LOWORD(lpText), szText, len + 1);
4968         }
4969         pszText = szText;
4970         if (!pszText) {
4971             WARN("Failed to load id %d\n", LOWORD(lpText));
4972             __ms_va_end(args);
4973             return 0;
4974         }
4975     }
4976     else
4977         pszText = lpText;
4978 
4979     FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
4980                    pszText, 0, 0, (LPWSTR)&pszTemp, 0, &args);
4981 
4982     __ms_va_end(args);
4983 
4984 #ifdef __REACTOS__
4985     uType |= MB_SETFOREGROUND;
4986 #endif
4987     ret = MessageBoxW(hWnd, pszTemp, pszTitle, uType);
4988 
4989     HeapFree(GetProcessHeap(), 0, szText);
4990     LocalFree(pszTemp);
4991     return ret;
4992 }
4993 
4994 /***********************************************************************
4995  *              ZoneComputePaneSize [SHLWAPI.382]
4996  */
4997 UINT WINAPI ZoneComputePaneSize(HWND hwnd)
4998 {
4999     FIXME("\n");
5000     return 0x95;
5001 }
5002 
5003 /***********************************************************************
5004  *              SHChangeNotifyWrap [SHLWAPI.394]
5005  */
5006 void WINAPI SHChangeNotifyWrap(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2)
5007 {
5008     SHChangeNotify(wEventId, uFlags, dwItem1, dwItem2);
5009 }
5010 
5011 typedef struct SHELL_USER_SID {   /* according to MSDN this should be in shlobj.h... */
5012     SID_IDENTIFIER_AUTHORITY sidAuthority;
5013     DWORD                    dwUserGroupID;
5014     DWORD                    dwUserID;
5015 } SHELL_USER_SID, *PSHELL_USER_SID;
5016 
5017 typedef struct SHELL_USER_PERMISSION { /* ...and this should be in shlwapi.h */
5018     SHELL_USER_SID susID;
5019     DWORD          dwAccessType;
5020     BOOL           fInherit;
5021     DWORD          dwAccessMask;
5022     DWORD          dwInheritMask;
5023     DWORD          dwInheritAccessMask;
5024 } SHELL_USER_PERMISSION, *PSHELL_USER_PERMISSION;
5025 
5026 /***********************************************************************
5027  *             GetShellSecurityDescriptor [SHLWAPI.475]
5028  *
5029  * prepares SECURITY_DESCRIPTOR from a set of ACEs
5030  *
5031  * PARAMS
5032  *  apUserPerm [I] array of pointers to SHELL_USER_PERMISSION structures,
5033  *                 each of which describes permissions to apply
5034  *  cUserPerm  [I] number of entries in apUserPerm array
5035  *
5036  * RETURNS
5037  *  success: pointer to SECURITY_DESCRIPTOR
5038  *  failure: NULL
5039  *
5040  * NOTES
5041  *  Call should free returned descriptor with LocalFree
5042  */
5043 PSECURITY_DESCRIPTOR WINAPI GetShellSecurityDescriptor(const PSHELL_USER_PERMISSION *apUserPerm, int cUserPerm)
5044 {
5045     PSID *sidlist;
5046     PSID  cur_user = NULL;
5047     BYTE  tuUser[2000];
5048     DWORD acl_size;
5049     int   sid_count, i;
5050     PSECURITY_DESCRIPTOR psd = NULL;
5051 
5052     TRACE("%p %d\n", apUserPerm, cUserPerm);
5053 
5054     if (apUserPerm == NULL || cUserPerm <= 0)
5055         return NULL;
5056 
5057     sidlist = HeapAlloc(GetProcessHeap(), 0, cUserPerm * sizeof(PSID));
5058     if (!sidlist)
5059         return NULL;
5060 
5061     acl_size = sizeof(ACL);
5062 
5063     for(sid_count = 0; sid_count < cUserPerm; sid_count++)
5064     {
5065         static SHELL_USER_SID null_sid = {{SECURITY_NULL_SID_AUTHORITY}, 0, 0};
5066         PSHELL_USER_PERMISSION perm = apUserPerm[sid_count];
5067         PSHELL_USER_SID sid = &perm->susID;
5068         PSID pSid;
5069         BOOL ret = TRUE;
5070 
5071         if (!memcmp((void*)sid, (void*)&null_sid, sizeof(SHELL_USER_SID)))
5072         {  /* current user's SID */
5073             if (!cur_user)
5074             {
5075                 HANDLE Token;
5076                 DWORD bufsize = sizeof(tuUser);
5077 
5078                 ret = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &Token);
5079                 if (ret)
5080                 {
5081                     ret = GetTokenInformation(Token, TokenUser, (void*)tuUser, bufsize, &bufsize );
5082                     if (ret)
5083                         cur_user = ((PTOKEN_USER)tuUser)->User.Sid;
5084                     CloseHandle(Token);
5085                 }
5086             }
5087             pSid = cur_user;
5088         } else if (sid->dwUserID==0) /* one sub-authority */
5089             ret = AllocateAndInitializeSid(&sid->sidAuthority, 1, sid->dwUserGroupID, 0,
5090                     0, 0, 0, 0, 0, 0, &pSid);
5091         else
5092             ret = AllocateAndInitializeSid(&sid->sidAuthority, 2, sid->dwUserGroupID, sid->dwUserID,
5093                     0, 0, 0, 0, 0, 0, &pSid);
5094         if (!ret)
5095             goto free_sids;
5096 
5097         sidlist[sid_count] = pSid;
5098         /* increment acl_size (1 ACE for non-inheritable and 2 ACEs for inheritable records */
5099         acl_size += (sizeof(ACCESS_ALLOWED_ACE)-sizeof(DWORD) + GetLengthSid(pSid)) * (perm->fInherit ? 2 : 1);
5100     }
5101 
5102     psd = LocalAlloc(0, sizeof(SECURITY_DESCRIPTOR) + acl_size);
5103 
5104     if (psd != NULL)
5105     {
5106         PACL pAcl = (PACL)(((BYTE*)psd)+sizeof(SECURITY_DESCRIPTOR));
5107 
5108         if (!InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION))
5109             goto error;
5110 
5111         if (!InitializeAcl(pAcl, acl_size, ACL_REVISION))
5112             goto error;
5113 
5114         for(i = 0; i < sid_count; i++)
5115         {
5116             PSHELL_USER_PERMISSION sup = apUserPerm[i];
5117             PSID sid = sidlist[i];
5118 
5119             switch(sup->dwAccessType)
5120             {
5121                 case ACCESS_ALLOWED_ACE_TYPE:
5122                     if (!AddAccessAllowedAce(pAcl, ACL_REVISION, sup->dwAccessMask, sid))
5123                         goto error;
5124                     if (sup->fInherit && !AddAccessAllowedAceEx(pAcl, ACL_REVISION,
5125                                 (BYTE)sup->dwInheritMask, sup->dwInheritAccessMask, sid))
5126                         goto error;
5127                     break;
5128                 case ACCESS_DENIED_ACE_TYPE:
5129                     if (!AddAccessDeniedAce(pAcl, ACL_REVISION, sup->dwAccessMask, sid))
5130                         goto error;
5131                     if (sup->fInherit && !AddAccessDeniedAceEx(pAcl, ACL_REVISION,
5132                                 (BYTE)sup->dwInheritMask, sup->dwInheritAccessMask, sid))
5133                         goto error;
5134                     break;
5135                 default:
5136                     goto error;
5137             }
5138         }
5139 
5140         if (!SetSecurityDescriptorDacl(psd, TRUE, pAcl, FALSE))
5141             goto error;
5142     }
5143     goto free_sids;
5144 
5145 error:
5146     LocalFree(psd);
5147     psd = NULL;
5148 free_sids:
5149     for(i = 0; i < sid_count; i++)
5150     {
5151         if (!cur_user || sidlist[i] != cur_user)
5152             FreeSid(sidlist[i]);
5153     }
5154     HeapFree(GetProcessHeap(), 0, sidlist);
5155 
5156     return psd;
5157 }
5158 
5159 #ifndef __REACTOS__ /* See propbag.cpp */
5160 /***********************************************************************
5161  *             SHCreatePropertyBagOnRegKey [SHLWAPI.471]
5162  *
5163  * Creates a property bag from a registry key
5164  *
5165  * PARAMS
5166  *  hKey       [I] Handle to the desired registry key
5167  *  subkey     [I] Name of desired subkey, or NULL to open hKey directly
5168  *  grfMode    [I] Optional flags
5169  *  riid       [I] IID of requested property bag interface
5170  *  ppv        [O] Address to receive pointer to the new interface
5171  *
5172  * RETURNS
5173  *  success: 0
5174  *  failure: error code
5175  *
5176  */
5177 HRESULT WINAPI SHCreatePropertyBagOnRegKey (HKEY hKey, LPCWSTR subkey,
5178     DWORD grfMode, REFIID riid, void **ppv)
5179 {
5180     FIXME("%p %s %d %s %p STUB\n", hKey, debugstr_w(subkey), grfMode,
5181           debugstr_guid(riid), ppv);
5182 
5183     return E_NOTIMPL;
5184 }
5185 #endif
5186 
5187 #ifndef __REACTOS__ /* See propbag.cpp */
5188 /***********************************************************************
5189  *             SHGetViewStatePropertyBag [SHLWAPI.515]
5190  *
5191  * Retrieves a property bag in which the view state information of a folder
5192  * can be stored.
5193  *
5194  * PARAMS
5195  *  pidl        [I] PIDL of the folder requested
5196  *  bag_name    [I] Name of the property bag requested
5197  *  flags       [I] Optional flags
5198  *  riid        [I] IID of requested property bag interface
5199  *  ppv         [O] Address to receive pointer to the new interface
5200  *
5201  * RETURNS
5202  *  success: S_OK
5203  *  failure: error code
5204  *
5205  */
5206 HRESULT WINAPI SHGetViewStatePropertyBag(LPCITEMIDLIST pidl, LPWSTR bag_name,
5207     DWORD flags, REFIID riid, void **ppv)
5208 {
5209     FIXME("%p %s %d %s %p STUB\n", pidl, debugstr_w(bag_name), flags,
5210           debugstr_guid(riid), ppv);
5211 
5212     return E_NOTIMPL;
5213 }
5214 #endif
5215 
5216 /***********************************************************************
5217  *             SHFormatDateTimeW [SHLWAPI.354]
5218  *
5219  * Produces a string representation of a time.
5220  *
5221  * PARAMS
5222  *  fileTime   [I] Pointer to FILETIME structure specifying the time
5223  *  flags      [I] Flags specifying the desired output
5224  *  buf        [O] Pointer to buffer for output
5225  *  size       [I] Number of characters that can be contained in buffer
5226  *
5227  * RETURNS
5228  *  success: number of characters written to the buffer
5229  *  failure: 0
5230  *
5231  */
5232 INT WINAPI SHFormatDateTimeW(const FILETIME UNALIGNED *fileTime, DWORD *flags,
5233     LPWSTR buf, UINT size)
5234 {
5235 #define SHFORMATDT_UNSUPPORTED_FLAGS (FDTF_RELATIVE | FDTF_LTRDATE | FDTF_RTLDATE | FDTF_NOAUTOREADINGORDER)
5236     DWORD fmt_flags = flags ? *flags : FDTF_DEFAULT;
5237     SYSTEMTIME st;
5238     FILETIME ft;
5239     INT ret = 0;
5240 
5241     TRACE("%p %p %p %u\n", fileTime, flags, buf, size);
5242 
5243     if (!buf || !size)
5244         return 0;
5245 
5246     if (fmt_flags & SHFORMATDT_UNSUPPORTED_FLAGS)
5247         FIXME("ignoring some flags - 0x%08x\n", fmt_flags & SHFORMATDT_UNSUPPORTED_FLAGS);
5248 
5249     FileTimeToLocalFileTime(fileTime, &ft);
5250     FileTimeToSystemTime(&ft, &st);
5251 
5252     /* first of all date */
5253     if (fmt_flags & (FDTF_LONGDATE | FDTF_SHORTDATE))
5254     {
5255         static const WCHAR sep1[] = {',',' ',0};
5256         static const WCHAR sep2[] = {' ',0};
5257 
5258         DWORD date = fmt_flags & FDTF_LONGDATE ? DATE_LONGDATE : DATE_SHORTDATE;
5259         ret = GetDateFormatW(LOCALE_USER_DEFAULT, date, &st, NULL, buf, size);
5260         if (ret >= size) return ret;
5261 
5262         /* add separator */
5263         if (ret < size && (fmt_flags & (FDTF_LONGTIME | FDTF_SHORTTIME)))
5264         {
5265             if ((fmt_flags & FDTF_LONGDATE) && (ret < size + 2))
5266             {
5267                 lstrcatW(&buf[ret-1], sep1);
5268                 ret += 2;
5269             }
5270             else
5271             {
5272                 lstrcatW(&buf[ret-1], sep2);
5273                 ret++;
5274             }
5275         }
5276     }
5277     /* time part */
5278     if (fmt_flags & (FDTF_LONGTIME | FDTF_SHORTTIME))
5279     {
5280         DWORD time = fmt_flags & FDTF_LONGTIME ? 0 : TIME_NOSECONDS;
5281 
5282         if (ret) ret--;
5283         ret += GetTimeFormatW(LOCALE_USER_DEFAULT, time, &st, NULL, &buf[ret], size - ret);
5284     }
5285 
5286     return ret;
5287 
5288 #undef SHFORMATDT_UNSUPPORTED_FLAGS
5289 }
5290 
5291 /***********************************************************************
5292  *             SHFormatDateTimeA [SHLWAPI.353]
5293  *
5294  * See SHFormatDateTimeW.
5295  *
5296  */
5297 INT WINAPI SHFormatDateTimeA(const FILETIME UNALIGNED *fileTime, DWORD *flags,
5298     LPSTR buf, UINT size)
5299 {
5300     WCHAR *bufW;
5301     INT retval;
5302 
5303     if (!buf || !size)
5304         return 0;
5305 
5306     bufW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * size);
5307     retval = SHFormatDateTimeW(fileTime, flags, bufW, size);
5308 
5309     if (retval != 0)
5310         WideCharToMultiByte(CP_ACP, 0, bufW, -1, buf, size, NULL, NULL);
5311 
5312     HeapFree(GetProcessHeap(), 0, bufW);
5313     return retval;
5314 }
5315 
5316 /***********************************************************************
5317  *             ZoneCheckUrlExW [SHLWAPI.231]
5318  *
5319  * Checks the details of the security zone for the supplied site. (?)
5320  *
5321  * PARAMS
5322  *
5323  *  szURL   [I] Pointer to the URL to check
5324  *
5325  *  Other parameters currently unknown.
5326  *
5327  * RETURNS
5328  *  unknown
5329  */
5330 
5331 INT WINAPI ZoneCheckUrlExW(LPWSTR szURL, PVOID pUnknown, DWORD dwUnknown2,
5332     DWORD dwUnknown3, DWORD dwUnknown4, DWORD dwUnknown5, DWORD dwUnknown6,
5333     DWORD dwUnknown7)
5334 {
5335     FIXME("(%s,%p,%x,%x,%x,%x,%x,%x) STUB\n", debugstr_w(szURL), pUnknown, dwUnknown2,
5336         dwUnknown3, dwUnknown4, dwUnknown5, dwUnknown6, dwUnknown7);
5337 
5338     return 0;
5339 }
5340 
5341 /***********************************************************************
5342  *             SHVerbExistsNA [SHLWAPI.196]
5343  *
5344  *
5345  * PARAMS
5346  *
5347  *  verb [I] a string, often appears to be an extension.
5348  *
5349  *  Other parameters currently unknown.
5350  *
5351  * RETURNS
5352  *  unknown
5353  */
5354 INT WINAPI SHVerbExistsNA(LPSTR verb, PVOID pUnknown, PVOID pUnknown2, DWORD dwUnknown3)
5355 {
5356     FIXME("(%s, %p, %p, %i) STUB\n",verb, pUnknown, pUnknown2, dwUnknown3);
5357     return 0;
5358 }
5359 
5360 /*************************************************************************
5361  *      @	[SHLWAPI.538]
5362  *
5363  *  Undocumented:  Implementation guessed at via Name and behavior
5364  *
5365  * PARAMS
5366  *  lpUnknown [I] Object to get an IServiceProvider interface from
5367  *  riid      [I] Function requested for QueryService call
5368  *  lppOut    [O] Destination for the service interface pointer
5369  *
5370  * RETURNS
5371  *  Success: S_OK. lppOut contains an object providing the requested service
5372  *  Failure: An HRESULT error code
5373  *
5374  * NOTES
5375  *  lpUnknown is expected to support the IServiceProvider interface.
5376  */
5377 HRESULT WINAPI IUnknown_QueryServiceForWebBrowserApp(IUnknown* lpUnknown,
5378         REFGUID riid, LPVOID *lppOut)
5379 {
5380     FIXME("%p %s %p semi-STUB\n", lpUnknown, debugstr_guid(riid), lppOut);
5381     return IUnknown_QueryService(lpUnknown,&IID_IWebBrowserApp,riid,lppOut);
5382 }
5383 
5384 #ifdef __REACTOS__
5385 HRESULT VariantChangeTypeForRead(_Inout_ VARIANTARG *pvarg, _In_ VARTYPE vt)
5386 {
5387     HRESULT hr;
5388     VARIANTARG vargTemp;
5389     VARIANT variTemp;
5390 
5391     if (V_VT(pvarg) == vt || vt == VT_EMPTY)
5392         return S_OK;
5393 
5394     vargTemp = *pvarg;
5395 
5396     if (V_VT(&vargTemp) != VT_BSTR || vt <= VT_NULL)
5397         goto DoDefault;
5398 
5399     if (vt == VT_I1 || vt == VT_I2 || vt == VT_I4)
5400     {
5401         if (!StrToIntExW(V_BSTR(&vargTemp), STIF_SUPPORT_HEX, &V_I4(&variTemp)))
5402             goto DoDefault;
5403 
5404         V_VT(&variTemp) = VT_INT;
5405         VariantInit(pvarg);
5406         hr = VariantChangeType(pvarg, &variTemp, 0, vt);
5407         VariantClear(&vargTemp);
5408         return hr;
5409     }
5410 
5411     if (vt <= VT_DECIMAL)
5412         goto DoDefault;
5413 
5414     if (vt == VT_UI1 || vt == VT_UI2 || vt == VT_UI4)
5415     {
5416         if (!StrToIntExW(V_BSTR(&vargTemp), STIF_SUPPORT_HEX, (LPINT)&V_UI4(&variTemp)))
5417             goto DoDefault;
5418 
5419         V_VT(&variTemp) = VT_UINT;
5420         VariantInit(pvarg);
5421         hr = VariantChangeType(pvarg, &variTemp, 0, vt);
5422         VariantClear(&vargTemp);
5423         return hr;
5424     }
5425 
5426     if (vt == VT_INT || vt == VT_UINT)
5427     {
5428         if (!StrToIntExW(V_BSTR(&vargTemp), STIF_SUPPORT_HEX, &V_INT(&variTemp)))
5429             goto DoDefault;
5430 
5431         V_VT(&variTemp) = VT_UINT;
5432         VariantInit(pvarg);
5433         hr = VariantChangeType(pvarg, &variTemp, 0, vt);
5434         VariantClear(&vargTemp);
5435         return hr;
5436     }
5437 
5438 DoDefault:
5439     VariantInit(pvarg);
5440     hr = VariantChangeType(pvarg, &vargTemp, 0, vt);
5441     VariantClear(&vargTemp);
5442     return hr;
5443 }
5444 
5445 BOOL
5446 VariantArrayToBuffer(
5447     _In_ const VARIANT *pvarIn,
5448     _Out_writes_(cbSize) LPVOID pvDest,
5449     _In_ SIZE_T cbSize)
5450 {
5451     LPVOID pvData;
5452     LONG LowerBound, UpperBound;
5453     LPSAFEARRAY pArray;
5454 
5455     /* Only supports byte array */
5456     if (!pvarIn || V_VT(pvarIn) != (VT_UI1 | VT_ARRAY))
5457         return FALSE;
5458 
5459     /* Boundary check and access */
5460     pArray = V_ARRAY(pvarIn);
5461     if (SafeArrayGetDim(pArray) == 1 &&
5462         SUCCEEDED(SafeArrayGetLBound(pArray, 1, &LowerBound)) &&
5463         SUCCEEDED(SafeArrayGetUBound(pArray, 1, &UpperBound)) &&
5464         ((LONG)cbSize <= UpperBound - LowerBound + 1) &&
5465         SUCCEEDED(SafeArrayAccessData(pArray, &pvData)))
5466     {
5467         CopyMemory(pvDest, pvData, cbSize);
5468         SafeArrayUnaccessData(pArray);
5469         return TRUE; /* Success */
5470     }
5471 
5472     return FALSE; /* Failure */
5473 }
5474 
5475 /**************************************************************************
5476  *  SHPropertyBag_ReadType (SHLWAPI.493)
5477  *
5478  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readtype.htm
5479  */
5480 HRESULT WINAPI
5481 SHPropertyBag_ReadType(IPropertyBag *ppb, LPCWSTR pszPropName, VARIANTARG *pvarg, VARTYPE vt)
5482 {
5483     HRESULT hr;
5484 
5485     VariantInit(pvarg);
5486     V_VT(pvarg) = vt;
5487 
5488     hr = IPropertyBag_Read(ppb, pszPropName, pvarg, NULL);
5489     if (FAILED(hr))
5490     {
5491         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
5492         VariantInit(pvarg);
5493         return hr;
5494     }
5495 
5496     return VariantChangeTypeForRead(pvarg, vt);
5497 }
5498 
5499 /**************************************************************************
5500  *  SHPropertyBag_ReadBOOL (SHLWAPI.534)
5501  *
5502  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readbool.htm
5503  */
5504 HRESULT WINAPI SHPropertyBag_ReadBOOL(IPropertyBag *ppb, LPCWSTR pszPropName, BOOL *pbValue)
5505 {
5506     HRESULT hr;
5507     VARIANTARG varg;
5508 
5509     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pbValue);
5510 
5511     if (!ppb || !pszPropName || !pbValue)
5512     {
5513         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pbValue);
5514         return E_INVALIDARG;
5515     }
5516 
5517     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_BOOL);
5518     if (SUCCEEDED(hr))
5519         *pbValue = (V_BOOL(&varg) == VARIANT_TRUE);
5520 
5521     return hr;
5522 }
5523 
5524 /**************************************************************************
5525  *  SHPropertyBag_ReadBOOLOld (SHLWAPI.498)
5526  *
5527  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readboolold.htm
5528  */
5529 BOOL WINAPI SHPropertyBag_ReadBOOLOld(IPropertyBag *ppb, LPCWSTR pszPropName, BOOL bDefValue)
5530 {
5531     VARIANTARG varg;
5532     HRESULT hr;
5533 
5534     TRACE("%p %s %d\n", ppb, debugstr_w(pszPropName), bDefValue);
5535 
5536     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_BOOL);
5537     if (FAILED(hr))
5538         return bDefValue;
5539 
5540     return V_BOOL(&varg) == VARIANT_TRUE;
5541 }
5542 
5543 /**************************************************************************
5544  *  SHPropertyBag_ReadSHORT (SHLWAPI.527)
5545  *
5546  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readshort.htm
5547  */
5548 HRESULT WINAPI SHPropertyBag_ReadSHORT(IPropertyBag *ppb, LPCWSTR pszPropName, SHORT *psValue)
5549 {
5550     HRESULT hr;
5551     VARIANTARG varg;
5552 
5553     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), psValue);
5554 
5555     if (!ppb || !pszPropName || !psValue)
5556     {
5557         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), psValue);
5558         return E_INVALIDARG;
5559     }
5560 
5561     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_UI2);
5562     if (SUCCEEDED(hr))
5563         *psValue = V_UI2(&varg);
5564 
5565     return hr;
5566 }
5567 #endif
5568 
5569 /**************************************************************************
5570  *  SHPropertyBag_ReadLONG (SHLWAPI.496)
5571  *
5572  * This function asks a property bag to read a named property as a LONG.
5573  *
5574  * PARAMS
5575  *  ppb: a IPropertyBag interface
5576  *  pszPropName:  Unicode string that names the property
5577  *  pValue: address to receive the property value as a 32-bit signed integer
5578  *
5579  * RETURNS
5580  *  HRESULT codes
5581 #ifdef __REACTOS__
5582  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readlong.htm
5583 #endif
5584  */
5585 HRESULT WINAPI SHPropertyBag_ReadLONG(IPropertyBag *ppb, LPCWSTR pszPropName, LPLONG pValue)
5586 {
5587 #ifdef __REACTOS__
5588     HRESULT hr;
5589     VARIANTARG varg;
5590 
5591     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pValue);
5592 
5593     if (!ppb || !pszPropName || !pValue)
5594     {
5595         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pValue);
5596         return E_INVALIDARG;
5597     }
5598 
5599     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_I4);
5600     if (SUCCEEDED(hr))
5601         *pValue = V_I4(&varg);
5602 #else
5603     VARIANT var;
5604     HRESULT hr;
5605     TRACE("%p %s %p\n", ppb,debugstr_w(pszPropName),pValue);
5606     if (!pszPropName || !ppb || !pValue)
5607         return E_INVALIDARG;
5608     V_VT(&var) = VT_I4;
5609     hr = IPropertyBag_Read(ppb, pszPropName, &var, NULL);
5610     if (SUCCEEDED(hr))
5611     {
5612         if (V_VT(&var) == VT_I4)
5613             *pValue = V_I4(&var);
5614         else
5615             hr = DISP_E_BADVARTYPE;
5616     }
5617 #endif
5618     return hr;
5619 }
5620 
5621 #ifdef __REACTOS__
5622 /**************************************************************************
5623  *  SHPropertyBag_ReadDWORD (SHLWAPI.507)
5624  *
5625  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readdword.htm
5626  */
5627 HRESULT WINAPI SHPropertyBag_ReadDWORD(IPropertyBag *ppb, LPCWSTR pszPropName, DWORD *pdwValue)
5628 {
5629     HRESULT hr;
5630     VARIANTARG varg;
5631 
5632     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pdwValue);
5633 
5634     if (!ppb || !pszPropName || !pdwValue)
5635     {
5636         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pdwValue);
5637         return E_INVALIDARG;
5638     }
5639 
5640     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_UI4);
5641     if (SUCCEEDED(hr))
5642         *pdwValue = V_UI4(&varg);
5643 
5644     return hr;
5645 }
5646 
5647 /**************************************************************************
5648  *  SHPropertyBag_ReadBSTR (SHLWAPI.520)
5649  *
5650  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readbstr.htm
5651  */
5652 HRESULT WINAPI SHPropertyBag_ReadBSTR(IPropertyBag *ppb, LPCWSTR pszPropName, BSTR *pbstr)
5653 {
5654     HRESULT hr;
5655     VARIANTARG varg;
5656 
5657     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pbstr);
5658 
5659     if (!ppb || !pszPropName || !pbstr)
5660     {
5661         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pbstr);
5662         return E_INVALIDARG;
5663     }
5664 
5665     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_BSTR);
5666     if (FAILED(hr))
5667         *pbstr = NULL;
5668     else
5669         *pbstr = V_BSTR(&varg);
5670 
5671     return hr;
5672 }
5673 
5674 /**************************************************************************
5675  *  SHPropertyBag_ReadStr (SHLWAPI.494)
5676  *
5677  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readstr.htm
5678  */
5679 HRESULT WINAPI SHPropertyBag_ReadStr(IPropertyBag *ppb, LPCWSTR pszPropName, LPWSTR pszDst, int cchMax)
5680 {
5681     HRESULT hr;
5682     VARIANTARG varg;
5683 
5684     TRACE("%p %s %p %d\n", ppb, debugstr_w(pszPropName), pszDst, cchMax);
5685 
5686     if (!ppb || !pszPropName || !pszDst)
5687     {
5688         ERR("%p %s %p %d\n", ppb, debugstr_w(pszPropName), pszDst, cchMax);
5689         return E_INVALIDARG;
5690     }
5691 
5692     hr = SHPropertyBag_ReadType(ppb, pszPropName, &varg, VT_BSTR);
5693     if (FAILED(hr))
5694         return E_FAIL;
5695 
5696     StrCpyNW(pszDst, V_BSTR(&varg), cchMax);
5697     VariantClear(&varg);
5698     return hr;
5699 }
5700 
5701 /**************************************************************************
5702  *  SHPropertyBag_ReadPOINTL (SHLWAPI.521)
5703  *
5704  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readpointl.htm
5705  */
5706 HRESULT WINAPI SHPropertyBag_ReadPOINTL(IPropertyBag *ppb, LPCWSTR pszPropName, POINTL *pptl)
5707 {
5708     HRESULT hr;
5709     int cch, cch2;
5710     WCHAR *pch, szBuff[MAX_PATH];
5711 
5712     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pptl);
5713 
5714     if (!ppb || !pszPropName || !pptl)
5715     {
5716         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pptl);
5717         return E_INVALIDARG;
5718     }
5719 
5720     StrCpyNW(szBuff, pszPropName, _countof(szBuff));
5721 
5722     cch = lstrlenW(szBuff);
5723     cch2 = _countof(szBuff) - cch;
5724     if (cch2 < _countof(L".x"))
5725     {
5726         ERR("%s is too long\n", debugstr_w(pszPropName));
5727         return E_FAIL;
5728     }
5729 
5730     pch = &szBuff[cch];
5731 
5732     StrCpyNW(pch, L".x", cch2);
5733     hr = SHPropertyBag_ReadLONG(ppb, szBuff, &pptl->x);
5734     if (FAILED(hr))
5735         return hr;
5736 
5737     StrCpyNW(pch, L".y", cch2);
5738     return SHPropertyBag_ReadLONG(ppb, szBuff, &pptl->y);
5739 }
5740 
5741 /**************************************************************************
5742  *  SHPropertyBag_ReadPOINTS (SHLWAPI.525)
5743  *
5744  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readpoints.htm
5745  */
5746 HRESULT WINAPI SHPropertyBag_ReadPOINTS(IPropertyBag *ppb, LPCWSTR pszPropName, POINTS *ppts)
5747 {
5748     HRESULT hr;
5749     POINTL ptl;
5750 
5751     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), ppts);
5752 
5753     if (!ppb || !pszPropName || !ppts)
5754     {
5755         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), ppts);
5756         return E_INVALIDARG;
5757     }
5758 
5759     hr = SHPropertyBag_ReadPOINTL(ppb, pszPropName, &ptl);
5760     if (FAILED(hr))
5761         return hr;
5762 
5763     ppts->x = ptl.x;
5764     ppts->y = ptl.y;
5765     return hr;
5766 }
5767 
5768 /**************************************************************************
5769  *  SHPropertyBag_ReadRECTL (SHLWAPI.523)
5770  *
5771  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readrectl.htm
5772  */
5773 HRESULT WINAPI SHPropertyBag_ReadRECTL(IPropertyBag *ppb, LPCWSTR pszPropName, RECTL *prcl)
5774 {
5775     HRESULT hr;
5776     int cch, cch2;
5777     WCHAR *pch, szBuff[MAX_PATH];
5778 
5779     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), prcl);
5780 
5781     if (!ppb || !pszPropName || !prcl)
5782     {
5783         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), prcl);
5784         return E_INVALIDARG;
5785     }
5786 
5787     StrCpyNW(szBuff, pszPropName, _countof(szBuff));
5788 
5789     cch = lstrlenW(szBuff);
5790     cch2 = _countof(szBuff) - cch;
5791     if (cch2 < _countof(L".bottom"))
5792     {
5793         ERR("%s is too long\n", debugstr_w(pszPropName));
5794         return E_FAIL;
5795     }
5796 
5797     pch = &szBuff[cch];
5798 
5799     StrCpyNW(pch, L".left", cch2);
5800     hr = SHPropertyBag_ReadLONG(ppb, szBuff, &prcl->left);
5801     if (FAILED(hr))
5802         return hr;
5803 
5804     StrCpyNW(pch, L".top", cch2);
5805     hr = SHPropertyBag_ReadLONG(ppb, szBuff, &prcl->top);
5806     if (FAILED(hr))
5807         return hr;
5808 
5809     StrCpyNW(pch, L".right", cch2);
5810     hr = SHPropertyBag_ReadLONG(ppb, szBuff, &prcl->right);
5811     if (FAILED(hr))
5812         return hr;
5813 
5814     StrCpyNW(pch, L".bottom", cch2);
5815     return SHPropertyBag_ReadLONG(ppb, szBuff, &prcl->bottom);
5816 }
5817 
5818 /**************************************************************************
5819  *  SHPropertyBag_ReadGUID (SHLWAPI.505)
5820  *
5821  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readguid.htm
5822  */
5823 HRESULT WINAPI SHPropertyBag_ReadGUID(IPropertyBag *ppb, LPCWSTR pszPropName, GUID *pguid)
5824 {
5825     HRESULT hr;
5826     BOOL bRet;
5827     VARIANT vari;
5828 
5829     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pguid);
5830 
5831     if (!ppb || !pszPropName || !pguid)
5832     {
5833         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pguid);
5834         return E_INVALIDARG;
5835     }
5836 
5837     hr = SHPropertyBag_ReadType(ppb, pszPropName, &vari, VT_EMPTY);
5838     if (FAILED(hr))
5839     {
5840         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pguid);
5841         return hr;
5842     }
5843 
5844     if (V_VT(&vari) == (VT_UI1 | VT_ARRAY)) /* Byte Array */
5845         bRet = VariantArrayToBuffer(&vari, pguid, sizeof(*pguid));
5846     else if (V_VT(&vari) == VT_BSTR)
5847         bRet = GUIDFromStringW(V_BSTR(&vari), pguid);
5848     else
5849 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
5850         bRet = FALSE;
5851 #else
5852         bRet = TRUE; /* This is by design in WinXP/Win2k3. */
5853 #endif
5854 
5855     if (!bRet)
5856         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pguid);
5857 
5858     VariantClear(&vari);
5859     return (bRet ? S_OK : E_FAIL);
5860 }
5861 
5862 /**************************************************************************
5863  *  SHPropertyBag_ReadStream (SHLWAPI.531)
5864  *
5865  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/readstream.htm
5866  */
5867 HRESULT WINAPI SHPropertyBag_ReadStream(IPropertyBag *ppb, LPCWSTR pszPropName, IStream **ppStream)
5868 {
5869     HRESULT hr;
5870     VARIANT vari;
5871 
5872     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), ppStream);
5873 
5874     if (!ppb || !pszPropName || !ppStream)
5875     {
5876         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), ppStream);
5877         return E_INVALIDARG;
5878     }
5879 
5880     hr = SHPropertyBag_ReadType(ppb, pszPropName, &vari, VT_UNKNOWN);
5881     if (FAILED(hr))
5882         return hr;
5883 
5884     hr = IUnknown_QueryInterface(V_UNKNOWN(&vari), &IID_IStream, (void **)ppStream);
5885     IUnknown_Release(V_UNKNOWN(&vari));
5886 
5887     return hr;
5888 }
5889 
5890 /**************************************************************************
5891  *  SHPropertyBag_Delete (SHLWAPI.535)
5892  *
5893  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/delete.htm
5894  */
5895 HRESULT WINAPI SHPropertyBag_Delete(IPropertyBag *ppb, LPCWSTR pszPropName)
5896 {
5897     VARIANT vari;
5898 
5899     TRACE("%p %s\n", ppb, debugstr_w(pszPropName));
5900 
5901     if (!ppb || !pszPropName)
5902     {
5903         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
5904         return E_INVALIDARG;
5905     }
5906 
5907     V_VT(&vari) = VT_EMPTY;
5908     return IPropertyBag_Write(ppb, pszPropName, &vari);
5909 }
5910 
5911 /**************************************************************************
5912  *  SHPropertyBag_WriteBOOL (SHLWAPI.499)
5913  *
5914  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writebool.htm
5915  */
5916 HRESULT WINAPI SHPropertyBag_WriteBOOL(IPropertyBag *ppb, LPCWSTR pszPropName, BOOL bValue)
5917 {
5918     VARIANT vari;
5919 
5920     TRACE("%p %s %d\n", ppb, debugstr_w(pszPropName), bValue);
5921 
5922     if (!ppb || !pszPropName)
5923     {
5924         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
5925         return E_INVALIDARG;
5926     }
5927 
5928     V_VT(&vari) = VT_BOOL;
5929     V_BOOL(&vari) = (bValue ? VARIANT_TRUE : VARIANT_FALSE); /* NOTE: VARIANT_TRUE is (SHORT)-1 */
5930     return IPropertyBag_Write(ppb, pszPropName, &vari);
5931 }
5932 
5933 /**************************************************************************
5934  *  SHPropertyBag_WriteSHORT (SHLWAPI.528)
5935  *
5936  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writeshort.htm
5937  */
5938 HRESULT WINAPI SHPropertyBag_WriteSHORT(IPropertyBag *ppb, LPCWSTR pszPropName, SHORT sValue)
5939 {
5940     VARIANT vari;
5941 
5942     TRACE("%p %s %d\n", ppb, debugstr_w(pszPropName), sValue);
5943 
5944     if (!ppb || !pszPropName)
5945     {
5946         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
5947         return E_INVALIDARG;
5948     }
5949 
5950     V_VT(&vari) = VT_UI2;
5951     V_UI2(&vari) = sValue;
5952     return IPropertyBag_Write(ppb, pszPropName, &vari);
5953 }
5954 
5955 /**************************************************************************
5956  *  SHPropertyBag_WriteLONG (SHLWAPI.497)
5957  *
5958  * This function asks a property bag to write a named property as a LONG.
5959  *
5960  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writelong.htm
5961  */
5962 HRESULT WINAPI SHPropertyBag_WriteLONG(IPropertyBag *ppb, LPCWSTR pszPropName, LONG lValue)
5963 {
5964     VARIANT vari;
5965 
5966     TRACE("%p %s %ld\n", ppb, debugstr_w(pszPropName), lValue);
5967 
5968     if (!ppb || !pszPropName)
5969     {
5970         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
5971         return E_INVALIDARG;
5972     }
5973 
5974     V_VT(&vari) = VT_I4;
5975     V_I4(&vari) = lValue;
5976     return IPropertyBag_Write(ppb, pszPropName, &vari);
5977 }
5978 
5979 /**************************************************************************
5980  *  SHPropertyBag_WriteDWORD (SHLWAPI.508)
5981  *
5982  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writedword.htm
5983  */
5984 HRESULT WINAPI SHPropertyBag_WriteDWORD(IPropertyBag *ppb, LPCWSTR pszPropName, DWORD dwValue)
5985 {
5986     VARIANT vari;
5987 
5988     TRACE("%p %s %lu\n", ppb, debugstr_w(pszPropName), dwValue);
5989 
5990     if (!ppb || !pszPropName)
5991     {
5992         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
5993         return E_INVALIDARG;
5994     }
5995 
5996     V_VT(&vari) = VT_UI4;
5997     V_UI4(&vari) = dwValue;
5998     return IPropertyBag_Write(ppb, pszPropName, &vari);
5999 }
6000 
6001 /**************************************************************************
6002  *  SHPropertyBag_WriteStr (SHLWAPI.495)
6003  *
6004  * This function asks a property bag to write a string as the value of a named property.
6005  *
6006  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writestr.htm
6007  */
6008 HRESULT WINAPI SHPropertyBag_WriteStr(IPropertyBag *ppb, LPCWSTR pszPropName, LPCWSTR pszValue)
6009 {
6010     HRESULT hr;
6011     VARIANT vari;
6012 
6013     TRACE("%p %s %s\n", ppb, debugstr_w(pszPropName), debugstr_w(pszValue));
6014 
6015     if (!ppb || !pszPropName)
6016     {
6017         ERR("%p %s\n", ppb, debugstr_w(pszPropName));
6018         return E_INVALIDARG;
6019     }
6020 
6021     V_BSTR(&vari) = SysAllocString(pszValue);
6022     if (!V_BSTR(&vari))
6023         return E_OUTOFMEMORY;
6024 
6025     V_VT(&vari) = VT_BSTR;
6026     hr = IPropertyBag_Write(ppb, pszPropName, &vari);
6027 
6028     SysFreeString(V_BSTR(&vari));
6029     return hr;
6030 }
6031 
6032 /**************************************************************************
6033  *  SHPropertyBag_WriteGUID (SHLWAPI.506)
6034  *
6035  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writeguid.htm
6036  */
6037 HRESULT WINAPI SHPropertyBag_WriteGUID(IPropertyBag *ppb, LPCWSTR pszPropName, const GUID *pguid)
6038 {
6039     WCHAR szBuff[64];
6040 
6041     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pguid);
6042 
6043     if (!ppb || !pszPropName || !pguid)
6044     {
6045         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pguid);
6046         return E_INVALIDARG;
6047     }
6048 
6049     SHStringFromGUIDW(pguid, szBuff, _countof(szBuff));
6050     return SHPropertyBag_WriteStr(ppb, pszPropName, szBuff);
6051 }
6052 
6053 /**************************************************************************
6054  *  SHPropertyBag_WriteStream (SHLWAPI.532)
6055  *
6056  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writestream.htm
6057  */
6058 HRESULT WINAPI SHPropertyBag_WriteStream(IPropertyBag *ppb, LPCWSTR pszPropName, IStream *pStream)
6059 {
6060     VARIANT vari;
6061 
6062     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pStream);
6063 
6064     if (!ppb || !pszPropName || !pStream)
6065     {
6066         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pStream);
6067         return E_INVALIDARG;
6068     }
6069 
6070     V_VT(&vari) = VT_UNKNOWN;
6071     V_UNKNOWN(&vari) = (IUnknown*)pStream;
6072     return IPropertyBag_Write(ppb, pszPropName, &vari);
6073 }
6074 
6075 /**************************************************************************
6076  *  SHPropertyBag_WritePOINTL (SHLWAPI.522)
6077  *
6078  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writepointl.htm
6079  */
6080 HRESULT WINAPI SHPropertyBag_WritePOINTL(IPropertyBag *ppb, LPCWSTR pszPropName, const POINTL *pptl)
6081 {
6082     HRESULT hr;
6083     int cch, cch2;
6084     WCHAR *pch, szBuff[MAX_PATH];
6085 
6086     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), pptl);
6087 
6088     if (!ppb || !pszPropName || !pptl)
6089     {
6090         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), pptl);
6091         return E_INVALIDARG;
6092     }
6093 
6094     StrCpyNW(szBuff, pszPropName, _countof(szBuff));
6095 
6096     cch = lstrlenW(szBuff);
6097     cch2 = _countof(szBuff) - cch;
6098     if (cch2 < _countof(L".x"))
6099     {
6100         ERR("%s is too long\n", debugstr_w(pszPropName));
6101         return E_FAIL;
6102     }
6103 
6104     pch = &szBuff[cch];
6105 
6106     StrCpyNW(pch, L".x", cch2);
6107     hr = SHPropertyBag_WriteLONG(ppb, szBuff, pptl->x);
6108     if (FAILED(hr))
6109         return hr;
6110 
6111     StrCpyNW(pch, L".y", cch2);
6112     hr = SHPropertyBag_WriteLONG(ppb, szBuff, pptl->y);
6113     if (FAILED(hr))
6114     {
6115         StrCpyNW(pch, L".x", cch2);
6116         return SHPropertyBag_Delete(ppb, szBuff);
6117     }
6118 
6119     return hr;
6120 }
6121 
6122 /**************************************************************************
6123  *  SHPropertyBag_WritePOINTS (SHLWAPI.526)
6124  *
6125  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writepoints.htm
6126  */
6127 HRESULT WINAPI SHPropertyBag_WritePOINTS(IPropertyBag *ppb, LPCWSTR pszPropName, const POINTS *ppts)
6128 {
6129     POINTL pt;
6130 
6131     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), ppts);
6132 
6133     if (!ppb || !pszPropName || !ppts)
6134     {
6135         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), ppts);
6136         return E_INVALIDARG;
6137     }
6138 
6139     pt.x = ppts->x;
6140     pt.y = ppts->y;
6141     return SHPropertyBag_WritePOINTL(ppb, pszPropName, &pt);
6142 }
6143 
6144 /**************************************************************************
6145  *  SHPropertyBag_WriteRECTL (SHLWAPI.524)
6146  *
6147  * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/writerectl.htm
6148  */
6149 HRESULT WINAPI SHPropertyBag_WriteRECTL(IPropertyBag *ppb, LPCWSTR pszPropName, const RECTL *prcl)
6150 {
6151     HRESULT hr;
6152     int cch, cch2;
6153     WCHAR *pch, szBuff[MAX_PATH];
6154 
6155     TRACE("%p %s %p\n", ppb, debugstr_w(pszPropName), prcl);
6156 
6157     if (!ppb || !pszPropName || !prcl)
6158     {
6159         ERR("%p %s %p\n", ppb, debugstr_w(pszPropName), prcl);
6160         return E_INVALIDARG;
6161     }
6162 
6163     StrCpyNW(szBuff, pszPropName, _countof(szBuff));
6164 
6165     cch = lstrlenW(szBuff);
6166     cch2 = _countof(szBuff) - cch;
6167     if (cch2 < _countof(L".bottom"))
6168     {
6169         ERR("%s is too long\n", debugstr_w(pszPropName));
6170         return E_FAIL;
6171     }
6172 
6173     pch = &szBuff[cch];
6174 
6175     StrCpyNW(pch, L".left", cch2);
6176     hr = SHPropertyBag_WriteLONG(ppb, szBuff, prcl->left);
6177     if (SUCCEEDED(hr))
6178     {
6179         StrCpyNW(pch, L".top", cch2);
6180         hr = SHPropertyBag_WriteLONG(ppb, szBuff, prcl->top);
6181         if (SUCCEEDED(hr))
6182         {
6183             StrCpyNW(pch, L".right", cch2);
6184             hr = SHPropertyBag_WriteLONG(ppb, szBuff, prcl->right);
6185             if (SUCCEEDED(hr))
6186             {
6187                 StrCpyNW(pch, L".bottom", cch2);
6188                 hr = SHPropertyBag_WriteLONG(ppb, szBuff, prcl->bottom);
6189                 if (SUCCEEDED(hr))
6190                     return hr; /* All successful */
6191 
6192                 StrCpyNW(pch, L".right", cch2);
6193                 hr = SHPropertyBag_Delete(ppb, szBuff);
6194                 if (SUCCEEDED(hr))
6195                     return hr;
6196             }
6197 
6198             StrCpyNW(pch, L".top", cch2);
6199             hr = SHPropertyBag_Delete(ppb, szBuff);
6200             if (SUCCEEDED(hr))
6201                 return hr;
6202         }
6203 
6204         StrCpyNW(pch, L".left", cch2);
6205         hr = SHPropertyBag_Delete(ppb, szBuff);
6206         if (SUCCEEDED(hr))
6207             return hr;
6208     }
6209 
6210     return hr;
6211 }
6212 #endif
6213 
6214 /* return flags for SHGetObjectCompatFlags, names derived from registry value names */
6215 #define OBJCOMPAT_OTNEEDSSFCACHE           0x00000001
6216 #define OBJCOMPAT_NO_WEBVIEW               0x00000002
6217 #define OBJCOMPAT_UNBINDABLE               0x00000004
6218 #define OBJCOMPAT_PINDLL                   0x00000008
6219 #define OBJCOMPAT_NEEDSFILESYSANCESTOR     0x00000010
6220 #define OBJCOMPAT_NOTAFILESYSTEM           0x00000020
6221 #define OBJCOMPAT_CTXMENU_NOVERBS          0x00000040
6222 #define OBJCOMPAT_CTXMENU_LIMITEDQI        0x00000080
6223 #define OBJCOMPAT_COCREATESHELLFOLDERONLY  0x00000100
6224 #define OBJCOMPAT_NEEDSSTORAGEANCESTOR     0x00000200
6225 #define OBJCOMPAT_NOLEGACYWEBVIEW          0x00000400
6226 #define OBJCOMPAT_CTXMENU_XPQCMFLAGS       0x00001000
6227 #define OBJCOMPAT_NOIPROPERTYSTORE         0x00002000
6228 
6229 /* a search table for compatibility flags */
6230 struct objcompat_entry {
6231     const WCHAR name[30];
6232     DWORD value;
6233 };
6234 
6235 /* expected to be sorted by name */
6236 static const struct objcompat_entry objcompat_table[] = {
6237     { {'C','O','C','R','E','A','T','E','S','H','E','L','L','F','O','L','D','E','R','O','N','L','Y',0},
6238       OBJCOMPAT_COCREATESHELLFOLDERONLY },
6239     { {'C','T','X','M','E','N','U','_','L','I','M','I','T','E','D','Q','I',0},
6240       OBJCOMPAT_CTXMENU_LIMITEDQI },
6241     { {'C','T','X','M','E','N','U','_','N','O','V','E','R','B','S',0},
6242       OBJCOMPAT_CTXMENU_LIMITEDQI },
6243     { {'C','T','X','M','E','N','U','_','X','P','Q','C','M','F','L','A','G','S',0},
6244       OBJCOMPAT_CTXMENU_XPQCMFLAGS },
6245     { {'N','E','E','D','S','F','I','L','E','S','Y','S','A','N','C','E','S','T','O','R',0},
6246       OBJCOMPAT_NEEDSFILESYSANCESTOR },
6247     { {'N','E','E','D','S','S','T','O','R','A','G','E','A','N','C','E','S','T','O','R',0},
6248       OBJCOMPAT_NEEDSSTORAGEANCESTOR },
6249     { {'N','O','I','P','R','O','P','E','R','T','Y','S','T','O','R','E',0},
6250       OBJCOMPAT_NOIPROPERTYSTORE },
6251     { {'N','O','L','E','G','A','C','Y','W','E','B','V','I','E','W',0},
6252       OBJCOMPAT_NOLEGACYWEBVIEW },
6253     { {'N','O','T','A','F','I','L','E','S','Y','S','T','E','M',0},
6254       OBJCOMPAT_NOTAFILESYSTEM },
6255     { {'N','O','_','W','E','B','V','I','E','W',0},
6256       OBJCOMPAT_NO_WEBVIEW },
6257     { {'O','T','N','E','E','D','S','S','F','C','A','C','H','E',0},
6258       OBJCOMPAT_OTNEEDSSFCACHE },
6259     { {'P','I','N','D','L','L',0},
6260       OBJCOMPAT_PINDLL },
6261     { {'U','N','B','I','N','D','A','B','L','E',0},
6262       OBJCOMPAT_UNBINDABLE }
6263 };
6264 
6265 /**************************************************************************
6266  *  SHGetObjectCompatFlags (SHLWAPI.476)
6267  *
6268  * Function returns an integer representation of compatibility flags stored
6269  * in registry for CLSID under ShellCompatibility subkey.
6270  *
6271  * PARAMS
6272  *  pUnk:  pointer to object IUnknown interface, idetifies CLSID
6273  *  clsid: pointer to CLSID to retrieve data for
6274  *
6275  * RETURNS
6276  *  0 on failure, flags set on success
6277  */
6278 DWORD WINAPI SHGetObjectCompatFlags(IUnknown *pUnk, const CLSID *clsid)
6279 {
6280     static const WCHAR compatpathW[] =
6281         {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
6282          'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
6283          'S','h','e','l','l','C','o','m','p','a','t','i','b','i','l','i','t','y','\\',
6284          'O','b','j','e','c','t','s','\\','%','s',0};
6285     WCHAR strW[sizeof(compatpathW)/sizeof(WCHAR) + 38 /* { CLSID } */];
6286     DWORD ret, length = sizeof(strW)/sizeof(WCHAR);
6287     OLECHAR *clsid_str;
6288     HKEY key;
6289     INT i;
6290 
6291     TRACE("%p %s\n", pUnk, debugstr_guid(clsid));
6292 
6293     if (!pUnk && !clsid) return 0;
6294 
6295     if (pUnk && !clsid)
6296     {
6297         FIXME("iface not handled\n");
6298         return 0;
6299     }
6300 
6301     StringFromCLSID(clsid, &clsid_str);
6302     sprintfW(strW, compatpathW, clsid_str);
6303     CoTaskMemFree(clsid_str);
6304 
6305     ret = RegOpenKeyW(HKEY_LOCAL_MACHINE, strW, &key);
6306     if (ret != ERROR_SUCCESS) return 0;
6307 
6308     /* now collect flag values */
6309     ret = 0;
6310     for (i = 0; RegEnumValueW(key, i, strW, &length, NULL, NULL, NULL, NULL) == ERROR_SUCCESS; i++)
6311     {
6312         INT left, right, res, x;
6313 
6314         /* search in table */
6315         left  = 0;
6316         right = sizeof(objcompat_table) / sizeof(struct objcompat_entry) - 1;
6317 
6318         while (right >= left) {
6319             x = (left + right) / 2;
6320             res = strcmpW(strW, objcompat_table[x].name);
6321             if (res == 0)
6322             {
6323                 ret |= objcompat_table[x].value;
6324                 break;
6325             }
6326             else if (res < 0)
6327                 right = x - 1;
6328             else
6329                 left = x + 1;
6330         }
6331 
6332         length = sizeof(strW)/sizeof(WCHAR);
6333     }
6334 
6335     return ret;
6336 }
6337