comctl32undoc.c 34 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3
/*
 * Undocumented functions from COMCTL32.DLL
 *
4
 * Copyright 1998 Eric Kohl
Alexandre Julliard's avatar
Alexandre Julliard committed
5
 *           1998 Juergen Schmied <j.schmied@metronet.de>
6 7
 *           2000 Eric Kohl for CodeWeavers
 *
8 9 10 11 12 13 14 15 16 17 18 19
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
22 23 24 25 26 27 28
 * NOTES
 *     All of these functions are UNDOCUMENTED!! And I mean UNDOCUMENTED!!!!
 *     Do NOT rely on names or contents of undocumented structures and types!!!
 *     These functions are used by EXPLORER.EXE, IEXPLORE.EXE and
 *     COMCTL32.DLL (internally).
 *
 */
29 30
#include "config.h"
#include "wine/port.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
31

32
#include <stdarg.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
33 34
#include <string.h>
#include <ctype.h>
35
#include <limits.h>
36

37
#define COBJMACROS
38 39
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
40

41 42 43 44 45 46
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "winreg.h"
47 48
#include "commctrl.h"
#include "objbase.h"
49
#include "winerror.h"
50

51
#include "wine/unicode.h"
52 53
#include "comctl32.h"

54
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
55

56
WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
57

58 59
static const WCHAR strMRUList[] = { 'M','R','U','L','i','s','t',0 };

Alexandre Julliard's avatar
Alexandre Julliard committed
60 61 62
/**************************************************************************
 * Alloc [COMCTL32.71]
 *
63
 * Allocates memory block from the dll's private heap
Alexandre Julliard's avatar
Alexandre Julliard committed
64 65 66 67 68 69 70 71
 *
 * PARAMS
 *     dwSize [I] size of the allocated memory block
 *
 * RETURNS
 *     Success: pointer to allocated memory block
 *     Failure: NULL
 */
72
LPVOID WINAPI Alloc (DWORD dwSize)
Alexandre Julliard's avatar
Alexandre Julliard committed
73
{
74
    return LocalAlloc( LMEM_ZEROINIT, dwSize );
Alexandre Julliard's avatar
Alexandre Julliard committed
75 76 77 78 79 80 81
}


/**************************************************************************
 * ReAlloc [COMCTL32.72]
 *
 * Changes the size of an allocated memory block or allocates a memory
82
 * block using the dll's private heap.
Alexandre Julliard's avatar
Alexandre Julliard committed
83 84 85 86 87 88 89 90 91 92
 *
 * PARAMS
 *     lpSrc  [I] pointer to memory block which will be resized
 *     dwSize [I] new size of the memory block.
 *
 * RETURNS
 *     Success: pointer to the resized memory block
 *     Failure: NULL
 *
 * NOTES
93 94
 *     If lpSrc is a NULL-pointer, then ReAlloc allocates a memory
 *     block like Alloc.
Alexandre Julliard's avatar
Alexandre Julliard committed
95
 */
