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