comctl32undoc.c 33.9 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
#define NONAMELESSUNION
39

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

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

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

55
WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
56

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

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


/**************************************************************************
 * ReAlloc [COMCTL32.72]
 *
 * Changes the size of an allocated memory block or allocates a memory
81
 * block using the dll's private heap.
Alexandre Julliard's avatar
Alexandre Julliard committed
82 83 84 85 86 87 88 89 90 91
 *
 * 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
92 93
 *     If lpSrc is a NULL-pointer, then ReAlloc allocates a memory
 *     block like Alloc.
Alexandre Julliard's avatar
Alexandre Julliard committed
94
 */
95
LPVOID WINAPI ReAlloc (LPVOID lpSrc, DWORD dwSize)
Alexandre Julliard's avatar
Alexandre Julliard committed
96 97
{
    if (lpSrc)
98
        return LocalReAlloc( lpSrc, dwSize, LMEM_ZEROINIT | LMEM_MOVEABLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
99
    else
100
        return LocalAlloc( LMEM_ZEROINIT, dwSize);
Alexandre Julliard's avatar
Alexandre Julliard committed
101 102 103 104 105
}


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


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


Alexandre Julliard's avatar
Alexandre Julliard committed
141
/**************************************************************************
142
 * MRU-Functions  {COMCTL32}
Alexandre Julliard's avatar
Alexandre Julliard committed
143
 *
144
 * NOTES
145 146
 * 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
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
 * 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);
 *|
170
 *|typedef struct tagMRUINFO
171 172
 *|{
 *|    DWORD   cbSize;
173 174
 *|    UINT    uMax;
 *|    UINT    fFlags;
175
 *|    HKEY    hKey;
176
 *|    LPTSTR  lpszSubKey;
177
 *|    PROC    lpfnCompare;
178
 *|} MRUINFO, *LPMRUINFO;
179 180
 *
 * MEMBERS
181 182 183
 *  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
184 185
 *                  of the limited number of identifiers, this should be set to
 *                  a value from 1 to 30 by the caller.
186
 *  fFlags      [I] If bit 0 is set, the list will be used to store binary
187 188 189 190 191 192 193 194
 *                  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.
195
 *  lpfnCompare [I] A caller supplied comparison function, which may be either
196 197 198 199 200 201 202 203 204 205
 *                  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
206 207
 */

208 209 210 211
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);

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

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

240 241 242 243
/* 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 */
244 245 246 247 248 249 250 251 252

/* 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?
 */

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

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

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

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

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

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

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

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

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

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

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

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


/**************************************************************************
 *                  FindMRUData [COMCTL32.169]
377
 *
378 379 380 381 382 383 384 385 386 387 388 389 390
 * 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.
 */
391 392
INT WINAPI FindMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData,
                        LPINT lpRegNum)
393
{
394
    const WINEMRULIST *mp = hList;
395 396
    INT ret;
    UINT i;
397
    LPSTR dataA = NULL;
398

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

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

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

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

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

445
    return ret;
446 447 448
}


449 450
/**************************************************************************
 *              AddMRUData [COMCTL32.167]
451
 *
Andreas Mohr's avatar
Andreas Mohr committed
452
 * Add item to MRU binary list.  If item already exists in list then it is
453 454 455 456 457 458 459 460 461 462 463 464
 * 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.
 */
