2574 lines
65 KiB
C
2574 lines
65 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);
|
|
|
|
struct _DSA
|
|
{
|
|
INT nItemCount;
|
|
LPVOID pData;
|
|
INT nMaxCount;
|
|
INT nItemSize;
|
|
INT nGrow;
|
|
};
|
|
|
|
struct _DPA
|
|
{
|
|
INT nItemCount;
|
|
LPVOID *ptrs;
|
|
HANDLE hHeap;
|
|
INT nGrow;
|
|
INT nMaxCount;
|
|
};
|
|
|
|
typedef struct _STREAMDATA
|
|
{
|
|
DWORD dwSize;
|
|
DWORD dwData2;
|
|
DWORD dwItems;
|
|
} STREAMDATA, *PSTREAMDATA;
|
|
|
|
typedef struct _LOADDATA
|
|
{
|
|
INT nCount;
|
|
PVOID ptr;
|
|
} LOADDATA, *LPLOADDATA;
|
|
|
|
typedef HRESULT (CALLBACK *DPALOADPROC)(LPLOADDATA,IStream*,LPARAM);
|
|
|
|
static const WCHAR strMRUList[] = { 'M','R','U','L','i','s','t',0 };
|
|
|
|
/**************************************************************************
|
|
* DPA_LoadStream [COMCTL32.9]
|
|
*
|
|
* Loads a dynamic pointer array from a stream
|
|
*
|
|
* PARAMS
|
|
* phDpa [O] pointer to a handle to a dynamic pointer array
|
|
* loadProc [I] pointer to a callback function
|
|
* pStream [I] pointer to a stream
|
|
* lParam [I] application specific value
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*
|
|
* NOTES
|
|
* No more information available yet!
|
|
*/
|
|
HRESULT WINAPI DPA_LoadStream (HDPA *phDpa, DPALOADPROC loadProc,
|
|
IStream *pStream, LPARAM lParam)
|
|
{
|
|
HRESULT errCode;
|
|
LARGE_INTEGER position;
|
|
ULARGE_INTEGER newPosition;
|
|
STREAMDATA streamData;
|
|
LOADDATA loadData;
|
|
ULONG ulRead;
|
|
HDPA hDpa;
|
|
PVOID *ptr;
|
|
|
|
FIXME ("phDpa=%p loadProc=%p pStream=%p lParam=%lx\n",
|
|
phDpa, loadProc, pStream, lParam);
|
|
|
|
if (!phDpa || !loadProc || !pStream)
|
|
return E_INVALIDARG;
|
|
|
|
*phDpa = (HDPA)NULL;
|
|
|
|
position.QuadPart = 0;
|
|
|
|
/*
|
|
* Zero out our streamData
|
|
*/
|
|
memset(&streamData,0,sizeof(STREAMDATA));
|
|
|
|
errCode = IStream_Seek (pStream, position, STREAM_SEEK_CUR, &newPosition);
|
|
if (errCode != S_OK)
|
|
return errCode;
|
|
|
|
errCode = IStream_Read (pStream, &streamData, sizeof(STREAMDATA), &ulRead);
|
|
if (errCode != S_OK)
|
|
return errCode;
|
|
|
|
FIXME ("dwSize=%lu dwData2=%lu dwItems=%lu\n",
|
|
streamData.dwSize, streamData.dwData2, streamData.dwItems);
|
|
|
|
if ( ulRead < sizeof(STREAMDATA) ||
|
|
lParam < sizeof(STREAMDATA) ||
|
|
streamData.dwSize < sizeof(STREAMDATA) ||
|
|
streamData.dwData2 < 1) {
|
|
errCode = E_FAIL;
|
|
}
|
|
|
|
if (streamData.dwItems > (UINT_MAX / 2 / sizeof(VOID*))) /* 536870911 */
|
|
return E_OUTOFMEMORY;
|
|
|
|
/* create the dpa */
|
|
hDpa = DPA_Create (streamData.dwItems);
|
|
if (!hDpa)
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (!DPA_Grow (hDpa, streamData.dwItems))
|
|
return E_OUTOFMEMORY;
|
|
|
|
/* load data from the stream into the dpa */
|
|
ptr = hDpa->ptrs;
|
|
for (loadData.nCount = 0; loadData.nCount < streamData.dwItems; loadData.nCount++) {
|
|
errCode = (loadProc)(&loadData, pStream, lParam);
|
|
if (errCode != S_OK) {
|
|
errCode = S_FALSE;
|
|
break;
|
|
}
|
|
|
|
*ptr = loadData.ptr;
|
|
ptr++;
|
|
}
|
|
|
|
/* set the number of items */
|
|
hDpa->nItemCount = loadData.nCount;
|
|
|
|
/* store the handle to the dpa */
|
|
*phDpa = hDpa;
|
|
FIXME ("new hDpa=%p, errorcode=%lx\n", hDpa, errCode);
|
|
|
|
return errCode;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_SaveStream [COMCTL32.10]
|
|
*
|
|
* Saves a dynamic pointer array to a stream
|
|
*
|
|
* PARAMS
|
|
* hDpa [I] handle to a dynamic pointer array
|
|
* loadProc [I] pointer to a callback function
|
|
* pStream [I] pointer to a stream
|
|
* lParam [I] application specific value
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*
|
|
* NOTES
|
|
* No more information available yet!
|
|
*/
|
|
HRESULT WINAPI DPA_SaveStream (const HDPA hDpa, DPALOADPROC loadProc,
|
|
IStream *pStream, LPARAM lParam)
|
|
{
|
|
|
|
FIXME ("hDpa=%p loadProc=%p pStream=%p lParam=%lx\n",
|
|
hDpa, loadProc, pStream, lParam);
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_Merge [COMCTL32.11]
|
|
*
|
|
* Merge two dynamic pointers arrays.
|
|
*
|
|
* PARAMS
|
|
* hdpa1 [I] handle to a dynamic pointer array
|
|
* hdpa2 [I] handle to a dynamic pointer array
|
|
* dwFlags [I] flags
|
|
* pfnCompare [I] pointer to sort function
|
|
* pfnMerge [I] pointer to merge function
|
|
* lParam [I] application specific value
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*
|
|
* NOTES
|
|
* No more information available yet!
|
|
*/
|
|
BOOL WINAPI DPA_Merge (const HDPA hdpa1, const HDPA hdpa2, DWORD dwFlags,
|
|
PFNDPACOMPARE pfnCompare, PFNDPAMERGE pfnMerge,
|
|
LPARAM lParam)
|
|
{
|
|
INT nCount;
|
|
LPVOID *pWork1, *pWork2;
|
|
INT nResult, i;
|
|
INT nIndex;
|
|
|
|
TRACE("%p %p %08lx %p %p %08lx)\n",
|
|
hdpa1, hdpa2, dwFlags, pfnCompare, pfnMerge, lParam);
|
|
|
|
if (IsBadWritePtr (hdpa1, sizeof(*hdpa1)))
|
|
return FALSE;
|
|
|
|
if (IsBadWritePtr (hdpa2, sizeof(*hdpa2)))
|
|
return FALSE;
|
|
|
|
if (IsBadCodePtr ((FARPROC)pfnCompare))
|
|
return FALSE;
|
|
|
|
if (IsBadCodePtr ((FARPROC)pfnMerge))
|
|
return FALSE;
|
|
|
|
if (!(dwFlags & DPAM_NOSORT)) {
|
|
TRACE("sorting dpa's!\n");
|
|
if (hdpa1->nItemCount > 0)
|
|
DPA_Sort (hdpa1, pfnCompare, lParam);
|
|
TRACE ("dpa 1 sorted!\n");
|
|
if (hdpa2->nItemCount > 0)
|
|
DPA_Sort (hdpa2, pfnCompare, lParam);
|
|
TRACE ("dpa 2 sorted!\n");
|
|
}
|
|
|
|
if (hdpa2->nItemCount < 1)
|
|
return TRUE;
|
|
|
|
TRACE("hdpa1->nItemCount=%d hdpa2->nItemCount=%d\n",
|
|
hdpa1->nItemCount, hdpa2->nItemCount);
|
|
|
|
|
|
/* working but untrusted implementation */
|
|
|
|
pWork1 = &(hdpa1->ptrs[hdpa1->nItemCount - 1]);
|
|
pWork2 = &(hdpa2->ptrs[hdpa2->nItemCount - 1]);
|
|
|
|
nIndex = hdpa1->nItemCount - 1;
|
|
nCount = hdpa2->nItemCount - 1;
|
|
|
|
do
|
|
{
|
|
if (nIndex < 0) {
|
|
if ((nCount >= 0) && (dwFlags & DPAM_INSERT)) {
|
|
/* Now insert the remaining new items into DPA 1 */
|
|
TRACE("%d items to be inserted at start of DPA 1\n",
|
|
nCount+1);
|
|
for (i=nCount; i>=0; i--) {
|
|
PVOID ptr;
|
|
|
|
ptr = (pfnMerge)(3, *pWork2, NULL, lParam);
|
|
if (!ptr)
|
|
return FALSE;
|
|
DPA_InsertPtr (hdpa1, 0, ptr);
|
|
pWork2--;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
nResult = (pfnCompare)(*pWork1, *pWork2, lParam);
|
|
TRACE("compare result=%d, dpa1.cnt=%d, dpa2.cnt=%d\n",
|
|
nResult, nIndex, nCount);
|
|
|
|
if (nResult == 0)
|
|
{
|
|
PVOID ptr;
|
|
|
|
ptr = (pfnMerge)(1, *pWork1, *pWork2, lParam);
|
|
if (!ptr)
|
|
return FALSE;
|
|
|
|
nCount--;
|
|
pWork2--;
|
|
*pWork1 = ptr;
|
|
nIndex--;
|
|
pWork1--;
|
|
}
|
|
else if (nResult > 0)
|
|
{
|
|
/* item in DPA 1 missing from DPA 2 */
|
|
if (dwFlags & DPAM_DELETE)
|
|
{
|
|
/* Now delete the extra item in DPA1 */
|
|
PVOID ptr;
|
|
|
|
ptr = DPA_DeletePtr (hdpa1, hdpa1->nItemCount - 1);
|
|
|
|
(pfnMerge)(2, ptr, NULL, lParam);
|
|
}
|
|
nIndex--;
|
|
pWork1--;
|
|
}
|
|
else
|
|
{
|
|
/* new item in DPA 2 */
|
|
if (dwFlags & DPAM_INSERT)
|
|
{
|
|
/* Now insert the new item in DPA 1 */
|
|
PVOID ptr;
|
|
|
|
ptr = (pfnMerge)(3, *pWork2, NULL, lParam);
|
|
if (!ptr)
|
|
return FALSE;
|
|
DPA_InsertPtr (hdpa1, nIndex+1, ptr);
|
|
}
|
|
nCount--;
|
|
pWork2--;
|
|
}
|
|
|
|
}
|
|
while (nCount >= 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* 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;
|
|
static const WCHAR emptyW[] = {'\0'};
|
|
|
|
/* 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,
|
|
emptyW,
|
|
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;
|
|
static const WCHAR emptyW[] = {'\0'};
|
|
|
|
/* 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,
|
|
emptyW,
|
|
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;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DSA_Create [COMCTL32.320]
|
|
*
|
|
* Creates a dynamic storage array
|
|
*
|
|
* PARAMS
|
|
* nSize [I] size of the array elements
|
|
* nGrow [I] number of elements by which the array grows when it is filled
|
|
*
|
|
* RETURNS
|
|
* Success: pointer to an array control structure. Use this like a handle.
|
|
* Failure: NULL
|
|
*
|
|
* NOTES
|
|
* The DSA_ functions can be used to create and manipulate arrays of
|
|
* fixed-size memory blocks. These arrays can store any kind of data
|
|
* (e.g. strings and icons).
|
|
*/
|
|
HDSA WINAPI DSA_Create (INT nSize, INT nGrow)
|
|
{
|
|
HDSA hdsa;
|
|
|
|
TRACE("(size=%d grow=%d)\n", nSize, nGrow);
|
|
|
|
hdsa = Alloc (sizeof(*hdsa));
|
|
if (hdsa)
|
|
{
|
|
hdsa->nItemCount = 0;
|
|
hdsa->pData = NULL;
|
|
hdsa->nMaxCount = 0;
|
|
hdsa->nItemSize = nSize;
|
|
hdsa->nGrow = max(1, nGrow);
|
|
}
|
|
|
|
return hdsa;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DSA_Destroy [COMCTL32.321]
|
|
*
|
|
* Destroys a dynamic storage array
|
|
*
|
|
* PARAMS
|
|
* hdsa [I] pointer to the array control structure
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI DSA_Destroy (const HDSA hdsa)
|
|
{
|
|
TRACE("(%p)\n", hdsa);
|
|
|
|
if (!hdsa)
|
|
return FALSE;
|
|
|
|
if (hdsa->pData && (!Free (hdsa->pData)))
|
|
return FALSE;
|
|
|
|
return Free (hdsa);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DSA_GetItem [COMCTL32.322]
|
|
*
|
|
* Copies the specified item into a caller-supplied buffer.
|
|
*
|
|
* PARAMS
|
|
* hdsa [I] pointer to the array control structure
|
|
* nIndex [I] number of the Item to get
|
|
* pDest [O] destination buffer. Has to be >= dwElementSize.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI DSA_GetItem (const HDSA hdsa, INT nIndex, LPVOID pDest)
|
|
{
|
|
LPVOID pSrc;
|
|
|
|
TRACE("(%p %d %p)\n", hdsa, nIndex, pDest);
|
|
|
|
if (!hdsa)
|
|
return FALSE;
|
|
if ((nIndex < 0) || (nIndex >= hdsa->nItemCount))
|
|
return FALSE;
|
|
|
|
pSrc = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
|
|
memmove (pDest, pSrc, hdsa->nItemSize);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DSA_GetItemPtr [COMCTL32.323]
|
|
*
|
|
* Retrieves a pointer to the specified item.
|
|
*
|
|
* PARAMS
|
|
* hdsa [I] pointer to the array control structure
|
|
* nIndex [I] index of the desired item
|
|
*
|
|
* RETURNS
|
|
* Success: pointer to an item
|
|
* Failure: NULL
|
|
*/
|
|
LPVOID WINAPI DSA_GetItemPtr (const HDSA hdsa, INT nIndex)
|
|
{
|
|
LPVOID pSrc;
|
|
|
|
TRACE("(%p %d)\n", hdsa, nIndex);
|
|
|
|
if (!hdsa)
|
|
return NULL;
|
|
if ((nIndex < 0) || (nIndex >= hdsa->nItemCount))
|
|
return NULL;
|
|
|
|
pSrc = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
|
|
|
|
TRACE("-- ret=%p\n", pSrc);
|
|
|
|
return pSrc;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DSA_SetItem [COMCTL32.325]
|
|
*
|
|
* Sets the contents of an item in the array.
|
|
*
|
|
* PARAMS
|
|
* hdsa [I] pointer to the array control structure
|
|
* nIndex [I] index for the item
|
|
* pSrc [I] pointer to the new item data
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI DSA_SetItem (const HDSA hdsa, INT nIndex, LPVOID pSrc)
|
|
{
|
|
INT nSize, nNewItems;
|
|
LPVOID pDest, lpTemp;
|
|
|
|
TRACE("(%p %d %p)\n", hdsa, nIndex, pSrc);
|
|
|
|
if ((!hdsa) || nIndex < 0)
|
|
return FALSE;
|
|
|
|
if (hdsa->nItemCount <= nIndex) {
|
|
/* within the old array */
|
|
if (hdsa->nMaxCount > nIndex) {
|
|
/* within the allocated space, set a new boundary */
|
|
hdsa->nItemCount = nIndex + 1;
|
|
}
|
|
else {
|
|
/* resize the block of memory */
|
|
nNewItems =
|
|
hdsa->nGrow * ((INT)(((nIndex + 1) - 1) / hdsa->nGrow) + 1);
|
|
nSize = hdsa->nItemSize * nNewItems;
|
|
|
|
lpTemp = ReAlloc (hdsa->pData, nSize);
|
|
if (!lpTemp)
|
|
return FALSE;
|
|
|
|
hdsa->nMaxCount = nNewItems;
|
|
hdsa->nItemCount = nIndex + 1;
|
|
hdsa->pData = lpTemp;
|
|
}
|
|
}
|
|
|
|
/* put the new entry in */
|
|
pDest = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
|
|
TRACE("-- move dest=%p src=%p size=%d\n",
|
|
pDest, pSrc, hdsa->nItemSize);
|
|
memmove (pDest, pSrc, hdsa->nItemSize);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DSA_InsertItem [COMCTL32.324]
|
|
*
|
|
* Inserts an item into the array at the specified index.
|
|
*
|
|
* PARAMS
|
|
* hdsa [I] pointer to the array control structure
|
|
* nIndex [I] index for the new item
|
|
* pSrc [I] pointer to the element
|
|
*
|
|
* RETURNS
|
|
* Success: position of the new item
|
|
* Failure: -1
|
|
*/
|
|
INT WINAPI DSA_InsertItem (const HDSA hdsa, INT nIndex, LPVOID pSrc)
|
|
{
|
|
INT nNewItems, nSize;
|
|
LPVOID lpTemp, lpDest;
|
|
|
|
TRACE("(%p %d %p)\n", hdsa, nIndex, pSrc);
|
|
|
|
if ((!hdsa) || nIndex < 0)
|
|
return -1;
|
|
|
|
/* when nIndex >= nItemCount then append */
|
|
if (nIndex >= hdsa->nItemCount)
|
|
nIndex = hdsa->nItemCount;
|
|
|
|
/* do we need to resize ? */
|
|
if (hdsa->nItemCount >= hdsa->nMaxCount) {
|
|
nNewItems = hdsa->nMaxCount + hdsa->nGrow;
|
|
nSize = hdsa->nItemSize * nNewItems;
|
|
|
|
lpTemp = ReAlloc (hdsa->pData, nSize);
|
|
if (!lpTemp)
|
|
return -1;
|
|
|
|
hdsa->nMaxCount = nNewItems;
|
|
hdsa->pData = lpTemp;
|
|
}
|
|
|
|
/* do we need to move elements ? */
|
|
if (nIndex < hdsa->nItemCount) {
|
|
lpTemp = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
|
|
lpDest = (char *) lpTemp + hdsa->nItemSize;
|
|
nSize = (hdsa->nItemCount - nIndex) * hdsa->nItemSize;
|
|
TRACE("-- move dest=%p src=%p size=%d\n",
|
|
lpDest, lpTemp, nSize);
|
|
memmove (lpDest, lpTemp, nSize);
|
|
}
|
|
|
|
/* ok, we can put the new Item in */
|
|
hdsa->nItemCount++;
|
|
lpDest = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
|
|
TRACE("-- move dest=%p src=%p size=%d\n",
|
|
lpDest, pSrc, hdsa->nItemSize);
|
|
memmove (lpDest, pSrc, hdsa->nItemSize);
|
|
|
|
return nIndex;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DSA_DeleteItem [COMCTL32.326]
|
|
*
|
|
* Deletes the specified item from the array.
|
|
*
|
|
* PARAMS
|
|
* hdsa [I] pointer to the array control structure
|
|
* nIndex [I] index for the element to delete
|
|
*
|
|
* RETURNS
|
|
* Success: number of the deleted element
|
|
* Failure: -1
|
|
*/
|
|
INT WINAPI DSA_DeleteItem (const HDSA hdsa, INT nIndex)
|
|
{
|
|
LPVOID lpDest,lpSrc;
|
|
INT nSize;
|
|
|
|
TRACE("(%p %d)\n", hdsa, nIndex);
|
|
|
|
if (!hdsa)
|
|
return -1;
|
|
if (nIndex < 0 || nIndex >= hdsa->nItemCount)
|
|
return -1;
|
|
|
|
/* do we need to move ? */
|
|
if (nIndex < hdsa->nItemCount - 1) {
|
|
lpDest = (char *) hdsa->pData + (hdsa->nItemSize * nIndex);
|
|
lpSrc = (char *) lpDest + hdsa->nItemSize;
|
|
nSize = hdsa->nItemSize * (hdsa->nItemCount - nIndex - 1);
|
|
TRACE("-- move dest=%p src=%p size=%d\n",
|
|
lpDest, lpSrc, nSize);
|
|
memmove (lpDest, lpSrc, nSize);
|
|
}
|
|
|
|
hdsa->nItemCount--;
|
|
|
|
/* free memory ? */
|
|
if ((hdsa->nMaxCount - hdsa->nItemCount) >= hdsa->nGrow) {
|
|
nSize = hdsa->nItemSize * hdsa->nItemCount;
|
|
|
|
lpDest = ReAlloc (hdsa->pData, nSize);
|
|
if (!lpDest)
|
|
return -1;
|
|
|
|
hdsa->nMaxCount = hdsa->nItemCount;
|
|
hdsa->pData = lpDest;
|
|
}
|
|
|
|
return nIndex;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DSA_DeleteAllItems [COMCTL32.327]
|
|
*
|
|
* Removes all items and reinitializes the array.
|
|
*
|
|
* PARAMS
|
|
* hdsa [I] pointer to the array control structure
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI DSA_DeleteAllItems (const HDSA hdsa)
|
|
{
|
|
TRACE("(%p)\n", hdsa);
|
|
|
|
if (!hdsa)
|
|
return FALSE;
|
|
if (hdsa->pData && (!Free (hdsa->pData)))
|
|
return FALSE;
|
|
|
|
hdsa->nItemCount = 0;
|
|
hdsa->pData = NULL;
|
|
hdsa->nMaxCount = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_Destroy [COMCTL32.329]
|
|
*
|
|
* Destroys a dynamic pointer array
|
|
*
|
|
* PARAMS
|
|
* hdpa [I] handle (pointer) to the pointer array
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI DPA_Destroy (const HDPA hdpa)
|
|
{
|
|
TRACE("(%p)\n", hdpa);
|
|
|
|
if (!hdpa)
|
|
return FALSE;
|
|
|
|
if (hdpa->ptrs && (!HeapFree (hdpa->hHeap, 0, hdpa->ptrs)))
|
|
return FALSE;
|
|
|
|
return HeapFree (hdpa->hHeap, 0, hdpa);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_Grow [COMCTL32.330]
|
|
*
|
|
* Sets the growth amount.
|
|
*
|
|
* PARAMS
|
|
* hdpa [I] handle (pointer) to the existing (source) pointer array
|
|
* nGrow [I] number of items by which the array grows when it's too small
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI DPA_Grow (const HDPA hdpa, INT nGrow)
|
|
{
|
|
TRACE("(%p %d)\n", hdpa, nGrow);
|
|
|
|
if (!hdpa)
|
|
return FALSE;
|
|
|
|
hdpa->nGrow = max(8, nGrow);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_Clone [COMCTL32.331]
|
|
*
|
|
* Copies a pointer array to an other one or creates a copy
|
|
*
|
|
* PARAMS
|
|
* hdpa [I] handle (pointer) to the existing (source) pointer array
|
|
* hdpaNew [O] handle (pointer) to the destination pointer array
|
|
*
|
|
* RETURNS
|
|
* Success: pointer to the destination pointer array.
|
|
* Failure: NULL
|
|
*
|
|
* NOTES
|
|
* - If the 'hdpaNew' is a NULL-Pointer, a copy of the source pointer
|
|
* array will be created and it's handle (pointer) is returned.
|
|
* - If 'hdpa' is a NULL-Pointer, the original implementation crashes,
|
|
* this implementation just returns NULL.
|
|
*/
|
|
HDPA WINAPI DPA_Clone (const HDPA hdpa, const HDPA hdpaNew)
|
|
{
|
|
INT nNewItems, nSize;
|
|
HDPA hdpaTemp;
|
|
|
|
if (!hdpa)
|
|
return NULL;
|
|
|
|
TRACE("(%p %p)\n", hdpa, hdpaNew);
|
|
|
|
if (!hdpaNew) {
|
|
/* create a new DPA */
|
|
hdpaTemp = HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
|
|
sizeof(*hdpaTemp));
|
|
hdpaTemp->hHeap = hdpa->hHeap;
|
|
hdpaTemp->nGrow = hdpa->nGrow;
|
|
}
|
|
else
|
|
hdpaTemp = hdpaNew;
|
|
|
|
if (hdpaTemp->ptrs) {
|
|
/* remove old pointer array */
|
|
HeapFree (hdpaTemp->hHeap, 0, hdpaTemp->ptrs);
|
|
hdpaTemp->ptrs = NULL;
|
|
hdpaTemp->nItemCount = 0;
|
|
hdpaTemp->nMaxCount = 0;
|
|
}
|
|
|
|
/* create a new pointer array */
|
|
nNewItems = hdpaTemp->nGrow *
|
|
((INT)((hdpa->nItemCount - 1) / hdpaTemp->nGrow) + 1);
|
|
nSize = nNewItems * sizeof(LPVOID);
|
|
hdpaTemp->ptrs = HeapAlloc (hdpaTemp->hHeap, HEAP_ZERO_MEMORY, nSize);
|
|
hdpaTemp->nMaxCount = nNewItems;
|
|
|
|
/* clone the pointer array */
|
|
hdpaTemp->nItemCount = hdpa->nItemCount;
|
|
memmove (hdpaTemp->ptrs, hdpa->ptrs,
|
|
hdpaTemp->nItemCount * sizeof(LPVOID));
|
|
|
|
return hdpaTemp;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_GetPtr [COMCTL32.332]
|
|
*
|
|
* Retrieves a pointer from a dynamic pointer array
|
|
*
|
|
* PARAMS
|
|
* hdpa [I] handle (pointer) to the pointer array
|
|
* nIndex [I] array index of the desired pointer
|
|
*
|
|
* RETURNS
|
|
* Success: pointer
|
|
* Failure: NULL
|
|
*/
|
|
LPVOID WINAPI DPA_GetPtr (const HDPA hdpa, INT nIndex)
|
|
{
|
|
TRACE("(%p %d)\n", hdpa, nIndex);
|
|
|
|
if (!hdpa)
|
|
return NULL;
|
|
if (!hdpa->ptrs) {
|
|
WARN("no pointer array.\n");
|
|
return NULL;
|
|
}
|
|
if ((nIndex < 0) || (nIndex >= hdpa->nItemCount)) {
|
|
WARN("not enough pointers in array (%d vs %d).\n",nIndex,hdpa->nItemCount);
|
|
return NULL;
|
|
}
|
|
|
|
TRACE("-- %p\n", hdpa->ptrs[nIndex]);
|
|
|
|
return hdpa->ptrs[nIndex];
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_GetPtrIndex [COMCTL32.333]
|
|
*
|
|
* Retrieves the index of the specified pointer
|
|
*
|
|
* PARAMS
|
|
* hdpa [I] handle (pointer) to the pointer array
|
|
* p [I] pointer
|
|
*
|
|
* RETURNS
|
|
* Success: index of the specified pointer
|
|
* Failure: -1
|
|
*/
|
|
INT WINAPI DPA_GetPtrIndex (const HDPA hdpa, LPVOID p)
|
|
{
|
|
INT i;
|
|
|
|
if (!hdpa || !hdpa->ptrs)
|
|
return -1;
|
|
|
|
for (i = 0; i < hdpa->nItemCount; i++) {
|
|
if (hdpa->ptrs[i] == p)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_InsertPtr [COMCTL32.334]
|
|
*
|
|
* Inserts a pointer into a dynamic pointer array
|
|
*
|
|
* PARAMS
|
|
* hdpa [I] handle (pointer) to the array
|
|
* i [I] array index
|
|
* p [I] pointer to insert
|
|
*
|
|
* RETURNS
|
|
* Success: index of the inserted pointer
|
|
* Failure: -1
|
|
*/
|
|
INT WINAPI DPA_InsertPtr (const HDPA hdpa, INT i, LPVOID p)
|
|
{
|
|
TRACE("(%p %d %p)\n", hdpa, i, p);
|
|
|
|
if (!hdpa || i < 0) return -1;
|
|
|
|
if (i >= 0x7fff)
|
|
i = hdpa->nItemCount;
|
|
|
|
if (i >= hdpa->nItemCount)
|
|
return DPA_SetPtr(hdpa, i, p) ? i : -1;
|
|
|
|
/* create empty spot at the end */
|
|
if (!DPA_SetPtr(hdpa, hdpa->nItemCount, 0)) return -1;
|
|
memmove (hdpa->ptrs + i + 1, hdpa->ptrs + i, (hdpa->nItemCount - i - 1) * sizeof(LPVOID));
|
|
hdpa->ptrs[i] = p;
|
|
return i;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* DPA_SetPtr [COMCTL32.335]
|
|
*
|
|
* Sets a pointer in the pointer array
|
|
*
|
|
* PARAMS
|
|
* hdpa [I] handle (pointer) to the pointer array
|
|
* i [I] index of the pointer that will be set
|
|
* p [I] pointer to be set
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI DPA_SetPtr (const HDPA hdpa, INT i, LPVOID p)
|
|
{
|
|
LPVOID *lpTemp;
|
|
|
|
TRACE("(%p %d %p)\n", hdpa, i, p);
|
|
|
|
if (!hdpa || i < 0 || i > 0x7fff)
|
|
return FALSE;
|
|
|
|
if (hdpa->nItemCount <= i) {
|
|
/* within the old array */
|
|
if (hdpa->nMaxCount <= i) {
|
|
/* resize the block of memory */
|
|
INT nNewItems =
|
|
hdpa->nGrow * ((INT)(((i+1) - 1) / hdpa->nGrow) + 1);
|
|
INT nSize = nNewItems * sizeof(LPVOID);
|
|
|
|
if (hdpa->ptrs)
|
|
lpTemp = HeapReAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY, hdpa->ptrs, nSize);
|
|
else
|
|
lpTemp = HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY, nSize);
|
|
|
|
if (!lpTemp)
|
|
return FALSE;
|
|
|
|
hdpa->nMaxCount = nNewItems;
|
|
hdpa->ptrs = lpTemp;
|
|
}
|
|
hdpa->nItemCount = i+1;
|
|
}
|
|
|
|
/* put the new entry in */
|
|
hdpa->ptrs[i] = p;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_DeletePtr [COMCTL32.336]
|
|
*
|
|
* Removes a pointer from the pointer array.
|
|
*
|
|
* PARAMS
|
|
* hdpa [I] handle (pointer) to the pointer array
|
|
* i [I] index of the pointer that will be deleted
|
|
*
|
|
* RETURNS
|
|
* Success: deleted pointer
|
|
* Failure: NULL
|
|
*/
|
|
LPVOID WINAPI DPA_DeletePtr (const HDPA hdpa, INT i)
|
|
{
|
|
LPVOID *lpDest, *lpSrc, lpTemp = NULL;
|
|
INT nSize;
|
|
|
|
TRACE("(%p %d)\n", hdpa, i);
|
|
|
|
if ((!hdpa) || i < 0 || i >= hdpa->nItemCount)
|
|
return NULL;
|
|
|
|
lpTemp = hdpa->ptrs[i];
|
|
|
|
/* do we need to move ?*/
|
|
if (i < hdpa->nItemCount - 1) {
|
|
lpDest = hdpa->ptrs + i;
|
|
lpSrc = lpDest + 1;
|
|
nSize = (hdpa->nItemCount - i - 1) * sizeof(LPVOID);
|
|
TRACE("-- move dest=%p src=%p size=%x\n",
|
|
lpDest, lpSrc, nSize);
|
|
memmove (lpDest, lpSrc, nSize);
|
|
}
|
|
|
|
hdpa->nItemCount --;
|
|
|
|
/* free memory ?*/
|
|
if ((hdpa->nMaxCount - hdpa->nItemCount) >= hdpa->nGrow) {
|
|
INT nNewItems = max(hdpa->nGrow * 2, hdpa->nItemCount);
|
|
nSize = nNewItems * sizeof(LPVOID);
|
|
lpDest = HeapReAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
|
|
hdpa->ptrs, nSize);
|
|
if (!lpDest)
|
|
return NULL;
|
|
|
|
hdpa->nMaxCount = nNewItems;
|
|
hdpa->ptrs = (LPVOID*)lpDest;
|
|
}
|
|
|
|
return lpTemp;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_DeleteAllPtrs [COMCTL32.337]
|
|
*
|
|
* Removes all pointers and reinitializes the array.
|
|
*
|
|
* PARAMS
|
|
* hdpa [I] handle (pointer) to the pointer array
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI DPA_DeleteAllPtrs (const HDPA hdpa)
|
|
{
|
|
TRACE("(%p)\n", hdpa);
|
|
|
|
if (!hdpa)
|
|
return FALSE;
|
|
|
|
if (hdpa->ptrs && (!HeapFree (hdpa->hHeap, 0, hdpa->ptrs)))
|
|
return FALSE;
|
|
|
|
hdpa->nItemCount = 0;
|
|
hdpa->nMaxCount = hdpa->nGrow * 2;
|
|
hdpa->ptrs = HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
|
|
hdpa->nMaxCount * sizeof(LPVOID));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_QuickSort [Internal]
|
|
*
|
|
* Ordinary quicksort (used by DPA_Sort).
|
|
*
|
|
* PARAMS
|
|
* lpPtrs [I] pointer to the pointer array
|
|
* l [I] index of the "left border" of the partition
|
|
* r [I] index of the "right border" of the partition
|
|
* pfnCompare [I] pointer to the compare function
|
|
* lParam [I] user defined value (3rd parameter in compare function)
|
|
*
|
|
* RETURNS
|
|
* NONE
|
|
*/
|
|
static VOID DPA_QuickSort (LPVOID *lpPtrs, INT l, INT r,
|
|
PFNDPACOMPARE pfnCompare, LPARAM lParam)
|
|
{
|
|
INT m;
|
|
LPVOID t;
|
|
|
|
TRACE("l=%i r=%i\n", l, r);
|
|
|
|
if (l==r) /* one element is always sorted */
|
|
return;
|
|
if (r<l) /* oops, got it in the wrong order */
|
|
{
|
|
DPA_QuickSort(lpPtrs, r, l, pfnCompare, lParam);
|
|
return;
|
|
}
|
|
m = (l+r)/2; /* divide by two */
|
|
DPA_QuickSort(lpPtrs, l, m, pfnCompare, lParam);
|
|
DPA_QuickSort(lpPtrs, m+1, r, pfnCompare, lParam);
|
|
|
|
/* join the two sides */
|
|
while( (l<=m) && (m<r) )
|
|
{
|
|
if(pfnCompare(lpPtrs[l],lpPtrs[m+1],lParam)>0)
|
|
{
|
|
t = lpPtrs[m+1];
|
|
memmove(&lpPtrs[l+1],&lpPtrs[l],(m-l+1)*sizeof(lpPtrs[l]));
|
|
lpPtrs[l] = t;
|
|
|
|
m++;
|
|
}
|
|
l++;
|
|
}
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_Sort [COMCTL32.338]
|
|
*
|
|
* Sorts a pointer array using a user defined compare function
|
|
*
|
|
* PARAMS
|
|
* hdpa [I] handle (pointer) to the pointer array
|
|
* pfnCompare [I] pointer to the compare function
|
|
* lParam [I] user defined value (3rd parameter of compare function)
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE
|
|
* Failure: FALSE
|
|
*/
|
|
BOOL WINAPI DPA_Sort (const HDPA hdpa, PFNDPACOMPARE pfnCompare, LPARAM lParam)
|
|
{
|
|
if (!hdpa || !pfnCompare)
|
|
return FALSE;
|
|
|
|
TRACE("(%p %p 0x%lx)\n", hdpa, pfnCompare, lParam);
|
|
|
|
if ((hdpa->nItemCount > 1) && (hdpa->ptrs))
|
|
DPA_QuickSort (hdpa->ptrs, 0, hdpa->nItemCount - 1,
|
|
pfnCompare, lParam);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_Search [COMCTL32.339]
|
|
*
|
|
* Searches a pointer array for a specified pointer
|
|
*
|
|
* PARAMS
|
|
* hdpa [I] handle (pointer) to the pointer array
|
|
* pFind [I] pointer to search for
|
|
* nStart [I] start index
|
|
* pfnCompare [I] pointer to the compare function
|
|
* lParam [I] user defined value (3rd parameter of compare function)
|
|
* uOptions [I] search options
|
|
*
|
|
* RETURNS
|
|
* Success: index of the pointer in the array.
|
|
* Failure: -1
|
|
*
|
|
* NOTES
|
|
* Binary search taken from R.Sedgewick "Algorithms in C"!
|
|
* Function is NOT tested!
|
|
* If something goes wrong, blame HIM not ME! (Eric Kohl)
|
|
*/
|
|
INT WINAPI DPA_Search (const HDPA hdpa, LPVOID pFind, INT nStart,
|
|
PFNDPACOMPARE pfnCompare, LPARAM lParam, UINT uOptions)
|
|
{
|
|
if (!hdpa || !pfnCompare || !pFind)
|
|
return -1;
|
|
|
|
TRACE("(%p %p %d %p 0x%08lx 0x%08x)\n",
|
|
hdpa, pFind, nStart, pfnCompare, lParam, uOptions);
|
|
|
|
if (uOptions & DPAS_SORTED) {
|
|
/* array is sorted --> use binary search */
|
|
INT l, r, x, n;
|
|
LPVOID *lpPtr;
|
|
|
|
TRACE("binary search\n");
|
|
|
|
l = (nStart == -1) ? 0 : nStart;
|
|
r = hdpa->nItemCount - 1;
|
|
lpPtr = hdpa->ptrs;
|
|
while (r >= l) {
|
|
x = (l + r) / 2;
|
|
n = (pfnCompare)(pFind, lpPtr[x], lParam);
|
|
if (n < 0)
|
|
r = x - 1;
|
|
else
|
|
l = x + 1;
|
|
if (n == 0) {
|
|
TRACE("-- ret=%d\n", n);
|
|
return n;
|
|
}
|
|
}
|
|
|
|
if (uOptions & DPAS_INSERTBEFORE) {
|
|
if (r == -1) r = 0;
|
|
TRACE("-- ret=%d\n", r);
|
|
return r;
|
|
}
|
|
|
|
if (uOptions & DPAS_INSERTAFTER) {
|
|
TRACE("-- ret=%d\n", l);
|
|
return l;
|
|
}
|
|
}
|
|
else {
|
|
/* array is not sorted --> use linear search */
|
|
LPVOID *lpPtr;
|
|
INT nIndex;
|
|
|
|
TRACE("linear search\n");
|
|
|
|
nIndex = (nStart == -1)? 0 : nStart;
|
|
lpPtr = hdpa->ptrs;
|
|
for (; nIndex < hdpa->nItemCount; nIndex++) {
|
|
if ((pfnCompare)(pFind, lpPtr[nIndex], lParam) == 0) {
|
|
TRACE("-- ret=%d\n", nIndex);
|
|
return nIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
TRACE("-- not found: ret=-1\n");
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_CreateEx [COMCTL32.340]
|
|
*
|
|
* Creates a dynamic pointer array using the specified size and heap.
|
|
*
|
|
* PARAMS
|
|
* nGrow [I] number of items by which the array grows when it is filled
|
|
* hHeap [I] handle to the heap where the array is stored
|
|
*
|
|
* RETURNS
|
|
* Success: handle (pointer) to the pointer array.
|
|
* Failure: NULL
|
|
*
|
|
* NOTES
|
|
* The DPA_ functions can be used to create and manipulate arrays of
|
|
* pointers.
|
|
*/
|
|
HDPA WINAPI DPA_CreateEx (INT nGrow, HANDLE hHeap)
|
|
{
|
|
HDPA hdpa;
|
|
|
|
TRACE("(%d %p)\n", nGrow, hHeap);
|
|
|
|
if (hHeap)
|
|
hdpa = HeapAlloc (hHeap, HEAP_ZERO_MEMORY, sizeof(*hdpa));
|
|
else
|
|
hdpa = Alloc (sizeof(*hdpa));
|
|
|
|
if (hdpa) {
|
|
hdpa->nGrow = max(8, nGrow);
|
|
hdpa->hHeap = hHeap ? hHeap : GetProcessHeap();
|
|
hdpa->nMaxCount = hdpa->nGrow * 2;
|
|
hdpa->ptrs = HeapAlloc (hdpa->hHeap, HEAP_ZERO_MEMORY,
|
|
hdpa->nMaxCount * sizeof(LPVOID));
|
|
}
|
|
|
|
TRACE("-- %p\n", hdpa);
|
|
|
|
return hdpa;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_Create [COMCTL32.328]
|
|
*
|
|
* Creates a dynamic pointer array.
|
|
*
|
|
* PARAMS
|
|
* nGrow [I] number of items by which the array grows when it is filled
|
|
*
|
|
* RETURNS
|
|
* Success: handle (pointer) to the pointer array.
|
|
* Failure: NULL
|
|
*
|
|
* NOTES
|
|
* The DPA_ functions can be used to create and manipulate arrays of
|
|
* pointers.
|
|
*/
|
|
HDPA WINAPI DPA_Create (INT nGrow)
|
|
{
|
|
return DPA_CreateEx( nGrow, 0 );
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* 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);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_EnumCallback [COMCTL32.385]
|
|
*
|
|
* Enumerates all items in a dynamic pointer array.
|
|
*
|
|
* PARAMS
|
|
* hdpa [I] handle to the dynamic pointer array
|
|
* enumProc [I]
|
|
* lParam [I]
|
|
*
|
|
* RETURNS
|
|
* none
|
|
*/
|
|
VOID WINAPI DPA_EnumCallback (HDPA hdpa, PFNDPAENUMCALLBACK enumProc,
|
|
LPVOID lParam)
|
|
{
|
|
INT i;
|
|
|
|
TRACE("(%p %p %p)\n", hdpa, enumProc, lParam);
|
|
|
|
if (!hdpa)
|
|
return;
|
|
if (hdpa->nItemCount <= 0)
|
|
return;
|
|
|
|
for (i = 0; i < hdpa->nItemCount; i++) {
|
|
if ((enumProc)(hdpa->ptrs[i], lParam) == 0)
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DPA_DestroyCallback [COMCTL32.386]
|
|
*
|
|
* Enumerates all items in a dynamic pointer array and destroys it.
|
|
*
|
|
* PARAMS
|
|
* hdpa [I] handle to the dynamic pointer array
|
|
* enumProc [I]
|
|
* lParam [I]
|
|
*
|
|
* RETURNS
|
|
* none
|
|
*/
|
|
void WINAPI DPA_DestroyCallback (HDPA hdpa, PFNDPAENUMCALLBACK enumProc,
|
|
LPVOID lParam)
|
|
{
|
|
TRACE("(%p %p %p)\n", hdpa, enumProc, lParam);
|
|
|
|
DPA_EnumCallback (hdpa, enumProc, lParam);
|
|
DPA_Destroy (hdpa);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DSA_EnumCallback [COMCTL32.387]
|
|
*
|
|
* Enumerates all items in a dynamic storage array.
|
|
*
|
|
* PARAMS
|
|
* hdsa [I] handle to the dynamic storage array
|
|
* enumProc [I]
|
|
* lParam [I]
|
|
*
|
|
* RETURNS
|
|
* none
|
|
*/
|
|
VOID WINAPI DSA_EnumCallback (HDSA hdsa, PFNDSAENUMCALLBACK enumProc,
|
|
LPVOID lParam)
|
|
{
|
|
INT i;
|
|
|
|
TRACE("(%p %p %p)\n", hdsa, enumProc, lParam);
|
|
|
|
if (!hdsa)
|
|
return;
|
|
if (hdsa->nItemCount <= 0)
|
|
return;
|
|
|
|
for (i = 0; i < hdsa->nItemCount; i++) {
|
|
LPVOID lpItem = DSA_GetItemPtr (hdsa, i);
|
|
if ((enumProc)(lpItem, lParam) == 0)
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* DSA_DestroyCallback [COMCTL32.388]
|
|
*
|
|
* Enumerates all items in a dynamic storage array and destroys it.
|
|
*
|
|
* PARAMS
|
|
* hdsa [I] handle to the dynamic storage array
|
|
* enumProc [I]
|
|
* lParam [I]
|
|
*
|
|
* RETURNS
|
|
* none
|
|
*/
|
|
void WINAPI DSA_DestroyCallback (HDSA hdsa, PFNDSAENUMCALLBACK enumProc,
|
|
LPVOID lParam)
|
|
{
|
|
TRACE("(%p %p %p)\n", hdsa, enumProc, lParam);
|
|
|
|
DSA_EnumCallback (hdsa, enumProc, lParam);
|
|
DSA_Destroy (hdsa);
|
|
}
|