Add traces, add unit tests for IPropertyStorage, and fix the problems
they caught.
This commit is contained in:
parent
6d83105054
commit
50794ce787
|
@ -22,6 +22,9 @@
|
|||
#include "windef.h"
|
||||
#include "winbase.h"
|
||||
#include "dictionary.h"
|
||||
#include "wine/debug.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(storage);
|
||||
|
||||
struct dictionary_entry
|
||||
{
|
||||
|
@ -36,12 +39,14 @@ struct dictionary
|
|||
destroyfunc destroy;
|
||||
void *extra;
|
||||
struct dictionary_entry *head;
|
||||
UINT num_entries;
|
||||
};
|
||||
|
||||
struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra)
|
||||
{
|
||||
struct dictionary *ret;
|
||||
|
||||
TRACE("(%p, %p, %p)\n", c, d, extra);
|
||||
if (!c)
|
||||
return NULL;
|
||||
ret = HeapAlloc(GetProcessHeap(), 0, sizeof(struct dictionary));
|
||||
|
@ -51,12 +56,15 @@ struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra)
|
|||
ret->destroy = d;
|
||||
ret->extra = extra;
|
||||
ret->head = NULL;
|
||||
ret->num_entries = 0;
|
||||
}
|
||||
TRACE("returning %p\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dictionary_destroy(struct dictionary *d)
|
||||
{
|
||||
TRACE("(%p)\n", d);
|
||||
if (d)
|
||||
{
|
||||
struct dictionary_entry *p;
|
||||
|
@ -74,6 +82,11 @@ void dictionary_destroy(struct dictionary *d)
|
|||
}
|
||||
}
|
||||
|
||||
UINT dictionary_num_entries(struct dictionary *d)
|
||||
{
|
||||
return d ? d->num_entries : 0;
|
||||
}
|
||||
|
||||
/* Returns the address of the pointer to the node containing k. (It returns
|
||||
* the address of either h->head or the address of the next member of the
|
||||
* prior node. It's useful when you want to delete.)
|
||||
|
@ -101,6 +114,7 @@ void dictionary_insert(struct dictionary *d, const void *k, const void *v)
|
|||
{
|
||||
struct dictionary_entry **prior;
|
||||
|
||||
TRACE("(%p, %p, %p)\n", d, k, v);
|
||||
if (!d)
|
||||
return;
|
||||
if ((prior = dictionary_find_internal(d, k)))
|
||||
|
@ -121,6 +135,7 @@ void dictionary_insert(struct dictionary *d, const void *k, const void *v)
|
|||
elem->value = (void *)v;
|
||||
elem->next = d->head;
|
||||
d->head = elem;
|
||||
d->num_entries++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,6 +144,7 @@ BOOL dictionary_find(struct dictionary *d, const void *k, void **value)
|
|||
struct dictionary_entry **prior;
|
||||
BOOL ret = FALSE;
|
||||
|
||||
TRACE("(%p, %p, %p)\n", d, k, value);
|
||||
if (!d)
|
||||
return FALSE;
|
||||
if (!value)
|
||||
|
@ -138,6 +154,7 @@ BOOL dictionary_find(struct dictionary *d, const void *k, void **value)
|
|||
*value = (*prior)->value;
|
||||
ret = TRUE;
|
||||
}
|
||||
TRACE("returning %d (%p)\n", ret, *value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -145,6 +162,7 @@ void dictionary_remove(struct dictionary *d, const void *k)
|
|||
{
|
||||
struct dictionary_entry **prior, *temp;
|
||||
|
||||
TRACE("(%p, %p)\n", d, k);
|
||||
if (!d)
|
||||
return;
|
||||
if ((prior = dictionary_find_internal(d, k)))
|
||||
|
@ -154,17 +172,20 @@ void dictionary_remove(struct dictionary *d, const void *k)
|
|||
d->destroy((*prior)->key, (*prior)->value, d->extra);
|
||||
*prior = (*prior)->next;
|
||||
HeapFree(GetProcessHeap(), 0, temp);
|
||||
d->num_entries--;
|
||||
}
|
||||
}
|
||||
|
||||
void dictionary_enumerate(struct dictionary *d, enumeratefunc e)
|
||||
void dictionary_enumerate(struct dictionary *d, enumeratefunc e, void *closure)
|
||||
{
|
||||
struct dictionary_entry *p;
|
||||
|
||||
TRACE("(%p, %p, %p)\n", d, e, closure);
|
||||
if (!d)
|
||||
return;
|
||||
if (!e)
|
||||
return;
|
||||
for (p = d->head; p; p = p->next)
|
||||
e(p->key, p->value, d->extra);
|
||||
if (!e(p->key, p->value, d->extra, closure))
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -42,7 +42,8 @@ typedef void (*destroyfunc)(void *k, void *v, void *extra);
|
|||
/* Called for each element in the dictionary. Return FALSE if you don't want
|
||||
* to enumerate any more.
|
||||
*/
|
||||
typedef BOOL (*enumeratefunc)(const void *k, const void *d, void *extra);
|
||||
typedef BOOL (*enumeratefunc)(const void *k, const void *v, void *extra,
|
||||
void *closure);
|
||||
|
||||
/* Constructs a dictionary, using c as a comparison function for keys.
|
||||
* If d is not NULL, it will be called whenever an item is about to be removed
|
||||
|
@ -56,6 +57,11 @@ struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra);
|
|||
/* Assumes d is not NULL. */
|
||||
void dictionary_destroy(struct dictionary *d);
|
||||
|
||||
/* Returns how many entries have been stored in the dictionary. If two values
|
||||
* with the same key are inserted, only one is counted.
|
||||
*/
|
||||
UINT dictionary_num_entries(struct dictionary *d);
|
||||
|
||||
/* Sets an element with key k and value v to the dictionary. If a value
|
||||
* already exists with key k, its value is replaced, and the destroyfunc (if
|
||||
* set) is called for the previous item.
|
||||
|
@ -82,6 +88,6 @@ BOOL dictionary_find(struct dictionary *d, const void *k, void **v);
|
|||
*/
|
||||
void dictionary_remove(struct dictionary *d, const void *k);
|
||||
|
||||
void dictionary_enumerate(struct dictionary *d, enumeratefunc e);
|
||||
void dictionary_enumerate(struct dictionary *d, enumeratefunc e, void *closure);
|
||||
|
||||
#endif /* ndef __DICTIONARY_H__ */
|
||||
|
|
|
@ -79,6 +79,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(storage);
|
|||
#define PROPSETHDR_OSVER_KIND_MAC 1
|
||||
#define PROPSETHDR_OSVER_KIND_WIN32 2
|
||||
|
||||
#define CP_UNICODE 1200
|
||||
|
||||
/* The format version (and what it implies) is described here:
|
||||
* http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp
|
||||
*/
|
||||
|
@ -106,7 +108,7 @@ typedef struct tagPROPERTYSECTIONHEADER
|
|||
typedef struct tagPROPERTYIDOFFSET
|
||||
{
|
||||
DWORD propid;
|
||||
DWORD dwOffset;
|
||||
DWORD dwOffset; /* from beginning of section */
|
||||
} PROPERTYIDOFFSET;
|
||||
|
||||
/* Initializes the property storage from the stream (and undoes any uncommitted
|
||||
|
@ -209,6 +211,7 @@ static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
|
|||
if (!This)
|
||||
return NULL;
|
||||
dictionary_find(This->propid_to_prop, (void *)propid, (void **)&ret);
|
||||
TRACE("returning %p\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -222,6 +225,7 @@ static PROPVARIANT *PropertyStorage_FindPropertyByName(
|
|||
return NULL;
|
||||
if (dictionary_find(This->name_to_propid, name, (void **)&propid))
|
||||
ret = PropertyStorage_FindProperty(This, (PROPID)propid);
|
||||
TRACE("returning %p\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -233,6 +237,7 @@ static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
|
|||
if (!This)
|
||||
return NULL;
|
||||
dictionary_find(This->propid_to_name, (void *)propid, (void **)&ret);
|
||||
TRACE("returning %p\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -246,7 +251,7 @@ static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
|
|||
PROPVARIANT rgpropvar[])
|
||||
{
|
||||
PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
|
||||
HRESULT hr = S_OK;
|
||||
HRESULT hr = S_FALSE;
|
||||
ULONG i;
|
||||
|
||||
TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
|
||||
|
@ -298,6 +303,8 @@ static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
|
|||
{
|
||||
PropVariantCopy(prop, propvar);
|
||||
dictionary_insert(This->propid_to_prop, (void *)propid, prop);
|
||||
if (propid > This->highestProp)
|
||||
This->highestProp = propid;
|
||||
}
|
||||
else
|
||||
hr = STG_E_INSUFFICIENTMEMORY;
|
||||
|
@ -341,6 +348,9 @@ static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
|
|||
PropVariantCopy(prop, &rgpropvar[i]);
|
||||
else
|
||||
{
|
||||
/* Note that I don't do the special cases here that I do below,
|
||||
* because naming the special PIDs isn't supported.
|
||||
*/
|
||||
if (propidNameFirst < PID_FIRST_USABLE ||
|
||||
propidNameFirst >= PID_MIN_READONLY)
|
||||
hr = STG_E_INVALIDPARAMETER;
|
||||
|
@ -352,11 +362,12 @@ static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
|
|||
len * sizeof(WCHAR));
|
||||
|
||||
strcpyW(name, rgpspec[i].u.lpwstr);
|
||||
TRACE("Adding prop name %s, propid %ld\n", debugstr_w(name),
|
||||
nextId);
|
||||
dictionary_insert(This->name_to_propid, name,
|
||||
(void *)nextId);
|
||||
dictionary_insert(This->propid_to_name, (void *)nextId,
|
||||
name);
|
||||
This->highestProp = nextId;
|
||||
hr = PropertyStorage_StorePropWithId(This, nextId,
|
||||
&rgpropvar[i]);
|
||||
}
|
||||
|
@ -364,12 +375,38 @@ static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
|
|||
}
|
||||
else
|
||||
{
|
||||
/* FIXME: certain propid's have special behavior. E.g., you can't
|
||||
* set propid 0, and setting PID_BEHAVIOR affects the
|
||||
* case-sensitivity.
|
||||
*/
|
||||
hr = PropertyStorage_StorePropWithId(This, rgpspec[i].u.propid,
|
||||
&rgpropvar[i]);
|
||||
switch (rgpspec[i].u.propid)
|
||||
{
|
||||
case PID_DICTIONARY:
|
||||
/* Can't set the dictionary */
|
||||
hr = STG_E_INVALIDPARAMETER;
|
||||
break;
|
||||
case PID_CODEPAGE:
|
||||
/* Can only set the code page if nothing else has been set */
|
||||
if (dictionary_num_entries(This->propid_to_prop) == 0 &&
|
||||
rgpropvar[i].vt == VT_I2)
|
||||
This->codePage = rgpropvar[i].u.iVal;
|
||||
else
|
||||
hr = STG_E_INVALIDPARAMETER;
|
||||
break;
|
||||
case PID_LOCALE:
|
||||
/* Can only set the locale if nothing else has been set */
|
||||
if (dictionary_num_entries(This->propid_to_prop) == 0 &&
|
||||
rgpropvar[i].vt == VT_I4)
|
||||
This->locale = rgpropvar[i].u.lVal;
|
||||
else
|
||||
hr = STG_E_INVALIDPARAMETER;
|
||||
break;
|
||||
case PID_ILLEGAL:
|
||||
/* silently ignore like MSDN says */
|
||||
break;
|
||||
default:
|
||||
if (rgpspec[i].u.propid >= PID_MIN_READONLY)
|
||||
hr = STG_E_INVALIDPARAMETER;
|
||||
else
|
||||
hr = PropertyStorage_StorePropWithId(This,
|
||||
rgpspec[i].u.propid, &rgpropvar[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&This->cs);
|
||||
|
@ -391,7 +428,7 @@ static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
|
|||
TRACE("(%p, %ld, %p)\n", iface, cpspec, rgpspec);
|
||||
if (!This)
|
||||
return E_INVALIDARG;
|
||||
if (!rgpspec)
|
||||
if (cpspec && !rgpspec)
|
||||
return E_INVALIDARG;
|
||||
if (!(This->grfMode & STGM_READWRITE))
|
||||
return STG_E_ACCESSDENIED;
|
||||
|
@ -409,13 +446,12 @@ static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
|
|||
}
|
||||
else
|
||||
{
|
||||
/* FIXME: certain propid's have special meaning. For example,
|
||||
* removing propid 0 is supposed to remove the dictionary, and
|
||||
* removing PID_BEHAVIOR should change this to a case-insensitive
|
||||
* property set. Unknown "read-only" propid's should be ignored.
|
||||
*/
|
||||
if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
|
||||
rgpspec[i].u.propid < PID_MIN_READONLY)
|
||||
dictionary_remove(This->propid_to_prop,
|
||||
(void *)rgpspec[i].u.propid);
|
||||
else
|
||||
hr = STG_E_INVALIDPARAMETER;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&This->cs);
|
||||
|
@ -433,17 +469,13 @@ static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
|
|||
{
|
||||
PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
|
||||
ULONG i;
|
||||
HRESULT hr;
|
||||
HRESULT hr = S_FALSE;
|
||||
|
||||
TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
|
||||
if (!This)
|
||||
return E_INVALIDARG;
|
||||
if (cpropid && (!rgpropid || !rglpwstrName))
|
||||
return E_INVALIDARG;
|
||||
/* MSDN says S_FALSE is returned if no strings matching rgpropid are found,
|
||||
* default to that
|
||||
*/
|
||||
hr = S_FALSE;
|
||||
EnterCriticalSection(&This->cs);
|
||||
for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
|
||||
{
|
||||
|
@ -664,6 +696,11 @@ static int PropertyStorage_PropNameCompare(const void *a, const void *b,
|
|||
{
|
||||
PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
|
||||
|
||||
TRACE("(%s, %s)\n", debugstr_w(a), debugstr_w(b));
|
||||
/* FIXME: this assumes property names are always Unicode, but they
|
||||
* might be ANSI, depending on whether This->grfFlags & PROPSETFLAG_ANSI
|
||||
* is true.
|
||||
*/
|
||||
if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
|
||||
return strcmpW((LPCWSTR)a, (LPCWSTR)b);
|
||||
else
|
||||
|
@ -678,6 +715,7 @@ static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
|
|||
static int PropertyStorage_PropCompare(const void *a, const void *b,
|
||||
void *extra)
|
||||
{
|
||||
TRACE("(%ld, %ld)\n", (PROPID)a, (PROPID)b);
|
||||
return (PROPID)a - (PROPID)b;
|
||||
}
|
||||
|
||||
|
@ -691,6 +729,8 @@ static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
|
|||
* the entries according to the values of This->codePage and This->locale.
|
||||
* FIXME: there isn't any checking whether the read property extends past the
|
||||
* end of the buffer.
|
||||
* FIXME: this always stores dictionary entries as Unicode, but it should store
|
||||
* them as ANSI if (This->grfFlags & PROPSETFLAG_ANSI) is true.
|
||||
*/
|
||||
static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
|
||||
BYTE *ptr)
|
||||
|
@ -699,10 +739,6 @@ static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
|
|||
HRESULT hr = S_OK;
|
||||
|
||||
ptr += sizeof(DWORD);
|
||||
This->name_to_propid = dictionary_create(PropertyStorage_PropNameCompare,
|
||||
PropertyStorage_PropNameDestroy, This);
|
||||
This->propid_to_name = dictionary_create(PropertyStorage_PropCompare, NULL,
|
||||
This);
|
||||
if (This->name_to_propid && This->propid_to_name)
|
||||
{
|
||||
DWORD i;
|
||||
|
@ -742,7 +778,7 @@ static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
|
|||
}
|
||||
ptr += sizeof(DWORD) + cbEntry;
|
||||
/* Unicode entries are padded to DWORD boundaries */
|
||||
if (This->codePage == 1200 && cbEntry % sizeof(DWORD))
|
||||
if (This->codePage == CP_UNICODE && cbEntry % sizeof(DWORD))
|
||||
ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
|
||||
}
|
||||
}
|
||||
|
@ -863,6 +899,8 @@ static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
|
|||
ULONG count = 0;
|
||||
HRESULT hr;
|
||||
|
||||
assert(stm);
|
||||
assert(hdr);
|
||||
hr = IStream_Read(stm, buf, sizeof(buf), &count);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
@ -896,6 +934,8 @@ static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
|
|||
ULONG count = 0;
|
||||
HRESULT hr;
|
||||
|
||||
assert(stm);
|
||||
assert(fmt);
|
||||
hr = IStream_Read(stm, buf, sizeof(buf), &count);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
@ -923,6 +963,8 @@ static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
|
|||
ULONG count = 0;
|
||||
HRESULT hr;
|
||||
|
||||
assert(stm);
|
||||
assert(hdr);
|
||||
hr = IStream_Read(stm, buf, sizeof(buf), &count);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
@ -962,9 +1004,29 @@ static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
|
|||
This->dirty = FALSE;
|
||||
This->highestProp = 0;
|
||||
dictionary_destroy(This->name_to_propid);
|
||||
This->name_to_propid = NULL;
|
||||
dictionary_destroy(This->propid_to_name);
|
||||
This->propid_to_name = NULL;
|
||||
dictionary_destroy(This->propid_to_prop);
|
||||
This->name_to_propid = dictionary_create(PropertyStorage_PropNameCompare,
|
||||
PropertyStorage_PropNameDestroy, This);
|
||||
if (!This->name_to_propid)
|
||||
{
|
||||
hr = STG_E_INSUFFICIENTMEMORY;
|
||||
goto end;
|
||||
}
|
||||
This->propid_to_name = dictionary_create(PropertyStorage_PropCompare, NULL,
|
||||
This);
|
||||
if (!This->propid_to_name)
|
||||
{
|
||||
hr = STG_E_INSUFFICIENTMEMORY;
|
||||
goto end;
|
||||
}
|
||||
This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
|
||||
PropertyStorage_PropertyDestroy, This);
|
||||
if (!This->propid_to_prop)
|
||||
{
|
||||
hr = STG_E_INSUFFICIENTMEMORY;
|
||||
goto end;
|
||||
}
|
||||
hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
|
@ -1047,9 +1109,6 @@ static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
|
|||
sizeof(PROPERTYSECTIONHEADER), &count);
|
||||
if (FAILED(hr))
|
||||
goto end;
|
||||
dictionary_destroy(This->propid_to_prop);
|
||||
This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
|
||||
PropertyStorage_PropertyDestroy, This);
|
||||
for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
|
||||
{
|
||||
PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
|
||||
|
@ -1064,7 +1123,7 @@ static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
|
|||
idOffset->propid < PID_MIN_READONLY && idOffset->propid >
|
||||
This->highestProp)
|
||||
This->highestProp = idOffset->propid;
|
||||
if (idOffset->propid == 0)
|
||||
if (idOffset->propid == PID_DICTIONARY)
|
||||
{
|
||||
/* Don't read the dictionary yet, its entries depend on the
|
||||
* code page. Just store the offset so we know to read it
|
||||
|
@ -1083,10 +1142,6 @@ static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
|
|||
buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER) +
|
||||
sizeof(DWORD))))
|
||||
{
|
||||
/* FIXME: the PID_CODEPAGE and PID_LOCALE special cases
|
||||
* aren't really needed, just look them up in
|
||||
* propid_to_prop when needed
|
||||
*/
|
||||
switch(idOffset->propid)
|
||||
{
|
||||
case PID_CODEPAGE:
|
||||
|
@ -1117,7 +1172,7 @@ static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
|
|||
if (This->grfFlags & PROPSETFLAG_ANSI)
|
||||
This->codePage = GetACP();
|
||||
else
|
||||
This->codePage = 1200;
|
||||
This->codePage = CP_UNICODE;
|
||||
}
|
||||
if (!This->locale)
|
||||
This->locale = LOCALE_SYSTEM_DEFAULT;
|
||||
|
@ -1128,10 +1183,18 @@ static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
|
|||
|
||||
end:
|
||||
HeapFree(GetProcessHeap(), 0, buf);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
dictionary_destroy(This->name_to_propid);
|
||||
This->name_to_propid = NULL;
|
||||
dictionary_destroy(This->propid_to_name);
|
||||
This->propid_to_name = NULL;
|
||||
dictionary_destroy(This->propid_to_prop);
|
||||
This->propid_to_prop = NULL;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
static HRESULT PropertyStorage_WriteToStream(IPropertyStorage *iface)
|
||||
{
|
||||
FIXME("\n");
|
||||
|
|
|
@ -2,5 +2,6 @@ Makefile
|
|||
marshal.ok
|
||||
moniker.ok
|
||||
propvariant.ok
|
||||
stg_prop.ok
|
||||
storage32.ok
|
||||
testlist.c
|
||||
|
|
|
@ -10,6 +10,7 @@ CTESTS = \
|
|||
marshal.c \
|
||||
moniker.c \
|
||||
propvariant.c \
|
||||
stg_prop.c \
|
||||
storage32.c
|
||||
|
||||
@MAKE_TEST_RULES@
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
/* IPropertyStorage unit tests
|
||||
* Copyright 2005 Juan Lang
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#define COBJMACROS
|
||||
#include "objbase.h"
|
||||
#include "wine/test.h"
|
||||
|
||||
#ifdef NONAMELESSUNION
|
||||
# define U(x) (x).u
|
||||
#else
|
||||
# define U(x) (x)
|
||||
#endif
|
||||
|
||||
/* FIXME: this creates an ANSI storage, need try to find conditions under which
|
||||
* Unicode translation fails
|
||||
*/
|
||||
static void testProps(void)
|
||||
{
|
||||
static const WCHAR szDot[] = { '.',0 };
|
||||
static const WCHAR szPrefix[] = { 's','t','g',0 };
|
||||
static const WCHAR propName[] = { 'p','r','o','p',0 };
|
||||
WCHAR filename[MAX_PATH];
|
||||
HRESULT hr;
|
||||
IStorage *storage = NULL;
|
||||
IPropertySetStorage *propSetStorage = NULL;
|
||||
IPropertyStorage *propertyStorage = NULL;
|
||||
PROPSPEC spec;
|
||||
PROPVARIANT var;
|
||||
|
||||
if(!GetTempFileNameW(szDot, szPrefix, 0, filename))
|
||||
return;
|
||||
|
||||
DeleteFileW(filename);
|
||||
|
||||
hr = StgCreateDocfile(filename,
|
||||
STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, &storage);
|
||||
ok(SUCCEEDED(hr), "StgCreateDocfile failed: 0x%08lx\n", hr);
|
||||
|
||||
hr = StgCreatePropSetStg(storage, 0, &propSetStorage);
|
||||
ok(SUCCEEDED(hr), "StgCreatePropSetStg failed: 0x%08lx\n", hr);
|
||||
|
||||
hr = IPropertySetStorage_Create(propSetStorage,
|
||||
&FMTID_SummaryInformation, NULL, PROPSETFLAG_ANSI,
|
||||
STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
|
||||
&propertyStorage);
|
||||
ok(SUCCEEDED(hr), "QI -> IPropertyStorage failed: 0x%08lx\n", hr);
|
||||
|
||||
hr = IPropertyStorage_WriteMultiple(propertyStorage, 0, NULL, NULL, 0);
|
||||
ok(SUCCEEDED(hr), "WriteMultiple with 0 args failed: 0x%08lx\n", hr);
|
||||
hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, NULL, NULL, 0);
|
||||
ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr);
|
||||
|
||||
/* test setting one that I can't set */
|
||||
spec.ulKind = PRSPEC_PROPID;
|
||||
U(spec).propid = PID_DICTIONARY;
|
||||
PropVariantClear(&var);
|
||||
var.vt = VT_I4;
|
||||
U(var).lVal = 1;
|
||||
hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
|
||||
ok(hr == STG_E_INVALIDPARAMETER,
|
||||
"Expected STG_E_INVALIDPARAMETER, got 0x%08lx\n", hr);
|
||||
|
||||
/* test setting one by name with an invalid propidNameFirst */
|
||||
spec.ulKind = PRSPEC_LPWSTR;
|
||||
U(spec).lpwstr = (LPOLESTR)propName;
|
||||
hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var,
|
||||
PID_DICTIONARY);
|
||||
ok(hr == STG_E_INVALIDPARAMETER,
|
||||
"Expected STG_E_INVALIDPARAMETER, got 0x%08lx\n", hr);
|
||||
|
||||
/* test setting behavior (case-sensitive) */
|
||||
spec.ulKind = PRSPEC_PROPID;
|
||||
U(spec).propid = PID_BEHAVIOR;
|
||||
U(var).lVal = 1;
|
||||
hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
|
||||
ok(hr == STG_E_INVALIDPARAMETER,
|
||||
"Expected STG_E_INVALIDPARAMETER, got 0x%08lx\n", hr);
|
||||
|
||||
/* set one by value.. */
|
||||
spec.ulKind = PRSPEC_PROPID;
|
||||
U(spec).propid = PID_FIRST_USABLE;
|
||||
U(var).lVal = 1;
|
||||
hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
|
||||
ok(SUCCEEDED(hr), "WriteMultiple failed: 0x%08lx\n", hr);
|
||||
|
||||
/* finally, set one by name */
|
||||
spec.ulKind = PRSPEC_LPWSTR;
|
||||
U(spec).lpwstr = (LPOLESTR)propName;
|
||||
U(var).lVal = 2;
|
||||
hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var,
|
||||
PID_FIRST_USABLE);
|
||||
ok(SUCCEEDED(hr), "WriteMultiple failed: 0x%08lx\n", hr);
|
||||
|
||||
/* check reading */
|
||||
hr = IPropertyStorage_ReadMultiple(propertyStorage, 0, NULL, NULL);
|
||||
ok(SUCCEEDED(hr), "ReadMultiple with 0 args failed: 0x%08lx\n", hr);
|
||||
hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, NULL, NULL);
|
||||
ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr);
|
||||
/* read by propid */
|
||||
spec.ulKind = PRSPEC_PROPID;
|
||||
U(spec).propid = PID_FIRST_USABLE;
|
||||
hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
|
||||
ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08lx\n", hr);
|
||||
ok(var.vt == VT_I4 && U(var).lVal == 1,
|
||||
"Didn't get expected type or value for property (got type %d, value %ld)\n",
|
||||
var.vt, U(var).lVal);
|
||||
/* read by name */
|
||||
spec.ulKind = PRSPEC_LPWSTR;
|
||||
U(spec).lpwstr = (LPOLESTR)propName;
|
||||
hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
|
||||
ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08lx\n", hr);
|
||||
ok(var.vt == VT_I4 && U(var).lVal == 2,
|
||||
"Didn't get expected type or value for property (got type %d, value %ld)\n",
|
||||
var.vt, U(var).lVal);
|
||||
|
||||
/* check deleting */
|
||||
hr = IPropertyStorage_DeleteMultiple(propertyStorage, 0, NULL);
|
||||
ok(SUCCEEDED(hr), "DeleteMultiple with 0 args failed: 0x%08lx\n", hr);
|
||||
hr = IPropertyStorage_DeleteMultiple(propertyStorage, 1, NULL);
|
||||
ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr);
|
||||
/* contrary to what the docs say, you can't delete the dictionary */
|
||||
spec.ulKind = PRSPEC_PROPID;
|
||||
U(spec).propid = PID_DICTIONARY;
|
||||
hr = IPropertyStorage_DeleteMultiple(propertyStorage, 1, &spec);
|
||||
ok(hr == STG_E_INVALIDPARAMETER,
|
||||
"Expected STG_E_INVALIDPARAMETER, got 0x%08lx\n", hr);
|
||||
/* now delete the first value.. */
|
||||
U(spec).propid = PID_FIRST_USABLE;
|
||||
hr = IPropertyStorage_DeleteMultiple(propertyStorage, 1, &spec);
|
||||
ok(SUCCEEDED(hr), "DeleteMultiple failed: 0x%08lx\n", hr);
|
||||
/* and check that it's no longer readable */
|
||||
hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
|
||||
ok(hr == S_FALSE, "Expected S_FALSE, got 0x%08lx\n", hr);
|
||||
|
||||
IPropertyStorage_Release(propertyStorage);
|
||||
IPropertySetStorage_Release(propSetStorage);
|
||||
IStorage_Release(storage);
|
||||
|
||||
DeleteFileW(filename);
|
||||
}
|
||||
|
||||
START_TEST(stg_prop)
|
||||
{
|
||||
testProps();
|
||||
}
|
Loading…
Reference in New Issue