1244 lines
34 KiB
C
1244 lines
34 KiB
C
/*
|
|
* Undocumented functions from COMCTL32.DLL
|
|
*
|
|
* Copyright 1998 Eric Kohl
|
|
* 1998 Juergen Schmied <j.schmied@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "config.h"
|
|
#include "wine/port.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
|
|
#define COBJMACROS
|
|
#define NONAMELESSUNION
|
|
#define NONAMELESSSTRUCT
|
|
|
|
#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 "wine/unicode.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 );
|
|
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 tagCREATEMRULIST
|
|
*|{
|
|
*| DWORD cbSize;
|
|
*| DWORD nMaxItems;
|
|
*| DWORD dwFlags;
|
|
*| HKEY hKey;
|
|
*| LPCTSTR lpszSubKey;
|
|
*| PROC lpfnCompare;
|
|
*|} CREATEMRULIST, *LPCREATEMRULIST;
|
|
*
|
|
* MEMBERS
|
|
* cbSize [I] The size of the CREATEMRULIST structure. This must be set
|
|
* to sizeof(CREATEMRULIST) by the caller.
|
|
* nMaxItems [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.
|
|
* dwFlags [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 struct tagCREATEMRULISTA
|
|
{
|
|
DWORD cbSize;
|
|
DWORD nMaxItems;
|
|
DWORD dwFlags;
|
|
HKEY hKey;
|
|
LPCSTR lpszSubKey;
|
|
PROC lpfnCompare;
|
|
} CREATEMRULISTA, *LPCREATEMRULISTA;
|
|
|
|
typedef struct tagCREATEMRULISTW
|
|
{
|
|
DWORD cbSize;
|
|
DWORD nMaxItems;
|
|
DWORD dwFlags;
|
|
HKEY hKey;
|
|
LPCWSTR lpszSubKey;
|
|
PROC lpfnCompare;
|
|
} CREATEMRULISTW, *LPCREATEMRULISTW;
|
|
|
|
/* dwFlags */
|
|
#define MRUF_STRING_LIST 0 /* list will contain strings */
|
|
#define MRUF_BINARY_LIST 1 /* list will contain binary data */
|
|
#define MRUF_DELAYED_SAVE 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
|
|
{
|
|
CREATEMRULISTW 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,
|
|
(strlenW(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.dwFlags & MRUF_BINARY_LIST) ?
|
|
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=%ld\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 = (LPWINEMRULIST)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.nMaxItems; i++) {
|
|
if (mp->array[i])
|
|
Free(mp->array[i]);
|
|
}
|
|
Free(mp->realMRU);
|
|
Free(mp->array);
|
|
Free((LPWSTR)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)
|
|
{
|
|
LPWINEMRULIST mp = (LPWINEMRULIST)hList;
|
|
INT ret;
|
|
UINT i;
|
|
LPSTR dataA = NULL;
|
|
|
|
if (!mp->extview.lpfnCompare) {
|
|
ERR("MRU list not properly created. No compare procedure.\n");
|
|
return -1;
|
|
}
|
|
|
|
if(!(mp->extview.dwFlags & MRUF_BINARY_LIST) && !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.dwFlags & MRUF_BINARY_LIST) {
|
|
if (!mp->extview.lpfnCompare(lpData, &mp->array[i]->datastart,
|
|
cbData))
|
|
break;
|
|
}
|
|
else {
|
|
if(mp->isUnicode) {
|
|
if (!mp->extview.lpfnCompare(lpData, &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.lpfnCompare(dataA, itemA);
|
|
Free(itemA);
|
|
if(!cmp)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(dataA)
|
|
Free(dataA);
|
|
if (i < mp->cursize)
|
|
ret = i;
|
|
else
|
|
ret = -1;
|
|
if (lpRegNum && (ret != -1))
|
|
*lpRegNum = 'a' + i;
|
|
|
|
TRACE("(%p, %p, %ld, %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 = (LPWINEMRULIST)hList;
|
|
LPWINEMRUITEM witem;
|
|
INT i, replace;
|
|
|
|
if ((replace = FindMRUData (hList, lpData, cbData, NULL)) >= 0) {
|
|
/* Item exists, just move it to the front */
|
|
LPWSTR pos = strchrW(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.nMaxItems) {
|
|
/* 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, %ld) adding data, /%c/ now most current\n",
|
|
hList, lpData, cbData, replace+'a');
|
|
|
|
if (!(mp->extview.dwFlags & MRUF_DELAYED_SAVE)) {
|
|
/* 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,
|
|
(strlenW(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);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* CreateMRUListLazy_common (internal)
|
|
*/
|
|
static HANDLE CreateMRUListLazy_common(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.nMaxItems + 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.nMaxItems * 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("(%lu %lu %lx %lx \"%s\" %p): Could not open key, error=%d\n",
|
|
mp->extview.cbSize, mp->extview.nMaxItems, mp->extview.dwFlags,
|
|
(DWORD)mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey),
|
|
mp->extview.lpfnCompare, err);
|
|
return 0;
|
|
}
|
|
|
|
/* get values from key 'MRUList' */
|
|
if (newkey) {
|
|
datasize = mp->extview.nMaxItems + 1;
|
|
if((err=RegQueryValueExW( newkey, strMRUList, 0, &type,
|
|
(LPBYTE)mp->realMRU, &datasize))) {
|
|
/* not present - set size to 1 (will become 0 later) */
|
|
datasize = 1;
|
|
*mp->realMRU = 0;
|
|
}
|
|
|
|
TRACE("MRU list = %s, datasize = %ld\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("(%lu %lu %lx %lx \"%s\" %p): Current Size = %ld\n",
|
|
mp->extview.cbSize, mp->extview.nMaxItems, mp->extview.dwFlags,
|
|
(DWORD)mp->extview.hKey, debugstr_w(mp->extview.lpszSubKey),
|
|
mp->extview.lpfnCompare, mp->cursize);
|
|
return (HANDLE)mp;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* CreateMRUListLazyW [COMCTL32.404]
|
|
*
|
|
* See CreateMRUListLazyA.
|
|
*/
|
|
HANDLE WINAPI CreateMRUListLazyW (LPCREATEMRULISTW lpcml, DWORD dwParam2,
|
|
DWORD dwParam3, DWORD dwParam4)
|
|
{
|
|
LPWINEMRULIST mp;
|
|
|
|
/* Native does not check for a NULL lpcml */
|
|
|
|
if (lpcml->cbSize != sizeof(CREATEMRULISTW) || !lpcml->hKey ||
|
|
IsBadStringPtrW(lpcml->lpszSubKey, -1))
|
|
return NULL;
|
|
|
|
mp = Alloc(sizeof(WINEMRULIST));
|
|
memcpy(&mp->extview, lpcml, sizeof(CREATEMRULISTW));
|
|
mp->extview.lpszSubKey = Alloc((strlenW(lpcml->lpszSubKey) + 1) * sizeof(WCHAR));
|
|
strcpyW((LPWSTR)mp->extview.lpszSubKey, lpcml->lpszSubKey);
|
|
mp->isUnicode = TRUE;
|
|
|
|
return CreateMRUListLazy_common(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 (LPCREATEMRULISTA lpcml, DWORD dwParam2,
|
|
DWORD dwParam3, DWORD dwParam4)
|
|
{
|
|
LPWINEMRULIST mp;
|
|
DWORD len;
|
|
|
|
/* Native does not check for a NULL lpcml */
|
|
|
|
if (lpcml->cbSize != sizeof(CREATEMRULISTA) || !lpcml->hKey ||
|
|
IsBadStringPtrA(lpcml->lpszSubKey, -1))
|
|
return 0;
|
|
|
|
mp = Alloc(sizeof(WINEMRULIST));
|
|
memcpy(&mp->extview, lpcml, sizeof(CREATEMRULISTW));
|
|
len = MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1, NULL, 0);
|
|
mp->extview.lpszSubKey = Alloc(len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, lpcml->lpszSubKey, -1,
|
|
(LPWSTR)mp->extview.lpszSubKey, len);
|
|
mp->isUnicode = FALSE;
|
|
return CreateMRUListLazy_common(mp);
|
|
}
|
|
|
|
/**************************************************************************
|
|
* CreateMRUListW [COMCTL32.400]
|
|
*
|
|
* See CreateMRUListA.
|
|
*/
|
|
HANDLE WINAPI CreateMRUListW (LPCREATEMRULISTW lpcml)
|
|
{
|
|
return CreateMRUListLazyW(lpcml, 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 (LPCREATEMRULISTA lpcml)
|
|
{
|
|
return CreateMRUListLazyA (lpcml, 0, 0, 0);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* EnumMRUListW [COMCTL32.403]
|
|
*
|
|
* Enumerate item in a most-recenty-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)
|
|
{
|
|
LPWINEMRULIST mp = (LPWINEMRULIST) hList;
|
|
LPWINEMRUITEM witem;
|
|
INT desired, datasize;
|
|
|
|
if (nItemPos >= mp->cursize) return -1;
|
|
if ((nItemPos < 0) || !lpBuffer) return mp->cursize;
|
|
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, %ld): 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)
|
|
{
|
|
LPWINEMRULIST mp = (LPWINEMRULIST) hList;
|
|
LPWINEMRUITEM witem;
|
|
INT desired, datasize;
|
|
DWORD lenA;
|
|
|
|
if (nItemPos >= mp->cursize) return -1;
|
|
if ((nItemPos < 0) || !lpBuffer) return mp->cursize;
|
|
desired = mp->realMRU[nItemPos];
|
|
desired -= 'a';
|
|
TRACE("nItemPos=%d, desired=%d\n", nItemPos, desired);
|
|
witem = mp->array[desired];
|
|
if(mp->extview.dwFlags & MRUF_BINARY_LIST) {
|
|
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( witem->size, nBufferSize );
|
|
WideCharToMultiByte(CP_ACP, 0, (LPWSTR)&witem->datastart, -1,
|
|
lpBuffer, datasize, NULL, NULL);
|
|
}
|
|
TRACE("(%p, %d, %p, %ld): returning len=%d\n",
|
|
hList, nItemPos, lpBuffer, nBufferSize, datasize);
|
|
return datasize;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* Str_GetPtrA [COMCTL32.233]
|
|
*
|
|
* Copies a string into a destination buffer.
|
|
*
|
|
* PARAMS
|
|
* lpSrc [I] Source string
|
|
* lpDest [O] Destination buffer
|
|
* nMaxLen [I] Size of buffer in characters
|
|
*
|
|
* RETURNS
|
|
* The number of characters copied.
|
|
*/
|
|
INT WINAPI Str_GetPtrA (LPCSTR lpSrc, LPSTR lpDest, INT nMaxLen)
|
|
{
|
|
INT len;
|
|
|
|
TRACE("(%p %p %d)\n", lpSrc, lpDest, nMaxLen);
|
|
|
|
if (!lpDest && lpSrc)
|
|
return strlen (lpSrc);
|
|
|
|
if (nMaxLen == 0)
|
|
return 0;
|
|
|
|
if (lpSrc == NULL) {
|
|
lpDest[0] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
len = strlen (lpSrc);
|
|
if (len >= nMaxLen)
|
|
len = nMaxLen - 1;
|
|
|
|
RtlMoveMemory (lpDest, lpSrc, len);
|
|
lpDest[len] = '\0';
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* Str_SetPtrA [COMCTL32.234]
|
|
*
|
|
* Makes a copy of a string, allocating memory if necessary.
|
|
*
|
|
* PARAMS
|
|
* lppDest [O] Pointer to destination string
|
|
* lpSrc [I] Source string
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*
|
|
* NOTES
|
|
* Set lpSrc to NULL to free the memory allocated by a previous call
|
|
* to this function.
|
|
*/
|
|
BOOL WINAPI Str_SetPtrA (LPSTR *lppDest, LPCSTR lpSrc)
|
|
{
|
|
TRACE("(%p %p)\n", lppDest, lpSrc);
|
|
|
|
if (lpSrc) {
|
|
LPSTR ptr = ReAlloc (*lppDest, strlen (lpSrc) + 1);
|
|
if (!ptr)
|
|
return FALSE;
|
|
strcpy (ptr, lpSrc);
|
|
*lppDest = ptr;
|
|
}
|
|
else {
|
|
if (*lppDest) {
|
|
Free (*lppDest);
|
|
*lppDest = NULL;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* Str_GetPtrW [COMCTL32.235]
|
|
*
|
|
* See Str_GetPtrA.
|
|
*/
|
|
INT WINAPI Str_GetPtrW (LPCWSTR lpSrc, LPWSTR lpDest, INT nMaxLen)
|
|
{
|
|
INT len;
|
|
|
|
TRACE("(%p %p %d)\n", lpSrc, lpDest, nMaxLen);
|
|
|
|
if (!lpDest && lpSrc)
|
|
return strlenW (lpSrc);
|
|
|
|
if (nMaxLen == 0)
|
|
return 0;
|
|
|
|
if (lpSrc == NULL) {
|
|
lpDest[0] = L'\0';
|
|
return 0;
|
|
}
|
|
|
|
len = strlenW (lpSrc);
|
|
if (len >= nMaxLen)
|
|
len = nMaxLen - 1;
|
|
|
|
RtlMoveMemory (lpDest, lpSrc, len*sizeof(WCHAR));
|
|
lpDest[len] = L'\0';
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* Str_SetPtrW [COMCTL32.236]
|
|
*
|
|
* See Str_SetPtrA.
|
|
*/
|
|
BOOL WINAPI Str_SetPtrW (LPWSTR *lppDest, LPCWSTR lpSrc)
|
|
{
|
|
TRACE("(%p %p)\n", lppDest, lpSrc);
|
|
|
|
if (lpSrc) {
|
|
INT len = strlenW (lpSrc) + 1;
|
|
LPWSTR ptr = ReAlloc (*lppDest, len * sizeof(WCHAR));
|
|
if (!ptr)
|
|
return FALSE;
|
|
strcpyW (ptr, lpSrc);
|
|
*lppDest = ptr;
|
|
}
|
|
else {
|
|
if (*lppDest) {
|
|
Free (*lppDest);
|
|
*lppDest = NULL;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* 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_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 {
|
|
if (*lppDest) {
|
|
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 (LPNOTIFYDATA lpNotify, UINT uCode, LPNMHDR lpHdr)
|
|
{
|
|
NMHDR nmhdr;
|
|
LPNMHDR lpNmh = NULL;
|
|
UINT idFrom = 0;
|
|
|
|
TRACE("(%p %p %d %p 0x%08lx)\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 (¬ify, 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%08lx)\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 (¬ify, uCode, lpHdr);
|
|
}
|