xref: /reactos/dll/win32/comctl32/comctl32undoc.c (revision cdf90707)
1 /*
2  * Undocumented functions from COMCTL32.DLL
3  *
4  * Copyright 1998 Eric Kohl
5  *           1998 Juergen Schmied <j.schmied@metronet.de>
6  *           2000 Eric Kohl for CodeWeavers
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  * NOTES
23  *     All of these functions are UNDOCUMENTED!! And I mean UNDOCUMENTED!!!!
24  *     Do NOT rely on names or contents of undocumented structures and types!!!
25  *     These functions are used by EXPLORER.EXE, IEXPLORE.EXE and
26  *     COMCTL32.DLL (internally).
27  *
28  */
29 #include "config.h"
30 #include "wine/port.h"
31 
32 #include <stdarg.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <limits.h>
36 
37 #define COBJMACROS
38 #define NONAMELESSUNION
39 
40 #include "windef.h"
41 #include "winbase.h"
42 #include "wingdi.h"
43 #include "winuser.h"
44 #include "winnls.h"
45 #include "winreg.h"
46 #include "commctrl.h"
47 #include "objbase.h"
48 #include "winerror.h"
49 
50 #include "wine/unicode.h"
51 #include "comctl32.h"
52 
53 #include "wine/debug.h"
54 
55 WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
56 
57 static const WCHAR strMRUList[] = { 'M','R','U','L','i','s','t',0 };
58 
59 /**************************************************************************
60  * Alloc [COMCTL32.71]
61  *
62  * Allocates memory block from the dll's private heap
63  *
64  * PARAMS
65  *     dwSize [I] size of the allocated memory block
66  *
67  * RETURNS
68  *     Success: pointer to allocated memory block
69  *     Failure: NULL
70  */
71 LPVOID WINAPI Alloc (DWORD dwSize)
72 {
73     return LocalAlloc( LMEM_ZEROINIT, dwSize );
74 }
75 
76 
77 /**************************************************************************
78  * ReAlloc [COMCTL32.72]
79  *
80  * Changes the size of an allocated memory block or allocates a memory
81  * block using the dll's private heap.
82  *
83  * PARAMS
84  *     lpSrc  [I] pointer to memory block which will be resized
85  *     dwSize [I] new size of the memory block.
86  *
87  * RETURNS
88  *     Success: pointer to the resized memory block
89  *     Failure: NULL
90  *
91  * NOTES
92  *     If lpSrc is a NULL-pointer, then ReAlloc allocates a memory
93  *     block like Alloc.
94  */
95 LPVOID WINAPI ReAlloc (LPVOID lpSrc, DWORD dwSize)
96 {
97     if (lpSrc)
98         return LocalReAlloc( lpSrc, dwSize, LMEM_ZEROINIT | LMEM_MOVEABLE );
99     else
100         return LocalAlloc( LMEM_ZEROINIT, dwSize);
101 }
102 
103 
104 /**************************************************************************
105  * Free [COMCTL32.73]
106  *
107  * Frees an allocated memory block from the dll's private heap.
108  *
109  * PARAMS
110  *     lpMem [I] pointer to memory block which will be freed
111  *
112  * RETURNS
113  *     Success: TRUE
114  *     Failure: FALSE
115  */
116 BOOL WINAPI Free (LPVOID lpMem)
117 {
118     return !LocalFree( lpMem );
119 }
120 
121 
122 /**************************************************************************
123  * GetSize [COMCTL32.74]
124  *
125  * Retrieves the size of the specified memory block from the dll's
126  * private heap.
127  *
128  * PARAMS
129  *     lpMem [I] pointer to an allocated memory block
130  *
131  * RETURNS
132  *     Success: size of the specified memory block
133  *     Failure: 0
134  */
135 DWORD WINAPI GetSize (LPVOID lpMem)
136 {
137     return LocalSize( lpMem );
138 }
139 
140 
141 /**************************************************************************
142  * MRU-Functions  {COMCTL32}
143  *
144  * NOTES
145  * The MRU-API is a set of functions to manipulate lists of M.R.U. (Most Recently
146  * Used) items. It is an undocumented API that is used (at least) by the shell
147  * and explorer to implement their recent documents feature.
148  *
149  * Since these functions are undocumented, they are unsupported by MS and
150  * may change at any time.
151  *
152  * Internally, the list is implemented as a last in, last out list of items
153  * persisted into the system registry under a caller chosen key. Each list
154  * item is given a one character identifier in the Ascii range from 'a' to
155  * '}'. A list of the identifiers in order from newest to oldest is stored
156  * under the same key in a value named "MRUList".
157  *
158  * Items are re-ordered by changing the order of the values in the MRUList
159  * value. When a new item is added, it becomes the new value of the oldest
160  * identifier, and that identifier is moved to the front of the MRUList value.
161  *
162  * Wine stores MRU-lists in the same registry format as Windows, so when
163  * switching between the builtin and native comctl32.dll no problems or
164  * incompatibilities should occur.
165  *
166  * The following undocumented structure is used to create an MRU-list:
167  *|typedef INT (CALLBACK *MRUStringCmpFn)(LPCTSTR lhs, LPCTSTR rhs);
168  *|typedef INT (CALLBACK *MRUBinaryCmpFn)(LPCVOID lhs, LPCVOID rhs, DWORD length);
169  *|
170  *|typedef struct tagMRUINFO
171  *|{
172  *|    DWORD   cbSize;
173  *|    UINT    uMax;
174  *|    UINT    fFlags;
175  *|    HKEY    hKey;
176  *|    LPTSTR  lpszSubKey;
177  *|    PROC    lpfnCompare;
178  *|} MRUINFO, *LPMRUINFO;
179  *
180  * MEMBERS
181  *  cbSize      [I] The size of the MRUINFO structure. This must be set
182  *                  to sizeof(MRUINFO) by the caller.
183  *  uMax        [I] The maximum number of items allowed in the list. Because
184  *                  of the limited number of identifiers, this should be set to
185  *                  a value from 1 to 30 by the caller.
186  *  fFlags      [I] If bit 0 is set, the list will be used to store binary
187  *                  data, otherwise it is assumed to store strings. If bit 1
188  *                  is set, every change made to the list will be reflected in
189  *                  the registry immediately, otherwise changes will only be
190  *                  written when the list is closed.
191  *  hKey        [I] The registry key that the list should be written under.
192  *                  This must be supplied by the caller.
193  *  lpszSubKey  [I] A caller supplied name of a subkey under hKey to write
194  *                  the list to. This may not be blank.
195  *  lpfnCompare [I] A caller supplied comparison function, which may be either
196  *                  an MRUStringCmpFn if dwFlags does not have bit 0 set, or a
197  *                  MRUBinaryCmpFn otherwise.
198  *
199  * FUNCTIONS
200  *  - Create an MRU-list with CreateMRUList() or CreateMRUListLazy().
201  *  - Add items to an MRU-list with AddMRUString() or AddMRUData().
202  *  - Remove items from an MRU-list with DelMRUString().
203  *  - Find data in an MRU-list with FindMRUString() or FindMRUData().
204  *  - Iterate through an MRU-list with EnumMRUList().
205  *  - Free an MRU-list with FreeMRUList().
206  */
207 
208 typedef INT (CALLBACK *MRUStringCmpFnA)(LPCSTR lhs, LPCSTR rhs);
209 typedef INT (CALLBACK *MRUStringCmpFnW)(LPCWSTR lhs, LPCWSTR rhs);
210 typedef INT (CALLBACK *MRUBinaryCmpFn)(LPCVOID lhs, LPCVOID rhs, DWORD length);
211 
212 typedef struct tagMRUINFOA
213 {
214     DWORD  cbSize;
215     UINT   uMax;
216     UINT   fFlags;
217     HKEY   hKey;
218     LPSTR  lpszSubKey;
219     union
220     {
221         MRUStringCmpFnA string_cmpfn;
222         MRUBinaryCmpFn  binary_cmpfn;
223     } u;
224 } MRUINFOA, *LPMRUINFOA;
225 
226 typedef struct tagMRUINFOW
227 {
228     DWORD   cbSize;
229     UINT    uMax;
230     UINT    fFlags;
231     HKEY    hKey;
232     LPWSTR  lpszSubKey;
233     union
234     {
235         MRUStringCmpFnW string_cmpfn;
236         MRUBinaryCmpFn  binary_cmpfn;
237     } u;
238 } MRUINFOW, *LPMRUINFOW;
239 
240 /* MRUINFO.fFlags */
241 #define MRU_STRING     0 /* list will contain strings */
242 #define MRU_BINARY     1 /* list will contain binary data */
243 #define MRU_CACHEWRITE 2 /* only save list order to reg. is FreeMRUList */
244 
245 /* If list is a string list lpfnCompare has the following prototype
246  * int CALLBACK MRUCompareString(LPCSTR s1, LPCSTR s2)
247  * for binary lists the prototype is
248  * int CALLBACK MRUCompareBinary(LPCVOID data1, LPCVOID data2, DWORD cbData)
249  * where cbData is the no. of bytes to compare.
250  * Need to check what return value means identical - 0?
251  */
252 
253 typedef struct tagWINEMRUITEM
254 {
255     DWORD          size;        /* size of data stored               */
256     DWORD          itemFlag;    /* flags                             */
257     BYTE           datastart;
258 } WINEMRUITEM, *LPWINEMRUITEM;
259 
260 /* itemFlag */
261 #define WMRUIF_CHANGED   0x0001 /* this dataitem changed             */
262 
263 typedef struct tagWINEMRULIST
264 {
265     MRUINFOW       extview;     /* original create information       */
266     BOOL           isUnicode;   /* is compare fn Unicode */
267     DWORD          wineFlags;   /* internal flags                    */
268     DWORD          cursize;     /* current size of realMRU           */
269     LPWSTR         realMRU;     /* pointer to string of index names  */
270     LPWINEMRUITEM  *array;      /* array of pointers to data         */
271                                 /* in 'a' to 'z' order               */
272 } WINEMRULIST, *LPWINEMRULIST;
273 
274 /* wineFlags */
275 #define WMRUF_CHANGED  0x0001   /* MRU list has changed              */
276 
277 /**************************************************************************
278  *              MRU_SaveChanged (internal)
279  *
280  * Local MRU saving code
281  */
282 static void MRU_SaveChanged ( LPWINEMRULIST mp )
283 {
284     UINT i, err;
285     HKEY newkey;
286     WCHAR realname[2];
287     LPWINEMRUITEM witem;
288 
289     /* or should we do the following instead of RegOpenKeyEx:
290      */
291 
292     /* open the sub key */
293     if ((err = RegOpenKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
294 			      0, KEY_WRITE, &newkey))) {
295 	/* not present - what to do ??? */
296 	ERR("Could not open key, error=%d, attempting to create\n",
297 	    err);
298 	if ((err = RegCreateKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
299 				    0,
300 				    NULL,
301 				    REG_OPTION_NON_VOLATILE,
302 				    KEY_READ | KEY_WRITE,
303 				    0,
304 				    &newkey,
305 				    0))) {
306 	    ERR("failed to create key /%s/, err=%d\n",
307 		debugstr_w(mp->extview.lpszSubKey), err);
308 	    return;
309 	}
310     }
311     if (mp->wineFlags & WMRUF_CHANGED) {
312 	mp->wineFlags &= ~WMRUF_CHANGED;
313 	err = RegSetValueExW(newkey, strMRUList, 0, REG_SZ, (LPBYTE)mp->realMRU,
314 			     (strlenW(mp->realMRU) + 1)*sizeof(WCHAR));
315 	if (err) {
316 	    ERR("error saving MRUList, err=%d\n", err);
317 	}
318 	TRACE("saving MRUList=/%s/\n", debugstr_w(mp->realMRU));
319     }
320     realname[1] = 0;
321     for(i=0; i<mp->cursize; i++) {
322 	witem = mp->array[i];
323 	if (witem->itemFlag & WMRUIF_CHANGED) {
324 	    witem->itemFlag &= ~WMRUIF_CHANGED;
325 	    realname[0] = 'a' + i;
326 	    err = RegSetValueExW(newkey, realname, 0,
327 				 (mp->extview.fFlags & MRU_BINARY) ?
328 				 REG_BINARY : REG_SZ,
329 				 &witem->datastart, witem->size);
330 	    if (err) {
331 		ERR("error saving /%s/, err=%d\n", debugstr_w(realname), err);
332 	    }
333             TRACE("saving value for name /%s/ size=%d\n",
334 		  debugstr_w(realname), witem->size);
335 	}
336     }
337     RegCloseKey( newkey );
338 }
339 
340 /**************************************************************************
341  *              FreeMRUList [COMCTL32.152]
342  *
343  * Frees a most-recently-used items list.
344  *
345  * PARAMS
346  *     hMRUList [I] Handle to list.
347  *
348  * RETURNS
349  *     Nothing.
350  */
351 void WINAPI FreeMRUList (HANDLE hMRUList)
352 {
353     LPWINEMRULIST mp = hMRUList;
354     UINT i;
355 
356     TRACE("(%p)\n", hMRUList);
357     if (!hMRUList)
358         return;
359 
360     if (mp->wineFlags & WMRUF_CHANGED) {
361 	/* need to open key and then save the info */
362 	MRU_SaveChanged( mp );
363     }
364 
365     for(i=0; i<mp->extview.uMax; i++)
366         Free(mp->array[i]);
367 
368     Free(mp->realMRU);
369     Free(mp->array);
370     Free(mp->extview.lpszSubKey);
371     Free(mp);
372 }
373 
374 
375 /**************************************************************************
376  *                  FindMRUData [COMCTL32.169]
377  *
378  * Searches binary list for item that matches lpData of length cbData.
379  * Returns position in list order 0 -> MRU and if lpRegNum != NULL then value
380  * corresponding to item's reg. name will be stored in it ('a' -> 0).
381  *
382  * PARAMS
383  *    hList [I] list handle
384  *    lpData [I] data to find
385  *    cbData [I] length of data
386  *    lpRegNum [O] position in registry (maybe NULL)
387  *
388  * RETURNS
389  *    Position in list 0 -> MRU.  -1 if item not found.
390  */
391 INT WINAPI FindMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData,
392                         LPINT lpRegNum)
393 {
394     const WINEMRULIST *mp = hList;
395     INT ret;
396     UINT i;
397     LPSTR dataA = NULL;
398 
399     if (!mp || !mp->extview.u.string_cmpfn)
400 	return -1;
401 
402     if(!(mp->extview.fFlags & MRU_BINARY) && !mp->isUnicode) {
403         DWORD len = WideCharToMultiByte(CP_ACP, 0, lpData, -1,
404 					NULL, 0, NULL, NULL);
405 	dataA = Alloc(len);
406 	WideCharToMultiByte(CP_ACP, 0, lpData, -1, dataA, len, NULL, NULL);
407     }
408 
409     for(i=0; i<mp->cursize; i++) {
410 	if (mp->extview.fFlags & MRU_BINARY) {
411 	    if (!mp->extview.u.binary_cmpfn(lpData, &mp->array[i]->datastart, cbData))
412 		break;
413 	}
414 	else {
415 	    if(mp->isUnicode) {
416 	        if (!mp->extview.u.string_cmpfn(lpData, (LPWSTR)&mp->array[i]->datastart))
417 		    break;
418 	    } else {
419 	        DWORD len = WideCharToMultiByte(CP_ACP, 0,
420 						(LPWSTR)&mp->array[i]->datastart, -1,
421 						NULL, 0, NULL, NULL);
422 		LPSTR itemA = Alloc(len);
423 		INT cmp;
424 		WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&mp->array[i]->datastart, -1,
425 				    itemA, len, NULL, NULL);
426 
427 	        cmp = mp->extview.u.string_cmpfn((LPWSTR)dataA, (LPWSTR)itemA);
428 		Free(itemA);
429 		if(!cmp)
430 		    break;
431 	    }
432 	}
433     }
434     Free(dataA);
435     if (i < mp->cursize)
436 	ret = i;
437     else
438 	ret = -1;
439     if (lpRegNum && (ret != -1))
440 	*lpRegNum = 'a' + i;
441 
442     TRACE("(%p, %p, %d, %p) returning %d\n",
443 	   hList, lpData, cbData, lpRegNum, ret);
444 
445     return ret;
446 }
447 
448 
449 /**************************************************************************
450  *              AddMRUData [COMCTL32.167]
451  *
452  * Add item to MRU binary list.  If item already exists in list then it is
453  * simply moved up to the top of the list and not added again.  If list is
454  * full then the least recently used item is removed to make room.
455  *
456  * PARAMS
457  *     hList [I] Handle to list.
458  *     lpData [I] ptr to data to add.
459  *     cbData [I] no. of bytes of data.
460  *
461  * RETURNS
462  *     No. corresponding to registry name where value is stored 'a' -> 0 etc.
463  *     -1 on error.
464  */
465 INT WINAPI AddMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData)
466 {
467     LPWINEMRULIST mp = hList;
468     LPWINEMRUITEM witem;
469     INT i, replace;
470 
471     if ((replace = FindMRUData (hList, lpData, cbData, NULL)) >= 0) {
472         /* Item exists, just move it to the front */
473         LPWSTR pos = strchrW(mp->realMRU, replace + 'a');
474         while (pos > mp->realMRU)
475         {
476             pos[0] = pos[-1];
477             pos--;
478         }
479     }
480     else {
481 	/* either add a new entry or replace oldest */
482 	if (mp->cursize < mp->extview.uMax) {
483 	    /* Add in a new item */
484 	    replace = mp->cursize;
485 	    mp->cursize++;
486 	}
487 	else {
488 	    /* get the oldest entry and replace data */
489 	    replace = mp->realMRU[mp->cursize - 1] - 'a';
490 	    Free(mp->array[replace]);
491 	}
492 
493         /* Allocate space for new item and move in the data */
494         mp->array[replace] = witem = Alloc(cbData + sizeof(WINEMRUITEM));
495         witem->itemFlag |= WMRUIF_CHANGED;
496         witem->size = cbData;
497         memcpy( &witem->datastart, lpData, cbData);
498 
499         /* now rotate MRU list */
500         for(i=mp->cursize-1; i>=1; i--)
501             mp->realMRU[i] = mp->realMRU[i-1];
502     }
503 
504     /* The new item gets the front spot */
505     mp->wineFlags |= WMRUF_CHANGED;
506     mp->realMRU[0] = replace + 'a';
507 
508     TRACE("(%p, %p, %d) adding data, /%c/ now most current\n",
509           hList, lpData, cbData, replace+'a');
510 
511     if (!(mp->extview.fFlags & MRU_CACHEWRITE)) {
512 	/* save changed stuff right now */
513 	MRU_SaveChanged( mp );
514     }
515 
516     return replace;
517 }
518 
519 /**************************************************************************
520  *              AddMRUStringW [COMCTL32.401]
521  *
522  * Add an item to an MRU string list.
523  *
524  * PARAMS
525  *     hList      [I] Handle to list.
526  *     lpszString [I] The string to add.
527  *
528  * RETURNS
529  *   Success: The number corresponding to the registry name where the string
530  *            has been stored (0 maps to 'a', 1 to 'b' and so on).
531  *   Failure: -1, if hList is NULL or memory allocation fails. If lpszString
532  *            is invalid, the function returns 0, and GetLastError() returns
533  *            ERROR_INVALID_PARAMETER. The last error value is set only in
534  *            this case.
535  *
536  * NOTES
537  *  -If lpszString exists in the list already, it is moved to the top of the
538  *   MRU list (it is not duplicated).
539  *  -If the list is full the least recently used list entry is replaced with
540  *   lpszString.
541  *  -If this function returns 0 you should check the last error value to
542  *   ensure the call really succeeded.
543  */
544 INT WINAPI AddMRUStringW(HANDLE hList, LPCWSTR lpszString)
545 {
546     TRACE("(%p,%s)\n", hList, debugstr_w(lpszString));
547 
548     if (!hList)
549         return -1;
550 
551     if (!lpszString || IsBadStringPtrW(lpszString, -1))
552     {
553         SetLastError(ERROR_INVALID_PARAMETER);
554         return 0;
555     }
556 
557     return AddMRUData(hList, lpszString,
558                       (strlenW(lpszString) + 1) * sizeof(WCHAR));
559 }
560 
561 /**************************************************************************
562  *              AddMRUStringA [COMCTL32.153]
563  *
564  * See AddMRUStringW.
565  */
566 INT WINAPI AddMRUStringA(HANDLE hList, LPCSTR lpszString)
567 {
568     DWORD len;
569     LPWSTR stringW;
570     INT ret;
571 
572     TRACE("(%p,%s)\n", hList, debugstr_a(lpszString));
573 
574     if (!hList)
575         return -1;
576 
577     if (IsBadStringPtrA(lpszString, -1))
578     {
579         SetLastError(ERROR_INVALID_PARAMETER);
580 	return 0;
581     }
582 
583     len = MultiByteToWideChar(CP_ACP, 0, lpszString, -1, NULL, 0) * sizeof(WCHAR);
584     stringW = Alloc(len);
585     if (!stringW)
586         return -1;
587 
588     MultiByteToWideChar(CP_ACP, 0, lpszString, -1, stringW, len/sizeof(WCHAR));
589     ret = AddMRUData(hList, stringW, len);
590     Free(stringW);
591     return ret;
592 }
593 
594 /**************************************************************************
595  *              DelMRUString [COMCTL32.156]
596  *
597  * Removes item from either string or binary list (despite its name)
598  *
599  * PARAMS
600  *    hList [I] list handle
601  *    nItemPos [I] item position to remove 0 -> MRU
602  *
603  * RETURNS
604  *    TRUE if successful, FALSE if nItemPos is out of range.
605  */
606 BOOL WINAPI DelMRUString(HANDLE hList, INT nItemPos)
607 {
608     FIXME("(%p, %d): stub\n", hList, nItemPos);
609     return TRUE;
610 }
611 
612 /**************************************************************************
613  *                  FindMRUStringW [COMCTL32.402]
614  *
615  * See FindMRUStringA.
616  */
617 INT WINAPI FindMRUStringW (HANDLE hList, LPCWSTR lpszString, LPINT lpRegNum)
618 {
619   return FindMRUData(hList, lpszString,
620                      (lstrlenW(lpszString) + 1) * sizeof(WCHAR), lpRegNum);
621 }
622 
623 /**************************************************************************
624  *                  FindMRUStringA [COMCTL32.155]
625  *
626  * Searches string list for item that matches lpszString.
627  * Returns position in list order 0 -> MRU and if lpRegNum != NULL then value
628  * corresponding to item's reg. name will be stored in it ('a' -> 0).
629  *
630  * PARAMS
631  *    hList [I] list handle
632  *    lpszString [I] string to find
633  *    lpRegNum [O] position in registry (maybe NULL)
634  *
635  * RETURNS
636  *    Position in list 0 -> MRU.  -1 if item not found.
637  */
638 INT WINAPI FindMRUStringA (HANDLE hList, LPCSTR lpszString, LPINT lpRegNum)
639 {
640     DWORD len = MultiByteToWideChar(CP_ACP, 0, lpszString, -1, NULL, 0);
641     LPWSTR stringW = Alloc(len * sizeof(WCHAR));
642     INT ret;
643 
644     MultiByteToWideChar(CP_ACP, 0, lpszString, -1, stringW, len);
645     ret = FindMRUData(hList, stringW, len * sizeof(WCHAR), lpRegNum);
646     Free(stringW);
647     return ret;
648 }
649 
650 /*************************************************************************
651  *                 create_mru_list (internal)
652  */
653 static HANDLE create_mru_list(LPWINEMRULIST mp)
654 {
655     UINT i, err;
656     HKEY newkey;
657     DWORD datasize, dwdisp;
658     WCHAR realname[2];
659     LPWINEMRUITEM witem;
660     DWORD type;
661 
662     /* get space to save indices that will turn into names
663      * but in order of most to least recently used
664      */
665     mp->realMRU = Alloc((mp->extview.uMax + 2) * sizeof(WCHAR));
666 
667     /* get space to save pointers to actual data in order of
668      * 'a' to 'z' (0 to n).
669      */
670     mp->array = Alloc(mp->extview.uMax * sizeof(LPVOID));
671 
672     /* open the sub key */
673     if ((err = RegCreateKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
674 			        0,
675 				NULL,
676 				REG_OPTION_NON_VOLATILE,
677 				KEY_READ | KEY_WRITE,
678                                 0,
679 				&newkey,
680 				&dwdisp))) {
681 	/* error - what to do ??? */
682 	ERR("(%u %u %x %p %s %p): Could not open key, error=%d\n",
683 	    mp->extview.cbSize, mp->extview.uMax, mp->extview.fFlags,
684 	    mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey),
685             mp->extview.u.string_cmpfn, err);
686 	return 0;
687     }
688 
689     /* get values from key 'MRUList' */
690     if (newkey) {
691 	datasize = (mp->extview.uMax + 1) * sizeof(WCHAR);
692 	if (RegQueryValueExW( newkey, strMRUList, 0, &type,
693 				  (LPBYTE)mp->realMRU, &datasize)) {
694 	    /* not present - set size to 1 (will become 0 later) */
695 	    datasize = 1;
696 	    *mp->realMRU = 0;
697 	}
698         else
699             datasize /= sizeof(WCHAR);
700 
701 	TRACE("MRU list = %s, datasize = %d\n", debugstr_w(mp->realMRU), datasize);
702 
703 	mp->cursize = datasize - 1;
704 	/* datasize now has number of items in the MRUList */
705 
706 	/* get actual values for each entry */
707 	realname[1] = 0;
708 	for(i=0; i<mp->cursize; i++) {
709 	    realname[0] = 'a' + i;
710 	    if(RegQueryValueExW( newkey, realname, 0, &type, 0, &datasize)) {
711 		/* not present - what to do ??? */
712 		ERR("Key %s not found 1\n", debugstr_w(realname));
713 	    }
714 	    mp->array[i] = witem = Alloc(datasize + sizeof(WINEMRUITEM));
715 	    witem->size = datasize;
716 	    if(RegQueryValueExW( newkey, realname, 0, &type,
717 				 &witem->datastart, &datasize)) {
718 		/* not present - what to do ??? */
719 		ERR("Key %s not found 2\n", debugstr_w(realname));
720 	    }
721 	}
722 	RegCloseKey( newkey );
723     }
724     else
725 	mp->cursize = 0;
726 
727     TRACE("(%u %u %x %p %s %p): Current Size = %d\n",
728 	  mp->extview.cbSize, mp->extview.uMax, mp->extview.fFlags,
729 	  mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey),
730 	  mp->extview.u.string_cmpfn, mp->cursize);
731     return mp;
732 }
733 
734 /**************************************************************************
735  *                  CreateMRUListLazyW [COMCTL32.404]
736  *
737  * See CreateMRUListLazyA.
738  */
739 HANDLE WINAPI CreateMRUListLazyW (const MRUINFOW *infoW, DWORD dwParam2,
740                                   DWORD dwParam3, DWORD dwParam4)
741 {
742     LPWINEMRULIST mp;
743 
744     /* Native does not check for a NULL lpcml */
745     if (!infoW->hKey || IsBadStringPtrW(infoW->lpszSubKey, -1))
746 	return NULL;
747 
748     mp = Alloc(sizeof(WINEMRULIST));
749     memcpy(&mp->extview, infoW, sizeof(MRUINFOW));
750     mp->extview.lpszSubKey = Alloc((strlenW(infoW->lpszSubKey) + 1) * sizeof(WCHAR));
751     strcpyW(mp->extview.lpszSubKey, infoW->lpszSubKey);
752     mp->isUnicode = TRUE;
753 
754     return create_mru_list(mp);
755 }
756 
757 /**************************************************************************
758  *                  CreateMRUListLazyA [COMCTL32.157]
759  *
760  * Creates a most-recently-used list.
761  *
762  * PARAMS
763  *     lpcml    [I] ptr to CREATEMRULIST structure.
764  *     dwParam2 [I] Unknown
765  *     dwParam3 [I] Unknown
766  *     dwParam4 [I] Unknown
767  *
768  * RETURNS
769  *     Handle to MRU list.
770  */
771 HANDLE WINAPI CreateMRUListLazyA (const MRUINFOA *lpcml, DWORD dwParam2,
772                                   DWORD dwParam3, DWORD dwParam4)
773 {
774     LPWINEMRULIST mp;
775     DWORD len;
776 
777     /* Native does not check for a NULL lpcml */
778 
779     if (!lpcml->hKey || IsBadStringPtrA(lpcml->lpszSubKey, -1))
780 	return 0;
781 
782     mp = Alloc(sizeof(WINEMRULIST));
783     memcpy(&mp->extview, lpcml, sizeof(MRUINFOA));
784     len = MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1, NULL, 0);
785     mp->extview.lpszSubKey = Alloc(len * sizeof(WCHAR));
786     MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1,
787 			mp->extview.lpszSubKey, len);
788     mp->isUnicode = FALSE;
789     return create_mru_list(mp);
790 }
791 
792 /**************************************************************************
793  *              CreateMRUListW [COMCTL32.400]
794  *
795  * See CreateMRUListA.
796  */
797 HANDLE WINAPI CreateMRUListW (const MRUINFOW *infoW)
798 {
799     return CreateMRUListLazyW(infoW, 0, 0, 0);
800 }
801 
802 /**************************************************************************
803  *              CreateMRUListA [COMCTL32.151]
804  *
805  * Creates a most-recently-used list.
806  *
807  * PARAMS
808  *     lpcml [I] ptr to CREATEMRULIST structure.
809  *
810  * RETURNS
811  *     Handle to MRU list.
812  */
813 HANDLE WINAPI CreateMRUListA (const MRUINFOA *lpcml)
814 {
815      return CreateMRUListLazyA (lpcml, 0, 0, 0);
816 }
817 
818 
819 /**************************************************************************
820  *                EnumMRUListW [COMCTL32.403]
821  *
822  * Enumerate item in a most-recently-used list
823  *
824  * PARAMS
825  *    hList [I] list handle
826  *    nItemPos [I] item position to enumerate
827  *    lpBuffer [O] buffer to receive item
828  *    nBufferSize [I] size of buffer
829  *
830  * RETURNS
831  *    For binary lists specifies how many bytes were copied to buffer, for
832  *    string lists specifies full length of string.  Enumerating past the end
833  *    of list returns -1.
834  *    If lpBuffer == NULL or nItemPos is -ve return value is no. of items in
835  *    the list.
836  */
837 INT WINAPI EnumMRUListW (HANDLE hList, INT nItemPos, LPVOID lpBuffer,
838                          DWORD nBufferSize)
839 {
840     const WINEMRULIST *mp = hList;
841     const WINEMRUITEM *witem;
842     INT desired, datasize;
843 
844     if (!mp) return -1;
845     if ((nItemPos < 0) || !lpBuffer) return mp->cursize;
846     if (nItemPos >= mp->cursize) return -1;
847     desired = mp->realMRU[nItemPos];
848     desired -= 'a';
849     TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired);
850     witem = mp->array[desired];
851     datasize = min( witem->size, nBufferSize );
852     memcpy( lpBuffer, &witem->datastart, datasize);
853     TRACE("(%p, %d, %p, %d): returning len=%d\n",
854 	  hList, nItemPos, lpBuffer, nBufferSize, datasize);
855     return datasize;
856 }
857 
858 /**************************************************************************
859  *                EnumMRUListA [COMCTL32.154]
860  *
861  * See EnumMRUListW.
862  */
863 INT WINAPI EnumMRUListA (HANDLE hList, INT nItemPos, LPVOID lpBuffer,
864                          DWORD nBufferSize)
865 {
866     const WINEMRULIST *mp = hList;
867     LPWINEMRUITEM witem;
868     INT desired, datasize;
869     DWORD lenA;
870 
871     if (!mp) return -1;
872     if ((nItemPos < 0) || !lpBuffer) return mp->cursize;
873     if (nItemPos >= mp->cursize) return -1;
874     desired = mp->realMRU[nItemPos];
875     desired -= 'a';
876     TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired);
877     witem = mp->array[desired];
878     if(mp->extview.fFlags & MRU_BINARY) {
879         datasize = min( witem->size, nBufferSize );
880 	memcpy( lpBuffer, &witem->datastart, datasize);
881     } else {
882         lenA = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1,
883 				   NULL, 0, NULL, NULL);
884 	datasize = min( lenA, nBufferSize );
885 	WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1,
886 			    lpBuffer, datasize, NULL, NULL);
887         ((char *)lpBuffer)[ datasize - 1 ] = '\0';
888         datasize = lenA - 1;
889     }
890     TRACE("(%p, %d, %p, %d): returning len=%d\n",
891 	  hList, nItemPos, lpBuffer, nBufferSize, datasize);
892     return datasize;
893 }
894 
895 /**************************************************************************
896  * Str_GetPtrWtoA [internal]
897  *
898  * Converts a unicode string into a multi byte string
899  *
900  * PARAMS
901  *     lpSrc   [I] Pointer to the unicode source string
902  *     lpDest  [O] Pointer to caller supplied storage for the multi byte string
903  *     nMaxLen [I] Size, in bytes, of the destination buffer
904  *
905  * RETURNS
906  *     Length, in bytes, of the converted string.
907  */
908 
909 INT Str_GetPtrWtoA (LPCWSTR lpSrc, LPSTR lpDest, INT nMaxLen)
910 {
911     INT len;
912 
913     TRACE("(%s %p %d)\n", debugstr_w(lpSrc), lpDest, nMaxLen);
914 
915     if (!lpDest && lpSrc)
916 	return WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, 0, 0, NULL, NULL);
917 
918     if (nMaxLen == 0)
919 	return 0;
920 
921     if (lpSrc == NULL) {
922 	lpDest[0] = '\0';
923 	return 0;
924     }
925 
926     len = WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, 0, 0, NULL, NULL);
927     if (len >= nMaxLen)
928 	len = nMaxLen - 1;
929 
930     WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, lpDest, len, NULL, NULL);
931     lpDest[len] = '\0';
932 
933     return len;
934 }
935 
936 /**************************************************************************
937  * Str_GetPtrAtoW [internal]
938  *
939  * Converts a multibyte string into a unicode string
940  *
941  * PARAMS
942  *     lpSrc   [I] Pointer to the multibyte source string
943  *     lpDest  [O] Pointer to caller supplied storage for the unicode string
944  *     nMaxLen [I] Size, in characters, of the destination buffer
945  *
946  * RETURNS
947  *     Length, in characters, of the converted string.
948  */
949 
950 INT Str_GetPtrAtoW (LPCSTR lpSrc, LPWSTR lpDest, INT nMaxLen)
951 {
952     INT len;
953 
954     TRACE("(%s %p %d)\n", debugstr_a(lpSrc), lpDest, nMaxLen);
955 
956     if (!lpDest && lpSrc)
957 	return MultiByteToWideChar(CP_ACP, 0, lpSrc, -1, 0, 0);
958 
959     if (nMaxLen == 0)
960 	return 0;
961 
962     if (lpSrc == NULL) {
963 	lpDest[0] = '\0';
964 	return 0;
965     }
966 
967     len = MultiByteToWideChar(CP_ACP, 0, lpSrc, -1, 0, 0);
968     if (len >= nMaxLen)
969 	len = nMaxLen - 1;
970 
971     MultiByteToWideChar(CP_ACP, 0, lpSrc, -1, lpDest, len);
972     lpDest[len] = '\0';
973 
974     return len;
975 }
976 
977 
978 /**************************************************************************
979  * Str_SetPtrAtoW [internal]
980  *
981  * Converts a multi byte string to a unicode string.
982  * If the pointer to the destination buffer is NULL a buffer is allocated.
983  * If the destination buffer is too small to keep the converted multi byte
984  * string the destination buffer is reallocated. If the source pointer is
985  * NULL, the destination buffer is freed.
986  *
987  * PARAMS
988  *     lppDest [I/O] pointer to a pointer to the destination buffer
989  *     lpSrc   [I] pointer to a multi byte string
990  *
991  * RETURNS
992  *     TRUE: conversion successful
993  *     FALSE: error
994  */
995 BOOL Str_SetPtrAtoW (LPWSTR *lppDest, LPCSTR lpSrc)
996 {
997     TRACE("(%p %s)\n", lppDest, lpSrc);
998 
999     if (lpSrc) {
1000 	INT len = MultiByteToWideChar(CP_ACP,0,lpSrc,-1,NULL,0);
1001 	LPWSTR ptr = ReAlloc (*lppDest, len*sizeof(WCHAR));
1002 
1003 	if (!ptr)
1004 	    return FALSE;
1005 	MultiByteToWideChar(CP_ACP,0,lpSrc,-1,ptr,len);
1006 	*lppDest = ptr;
1007     }
1008     else {
1009         Free (*lppDest);
1010         *lppDest = NULL;
1011     }
1012 
1013     return TRUE;
1014 }
1015 
1016 /**************************************************************************
1017  * Str_SetPtrWtoA [internal]
1018  *
1019  * Converts a unicode string to a multi byte string.
1020  * If the pointer to the destination buffer is NULL a buffer is allocated.
1021  * If the destination buffer is too small to keep the converted wide
1022  * string the destination buffer is reallocated. If the source pointer is
1023  * NULL, the destination buffer is freed.
1024  *
1025  * PARAMS
1026  *     lppDest [I/O] pointer to a pointer to the destination buffer
1027  *     lpSrc   [I] pointer to a wide string
1028  *
1029  * RETURNS
1030  *     TRUE: conversion successful
1031  *     FALSE: error
1032  */
1033 BOOL Str_SetPtrWtoA (LPSTR *lppDest, LPCWSTR lpSrc)
1034 {
1035     TRACE("(%p %s)\n", lppDest, debugstr_w(lpSrc));
1036 
1037     if (lpSrc) {
1038         INT len = WideCharToMultiByte(CP_ACP,0,lpSrc,-1,NULL,0,NULL,FALSE);
1039         LPSTR ptr = ReAlloc (*lppDest, len*sizeof(CHAR));
1040 
1041         if (!ptr)
1042             return FALSE;
1043         WideCharToMultiByte(CP_ACP,0,lpSrc,-1,ptr,len,NULL,FALSE);
1044         *lppDest = ptr;
1045     }
1046     else {
1047         Free (*lppDest);
1048         *lppDest = NULL;
1049     }
1050 
1051     return TRUE;
1052 }
1053 
1054 
1055 /**************************************************************************
1056  * Notification functions
1057  */
1058 
1059 typedef struct tagNOTIFYDATA
1060 {
1061     HWND hwndFrom;
1062     HWND hwndTo;
1063     DWORD  dwParam3;
1064     DWORD  dwParam4;
1065     DWORD  dwParam5;
1066     DWORD  dwParam6;
1067 } NOTIFYDATA, *LPNOTIFYDATA;
1068 
1069 
1070 /**************************************************************************
1071  * DoNotify [Internal]
1072  */
1073 
1074 static LRESULT DoNotify (const NOTIFYDATA *lpNotify, UINT uCode, LPNMHDR lpHdr)
1075 {
1076     NMHDR nmhdr;
1077     LPNMHDR lpNmh = NULL;
1078     UINT idFrom = 0;
1079 
1080     TRACE("(%p %p %d %p 0x%08x)\n",
1081 	   lpNotify->hwndFrom, lpNotify->hwndTo, uCode, lpHdr,
1082 	   lpNotify->dwParam5);
1083 
1084     if (!lpNotify->hwndTo)
1085 	return 0;
1086 
1087     if (lpNotify->hwndFrom == (HWND)-1) {
1088 	lpNmh = lpHdr;
1089 	idFrom = lpHdr->idFrom;
1090     }
1091     else {
1092 	if (lpNotify->hwndFrom)
1093 	    idFrom = GetDlgCtrlID (lpNotify->hwndFrom);
1094 
1095 	lpNmh = (lpHdr) ? lpHdr : &nmhdr;
1096 
1097 	lpNmh->hwndFrom = lpNotify->hwndFrom;
1098 	lpNmh->idFrom = idFrom;
1099 	lpNmh->code = uCode;
1100     }
1101 
1102     return SendMessageW (lpNotify->hwndTo, WM_NOTIFY, idFrom, (LPARAM)lpNmh);
1103 }
1104 
1105 
1106 /**************************************************************************
1107  * SendNotify [COMCTL32.341]
1108  *
1109  * Sends a WM_NOTIFY message to the specified window.
1110  *
1111  * PARAMS
1112  *     hwndTo   [I] Window to receive the message
1113  *     hwndFrom [I] Window that the message is from (see notes)
1114  *     uCode    [I] Notification code
1115  *     lpHdr    [I] The NMHDR and any additional information to send or NULL
1116  *
1117  * RETURNS
1118  *     Success: return value from notification
1119  *     Failure: 0
1120  *
1121  * NOTES
1122  *     If hwndFrom is -1 then the identifier of the control sending the
1123  *     message is taken from the NMHDR structure.
1124  *     If hwndFrom is not -1 then lpHdr can be NULL.
1125  */
1126 LRESULT WINAPI SendNotify (HWND hwndTo, HWND hwndFrom, UINT uCode, LPNMHDR lpHdr)
1127 {
1128     NOTIFYDATA notify;
1129 
1130     TRACE("(%p %p %d %p)\n",
1131 	   hwndTo, hwndFrom, uCode, lpHdr);
1132 
1133     notify.hwndFrom = hwndFrom;
1134     notify.hwndTo   = hwndTo;
1135     notify.dwParam5 = 0;
1136     notify.dwParam6 = 0;
1137 
1138     return DoNotify (&notify, uCode, lpHdr);
1139 }
1140 
1141 
1142 /**************************************************************************
1143  * SendNotifyEx [COMCTL32.342]
1144  *
1145  * Sends a WM_NOTIFY message to the specified window.
1146  *
1147  * PARAMS
1148  *     hwndFrom [I] Window to receive the message
1149  *     hwndTo   [I] Window that the message is from
1150  *     uCode    [I] Notification code
1151  *     lpHdr    [I] The NMHDR and any additional information to send or NULL
1152  *     dwParam5 [I] Unknown
1153  *
1154  * RETURNS
1155  *     Success: return value from notification
1156  *     Failure: 0
1157  *
1158  * NOTES
1159  *     If hwndFrom is -1 then the identifier of the control sending the
1160  *     message is taken from the NMHDR structure.
1161  *     If hwndFrom is not -1 then lpHdr can be NULL.
1162  */
1163 LRESULT WINAPI SendNotifyEx (HWND hwndTo, HWND hwndFrom, UINT uCode,
1164                              LPNMHDR lpHdr, DWORD dwParam5)
1165 {
1166     NOTIFYDATA notify;
1167     HWND hwndNotify;
1168 
1169     TRACE("(%p %p %d %p 0x%08x)\n",
1170 	   hwndFrom, hwndTo, uCode, lpHdr, dwParam5);
1171 
1172     hwndNotify = hwndTo;
1173     if (!hwndTo) {
1174 	if (IsWindow (hwndFrom)) {
1175 	    hwndNotify = GetParent (hwndFrom);
1176 	    if (!hwndNotify)
1177 		return 0;
1178 	}
1179     }
1180 
1181     notify.hwndFrom = hwndFrom;
1182     notify.hwndTo   = hwndNotify;
1183     notify.dwParam5 = dwParam5;
1184     notify.dwParam6 = 0;
1185 
1186     return DoNotify (&notify, uCode, lpHdr);
1187 }
1188