Add traces, add unit tests for IPropertyStorage, and fix the problems

they caught.
This commit is contained in:
Juan Lang 2005-04-11 14:30:24 +00:00 committed by Alexandre Julliard
parent 6d83105054
commit 50794ce787
6 changed files with 295 additions and 43 deletions

View File

@ -22,6 +22,9 @@
#include "windef.h" #include "windef.h"
#include "winbase.h" #include "winbase.h"
#include "dictionary.h" #include "dictionary.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(storage);
struct dictionary_entry struct dictionary_entry
{ {
@ -36,12 +39,14 @@ struct dictionary
destroyfunc destroy; destroyfunc destroy;
void *extra; void *extra;
struct dictionary_entry *head; struct dictionary_entry *head;
UINT num_entries;
}; };
struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra) struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra)
{ {
struct dictionary *ret; struct dictionary *ret;
TRACE("(%p, %p, %p)\n", c, d, extra);
if (!c) if (!c)
return NULL; return NULL;
ret = HeapAlloc(GetProcessHeap(), 0, sizeof(struct dictionary)); 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->destroy = d;
ret->extra = extra; ret->extra = extra;
ret->head = NULL; ret->head = NULL;
ret->num_entries = 0;
} }
TRACE("returning %p\n", ret);
return ret; return ret;
} }
void dictionary_destroy(struct dictionary *d) void dictionary_destroy(struct dictionary *d)
{ {
TRACE("(%p)\n", d);
if (d) if (d)
{ {
struct dictionary_entry *p; 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 /* 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 * 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.) * 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; struct dictionary_entry **prior;
TRACE("(%p, %p, %p)\n", d, k, v);
if (!d) if (!d)
return; return;
if ((prior = dictionary_find_internal(d, k))) 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->value = (void *)v;
elem->next = d->head; elem->next = d->head;
d->head = elem; 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; struct dictionary_entry **prior;
BOOL ret = FALSE; BOOL ret = FALSE;
TRACE("(%p, %p, %p)\n", d, k, value);
if (!d) if (!d)
return FALSE; return FALSE;
if (!value) if (!value)
@ -138,6 +154,7 @@ BOOL dictionary_find(struct dictionary *d, const void *k, void **value)
*value = (*prior)->value; *value = (*prior)->value;
ret = TRUE; ret = TRUE;
} }
TRACE("returning %d (%p)\n", ret, *value);
return ret; return ret;
} }
@ -145,6 +162,7 @@ void dictionary_remove(struct dictionary *d, const void *k)
{ {
struct dictionary_entry **prior, *temp; struct dictionary_entry **prior, *temp;
TRACE("(%p, %p)\n", d, k);
if (!d) if (!d)
return; return;
if ((prior = dictionary_find_internal(d, k))) 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); d->destroy((*prior)->key, (*prior)->value, d->extra);
*prior = (*prior)->next; *prior = (*prior)->next;
HeapFree(GetProcessHeap(), 0, temp); 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; struct dictionary_entry *p;
TRACE("(%p, %p, %p)\n", d, e, closure);
if (!d) if (!d)
return; return;
if (!e) if (!e)
return; return;
for (p = d->head; p; p = p->next) 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;
} }

View File

@ -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 /* Called for each element in the dictionary. Return FALSE if you don't want
* to enumerate any more. * 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. /* 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 * 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. */ /* Assumes d is not NULL. */
void dictionary_destroy(struct dictionary *d); 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 /* 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 * already exists with key k, its value is replaced, and the destroyfunc (if
* set) is called for the previous item. * 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_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__ */ #endif /* ndef __DICTIONARY_H__ */

View File

@ -79,6 +79,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(storage);
#define PROPSETHDR_OSVER_KIND_MAC 1 #define PROPSETHDR_OSVER_KIND_MAC 1
#define PROPSETHDR_OSVER_KIND_WIN32 2 #define PROPSETHDR_OSVER_KIND_WIN32 2
#define CP_UNICODE 1200
/* The format version (and what it implies) is described here: /* The format version (and what it implies) is described here:
* http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp * http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp
*/ */
@ -106,7 +108,7 @@ typedef struct tagPROPERTYSECTIONHEADER
typedef struct tagPROPERTYIDOFFSET typedef struct tagPROPERTYIDOFFSET
{ {
DWORD propid; DWORD propid;
DWORD dwOffset; DWORD dwOffset; /* from beginning of section */
} PROPERTYIDOFFSET; } PROPERTYIDOFFSET;
/* Initializes the property storage from the stream (and undoes any uncommitted /* Initializes the property storage from the stream (and undoes any uncommitted
@ -209,6 +211,7 @@ static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
if (!This) if (!This)
return NULL; return NULL;
dictionary_find(This->propid_to_prop, (void *)propid, (void **)&ret); dictionary_find(This->propid_to_prop, (void *)propid, (void **)&ret);
TRACE("returning %p\n", ret);
return ret; return ret;
} }
@ -222,6 +225,7 @@ static PROPVARIANT *PropertyStorage_FindPropertyByName(
return NULL; return NULL;
if (dictionary_find(This->name_to_propid, name, (void **)&propid)) if (dictionary_find(This->name_to_propid, name, (void **)&propid))
ret = PropertyStorage_FindProperty(This, (PROPID)propid); ret = PropertyStorage_FindProperty(This, (PROPID)propid);
TRACE("returning %p\n", ret);
return ret; return ret;
} }
@ -233,6 +237,7 @@ static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
if (!This) if (!This)
return NULL; return NULL;
dictionary_find(This->propid_to_name, (void *)propid, (void **)&ret); dictionary_find(This->propid_to_name, (void *)propid, (void **)&ret);
TRACE("returning %p\n", ret);
return ret; return ret;
} }
@ -246,7 +251,7 @@ static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
PROPVARIANT rgpropvar[]) PROPVARIANT rgpropvar[])
{ {
PropertyStorage_impl *This = (PropertyStorage_impl *)iface; PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
HRESULT hr = S_OK; HRESULT hr = S_FALSE;
ULONG i; ULONG i;
TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar); TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
@ -298,6 +303,8 @@ static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
{ {
PropVariantCopy(prop, propvar); PropVariantCopy(prop, propvar);
dictionary_insert(This->propid_to_prop, (void *)propid, prop); dictionary_insert(This->propid_to_prop, (void *)propid, prop);
if (propid > This->highestProp)
This->highestProp = propid;
} }
else else
hr = STG_E_INSUFFICIENTMEMORY; hr = STG_E_INSUFFICIENTMEMORY;
@ -341,6 +348,9 @@ static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
PropVariantCopy(prop, &rgpropvar[i]); PropVariantCopy(prop, &rgpropvar[i]);
else 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 || if (propidNameFirst < PID_FIRST_USABLE ||
propidNameFirst >= PID_MIN_READONLY) propidNameFirst >= PID_MIN_READONLY)
hr = STG_E_INVALIDPARAMETER; hr = STG_E_INVALIDPARAMETER;
@ -352,11 +362,12 @@ static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
len * sizeof(WCHAR)); len * sizeof(WCHAR));
strcpyW(name, rgpspec[i].u.lpwstr); 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, dictionary_insert(This->name_to_propid, name,
(void *)nextId); (void *)nextId);
dictionary_insert(This->propid_to_name, (void *)nextId, dictionary_insert(This->propid_to_name, (void *)nextId,
name); name);
This->highestProp = nextId;
hr = PropertyStorage_StorePropWithId(This, nextId, hr = PropertyStorage_StorePropWithId(This, nextId,
&rgpropvar[i]); &rgpropvar[i]);
} }
@ -364,12 +375,38 @@ static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
} }
else else
{ {
/* FIXME: certain propid's have special behavior. E.g., you can't switch (rgpspec[i].u.propid)
* set propid 0, and setting PID_BEHAVIOR affects the {
* case-sensitivity. case PID_DICTIONARY:
*/ /* Can't set the dictionary */
hr = PropertyStorage_StorePropWithId(This, rgpspec[i].u.propid, hr = STG_E_INVALIDPARAMETER;
&rgpropvar[i]); 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); LeaveCriticalSection(&This->cs);
@ -391,7 +428,7 @@ static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
TRACE("(%p, %ld, %p)\n", iface, cpspec, rgpspec); TRACE("(%p, %ld, %p)\n", iface, cpspec, rgpspec);
if (!This) if (!This)
return E_INVALIDARG; return E_INVALIDARG;
if (!rgpspec) if (cpspec && !rgpspec)
return E_INVALIDARG; return E_INVALIDARG;
if (!(This->grfMode & STGM_READWRITE)) if (!(This->grfMode & STGM_READWRITE))
return STG_E_ACCESSDENIED; return STG_E_ACCESSDENIED;
@ -409,13 +446,12 @@ static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
} }
else else
{ {
/* FIXME: certain propid's have special meaning. For example, if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
* removing propid 0 is supposed to remove the dictionary, and rgpspec[i].u.propid < PID_MIN_READONLY)
* removing PID_BEHAVIOR should change this to a case-insensitive
* property set. Unknown "read-only" propid's should be ignored.
*/
dictionary_remove(This->propid_to_prop, dictionary_remove(This->propid_to_prop,
(void *)rgpspec[i].u.propid); (void *)rgpspec[i].u.propid);
else
hr = STG_E_INVALIDPARAMETER;
} }
} }
LeaveCriticalSection(&This->cs); LeaveCriticalSection(&This->cs);
@ -433,17 +469,13 @@ static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
{ {
PropertyStorage_impl *This = (PropertyStorage_impl *)iface; PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
ULONG i; ULONG i;
HRESULT hr; HRESULT hr = S_FALSE;
TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName); TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
if (!This) if (!This)
return E_INVALIDARG; return E_INVALIDARG;
if (cpropid && (!rgpropid || !rglpwstrName)) if (cpropid && (!rgpropid || !rglpwstrName))
return E_INVALIDARG; 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); EnterCriticalSection(&This->cs);
for (i = 0; i < cpropid && SUCCEEDED(hr); i++) 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; 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) if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
return strcmpW((LPCWSTR)a, (LPCWSTR)b); return strcmpW((LPCWSTR)a, (LPCWSTR)b);
else 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, static int PropertyStorage_PropCompare(const void *a, const void *b,
void *extra) void *extra)
{ {
TRACE("(%ld, %ld)\n", (PROPID)a, (PROPID)b);
return (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. * 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 * FIXME: there isn't any checking whether the read property extends past the
* end of the buffer. * 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, static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
BYTE *ptr) BYTE *ptr)
@ -699,10 +739,6 @@ static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
HRESULT hr = S_OK; HRESULT hr = S_OK;
ptr += sizeof(DWORD); 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) if (This->name_to_propid && This->propid_to_name)
{ {
DWORD i; DWORD i;
@ -742,7 +778,7 @@ static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
} }
ptr += sizeof(DWORD) + cbEntry; ptr += sizeof(DWORD) + cbEntry;
/* Unicode entries are padded to DWORD boundaries */ /* 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)); ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
} }
} }
@ -863,6 +899,8 @@ static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
ULONG count = 0; ULONG count = 0;
HRESULT hr; HRESULT hr;
assert(stm);
assert(hdr);
hr = IStream_Read(stm, buf, sizeof(buf), &count); hr = IStream_Read(stm, buf, sizeof(buf), &count);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
@ -896,6 +934,8 @@ static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
ULONG count = 0; ULONG count = 0;
HRESULT hr; HRESULT hr;
assert(stm);
assert(fmt);
hr = IStream_Read(stm, buf, sizeof(buf), &count); hr = IStream_Read(stm, buf, sizeof(buf), &count);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
@ -923,6 +963,8 @@ static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
ULONG count = 0; ULONG count = 0;
HRESULT hr; HRESULT hr;
assert(stm);
assert(hdr);
hr = IStream_Read(stm, buf, sizeof(buf), &count); hr = IStream_Read(stm, buf, sizeof(buf), &count);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
@ -962,9 +1004,29 @@ static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
This->dirty = FALSE; This->dirty = FALSE;
This->highestProp = 0; This->highestProp = 0;
dictionary_destroy(This->name_to_propid); dictionary_destroy(This->name_to_propid);
This->name_to_propid = NULL;
dictionary_destroy(This->propid_to_name); 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); hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
if (FAILED(hr)) if (FAILED(hr))
goto end; goto end;
@ -1047,9 +1109,6 @@ static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
sizeof(PROPERTYSECTIONHEADER), &count); sizeof(PROPERTYSECTIONHEADER), &count);
if (FAILED(hr)) if (FAILED(hr))
goto end; 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++) for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
{ {
PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf + PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
@ -1064,7 +1123,7 @@ static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
idOffset->propid < PID_MIN_READONLY && idOffset->propid > idOffset->propid < PID_MIN_READONLY && idOffset->propid >
This->highestProp) This->highestProp)
This->highestProp = idOffset->propid; This->highestProp = idOffset->propid;
if (idOffset->propid == 0) if (idOffset->propid == PID_DICTIONARY)
{ {
/* Don't read the dictionary yet, its entries depend on the /* Don't read the dictionary yet, its entries depend on the
* code page. Just store the offset so we know to read it * 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) + buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER) +
sizeof(DWORD)))) 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) switch(idOffset->propid)
{ {
case PID_CODEPAGE: case PID_CODEPAGE:
@ -1117,7 +1172,7 @@ static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
if (This->grfFlags & PROPSETFLAG_ANSI) if (This->grfFlags & PROPSETFLAG_ANSI)
This->codePage = GetACP(); This->codePage = GetACP();
else else
This->codePage = 1200; This->codePage = CP_UNICODE;
} }
if (!This->locale) if (!This->locale)
This->locale = LOCALE_SYSTEM_DEFAULT; This->locale = LOCALE_SYSTEM_DEFAULT;
@ -1128,10 +1183,18 @@ static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
end: end:
HeapFree(GetProcessHeap(), 0, buf); 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; return hr;
} }
static HRESULT PropertyStorage_WriteToStream(IPropertyStorage *iface) static HRESULT PropertyStorage_WriteToStream(IPropertyStorage *iface)
{ {
FIXME("\n"); FIXME("\n");

View File

@ -2,5 +2,6 @@ Makefile
marshal.ok marshal.ok
moniker.ok moniker.ok
propvariant.ok propvariant.ok
stg_prop.ok
storage32.ok storage32.ok
testlist.c testlist.c

View File

@ -10,6 +10,7 @@ CTESTS = \
marshal.c \ marshal.c \
moniker.c \ moniker.c \
propvariant.c \ propvariant.c \
stg_prop.c \
storage32.c storage32.c
@MAKE_TEST_RULES@ @MAKE_TEST_RULES@

160
dlls/ole32/tests/stg_prop.c Normal file
View File

@ -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();
}