[PATCH 4/4] comctl32: Move MRU functions to another file.

Nikolay Sivov nsivov at codeweavers.com
Mon May 4 06:12:00 CDT 2020


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/comctl32/Makefile.in     |    1 -
 dlls/comctl32/comctl32undoc.c | 1184 ---------------------------------
 dlls/comctl32/commctrl.c      |  962 +++++++++++++++++++++++++++
 3 files changed, 962 insertions(+), 1185 deletions(-)
 delete mode 100644 dlls/comctl32/comctl32undoc.c

diff --git a/dlls/comctl32/Makefile.in b/dlls/comctl32/Makefile.in
index 9a56e36e13..e548d3761a 100644
--- a/dlls/comctl32/Makefile.in
+++ b/dlls/comctl32/Makefile.in
@@ -11,7 +11,6 @@ C_SRCS = \
 	button.c \
 	combo.c \
 	comboex.c \
-	comctl32undoc.c \
 	commctrl.c \
 	datetime.c \
 	dpa.c \
diff --git a/dlls/comctl32/comctl32undoc.c b/dlls/comctl32/comctl32undoc.c
deleted file mode 100644
index 753b0f5f50..0000000000
--- a/dlls/comctl32/comctl32undoc.c
+++ /dev/null
@@ -1,1184 +0,0 @@
-/*
- * Undocumented functions from COMCTL32.DLL
- *
- * Copyright 1998 Eric Kohl
- *           1998 Juergen Schmied <j.schmied at metronet.de>
- *           2000 Eric Kohl for CodeWeavers
- *
- * 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
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * 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).
- *
- */
-
-#include <stdarg.h>
-#include <string.h>
-#include <ctype.h>
-#include <limits.h>
-
-#define COBJMACROS
-#define NONAMELESSUNION
-
-#include "windef.h"
-#include "winbase.h"
-#include "wingdi.h"
-#include "winuser.h"
-#include "winnls.h"
-#include "winreg.h"
-#include "commctrl.h"
-#include "objbase.h"
-#include "winerror.h"
-
-#include "comctl32.h"
-
-#include "wine/debug.h"
-
-WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
-
-static const WCHAR strMRUList[] = { 'M','R','U','L','i','s','t',0 };
-
-/**************************************************************************
- * Alloc [COMCTL32.71]
- *
- * Allocates memory block from the dll's private heap
- *
- * PARAMS
- *     dwSize [I] size of the allocated memory block
- *
- * RETURNS
- *     Success: pointer to allocated memory block
- *     Failure: NULL
- */
-LPVOID WINAPI Alloc (DWORD dwSize)
-{
-    return LocalAlloc( LMEM_ZEROINIT, dwSize );
-}
-
-
-/**************************************************************************
- * ReAlloc [COMCTL32.72]
- *
- * Changes the size of an allocated memory block or allocates a memory
- * block using the dll's private heap.
- *
- * 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
- *     If lpSrc is a NULL-pointer, then ReAlloc allocates a memory
- *     block like Alloc.
- */
-LPVOID WINAPI ReAlloc (LPVOID lpSrc, DWORD dwSize)
-{
-    if (lpSrc)
-        return LocalReAlloc( lpSrc, dwSize, LMEM_ZEROINIT | LMEM_MOVEABLE );
-    else
-        return LocalAlloc( LMEM_ZEROINIT, dwSize);
-}
-
-
-/**************************************************************************
- * Free [COMCTL32.73]
- *
- * Frees an allocated memory block from the dll's private heap.
- *
- * PARAMS
- *     lpMem [I] pointer to memory block which will be freed
- *
- * RETURNS
- *     Success: TRUE
- *     Failure: FALSE
- */
-BOOL WINAPI Free (LPVOID lpMem)
-{
-    return !LocalFree( lpMem );
-}
-
-
-/**************************************************************************
- * GetSize [COMCTL32.74]
- *
- * Retrieves the size of the specified memory block from the dll's
- * private heap.
- *
- * PARAMS
- *     lpMem [I] pointer to an allocated memory block
- *
- * RETURNS
- *     Success: size of the specified memory block
- *     Failure: 0
- */
-DWORD WINAPI GetSize (LPVOID lpMem)
-{
-    return LocalSize( lpMem );
-}
-
-
-/**************************************************************************
- * MRU-Functions  {COMCTL32}
- *
- * 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);
- *|
- *|typedef struct tagMRUINFO
- *|{
- *|    DWORD   cbSize;
- *|    UINT    uMax;
- *|    UINT    fFlags;
- *|    HKEY    hKey;
- *|    LPTSTR  lpszSubKey;
- *|    PROC    lpfnCompare;
- *|} MRUINFO, *LPMRUINFO;
- *
- * MEMBERS
- *  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
- *                  of the limited number of identifiers, this should be set to
- *                  a value from 1 to 30 by the caller.
- *  fFlags      [I] If bit 0 is set, the list will be used to store binary
- *                  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.
- *  lpfnCompare [I] A caller supplied comparison function, which may be either
- *                  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().
- */
-
-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);
-
-typedef struct tagMRUINFOA
-{
-    DWORD  cbSize;
-    UINT   uMax;
-    UINT   fFlags;
-    HKEY   hKey;
-    LPSTR  lpszSubKey;
-    union
-    {
-        MRUStringCmpFnA string_cmpfn;
-        MRUBinaryCmpFn  binary_cmpfn;
-    } u;
-} MRUINFOA, *LPMRUINFOA;
-
-typedef struct tagMRUINFOW
-{
-    DWORD   cbSize;
-    UINT    uMax;
-    UINT    fFlags;
-    HKEY    hKey;
-    LPWSTR  lpszSubKey;
-    union
-    {
-        MRUStringCmpFnW string_cmpfn;
-        MRUBinaryCmpFn  binary_cmpfn;
-    } u;
-} MRUINFOW, *LPMRUINFOW;
-
-/* 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 */
-
-/* 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?
- */
-
-typedef struct tagWINEMRUITEM
-{
-    DWORD          size;        /* size of data stored               */
-    DWORD          itemFlag;    /* flags                             */
-    BYTE           datastart;
-} WINEMRUITEM, *LPWINEMRUITEM;
-
-/* itemFlag */
-#define WMRUIF_CHANGED   0x0001 /* this dataitem changed             */
-
-typedef struct tagWINEMRULIST
-{
-    MRUINFOW       extview;     /* original create information       */
-    BOOL           isUnicode;   /* is compare fn Unicode */
-    DWORD          wineFlags;   /* internal flags                    */
-    DWORD          cursize;     /* current size of realMRU           */
-    LPWSTR         realMRU;     /* pointer to string of index names  */
-    LPWINEMRUITEM  *array;      /* array of pointers to data         */
-                                /* in 'a' to 'z' order               */
-} WINEMRULIST, *LPWINEMRULIST;
-
-/* wineFlags */
-#define WMRUF_CHANGED  0x0001   /* MRU list has changed              */
-
-/**************************************************************************
- *              MRU_SaveChanged (internal)
- *
- * Local MRU saving code
- */
-static void MRU_SaveChanged ( LPWINEMRULIST mp )
-{
-    UINT i, err;
-    HKEY newkey;
-    WCHAR realname[2];
-    LPWINEMRUITEM witem;
-
-    /* or should we do the following instead of RegOpenKeyEx:
-     */
-
-    /* open the sub key */
-    if ((err = RegOpenKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
-			      0, KEY_WRITE, &newkey))) {
-	/* not present - what to do ??? */
-	ERR("Could not open key, error=%d, attempting to create\n",
-	    err);
-	if ((err = RegCreateKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
-				    0,
-				    NULL,
-				    REG_OPTION_NON_VOLATILE,
-				    KEY_READ | KEY_WRITE,
-				    0,
-				    &newkey,
-				    0))) {
-	    ERR("failed to create key /%s/, err=%d\n",
-		debugstr_w(mp->extview.lpszSubKey), err);
-	    return;
-	}
-    }
-    if (mp->wineFlags & WMRUF_CHANGED) {
-	mp->wineFlags &= ~WMRUF_CHANGED;
-	err = RegSetValueExW(newkey, strMRUList, 0, REG_SZ, (LPBYTE)mp->realMRU,
-			     (lstrlenW(mp->realMRU) + 1)*sizeof(WCHAR));
-	if (err) {
-	    ERR("error saving MRUList, err=%d\n", err);
-	}
-	TRACE("saving MRUList=/%s/\n", debugstr_w(mp->realMRU));
-    }
-    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;
-	    err = RegSetValueExW(newkey, realname, 0,
-				 (mp->extview.fFlags & MRU_BINARY) ?
-				 REG_BINARY : REG_SZ,
-				 &witem->datastart, witem->size);
-	    if (err) {
-		ERR("error saving /%s/, err=%d\n", debugstr_w(realname), err);
-	    }
-            TRACE("saving value for name /%s/ size=%d\n",
-		  debugstr_w(realname), witem->size);
-	}
-    }
-    RegCloseKey( newkey );
-}
-
-/**************************************************************************
- *              FreeMRUList [COMCTL32.152]
- *
- * Frees a most-recently-used items list.
- *
- * PARAMS
- *     hMRUList [I] Handle to list.
- *
- * RETURNS
- *     Nothing.
- */
-void WINAPI FreeMRUList (HANDLE hMRUList)
-{
-    LPWINEMRULIST mp = hMRUList;
-    UINT i;
-
-    TRACE("(%p)\n", hMRUList);
-    if (!hMRUList)
-        return;
-
-    if (mp->wineFlags & WMRUF_CHANGED) {
-	/* need to open key and then save the info */
-	MRU_SaveChanged( mp );
-    }
-
-    for(i=0; i<mp->extview.uMax; i++)
-        Free(mp->array[i]);
-
-    Free(mp->realMRU);
-    Free(mp->array);
-    Free(mp->extview.lpszSubKey);
-    Free(mp);
-}
-
-
-/**************************************************************************
- *                  FindMRUData [COMCTL32.169]
- *
- * 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.
- */
-INT WINAPI FindMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData,
-                        LPINT lpRegNum)
-{
-    const WINEMRULIST *mp = hList;
-    INT ret;
-    UINT i;
-    LPSTR dataA = NULL;
-
-    if (!mp || !mp->extview.u.string_cmpfn)
-	return -1;
-
-    if(!(mp->extview.fFlags & MRU_BINARY) && !mp->isUnicode) {
-        DWORD len = WideCharToMultiByte(CP_ACP, 0, lpData, -1,
-					NULL, 0, NULL, NULL);
-	dataA = Alloc(len);
-	WideCharToMultiByte(CP_ACP, 0, lpData, -1, dataA, len, NULL, NULL);
-    }
-
-    for(i=0; i<mp->cursize; i++) {
-	if (mp->extview.fFlags & MRU_BINARY) {
-	    if (!mp->extview.u.binary_cmpfn(lpData, &mp->array[i]->datastart, cbData))
-		break;
-	}
-	else {
-	    if(mp->isUnicode) {
-	        if (!mp->extview.u.string_cmpfn(lpData, (LPWSTR)&mp->array[i]->datastart))
-		    break;
-	    } else {
-	        DWORD len = WideCharToMultiByte(CP_ACP, 0,
-						(LPWSTR)&mp->array[i]->datastart, -1,
-						NULL, 0, NULL, NULL);
-		LPSTR itemA = Alloc(len);
-		INT cmp;
-		WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&mp->array[i]->datastart, -1,
-				    itemA, len, NULL, NULL);
-
-	        cmp = mp->extview.u.string_cmpfn((LPWSTR)dataA, (LPWSTR)itemA);
-		Free(itemA);
-		if(!cmp)
-		    break;
-	    }
-	}
-    }
-    Free(dataA);
-    if (i < mp->cursize)
-	ret = i;
-    else
-	ret = -1;
-    if (lpRegNum && (ret != -1))
-	*lpRegNum = 'a' + i;
-
-    TRACE("(%p, %p, %d, %p) returning %d\n",
-	   hList, lpData, cbData, lpRegNum, ret);
-
-    return ret;
-}
-
-
-/**************************************************************************
- *              AddMRUData [COMCTL32.167]
- *
- * Add item to MRU binary list.  If item already exists in list then it is
- * 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.
- */
-INT WINAPI AddMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData)
-{
-    LPWINEMRULIST mp = hList;
-    LPWINEMRUITEM witem;
-    INT i, replace;
-
-    if ((replace = FindMRUData (hList, lpData, cbData, NULL)) >= 0) {
-        /* Item exists, just move it to the front */
-        LPWSTR pos = wcschr(mp->realMRU, replace + 'a');
-        while (pos > mp->realMRU)
-        {
-            pos[0] = pos[-1];
-            pos--;
-        }
-    }
-    else {
-	/* either add a new entry or replace oldest */
-	if (mp->cursize < mp->extview.uMax) {
-	    /* 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';
-	    Free(mp->array[replace]);
-	}
-
-        /* 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);
-
-        /* now rotate MRU list */
-        for(i=mp->cursize-1; i>=1; i--)
-            mp->realMRU[i] = mp->realMRU[i-1];
-    }
-
-    /* The new item gets the front spot */
-    mp->wineFlags |= WMRUF_CHANGED;
-    mp->realMRU[0] = replace + 'a';
-
-    TRACE("(%p, %p, %d) adding data, /%c/ now most current\n",
-          hList, lpData, cbData, replace+'a');
-
-    if (!(mp->extview.fFlags & MRU_CACHEWRITE)) {
-	/* save changed stuff right now */
-	MRU_SaveChanged( mp );
-    }
-
-    return replace;
-}
-
-/**************************************************************************
- *              AddMRUStringW [COMCTL32.401]
- *
- * Add an item to an MRU string list.
- *
- * PARAMS
- *     hList      [I] Handle to list.
- *     lpszString [I] The string to add.
- *
- * RETURNS
- *   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.
- */
-INT WINAPI AddMRUStringW(HANDLE hList, LPCWSTR lpszString)
-{
-    TRACE("(%p,%s)\n", hList, debugstr_w(lpszString));
-
-    if (!hList)
-        return -1;
-
-    if (!lpszString || IsBadStringPtrW(lpszString, -1))
-    {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return 0;
-    }
-
-    return AddMRUData(hList, lpszString,
-                      (lstrlenW(lpszString) + 1) * sizeof(WCHAR));
-}
-
-/**************************************************************************
- *              AddMRUStringA [COMCTL32.153]
- *
- * See AddMRUStringW.
- */
-INT WINAPI AddMRUStringA(HANDLE hList, LPCSTR lpszString)
-{
-    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;
-
-    MultiByteToWideChar(CP_ACP, 0, lpszString, -1, stringW, len/sizeof(WCHAR));
-    ret = AddMRUData(hList, stringW, len);
-    Free(stringW);
-    return ret;
-}
-
-/**************************************************************************
- *              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
- *    TRUE if successful, FALSE if nItemPos is out of range.
- */
-BOOL WINAPI DelMRUString(HANDLE hList, INT nItemPos)
-{
-    FIXME("(%p, %d): stub\n", hList, nItemPos);
-    return TRUE;
-}
-
-/**************************************************************************
- *                  FindMRUStringW [COMCTL32.402]
- *
- * See FindMRUStringA.
- */
-INT WINAPI FindMRUStringW (HANDLE hList, LPCWSTR lpszString, LPINT lpRegNum)
-{
-  return FindMRUData(hList, lpszString,
-                     (lstrlenW(lpszString) + 1) * sizeof(WCHAR), lpRegNum);
-}
-
-/**************************************************************************
- *                  FindMRUStringA [COMCTL32.155]
- *
- * 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.
- */
-INT WINAPI FindMRUStringA (HANDLE hList, LPCSTR lpszString, LPINT lpRegNum)
-{
-    DWORD len = MultiByteToWideChar(CP_ACP, 0, lpszString, -1, NULL, 0);
-    LPWSTR stringW = Alloc(len * sizeof(WCHAR));
-    INT ret;
-
-    MultiByteToWideChar(CP_ACP, 0, lpszString, -1, stringW, len);
-    ret = FindMRUData(hList, stringW, len * sizeof(WCHAR), lpRegNum);
-    Free(stringW);
-    return ret;
-}
-
-/*************************************************************************
- *                 create_mru_list (internal)
- */
-static HANDLE create_mru_list(LPWINEMRULIST mp)
-{
-    UINT i, err;
-    HKEY newkey;
-    DWORD datasize, dwdisp;
-    WCHAR realname[2];
-    LPWINEMRUITEM witem;
-    DWORD type;
-
-    /* get space to save indices that will turn into names
-     * but in order of most to least recently used
-     */
-    mp->realMRU = Alloc((mp->extview.uMax + 2) * sizeof(WCHAR));
-
-    /* get space to save pointers to actual data in order of
-     * 'a' to 'z' (0 to n).
-     */
-    mp->array = Alloc(mp->extview.uMax * sizeof(LPVOID));
-
-    /* open the sub key */
-    if ((err = RegCreateKeyExW( mp->extview.hKey, mp->extview.lpszSubKey,
-			        0,
-				NULL,
-				REG_OPTION_NON_VOLATILE,
-				KEY_READ | KEY_WRITE,
-                                0,
-				&newkey,
-				&dwdisp))) {
-	/* error - what to do ??? */
-	ERR("(%u %u %x %p %s %p): Could not open key, error=%d\n",
-	    mp->extview.cbSize, mp->extview.uMax, mp->extview.fFlags,
-	    mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey),
-            mp->extview.u.string_cmpfn, err);
-	return 0;
-    }
-
-    /* get values from key 'MRUList' */
-    if (newkey) {
-	datasize = (mp->extview.uMax + 1) * sizeof(WCHAR);
-	if (RegQueryValueExW( newkey, strMRUList, 0, &type,
-				  (LPBYTE)mp->realMRU, &datasize)) {
-	    /* not present - set size to 1 (will become 0 later) */
-	    datasize = 1;
-	    *mp->realMRU = 0;
-	}
-        else
-            datasize /= sizeof(WCHAR);
-
-	TRACE("MRU list = %s, datasize = %d\n", debugstr_w(mp->realMRU), datasize);
-
-	mp->cursize = datasize - 1;
-	/* datasize now has number of items in the MRUList */
-
-	/* get actual values for each entry */
-	realname[1] = 0;
-	for(i=0; i<mp->cursize; i++) {
-	    realname[0] = 'a' + i;
-	    if(RegQueryValueExW( newkey, realname, 0, &type, 0, &datasize)) {
-		/* not present - what to do ??? */
-		ERR("Key %s not found 1\n", debugstr_w(realname));
-	    }
-	    mp->array[i] = witem = Alloc(datasize + sizeof(WINEMRUITEM));
-	    witem->size = datasize;
-	    if(RegQueryValueExW( newkey, realname, 0, &type,
-				 &witem->datastart, &datasize)) {
-		/* not present - what to do ??? */
-		ERR("Key %s not found 2\n", debugstr_w(realname));
-	    }
-	}
-	RegCloseKey( newkey );
-    }
-    else
-	mp->cursize = 0;
-
-    TRACE("(%u %u %x %p %s %p): Current Size = %d\n",
-	  mp->extview.cbSize, mp->extview.uMax, mp->extview.fFlags,
-	  mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey),
-	  mp->extview.u.string_cmpfn, mp->cursize);
-    return mp;
-}
-
-/**************************************************************************
- *                  CreateMRUListLazyW [COMCTL32.404]
- *
- * See CreateMRUListLazyA.
- */
-HANDLE WINAPI CreateMRUListLazyW (const MRUINFOW *infoW, DWORD dwParam2,
-                                  DWORD dwParam3, DWORD dwParam4)
-{
-    LPWINEMRULIST mp;
-
-    /* Native does not check for a NULL lpcml */
-    if (!infoW->hKey || IsBadStringPtrW(infoW->lpszSubKey, -1))
-	return NULL;
-
-    mp = Alloc(sizeof(WINEMRULIST));
-    memcpy(&mp->extview, infoW, sizeof(MRUINFOW));
-    mp->extview.lpszSubKey = Alloc((lstrlenW(infoW->lpszSubKey) + 1) * sizeof(WCHAR));
-    lstrcpyW(mp->extview.lpszSubKey, infoW->lpszSubKey);
-    mp->isUnicode = TRUE;
-
-    return create_mru_list(mp);
-}
-
-/**************************************************************************
- *                  CreateMRUListLazyA [COMCTL32.157]
- *
- * 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.
- */
-HANDLE WINAPI CreateMRUListLazyA (const MRUINFOA *lpcml, DWORD dwParam2,
-                                  DWORD dwParam3, DWORD dwParam4)
-{
-    LPWINEMRULIST mp;
-    DWORD len;
-
-    /* Native does not check for a NULL lpcml */
-
-    if (!lpcml->hKey || IsBadStringPtrA(lpcml->lpszSubKey, -1))
-	return 0;
-
-    mp = Alloc(sizeof(WINEMRULIST));
-    memcpy(&mp->extview, lpcml, sizeof(MRUINFOA));
-    len = MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1, NULL, 0);
-    mp->extview.lpszSubKey = Alloc(len * sizeof(WCHAR));
-    MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1,
-			mp->extview.lpszSubKey, len);
-    mp->isUnicode = FALSE;
-    return create_mru_list(mp);
-}
-
-/**************************************************************************
- *              CreateMRUListW [COMCTL32.400]
- *
- * See CreateMRUListA.
- */
-HANDLE WINAPI CreateMRUListW (const MRUINFOW *infoW)
-{
-    return CreateMRUListLazyW(infoW, 0, 0, 0);
-}
-
-/**************************************************************************
- *              CreateMRUListA [COMCTL32.151]
- *
- * Creates a most-recently-used list.
- *
- * PARAMS
- *     lpcml [I] ptr to CREATEMRULIST structure.
- *
- * RETURNS
- *     Handle to MRU list.
- */
-HANDLE WINAPI CreateMRUListA (const MRUINFOA *lpcml)
-{
-     return CreateMRUListLazyA (lpcml, 0, 0, 0);
-}
-
-
-/**************************************************************************
- *                EnumMRUListW [COMCTL32.403]
- *
- * Enumerate item in a most-recently-used list
- *
- * 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
- *    the list.
- */
-INT WINAPI EnumMRUListW (HANDLE hList, INT nItemPos, LPVOID lpBuffer,
-                         DWORD nBufferSize)
-{
-    const WINEMRULIST *mp = hList;
-    const WINEMRUITEM *witem;
-    INT desired, datasize;
-
-    if (!mp) return -1;
-    if ((nItemPos < 0) || !lpBuffer) return mp->cursize;
-    if (nItemPos >= mp->cursize) return -1;
-    desired = mp->realMRU[nItemPos];
-    desired -= 'a';
-    TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired);
-    witem = mp->array[desired];
-    datasize = min( witem->size, nBufferSize );
-    memcpy( lpBuffer, &witem->datastart, datasize);
-    TRACE("(%p, %d, %p, %d): returning len=%d\n",
-	  hList, nItemPos, lpBuffer, nBufferSize, datasize);
-    return datasize;
-}
-
-/**************************************************************************
- *                EnumMRUListA [COMCTL32.154]
- *
- * See EnumMRUListW.
- */
-INT WINAPI EnumMRUListA (HANDLE hList, INT nItemPos, LPVOID lpBuffer,
-                         DWORD nBufferSize)
-{
-    const WINEMRULIST *mp = hList;
-    LPWINEMRUITEM witem;
-    INT desired, datasize;
-    DWORD lenA;
-
-    if (!mp) return -1;
-    if ((nItemPos < 0) || !lpBuffer) return mp->cursize;
-    if (nItemPos >= mp->cursize) return -1;
-    desired = mp->realMRU[nItemPos];
-    desired -= 'a';
-    TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired);
-    witem = mp->array[desired];
-    if(mp->extview.fFlags & MRU_BINARY) {
-        datasize = min( witem->size, nBufferSize );
-	memcpy( lpBuffer, &witem->datastart, datasize);
-    } else {
-        lenA = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1,
-				   NULL, 0, NULL, NULL);
-	datasize = min( lenA, nBufferSize );
-	WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1,
-			    lpBuffer, datasize, NULL, NULL);
-        ((char *)lpBuffer)[ datasize - 1 ] = '\0';
-        datasize = lenA - 1;
-    }
-    TRACE("(%p, %d, %p, %d): returning len=%d\n",
-	  hList, nItemPos, lpBuffer, nBufferSize, datasize);
-    return datasize;
-}
-
-/**************************************************************************
- * 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.
- */
-
-INT Str_GetPtrWtoA (LPCWSTR lpSrc, LPSTR lpDest, INT nMaxLen)
-{
-    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;
-}
-
-/**************************************************************************
- * 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;
-}
-
-
-/**************************************************************************
- * 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
- */
-BOOL Str_SetPtrAtoW (LPWSTR *lppDest, LPCSTR lpSrc)
-{
-    TRACE("(%p %s)\n", lppDest, lpSrc);
-
-    if (lpSrc) {
-	INT len = MultiByteToWideChar(CP_ACP,0,lpSrc,-1,NULL,0);
-	LPWSTR ptr = ReAlloc (*lppDest, len*sizeof(WCHAR));
-
-	if (!ptr)
-	    return FALSE;
-	MultiByteToWideChar(CP_ACP,0,lpSrc,-1,ptr,len);
-	*lppDest = ptr;
-    }
-    else {
-        Free (*lppDest);
-        *lppDest = NULL;
-    }
-
-    return TRUE;
-}
-
-/**************************************************************************
- * 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 {
-        Free (*lppDest);
-        *lppDest = NULL;
-    }
-
-    return TRUE;
-}
-
-
-/**************************************************************************
- * Notification functions
- */
-
-typedef struct tagNOTIFYDATA
-{
-    HWND hwndFrom;
-    HWND hwndTo;
-    DWORD  dwParam3;
-    DWORD  dwParam4;
-    DWORD  dwParam5;
-    DWORD  dwParam6;
-} NOTIFYDATA, *LPNOTIFYDATA;
-
-
-/**************************************************************************
- * DoNotify [Internal]
- */
-
-static LRESULT DoNotify (const NOTIFYDATA *lpNotify, UINT uCode, LPNMHDR lpHdr)
-{
-    NMHDR nmhdr;
-    LPNMHDR lpNmh = NULL;
-    UINT idFrom = 0;
-
-    TRACE("(%p %p %d %p 0x%08x)\n",
-	   lpNotify->hwndFrom, lpNotify->hwndTo, uCode, lpHdr,
-	   lpNotify->dwParam5);
-
-    if (!lpNotify->hwndTo)
-	return 0;
-
-    if (lpNotify->hwndFrom == (HWND)-1) {
-	lpNmh = lpHdr;
-	idFrom = lpHdr->idFrom;
-    }
-    else {
-	if (lpNotify->hwndFrom)
-	    idFrom = GetDlgCtrlID (lpNotify->hwndFrom);
-
-	lpNmh = (lpHdr) ? lpHdr : &nmhdr;
-
-	lpNmh->hwndFrom = lpNotify->hwndFrom;
-	lpNmh->idFrom = idFrom;
-	lpNmh->code = uCode;
-    }
-
-    return SendMessageW (lpNotify->hwndTo, WM_NOTIFY, idFrom, (LPARAM)lpNmh);
-}
-
-
-/**************************************************************************
- * SendNotify [COMCTL32.341]
- *
- * Sends a WM_NOTIFY message to the specified window.
- *
- * PARAMS
- *     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
- *
- * RETURNS
- *     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.
- */
-LRESULT WINAPI SendNotify (HWND hwndTo, HWND hwndFrom, UINT uCode, LPNMHDR lpHdr)
-{
-    NOTIFYDATA notify;
-
-    TRACE("(%p %p %d %p)\n",
-	   hwndTo, hwndFrom, uCode, lpHdr);
-
-    notify.hwndFrom = hwndFrom;
-    notify.hwndTo   = hwndTo;
-    notify.dwParam5 = 0;
-    notify.dwParam6 = 0;
-
-    return DoNotify (&notify, uCode, lpHdr);
-}
-
-
-/**************************************************************************
- * SendNotifyEx [COMCTL32.342]
- *
- * Sends a WM_NOTIFY message to the specified window.
- *
- * PARAMS
- *     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
- *
- * RETURNS
- *     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.
- */
-LRESULT WINAPI SendNotifyEx (HWND hwndTo, HWND hwndFrom, UINT uCode,
-                             LPNMHDR lpHdr, DWORD dwParam5)
-{
-    NOTIFYDATA notify;
-    HWND hwndNotify;
-
-    TRACE("(%p %p %d %p 0x%08x)\n",
-	   hwndFrom, hwndTo, uCode, lpHdr, dwParam5);
-
-    hwndNotify = hwndTo;
-    if (!hwndTo) {
-	if (IsWindow (hwndFrom)) {
-	    hwndNotify = GetParent (hwndFrom);
-	    if (!hwndNotify)
-		return 0;
-	}
-    }
-
-    notify.hwndFrom = hwndFrom;
-    notify.hwndTo   = hwndNotify;
-    notify.dwParam5 = dwParam5;
-    notify.dwParam6 = 0;
-
-    return DoNotify (&notify, uCode, lpHdr);
-}
diff --git a/dlls/comctl32/commctrl.c b/dlls/comctl32/commctrl.c
index ac53a2cf0f..744188a64a 100644
--- a/dlls/comctl32/commctrl.c
+++ b/dlls/comctl32/commctrl.c
@@ -2,6 +2,7 @@
  * Common controls functions
  *
  * Copyright 1997 Dimitrie O. Paun