96
LPVOID WINAPI ReAlloc (LPVOID lpSrc, DWORD dwSize)
Alexandre Julliard's avatar
Alexandre Julliard committed
97 98
{
    if (lpSrc)
99
        return LocalReAlloc( lpSrc, dwSize, LMEM_ZEROINIT | LMEM_MOVEABLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
100
    else
101
        return LocalAlloc( LMEM_ZEROINIT, dwSize);
Alexandre Julliard's avatar
Alexandre Julliard committed
102 103 104 105 106
}


/**************************************************************************
 * Free [COMCTL32.73]
107
 *
108
 * Frees an allocated memory block from the dll's private heap.
Alexandre Julliard's avatar
Alexandre Julliard committed
109 110 111 112 113 114 115 116
 *
 * PARAMS
 *     lpMem [I] pointer to memory block which will be freed
 *
 * RETURNS
 *     Success: TRUE
 *     Failure: FALSE
 */
117
BOOL WINAPI Free (LPVOID lpMem)
Alexandre Julliard's avatar
Alexandre Julliard committed
118
{
119
    return !LocalFree( lpMem );
Alexandre Julliard's avatar
Alexandre Julliard committed
120 121 122 123 124 125 126
}


/**************************************************************************
 * GetSize [COMCTL32.74]
 *
 * Retrieves the size of the specified memory block from the dll's
127
 * private heap.
Alexandre Julliard's avatar
Alexandre Julliard committed
128 129 130 131 132 133 134 135
 *
 * PARAMS
 *     lpMem [I] pointer to an allocated memory block
 *
 * RETURNS
 *     Success: size of the specified memory block
 *     Failure: 0
 */
136
DWORD WINAPI GetSize (LPVOID lpMem)
Alexandre Julliard's avatar
Alexandre Julliard committed
137
{
138
    return LocalSize( lpMem );
Alexandre Julliard's avatar
Alexandre Julliard committed
139 140 141
}


Alexandre Julliard's avatar
Alexandre Julliard committed
142
/**************************************************************************
143
 * MRU-Functions  {COMCTL32}
Alexandre Julliard's avatar
Alexandre Julliard committed
144
 *
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
 * NOTES
 * The MRU-Api is a set of functions to manipulate lists of M.R.U. (Most Recently
 * Used) items. It is an undocumented Api that is used (at least) by the shell
 * and explorer to implement their recent documents feature.
 *
 * Since these functions are undocumented, they are unsupported by MS and
 * may change at any time.
 *
 * Internally, the list is implemented as a last in, last out list of items
 * persisted into the system registry under a caller chosen key. Each list
 * item is given a one character identifier in the Ascii range from 'a' to
 * '}'. A list of the identifiers in order from newest to oldest is stored
 * under the same key in a value named "MRUList".
 *
 * Items are re-ordered by changing the order of the values in the MRUList
 * value. When a new item is added, it becomes the new value of the oldest
 * identifier, and that identifier is moved to the front of the MRUList value.
 * 
 * Wine stores MRU-lists in the same registry format as Windows, so when
 * switching between the builtin and native comctl32.dll no problems or
 * incompatibilities should occur.
 *
 * The following undocumented structure is used to create an MRU-list:
 *|typedef INT (CALLBACK *MRUStringCmpFn)(LPCTSTR lhs, LPCTSTR rhs);
 *|typedef INT (CALLBACK *MRUBinaryCmpFn)(LPCVOID lhs, LPCVOID rhs, DWORD length);
 *|
171
 *|typedef struct tagMRUINFO
172 173
 *|{
 *|    DWORD   cbSize;
174 175
 *|    UINT    uMax;
 *|    UINT    fFlags;
176
 *|    HKEY    hKey;
177
 *|    LPTSTR  lpszSubKey;
178
 *|    PROC    lpfnCompare;
179
 *|} MRUINFO, *LPMRUINFO;
180 181
 *
 * MEMBERS
182 183 184
 *  cbSize      [I] The size of the MRUINFO structure. This must be set
 *                  to sizeof(MRUINFO) by the caller.
 *  uMax        [I] The maximum number of items allowed in the list. Because
185 186
 *                  of the limited number of identifiers, this should be set to
 *                  a value from 1 to 30 by the caller.
187
 *  fFlags      [I] If bit 0 is set, the list will be used to store binary
188 189 190 191 192 193 194 195
 *                  data, otherwise it is assumed to store strings. If bit 1
 *                  is set, every change made to the list will be reflected in
 *                  the registry immediately, otherwise changes will only be
 *                  written when the list is closed.
 *  hKey        [I] The registry key that the list should be written under.
 *                  This must be supplied by the caller.
 *  lpszSubKey  [I] A caller supplied name of a subkey under hKey to write
 *                  the list to. This may not be blank.
196
 *  lpfnCompare [I] A caller supplied comparison function, which may be either
197 198 199 200 201 202 203 204 205 206
 *                  an MRUStringCmpFn if dwFlags does not have bit 0 set, or a
 *                  MRUBinaryCmpFn otherwise.
 *
 * FUNCTIONS
 *  - Create an MRU-list with CreateMRUList() or CreateMRUListLazy().
 *  - Add items to an MRU-list with AddMRUString() or AddMRUData().
 *  - Remove items from an MRU-list with DelMRUString().
 *  - Find data in an MRU-list with FindMRUString() or FindMRUData().
 *  - Iterate through an MRU-list with EnumMRUList().
 *  - Free an MRU-list with FreeMRUList().
Alexandre Julliard's avatar
Alexandre Julliard committed
207 208
 */

209 210 211 212
typedef INT (CALLBACK *MRUStringCmpFnA)(LPCSTR lhs, LPCSTR rhs);
typedef INT (CALLBACK *MRUStringCmpFnW)(LPCWSTR lhs, LPCWSTR rhs);
typedef INT (CALLBACK *MRUBinaryCmpFn)(LPCVOID lhs, LPCVOID rhs, DWORD length);

213
typedef struct tagMRUINFOA
Alexandre Julliard's avatar
Alexandre Julliard committed
214
{
215
    DWORD  cbSize;
216 217
    UINT   uMax;
    UINT   fFlags;
218
    HKEY   hKey;
219
    LPSTR  lpszSubKey;
220 221 222 223 224
    union
    {
        MRUStringCmpFnA string_cmpfn;
        MRUBinaryCmpFn  binary_cmpfn;
    } u;
225
} MRUINFOA, *LPMRUINFOA;
226

227
typedef struct tagMRUINFOW
228
{
229
    DWORD   cbSize;
230 231
    UINT    uMax;
    UINT    fFlags;
232
    HKEY    hKey;
233
    LPWSTR  lpszSubKey;
234 235 236 237 238
    union
    {
        MRUStringCmpFnW string_cmpfn;
        MRUBinaryCmpFn  binary_cmpfn;
    } u;
239
} MRUINFOW, *LPMRUINFOW;
240

241 242 243 244
/* MRUINFO.fFlags */
#define MRU_STRING     0 /* list will contain strings */
#define MRU_BINARY     1 /* list will contain binary data */
#define MRU_CACHEWRITE 2 /* only save list order to reg. is FreeMRUList */
245 246 247 248 249 250 251 252 253

/* If list is a string list lpfnCompare has the following prototype
 * int CALLBACK MRUCompareString(LPCSTR s1, LPCSTR s2)
 * for binary lists the prototype is
 * int CALLBACK MRUCompareBinary(LPCVOID data1, LPCVOID data2, DWORD cbData)
 * where cbData is the no. of bytes to compare.
 * Need to check what return value means identical - 0?
 */

254
typedef struct tagWINEMRUITEM
255
{
256 257 258 259 260 261 262 263 264 265
    DWORD          size;        /* size of data stored               */
    DWORD          itemFlag;    /* flags                             */
    BYTE           datastart;
} WINEMRUITEM, *LPWINEMRUITEM;

/* itemFlag */
#define WMRUIF_CHANGED   0x0001 /* this dataitem changed             */

typedef struct tagWINEMRULIST
{
266
    MRUINFOW       extview;     /* original create information       */
267
    BOOL           isUnicode;   /* is compare fn Unicode */
268 269
    DWORD          wineFlags;   /* internal flags                    */
    DWORD          cursize;     /* current size of realMRU           */
270
    LPWSTR         realMRU;     /* pointer to string of index names  */
271 272 273 274 275 276
    LPWINEMRUITEM  *array;      /* array of pointers to data         */
                                /* in 'a' to 'z' order               */
} WINEMRULIST, *LPWINEMRULIST;

/* wineFlags */
#define WMRUF_CHANGED  0x0001   /* MRU list has changed              */
277

278
/**************************************************************************
Robert Shearman's avatar
Robert Shearman committed
279
 *              MRU_SaveChanged (internal)
280
 *
281
 * Local MRU saving code
282
 */
283
static void MRU_SaveChanged ( LPWINEMRULIST mp )
284
{
285
    UINT i, err;
286
    HKEY newkey;
287
    WCHAR realname[2];
288 289 290 291 292 293
    LPWINEMRUITEM witem;

    /* or should we do the following instead of RegOpenKeyEx:
     */

    /* open the sub key */
294
    if ((err = RegOpenKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
295
			      0, KEY_WRITE, &newkey))) {
296
	/* not present - what to do ??? */
297
	ERR("Could not open key, error=%d, attempting to create\n",
298
	    err);
299
	if ((err = RegCreateKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
300
				    0,
301
				    NULL,
302 303 304 305 306 307
				    REG_OPTION_NON_VOLATILE,
				    KEY_READ | KEY_WRITE,
				    0,
				    &newkey,
				    0))) {
	    ERR("failed to create key /%s/, err=%d\n",
308
		debugstr_w(mp->extview.lpszSubKey), err);
309 310 311 312 313
	    return;
	}
    }
    if (mp->wineFlags & WMRUF_CHANGED) {
	mp->wineFlags &= ~WMRUF_CHANGED;
314 315
	err = RegSetValueExW(newkey, strMRUList, 0, REG_SZ, (LPBYTE)mp->realMRU,
			     (strlenW(mp->realMRU) + 1)*sizeof(WCHAR));
316 317 318
	if (err) {
	    ERR("error saving MRUList, err=%d\n", err);
	}
319
	TRACE("saving MRUList=/%s/\n", debugstr_w(mp->realMRU));
320 321 322 323 324 325 326
    }
    realname[1] = 0;
    for(i=0; i<mp->cursize; i++) {
	witem = mp->array[i];
	if (witem->itemFlag & WMRUIF_CHANGED) {
	    witem->itemFlag &= ~WMRUIF_CHANGED;
	    realname[0] = 'a' + i;
327
	    err = RegSetValueExW(newkey, realname, 0,
328
				 (mp->extview.fFlags & MRU_BINARY) ?
329 330 331
				 REG_BINARY : REG_SZ,
				 &witem->datastart, witem->size);
	    if (err) {
332
		ERR("error saving /%s/, err=%d\n", debugstr_w(realname), err);
333
	    }
334
            TRACE("saving value for name /%s/ size=%d\n",
335
		  debugstr_w(realname), witem->size);
336 337 338 339
	}
    }
    RegCloseKey( newkey );
}
Alexandre Julliard's avatar
Alexandre Julliard committed
340

