Fix indentation for consistency with the rest of the file.
[wine] / dlls / comctl32 / comctl32undoc.c
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 #define NONAMELESSSTRUCT
40
41 #include "windef.h"
42 #include "winbase.h"
43 #include "wingdi.h"
44 #include "winuser.h"
45 #include "winnls.h"
46 #include "winreg.h"
47 #include "commctrl.h"
48 #include "objbase.h"
49 #include "winerror.h"
50
51 #include "wine/unicode.h"
52 #include "comctl32.h"
53
54 #include "wine/debug.h"
55
56 WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
57
58 struct _DSA
59 {
60     INT  nItemCount;
61     LPVOID pData;
62     INT  nMaxCount;
63     INT  nItemSize;
64     INT  nGrow;
65 };
66
67 struct _DPA
68 {
69     INT    nItemCount;
70     LPVOID   *ptrs;
71     HANDLE hHeap;
72     INT    nGrow;
73     INT    nMaxCount;
74 };
75
76 typedef struct _STREAMDATA
77 {
78     DWORD dwSize;
79     DWORD dwData2;
80     DWORD dwItems;
81 } STREAMDATA, *PSTREAMDATA;
82
83 typedef struct _LOADDATA
84 {
85     INT   nCount;
86     PVOID ptr;
87 } LOADDATA, *LPLOADDATA;
88
89 typedef HRESULT (CALLBACK *DPALOADPROC)(LPLOADDATA,IStream*,LPARAM);
90
91 /**************************************************************************
92  * DPA_LoadStream [COMCTL32.9]
93  *
94  * Loads a dynamic pointer array from a stream
95  *
96  * PARAMS
97  *     phDpa    [O] pointer to a handle to a dynamic pointer array
98  *     loadProc [I] pointer to a callback function
99  *     pStream  [I] pointer to a stream
100  *     lParam   [I] application specific value
101  *
102  * RETURNS
103  *     Success: TRUE
104  *     Failure: FALSE 
105  *
106  * NOTES
107  *     No more information available yet!
108  */
109 HRESULT WINAPI DPA_LoadStream (HDPA *phDpa, DPALOADPROC loadProc,
110                                IStream *pStream, LPARAM lParam)
111 {
112     HRESULT errCode;
113     LARGE_INTEGER position;
114     ULARGE_INTEGER newPosition;
115     STREAMDATA  streamData;
116     LOADDATA loadData;
117     ULONG ulRead;
118     HDPA hDpa;
119     PVOID *ptr;
120
121     FIXME ("phDpa=%p loadProc=%p pStream=%p lParam=%lx\n",
122            phDpa, loadProc, pStream, lParam);
123
124     if (!phDpa || !loadProc || !pStream)
125         return E_INVALIDARG;
126
127     *phDpa = (HDPA)NULL;
128
129     position.QuadPart = 0;
130
131     /*
132      * Zero out our streamData
133      */
134     memset(&streamData,0,sizeof(STREAMDATA));
135
136     errCode = IStream_Seek (pStream, position, STREAM_SEEK_CUR, &newPosition);
137     if (errCode != S_OK)
138         return errCode;
139
140     errCode = IStream_Read (pStream, &streamData, sizeof(STREAMDATA), &ulRead);
141     if (errCode != S_OK)
142         return errCode;
143
144     FIXME ("dwSize=%lu dwData2=%lu dwItems=%lu\n",
145            streamData.dwSize, streamData.dwData2, streamData.dwItems);
146
147     if ( ulRead < sizeof(STREAMDATA) ||
148     lParam < sizeof(STREAMDATA) ||
149         streamData.dwSize < sizeof(STREAMDATA) ||
150         streamData.dwData2 < 1) {
151         errCode = E_FAIL;
152     }
153
154     if (streamData.dwItems > (UINT_MAX / 2 / sizeof(VOID*))) /* 536870911 */
155         return E_OUTOFMEMORY;
156
157     /* create the dpa */
158     hDpa = DPA_Create (streamData.dwItems);
159     if (!hDpa)
160         return E_OUTOFMEMORY;
161
162     if (!DPA_Grow (hDpa, streamData.dwItems))
163         return E_OUTOFMEMORY;
164
165     /* load data from the stream into the dpa */
166     ptr = hDpa->ptrs;
167     for (loadData.nCount = 0; loadData.nCount < streamData.dwItems; loadData.nCount++) {
168         errCode = (loadProc)(&loadData, pStream, lParam);
169         if (errCode != S_OK) {
170             errCode = S_FALSE;
171             break;
172         }
173
174         *ptr = loadData.ptr;
175         ptr++;
176     }
177
178     /* set the number of items */
179     hDpa->nItemCount = loadData.nCount;
180
181     /* store the handle to the dpa */
182     *phDpa = hDpa;
183     FIXME ("new hDpa=%p, errorcode=%lx\n", hDpa, errCode);
184
185     return errCode;
186 }
187
188
189 /**************************************************************************
190  * DPA_SaveStream [COMCTL32.10]
191  *
192  * Saves a dynamic pointer array to a stream
193  *
194  * PARAMS
195  *     hDpa     [I] handle to a dynamic pointer array
196  *     loadProc [I] pointer to a callback function
197  *     pStream  [I] pointer to a stream
198  *     lParam   [I] application specific value
199  *
200  * RETURNS
201  *     Success: TRUE
202  *     Failure: FALSE 
203  *
204  * NOTES
205  *     No more information available yet!
206  */
207 HRESULT WINAPI DPA_SaveStream (const HDPA hDpa, DPALOADPROC loadProc,
208                                IStream *pStream, LPARAM lParam)
209 {
210
211     FIXME ("hDpa=%p loadProc=%p pStream=%p lParam=%lx\n",
212            hDpa, loadProc, pStream, lParam);
213
214     return E_FAIL;
215 }
216
217
218 /**************************************************************************
219  * DPA_Merge [COMCTL32.11]
220  *
221  * Merge two dynamic pointers arrays.
222  *
223  * PARAMS
224  *     hdpa1       [I] handle to a dynamic pointer array
225  *     hdpa2       [I] handle to a dynamic pointer array
226  *     dwFlags     [I] flags
227  *     pfnCompare  [I] pointer to sort function
228  *     pfnMerge    [I] pointer to merge function
229  *     lParam      [I] application specific value
230  *
231  * RETURNS
232  *     Success: TRUE
233  *     Failure: FALSE 
234  *
235  * NOTES
236  *     No more information available yet!
237  */
238 BOOL WINAPI DPA_Merge (const HDPA hdpa1, const HDPA hdpa2, DWORD dwFlags,
239                        PFNDPACOMPARE pfnCompare, PFNDPAMERGE pfnMerge,
240                        LPARAM lParam)
241 {
242     INT nCount;
243     LPVOID *pWork1, *pWork2;
244     INT nResult, i;
245     INT nIndex;
246
247     TRACE("%p %p %08lx %p %p %08lx)\n",
248            hdpa1, hdpa2, dwFlags, pfnCompare, pfnMerge, lParam);
249
250     if (IsBadWritePtr (hdpa1, sizeof(*hdpa1)))
251         return FALSE;
252
253     if (IsBadWritePtr (hdpa2, sizeof(*hdpa2)))
254         return FALSE;
255
256     if (IsBadCodePtr ((FARPROC)pfnCompare))
257         return FALSE;
258
259     if (IsBadCodePtr ((FARPROC)pfnMerge))
260         return FALSE;
261
262     if (!(dwFlags & DPAM_NOSORT)) {
263         TRACE("sorting dpa's!\n");
264         if (hdpa1->nItemCount > 0)
265         DPA_Sort (hdpa1, pfnCompare, lParam);
266         TRACE ("dpa 1 sorted!\n");
267         if (hdpa2->nItemCount > 0)
268         DPA_Sort (hdpa2, pfnCompare, lParam);
269         TRACE ("dpa 2 sorted!\n");
270     }
271
272     if (hdpa2->nItemCount < 1)
273         return TRUE;
274
275     TRACE("hdpa1->nItemCount=%d hdpa2->nItemCount=%d\n",
276            hdpa1->nItemCount, hdpa2->nItemCount);
277
278
279     /* working but untrusted implementation */
280
281     pWork1 = &(hdpa1->ptrs[hdpa1->nItemCount - 1]);
282     pWork2 = &(hdpa2->ptrs[hdpa2->nItemCount - 1]);
283
284     nIndex = hdpa1->nItemCount - 1;
285     nCount = hdpa2->nItemCount - 1;
286
287     do
288     {
289         if (nIndex < 0) {
290             if ((nCount >= 0) && (dwFlags & DPAM_INSERT)) {
291                 /* Now insert the remaining new items into DPA 1 */
292                 TRACE("%d items to be inserted at start of DPA 1\n",
293                       nCount+1);
294                 for (i=nCount; i>=0; i--) {
295                     PVOID ptr;
296
297                     ptr = (pfnMerge)(3, *pWork2, NULL, lParam);
298                     if (!ptr)
299                         return FALSE;
300                     DPA_InsertPtr (hdpa1, 0, ptr);
301                     pWork2--;
302                 }
303             }
304             break;
305         }
306         nResult = (pfnCompare)(*pWork1, *pWork2, lParam);
307         TRACE("compare result=%d, dpa1.cnt=%d, dpa2.cnt=%d\n",
308               nResult, nIndex, nCount);
309
310         if (nResult == 0)
311         {
312             PVOID ptr;
313
314             ptr = (pfnMerge)(1, *pWork1, *pWork2, lParam);
315             if (!ptr)
316                 return FALSE;
317
318             nCount--;
319             pWork2--;
320             *pWork1 = ptr;
321             nIndex--;
322             pWork1--;
323         }
324         else if (nResult > 0)
325         {
326             /* item in DPA 1 missing from DPA 2 */
327             if (dwFlags & DPAM_DELETE)
328             {
329                 /* Now delete the extra item in DPA1 */
330                 PVOID ptr;
331
332                 ptr = DPA_DeletePtr (hdpa1, hdpa1->nItemCount - 1);
333
334                 (pfnMerge)(2, ptr, NULL, lParam);
335             }
336             nIndex--;
337             pWork1--;
338         }
339         else
340         {
341             /* new item in DPA 2 */
342             if (dwFlags & DPAM_INSERT)
343             {
344                 /* Now insert the new item in DPA 1 */
345                 PVOID ptr;
346
347                 ptr = (pfnMerge)(3, *pWork2, NULL, lParam);
348                 if (!ptr)
349                     return FALSE;
350                 DPA_InsertPtr (hdpa1, nIndex+1, ptr);
351             }
352             nCount--;
353             pWork2--;
354         }
355
356     }
357     while (nCount >= 0);
358
359     return TRUE;
360 }
361
362
363 /**************************************************************************
364  * Alloc [COMCTL32.71]
365  *
366  * Allocates memory block from the dll's private heap
367  *
368  * PARAMS
369  *     dwSize [I] size of the allocated memory block
370  *
371  * RETURNS
372  *     Success: pointer to allocated memory block
373  *     Failure: NULL
374  */
375 LPVOID WINAPI Alloc (DWORD dwSize)
376 {
377     return LocalAlloc( LMEM_ZEROINIT, dwSize );
378 }
379
380
381 /**************************************************************************
382  * ReAlloc [COMCTL32.72]
383  *
384  * Changes the size of an allocated memory block or allocates a memory
385  * block using the dll's private heap.
386  *
387  * PARAMS
388  *     lpSrc  [I] pointer to memory block which will be resized
389  *     dwSize [I] new size of the memory block.
390  *
391  * RETURNS
392  *     Success: pointer to the resized memory block
393  *     Failure: NULL
394  *
395  * NOTES
396  *     If lpSrc is a NULL-pointer, then ReAlloc allocates a memory
397  *     block like Alloc.
398  */
399 LPVOID WINAPI ReAlloc (LPVOID lpSrc, DWORD dwSize)
400 {
401     if (lpSrc)
402         return LocalReAlloc( lpSrc, dwSize, LMEM_ZEROINIT );
403     else
404         return LocalAlloc( LMEM_ZEROINIT, dwSize);
405 }
406
407
408 /**************************************************************************
409  * Free [COMCTL32.73]
410  *
411  * Frees an allocated memory block from the dll's private heap.
412  *
413  * PARAMS
414  *     lpMem [I] pointer to memory block which will be freed
415  *
416  * RETURNS
417  *     Success: TRUE
418  *     Failure: FALSE
419  */
420 BOOL WINAPI Free (LPVOID lpMem)
421 {
422     return !LocalFree( lpMem );
423 }
424
425
426 /**************************************************************************
427  * GetSize [COMCTL32.74]
428  *
429  * Retrieves the size of the specified memory block from the dll's
430  * private heap.
431  *
432  * PARAMS
433  *     lpMem [I] pointer to an allocated memory block
434  *
435  * RETURNS
436  *     Success: size of the specified memory block
437  *     Failure: 0
438  */
439 DWORD WINAPI GetSize (LPVOID lpMem)
440 {
441     return LocalSize( lpMem );
442 }
443
444
445 /**************************************************************************
446  * MRU-Functions  {COMCTL32}
447  *
448  * NOTES
449  * The MRU-Api is a set of functions to manipulate lists of M.R.U. (Most Recently
450  * Used) items. It is an undocumented Api that is used (at least) by the shell
451  * and explorer to implement their recent documents feature.
452  *
453  * Since these functions are undocumented, they are unsupported by MS and
454  * may change at any time.
455  *
456  * Internally, the list is implemented as a last in, last out list of items
457  * persisted into the system registry under a caller chosen key. Each list
458  * item is given a one character identifier in the Ascii range from 'a' to
459  * '}'. A list of the identifiers in order from newest to oldest is stored
460  * under the same key in a value named "MRUList".
461  *
462  * Items are re-ordered by changing the order of the values in the MRUList
463  * value. When a new item is added, it becomes the new value of the oldest
464  * identifier, and that identifier is moved to the front of the MRUList value.
465  * 
466  * Wine stores MRU-lists in the same registry format as Windows, so when
467  * switching between the builtin and native comctl32.dll no problems or
468  * incompatibilities should occur.
469  *
470  * The following undocumented structure is used to create an MRU-list:
471  *|typedef INT (CALLBACK *MRUStringCmpFn)(LPCTSTR lhs, LPCTSTR rhs);
472  *|typedef INT (CALLBACK *MRUBinaryCmpFn)(LPCVOID lhs, LPCVOID rhs, DWORD length);
473  *|
474  *|typedef struct tagCREATEMRULIST
475  *|{
476  *|    DWORD   cbSize;
477  *|    DWORD   nMaxItems;
478  *|    DWORD   dwFlags;
479  *|    HKEY    hKey;
480  *|    LPCTSTR lpszSubKey;
481  *|    PROC    lpfnCompare;
482  *|} CREATEMRULIST, *LPCREATEMRULIST;
483  *
484  * MEMBERS
485  *  cbSize      [I] The size of the CREATEMRULIST structure. This must be set
486  *                  to sizeof(CREATEMRULIST) by the caller.
487  *  nMaxItems   [I] The maximum number of items allowed in the list. Because
488  *                  of the limited number of identifiers, this should be set to
489  *                  a value from 1 to 30 by the caller.
490  *  dwFlags     [I] If bit 0 is set, the list will be used to store binary
491  *                  data, otherwise it is assumed to store strings. If bit 1
492  *                  is set, every change made to the list will be reflected in
493  *                  the registry immediately, otherwise changes will only be
494  *                  written when the list is closed.
495  *  hKey        [I] The registry key that the list should be written under.
496  *                  This must be supplied by the caller.
497  *  lpszSubKey  [I] A caller supplied name of a subkey under hKey to write
498  *                  the list to. This may not be blank.
499  *  lpfnCompare [I] A caller supplied comparison function, which may be either
500  *                  an MRUStringCmpFn if dwFlags does not have bit 0 set, or a
501  *                  MRUBinaryCmpFn otherwise.
502  *
503  * FUNCTIONS
504  *  - Create an MRU-list with CreateMRUList() or CreateMRUListLazy().
505  *  - Add items to an MRU-list with AddMRUString() or AddMRUData().
506  *  - Remove items from an MRU-list with DelMRUString().
507  *  - Find data in an MRU-list with FindMRUString() or FindMRUData().
508  *  - Iterate through an MRU-list with EnumMRUList().
509  *  - Free an MRU-list with FreeMRUList().
510  */
511
512 typedef struct tagCREATEMRULISTA
513 {
514     DWORD  cbSize;
515     DWORD  nMaxItems;
516     DWORD  dwFlags;
517     HKEY   hKey;
518     LPCSTR lpszSubKey;
519     PROC   lpfnCompare;
520 } CREATEMRULISTA, *LPCREATEMRULISTA;
521
522 typedef struct tagCREATEMRULISTW
523 {
524     DWORD   cbSize;
525     DWORD   nMaxItems;
526     DWORD   dwFlags;
527     HKEY    hKey;
528     LPCWSTR lpszSubKey;
529     PROC    lpfnCompare;
530 } CREATEMRULISTW, *LPCREATEMRULISTW;
531
532 /* dwFlags */
533 #define MRUF_STRING_LIST  0 /* list will contain strings */
534 #define MRUF_BINARY_LIST  1 /* list will contain binary data */
535 #define MRUF_DELAYED_SAVE 2 /* only save list order to reg. is FreeMRUList */
536
537 /* If list is a string list lpfnCompare has the following prototype
538  * int CALLBACK MRUCompareString(LPCSTR s1, LPCSTR s2)
539  * for binary lists the prototype is
540  * int CALLBACK MRUCompareBinary(LPCVOID data1, LPCVOID data2, DWORD cbData)
541  * where cbData is the no. of bytes to compare.
542  * Need to check what return value means identical - 0?
543  */
544
545 typedef struct tagWINEMRUITEM
546 {
547     DWORD          size;        /* size of data stored               */
548     DWORD          itemFlag;    /* flags                             */
549     BYTE           datastart;
550 } WINEMRUITEM, *LPWINEMRUITEM;
551
552 /* itemFlag */
553 #define WMRUIF_CHANGED   0x0001 /* this dataitem changed             */
554
555 typedef struct tagWINEMRULIST
556 {
557     CREATEMRULISTW extview;     /* original create information       */
558     BOOL           isUnicode;   /* is compare fn Unicode */
559     DWORD          wineFlags;   /* internal flags                    */
560     DWORD          cursize;     /* current size of realMRU           */
561     LPSTR          realMRU;     /* pointer to string of index names  */
562     LPWINEMRUITEM  *array;      /* array of pointers to data         */
563                                 /* in 'a' to 'z' order               */
564 } WINEMRULIST, *LPWINEMRULIST;
565
566 /* wineFlags */
567 #define WMRUF_CHANGED  0x0001   /* MRU list has changed              */
568
569 /**************************************************************************
570  *              MRU_SaveChanged (internal)
571  *
572  * Local MRU saving code
573  */
574 static void MRU_SaveChanged ( LPWINEMRULIST mp )
575 {
576     UINT i, err;
577     HKEY newkey;
578     WCHAR realname[2];
579     LPWINEMRUITEM witem;
580     static const WCHAR emptyW[] = {'\0'};
581
582     /* or should we do the following instead of RegOpenKeyEx:
583      */
584
585     /* open the sub key */
586     if ((err = RegOpenKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
587                               0, KEY_WRITE, &newkey))) {
588         /* not present - what to do ??? */
589         ERR("Can not open key, error=%d, attempting to create\n",
590             err);
591         if ((err = RegCreateKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
592                                     0,
593                                     emptyW,
594                                     REG_OPTION_NON_VOLATILE,
595                                     KEY_READ | KEY_WRITE,
596                                     0,
597                                     &newkey,
598                                     0))) {
599             ERR("failed to create key /%s/, err=%d\n",
600                 debugstr_w(mp->extview.lpszSubKey), err);
601             return;
602         }
603     }
604     if (mp->wineFlags & WMRUF_CHANGED) {
605         mp->wineFlags &= ~WMRUF_CHANGED;
606         err = RegSetValueExA(newkey, "MRUList", 0, REG_SZ,
607                              mp->realMRU, strlen(mp->realMRU) + 1);
608         if (err) {
609             ERR("error saving MRUList, err=%d\n", err);
610         }
611         TRACE("saving MRUList=/%s/\n", mp->realMRU);
612     }
613     realname[1] = 0;
614     for(i=0; i<mp->cursize; i++) {
615         witem = mp->array[i];
616         if (witem->itemFlag & WMRUIF_CHANGED) {
617             witem->itemFlag &= ~WMRUIF_CHANGED;
618             realname[0] = 'a' + i;
619             err = RegSetValueExW(newkey, realname, 0,
620                                  (mp->extview.dwFlags & MRUF_BINARY_LIST) ?
621                                  REG_BINARY : REG_SZ,
622                                  &witem->datastart, witem->size);
623             if (err) {
624                 ERR("error saving /%s/, err=%d\n", debugstr_w(realname), err);
625             }
626             TRACE("saving value for name /%s/ size=%ld\n",
627                   debugstr_w(realname), witem->size);
628         }
629     }
630     RegCloseKey( newkey );
631 }
632
633 /**************************************************************************
634  *              FreeMRUList [COMCTL32.152]
635  *
636  * Frees a most-recently-used items list.
637  *
638  * PARAMS
639  *     hMRUList [I] Handle to list.
640  *
641  * RETURNS
642  *     Nothing.
643  */
644 void WINAPI FreeMRUList (HANDLE hMRUList)
645 {
646     LPWINEMRULIST mp = (LPWINEMRULIST)hMRUList;
647     UINT i;
648
649     TRACE("(%p)\n", hMRUList);
650     if (!hMRUList)
651         return;
652
653     if (mp->wineFlags & WMRUF_CHANGED) {
654         /* need to open key and then save the info */
655         MRU_SaveChanged( mp );
656     }
657
658     for(i=0; i<mp->extview.nMaxItems; i++) {
659         if (mp->array[i])
660             Free(mp->array[i]);
661     }
662     Free(mp->realMRU);
663     Free(mp->array);
664     Free((LPWSTR)mp->extview.lpszSubKey);
665     Free(mp);
666 }
667
668
669 /**************************************************************************
670  *                  FindMRUData [COMCTL32.169]
671  *
672  * Searches binary list for item that matches lpData of length cbData.
673  * Returns position in list order 0 -> MRU and if lpRegNum != NULL then value
674  * corresponding to item's reg. name will be stored in it ('a' -> 0).
675  *
676  * PARAMS
677  *    hList [I] list handle
678  *    lpData [I] data to find
679  *    cbData [I] length of data
680  *    lpRegNum [O] position in registry (maybe NULL)
681  *
682  * RETURNS
683  *    Position in list 0 -> MRU.  -1 if item not found.
684  */
685 INT WINAPI FindMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData,
686                         LPINT lpRegNum)
687 {
688     LPWINEMRULIST mp = (LPWINEMRULIST)hList;
689     INT ret;
690     UINT i;
691     LPSTR dataA = NULL;
692
693     if (!mp->extview.lpfnCompare) {
694         ERR("MRU list not properly created. No compare procedure.\n");
695         return -1;
696     }
697
698     if(!(mp->extview.dwFlags & MRUF_BINARY_LIST) && !mp->isUnicode) {
699         DWORD len = WideCharToMultiByte(CP_ACP, 0, lpData, -1,
700                                         NULL, 0, NULL, NULL);
701         dataA = Alloc(len);
702         WideCharToMultiByte(CP_ACP, 0, lpData, -1, dataA, len, NULL, NULL);
703     }
704
705     for(i=0; i<mp->cursize; i++) {
706         if (mp->extview.dwFlags & MRUF_BINARY_LIST) {
707             if (!mp->extview.lpfnCompare(lpData, &mp->array[i]->datastart,
708                                          cbData))
709                 break;
710         }
711         else {
712             if(mp->isUnicode) {
713                 if (!mp->extview.lpfnCompare(lpData, &mp->array[i]->datastart))
714                     break;
715             } else {
716                 DWORD len = WideCharToMultiByte(CP_ACP, 0,
717                                                 (LPWSTR)&mp->array[i]->datastart, -1,
718                                                 NULL, 0, NULL, NULL);
719                 LPSTR itemA = Alloc(len);
720                 INT cmp;
721                 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&mp->array[i]->datastart, -1,
722                                     itemA, len, NULL, NULL);
723
724                 cmp = mp->extview.lpfnCompare(dataA, itemA);
725                 Free(itemA);
726                 if(!cmp)
727                     break;
728             }
729         }
730     }
731     if(dataA)
732         Free(dataA);
733     if (i < mp->cursize)
734         ret = i;
735     else
736         ret = -1;
737     if (lpRegNum && (ret != -1))
738         *lpRegNum = 'a' + i;
739
740     TRACE("(%p, %p, %ld, %p) returning %d\n",
741            hList, lpData, cbData, lpRegNum, ret);
742
743     return ret;
744 }
745
746
747 /**************************************************************************
748  *              AddMRUData [COMCTL32.167]
749  *
750  * Add item to MRU binary list.  If item already exists in list then it is
751  * simply moved up to the top of the list and not added again.  If list is
752  * full then the least recently used item is removed to make room.
753  *
754  * PARAMS
755  *     hList [I] Handle to list.
756  *     lpData [I] ptr to data to add.
757  *     cbData [I] no. of bytes of data.
758  *
759  * RETURNS
760  *     No. corresponding to registry name where value is stored 'a' -> 0 etc.
761  *     -1 on error.
762  */
763 INT WINAPI AddMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData)
764 {
765     LPWINEMRULIST mp = (LPWINEMRULIST)hList;
766     LPWINEMRUITEM witem;
767     INT i, replace;
768
769     if ((replace = FindMRUData (hList, lpData, cbData, NULL)) >= 0) {
770         /* Item exists, just move it to the front */
771         LPSTR pos = strchr(mp->realMRU, replace + 'a');
772         while (pos > mp->realMRU)
773         {
774             pos[0] = pos[-1];
775             pos--;
776         }
777     }
778     else {
779         /* either add a new entry or replace oldest */
780         if (mp->cursize < mp->extview.nMaxItems) {
781             /* Add in a new item */
782             replace = mp->cursize;
783             mp->cursize++;
784         }
785         else {
786             /* get the oldest entry and replace data */
787             replace = mp->realMRU[mp->cursize - 1] - 'a';
788             Free(mp->array[replace]);
789         }
790
791         /* Allocate space for new item and move in the data */
792         mp->array[replace] = witem = Alloc(cbData + sizeof(WINEMRUITEM));
793         witem->itemFlag |= WMRUIF_CHANGED;
794         witem->size = cbData;
795         memcpy( &witem->datastart, lpData, cbData);
796
797         /* now rotate MRU list */
798         for(i=mp->cursize-1; i>=1; i--)
799             mp->realMRU[i] = mp->realMRU[i-1];
800     }
801
802     /* The new item gets the front spot */
803     mp->wineFlags |= WMRUF_CHANGED;
804     mp->realMRU[0] = replace + 'a';
805
806     TRACE("(%p, %p, %ld) adding data, /%c/ now most current\n",
807           hList, lpData, cbData, replace+'a');
808
809     if (!(mp->extview.dwFlags & MRUF_DELAYED_SAVE)) {
810         /* save changed stuff right now */
811         MRU_SaveChanged( mp );
812     }
813
814     return replace;
815 }
816
817 /**************************************************************************
818  *              AddMRUStringW [COMCTL32.401]
819  *
820  * Add an item to an MRU string list.
821  *
822  * PARAMS
823  *     hList      [I] Handle to list.
824  *     lpszString [I] The string to add.
825  *
826  * RETURNS
827  *   Success: The number corresponding to the registry name where the string
828  *            has been stored (0 maps to 'a', 1 to 'b' and so on).
829  *   Failure: -1, if hList is NULL or memory allocation fails. If lpszString
830  *            is invalid, the function returns 0, and GetLastError() returns
831  *            ERROR_INVALID_PARAMETER. The last error value is set only in
832  *            this case.
833  *
834  * NOTES
835  *  -If lpszString exists in the list already, it is moved to the top of the
836  *   MRU list (it is not duplicated).
837  *  -If the list is full the least recently used list entry is replaced with
838  *   lpszString.
839  *  -If this function returns 0 you should check the last error value to
840  *   ensure the call really succeeded.
841  */
842 INT WINAPI AddMRUStringW(HANDLE hList, LPCWSTR lpszString)
843 {
844     TRACE("(%p,%s)\n", hList, debugstr_w(lpszString));
845
846     if (!hList)
847         return -1;
848
849     if (!lpszString || IsBadStringPtrW(lpszString, -1))
850     {
851         SetLastError(ERROR_INVALID_PARAMETER);
852         return 0;
853     }
854
855     return AddMRUData(hList, lpszString,
856                       (strlenW(lpszString) + 1) * sizeof(WCHAR));
857 }
858
859 /**************************************************************************
860  *              AddMRUStringA [COMCTL32.153]
861  *
862  * See AddMRUStringW.
863  */
864 INT WINAPI AddMRUStringA(HANDLE hList, LPCSTR lpszString)
865 {
866     DWORD len;
867     LPWSTR stringW;
868     INT ret;
869
870     TRACE("(%p,%s)\n", hList, debugstr_a(lpszString));
871
872     if (!hList)
873         return -1;
874
875     if (IsBadStringPtrA(lpszString, -1))
876     {
877         SetLastError(ERROR_INVALID_PARAMETER);
878         return 0;
879     }
880
881     len = MultiByteToWideChar(CP_ACP, 0, lpszString, -1, NULL, 0) * sizeof(WCHAR);
882     stringW = Alloc(len);
883     if (!stringW)
884         return -1;
885
886     MultiByteToWideChar(CP_ACP, 0, lpszString, -1, stringW, len);
887     ret = AddMRUData(hList, stringW, len);
888     Free(stringW);
889     return ret;
890 }
891
892 /**************************************************************************
893  *              DelMRUString [COMCTL32.156]
894  *
895  * Removes item from either string or binary list (despite its name)
896  *
897  * PARAMS
898  *    hList [I] list handle
899  *    nItemPos [I] item position to remove 0 -> MRU
900  *
901  * RETURNS
902  *    TRUE if successful, FALSE if nItemPos is out of range.
903  */
904 BOOL WINAPI DelMRUString(HANDLE hList, INT nItemPos)
905 {
906     FIXME("(%p, %d): stub\n", hList, nItemPos);
907     return TRUE;
908 }
909
910 /**************************************************************************
911  *                  FindMRUStringW [COMCTL32.402]
912  *
913  * See FindMRUStringA.
914  */
915 INT WINAPI FindMRUStringW (HANDLE hList, LPCWSTR lpszString, LPINT lpRegNum)
916 {
917   return FindMRUData(hList, lpszString,
918                      (lstrlenW(lpszString) + 1) * sizeof(WCHAR), lpRegNum);
919 }
920
921 /**************************************************************************
922  *                  FindMRUStringA [COMCTL32.155]
923  *
924  * Searches string list for item that matches lpszString.
925  * Returns position in list order 0 -> MRU and if lpRegNum != NULL then value
926  * corresponding to item's reg. name will be stored in it ('a' -> 0).
927  *
928  * PARAMS
929  *    hList [I] list handle
930  *    lpszString [I] string to find
931  *    lpRegNum [O] position in registry (maybe NULL)
932  *
933  * RETURNS
934  *    Position in list 0 -> MRU.  -1 if item not found.
935  */
936 INT WINAPI FindMRUStringA (HANDLE hList, LPCSTR lpszString, LPINT lpRegNum)
937 {
938     DWORD len = MultiByteToWideChar(CP_ACP, 0, lpszString, -1, NULL, 0);
939     LPWSTR stringW = Alloc(len * sizeof(WCHAR));
940     INT ret;
941
942     MultiByteToWideChar(CP_ACP, 0, lpszString, -1, stringW, len);
943     ret = FindMRUData(hList, stringW, len * sizeof(WCHAR), lpRegNum);
944     Free(stringW);
945     return ret;
946 }
947
948 /*************************************************************************
949  *                 CreateMRUListLazy_common (internal)
950  */
951 static HANDLE CreateMRUListLazy_common(LPWINEMRULIST mp)
952 {
953     UINT i, err;
954     HKEY newkey;
955     DWORD datasize, dwdisp;
956     WCHAR realname[2];
957     LPWINEMRUITEM witem;
958     DWORD type;
959     static const WCHAR emptyW[] = {'\0'};
960
961     /* get space to save indices that will turn into names
962      * but in order of most to least recently used
963      */
964     mp->realMRU = Alloc(mp->extview.nMaxItems + 2);
965
966     /* get space to save pointers to actual data in order of
967      * 'a' to 'z' (0 to n).
968      */
969     mp->array = Alloc(mp->extview.nMaxItems * sizeof(LPVOID));
970
971     /* open the sub key */
972     if ((err = RegCreateKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
973                                 0,
974                                 emptyW,
975                                 REG_OPTION_NON_VOLATILE,
976                                 KEY_READ | KEY_WRITE,
977                                 0,
978                                 &newkey,
979                                 &dwdisp))) {
980         /* error - what to do ??? */
981         ERR("(%lu %lu %lx %lx \"%s\" %p): Can not open key, error=%d\n",
982             mp->extview.cbSize, mp->extview.nMaxItems, mp->extview.dwFlags,
983             (DWORD)mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey),
984                                  mp->extview.lpfnCompare, err);
985         return 0;
986     }
987
988     /* get values from key 'MRUList' */
989     if (newkey) {
990         datasize = mp->extview.nMaxItems + 1;
991         if((err=RegQueryValueExA( newkey, "MRUList", 0, &type, mp->realMRU,
992                                   &datasize))) {
993             /* not present - set size to 1 (will become 0 later) */
994             datasize = 1;
995             *mp->realMRU = 0;
996         }
997
998         TRACE("MRU list = %s, datasize = %ld\n", mp->realMRU, datasize);
999
1000         mp->cursize = datasize - 1;
1001         /* datasize now has number of items in the MRUList */
1002
1003         /* get actual values for each entry */
1004         realname[1] = 0;
1005         for(i=0; i<mp->cursize; i++) {
1006             realname[0] = 'a' + i;
1007             if(RegQueryValueExW( newkey, realname, 0, &type, 0, &datasize)) {
1008                 /* not present - what to do ??? */
1009                 ERR("Key %s not found 1\n", debugstr_w(realname));
1010             }
1011             mp->array[i] = witem = Alloc(datasize + sizeof(WINEMRUITEM));
1012             witem->size = datasize;
1013             if(RegQueryValueExW( newkey, realname, 0, &type,
1014                                  &witem->datastart, &datasize)) {
1015                 /* not present - what to do ??? */
1016                 ERR("Key %s not found 2\n", debugstr_w(realname));
1017             }
1018         }
1019         RegCloseKey( newkey );
1020     }
1021     else
1022         mp->cursize = 0;
1023
1024     TRACE("(%lu %lu %lx %lx \"%s\" %p): Current Size = %ld\n",
1025           mp->extview.cbSize, mp->extview.nMaxItems, mp->extview.dwFlags,
1026           (DWORD)mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey),
1027           mp->extview.lpfnCompare, mp->cursize);
1028     return (HANDLE)mp;
1029 }
1030
1031 /**************************************************************************
1032  *                  CreateMRUListLazyW [COMCTL32.404]
1033  *
1034  * See CreateMRUListLazyA.
1035  */
1036 HANDLE WINAPI CreateMRUListLazyW (LPCREATEMRULISTW lpcml, DWORD dwParam2,
1037                                   DWORD dwParam3, DWORD dwParam4)
1038 {
1039     LPWINEMRULIST mp;
1040
1041     /* Native does not check for a NULL lpcml */
1042
1043     if (lpcml->cbSize != sizeof(CREATEMRULISTW) || !lpcml->hKey ||
1044         IsBadStringPtrW(lpcml->lpszSubKey, -1))
1045         return NULL;
1046
1047     mp = Alloc(sizeof(WINEMRULIST));
1048     memcpy(&mp->extview, lpcml, sizeof(CREATEMRULISTW));
1049     mp->extview.lpszSubKey = Alloc((strlenW(lpcml->lpszSubKey) + 1) * sizeof(WCHAR));
1050     strcpyW((LPWSTR)mp->extview.lpszSubKey, lpcml->lpszSubKey);
1051     mp->isUnicode = TRUE;
1052
1053     return CreateMRUListLazy_common(mp);
1054 }
1055
1056 /**************************************************************************
1057  *                  CreateMRUListLazyA [COMCTL32.157]
1058  *
1059  * Creates a most-recently-used list.
1060  *
1061  * PARAMS
1062  *     lpcml    [I] ptr to CREATEMRULIST structure.
1063  *     dwParam2 [I] Unknown
1064  *     dwParam3 [I] Unknown
1065  *     dwParam4 [I] Unknown
1066  *
1067  * RETURNS
1068  *     Handle to MRU list.
1069  */
1070 HANDLE WINAPI CreateMRUListLazyA (LPCREATEMRULISTA lpcml, DWORD dwParam2,
1071                                   DWORD dwParam3, DWORD dwParam4)
1072 {
1073     LPWINEMRULIST mp;
1074     DWORD len;
1075
1076     /* Native does not check for a NULL lpcml */
1077
1078     if (lpcml->cbSize != sizeof(CREATEMRULISTA) || !lpcml->hKey ||
1079         IsBadStringPtrA(lpcml->lpszSubKey, -1))
1080         return 0;
1081
1082     mp = Alloc(sizeof(WINEMRULIST));
1083     memcpy(&mp->extview, lpcml, sizeof(CREATEMRULISTW));
1084     len = MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1, NULL, 0);
1085     mp->extview.lpszSubKey = Alloc(len * sizeof(WCHAR));
1086     MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1,
1087                         (LPWSTR)mp->extview.lpszSubKey, len);
1088     mp->isUnicode = FALSE;
1089     return CreateMRUListLazy_common(mp);
1090 }
1091
1092 /**************************************************************************
1093  *              CreateMRUListW [COMCTL32.400]
1094  *
1095  * See CreateMRUListA.
1096  */
1097 HANDLE WINAPI CreateMRUListW (LPCREATEMRULISTW lpcml)
1098 {
1099     return CreateMRUListLazyW(lpcml, 0, 0, 0);
1100 }
1101
1102 /**************************************************************************
1103  *              CreateMRUListA [COMCTL32.151]
1104  *
1105  * Creates a most-recently-used list.
1106  *
1107  * PARAMS
1108  *     lpcml [I] ptr to CREATEMRULIST structure.
1109  *
1110  * RETURNS
1111  *     Handle to MRU list.
1112  */
1113 HANDLE WINAPI CreateMRUListA (LPCREATEMRULISTA lpcml)
1114 {
1115      return CreateMRUListLazyA (lpcml, 0, 0, 0);
1116 }
1117
1118
1119 /**************************************************************************
1120  *                EnumMRUListW [COMCTL32.403]
1121  *
1122  * Enumerate item in a most-recenty-used list
1123  *
1124  * PARAMS
1125  *    hList [I] list handle
1126  *    nItemPos [I] item position to enumerate
1127  *    lpBuffer [O] buffer to receive item
1128  *    nBufferSize [I] size of buffer
1129  *
1130  * RETURNS
1131  *    For binary lists specifies how many bytes were copied to buffer, for
1132  *    string lists specifies full length of string.  Enumerating past the end
1133  *    of list returns -1.
1134  *    If lpBuffer == NULL or nItemPos is -ve return value is no. of items in
1135  *    the list.
1136  */
1137 INT WINAPI EnumMRUListW (HANDLE hList, INT nItemPos, LPVOID lpBuffer,
1138                          DWORD nBufferSize)
1139 {
1140     LPWINEMRULIST mp = (LPWINEMRULIST) hList;
1141     LPWINEMRUITEM witem;
1142     INT desired, datasize;
1143
1144     if (nItemPos >= mp->cursize) return -1;
1145     if ((nItemPos < 0) || !lpBuffer) return mp->cursize;
1146     desired = mp->realMRU[nItemPos];
1147     desired -= 'a';
1148     TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired);
1149     witem = mp->array[desired];
1150     datasize = min( witem->size, nBufferSize );
1151     memcpy( lpBuffer, &witem->datastart, datasize);
1152     TRACE("(%p, %d, %p, %ld): returning len=%d\n",
1153           hList, nItemPos, lpBuffer, nBufferSize, datasize);
1154     return datasize;
1155 }
1156
1157 /**************************************************************************
1158  *                EnumMRUListA [COMCTL32.154]
1159  *
1160  * See EnumMRUListW.
1161  */
1162 INT WINAPI EnumMRUListA (HANDLE hList, INT nItemPos, LPVOID lpBuffer,
1163                          DWORD nBufferSize)
1164 {
1165     LPWINEMRULIST mp = (LPWINEMRULIST) hList;
1166     LPWINEMRUITEM witem;
1167     INT desired, datasize;
1168     DWORD lenA;
1169
1170     if (nItemPos >= mp->cursize) return -1;
1171     if ((nItemPos < 0) || !lpBuffer) return mp->cursize;
1172     desired = mp->realMRU[nItemPos];
1173     desired -= 'a';
1174     TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired);
1175     witem = mp->array[desired];
1176     if(mp->extview.dwFlags & MRUF_BINARY_LIST) {
1177         datasize = min( witem->size, nBufferSize );
1178         memcpy( lpBuffer, &witem->datastart, datasize);
1179     } else {
1180         lenA = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1,
1181                                    NULL, 0, NULL, NULL);
1182         datasize = min( witem->size, nBufferSize );
1183         WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1,
1184                             lpBuffer, datasize, NULL, NULL);
1185     }
1186     TRACE("(%p, %d, %p, %ld): returning len=%d\n",
1187           hList, nItemPos, lpBuffer, nBufferSize, datasize);
1188     return datasize;
1189 }
1190
1191
1192 /**************************************************************************
1193  * Str_GetPtrA [COMCTL32.233]
1194  *
1195  * Copies a string into a destination buffer.
1196  *
1197  * PARAMS
1198  *     lpSrc   [I] Source string
1199  *     lpDest  [O] Destination buffer
1200  *     nMaxLen [I] Size of buffer in characters
1201  *
1202  * RETURNS
1203  *     The number of characters copied.
1204  */
1205 INT WINAPI Str_GetPtrA (LPCSTR lpSrc, LPSTR lpDest, INT nMaxLen)
1206 {
1207     INT len;
1208
1209     TRACE("(%p %p %d)\n", lpSrc, lpDest, nMaxLen);
1210
1211     if (!lpDest && lpSrc)
1212         return strlen (lpSrc);
1213
1214     if (nMaxLen == 0)
1215         return 0;
1216
1217     if (lpSrc == NULL) {
1218         lpDest[0] = '\0';
1219         return 0;
1220     }
1221
1222     len = strlen (lpSrc);
1223     if (len >= nMaxLen)
1224         len = nMaxLen - 1;
1225
1226     RtlMoveMemory (lpDest, lpSrc, len);
1227     lpDest[len] = '\0';
1228
1229     return len;
1230 }
1231
1232
1233 /**************************************************************************
1234  * Str_SetPtrA [COMCTL32.234]
1235  *
1236  * Makes a copy of a string, allocating memory if necessary.
1237  *
1238  * PARAMS
1239  *     lppDest [O] Pointer to destination string
1240  *     lpSrc   [I] Source string
1241  *
1242  * RETURNS
1243  *     Success: TRUE
1244  *     Failure: FALSE
1245  *
1246  * NOTES
1247  *     Set lpSrc to NULL to free the memory allocated by a previous call
1248  *     to this function.
1249  */
1250 BOOL WINAPI Str_SetPtrA (LPSTR *lppDest, LPCSTR lpSrc)
1251 {
1252     TRACE("(%p %p)\n", lppDest, lpSrc);
1253
1254     if (lpSrc) {
1255         LPSTR ptr = ReAlloc (*lppDest, strlen (lpSrc) + 1);
1256         if (!ptr)
1257             return FALSE;
1258         strcpy (ptr, lpSrc);
1259         *lppDest = ptr;
1260     }
1261     else {
1262         if (*lppDest) {
1263             Free (*lppDest);
1264             *lppDest = NULL;
1265         }
1266     }
1267
1268     return TRUE;
1269 }
1270
1271
1272 /**************************************************************************
1273  * Str_GetPtrW [COMCTL32.235]
1274  *
1275  * See Str_GetPtrA.
1276  */
1277 INT WINAPI Str_GetPtrW (LPCWSTR lpSrc, LPWSTR lpDest, INT nMaxLen)
1278 {
1279     INT len;
1280
1281     TRACE("(%p %p %d)\n", lpSrc, lpDest, nMaxLen);
1282
1283     if (!lpDest && lpSrc)
1284         return strlenW (lpSrc);
1285
1286     if (nMaxLen == 0)
1287         return 0;
1288
1289     if (lpSrc == NULL) {
1290         lpDest[0] = L'\0';
1291         return 0;
1292     }
1293
1294     len = strlenW (lpSrc);
1295     if (len >= nMaxLen)
1296         len = nMaxLen - 1;
1297
1298     RtlMoveMemory (lpDest, lpSrc, len*sizeof(WCHAR));
1299     lpDest[len] = L'\0';
1300
1301     return len;
1302 }
1303
1304
1305 /**************************************************************************
1306  * Str_SetPtrW [COMCTL32.236]
1307  *
1308  * See Str_SetPtrA.
1309  */
1310 BOOL WINAPI Str_SetPtrW (LPWSTR *lppDest, LPCWSTR lpSrc)
1311 {
1312     TRACE("(%p %p)\n", lppDest, lpSrc);
1313
1314     if (lpSrc) {
1315         INT len = strlenW (lpSrc) + 1;
1316         LPWSTR ptr = ReAlloc (*lppDest, len * sizeof(WCHAR));
1317         if (!ptr)
1318             return FALSE;
1319         strcpyW (ptr, lpSrc);
1320         *lppDest = ptr;
1321     }
1322     else {
1323         if (*lppDest) {
1324             Free (*lppDest);
1325             *lppDest = NULL;
1326         }
1327     }
1328
1329     return TRUE;
1330 }
1331
1332
1333 /**************************************************************************
1334  * Str_GetPtrWtoA [internal]
1335  *
1336  * Converts a unicode string into a multi byte string
1337  *
1338  * PARAMS
1339  *     lpSrc   [I] Pointer to the unicode source string
1340  *     lpDest  [O] Pointer to caller supplied storage for the multi byte string
1341  *     nMaxLen [I] Size, in bytes, of the destination buffer
1342  *
1343  * RETURNS
1344  *     Length, in bytes, of the converted string.
1345  */
1346
1347 INT Str_GetPtrWtoA (LPCWSTR lpSrc, LPSTR lpDest, INT nMaxLen)
1348 {
1349     INT len;
1350
1351     TRACE("(%s %p %d)\n", debugstr_w(lpSrc), lpDest, nMaxLen);
1352
1353     if (!lpDest && lpSrc)
1354         return WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, 0, 0, NULL, NULL);
1355
1356     if (nMaxLen == 0)
1357         return 0;
1358
1359     if (lpSrc == NULL) {
1360         lpDest[0] = '\0';
1361         return 0;
1362     }
1363
1364     len = WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, 0, 0, NULL, NULL);
1365     if (len >= nMaxLen)
1366         len = nMaxLen - 1;
1367
1368     WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, lpDest, len, NULL, NULL);
1369     lpDest[len] = '\0';
1370
1371     return len;
1372 }
1373
1374
1375 /**************************************************************************
1376  * Str_SetPtrAtoW [internal]
1377  *
1378  * Converts a multi byte string to a unicode string.
1379  * If the pointer to the destination buffer is NULL a buffer is allocated.
1380  * If the destination buffer is too small to keep the converted multi byte
1381  * string the destination buffer is reallocated. If the source pointer is
1382  * NULL, the destination buffer is freed.
1383  *
1384  * PARAMS
1385  *     lppDest [I/O] pointer to a pointer to the destination buffer
1386  *     lpSrc   [I] pointer to a multi byte string
1387  *
1388  * RETURNS
1389  *     TRUE: conversion successful
1390  *     FALSE: error
1391  */
1392 BOOL Str_SetPtrAtoW (LPWSTR *lppDest, LPCSTR lpSrc)
1393 {
1394     TRACE("(%p %s)\n", lppDest, lpSrc);
1395
1396     if (lpSrc) {
1397         INT len = MultiByteToWideChar(CP_ACP,0,lpSrc,-1,NULL,0);
1398         LPWSTR ptr = ReAlloc (*lppDest, len*sizeof(WCHAR));
1399
1400         if (!ptr)
1401             return FALSE;
1402         MultiByteToWideChar(CP_ACP,0,lpSrc,-1,ptr,len);
1403         *lppDest = ptr;
1404     }
1405     else {
1406         if (*lppDest) {
1407             Free (*lppDest);
1408             *lppDest = NULL;
1409         }
1410     }
1411
1412     return TRUE;
1413 }
1414
1415
1416 /**************************************************************************
1417  * DSA_Create [COMCTL32.320]
1418  *
1419  * Creates a dynamic storage array
1420  *
1421  * PARAMS
1422  *     nSize [I] size of the array elements
1423  *     nGrow [I] number of elements by which the array grows when it is filled
1424  *
1425  * RETURNS
1426  *     Success: pointer to an array control structure. Use this like a handle.
1427  *     Failure: NULL
1428  *
1429  * NOTES
1430  *     The DSA_ functions can be used to create and manipulate arrays of
1431  *     fixed-size memory blocks. These arrays can store any kind of data
1432  *     (e.g. strings and icons).
1433  */
1434 HDSA WINAPI DSA_Create (INT nSize, INT nGrow)
1435 {
1436     HDSA hdsa;
1437
1438     TRACE("(size=%d grow=%d)\n", nSize, nGrow);
1439
1440     hdsa = Alloc (sizeof(*hdsa));
1441     if (hdsa)
1442     {
1443         hdsa->nItemCount = 0;
1444         hdsa->pData = NULL;
1445         hdsa->nMaxCount = 0;
1446         hdsa->nItemSize = nSize;
1447         hdsa->nGrow = max(1, nGrow);
1448     }
1449
1450     return hdsa;
1451 }
1452
1453
1454 /**************************************************************************
1455  * DSA_Destroy [COMCTL32.321]
1456  * 
1457  * Destroys a dynamic storage array
1458  *
1459  * PARAMS
1460  *     hdsa [I] pointer to the array control structure
1461  *
1462  * RETURNS
1463  *     Success: TRUE
1464  *     Failure: FALSE
1465  */
1466 BOOL WINAPI DSA_Destroy (const HDSA hdsa)
1467 {
1468     TRACE("(%p)\n", hdsa);
1469
1470     if (!hdsa)
1471         return FALSE;
1472
1473     if (hdsa->pData && (!Free (hdsa->pData)))
1474         return FALSE;
1475
1476     return Free (hdsa);
1477 }
1478
1479
1480 /**************************************************************************
1481  * DSA_GetItem [COMCTL32.322]
1482  *
1483  * Copies the specified item into a caller-supplied buffer.
1484  *
1485  * PARAMS
1486  *     hdsa   [I] pointer to the array control structure
1487  *     nIndex [I] number of the Item to get
1488  *     pDest  [O] destination buffer. Has to be >= dwElementSize.
1489  *
1490  * RETURNS
1491  *     Success: TRUE
1492  *     Failure: FALSE
1493  */
1494 BOOL WINAPI DSA_GetItem (const HDSA hdsa, INT nIndex, LPVOID pDest)
1495 {
1496     LPVOID pSrc;
1497
1498     TRACE("(%p %d %p)\n", hdsa, nIndex, pDest);
1499
1500     if (!hdsa)
1501         return FALSE;
1502     if ((nIndex < 0) || (nIndex >= hdsa->nItemCount))
1503         return FALSE;
1504
1505     pSrc = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
1506     memmove (pDest, pSrc, hdsa->nItemSize);
1507
1508     return TRUE;
1509 }
1510
1511
1512 /**************************************************************************
1513  * DSA_GetItemPtr [COMCTL32.323]
1514  *
1515  * Retrieves a pointer to the specified item.
1516  *
1517  * PARAMS
1518  *     hdsa   [I] pointer to the array control structure
1519  *     nIndex [I] index of the desired item
1520  *
1521  * RETURNS
1522  *     Success: pointer to an item
1523  *     Failure: NULL
1524  */
1525 LPVOID WINAPI DSA_GetItemPtr (const HDSA hdsa, INT nIndex)
1526 {
1527     LPVOID pSrc;
1528
1529     TRACE("(%p %d)\n", hdsa, nIndex);
1530
1531     if (!hdsa)
1532         return NULL;
1533     if ((nIndex < 0) || (nIndex >= hdsa->nItemCount))
1534         return NULL;
1535
1536     pSrc = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
1537
1538     TRACE("-- ret=%p\n", pSrc);
1539
1540     return pSrc;
1541 }
1542
1543
1544 /**************************************************************************
1545  * DSA_SetItem [COMCTL32.325]
1546  *
1547  * Sets the contents of an item in the array.
1548  *
1549  * PARAMS
1550  *     hdsa   [I] pointer to the array control structure
1551  *     nIndex [I] index for the item
1552  *     pSrc   [I] pointer to the new item data
1553  *
1554  * RETURNS
1555  *     Success: TRUE
1556  *     Failure: FALSE
1557  */
1558 BOOL WINAPI DSA_SetItem (const HDSA hdsa, INT nIndex, LPVOID pSrc)
1559 {
1560     INT  nSize, nNewItems;
1561     LPVOID pDest, lpTemp;
1562
1563     TRACE("(%p %d %p)\n", hdsa, nIndex, pSrc);
1564
1565     if ((!hdsa) || nIndex < 0)
1566         return FALSE;
1567
1568     if (hdsa->nItemCount <= nIndex) {
1569         /* within the old array */
1570         if (hdsa->nMaxCount > nIndex) {
1571             /* within the allocated space, set a new boundary */
1572             hdsa->nItemCount = nIndex + 1;
1573         }
1574         else {
1575             /* resize the block of memory */
1576             nNewItems =
1577                 hdsa->nGrow * ((INT)(((nIndex + 1) - 1) / hdsa->nGrow) + 1);
1578             nSize = hdsa->nItemSize * nNewItems;
1579
1580             lpTemp = ReAlloc (hdsa->pData, nSize);
1581             if (!lpTemp)
1582                 return FALSE;
1583
1584             hdsa->nMaxCount = nNewItems;
1585             hdsa->nItemCount = nIndex + 1;
1586             hdsa->pData = lpTemp;
1587         }
1588     }
1589
1590     /* put the new entry in */
1591     pDest = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
1592     TRACE("-- move dest=%p src=%p size=%d\n",
1593            pDest, pSrc, hdsa->nItemSize);
1594     memmove (pDest, pSrc, hdsa->nItemSize);
1595
1596     return TRUE;
1597 }
1598
1599
1600 /**************************************************************************
1601  * DSA_InsertItem [COMCTL32.324]
1602  *
1603  * Inserts an item into the array at the specified index.
1604  *
1605  * PARAMS
1606  *     hdsa   [I] pointer to the array control structure
1607  *     nIndex [I] index for the new item
1608  *     pSrc   [I] pointer to the element
1609  *
1610  * RETURNS
1611  *     Success: position of the new item
1612  *     Failure: -1
1613  */
1614 INT WINAPI DSA_InsertItem (const HDSA hdsa, INT nIndex, LPVOID pSrc)
1615 {
1616     INT   nNewItems, nSize;
1617     LPVOID  lpTemp, lpDest;
1618
1619     TRACE("(%p %d %p)\n", hdsa, nIndex, pSrc);
1620
1621     if ((!hdsa) || nIndex < 0)
1622         return -1;
1623
1624     /* when nIndex >= nItemCount then append */
1625     if (nIndex >= hdsa->nItemCount)
1626         nIndex = hdsa->nItemCount;
1627
1628     /* do we need to resize ? */
1629     if (hdsa->nItemCount >= hdsa->nMaxCount) {
1630         nNewItems = hdsa->nMaxCount + hdsa->nGrow;
1631         nSize = hdsa->nItemSize * nNewItems;
1632
1633         lpTemp = ReAlloc (hdsa->pData, nSize);
1634         if (!lpTemp)
1635             return -1;
1636
1637         hdsa->nMaxCount = nNewItems;
1638         hdsa->pData = lpTemp;
1639     }
1640
1641     /* do we need to move elements ? */
1642     if (nIndex < hdsa->nItemCount) {
1643         lpTemp = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
1644         lpDest = (char *) lpTemp + hdsa->nItemSize;
1645         nSize = (hdsa->nItemCount - nIndex) * hdsa->nItemSize;
1646         TRACE("-- move dest=%p src=%p size=%d\n",
1647                lpDest, lpTemp, nSize);
1648         memmove (lpDest, lpTemp, nSize);
1649     }
1650
1651     /* ok, we can put the new Item in */
1652     hdsa->nItemCount++;
1653     lpDest = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
1654     TRACE("-- move dest=%p src=%p size=%d\n",
1655            lpDest, pSrc, hdsa->nItemSize);
1656     memmove (lpDest, pSrc, hdsa->nItemSize);
1657
1658     return nIndex;
1659 }
1660
1661
1662 /**************************************************************************
1663  * DSA_DeleteItem [COMCTL32.326]
1664  *
1665  * Deletes the specified item from the array.
1666  *
1667  * PARAMS
1668  *     hdsa   [I] pointer to the array control structure
1669  *     nIndex [I] index for the element to delete
1670  *
1671  * RETURNS
1672  *     Success: number of the deleted element
1673  *     Failure: -1
1674  */
1675 INT WINAPI DSA_DeleteItem (const HDSA hdsa, INT nIndex)
1676 {
1677     LPVOID lpDest,lpSrc;
1678     INT  nSize;
1679
1680     TRACE("(%p %d)\n", hdsa, nIndex);
1681
1682     if (!hdsa)
1683         return -1;
1684     if (nIndex < 0 || nIndex >= hdsa->nItemCount)
1685         return -1;
1686
1687     /* do we need to move ? */
1688     if (nIndex < hdsa->nItemCount - 1) {
1689         lpDest = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
1690         lpSrc = (char *) lpDest + hdsa->nItemSize;
1691         nSize = hdsa->nItemSize * (hdsa->nItemCount - nIndex - 1);
1692         TRACE("-- move dest=%p src=%p size=%d\n",
1693                lpDest, lpSrc, nSize);
1694         memmove (lpDest, lpSrc, nSize);
1695     }
1696
1697     hdsa->nItemCount--;
1698
1699     /* free memory ? */
1700     if ((hdsa->nMaxCount - hdsa->nItemCount) >= hdsa->nGrow) {
1701         nSize = hdsa->nItemSize * hdsa->nItemCount;
1702
1703         lpDest = ReAlloc (hdsa->pData, nSize);
1704         if (!lpDest)
1705             return -1;
1706
1707         hdsa->nMaxCount = hdsa->nItemCount;
1708         hdsa->pData = lpDest;
1709     }
1710
1711     return nIndex;
1712 }
1713
1714
1715 /**************************************************************************
1716  * DSA_DeleteAllItems [COMCTL32.327]
1717  *
1718  * Removes all items and reinitializes the array.
1719  *
1720  * PARAMS
1721  *     hdsa [I] pointer to the array control structure
1722  *
1723  * RETURNS
1724  *     Success: TRUE
1725  *     Failure: FALSE
1726  */
1727 BOOL WINAPI DSA_DeleteAllItems (const HDSA hdsa)
1728 {
1729     TRACE("(%p)\n", hdsa);
1730
1731     if (!hdsa)
1732         return FALSE;
1733     if (hdsa->pData && (!Free (hdsa->pData)))
1734         return FALSE;
1735
1736     hdsa->nItemCount = 0;
1737     hdsa->pData = NULL;
1738     hdsa->nMaxCount = 0;
1739
1740     return TRUE;
1741 }
1742
1743
1744 /**************************************************************************
1745  * DPA_Destroy [COMCTL32.329]
1746  *
1747  * Destroys a dynamic pointer array
1748  *
1749  * PARAMS
1750  *     hdpa [I] handle (pointer) to the pointer array
1751  *
1752  * RETURNS
1753  *     Success: TRUE
1754  *     Failure: FALSE
1755  */
1756 BOOL WINAPI DPA_Destroy (const HDPA hdpa)
1757 {
1758     TRACE("(%p)\n", hdpa);
1759
1760     if (!hdpa)
1761         return FALSE;
1762
1763     if (hdpa->ptrs && (!HeapFree (hdpa->hHeap, 0, hdpa->ptrs)))
1764         return FALSE;
1765
1766     return HeapFree (hdpa->hHeap, 0, hdpa);
1767 }
1768
1769
1770 /**************************************************************************
1771  * DPA_Grow [COMCTL32.330]
1772  *
1773  * Sets the growth amount.
1774  *
1775  * PARAMS
1776  *     hdpa  [I] handle (pointer) to the existing (source) pointer array
1777  *     nGrow [I] number of items by which the array grows when it's too small
1778  *
1779  * RETURNS
1780  *     Success: TRUE
1781  *     Failure: FALSE
1782  */
1783 BOOL WINAPI DPA_Grow (const HDPA hdpa, INT nGrow)
1784 {
1785     TRACE("(%p %d)\n", hdpa, nGrow);
1786
1787     if (!hdpa)
1788         return FALSE;
1789
1790     hdpa->nGrow = max(8, nGrow);
1791
1792     return TRUE;
1793 }
1794
1795
1796 /**************************************************************************
1797  * DPA_Clone [COMCTL32.331]
1798  *
1799  * Copies a pointer array to an other one or creates a copy
1800  *
1801  * PARAMS
1802  *     hdpa    [I] handle (pointer) to the existing (source) pointer array
1803  *     hdpaNew [O] handle (pointer) to the destination pointer array
1804  *
1805  * RETURNS
1806  *     Success: pointer to the destination pointer array.
1807  *     Failure: NULL
1808  *
1809  * NOTES
1810  *     - If the 'hdpaNew' is a NULL-Pointer, a copy of the source pointer
1811  *       array will be created and it's handle (pointer) is returned.
1812  *     - If 'hdpa' is a NULL-Pointer, the original implementation crashes,
1813  *       this implementation just returns NULL.
1814  */
1815 HDPA WINAPI DPA_Clone (const HDPA hdpa, const HDPA hdpaNew)
1816 {
1817     INT nNewItems, nSize;
1818     HDPA hdpaTemp;
1819
1820     if (!hdpa)
1821         return NULL;
1822
1823     TRACE("(%p %p)\n", hdpa, hdpaNew);
1824
1825     if (!hdpaNew) {
1826         /* create a new DPA */
1827         hdpaTemp = (HDPA)HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
1828                                     sizeof(*hdpaTemp));
1829         hdpaTemp->hHeap = hdpa->hHeap;
1830         hdpaTemp->nGrow = hdpa->nGrow;
1831     }
1832     else
1833         hdpaTemp = hdpaNew;
1834
1835     if (hdpaTemp->ptrs) {
1836         /* remove old pointer array */
1837         HeapFree (hdpaTemp->hHeap, 0, hdpaTemp->ptrs);
1838         hdpaTemp->ptrs = NULL;
1839         hdpaTemp->nItemCount = 0;
1840         hdpaTemp->nMaxCount = 0;
1841     }
1842
1843     /* create a new pointer array */
1844     nNewItems = hdpaTemp->nGrow *
1845                 ((INT)((hdpa->nItemCount - 1) / hdpaTemp->nGrow) + 1);
1846     nSize = nNewItems * sizeof(LPVOID);
1847     hdpaTemp->ptrs =
1848         (LPVOID*)HeapAlloc (hdpaTemp->hHeap, HEAP_ZERO_MEMORY, nSize);
1849     hdpaTemp->nMaxCount = nNewItems;
1850
1851     /* clone the pointer array */
1852     hdpaTemp->nItemCount = hdpa->nItemCount;
1853     memmove (hdpaTemp->ptrs, hdpa->ptrs,
1854              hdpaTemp->nItemCount * sizeof(LPVOID));
1855
1856     return hdpaTemp;
1857 }
1858
1859
1860 /**************************************************************************
1861  * DPA_GetPtr [COMCTL32.332]
1862  *
1863  * Retrieves a pointer from a dynamic pointer array
1864  *
1865  * PARAMS
1866  *     hdpa   [I] handle (pointer) to the pointer array
1867  *     nIndex [I] array index of the desired pointer
1868  *
1869  * RETURNS
1870  *     Success: pointer
1871  *     Failure: NULL
1872  */
1873 LPVOID WINAPI DPA_GetPtr (const HDPA hdpa, INT nIndex)
1874 {
1875     TRACE("(%p %d)\n", hdpa, nIndex);
1876
1877     if (!hdpa)
1878         return NULL;
1879     if (!hdpa->ptrs) {
1880         WARN("no pointer array.\n");
1881         return NULL;
1882     }
1883     if ((nIndex < 0) || (nIndex >= hdpa->nItemCount)) {
1884         WARN("not enough pointers in array (%d vs %d).\n",nIndex,hdpa->nItemCount);
1885         return NULL;
1886     }
1887
1888     TRACE("-- %p\n", hdpa->ptrs[nIndex]);
1889
1890     return hdpa->ptrs[nIndex];
1891 }
1892
1893
1894 /**************************************************************************
1895  * DPA_GetPtrIndex [COMCTL32.333]
1896  *
1897  * Retrieves the index of the specified pointer
1898  *
1899  * PARAMS
1900  *     hdpa   [I] handle (pointer) to the pointer array
1901  *     p      [I] pointer
1902  *
1903  * RETURNS
1904  *     Success: index of the specified pointer
1905  *     Failure: -1
1906  */
1907 INT WINAPI DPA_GetPtrIndex (const HDPA hdpa, LPVOID p)
1908 {
1909     INT i;
1910
1911     if (!hdpa || !hdpa->ptrs)
1912         return -1;
1913
1914     for (i = 0; i < hdpa->nItemCount; i++) {
1915         if (hdpa->ptrs[i] == p)
1916             return i;
1917     }
1918
1919     return -1;
1920 }
1921
1922
1923 /**************************************************************************
1924  * DPA_InsertPtr [COMCTL32.334]
1925  *
1926  * Inserts a pointer into a dynamic pointer array
1927  *
1928  * PARAMS
1929  *     hdpa [I] handle (pointer) to the array
1930  *     i    [I] array index
1931  *     p    [I] pointer to insert
1932  *
1933  * RETURNS
1934  *     Success: index of the inserted pointer
1935  *     Failure: -1
1936  */
1937 INT WINAPI DPA_InsertPtr (const HDPA hdpa, INT i, LPVOID p)
1938 {
1939     TRACE("(%p %d %p)\n", hdpa, i, p);
1940
1941     if (!hdpa || i < 0) return -1;
1942
1943     if (i >= 0x7fff)
1944         i = hdpa->nItemCount;
1945
1946     if (i >= hdpa->nItemCount)
1947         return DPA_SetPtr(hdpa, i, p) ? i : -1;
1948
1949     /* create empty spot at the end */
1950     if (!DPA_SetPtr(hdpa, hdpa->nItemCount, 0)) return -1;
1951     memmove (hdpa->ptrs + i + 1, hdpa->ptrs + i, (hdpa->nItemCount - i - 1) * sizeof(LPVOID));
1952     hdpa->ptrs[i] = p;
1953     return i;
1954 }
1955
1956 /**************************************************************************
1957  * DPA_SetPtr [COMCTL32.335]
1958  *
1959  * Sets a pointer in the pointer array
1960  *
1961  * PARAMS
1962  *     hdpa [I] handle (pointer) to the pointer array
1963  *     i    [I] index of the pointer that will be set
1964  *     p    [I] pointer to be set
1965  *
1966  * RETURNS
1967  *     Success: TRUE
1968  *     Failure: FALSE
1969  */
1970 BOOL WINAPI DPA_SetPtr (const HDPA hdpa, INT i, LPVOID p)
1971 {
1972     LPVOID *lpTemp;
1973
1974     TRACE("(%p %d %p)\n", hdpa, i, p);
1975
1976     if (!hdpa || i < 0 || i > 0x7fff)
1977         return FALSE;
1978
1979     if (hdpa->nItemCount <= i) {
1980         /* within the old array */
1981         if (hdpa->nMaxCount <= i) {
1982             /* resize the block of memory */
1983             INT nNewItems =
1984                 hdpa->nGrow * ((INT)(((i+1) - 1) / hdpa->nGrow) + 1);
1985             INT nSize = nNewItems * sizeof(LPVOID);
1986
1987             if (hdpa->ptrs)
1988                 lpTemp = (LPVOID*)HeapReAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY, hdpa->ptrs, nSize);
1989             else
1990                 lpTemp = (LPVOID*)HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY, nSize);
1991             
1992             if (!lpTemp)
1993                 return FALSE;
1994
1995             hdpa->nMaxCount = nNewItems;
1996             hdpa->ptrs = lpTemp;
1997         }
1998         hdpa->nItemCount = i+1;
1999     }
2000
2001     /* put the new entry in */
2002     hdpa->ptrs[i] = p;
2003
2004     return TRUE;
2005 }
2006
2007
2008 /**************************************************************************
2009  * DPA_DeletePtr [COMCTL32.336]
2010  *
2011  * Removes a pointer from the pointer array.
2012  *
2013  * PARAMS
2014  *     hdpa [I] handle (pointer) to the pointer array
2015  *     i    [I] index of the pointer that will be deleted
2016  *
2017  * RETURNS
2018  *     Success: deleted pointer
2019  *     Failure: NULL
2020  */
2021 LPVOID WINAPI DPA_DeletePtr (const HDPA hdpa, INT i)
2022 {
2023     LPVOID *lpDest, *lpSrc, lpTemp = NULL;
2024     INT  nSize;
2025
2026     TRACE("(%p %d)\n", hdpa, i);
2027
2028     if ((!hdpa) || i < 0 || i >= hdpa->nItemCount)
2029         return NULL;
2030
2031     lpTemp = hdpa->ptrs[i];
2032
2033     /* do we need to move ?*/
2034     if (i < hdpa->nItemCount - 1) {
2035         lpDest = hdpa->ptrs + i;
2036         lpSrc = lpDest + 1;
2037         nSize = (hdpa->nItemCount - i - 1) * sizeof(LPVOID);
2038         TRACE("-- move dest=%p src=%p size=%x\n",
2039                lpDest, lpSrc, nSize);
2040         memmove (lpDest, lpSrc, nSize);
2041     }
2042
2043     hdpa->nItemCount --;
2044
2045     /* free memory ?*/
2046     if ((hdpa->nMaxCount - hdpa->nItemCount) >= hdpa->nGrow) {
2047         INT nNewItems = max(hdpa->nGrow * 2, hdpa->nItemCount);
2048         nSize = nNewItems * sizeof(LPVOID);
2049         lpDest = (LPVOID)HeapReAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
2050                                       hdpa->ptrs, nSize);
2051         if (!lpDest)
2052             return NULL;
2053
2054         hdpa->nMaxCount = nNewItems;
2055         hdpa->ptrs = (LPVOID*)lpDest;
2056     }
2057
2058     return lpTemp;
2059 }
2060
2061
2062 /**************************************************************************
2063  * DPA_DeleteAllPtrs [COMCTL32.337]
2064  *
2065  * Removes all pointers and reinitializes the array.
2066  *
2067  * PARAMS
2068  *     hdpa [I] handle (pointer) to the pointer array
2069  *
2070  * RETURNS
2071  *     Success: TRUE
2072  *     Failure: FALSE
2073  */
2074 BOOL WINAPI DPA_DeleteAllPtrs (const HDPA hdpa)
2075 {
2076     TRACE("(%p)\n", hdpa);
2077
2078     if (!hdpa)
2079         return FALSE;
2080
2081     if (hdpa->ptrs && (!HeapFree (hdpa->hHeap, 0, hdpa->ptrs)))
2082         return FALSE;
2083
2084     hdpa->nItemCount = 0;
2085     hdpa->nMaxCount = hdpa->nGrow * 2;
2086     hdpa->ptrs = (LPVOID*)HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
2087                                      hdpa->nMaxCount * sizeof(LPVOID));
2088
2089     return TRUE;
2090 }
2091
2092
2093 /**************************************************************************
2094  * DPA_QuickSort [Internal]
2095  *
2096  * Ordinary quicksort (used by DPA_Sort).
2097  *
2098  * PARAMS
2099  *     lpPtrs     [I] pointer to the pointer array
2100  *     l          [I] index of the "left border" of the partition
2101  *     r          [I] index of the "right border" of the partition
2102  *     pfnCompare [I] pointer to the compare function
2103  *     lParam     [I] user defined value (3rd parameter in compare function)
2104  *
2105  * RETURNS
2106  *     NONE
2107  */
2108 static VOID DPA_QuickSort (LPVOID *lpPtrs, INT l, INT r,
2109                            PFNDPACOMPARE pfnCompare, LPARAM lParam)
2110 {
2111     INT m;
2112     LPVOID t;
2113
2114     TRACE("l=%i r=%i\n", l, r);
2115
2116     if (l==r)    /* one element is always sorted */
2117         return;
2118     if (r<l)     /* oops, got it in the wrong order */
2119         {
2120         DPA_QuickSort(lpPtrs, r, l, pfnCompare, lParam);
2121         return;
2122         }
2123     m = (l+r)/2; /* divide by two */
2124     DPA_QuickSort(lpPtrs, l, m, pfnCompare, lParam);
2125     DPA_QuickSort(lpPtrs, m+1, r, pfnCompare, lParam);
2126
2127     /* join the two sides */
2128     while( (l<=m) && (m<r) )
2129     {
2130         if(pfnCompare(lpPtrs[l],lpPtrs[m+1],lParam)>0)
2131         {
2132             t = lpPtrs[m+1];
2133             memmove(&lpPtrs[l+1],&lpPtrs[l],(m-l+1)*sizeof(lpPtrs[l]));
2134             lpPtrs[l] = t;
2135
2136             m++;
2137         }
2138         l++;
2139     }
2140 }
2141
2142
2143 /**************************************************************************
2144  * DPA_Sort [COMCTL32.338]
2145  *
2146  * Sorts a pointer array using a user defined compare function
2147  *
2148  * PARAMS
2149  *     hdpa       [I] handle (pointer) to the pointer array
2150  *     pfnCompare [I] pointer to the compare function
2151  *     lParam     [I] user defined value (3rd parameter of compare function)
2152  *
2153  * RETURNS
2154  *     Success: TRUE
2155  *     Failure: FALSE
2156  */
2157 BOOL WINAPI DPA_Sort (const HDPA hdpa, PFNDPACOMPARE pfnCompare, LPARAM lParam)
2158 {
2159     if (!hdpa || !pfnCompare)
2160         return FALSE;
2161
2162     TRACE("(%p %p 0x%lx)\n", hdpa, pfnCompare, lParam);
2163
2164     if ((hdpa->nItemCount > 1) && (hdpa->ptrs))
2165         DPA_QuickSort (hdpa->ptrs, 0, hdpa->nItemCount - 1,
2166                        pfnCompare, lParam);
2167
2168     return TRUE;
2169 }
2170
2171
2172 /**************************************************************************
2173  * DPA_Search [COMCTL32.339]
2174  *
2175  * Searches a pointer array for a specified pointer
2176  *
2177  * PARAMS
2178  *     hdpa       [I] handle (pointer) to the pointer array
2179  *     pFind      [I] pointer to search for
2180  *     nStart     [I] start index
2181  *     pfnCompare [I] pointer to the compare function
2182  *     lParam     [I] user defined value (3rd parameter of compare function)
2183  *     uOptions   [I] search options
2184  *
2185  * RETURNS
2186  *     Success: index of the pointer in the array.
2187  *     Failure: -1
2188  *
2189  * NOTES
2190  *     Binary search taken from R.Sedgewick "Algorithms in C"!
2191  *     Function is NOT tested!
2192  *     If something goes wrong, blame HIM not ME! (Eric Kohl)
2193  */
2194 INT WINAPI DPA_Search (const HDPA hdpa, LPVOID pFind, INT nStart,
2195                        PFNDPACOMPARE pfnCompare, LPARAM lParam, UINT uOptions)
2196 {
2197     if (!hdpa || !pfnCompare || !pFind)
2198         return -1;
2199
2200     TRACE("(%p %p %d %p 0x%08lx 0x%08x)\n",
2201            hdpa, pFind, nStart, pfnCompare, lParam, uOptions);
2202
2203     if (uOptions & DPAS_SORTED) {
2204         /* array is sorted --> use binary search */
2205         INT l, r, x, n;
2206         LPVOID *lpPtr;
2207
2208         TRACE("binary search\n");
2209
2210         l = (nStart == -1) ? 0 : nStart;
2211         r = hdpa->nItemCount - 1;
2212         lpPtr = hdpa->ptrs;
2213         while (r >= l) {
2214             x = (l + r) / 2;
2215             n = (pfnCompare)(pFind, lpPtr[x], lParam);
2216             if (n < 0)
2217                 r = x - 1;
2218             else
2219                 l = x + 1;
2220             if (n == 0) {
2221                 TRACE("-- ret=%d\n", n);
2222                 return n;
2223             }
2224         }
2225
2226         if (uOptions & DPAS_INSERTBEFORE) {
2227             if (r == -1) r = 0;
2228             TRACE("-- ret=%d\n", r);
2229             return r;
2230         }
2231
2232         if (uOptions & DPAS_INSERTAFTER) {
2233             TRACE("-- ret=%d\n", l);
2234             return l;
2235         }
2236     }
2237     else {
2238         /* array is not sorted --> use linear search */
2239         LPVOID *lpPtr;
2240         INT  nIndex;
2241
2242         TRACE("linear search\n");
2243
2244         nIndex = (nStart == -1)? 0 : nStart;
2245         lpPtr = hdpa->ptrs;
2246         for (; nIndex < hdpa->nItemCount; nIndex++) {
2247             if ((pfnCompare)(pFind, lpPtr[nIndex], lParam) == 0) {
2248                 TRACE("-- ret=%d\n", nIndex);
2249                 return nIndex;
2250             }
2251         }
2252     }
2253
2254     TRACE("-- not found: ret=-1\n");
2255     return -1;
2256 }
2257
2258
2259 /**************************************************************************
2260  * DPA_CreateEx [COMCTL32.340]
2261  *
2262  * Creates a dynamic pointer array using the specified size and heap.
2263  *
2264  * PARAMS
2265  *     nGrow [I] number of items by which the array grows when it is filled
2266  *     hHeap [I] handle to the heap where the array is stored
2267  *
2268  * RETURNS
2269  *     Success: handle (pointer) to the pointer array.
2270  *     Failure: NULL
2271  *
2272  * NOTES
2273  *     The DPA_ functions can be used to create and manipulate arrays of
2274  *     pointers.
2275  */
2276 HDPA WINAPI DPA_CreateEx (INT nGrow, HANDLE hHeap)
2277 {
2278     HDPA hdpa;
2279
2280     TRACE("(%d %p)\n", nGrow, hHeap);
2281
2282     if (hHeap)
2283         hdpa = (HDPA)HeapAlloc (hHeap, HEAP_ZERO_MEMORY, sizeof(*hdpa));
2284     else
2285         hdpa = Alloc (sizeof(*hdpa));
2286
2287     if (hdpa) {
2288         hdpa->nGrow = max(8, nGrow);
2289         hdpa->hHeap = hHeap ? hHeap : GetProcessHeap();
2290         hdpa->nMaxCount = hdpa->nGrow * 2;
2291         hdpa->ptrs = HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
2292                                 hdpa->nMaxCount * sizeof(LPVOID));
2293     }
2294
2295     TRACE("-- %p\n", hdpa);
2296
2297     return hdpa;
2298 }
2299
2300
2301 /**************************************************************************
2302  * DPA_Create [COMCTL32.328]
2303  *
2304  * Creates a dynamic pointer array.
2305  *
2306  * PARAMS
2307  *     nGrow [I] number of items by which the array grows when it is filled
2308  *
2309  * RETURNS
2310  *     Success: handle (pointer) to the pointer array.
2311  *     Failure: NULL
2312  *
2313  * NOTES
2314  *     The DPA_ functions can be used to create and manipulate arrays of
2315  *     pointers.
2316  */
2317 HDPA WINAPI DPA_Create (INT nGrow)
2318 {
2319     return DPA_CreateEx( nGrow, 0 );
2320 }
2321
2322
2323 /**************************************************************************
2324  * Notification functions
2325  */
2326
2327 typedef struct tagNOTIFYDATA
2328 {
2329     HWND hwndFrom;
2330     HWND hwndTo;
2331     DWORD  dwParam3;
2332     DWORD  dwParam4;
2333     DWORD  dwParam5;
2334     DWORD  dwParam6;
2335 } NOTIFYDATA, *LPNOTIFYDATA;
2336
2337
2338 /**************************************************************************
2339  * DoNotify [Internal]
2340  */
2341
2342 static LRESULT DoNotify (LPNOTIFYDATA lpNotify, UINT uCode, LPNMHDR lpHdr)
2343 {
2344     NMHDR nmhdr;
2345     LPNMHDR lpNmh = NULL;
2346     UINT idFrom = 0;
2347
2348     TRACE("(%p %p %d %p 0x%08lx)\n",
2349            lpNotify->hwndFrom, lpNotify->hwndTo, uCode, lpHdr,
2350            lpNotify->dwParam5);
2351
2352     if (!lpNotify->hwndTo)
2353         return 0;
2354
2355     if (lpNotify->hwndFrom == (HWND)-1) {
2356         lpNmh = lpHdr;
2357         idFrom = lpHdr->idFrom;
2358     }
2359     else {
2360         if (lpNotify->hwndFrom)
2361             idFrom = GetDlgCtrlID (lpNotify->hwndFrom);
2362
2363         lpNmh = (lpHdr) ? lpHdr : &nmhdr;
2364
2365         lpNmh->hwndFrom = lpNotify->hwndFrom;
2366         lpNmh->idFrom = idFrom;
2367         lpNmh->code = uCode;
2368     }
2369
2370     return SendMessageA (lpNotify->hwndTo, WM_NOTIFY, idFrom, (LPARAM)lpNmh);
2371 }
2372
2373
2374 /**************************************************************************
2375  * SendNotify [COMCTL32.341]
2376  *
2377  * Sends a WM_NOTIFY message to the specified window.
2378  *
2379  * PARAMS
2380  *     hwndTo   [I] Window to receive the message
2381  *     hwndFrom [I] Window that the message is from (see notes)
2382  *     uCode    [I] Notification code
2383  *     lpHdr    [I] The NMHDR and any additional information to send or NULL
2384  *
2385  * RETURNS
2386  *     Success: return value from notification
2387  *     Failure: 0
2388  *
2389  * NOTES
2390  *     If hwndFrom is -1 then the identifier of the control sending the
2391  *     message is taken from the NMHDR structure.
2392  *     If hwndFrom is not -1 then lpHdr can be NULL.
2393  */
2394 LRESULT WINAPI SendNotify (HWND hwndTo, HWND hwndFrom, UINT uCode, LPNMHDR lpHdr)
2395 {
2396     NOTIFYDATA notify;
2397
2398     TRACE("(%p %p %d %p)\n",
2399            hwndTo, hwndFrom, uCode, lpHdr);
2400
2401     notify.hwndFrom = hwndFrom;
2402     notify.hwndTo   = hwndTo;
2403     notify.dwParam5 = 0;
2404     notify.dwParam6 = 0;
2405
2406     return DoNotify (&notify, uCode, lpHdr);
2407 }
2408
2409
2410 /**************************************************************************
2411  * SendNotifyEx [COMCTL32.342]
2412  *
2413  * Sends a WM_NOTIFY message to the specified window.
2414  *
2415  * PARAMS
2416  *     hwndFrom [I] Window to receive the message
2417  *     hwndTo   [I] Window that the message is from
2418  *     uCode    [I] Notification code
2419  *     lpHdr    [I] The NMHDR and any additional information to send or NULL
2420  *     dwParam5 [I] Unknown
2421  *
2422  * RETURNS
2423  *     Success: return value from notification
2424  *     Failure: 0
2425  *
2426  * NOTES
2427  *     If hwndFrom is -1 then the identifier of the control sending the
2428  *     message is taken from the NMHDR structure.
2429  *     If hwndFrom is not -1 then lpHdr can be NULL.
2430  */
2431 LRESULT WINAPI SendNotifyEx (HWND hwndTo, HWND hwndFrom, UINT uCode,
2432                              LPNMHDR lpHdr, DWORD dwParam5)
2433 {
2434     NOTIFYDATA notify;
2435     HWND hwndNotify;
2436
2437     TRACE("(%p %p %d %p 0x%08lx)\n",
2438            hwndFrom, hwndTo, uCode, lpHdr, dwParam5);
2439
2440     hwndNotify = hwndTo;
2441     if (!hwndTo) {
2442         if (IsWindow (hwndFrom)) {
2443             hwndNotify = GetParent (hwndFrom);
2444             if (!hwndNotify)
2445                 return 0;
2446         }
2447     }
2448
2449     notify.hwndFrom = hwndFrom;
2450     notify.hwndTo   = hwndNotify;
2451     notify.dwParam5 = dwParam5;
2452     notify.dwParam6 = 0;
2453
2454     return DoNotify (&notify, uCode, lpHdr);
2455 }
2456
2457
2458
2459
2460 /**************************************************************************
2461  * DPA_EnumCallback [COMCTL32.385]
2462  *
2463  * Enumerates all items in a dynamic pointer array.
2464  *
2465  * PARAMS
2466  *     hdpa     [I] handle to the dynamic pointer array
2467  *     enumProc [I]
2468  *     lParam   [I]
2469  *
2470  * RETURNS
2471  *     none
2472  */
2473 VOID WINAPI DPA_EnumCallback (HDPA hdpa, PFNDPAENUMCALLBACK enumProc,
2474                               LPVOID lParam)
2475 {
2476     INT i;
2477
2478     TRACE("(%p %p %p)\n", hdpa, enumProc, lParam);
2479
2480     if (!hdpa)
2481         return;
2482     if (hdpa->nItemCount <= 0)
2483         return;
2484
2485     for (i = 0; i < hdpa->nItemCount; i++) {
2486         if ((enumProc)(hdpa->ptrs[i], lParam) == 0)
2487             return;
2488     }
2489
2490     return;
2491 }
2492
2493
2494 /**************************************************************************
2495  * DPA_DestroyCallback [COMCTL32.386]
2496  *
2497  * Enumerates all items in a dynamic pointer array and destroys it.
2498  *
2499  * PARAMS
2500  *     hdpa     [I] handle to the dynamic pointer array
2501  *     enumProc [I]
2502  *     lParam   [I]
2503  *
2504  * RETURNS
2505  *     none
2506  */
2507 void WINAPI DPA_DestroyCallback (HDPA hdpa, PFNDPAENUMCALLBACK enumProc,
2508                                  LPVOID lParam)
2509 {
2510     TRACE("(%p %p %p)\n", hdpa, enumProc, lParam);
2511
2512     DPA_EnumCallback (hdpa, enumProc, lParam);
2513     DPA_Destroy (hdpa);
2514 }
2515
2516
2517 /**************************************************************************
2518  * DSA_EnumCallback [COMCTL32.387]
2519  *
2520  * Enumerates all items in a dynamic storage array.
2521  *
2522  * PARAMS
2523  *     hdsa     [I] handle to the dynamic storage array
2524  *     enumProc [I]
2525  *     lParam   [I]
2526  *
2527  * RETURNS
2528  *     none
2529  */
2530 VOID WINAPI DSA_EnumCallback (HDSA hdsa, PFNDSAENUMCALLBACK enumProc,
2531                               LPVOID lParam)
2532 {
2533     INT i;
2534
2535     TRACE("(%p %p %p)\n", hdsa, enumProc, lParam);
2536
2537     if (!hdsa)
2538         return;
2539     if (hdsa->nItemCount <= 0)
2540         return;
2541
2542     for (i = 0; i < hdsa->nItemCount; i++) {
2543         LPVOID lpItem = DSA_GetItemPtr (hdsa, i);
2544         if ((enumProc)(lpItem, lParam) == 0)
2545             return;
2546     }
2547
2548     return;
2549 }
2550
2551
2552 /**************************************************************************
2553  * DSA_DestroyCallback [COMCTL32.388]
2554  *
2555  * Enumerates all items in a dynamic storage array and destroys it.
2556  *
2557  * PARAMS
2558  *     hdsa     [I] handle to the dynamic storage array
2559  *     enumProc [I]
2560  *     lParam   [I]
2561  *
2562  * RETURNS
2563  *     none
2564  */
2565 void WINAPI DSA_DestroyCallback (HDSA hdsa, PFNDSAENUMCALLBACK enumProc,
2566                                  LPVOID lParam)
2567 {
2568     TRACE("(%p %p %p)\n", hdsa, enumProc, lParam);
2569
2570     DSA_EnumCallback (hdsa, enumProc, lParam);
2571     DSA_Destroy (hdsa);
2572 }