465
INT WINAPI AddMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData)
Alexandre Julliard's avatar
Alexandre Julliard committed
466
{
467
    LPWINEMRULIST mp = hList;
468
    LPWINEMRUITEM witem;
469
    INT i, replace;
470

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

493 494 495 496 497
        /* 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);
498

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

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

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

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

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

519
/**************************************************************************
520
 *              AddMRUStringW [COMCTL32.401]
521
 *
522
 * Add an item to an MRU string list.
523 524
 *
 * PARAMS
525 526
 *     hList      [I] Handle to list.
 *     lpszString [I] The string to add.
527 528
 *
 * RETURNS
529 530 531 532 533 534 535 536 537 538 539 540 541 542
 *   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.
543
 */
544
INT WINAPI AddMRUStringW(HANDLE hList, LPCWSTR lpszString)
545
{
546
    TRACE("(%p,%s)\n", hList, debugstr_w(lpszString));
547

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

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

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

/**************************************************************************
 *              AddMRUStringA [COMCTL32.153]
Robert Shearman's avatar
Robert Shearman committed
563 564
 *
 * See AddMRUStringW.
565
 */
566
INT WINAPI AddMRUStringA(HANDLE hList, LPCSTR lpszString)
567
{
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
    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
587

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

594 595 596 597 598 599 600 601 602 603
/**************************************************************************
 *              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
604
 *    TRUE if successful, FALSE if nItemPos is out of range.
605
 */
606
BOOL WINAPI DelMRUString(HANDLE hList, INT nItemPos)
607
{
608
    FIXME("(%p, %d): stub\n", hList, nItemPos);
609
    return TRUE;
610
}
Alexandre Julliard's avatar
Alexandre Julliard committed
611

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

623 624
/**************************************************************************
 *                  FindMRUStringA [COMCTL32.155]
625
 *
626 627 628 629 630 631 632 633 634 635 636 637
 * 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.
 */
638
INT WINAPI FindMRUStringA (HANDLE hList, LPCSTR lpszString, LPINT lpRegNum)
639
{
640
    DWORD len = MultiByteToWideChar(CP_ACP, 0, lpszString, -1, NULL, 0);
641
    LPWSTR stringW = Alloc(len * sizeof(WCHAR));
642 643 644 645
    INT ret;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/**************************************************************************
 *                  CreateMRUListLazyA [COMCTL32.157]
Robert Shearman's avatar
Robert Shearman committed
759 760 761 762 763 764 765 766 767 768 769
 *
 * 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.
770
 */
771
HANDLE WINAPI CreateMRUListLazyA (const MRUINFOA *lpcml, DWORD dwParam2,
772
                                  DWORD dwParam3, DWORD dwParam4)
773 774 775 776
{
    LPWINEMRULIST mp;
    DWORD len;

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

779
    if (!lpcml->hKey || IsBadStringPtrA(lpcml->lpszSubKey, -1))
780 781
	return 0;

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

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

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


/**************************************************************************
 *                EnumMRUListW [COMCTL32.403]
 *
822
 * Enumerate item in a most-recently-used list
823 824 825 826 827 828 829 830 831 832 833 834
 *
 * 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
835
 *    the list.
836
 */
837 838
INT WINAPI EnumMRUListW (HANDLE hList, INT nItemPos, LPVOID lpBuffer,
                         DWORD nBufferSize)
839
{
840
    const WINEMRULIST *mp = hList;
841
    const WINEMRUITEM *witem;
842 843
    INT desired, datasize;

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

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

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

895 896 897 898 899 900 901 902 903 904 905 906 907 908
/**************************************************************************
 * 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.
 */

909
INT Str_GetPtrWtoA (LPCWSTR lpSrc, LPSTR lpDest, INT nMaxLen)
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935
{
    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;
}

936 937 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
/**************************************************************************
 * 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;
}

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

/**************************************************************************
 * 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
 */
995
BOOL Str_SetPtrAtoW (LPWSTR *lppDest, LPCSTR lpSrc)
996 997 998 999 1000
{
    TRACE("(%p %s)\n", lppDest, lpSrc);

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

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

    return TRUE;
}

1016 1017 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
/**************************************************************************
 * 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 {
1047 1048
        Free (*lppDest);
        *lppDest = NULL;
1049 1050 1051 1052 1053
    }

    return TRUE;
}

1054

Alexandre Julliard's avatar
Alexandre Julliard committed
1055
/**************************************************************************
1056
 * Notification functions
Alexandre Julliard's avatar
Alexandre Julliard committed
1057 1058
 */

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


/**************************************************************************
1071
 * DoNotify [Internal]
Alexandre Julliard's avatar
Alexandre Julliard committed
1072 1073
 */

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

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

1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
    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
1094

1095
	lpNmh = (lpHdr) ? lpHdr : &nmhdr;
1096

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

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


/**************************************************************************
1107
 * SendNotify [COMCTL32.341]
Alexandre Julliard's avatar
Alexandre Julliard committed
1108
 *
1109
 * Sends a WM_NOTIFY message to the specified window.
Alexandre Julliard's avatar
Alexandre Julliard committed
1110 1111
 *
 * PARAMS
1112 1113 1114 1115
 *     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
1116 1117
 *
 * RETURNS
1118 1119 1120 1121 1122 1123 1124
 *     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
1125
 */
1126
LRESULT WINAPI SendNotify (HWND hwndTo, HWND hwndFrom, UINT uCode, LPNMHDR lpHdr)
Alexandre Julliard's avatar
Alexandre Julliard committed
1127
{
1128
    NOTIFYDATA notify;
1129

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

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

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


/**************************************************************************
1143
 * SendNotifyEx [COMCTL32.342]
Alexandre Julliard's avatar
Alexandre Julliard committed
1144
 *
1145
 * Sends a WM_NOTIFY message to the specified window.
Robert Shearman's avatar
Robert Shearman committed
1146
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1147
 * PARAMS
1148 1149 1150 1151 1152
 *     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
1153 1154
 *
 * RETURNS
1155 1156 1157 1158 1159 1160 1161
 *     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
1162
 */
1163 1164
LRESULT WINAPI SendNotifyEx (HWND hwndTo, HWND hwndFrom, UINT uCode,
                             LPNMHDR lpHdr, DWORD dwParam5)
Alexandre Julliard's avatar
Alexandre Julliard committed
1165
{
1166 1167
    NOTIFYDATA notify;
    HWND hwndNotify;
Alexandre Julliard's avatar
Alexandre Julliard committed
1168

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

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

1181 1182 1183 1184
    notify.hwndFrom = hwndFrom;
    notify.hwndTo   = hwndNotify;
    notify.dwParam5 = dwParam5;
    notify.dwParam6 = 0;
1185 1186

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