341
/**************************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
342
 *              FreeMRUList [COMCTL32.152]
343
 *
Robert Shearman's avatar
Robert Shearman committed
344 345
 * Frees a most-recently-used items list.
 *
346 347 348
 * PARAMS
 *     hMRUList [I] Handle to list.
 *
Robert Shearman's avatar
Robert Shearman committed
349
 * RETURNS
350
 *     Nothing.
351
 */
352
void WINAPI FreeMRUList (HANDLE hMRUList)
353
{
354
    LPWINEMRULIST mp = hMRUList;
355
    UINT i;
356

357 358 359 360
    TRACE("(%p)\n", hMRUList);
    if (!hMRUList)
        return;

361 362 363 364 365
    if (mp->wineFlags & WMRUF_CHANGED) {
	/* need to open key and then save the info */
	MRU_SaveChanged( mp );
    }

366
    for(i=0; i<mp->extview.uMax; i++)
367 368
        Free(mp->array[i]);

369 370
    Free(mp->realMRU);
    Free(mp->array);
371
    Free(mp->extview.lpszSubKey);
372
    Free(mp);
373 374 375 376 377
}


/**************************************************************************
 *                  FindMRUData [COMCTL32.169]
378
 *
379 380 381 382 383 384 385 386 387 388 389 390 391
 * Searches binary list for item that matches lpData of length cbData.
 * Returns position in list order 0 -> MRU and if lpRegNum != NULL then value
 * corresponding to item's reg. name will be stored in it ('a' -> 0).
 *
 * PARAMS
 *    hList [I] list handle
 *    lpData [I] data to find
 *    cbData [I] length of data
 *    lpRegNum [O] position in registry (maybe NULL)
 *
 * RETURNS
 *    Position in list 0 -> MRU.  -1 if item not found.
 */
392 393
INT WINAPI FindMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData,
                        LPINT lpRegNum)
394
{
395
    const WINEMRULIST *mp = hList;
396 397
    INT ret;
    UINT i;
398
    LPSTR dataA = NULL;
399

400
    if (!mp || !mp->extview.u.string_cmpfn)
401
	return -1;
402

403
    if(!(mp->extview.fFlags & MRU_BINARY) && !mp->isUnicode) {
404 405
        DWORD len = WideCharToMultiByte(CP_ACP, 0, lpData, -1,
					NULL, 0, NULL, NULL);
406
	dataA = Alloc(len);
407 408 409
	WideCharToMultiByte(CP_ACP, 0, lpData, -1, dataA, len, NULL, NULL);
    }

410
    for(i=0; i<mp->cursize; i++) {
411
	if (mp->extview.fFlags & MRU_BINARY) {
412
	    if (!mp->extview.u.binary_cmpfn(lpData, &mp->array[i]->datastart, cbData))
413 414 415
		break;
	}
	else {
416
	    if(mp->isUnicode) {
417
	        if (!mp->extview.u.string_cmpfn(lpData, (LPWSTR)&mp->array[i]->datastart))
418 419 420 421 422
		    break;
	    } else {
	        DWORD len = WideCharToMultiByte(CP_ACP, 0,
						(LPWSTR)&mp->array[i]->datastart, -1,
						NULL, 0, NULL, NULL);
423
		LPSTR itemA = Alloc(len);
424 425 426 427
		INT cmp;
		WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&mp->array[i]->datastart, -1,
				    itemA, len, NULL, NULL);

428
	        cmp = mp->extview.u.string_cmpfn((LPWSTR)dataA, (LPWSTR)itemA);
429
		Free(itemA);
430 431 432
		if(!cmp)
		    break;
	    }
433 434
	}
    }
435
    Free(dataA);
436 437 438 439 440 441
    if (i < mp->cursize)
	ret = i;
    else
	ret = -1;
    if (lpRegNum && (ret != -1))
	*lpRegNum = 'a' + i;
442

443
    TRACE("(%p, %p, %d, %p) returning %d\n",
444
	   hList, lpData, cbData, lpRegNum, ret);
445

446
    return ret;
447 448 449
}