+ *           1998 Juergen Schmied <j.schmied at metronet.de>
  * Copyright 1998,2000 Eric Kohl
  * Copyright 2014-2015 Michael Müller
  *
@@ -57,6 +58,9 @@
 #include <string.h>
 #include <stdlib.h>
 
+#define COBJMACROS
+#define NONAMELESSUNION
+
 #include "windef.h"
 #include "winbase.h"
 #include "wingdi.h"
@@ -1733,3 +1737,961 @@ HRESULT WINAPI LoadIconMetric(HINSTANCE hinst, const WCHAR *name, int size, HICO
 
     return LoadIconWithScaleDown(hinst, name, cx, cy, icon);
 }
+
+static const WCHAR strMRUList[] = { 'M','R','U','L','i','s','t',0 };
+
+/**************************************************************************
+ * Alloc [COMCTL32.71]
+ *
+ * Allocates memory block from the dll's private heap
+ */
+void * WINAPI Alloc(DWORD size)
+{
+    return LocalAlloc(LMEM_ZEROINIT, size);
+}
+
+/**************************************************************************
+ * ReAlloc [COMCTL32.72]
+ *
+ * Changes the size of an allocated memory block or allocates a memory
+ * block using the dll's private heap.
+ *
+ */
+void * WINAPI ReAlloc(void *src, DWORD size)
+{
+    if (src)
+        return LocalReAlloc(src, size, LMEM_ZEROINIT | LMEM_MOVEABLE);
+    else
+        return LocalAlloc(LMEM_ZEROINIT, size);
+}
+
+/**************************************************************************
+ * Free [COMCTL32.73]
+ *
+ * Frees an allocated memory block from the dll's private heap.
+ */
+BOOL WINAPI Free(void *mem)
+{
+    return !LocalFree(mem);
+}
+
+/**************************************************************************
+ * GetSize [COMCTL32.74]
+ */
+DWORD WINAPI GetSize(void *mem)
+{
+    return LocalSize(mem);
+}
+
+/**************************************************************************
+ * MRU-Functions  {COMCTL32}
+ *
+ * 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);
+ *|
+ *|typedef struct tagMRUINFO
+ *|{
+ *|    DWORD   cbSize;
+ *|    UINT    uMax;
+ *|    UINT    fFlags;
+ *|    HKEY    hKey;
+ *|    LPTSTR  lpszSubKey;
+ *|    PROC    lpfnCompare;
+ *|} MRUINFO, *LPMRUINFO;
+ *
+ * MEMBERS
+ *  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
+ *                  of the limited number of identifiers, this should be set to
+ *                  a value from 1 to 30 by the caller.
+ *  fFlags      [I] If bit 0 is set, the list will be used to store binary
+ *                  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.
+ *  lpfnCompare [I] A caller supplied comparison function, which may be either
+ *                  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().
+ */
+
+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);
+
+struct MRUINFOA
+{
+    DWORD  cbSize;
+    UINT   uMax;
+    UINT   fFlags;
+    HKEY   hKey;
+    LPSTR  lpszSubKey;
+    union
+    {
+        MRUStringCmpFnA string_cmpfn;
+        MRUBinaryCmpFn  binary_cmpfn;
+    } u;
+};
+
+struct MRUINFOW
+{
+    DWORD   cbSize;
+    UINT    uMax;
+    UINT    fFlags;
+    HKEY    hKey;
+    LPWSTR  lpszSubKey;
+    union
+    {
+        MRUStringCmpFnW string_cmpfn;
+        MRUBinaryCmpFn  binary_cmpfn;
+    } u;
+};
+
+/* 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 */
+
+/* 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?
+ */
+
+typedef struct tagWINEMRUITEM
+{
+    DWORD          size;        /* size of data stored               */
+    DWORD          itemFlag;    /* flags                             */
+    BYTE           datastart;
+} WINEMRUITEM, *LPWINEMRUITEM;
+
+/* itemFlag */
+#define WMRUIF_CHANGED   0x0001 /* this dataitem changed             */
+
+typedef struct tagWINEMRULIST
+{
+    struct MRUINFOW extview;    /* original create information       */
+    BOOL           isUnicode;   /* is compare fn Unicode */
+    DWORD          wineFlags;   /* internal flags                    */
+    DWORD          cursize;     /* current size of realMRU           */
+    LPWSTR         realMRU;     /* pointer to string of index names  */
+    LPWINEMRUITEM  *array;      /* array of pointers to data         */
+                                /* in 'a' to 'z' order               */
+} WINEMRULIST, *LPWINEMRULIST;
+
+/* wineFlags */
+#define WMRUF_CHANGED  0x0001   /* MRU list has changed              */
+
+/**************************************************************************
+ *              MRU_SaveChanged (internal)
+ *
+ * Local MRU saving code
+ */
+static void MRU_SaveChanged(WINEMRULIST *mp)
+{
+    UINT i, err;
+    HKEY newkey;
+    WCHAR realname[2];
+    WINEMRUITEM *witem;
+
+    /* or should we do the following instead of RegOpenKeyEx:
+     */
+
+    /* open the sub key */
+    if ((err = RegOpenKeyExW(mp->extview.hKey, mp->extview.lpszSubKey, 0, KEY_WRITE, &newkey)))
+    {
+        /* not present - what to do ??? */
+        ERR("Could not open key, error=%d, attempting to create\n", err);
+        if ((err = RegCreateKeyExW( mp->extview.hKey, mp->extview.lpszSubKey, 0, NULL, REG_OPTION_NON_VOLATILE,
+                KEY_READ | KEY_WRITE, 0, &newkey, 0)))
+        {
+            ERR("failed to create key /%s/, err=%d\n", debugstr_w(mp->extview.lpszSubKey), err);
+            return;
+        }
+    }
+
+    if (mp->wineFlags & WMRUF_CHANGED)
+    {
+        mp->wineFlags &= ~WMRUF_CHANGED;
+        if ((err = RegSetValueExW(newkey, strMRUList, 0, REG_SZ, (BYTE *)mp->realMRU,
+                (lstrlenW(mp->realMRU) + 1)*sizeof(WCHAR))))
+        {
+            ERR("error saving MRUList, err=%d\n", err);
+        }
+        TRACE("saving MRUList=/%s/\n", debugstr_w(mp->realMRU));
+    }
+
+    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;
+            if ((err = RegSetValueExW(newkey, realname, 0, (mp->extview.fFlags & MRU_BINARY) ?
+                    REG_BINARY : REG_SZ, &witem->datastart, witem->size)))
+            {
+                ERR("error saving /%s/, err=%d\n", debugstr_w(realname), err);
+            }
+            TRACE("saving value for name /%s/ size=%d\n", debugstr_w(realname), witem->size);
+        }
+    }
+    RegCloseKey(newkey);
+}
+
+/**************************************************************************
+ *              FreeMRUList [COMCTL32.152]
+ *
+ * Frees a most-recently-used items list.
+ */
+void WINAPI FreeMRUList(HANDLE hMRUList)
+{
+    WINEMRULIST *mp = hMRUList;
+    unsigned int i;
+
+    TRACE("%p.\n", hMRUList);
+
+    if (!hMRUList)
+        return;
+
+    if (mp->wineFlags & WMRUF_CHANGED)
+    {
+        /* need to open key and then save the info */
+        MRU_SaveChanged(mp);
+    }
+
+    for (i = 0; i < mp->extview.uMax; ++i)
+        Free(mp->array[i]);
+
+    Free(mp->realMRU);
+    Free(mp->array);
+    Free(mp->extview.lpszSubKey);
+    Free(mp);
+}
+
+/**************************************************************************
+ *                  FindMRUData [COMCTL32.169]
+ *
+ * Searches binary list for item that matches data of given length.
+ * Returns position in list order 0 -> MRU and value corresponding to item's reg.
+ * name will be stored in it ('a' -> 0).
+ *
+ */
+INT WINAPI FindMRUData(HANDLE hList, const void *data, DWORD cbData, int *pos)
+{
+    const WINEMRULIST *mp = hList;
+    INT ret;
+    UINT i;
+    LPSTR dataA = NULL;
+
+    if (!mp || !mp->extview.u.string_cmpfn)
+        return -1;
+
+    if (!(mp->extview.fFlags & MRU_BINARY) && !mp->isUnicode)
+    {
+        DWORD len = WideCharToMultiByte(CP_ACP, 0, data, -1, NULL, 0, NULL, NULL);
+        dataA = Alloc(len);
+        WideCharToMultiByte(CP_ACP, 0, data, -1, dataA, len, NULL, NULL);
+    }
+
+    for (i = 0; i < mp->cursize; ++i)
+    {
+        if (mp->extview.fFlags & MRU_BINARY)
+        {
+            if (!mp->extview.u.binary_cmpfn(data, &mp->array[i]->datastart, cbData))
+                break;
+        }
+        else
+        {
+            if (mp->isUnicode)
+            {
+                if (!mp->extview.u.string_cmpfn(data, (LPWSTR)&mp->array[i]->datastart))
+                    break;
+            }
+            else
+            {
+                DWORD len = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&mp->array[i]->datastart, -1,
+                        NULL, 0, NULL, NULL);
+                LPSTR itemA = Alloc(len);
+                INT cmp;
+                WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&mp->array[i]->datastart, -1, itemA, len, NULL, NULL);
+
+                cmp = mp->extview.u.string_cmpfn((LPWSTR)dataA, (LPWSTR)itemA);
+                Free(itemA);
+                if (!cmp)
+                    break;
+            }
+        }
+    }
+
+    Free(dataA);
+    if (i < mp->cursize)
+        ret = i;
+    else
+        ret = -1;
+    if (pos && (ret != -1))
+        *pos = 'a' + i;
+
+    TRACE("%p, %p, %d, %p, returning %d.\n", hList, data, cbData, pos, ret);
+
+    return ret;
+}
+
+/**************************************************************************
+ *              AddMRUData [COMCTL32.167]
+ *
+ * Add item to MRU binary list.  If item already exists in list then it is
+ * 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.
+ *
+ */
+INT WINAPI AddMRUData(HANDLE hList, const void *data, DWORD cbData)
+{
+    WINEMRULIST *mp = hList;
+    WINEMRUITEM *witem;
+    INT i, replace;
+
+    if ((replace = FindMRUData(hList, data, cbData, NULL)) >= 0)
+    {
+        /* Item exists, just move it to the front */
+        LPWSTR pos = wcschr(mp->realMRU, replace + 'a');
+        while (pos > mp->realMRU)
+        {
+            pos[0] = pos[-1];
+            pos--;
+        }
+    }
+    else
+    {
+        /* either add a new entry or replace oldest */
+        if (mp->cursize < mp->extview.uMax)
+        {
+            /* 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';
+            Free(mp->array[replace]);
+        }
+
+        /* 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, data, cbData);
+
+        /* now rotate MRU list */
+        for (i = mp->cursize - 1; i >= 1; --i)
+            mp->realMRU[i] = mp->realMRU[i-1];
+    }
+
+    /* The new item gets the front spot */
+    mp->wineFlags |= WMRUF_CHANGED;
+    mp->realMRU[0] = replace + 'a';
+
+    TRACE("(%p, %p, %d) adding data, /%c/ now most current\n", hList, data, cbData, replace+'a');
+
+    if (!(mp->extview.fFlags & MRU_CACHEWRITE))
+    {
+        /* save changed stuff right now */
+        MRU_SaveChanged(mp);
+    }
+
+    return replace;
+}
+
+/**************************************************************************
+ *              AddMRUStringW [COMCTL32.401]
+ *
+ * Add an item to an MRU string list.
+ *
+ */
+INT WINAPI AddMRUStringW(HANDLE hList, const WCHAR *str)
+{
+    TRACE("%p, %s.\n", hList, debugstr_w(str));
+
+    if (!hList)
+        return -1;
+
+    if (!str || IsBadStringPtrW(str, -1))
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return 0;
+    }
+
+    return AddMRUData(hList, str, (lstrlenW(str) + 1) * sizeof(WCHAR));
+}
+
+/**************************************************************************
+ *              AddMRUStringA [COMCTL32.153]
+ */
+INT WINAPI AddMRUStringA(HANDLE hList, const char *str)
+{
+    WCHAR *strW;
+    DWORD len;
+    INT ret;
+
+    TRACE("%p, %s.\n", hList, debugstr_a(str));
+
+    if (!hList)
+        return -1;
+
+    if (IsBadStringPtrA(str, -1))
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return 0;
+    }
+
+    len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0) * sizeof(WCHAR);
+    strW = Alloc(len);
+    if (!strW)
+        return -1;
+
+    MultiByteToWideChar(CP_ACP, 0, str, -1, strW, len/sizeof(WCHAR));
+    ret = AddMRUData(hList, strW, len);
+    Free(strW);
+    return ret;
+}
+
+/**************************************************************************
+ *              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
+ *    TRUE if successful, FALSE if nItemPos is out of range.
+ */
+BOOL WINAPI DelMRUString(HANDLE hList, INT nItemPos)
+{
+    FIXME("(%p, %d): stub\n", hList, nItemPos);
+    return TRUE;
+}
+
+/**************************************************************************
+ *                  FindMRUStringW [COMCTL32.402]
+ */
+INT WINAPI FindMRUStringW(HANDLE hList, const WCHAR *str, int *pos)
+{
+    return FindMRUData(hList, str, (lstrlenW(str) + 1) * sizeof(WCHAR), pos);
+}
+
+/**************************************************************************
+ *                  FindMRUStringA [COMCTL32.155]
+ *
+ * Searches string list for item that matches given string.
+ *
+ * RETURNS
+ *    Position in list 0 -> MRU.  -1 if item not found.
+ */
+INT WINAPI FindMRUStringA(HANDLE hList, const char *str, int *pos)
+{
+    DWORD len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
+    WCHAR *strW = Alloc(len * sizeof(*strW));
+    INT ret;
+
+    MultiByteToWideChar(CP_ACP, 0, str, -1, strW, len);
+    ret = FindMRUData(hList, strW, len * sizeof(WCHAR), pos);
+    Free(strW);
+    return ret;
+}
+
+/*************************************************************************
+ *                 create_mru_list (internal)
+ */
+static HANDLE create_mru_list(WINEMRULIST *mp)
+{
+    UINT i, err;
+    HKEY newkey;
+    DWORD datasize, dwdisp;
+    WCHAR realname[2];
+    WINEMRUITEM *witem;
+    DWORD type;
+
+    /* get space to save indices that will turn into names
+     * but in order of most to least recently used
+     */
+    mp->realMRU = Alloc((mp->extview.uMax + 2) * sizeof(WCHAR));
+
+    /* get space to save pointers to actual data in order of
+     * 'a' to 'z' (0 to n).
+     */
+    mp->array = Alloc(mp->extview.uMax * sizeof(LPVOID));
+
+    /* open the sub key */
+    if ((err = RegCreateKeyExW(mp->extview.hKey, mp->extview.lpszSubKey, 0, NULL, REG_OPTION_NON_VOLATILE,
+            KEY_READ | KEY_WRITE, 0, &newkey, &dwdisp)))
+    {
+        /* error - what to do ??? */
+        ERR("(%u %u %x %p %s %p): Could not open key, error=%d\n", mp->extview.cbSize, mp->extview.uMax, mp->extview.fFlags,
+                mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey), mp->extview.u.string_cmpfn, err);
+        return 0;
+    }
+
+    /* get values from key 'MRUList' */
+    if (newkey)
+    {
+        datasize = (mp->extview.uMax + 1) * sizeof(WCHAR);
+        if (RegQueryValueExW( newkey, strMRUList, 0, &type, (BYTE *)mp->realMRU, &datasize))
+        {
+            /* not present - set size to 1 (will become 0 later) */
+            datasize = 1;
+            *mp->realMRU = 0;
+        }
+        else
+            datasize /= sizeof(WCHAR);
+
+        TRACE("MRU list = %s, datasize = %d\n", debugstr_w(mp->realMRU), datasize);
+
+        mp->cursize = datasize - 1;
+        /* datasize now has number of items in the MRUList */
+
+        /* get actual values for each entry */
+        realname[1] = 0;
+        for (i = 0; i < mp->cursize; ++i)
+        {
+            realname[0] = 'a' + i;
+            if (RegQueryValueExW(newkey, realname, 0, &type, 0, &datasize))
+            {
+                /* not present - what to do ??? */
+                ERR("Key %s not found 1\n", debugstr_w(realname));
+            }
+            mp->array[i] = witem = Alloc(datasize + sizeof(WINEMRUITEM));
+            witem->size = datasize;
+            if (RegQueryValueExW(newkey, realname, 0, &type, &witem->datastart, &datasize))
+            {
+                /* not present - what to do ??? */
+                ERR("Key %s not found 2\n", debugstr_w(realname));
+            }
+        }
+        RegCloseKey( newkey );
+    }
+    else
+        mp->cursize = 0;
+
+    TRACE("(%u %u %x %p %s %p): Current Size = %d\n", mp->extview.cbSize, mp->extview.uMax, mp->extview.fFlags,
+            mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey), mp->extview.u.string_cmpfn, mp->cursize);
+    return mp;
+}
+
+/**************************************************************************
+ *                  CreateMRUListLazyW [COMCTL32.404]
+ */
+HANDLE WINAPI CreateMRUListLazyW(const struct MRUINFOW *info, DWORD dwParam2, DWORD dwParam3, DWORD dwParam4)
+{
+    WINEMRULIST *mp;
+
+    /* Native does not check for a NULL. */
+    if (!info->hKey || IsBadStringPtrW(info->lpszSubKey, -1))
+        return NULL;
+
+    mp = Alloc(sizeof(*mp));
+    memcpy(&mp->extview, info, sizeof(*info));
+    mp->extview.lpszSubKey = Alloc((lstrlenW(info->lpszSubKey) + 1) * sizeof(WCHAR));
+    lstrcpyW(mp->extview.lpszSubKey, info->lpszSubKey);
+    mp->isUnicode = TRUE;
+
+    return create_mru_list(mp);
+}
+
+/**************************************************************************
+ *                  CreateMRUListLazyA [COMCTL32.157]
+ *
+ * Creates a most-recently-used list.
+ */
+HANDLE WINAPI CreateMRUListLazyA(const struct MRUINFOA *info, DWORD dwParam2, DWORD dwParam3, DWORD dwParam4)
+{
+    WINEMRULIST *mp;
+    DWORD len;
+
+    /* Native does not check for a NULL lpcml */
+
+    if (!info->hKey || IsBadStringPtrA(info->lpszSubKey, -1))
+        return 0;
+
+    mp = Alloc(sizeof(*mp));
+    memcpy(&mp->extview, info, sizeof(*info));
+    len = MultiByteToWideChar(CP_ACP, 0, info->lpszSubKey, -1, NULL, 0);
+    mp->extview.lpszSubKey = Alloc(len * sizeof(WCHAR));
+    MultiByteToWideChar(CP_ACP, 0, info->lpszSubKey, -1, mp->extview.lpszSubKey, len);
+    mp->isUnicode = FALSE;
+    return create_mru_list(mp);
+}
+
+/**************************************************************************
+ *              CreateMRUListW [COMCTL32.400]
+ */
+HANDLE WINAPI CreateMRUListW(const struct MRUINFOW *info)
+{
+    return CreateMRUListLazyW(info, 0, 0, 0);
+}
+
+/**************************************************************************
+ *              CreateMRUListA [COMCTL32.151]
+ */
+HANDLE WINAPI CreateMRUListA(const struct MRUINFOA *info)
+{
+     return CreateMRUListLazyA(info, 0, 0, 0);
+}
+
+/**************************************************************************
+ *                EnumMRUListW [COMCTL32.403]
+ *
+ * Enumerate item in a most-recently-used list
+ *
+ * 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
+ *    the list.
+ */
+INT WINAPI EnumMRUListW(HANDLE hList, INT nItemPos, void *buffer, DWORD nBufferSize)
+{
+    const WINEMRULIST *mp = hList;
+    const WINEMRUITEM *witem;
+    INT desired, datasize;
+
+    if (!mp) return -1;
+    if ((nItemPos < 0) || !buffer) return mp->cursize;
+    if (nItemPos >= mp->cursize) return -1;
+    desired = mp->realMRU[nItemPos];
+    desired -= 'a';
+    TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired);
+    witem = mp->array[desired];
+    datasize = min(witem->size, nBufferSize);
+    memcpy(buffer, &witem->datastart, datasize);
+    TRACE("(%p, %d, %p, %d): returning len=%d\n", hList, nItemPos, buffer, nBufferSize, datasize);
+    return datasize;
+}
+
+/**************************************************************************
+ *                EnumMRUListA [COMCTL32.154]
+ */
+INT WINAPI EnumMRUListA(HANDLE hList, INT nItemPos, void *buffer, DWORD nBufferSize)
+{
+    const WINEMRULIST *mp = hList;
+    WINEMRUITEM *witem;
+    INT desired, datasize;
+    DWORD lenA;
+
+    if (!mp) return -1;
+    if ((nItemPos < 0) || !buffer) return mp->cursize;
+    if (nItemPos >= mp->cursize) return -1;
+    desired = mp->realMRU[nItemPos];
+    desired -= 'a';
+    TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired);
+    witem = mp->array[desired];
+    if (mp->extview.fFlags & MRU_BINARY)
+    {
+        datasize = min(witem->size, nBufferSize);
+        memcpy(buffer, &witem->datastart, datasize);
+    }
+    else
+    {
+        lenA = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1, NULL, 0, NULL, NULL);
+        datasize = min(lenA, nBufferSize);
+        WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1, buffer, datasize, NULL, NULL);
+        ((char *)buffer)[ datasize - 1 ] = '\0';
+        datasize = lenA - 1;
+    }
+    TRACE("(%p, %d, %p, %d): returning len=%d\n", hList, nItemPos, buffer, nBufferSize, datasize);
+    return datasize;
+}
+
+/**************************************************************************
+ * Str_GetPtrWtoA [internal]
+ *
+ * Converts a unicode string into a multi byte string
+ *
+ */
+
+INT Str_GetPtrWtoA(const WCHAR *src, char *dst, INT nMaxLen)
+{
+    INT len;
+
+    TRACE("%s, %p, %d.\n", debugstr_w(src), dst, nMaxLen);
+
+    if (!dst && src)
+        return WideCharToMultiByte(CP_ACP, 0, src, -1, 0, 0, NULL, NULL);
+
+    if (!nMaxLen)
+        return 0;
+
+    if (!src)
+    {
+        dst[0] = 0;
+        return 0;
+    }
+
+    len = WideCharToMultiByte(CP_ACP, 0, src, -1, 0, 0, NULL, NULL);
+    if (len >= nMaxLen)
+        len = nMaxLen - 1;
+
+    WideCharToMultiByte(CP_ACP, 0, src, -1, dst, len, NULL, NULL);
+    dst[len] = '\0';
+
+    return len;
+}
+
+/**************************************************************************
+ * Str_GetPtrAtoW [internal]
+ *
+ * Converts a multibyte string into a unicode string
+ */
+
+INT Str_GetPtrAtoW(const char *src, WCHAR *dst, INT nMaxLen)
+{
+    INT len;
+
+    TRACE("%s, %p, %d.\n", debugstr_a(src), dst, nMaxLen);
+
+    if (!dst && src)
+        return MultiByteToWideChar(CP_ACP, 0, src, -1, 0, 0);
+
+    if (!nMaxLen)
+        return 0;
+
+    if (!src)
+    {
+        *dst = 0;
+        return 0;
+    }
+
+    len = MultiByteToWideChar(CP_ACP, 0, src, -1, 0, 0);
+    if (len >= nMaxLen)
+        len = nMaxLen - 1;
+
+    MultiByteToWideChar(CP_ACP, 0, src, -1, dst, len);
+    dst[len] = 0;
+
+    return len;
+}
+
+/**************************************************************************
+ * 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
+ */
+BOOL Str_SetPtrAtoW(WCHAR **dst, const char *src)
+{
+    TRACE("%p, %s.\n", dst, debugstr_a(src));
+
+    if (src)
+    {
+        INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
+        LPWSTR ptr = ReAlloc(*dst, len * sizeof(**dst));
+
+        if (!ptr)
+            return FALSE;
+        MultiByteToWideChar(CP_ACP, 0, src, -1, ptr, len);
+        *dst = ptr;
+    }
+    else
+    {
+        Free(*dst);
+        *dst = NULL;
+    }
+
+    return TRUE;
+}
+
+/**************************************************************************
+ * 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.
+ */
+BOOL Str_SetPtrWtoA(char **dst, const WCHAR *src)
+{
+    TRACE("%p, %s.\n", dst, debugstr_w(src));
+
+    if (src)
+    {
+        INT len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, FALSE);
+        LPSTR ptr = ReAlloc(*dst, len * sizeof(**dst));
+
+        if (!ptr)
+            return FALSE;
+        WideCharToMultiByte(CP_ACP, 0, src, -1, ptr, len, NULL, FALSE);
+        *dst = ptr;
+    }
+    else
+    {
+        Free(*dst);
+        *dst = NULL;
+    }
+
+    return TRUE;
+}
+
+/**************************************************************************
+ * Notification functions
+ */
+
+struct NOTIFYDATA
+{
+    HWND hwndFrom;
+    HWND hwndTo;
+    DWORD  dwParam3;
+    DWORD  dwParam4;
+    DWORD  dwParam5;
+    DWORD  dwParam6;
+};
+
+/**************************************************************************
+ * DoNotify [Internal]
+ */
+
+static LRESULT DoNotify(const struct NOTIFYDATA *notify, UINT code, NMHDR *hdr)
+{
+    NMHDR nmhdr;
+    NMHDR *lpNmh = NULL;
+    UINT idFrom = 0;
+
+    TRACE("%p, %p, %d, %p, %#x.\n", notify->hwndFrom, notify->hwndTo, code, hdr, notify->dwParam5);
+
+    if (!notify->hwndTo)
+        return 0;
+
+    if (notify->hwndFrom == (HWND)-1)
+    {
+        lpNmh = hdr;
+        idFrom = hdr->idFrom;
+    }
+    else
+    {
+        if (notify->hwndFrom)
+            idFrom = GetDlgCtrlID(notify->hwndFrom);
+
+        lpNmh = hdr ? hdr : &nmhdr;
+        lpNmh->hwndFrom = notify->hwndFrom;
+        lpNmh->idFrom = idFrom;
+        lpNmh->code = code;
+    }
+
+    return SendMessageW(notify->hwndTo, WM_NOTIFY, idFrom, (LPARAM)lpNmh);
+}
+
+/**************************************************************************
+ * SendNotify [COMCTL32.341]
+ *
+ * Sends a WM_NOTIFY message to the specified window.
+ *
+ */
+LRESULT WINAPI SendNotify(HWND hwndTo, HWND hwndFrom, UINT code, NMHDR *hdr)
+{
+    struct NOTIFYDATA notify;
+
+    TRACE("%p, %p, %d, %p.\n", hwndTo, hwndFrom, code, hdr);
+
+    notify.hwndFrom = hwndFrom;
+    notify.hwndTo   = hwndTo;
+    notify.dwParam5 = 0;
+    notify.dwParam6 = 0;
+
+    return DoNotify(&notify, code, hdr);
+}
+
+/**************************************************************************
+ * SendNotifyEx [COMCTL32.342]
+ *
+ * Sends a WM_NOTIFY message to the specified window.
+ *
+ * PARAMS
+ *     hwndFrom [I] Window to receive the message
+ *     hwndTo   [I] Window that the message is from
+ *     code     [I] Notification code
+ *     hdr      [I] The NMHDR and any additional information to send or NULL
+ *     dwParam5 [I] Unknown
+ *
+ * RETURNS
+ *     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.
+ */
+LRESULT WINAPI SendNotifyEx(HWND hwndTo, HWND hwndFrom, UINT code, NMHDR *hdr, DWORD dwParam5)
+{
+    struct NOTIFYDATA notify;
+    HWND hwndNotify;
+
+    TRACE("(%p %p %d %p %#x)\n", hwndFrom, hwndTo, code, hdr, dwParam5);
+
+    hwndNotify = hwndTo;
+    if (!hwndTo)
+    {
+        if (IsWindow(hwndFrom))
+        {
+            hwndNotify = GetParent(hwndFrom);
+            if (!hwndNotify)
+                return 0;
+        }
+    }
+
+    notify.hwndFrom = hwndFrom;
+    notify.hwndTo   = hwndNotify;
+    notify.dwParam5 = dwParam5;
+    notify.dwParam6 = 0;
+
+    return DoNotify(&notify, code, hdr);
+}
-- 
2.26.2




More information about the wine-devel mailing list