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