450 451
/**************************************************************************
 *              AddMRUData [COMCTL32.167]
452
 *
Andreas Mohr's avatar
Andreas Mohr committed
453
 * Add item to MRU binary list.  If item already exists in list then it is
454 455 456 457 458 459 460 461 462 463 464 465
 * simply moved up to the top of the list and not added again.  If list is
 * full then the least recently used item is removed to make room.
 *
 * PARAMS
 *     hList [I] Handle to list.
 *     lpData [I] ptr to data to add.
 *     cbData [I] no. of bytes of data.
 *
 * RETURNS
 *     No. corresponding to registry name where value is stored 'a' -> 0 etc.
 *     -1 on error.
 */
466
INT WINAPI AddMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData)
Alexandre Julliard's avatar
Alexandre Julliard committed
467
{
468
    LPWINEMRULIST mp = hList;
469
    LPWINEMRUITEM witem;
470
    INT i, replace;
471

472 473
    if ((replace = FindMRUData (hList, lpData, cbData, NULL)) >= 0) {
        /* Item exists, just move it to the front */
474
        LPWSTR pos = strchrW(mp->realMRU, replace + 'a');
475 476 477 478 479 480 481
        while (pos > mp->realMRU)
        {
            pos[0] = pos[-1];
            pos--;
        }
    }
    else {
482
	/* either add a new entry or replace oldest */
483
	if (mp->cursize < mp->extview.uMax) {
484 485 486 487 488 489 490
	    /* Add in a new item */
	    replace = mp->cursize;
	    mp->cursize++;
	}
	else {
	    /* get the oldest entry and replace data */
	    replace = mp->realMRU[mp->cursize - 1] - 'a';
491
	    Free(mp->array[replace]);
492
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
493

494 495 496 497 498
        /* Allocate space for new item and move in the data */
        mp->array[replace] = witem = Alloc(cbData + sizeof(WINEMRUITEM));
        witem->itemFlag |= WMRUIF_CHANGED;
        witem->size = cbData;
        memcpy( &witem->datastart, lpData, cbData);
499

500 501 502
        /* now rotate MRU list */
        for(i=mp->cursize-1; i>=1; i--)
            mp->realMRU[i] = mp->realMRU[i-1];
503
    }
504 505 506

    /* The new item gets the front spot */
    mp->wineFlags |= WMRUF_CHANGED;
507
    mp->realMRU[0] = replace + 'a';
508

509
    TRACE("(%p, %p, %d) adding data, /%c/ now most current\n",
510
          hList, lpData, cbData, replace+'a');
511

512
    if (!(mp->extview.fFlags & MRU_CACHEWRITE)) {
513 514 515 516
	/* save changed stuff right now */
	MRU_SaveChanged( mp );
    }

517
    return replace;
Alexandre Julliard's avatar
Alexandre Julliard committed
518 519
}

520
/**************************************************************************
521
 *              AddMRUStringW [COMCTL32.401]
522
 *
523
 * Add an item to an MRU string list.
524 525
 *
 * PARAMS
526 527
 *     hList      [I] Handle to list.
 *     lpszString [I] The string to add.
528 529
 *
 * RETURNS
530 531 532 533 534 535 536 537 538 539 540 541 542 543
 *   Success: The number corresponding to the registry name where the string
 *            has been stored (0 maps to 'a', 1 to 'b' and so on).
 *   Failure: -1, if hList is NULL or memory allocation fails. If lpszString
 *            is invalid, the function returns 0, and GetLastError() returns
 *            ERROR_INVALID_PARAMETER. The last error value is set only in
 *            this case.
 *
 * NOTES
 *  -If lpszString exists in the list already, it is moved to the top of the
 *   MRU list (it is not duplicated).
 *  -If the list is full the least recently used list entry is replaced with
 *   lpszString.
 *  -If this function returns 0 you should check the last error value to
 *   ensure the call really succeeded.
544
 */
545
INT WINAPI AddMRUStringW(HANDLE hList, LPCWSTR lpszString)
546
{
547
    TRACE("(%p,%s)\n", hList, debugstr_w(lpszString));
548

549 550 551 552 553 554 555 556 557 558 559
    if (!hList)
        return -1;

    if (!lpszString || IsBadStringPtrW(lpszString, -1))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0;
    }

    return AddMRUData(hList, lpszString,
                      (strlenW(lpszString) + 1) * sizeof(WCHAR));
560 561 562 563
}

/**************************************************************************
 *              AddMRUStringA [COMCTL32.153]
Robert Shearman's avatar
Robert Shearman committed
564 565
 *
 * See AddMRUStringW.
566
 */
567
INT WINAPI AddMRUStringA(HANDLE hList, LPCSTR lpszString)
568
{
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
    DWORD len;
    LPWSTR stringW;
    INT ret;

    TRACE("(%p,%s)\n", hList, debugstr_a(lpszString));

    if (!hList)
        return -1;

    if (IsBadStringPtrA(lpszString, -1))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
	return 0;
    }

    len = MultiByteToWideChar(CP_ACP, 0, lpszString, -1, NULL, 0) * sizeof(WCHAR);
    stringW = Alloc(len);
    if (!stringW)
        return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
588

589
    MultiByteToWideChar(CP_ACP, 0, lpszString, -1, stringW, len/sizeof(WCHAR));
590 591 592
    ret = AddMRUData(hList, stringW, len);
    Free(stringW);
    return ret;
593
}
Alexandre Julliard's avatar
Alexandre Julliard committed
594

595 596 597 598 599 600 601 602 603 604
/**************************************************************************
 *              DelMRUString [COMCTL32.156]
 *
 * Removes item from either string or binary list (despite its name)
 *
 * PARAMS
 *    hList [I] list handle
 *    nItemPos [I] item position to remove 0 -> MRU
 *
 * RETURNS
Andreas Mohr's avatar
Andreas Mohr committed
605
 *    TRUE if successful, FALSE if nItemPos is out of range.
606
 */
607
BOOL WINAPI DelMRUString(HANDLE hList, INT nItemPos)
608
{
609
    FIXME("(%p, %d): stub\n", hList, nItemPos);
610
    return TRUE;
611
}
Alexandre Julliard's avatar
Alexandre Julliard committed
612

