From 2d990806ec84b52f254d453220d4cbc953679961 Mon Sep 17 00:00:00 2001 From: Jon Griffiths Date: Tue, 9 Jul 2002 02:01:56 +0000 Subject: [PATCH] Implement/document @17,18,19,20,21,22 (Compact list API). --- dlls/shlwapi/Makefile.in | 2 + dlls/shlwapi/clist.c | 434 ++++++++++++++++++++++++++++++++ dlls/shlwapi/ordinal.c | 72 ------ dlls/shlwapi/shlwapi.spec | 12 +- dlls/shlwapi/tests/.cvsignore | 1 + dlls/shlwapi/tests/clist.c | 454 ++++++++++++++++++++++++++++++++++ 6 files changed, 897 insertions(+), 78 deletions(-) create mode 100644 dlls/shlwapi/clist.c create mode 100644 dlls/shlwapi/tests/clist.c diff --git a/dlls/shlwapi/Makefile.in b/dlls/shlwapi/Makefile.in index 704e40131a8..6e1e8701477 100644 --- a/dlls/shlwapi/Makefile.in +++ b/dlls/shlwapi/Makefile.in @@ -12,6 +12,7 @@ LDDLLFLAGS = @LDDLLFLAGS@ SYMBOLFILE = $(MODULE).tmp.o C_SRCS = \ + clist.c \ ordinal.c \ path.c \ reg.c \ @@ -24,6 +25,7 @@ C_SRCS = \ EXTRASUBDIRS = tests CTESTS = \ + tests/clist.c \ tests/shreg.c @MAKE_DLL_RULES@ diff --git a/dlls/shlwapi/clist.c b/dlls/shlwapi/clist.c new file mode 100644 index 00000000000..27084719f22 --- /dev/null +++ b/dlls/shlwapi/clist.c @@ -0,0 +1,434 @@ +/* + * SHLWAPI Compact List functions + * + * Copyright 2002 Jon Griffiths + * + * 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 + */ +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "wine/obj_base.h" +#include "wine/obj_storage.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +/* Compact list element (ordinals 17-22) */ +typedef struct tagSHLWAPI_CLIST +{ + ULONG ulSize; /* Size of this list element and its data */ + ULONG ulId; /* If -1, The real element follows */ + /* Item data (or a contained SHLWAPI_CLIST) follows... */ +} SHLWAPI_CLIST, *LPSHLWAPI_CLIST; + +typedef const SHLWAPI_CLIST* LPCSHLWAPI_CLIST; + +/* ulId for contained SHLWAPI_CLIST items */ +static const ULONG CLIST_ID_CONTAINER = -1u; + +HRESULT WINAPI SHLWAPI_20(LPSHLWAPI_CLIST*,LPCSHLWAPI_CLIST); + +/************************************************************************* + * NextItem + * + * Internal helper: move a clist pointer to the next item. + */ +inline static LPSHLWAPI_CLIST NextItem(LPCSHLWAPI_CLIST lpList) +{ + const char* address = (char*)lpList; + address += lpList->ulSize; + return (LPSHLWAPI_CLIST)address; +} + +/************************************************************************* + * @ [SHLWAPI.17] + * + * Write a compact list to a stream. + * + * PARAMS + * lpStream [I] Stream to write the list to + * lpList [I] List of items to write + * + * RETURNS + * Success: S_OK + * Failure: An HRESULT error code + * + * NOTES + * Ordinals 17,18,19,20,21 and 22 are related and together provide a compact + * list structure which may be stored and retrieved from a stream. + * + * The exposed API consists of: + * @17 Write a compact list to a stream + * @18 Read and create a list from a stream + * @19 Free a list + * @20 Insert a new item into a list + * @21 Remove an item from a list + * @22 Find an item in a list + * + * The compact list is stored packed into a memory array. Each element has a + * size and an associated ID. Elements must be less than 64k if the list is + * to be subsequently read from a stream. + * + * Elements are aligned on DWORD boundaries. If an elements data size is not + * a DWORD size multiple, the element is wrapped by inserting a surrounding + * element with an Id of -1, and size sufficient to pad to a DWORD boundary. + * + * These functions are slow for large objects and long lists. + */ +HRESULT WINAPI SHLWAPI_17(IStream* lpStream, LPSHLWAPI_CLIST lpList) +{ + ULONG ulSize; + HRESULT hRet = E_FAIL; + + TRACE("(%p,%p)\n", lpStream, lpList); + + if(lpList) + { + while (lpList->ulSize) + { + LPSHLWAPI_CLIST lpItem = lpList; + + if(lpList->ulId == CLIST_ID_CONTAINER) + lpItem++; + + hRet = IStream_Write(lpStream,lpItem,lpItem->ulSize,&ulSize); + if (FAILED(hRet)) + return hRet; + + if(lpItem->ulSize != ulSize) + return STG_E_MEDIUMFULL; + + lpList = NextItem(lpList); + } + } + + if(SUCCEEDED(hRet)) + { + ULONG ulDummy; + ulSize = 0; + + /* Write a terminating list entry with zero size */ + hRet = IStream_Write(lpStream, &ulSize,sizeof(ulSize),&ulDummy); + } + + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.18] + * + * Read and create a compact list from a stream + * + * PARAMS + * lpStream [I] Stream to read the list from + * lppList [0] Pointer to recieve the new List + * + * RETURNS + * Success: S_OK + * Failure: An HRESULT error code + * + * NOTES + * When read from a file, list objects are limited in size to 64k. + */ +HRESULT WINAPI SHLWAPI_18(IStream* lpStream, LPSHLWAPI_CLIST* lppList) +{ + SHLWAPI_CLIST bBuff[128]; /* Temporary storage for new list item */ + ULONG ulBuffSize = sizeof(bBuff); + LPSHLWAPI_CLIST pItem = bBuff; + ULONG ulRead, ulSize; + HRESULT hRet = S_OK; + + TRACE("(%p,%p)\n", lpStream, lppList); + + if(*lppList) + { + /* Free any existing list */ + LocalFree((HLOCAL)*lppList); + *lppList = NULL; + } + + do + { + /* Read the size of the next item */ + hRet = IStream_Read(lpStream, &ulSize,sizeof(ulSize),&ulRead); + + if(FAILED(hRet) || ulRead != sizeof(ulSize) || !ulSize) + break; /* Read failed or read zero size (the end of the list) */ + + if(ulSize > 0xFFFF) + { + LARGE_INTEGER liZero; + ULARGE_INTEGER ulPos; + + liZero.QuadPart = 0; + + /* Back the stream up; this object is too big for the list */ + if(SUCCEEDED(IStream_Seek(lpStream, liZero, STREAM_SEEK_CUR, &ulPos))) + { + liZero.QuadPart = ulPos.QuadPart - sizeof(ULONG); + IStream_Seek(lpStream, liZero, STREAM_SEEK_SET, NULL); + } + break; + } + else if (ulSize >= sizeof(SHLWAPI_CLIST)) + { + /* Add this new item to the list */ + if(ulSize > ulBuffSize) + { + /* We need more buffer space, allocate it */ + LPSHLWAPI_CLIST lpTemp; + + if (pItem == bBuff) + lpTemp = (LPSHLWAPI_CLIST)LocalAlloc(LMEM_ZEROINIT, ulSize); + else + lpTemp = (LPSHLWAPI_CLIST)LocalReAlloc((HLOCAL)pItem, ulSize, + LMEM_ZEROINIT|LMEM_MOVEABLE); + + if(!lpTemp) + { + hRet = E_OUTOFMEMORY; + break; + } + ulBuffSize = ulSize; + pItem = lpTemp; + } + + pItem->ulSize = ulSize; + ulSize -= sizeof(pItem->ulSize); /* already read this member */ + + /* Read the item Id and data */ + hRet = IStream_Read(lpStream, &pItem->ulId, ulSize, &ulRead); + + if(FAILED(hRet) || ulRead != ulSize) + break; + + SHLWAPI_20(lppList, pItem); /* Insert Item */ + } + } while(1); + + /* If we allocated space, free it */ + if(pItem != bBuff) + LocalFree((HLOCAL)pItem); + + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.19] + * + * Free a compact list. + * + * PARAMS + * lpList [I] List to free + * + * RETURNS + * Nothing. + */ +VOID WINAPI SHLWAPI_19(LPSHLWAPI_CLIST lpList) +{ + TRACE("(%p)\n", lpList); + + if (lpList) + LocalFree((HLOCAL)lpList); +} + +/************************************************************************* + * @ [SHLWAPI.20] + * + * Insert a new item into a compact list. + * + * PARAMS + * lppList [0] Pointer to the List + * lpNewItem [I] The new item to add to the list + * + * RETURNS + * Success: The size of the inserted item. + * Failure: An HRESULT error code. + */ +HRESULT WINAPI SHLWAPI_20(LPSHLWAPI_CLIST* lppList, LPCSHLWAPI_CLIST lpNewItem) +{ + LPSHLWAPI_CLIST lpInsertAt = NULL; + ULONG ulSize; + + TRACE("(%p,%p)\n", lppList, lpNewItem); + + if(!lppList || !lpNewItem || + lpNewItem->ulId == CLIST_ID_CONTAINER || + lpNewItem->ulSize < sizeof(SHLWAPI_CLIST)) + return E_INVALIDARG; + + ulSize = lpNewItem->ulSize; + + if(ulSize & 0x3) + { + /* Tune size to a ULONG boundary, add space for container element */ + ulSize = ((ulSize + 0x3) & 0xFFFFFFFC) + sizeof(SHLWAPI_CLIST); + TRACE("Creating container item, new size = %ld\n", ulSize); + } + + if(!*lppList) + { + /* An empty list. Allocate space for terminal ulSize also */ + *lppList = (LPSHLWAPI_CLIST)LocalAlloc(LMEM_ZEROINIT, + ulSize + sizeof(ULONG)); + lpInsertAt = *lppList; + } + else + { + /* Append to the end of the list */ + ULONG ulTotalSize = 0; + LPSHLWAPI_CLIST lpIter = *lppList; + + /* Iterate to the end of the list, calculating the total size */ + while (lpIter->ulSize) + { + ulTotalSize += lpIter->ulSize; + lpIter = NextItem(lpIter); + } + + /* Increase the size of the list */ + lpIter = (LPSHLWAPI_CLIST)LocalReAlloc((HLOCAL)*lppList, + ulTotalSize + ulSize+sizeof(ULONG), + LMEM_ZEROINIT | LMEM_MOVEABLE); + if(lpIter) + { + *lppList = lpIter; + lpInsertAt = (LPSHLWAPI_CLIST)((char*)lpIter + ulTotalSize); /* At end */ + } + } + + if(lpInsertAt) + { + /* Copy in the new item */ + LPSHLWAPI_CLIST lpDest = lpInsertAt; + + if(ulSize != lpNewItem->ulSize) + { + lpInsertAt->ulSize = ulSize; + lpInsertAt->ulId = CLIST_ID_CONTAINER; + lpDest++; + } + memcpy(lpDest, lpNewItem, lpNewItem->ulSize); + + /* Terminate the list */ + lpInsertAt = NextItem(lpInsertAt); + lpInsertAt->ulSize = 0; + + return lpNewItem->ulSize; + } + return S_OK; +} + +/************************************************************************* + * @ [SHLWAPI.21] + * + * Remove an item from a compact list. + * + * PARAMS + * lppList [O] List to remove the item from + * ulId [I] Id of item to remove + * + * RETURNS + * Success: TRUE. + * Failure: FALSE, If any parameters are invalid, or the item was not found. + */ +BOOL WINAPI SHLWAPI_21(LPSHLWAPI_CLIST* lppList, ULONG ulId) +{ + LPSHLWAPI_CLIST lpList = 0; + LPSHLWAPI_CLIST lpItem = NULL; + LPSHLWAPI_CLIST lpNext; + ULONG ulNewSize; + + TRACE("(%p,%ld)\n", lppList, ulId); + + if(lppList && (lpList = *lppList)) + { + /* Search for item in list */ + while (lpList->ulSize) + { + if(lpList->ulId == ulId || + (lpList->ulId == CLIST_ID_CONTAINER && lpList[1].ulId == ulId)) + { + lpItem = lpList; /* Found */ + break; + } + lpList = NextItem(lpList); + } + } + + if(!lpItem) + return FALSE; + + lpList = lpNext = NextItem(lpItem); + + /* Locate the end of the list */ + while (lpList->ulSize) + lpList = NextItem(lpList); + + /* Resize the list */ + ulNewSize = LocalSize((HLOCAL)*lppList) - lpItem->ulSize; + + /* Copy following elements over lpItem */ + memmove(lpItem, lpNext, (char *)lpList - (char *)lpNext + sizeof(ULONG)); + + if(ulNewSize <= sizeof(ULONG)) + { + LocalFree((HLOCAL)*lppList); + *lppList = NULL; /* Removed the last element */ + } + else + { + lpList = (LPSHLWAPI_CLIST)LocalReAlloc((HLOCAL)*lppList, ulNewSize, + LMEM_ZEROINIT|LMEM_MOVEABLE); + if(lpList) + *lppList = lpList; + } + return TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.22] + * + * Find an item in a compact list. + * + * PARAMS + * lpList [I] List to search + * ulId [I] ID of item to find + * + * RETURNS + * Success: A pointer to the list item found + * Failure: NULL + */ +LPSHLWAPI_CLIST WINAPI SHLWAPI_22(LPSHLWAPI_CLIST lpList, ULONG ulId) +{ + TRACE("(%p,%ld)\n", lpList, ulId); + + if(lpList) + { + while(lpList->ulSize) + { + if(lpList->ulId == ulId) + return lpList; /* Matched */ + else if(lpList->ulId == CLIST_ID_CONTAINER && lpList[1].ulId == ulId) + return lpList + 1; /* Contained item matches */ + + lpList = NextItem(lpList); + } + } + return NULL; +} diff --git a/dlls/shlwapi/ordinal.c b/dlls/shlwapi/ordinal.c index 7e5c4ce5188..1e1a7607ef7 100644 --- a/dlls/shlwapi/ordinal.c +++ b/dlls/shlwapi/ordinal.c @@ -737,78 +737,6 @@ HRESULT WINAPI SHLWAPI_16 ( return 0xabba1252; } -/************************************************************************* - * @ [SHLWAPI.18] - * - * w is pointer to address of callback routine - * x is pointer to LPVOID to receive address of locally allocated - * space size 0x14 - * return is 0 (unless out of memory???) - * - * related to _19, _21 and _22 below - * only seen invoked by SHDOCVW - */ -LONG WINAPI SHLWAPI_18 ( - LPVOID *w, - LPVOID x) -{ - FIXME("(%p %p)stub\n",w,x); - *((LPDWORD)x) = 0; - return 0; -} - -/************************************************************************* - * @ [SHLWAPI.19] - * - * w is address of allocated memory from _21 - * return is 0 (unless out of memory???) - * - * related to _18, _21 and _22 below - * only seen invoked by SHDOCVW - */ -LONG WINAPI SHLWAPI_19 ( - LPVOID w) -{ - FIXME("(%p) stub\n",w); - return 0; -} - -/************************************************************************* - * @ [SHLWAPI.21] - * - * w points to space allocated via .18 above - * LocalSize is done on it (retrieves 18) - * LocalReAlloc is done on it to size 8 with LMEM_MOVEABLE & LMEM_ZEROINIT - * x values seen 0xa0000005 - * returns 1 - * - * relates to _18, _19 and _22 above and below - * only seen invoked by SHDOCVW - */ -LONG WINAPI SHLWAPI_21 ( - LPVOID w, - DWORD x) -{ - FIXME("(%p %lx)stub\n",w,x); - return 1; -} - -/************************************************************************* - * @ [SHLWAPI.22] - * - * return is 'w' value seen in x is 0xa0000005 - * - * relates to _18, _19 and _21 above - * only seen invoked by SHDOCVW - */ -LPVOID WINAPI SHLWAPI_22 ( - LPVOID w, - DWORD x) -{ - FIXME("(%p %lx)stub\n",w,x); - return w; -} - /************************************************************************* * @ [SHLWAPI.23] * diff --git a/dlls/shlwapi/shlwapi.spec b/dlls/shlwapi/shlwapi.spec index 8da4f7b1a02..08f63d65422 100644 --- a/dlls/shlwapi/shlwapi.spec +++ b/dlls/shlwapi/shlwapi.spec @@ -16,12 +16,12 @@ init SHLWAPI_LibMain 14 stdcall @(ptr ptr) SHLWAPI_14 15 stdcall @(ptr ptr) SHLWAPI_15 16 stdcall @(long long long long) SHLWAPI_16 -17 stub @ -18 stdcall @(ptr ptr) SHLWAPI_18 -19 stdcall @(ptr) SHLWAPI_19 -20 stub @ -21 stdcall @(ptr long) SHLWAPI_21 -22 stdcall @(ptr long) SHLWAPI_22 +17 stdcall @ (ptr ptr) SHLWAPI_17 +18 stdcall @ (ptr ptr) SHLWAPI_18 +19 stdcall @ (ptr) SHLWAPI_19 +20 stdcall @ (ptr ptr) SHLWAPI_20 +21 stdcall @ (ptr long) SHLWAPI_21 +22 stdcall @ (ptr long) SHLWAPI_22 23 stdcall @(ptr ptr long) SHLWAPI_23 24 stdcall @(ptr ptr long) SHLWAPI_24 25 stdcall @(long) SHLWAPI_25 diff --git a/dlls/shlwapi/tests/.cvsignore b/dlls/shlwapi/tests/.cvsignore index b442d272e63..861ac164773 100644 --- a/dlls/shlwapi/tests/.cvsignore +++ b/dlls/shlwapi/tests/.cvsignore @@ -1,3 +1,4 @@ +clist.ok shlwapi_test.exe.spec.c shreg.ok testlist.c diff --git a/dlls/shlwapi/tests/clist.c b/dlls/shlwapi/tests/clist.c new file mode 100644 index 00000000000..db469e4d724 --- /dev/null +++ b/dlls/shlwapi/tests/clist.c @@ -0,0 +1,454 @@ +/* Unit test suite for SHLWAPI Compact List functions + * + * Copyright 2002 Jon Griffiths + * + * 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 + */ + +#include "wine/test.h" +#include "winbase.h" +#include "objbase.h" + +typedef struct tagSHLWAPI_CLIST +{ + ULONG ulSize; + ULONG ulId; +} SHLWAPI_CLIST, *LPSHLWAPI_CLIST; + +typedef const SHLWAPI_CLIST* LPCSHLWAPI_CLIST; + +/* Items to add */ +static const SHLWAPI_CLIST SHLWAPI_CLIST_items[] = +{ + {4, 1}, + {8, 3}, + {12, 2}, + {16, 8}, + {20, 9}, + {3, 11}, + {9, 82}, + {33, 16}, + {32, 55}, + {24, 100}, + {39, 116}, + { 0, 0} +}; + +/* Dummy IStream object for testing calls */ +typedef struct +{ + void* lpVtbl; + ULONG ref; + int readcalls; + BOOL failreadcall; + BOOL failreadsize; + BOOL readbeyondend; + BOOL readreturnlarge; + int writecalls; + BOOL failwritecall; + BOOL failwritesize; + int seekcalls; + int statcalls; + LPCSHLWAPI_CLIST item; +} _IDummyStream; + +static +HRESULT WINAPI QueryInterface(_IDummyStream *This,REFIID riid, LPVOID *ppvObj) +{ + return S_OK; +} + +static ULONG WINAPI AddRef(_IDummyStream *This) +{ + return ++This->ref; +} + +static ULONG WINAPI Release(_IDummyStream *This) +{ + return --This->ref; +} + +static HRESULT WINAPI Read(_IDummyStream* This, LPVOID lpMem, ULONG ulSize, + LPULONG lpRead) +{ + HRESULT hRet = S_OK; + ++This->readcalls; + + if (This->failreadcall) + { + return STG_E_ACCESSDENIED; + } + else if (This->failreadsize) + { + *lpRead = ulSize + 8; + return S_OK; + } + else if (This->readreturnlarge) + { + *((ULONG*)lpMem) = 0xffff01; + *lpRead = ulSize; + This->readreturnlarge = FALSE; + return S_OK; + } + if (ulSize == sizeof(ULONG)) + { + /* Read size of item */ + *((ULONG*)lpMem) = This->item->ulSize ? This->item->ulSize + sizeof(SHLWAPI_CLIST) : 0; + *lpRead = ulSize; + } + else + { + unsigned int i; + char* buff = (char*)lpMem; + + /* Read item data */ + if (!This->item->ulSize) + { + This->readbeyondend = TRUE; + *lpRead = 0; + return E_FAIL; /* Should never happen */ + } + *((ULONG*)lpMem) = This->item->ulId; + *lpRead = ulSize; + + for (i = 0; i < This->item->ulSize; i++) + buff[4+i] = i*2; + + This->item++; + } + return hRet; +} + +static HRESULT WINAPI Write(_IDummyStream* This, LPVOID lpMem, ULONG ulSize, + LPULONG lpWritten) +{ + HRESULT hRet = S_OK; + + ++This->writecalls; + if (This->failwritecall) + { + return STG_E_ACCESSDENIED; + } + else if (This->failwritesize) + { + *lpWritten = 0; + } + else + *lpWritten = ulSize; + return hRet; +} + +static HRESULT WINAPI Seek(_IDummyStream* This, LARGE_INTEGER dlibMove, + DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition) +{ + ++This->seekcalls; + if (plibNewPosition) + plibNewPosition->QuadPart = sizeof(ULONG); + return S_OK; +} + +static HRESULT WINAPI Stat(_IDummyStream* This, STATSTG* pstatstg, + DWORD grfStatFlag) +{ + ++This->statcalls; + if (pstatstg) + pstatstg->cbSize.QuadPart = 5000l; + return S_OK; +} + +/* VTable */ +static void* iclvt[] = +{ + QueryInterface, + AddRef, + Release, + Read, + Write, + Seek, + NULL, /* SetSize */ + NULL, /* CopyTo */ + NULL, /* Commit */ + NULL, /* Revert */ + NULL, /* LockRegion */ + NULL, /* UnlockRegion */ + Stat, + NULL /* Clone */ +}; + +/* Function ptrs for ordinal calls */ +static HMODULE SHLWAPI_hshlwapi = 0; + +static VOID (WINAPI *pSHLWAPI_19)(LPSHLWAPI_CLIST); +static HRESULT (WINAPI *pSHLWAPI_20)(LPSHLWAPI_CLIST*,LPCSHLWAPI_CLIST); +static BOOL (WINAPI *pSHLWAPI_21)(LPSHLWAPI_CLIST*,ULONG); +static LPSHLWAPI_CLIST (WINAPI *pSHLWAPI_22)(LPSHLWAPI_CLIST,ULONG); +static HRESULT (WINAPI *pSHLWAPI_17)(_IDummyStream*,LPSHLWAPI_CLIST); +static HRESULT (WINAPI *pSHLWAPI_18)(_IDummyStream*,LPSHLWAPI_CLIST*); + +static void InitFunctionPtrs() +{ + SHLWAPI_hshlwapi = LoadLibraryA("shlwapi.dll"); + ok(SHLWAPI_hshlwapi != 0, "LoadLibrary failed"); + if (SHLWAPI_hshlwapi) + { + pSHLWAPI_17 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)17); + ok(pSHLWAPI_17 != 0, "No Ordinal 17"); + pSHLWAPI_18 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)18); + ok(pSHLWAPI_18 != 0, "No Ordinal 18"); + pSHLWAPI_19 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)19); + ok(pSHLWAPI_19 != 0, "No Ordinal 19"); + pSHLWAPI_20 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)20); + ok(pSHLWAPI_20 != 0, "No Ordinal 20"); + pSHLWAPI_21 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)21); + ok(pSHLWAPI_21 != 0, "No Ordinal 21"); + pSHLWAPI_22 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)22); + ok(pSHLWAPI_22 != 0, "No Ordinal 22"); + } +} + +static void InitDummyStream(_IDummyStream* iface) +{ + iface->lpVtbl = (void*)iclvt; + iface->ref = 1; + iface->readcalls = 0; + iface->failreadcall = FALSE; + iface->failreadsize = FALSE; + iface->readbeyondend = FALSE; + iface->readreturnlarge = FALSE; + iface->writecalls = 0; + iface->failwritecall = FALSE; + iface->failwritesize = FALSE; + iface->seekcalls = 0; + iface->statcalls = 0; + iface->item = SHLWAPI_CLIST_items; +} + + +static void test_CList(void) +{ + _IDummyStream streamobj; + LPSHLWAPI_CLIST list = NULL; + LPCSHLWAPI_CLIST item = SHLWAPI_CLIST_items; + HRESULT hRet; + LPSHLWAPI_CLIST inserted; + char buff[64]; + unsigned int i; + + if (!pSHLWAPI_17 || !pSHLWAPI_18 || !pSHLWAPI_19 || !pSHLWAPI_20 || + !pSHLWAPI_21 || !pSHLWAPI_22) + return; + + /* Populate a list and test the items are added correctly */ + while (item->ulSize) + { + /* Create item and fill with data */ + inserted = (LPSHLWAPI_CLIST)buff; + inserted->ulSize = item->ulSize + sizeof(SHLWAPI_CLIST); + inserted->ulId = item->ulId; + for (i = 0; i < item->ulSize; i++) + buff[sizeof(SHLWAPI_CLIST)+i] = i*2; + + /* Add it */ + hRet = pSHLWAPI_20(&list, inserted); + ok(hRet > S_OK, "failed list add"); + + if (hRet > S_OK) + { + ok(list && list->ulSize, "item not added"); + + /* Find it */ + inserted = pSHLWAPI_22(list, item->ulId); + ok(inserted != NULL, "lost after adding"); + + ok(!inserted || inserted->ulId != -1u, "find returned a container"); + + /* Check size */ + if (inserted && inserted->ulSize & 0x3) + { + /* Contained */ + ok(inserted[-1].ulId == -1u, "invalid size is not countained"); + ok(inserted[-1].ulSize > inserted->ulSize+sizeof(SHLWAPI_CLIST), + "container too small"); + } + else if (inserted) + { + ok(inserted->ulSize==item->ulSize+sizeof(SHLWAPI_CLIST), + "id %ld size wrong (%ld!=%ld)", inserted->ulId, inserted->ulSize, + item->ulSize+sizeof(SHLWAPI_CLIST)); + } + if (inserted) + { + BOOL bDataOK = TRUE; + LPBYTE bufftest = (LPBYTE)inserted; + + for (i = 0; i < inserted->ulSize - sizeof(SHLWAPI_CLIST); i++) + if (bufftest[sizeof(SHLWAPI_CLIST)+i] != i*2) + bDataOK = FALSE; + + ok(bDataOK == TRUE, "data corrupted on insert"); + } + ok(!inserted || inserted->ulId==item->ulId, "find got wrong item"); + } + item++; + } + + /* Write the list */ + InitDummyStream(&streamobj); + + hRet = pSHLWAPI_17(&streamobj, list); + ok(hRet == S_OK, "write failed"); + if (hRet == S_OK) + { + /* 1 call for each element, + 1 for OK (use our null element for this) */ + ok(streamobj.writecalls == sizeof(SHLWAPI_CLIST_items)/sizeof(SHLWAPI_CLIST), + "wrong call count"); + ok(streamobj.readcalls == 0,"called Read() in write"); + ok(streamobj.seekcalls == 0,"called Seek() in write"); + } + + /* Failure cases for writing */ + InitDummyStream(&streamobj); + streamobj.failwritecall = TRUE; + hRet = pSHLWAPI_17(&streamobj, list); + ok(hRet == STG_E_ACCESSDENIED, "changed object failure return"); + ok(streamobj.writecalls == 1, "called object after failure"); + ok(streamobj.readcalls == 0,"called Read() after failure"); + ok(streamobj.seekcalls == 0,"called Seek() after failure"); + + InitDummyStream(&streamobj); + streamobj.failwritesize = TRUE; + hRet = pSHLWAPI_17(&streamobj, list); + ok(hRet == STG_E_MEDIUMFULL, "changed size failure return"); + ok(streamobj.writecalls == 1, "called object after size failure"); + ok(streamobj.readcalls == 0,"called Read() after failure"); + ok(streamobj.seekcalls == 0,"called Seek() after failure"); + + /* Invalid inputs for adding */ + inserted = (LPSHLWAPI_CLIST)buff; + inserted->ulSize = sizeof(SHLWAPI_CLIST) -1; + inserted->ulId = 33; + hRet = pSHLWAPI_20(&list, inserted); + ok(hRet == E_INVALIDARG, "allowed bad element size"); + + inserted->ulSize = 44; + inserted->ulId = -1; + hRet = pSHLWAPI_20(&list, inserted); + ok(hRet == E_INVALIDARG, "allowed adding a container"); + + item = SHLWAPI_CLIST_items; + + /* Look for non-existing item in populated list */ + inserted = pSHLWAPI_22(list, 99999999); + ok(inserted == NULL, "found a non-existing item"); + + while (item->ulSize) + { + /* Delete items */ + BOOL bRet = pSHLWAPI_21(&list, item->ulId); + ok(bRet == TRUE, "couldn't find item to delete"); + item++; + } + + /* Look for non-existing item in empty list */ + inserted = pSHLWAPI_22(list, 99999999); + ok(inserted == NULL, "found an item in empty list"); + + /* Create a list by reading in data */ + InitDummyStream(&streamobj); + + hRet = pSHLWAPI_18(&streamobj, &list); + ok(hRet == S_OK, "failed create from Read()"); + if (hRet == S_OK) + { + ok(streamobj.readbeyondend == FALSE, "read beyond end"); + /* 2 calls per item, but only 1 for the terminator */ + ok(streamobj.readcalls == sizeof(SHLWAPI_CLIST_items)/sizeof(SHLWAPI_CLIST)*2-1, + "wrong call count"); + ok(streamobj.writecalls == 0, "called Write() from create"); + ok(streamobj.seekcalls == 0,"called Seek() from create"); + + item = SHLWAPI_CLIST_items; + + /* Check the items were added correctly */ + while (item->ulSize) + { + inserted = pSHLWAPI_22(list, item->ulId); + ok(inserted != NULL, "lost after adding"); + + ok(!inserted || inserted->ulId != -1, "find returned a container"); + + /* Check size */ + if (inserted && inserted->ulSize & 0x3) + { + /* Contained */ + ok(inserted[-1].ulId == -1, "invalid size is not countained"); + ok(inserted[-1].ulSize > inserted->ulSize+sizeof(SHLWAPI_CLIST), + "container too small"); + } + else if (inserted) + { + ok(inserted->ulSize==item->ulSize+sizeof(SHLWAPI_CLIST), + "id %ld size wrong (%ld!=%ld)", inserted->ulId, inserted->ulSize, + item->ulSize+sizeof(SHLWAPI_CLIST)); + } + ok(!inserted || inserted->ulId==item->ulId, "find got wrong item"); + if (inserted) + { + BOOL bDataOK = TRUE; + char *bufftest = (char*)inserted; + + for (i = 0; i < inserted->ulSize - sizeof(SHLWAPI_CLIST); i++) + if (bufftest[sizeof(SHLWAPI_CLIST)+i] != i*2) + bDataOK = FALSE; + + ok(bDataOK == TRUE, "data corrupted on insert"); + } + item++; + } + } + + /* Failure cases for reading */ + InitDummyStream(&streamobj); + streamobj.failreadcall = TRUE; + hRet = pSHLWAPI_18(&streamobj, &list); + ok(hRet == STG_E_ACCESSDENIED, "changed object failure return"); + ok(streamobj.readbeyondend == FALSE, "read beyond end"); + ok(streamobj.readcalls == 1, "called object after read failure"); + ok(streamobj.writecalls == 0,"called Write() after read failure"); + ok(streamobj.seekcalls == 0,"called Seek() after read failure"); + + /* Read returns large object */ + InitDummyStream(&streamobj); + streamobj.readreturnlarge = TRUE; + hRet = pSHLWAPI_18(&streamobj, &list); + ok(hRet == S_OK, "failed create from Read() with large item"); + ok(streamobj.readbeyondend == FALSE, "read beyond end"); + ok(streamobj.readcalls == 1,"wrong call count"); + ok(streamobj.writecalls == 0,"called Write() after read failure"); + ok(streamobj.seekcalls == 2,"wrong Seek() call count (%d)", streamobj.seekcalls); + + pSHLWAPI_19(list); +} + + +START_TEST(clist) +{ + InitFunctionPtrs(); + + test_CList(); + + if (SHLWAPI_hshlwapi) + FreeLibrary(SHLWAPI_hshlwapi); +}