613 614
/**************************************************************************
 *                  FindMRUStringW [COMCTL32.402]
Robert Shearman's avatar
Robert Shearman committed
615 616
 *
 * See FindMRUStringA.
617
 */
618
INT WINAPI FindMRUStringW (HANDLE hList, LPCWSTR lpszString, LPINT lpRegNum)
619
{
620 621
  return FindMRUData(hList, lpszString,
                     (lstrlenW(lpszString) + 1) * sizeof(WCHAR), lpRegNum);
622 623
}

624 625
/**************************************************************************
 *                  FindMRUStringA [COMCTL32.155]
626
 *
627 628 629 630 631 632 633 634 635 636 637 638
 * Searches string list for item that matches lpszString.
 * Returns position in list order 0 -> MRU and if lpRegNum != NULL then value
 * corresponding to item's reg. name will be stored in it ('a' -> 0).
 *
 * PARAMS
 *    hList [I] list handle
 *    lpszString [I] string to find
 *    lpRegNum [O] position in registry (maybe NULL)
 *
 * RETURNS
 *    Position in list 0 -> MRU.  -1 if item not found.
 */
639
INT WINAPI FindMRUStringA (HANDLE hList, LPCSTR lpszString, LPINT lpRegNum)
640
{
641
    DWORD len = MultiByteToWideChar(CP_ACP, 0, lpszString, -1, NULL, 0);
642
    LPWSTR stringW = Alloc(len * sizeof(WCHAR));
643 644 645 646
    INT ret;

    MultiByteToWideChar(CP_ACP, 0, lpszString, -1, stringW, len);
    ret = FindMRUData(hList, stringW, len * sizeof(WCHAR), lpRegNum);
647
    Free(stringW);
648
    return ret;
649
}
Alexandre Julliard's avatar
Alexandre Julliard committed
650

651
/*************************************************************************
652
 *                 create_mru_list (internal)
653
 */
654
static HANDLE create_mru_list(LPWINEMRULIST mp)
Alexandre Julliard's avatar
Alexandre Julliard committed
655
{
656
    UINT i, err;
657 658
    HKEY newkey;
    DWORD datasize, dwdisp;
659
    WCHAR realname[2];
660 661
    LPWINEMRUITEM witem;
    DWORD type;
Alexandre Julliard's avatar
Alexandre Julliard committed
662

663
    /* get space to save indices that will turn into names
664 665
     * but in order of most to least recently used
     */
666
    mp->realMRU = Alloc((mp->extview.uMax + 2) * sizeof(WCHAR));
667 668 669 670

    /* get space to save pointers to actual data in order of
     * 'a' to 'z' (0 to n).
     */
671
    mp->array = Alloc(mp->extview.uMax * sizeof(LPVOID));
672 673

    /* open the sub key */
674
    if ((err = RegCreateKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
675
			        0,
676
				NULL,
677
				REG_OPTION_NON_VOLATILE,
678 679 680 681 682
				KEY_READ | KEY_WRITE,
                                0,
				&newkey,
				&dwdisp))) {
	/* error - what to do ??? */
683
	ERR("(%u %u %x %p %s %p): Could not open key, error=%d\n",
684
	    mp->extview.cbSize, mp->extview.uMax, mp->extview.fFlags,
Frank Richter's avatar
Frank Richter committed
685
	    mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey),
686
            mp->extview.u.string_cmpfn, err);
687 688
	return 0;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
689

690 691
    /* get values from key 'MRUList' */
    if (newkey) {
692
	datasize = (mp->extview.uMax + 1) * sizeof(WCHAR);
693 694
	if((err=RegQueryValueExW( newkey, strMRUList, 0, &type,
				  (LPBYTE)mp->realMRU, &datasize))) {
695 696 697 698
	    /* not present - set size to 1 (will become 0 later) */
	    datasize = 1;
	    *mp->realMRU = 0;
	}
699 700
        else
            datasize /= sizeof(WCHAR);
701

702
	TRACE("MRU list = %s, datasize = %d\n", debugstr_w(mp->realMRU), datasize);
Alexandre Julliard's avatar
Alexandre Julliard committed
703

704 705
	mp->cursize = datasize - 1;
	/* datasize now has number of items in the MRUList */
Alexandre Julliard's avatar
Alexandre Julliard committed
706

707 708 709 710
	/* get actual values for each entry */
	realname[1] = 0;
	for(i=0; i<mp->cursize; i++) {
	    realname[0] = 'a' + i;
711
	    if(RegQueryValueExW( newkey, realname, 0, &type, 0, &datasize)) {
712
		/* not present - what to do ??? */
713
		ERR("Key %s not found 1\n", debugstr_w(realname));
714
	    }
715
	    mp->array[i] = witem = Alloc(datasize + sizeof(WINEMRUITEM));
716
	    witem->size = datasize;
717
	    if(RegQueryValueExW( newkey, realname, 0, &type,
718 719
				 &witem->datastart, &datasize)) {
		/* not present - what to do ??? */
720
		ERR("Key %s not found 2\n", debugstr_w(realname));
721 722 723 724 725 726 727
	    }
	}
	RegCloseKey( newkey );
    }
    else
	mp->cursize = 0;

728
    TRACE("(%u %u %x %p %s %p): Current Size = %d\n",
729
	  mp->extview.cbSize, mp->extview.uMax, mp->extview.fFlags,
Frank Richter's avatar
Frank Richter committed
730
	  mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey),
731
	  mp->extview.u.string_cmpfn, mp->cursize);
732
    return mp;
Alexandre Julliard's avatar
Alexandre Julliard committed
733 734
}

735
/**************************************************************************
736
 *                  CreateMRUListLazyW [COMCTL32.404]
Robert Shearman's avatar
Robert Shearman committed
737 738
 *
 * See CreateMRUListLazyA.
739
 */
740
HANDLE WINAPI CreateMRUListLazyW (const MRUINFOW *infoW, DWORD dwParam2,
741
                                  DWORD dwParam3, DWORD dwParam4)
742 743 744
{
    LPWINEMRULIST mp;

745
    /* Native does not check for a NULL lpcml */
746
    if (!infoW->hKey || IsBadStringPtrW(infoW->lpszSubKey, -1))
747
	return NULL;
748

749
    mp = Alloc(sizeof(WINEMRULIST));
750 751 752
    memcpy(&mp->extview, infoW, sizeof(MRUINFOW));
    mp->extview.lpszSubKey = Alloc((strlenW(infoW->lpszSubKey) + 1) * sizeof(WCHAR));
    strcpyW(mp->extview.lpszSubKey, infoW->lpszSubKey);
753 754
    mp->isUnicode = TRUE;

755
    return create_mru_list(mp);
756 757 758 759
}

/**************************************************************************
 *                  CreateMRUListLazyA [COMCTL32.157]
Robert Shearman's avatar
Robert Shearman committed
760 761 762 763 764 765 766 767 768 769 770
 *
 * Creates a most-recently-used list.
 *
 * PARAMS
 *     lpcml    [I] ptr to CREATEMRULIST structure.
 *     dwParam2 [I] Unknown
 *     dwParam3 [I] Unknown
 *     dwParam4 [I] Unknown
 *
 * RETURNS
 *     Handle to MRU list.
771
 */
772
HANDLE WINAPI CreateMRUListLazyA (const MRUINFOA *lpcml, DWORD dwParam2,
773
                                  DWORD dwParam3, DWORD dwParam4)
774 775 776 777
{
    LPWINEMRULIST mp;
    DWORD len;

778
    /* Native does not check for a NULL lpcml */
779

780
    if (lpcml->cbSize != sizeof(MRUINFOA) || !lpcml->hKey ||
781
        IsBadStringPtrA(lpcml->lpszSubKey, -1))
782 783
	return 0;

784
    mp = Alloc(sizeof(WINEMRULIST));
785
    memcpy(&mp->extview, lpcml, sizeof(MRUINFOA));
786
    len = MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1, NULL, 0);
787
    mp->extview.lpszSubKey = Alloc(len * sizeof(WCHAR));
788
    MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1,
789
			mp->extview.lpszSubKey, len);
790
    mp->isUnicode = FALSE;
791
    return create_mru_list(mp);
792 793 794 795 796
}

/**************************************************************************
 *              CreateMRUListW [COMCTL32.400]
 *
Robert Shearman's avatar
Robert Shearman committed
797
 * See CreateMRUListA.
798
 */
799
HANDLE WINAPI CreateMRUListW (const MRUINFOW *infoW)
800
{
801
    return CreateMRUListLazyW(infoW, 0, 0, 0);
802 803 804 805
}

/**************************************************************************
 *              CreateMRUListA [COMCTL32.151]
Robert Shearman's avatar
Robert Shearman committed
806 807 808 809 810 811 812 813
 *
 * Creates a most-recently-used list.
 *
 * PARAMS
 *     lpcml [I] ptr to CREATEMRULIST structure.
 *
 * RETURNS
 *     Handle to MRU list.
814
 */
815
HANDLE WINAPI CreateMRUListA (const MRUINFOA *lpcml)
816 817 818 819 820 821 822 823
{
     return CreateMRUListLazyA (lpcml, 0, 0, 0);
}


/**************************************************************************
 *                EnumMRUListW [COMCTL32.403]
 *
824
 * Enumerate item in a most-recently-used list
825 826 827 828 829 830 831 832 833 834 835 836
 *
 * PARAMS
 *    hList [I] list handle
 *    nItemPos [I] item position to enumerate
 *    lpBuffer [O] buffer to receive item
 *    nBufferSize [I] size of buffer
 *
 * RETURNS
 *    For binary lists specifies how many bytes were copied to buffer, for
 *    string lists specifies full length of string.  Enumerating past the end
 *    of list returns -1.
 *    If lpBuffer == NULL or nItemPos is -ve return value is no. of items in
837
 *    the list.
838
 */
839 840
INT WINAPI EnumMRUListW (HANDLE hList, INT nItemPos, LPVOID lpBuffer,
                         DWORD nBufferSize)
841
{
842
    const WINEMRULIST *mp = hList;
843
    const WINEMRUITEM *witem;
844 845
    INT desired, datasize;

846
    if (!mp) return -1;
847
    if ((nItemPos < 0) || !lpBuffer) return mp->cursize;
848
    if (nItemPos >= mp->cursize) return -1;
849 850 851 852
    desired = mp->realMRU[nItemPos];
    desired -= 'a';
    TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired);
    witem = mp->array[desired];
853
    datasize = min( witem->size, nBufferSize );
854
    memcpy( lpBuffer, &witem->datastart, datasize);
855
    TRACE("(%p, %d, %p, %d): returning len=%d\n",
856 857
	  hList, nItemPos, lpBuffer, nBufferSize, datasize);
    return datasize;
858
}
Alexandre Julliard's avatar
Alexandre Julliard committed
859

860 861
/**************************************************************************
 *                EnumMRUListA [COMCTL32.154]
862
 *
Robert Shearman's avatar
Robert Shearman committed
863
 * See EnumMRUListW.
864
 */
865 866
INT WINAPI EnumMRUListA (HANDLE hList, INT nItemPos, LPVOID lpBuffer,
                         DWORD nBufferSize)
867
{
868
    const WINEMRULIST *mp = hList;
869 870 871 872
    LPWINEMRUITEM witem;
    INT desired, datasize;
    DWORD lenA;

873
    if (!mp) return -1;
874
    if ((nItemPos < 0) || !lpBuffer) return mp->cursize;
875
    if (nItemPos >= mp->cursize) return -1;
876 877 878 879
    desired = mp->realMRU[nItemPos];
    desired -= 'a';
    TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired);
    witem = mp->array[desired];
880
    if(mp->extview.fFlags & MRU_BINARY) {
881
        datasize = min( witem->size, nBufferSize );
882 883 884 885
	memcpy( lpBuffer, &witem->datastart, datasize);
    } else {
        lenA = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1,
				   NULL, 0, NULL, NULL);
886
	datasize = min( lenA, nBufferSize );
887 888
	WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1,
			    lpBuffer, datasize, NULL, NULL);
889 890
        ((char *)lpBuffer)[ datasize - 1 ] = '\0';
        datasize = lenA - 1;
891
    }
892
    TRACE("(%p, %d, %p, %d): returning len=%d\n",
893 894 895
	  hList, nItemPos, lpBuffer, nBufferSize, datasize);
    return datasize;
}
896

897 898 899 900 901 902 903 904 905 906 907 908 909 910
/**************************************************************************
 * Str_GetPtrWtoA [internal]
 *
 * Converts a unicode string into a multi byte string
 *
 * PARAMS
 *     lpSrc   [I] Pointer to the unicode source string
 *     lpDest  [O] Pointer to caller supplied storage for the multi byte string
 *     nMaxLen [I] Size, in bytes, of the destination buffer
 *
 * RETURNS
 *     Length, in bytes, of the converted string.
 */

911
INT Str_GetPtrWtoA (LPCWSTR lpSrc, LPSTR lpDest, INT nMaxLen)
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937
{
    INT len;

    TRACE("(%s %p %d)\n", debugstr_w(lpSrc), lpDest, nMaxLen);

    if (!lpDest && lpSrc)
	return WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, 0, 0, NULL, NULL);

    if (nMaxLen == 0)
	return 0;

    if (lpSrc == NULL) {
	lpDest[0] = '\0';
	return 0;
    }

    len = WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, 0, 0, NULL, NULL);
    if (len >= nMaxLen)
	len = nMaxLen - 1;

    WideCharToMultiByte(CP_ACP, 0, lpSrc, -1, lpDest, len, NULL, NULL);
    lpDest[len] = '\0';

    return len;
}

938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978
/**************************************************************************
 * Str_GetPtrAtoW [internal]
 *
 * Converts a multibyte string into a unicode string
 *
 * PARAMS
 *     lpSrc   [I] Pointer to the multibyte source string
 *     lpDest  [O] Pointer to caller supplied storage for the unicode string
 *     nMaxLen [I] Size, in characters, of the destination buffer
 *
 * RETURNS
 *     Length, in characters, of the converted string.
 */

INT Str_GetPtrAtoW (LPCSTR lpSrc, LPWSTR lpDest, INT nMaxLen)
{
    INT len;

    TRACE("(%s %p %d)\n", debugstr_a(lpSrc), lpDest, nMaxLen);

    if (!lpDest && lpSrc)
	return MultiByteToWideChar(CP_ACP, 0, lpSrc, -1, 0, 0);

    if (nMaxLen == 0)
	return 0;

    if (lpSrc == NULL) {
	lpDest[0] = '\0';
	return 0;
    }

    len = MultiByteToWideChar(CP_ACP, 0, lpSrc, -1, 0, 0);
    if (len >= nMaxLen)
	len = nMaxLen - 1;

    MultiByteToWideChar(CP_ACP, 0, lpSrc, -1, lpDest, len);
    lpDest[len] = '\0';

    return len;
}

979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996

/**************************************************************************
 * Str_SetPtrAtoW [internal]
 *
 * Converts a multi byte string to a unicode string.
 * If the pointer to the destination buffer is NULL a buffer is allocated.
 * If the destination buffer is too small to keep the converted multi byte
 * string the destination buffer is reallocated. If the source pointer is
 * NULL, the destination buffer is freed.
 *
 * PARAMS
 *     lppDest [I/O] pointer to a pointer to the destination buffer
 *     lpSrc   [I] pointer to a multi byte string
 *
 * RETURNS
 *     TRUE: conversion successful
 *     FALSE: error
 */
997
BOOL Str_SetPtrAtoW (LPWSTR *lppDest, LPCSTR lpSrc)
998 999 1000 1001 1002
{
    TRACE("(%p %s)\n", lppDest, lpSrc);

    if (lpSrc) {
	INT len = MultiByteToWideChar(CP_ACP,0,lpSrc,-1,NULL,0);
1003
	LPWSTR ptr = ReAlloc (*lppDest, len*sizeof(WCHAR));
1004 1005 1006 1007 1008 1009 1010

	if (!ptr)
	    return FALSE;
	MultiByteToWideChar(CP_ACP,0,lpSrc,-1,ptr,len);
	*lppDest = ptr;
    }
    else {
1011 1012
        Free (*lppDest);
        *lppDest = NULL;
1013 1014 1015 1016 1017
    }

    return TRUE;
}

1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
/**************************************************************************
 * Str_SetPtrWtoA [internal]
 *
 * Converts a unicode string to a multi byte string.
 * If the pointer to the destination buffer is NULL a buffer is allocated.
 * If the destination buffer is too small to keep the converted wide
 * string the destination buffer is reallocated. If the source pointer is
 * NULL, the destination buffer is freed.
 *
 * PARAMS
 *     lppDest [I/O] pointer to a pointer to the destination buffer
 *     lpSrc   [I] pointer to a wide string
 *
 * RETURNS
 *     TRUE: conversion successful
 *     FALSE: error
 */
BOOL Str_SetPtrWtoA (LPSTR *lppDest, LPCWSTR lpSrc)
{
    TRACE("(%p %s)\n", lppDest, debugstr_w(lpSrc));

    if (lpSrc) {
        INT len = WideCharToMultiByte(CP_ACP,0,lpSrc,-1,NULL,0,NULL,FALSE);
        LPSTR ptr = ReAlloc (*lppDest, len*sizeof(CHAR));

        if (!ptr)
            return FALSE;
        WideCharToMultiByte(CP_ACP,0,lpSrc,-1,ptr,len,NULL,FALSE);
        *lppDest = ptr;
    }
    else {
1049 1050
        Free (*lppDest);
        *lppDest = NULL;
1051 1052 1053 1054 1055
    }

    return TRUE;
}

1056

Alexandre Julliard's avatar
Alexandre Julliard committed
1057
/**************************************************************************
1058
 * Notification functions
Alexandre Julliard's avatar
Alexandre Julliard committed
1059 1060
 */

1061
typedef struct tagNOTIFYDATA
Alexandre Julliard's avatar
Alexandre Julliard committed
1062
{
1063 1064 1065 1066 1067 1068 1069
    HWND hwndFrom;
    HWND hwndTo;
    DWORD  dwParam3;
    DWORD  dwParam4;
    DWORD  dwParam5;
    DWORD  dwParam6;
} NOTIFYDATA, *LPNOTIFYDATA;
Alexandre Julliard's avatar
Alexandre Julliard committed
1070 1071 1072


/**************************************************************************
1073
 * DoNotify [Internal]
Alexandre Julliard's avatar
Alexandre Julliard committed
1074 1075
 */

1076
static LRESULT DoNotify (const NOTIFYDATA *lpNotify, UINT uCode, LPNMHDR lpHdr)
Alexandre Julliard's avatar
Alexandre Julliard committed
1077
{
1078 1079 1080
    NMHDR nmhdr;
    LPNMHDR lpNmh = NULL;
    UINT idFrom = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1081

1082
    TRACE("(%p %p %d %p 0x%08x)\n",
1083 1084
	   lpNotify->hwndFrom, lpNotify->hwndTo, uCode, lpHdr,
	   lpNotify->dwParam5);
Alexandre Julliard's avatar
Alexandre Julliard committed
1085

1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
    if (!lpNotify->hwndTo)
	return 0;

    if (lpNotify->hwndFrom == (HWND)-1) {
	lpNmh = lpHdr;
	idFrom = lpHdr->idFrom;
    }
    else {
	if (lpNotify->hwndFrom)
	    idFrom = GetDlgCtrlID (lpNotify->hwndFrom);
Alexandre Julliard's avatar
Alexandre Julliard committed
1096

1097
	lpNmh = (lpHdr) ? lpHdr : &nmhdr;
1098

1099 1100 1101 1102
	lpNmh->hwndFrom = lpNotify->hwndFrom;
	lpNmh->idFrom = idFrom;
	lpNmh->code = uCode;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1103

1104
    return SendMessageW (lpNotify->hwndTo, WM_NOTIFY, idFrom, (LPARAM)lpNmh);
Alexandre Julliard's avatar
Alexandre Julliard committed
1105 1106 1107 1108
}


/**************************************************************************
1109
 * SendNotify [COMCTL32.341]
Alexandre Julliard's avatar
Alexandre Julliard committed
1110
 *
1111
 * Sends a WM_NOTIFY message to the specified window.
Alexandre Julliard's avatar
Alexandre Julliard committed
1112 1113
 *
 * PARAMS
1114 1115 1116 1117
 *     hwndTo   [I] Window to receive the message
 *     hwndFrom [I] Window that the message is from (see notes)
 *     uCode    [I] Notification code
 *     lpHdr    [I] The NMHDR and any additional information to send or NULL
Alexandre Julliard's avatar
Alexandre Julliard committed
1118 1119
 *
 * RETURNS
1120 1121 1122 1123 1124 1125 1126
 *     Success: return value from notification
 *     Failure: 0
 *
 * NOTES
 *     If hwndFrom is -1 then the identifier of the control sending the
 *     message is taken from the NMHDR structure.
 *     If hwndFrom is not -1 then lpHdr can be NULL.
Alexandre Julliard's avatar
Alexandre Julliard committed
1127
 */
1128
LRESULT WINAPI SendNotify (HWND hwndTo, HWND hwndFrom, UINT uCode, LPNMHDR lpHdr)
Alexandre Julliard's avatar
Alexandre Julliard committed
1129
{
1130
    NOTIFYDATA notify;
1131

1132 1133
    TRACE("(%p %p %d %p)\n",
	   hwndTo, hwndFrom, uCode, lpHdr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1134

1135 1136 1137 1138
    notify.hwndFrom = hwndFrom;
    notify.hwndTo   = hwndTo;
    notify.dwParam5 = 0;
    notify.dwParam6 = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1139

1140
    return DoNotify (&notify, uCode, lpHdr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1141 1142 1143 1144
}


/**************************************************************************
1145
 * SendNotifyEx [COMCTL32.342]
Alexandre Julliard's avatar
Alexandre Julliard committed
1146
 *
1147
 * Sends a WM_NOTIFY message to the specified window.
Robert Shearman's avatar
Robert Shearman committed
1148
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1149
 * PARAMS
1150 1151 1152 1153 1154
 *     hwndFrom [I] Window to receive the message
 *     hwndTo   [I] Window that the message is from
 *     uCode    [I] Notification code
 *     lpHdr    [I] The NMHDR and any additional information to send or NULL
 *     dwParam5 [I] Unknown
Alexandre Julliard's avatar
Alexandre Julliard committed
1155 1156
 *
 * RETURNS
1157 1158 1159 1160 1161 1162 1163
 *     Success: return value from notification
 *     Failure: 0
 *
 * NOTES
 *     If hwndFrom is -1 then the identifier of the control sending the
 *     message is taken from the NMHDR structure.
 *     If hwndFrom is not -1 then lpHdr can be NULL.
Alexandre Julliard's avatar
Alexandre Julliard committed
1164
 */
1165 1166
LRESULT WINAPI SendNotifyEx (HWND hwndTo, HWND hwndFrom, UINT uCode,
                             LPNMHDR lpHdr, DWORD dwParam5)
Alexandre Julliard's avatar
Alexandre Julliard committed
1167
{
1168 1169
    NOTIFYDATA notify;
    HWND hwndNotify;
Alexandre Julliard's avatar
Alexandre Julliard committed
1170

1171
    TRACE("(%p %p %d %p 0x%08x)\n",
1172
	   hwndFrom, hwndTo, uCode, lpHdr, dwParam5);
Alexandre Julliard's avatar
Alexandre Julliard committed
1173

1174 1175 1176 1177 1178 1179 1180
    hwndNotify = hwndTo;
    if (!hwndTo) {
	if (IsWindow (hwndFrom)) {
	    hwndNotify = GetParent (hwndFrom);
	    if (!hwndNotify)
		return 0;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1181 1182
    }

1183 1184 1185 1186
    notify.hwndFrom = hwndFrom;
    notify.hwndTo   = hwndNotify;
    notify.dwParam5 = dwParam5;
    notify.dwParam6 = 0;
1187 1188

    return DoNotify (&notify, uCode, lpHdr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1189
}