Commit be84f8d9 authored by Juan Lang's avatar Juan Lang Committed by Alexandre Julliard

Begin implementing IPropertyStorage.

parent f7560908
......@@ -5,7 +5,7 @@ SRCDIR = @srcdir@
VPATH = @srcdir@
MODULE = ole32.dll
IMPORTS = advapi32 user32 gdi32 rpcrt4 kernel32 ntdll
EXTRALIBS = -luuid
EXTRALIBS = -luuid $(LIBUNICODE)
C_SRCS = \
antimoniker.c \
......@@ -15,6 +15,7 @@ C_SRCS = \
compositemoniker.c \
datacache.c \
defaulthandler.c \
dictionary.c \
errorinfo.c \
filemoniker.c \
ftmarshal.c \
......
/* Simple dictionary implementation using a linked list.
* FIXME: a skip list would be faster.
*
* 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 <assert.h>
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "dictionary.h"
struct dictionary_entry
{
void *key;
void *value;
struct dictionary_entry *next;
};
struct dictionary
{
comparefunc comp;
destroyfunc destroy;
void *extra;
struct dictionary_entry *head;
};
struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra)
{
struct dictionary *ret;
if (!c)
return NULL;
ret = HeapAlloc(GetProcessHeap(), 0, sizeof(struct dictionary));
if (ret)
{
ret->comp = c;
ret->destroy = d;
ret->extra = extra;
ret->head = NULL;
}
return ret;
}
void dictionary_destroy(struct dictionary *d)
{
if (d)
{
struct dictionary_entry *p;
for (p = d->head; p; )
{
struct dictionary_entry *next = p->next;
if (d->destroy)
d->destroy(p->key, p->value, d->extra);
HeapFree(GetProcessHeap(), 0, p);
p = next;
}
HeapFree(GetProcessHeap(), 0, d);
}
}
/* 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.)
* Assumes h and prev are not NULL.
*/
static struct dictionary_entry **dictionary_find_internal(struct dictionary *d,
const void *k)
{
struct dictionary_entry **ret = NULL;
struct dictionary_entry *p;
assert(d);
/* special case for head containing the desired element */
if (d->head && d->comp(k, d->head->key, d->extra) == 0)
ret = &d->head;
for (p = d->head; !ret && p && p->next; p = p->next)
{
if (d->comp(k, p->next->key, d->extra) == 0)
ret = &p->next;
}
return ret;
}
void dictionary_insert(struct dictionary *d, const void *k, const void *v)
{
struct dictionary_entry **prior;
if (!d)
return;
if ((prior = dictionary_find_internal(d, k)))
{
if (d->destroy)
d->destroy((*prior)->key, (*prior)->value, d->extra);
(*prior)->key = (void *)k;
(*prior)->value = (void *)v;
}
else
{
struct dictionary_entry *elem = (struct dictionary_entry *)
HeapAlloc(GetProcessHeap(), 0, sizeof(struct dictionary_entry));
if (!elem)
return;
elem->key = (void *)k;
elem->value = (void *)v;
elem->next = d->head;
d->head = elem;
}
}
BOOL dictionary_find(struct dictionary *d, const void *k, void **value)
{
struct dictionary_entry **prior;
BOOL ret = FALSE;
if (!d)
return FALSE;
if (!value)
return FALSE;
if ((prior = dictionary_find_internal(d, k)))
{
*value = (*prior)->value;
ret = TRUE;
}
return ret;
}
void dictionary_remove(struct dictionary *d, const void *k)
{
struct dictionary_entry **prior, *temp;
if (!d)
return;
if ((prior = dictionary_find_internal(d, k)))
{
temp = *prior;
if (d->destroy)
d->destroy((*prior)->key, (*prior)->value, d->extra);
*prior = (*prior)->next;
HeapFree(GetProcessHeap(), 0, temp);
}
}
void dictionary_enumerate(struct dictionary *d, enumeratefunc e)
{
struct dictionary_entry *p;
if (!d)
return;
if (!e)
return;
for (p = d->head; p; p = p->next)
e(p->key, p->value, d->extra);
}
/* Simple dictionary
*
* Copyright 2005 Juan Lang
*
* This is a pretty basic dictionary, or map if you prefer. It's not
* thread-safe.
*
* 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
*/
#ifndef __DICTIONARY_H__
#define __DICTIONARY_H__
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
struct dictionary;
/* Returns whether key a is less than, equal to, or greater than key b, in
* the same way (a - b) would for integers or strcmp(a, b) would for ANSI
* strings.
*/
typedef int (*comparefunc)(const void *a, const void *b, void *extra);
/* Called for every element removed from the dictionary. See
* dictionary_destroy, dictionary_insert, and dictionary_remove.
*/
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);
/* 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
* from the table, for example when dictionary_remove is called for a key, or
* when dictionary_destroy is called.
* extra is passed to c (and d, if it's provided).
* Assumes c is not NULL.
*/
struct dictionary *dictionary_create(comparefunc c, destroyfunc d, void *extra);
/* Assumes d is not NULL. */
void dictionary_destroy(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.
* Assumes k and v can be bitwise-copied.
* Both k and v are allowed to be NULL, in case you want to use integer
* values for either the key or the value.
* Assumes d is not NULL.
*/
void dictionary_insert(struct dictionary *d, const void *k, const void *v);
/* If a value with key k has been inserted into the dictionary, *v is set
* to its associated value. Returns FALSE if the key is not found, and TRUE
* if it is. *v is undefined if it returns FALSE. (It is not set to NULL,
* because this dictionary doesn't prevent you from using NULL as a value
* value; see dictionary_insert.)
* Assumes d and v are not NULL.
*/
BOOL dictionary_find(struct dictionary *d, const void *k, void **v);
/* Removes the element with key k from the dictionary. Calls the destroyfunc
* for the dictionary with the element if found (so you may destroy it if it's
* dynamically allocated.)
* Assumes d is not NULL.
*/
void dictionary_remove(struct dictionary *d, const void *k);
void dictionary_enumerate(struct dictionary *d, enumeratefunc e);
#endif /* ndef __DICTIONARY_H__ */
......@@ -9,6 +9,7 @@
* Copyright 1999 Sylvain St-Germain
* Copyright 1999 Thuy Nguyen
* Copyright 2005 Mike McCormack
* 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
......@@ -23,6 +24,24 @@
* 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
*
* There's a decent overview of property set storage here:
* http://msdn.microsoft.com/archive/en-us/dnarolegen/html/msdn_propset.asp
* It's a little bit out of date, and more definitive references are given
* below, but it gives the best "big picture" that I've found.
*
* TODO: There's a lot missing in here. Biggies:
* - There are all sorts of restricions I don't honor, like maximum property
* set byte size, maximum property name length
* - Certain bogus files could result in reading past the end of a buffer.
* - Write support is missing.
* - This will probably fail on big-endian machines, especially reading and
* writing strings.
* - Mac-generated files won't be read correctly, even if they're little
* endian, because I disregard whether the generator was a Mac. This means
* strings will probably be munged (as I don't understand Mac scripts.)
* - Not all PROPVARIANT types are supported.
* There are lots more unimplemented features, see FIXMEs below.
*/
#include <assert.h>
......@@ -41,7 +60,7 @@
#include "winuser.h"
#include "wine/unicode.h"
#include "wine/debug.h"
#include "dictionary.h"
#include "storage32.h"
WINE_DEFAULT_DEBUG_CHANNEL(storage);
......@@ -50,6 +69,54 @@ WINE_DEFAULT_DEBUG_CHANNEL(storage);
#define _ICOM_THIS_From_IPropertySetStorage(class, name) \
class* This = (class*)(((char*)name)-_IPropertySetStorage_Offset)
/* These are documented in MSDN, e.g.
* http://msdn.microsoft.com/library/en-us/stg/stg/property_set_header.asp
* http://msdn.microsoft.com/library/library/en-us/stg/stg/section.asp
* but they don't seem to be in any header file.
*/
#define PROPSETHDR_BYTEORDER_MAGIC 0xfffe
#define PROPSETHDR_OSVER_KIND_WIN16 0
#define PROPSETHDR_OSVER_KIND_MAC 1
#define PROPSETHDR_OSVER_KIND_WIN32 2
/* The format version (and what it implies) is described here:
* http://msdn.microsoft.com/library/en-us/stg/stg/format_version.asp
*/
typedef struct tagPROPERTYSETHEADER
{
WORD wByteOrder; /* always 0xfffe */
WORD wFormat; /* can be zero or one */
DWORD dwOSVer; /* OS version of originating system */
CLSID clsid; /* application CLSID */
DWORD reserved; /* always 1 */
} PROPERTYSETHEADER;
typedef struct tagFORMATIDOFFSET
{
FMTID fmtid;
DWORD dwOffset; /* from beginning of stream */
} FORMATIDOFFSET;
typedef struct tagPROPERTYSECTIONHEADER
{
DWORD cbSection;
DWORD cProperties;
} PROPERTYSECTIONHEADER;
typedef struct tagPROPERTYIDOFFSET
{
DWORD propid;
DWORD dwOffset;
} PROPERTYIDOFFSET;
/* Initializes the property storage from the stream (and undoes any uncommitted
* changes in the process.) Returns an error if there is an error reading or
* if the stream format doesn't match what's expected.
*/
static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface);
static HRESULT PropertyStorage_WriteToStream(IPropertyStorage *iface);
static IPropertyStorageVtbl IPropertyStorage_Vtbl;
/***********************************************************************
......@@ -59,7 +126,20 @@ typedef struct tagPropertyStorage_impl
{
IPropertyStorageVtbl *vtbl;
DWORD ref;
CRITICAL_SECTION cs;
IStream *stm;
BOOL dirty;
FMTID fmtid;
CLSID clsid;
DWORD originatorOS;
DWORD grfFlags;
DWORD grfMode;
UINT codePage;
LCID locale;
PROPID highestProp;
struct dictionary *name_to_propid;
struct dictionary *propid_to_name;
struct dictionary *propid_to_prop;
} PropertyStorage_impl;
/************************************************************************
......@@ -112,11 +192,50 @@ static ULONG WINAPI IPropertyStorage_fnRelease(
{
TRACE("Destroying %p\n", This);
IStream_Release(This->stm);
DeleteCriticalSection(&This->cs);
dictionary_destroy(This->name_to_propid);
dictionary_destroy(This->propid_to_name);
dictionary_destroy(This->propid_to_prop);
HeapFree(GetProcessHeap(), 0, This);
}
return ref;
}
static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
DWORD propid)
{
PROPVARIANT *ret = NULL;
if (!This)
return NULL;
dictionary_find(This->propid_to_prop, (void *)propid, (void **)&ret);
return ret;
}
static PROPVARIANT *PropertyStorage_FindPropertyByName(
PropertyStorage_impl *This, LPCWSTR name)
{
PROPVARIANT *ret = NULL;
PROPID propid;
if (!This || !name)
return NULL;
if (dictionary_find(This->name_to_propid, name, (void **)&propid))
ret = PropertyStorage_FindProperty(This, (PROPID)propid);
return ret;
}
static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
DWORD propid)
{
LPWSTR ret = NULL;
if (!This)
return NULL;
dictionary_find(This->propid_to_name, (void *)propid, (void **)&ret);
return ret;
}
/************************************************************************
* IPropertyStorage_fnReadMultiple (IPropertyStorage)
*/
......@@ -126,8 +245,64 @@ static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
const PROPSPEC rgpspec[],
PROPVARIANT rgpropvar[])
{
FIXME("\n");
return E_NOTIMPL;
PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
HRESULT hr = S_OK;
ULONG i;
TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
if (!This)
return E_INVALIDARG;
if (cpspec && (!rgpspec || !rgpropvar))
return E_INVALIDARG;
EnterCriticalSection(&This->cs);
for (i = 0; i < cpspec; i++)
{
PropVariantInit(&rgpropvar[i]);
if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
{
PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
rgpspec[i].u.lpwstr);
if (prop)
PropVariantCopy(&rgpropvar[i], prop);
}
else
{
PROPVARIANT *prop = PropertyStorage_FindProperty(This,
rgpspec[i].u.propid);
if (prop)
PropVariantCopy(&rgpropvar[i], prop);
}
}
LeaveCriticalSection(&This->cs);
return hr;
}
static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
PROPID propid, const PROPVARIANT *propvar)
{
HRESULT hr = S_OK;
PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
if (prop)
{
PropVariantClear(prop);
PropVariantCopy(prop, propvar);
}
else
{
prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(PROPVARIANT));
if (prop)
{
PropVariantCopy(prop, propvar);
dictionary_insert(This->propid_to_prop, (void *)propid, prop);
}
else
hr = STG_E_INSUFFICIENTMEMORY;
}
return hr;
}
/************************************************************************
......@@ -140,8 +315,65 @@ static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
const PROPVARIANT rgpropvar[],
PROPID propidNameFirst)
{
FIXME("\n");
return E_NOTIMPL;
PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
HRESULT hr = S_OK;
ULONG i;
TRACE("(%p, %ld, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
if (!This)
return E_INVALIDARG;
if (cpspec && (!rgpspec || !rgpropvar))
return E_INVALIDARG;
if (!(This->grfMode & STGM_READWRITE))
return STG_E_ACCESSDENIED;
EnterCriticalSection(&This->cs);
This->dirty = TRUE;
This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
PROPSETHDR_OSVER_KIND_WIN32) ;
for (i = 0; i < cpspec; i++)
{
if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
{
PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
rgpspec[i].u.lpwstr);
if (prop)
PropVariantCopy(prop, &rgpropvar[i]);
else
{
if (propidNameFirst < PID_FIRST_USABLE ||
propidNameFirst >= PID_MIN_READONLY)
hr = STG_E_INVALIDPARAMETER;
else
{
PROPID nextId = max(propidNameFirst, This->highestProp + 1);
size_t len = strlenW(rgpspec[i].u.lpwstr) + 1;
LPWSTR name = HeapAlloc(GetProcessHeap(), 0,
len * sizeof(WCHAR));
strcpyW(name, rgpspec[i].u.lpwstr);
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]);
}
}
}
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]);
}
}
LeaveCriticalSection(&This->cs);
return hr;
}
/************************************************************************
......@@ -152,8 +384,42 @@ static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
ULONG cpspec,
const PROPSPEC rgpspec[])
{
FIXME("\n");
return E_NOTIMPL;
PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
ULONG i;
HRESULT hr;
TRACE("(%p, %ld, %p)\n", iface, cpspec, rgpspec);
if (!This)
return E_INVALIDARG;
if (!rgpspec)
return E_INVALIDARG;
if (!(This->grfMode & STGM_READWRITE))
return STG_E_ACCESSDENIED;
hr = S_OK;
EnterCriticalSection(&This->cs);
for (i = 0; i < cpspec; i++)
{
if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
{
PROPID propid;
if (dictionary_find(This->name_to_propid,
(void *)rgpspec[i].u.lpwstr, (void **)&propid))
dictionary_remove(This->propid_to_prop, (void *)propid);
}
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.
*/
dictionary_remove(This->propid_to_prop,
(void *)rgpspec[i].u.propid);
}
}
LeaveCriticalSection(&This->cs);
return hr;
}
/************************************************************************
......@@ -165,8 +431,40 @@ static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
const PROPID rgpropid[],
LPOLESTR rglpwstrName[])
{
FIXME("\n");
return E_NOTIMPL;
PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
ULONG i;
HRESULT hr;
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++)
{
LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
if (name)
{
size_t len = lstrlenW(name);
hr = S_OK;
rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
if (rglpwstrName)
memcpy(rglpwstrName, name, (len + 1) * sizeof(WCHAR));
else
hr = STG_E_INSUFFICIENTMEMORY;
}
else
rglpwstrName[i] = NULL;
}
LeaveCriticalSection(&This->cs);
return hr;
}
/************************************************************************
......@@ -178,8 +476,33 @@ static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
const PROPID rgpropid[],
const LPOLESTR rglpwstrName[])
{
FIXME("\n");
return E_NOTIMPL;
PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
ULONG i;
HRESULT hr;
TRACE("(%p, %ld, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
if (!This)
return E_INVALIDARG;
if (cpropid && (!rgpropid || !rglpwstrName))
return E_INVALIDARG;
if (!(This->grfMode & STGM_READWRITE))
return STG_E_ACCESSDENIED;
hr = S_OK;
EnterCriticalSection(&This->cs);
for (i = 0; i < cpropid; i++)
{
if (rgpropid[i] != PID_ILLEGAL)
{
size_t len = lstrlenW(rglpwstrName[i] + 1);
LPWSTR name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
strcpyW(name, rglpwstrName[i]);
dictionary_insert(This->name_to_propid, name, (void *)rgpropid[i]);
dictionary_insert(This->propid_to_name, (void *)rgpropid[i], name);
}
}
LeaveCriticalSection(&This->cs);
return hr;
}
/************************************************************************
......@@ -190,8 +513,32 @@ static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
ULONG cpropid,
const PROPID rgpropid[])
{
FIXME("\n");
return E_NOTIMPL;
PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
ULONG i;
HRESULT hr;
TRACE("(%p, %ld, %p)\n", iface, cpropid, rgpropid);
if (!This)
return E_INVALIDARG;
if (cpropid && !rgpropid)
return E_INVALIDARG;
if (!(This->grfMode & STGM_READWRITE))
return STG_E_ACCESSDENIED;
hr = S_OK;
EnterCriticalSection(&This->cs);
for (i = 0; i < cpropid; i++)
{
LPWSTR name = NULL;
if (dictionary_find(This->propid_to_name, (void *)rgpropid[i],
(void **)&name))
{
dictionary_remove(This->propid_to_name, (void *)rgpropid[i]);
dictionary_remove(This->name_to_propid, name);
}
}
LeaveCriticalSection(&This->cs);
return hr;
}
/************************************************************************
......@@ -201,8 +548,21 @@ static HRESULT WINAPI IPropertyStorage_fnCommit(
IPropertyStorage* iface,
DWORD grfCommitFlags)
{
FIXME("\n");
return E_NOTIMPL;
PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
HRESULT hr;
TRACE("(%p, 0x%08lx)\n", iface, grfCommitFlags);
if (!This)
return E_INVALIDARG;
if (!(This->grfMode & STGM_READWRITE))
return STG_E_ACCESSDENIED;
EnterCriticalSection(&This->cs);
if (This->dirty)
hr = PropertyStorage_WriteToStream(iface);
else
hr = S_OK;
LeaveCriticalSection(&This->cs);
return hr;
}
/************************************************************************
......@@ -211,8 +571,20 @@ static HRESULT WINAPI IPropertyStorage_fnCommit(
static HRESULT WINAPI IPropertyStorage_fnRevert(
IPropertyStorage* iface)
{
FIXME("\n");
return E_NOTIMPL;
HRESULT hr;
PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
TRACE("%p\n", iface);
if (!This)
return E_INVALIDARG;
EnterCriticalSection(&This->cs);
if (This->dirty)
hr = PropertyStorage_ReadFromStream(iface);
else
hr = S_OK;
LeaveCriticalSection(&This->cs);
return hr;
}
/************************************************************************
......@@ -246,8 +618,16 @@ static HRESULT WINAPI IPropertyStorage_fnSetClass(
IPropertyStorage* iface,
REFCLSID clsid)
{
FIXME("\n");
return E_NOTIMPL;
PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
TRACE("%p, %s\n", iface, debugstr_guid(clsid));
if (!This || !clsid)
return E_INVALIDARG;
if (!(This->grfMode & STGM_READWRITE))
return STG_E_ACCESSDENIED;
memcpy(&This->clsid, clsid, sizeof(This->clsid));
This->dirty = TRUE;
return S_OK;
}
/************************************************************************
......@@ -257,30 +637,538 @@ static HRESULT WINAPI IPropertyStorage_fnStat(
IPropertyStorage* iface,
STATPROPSETSTG* statpsstg)
{
PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
STATSTG stat;
HRESULT hr;
TRACE("%p, %p\n", iface, statpsstg);
if (!This || !statpsstg)
return E_INVALIDARG;
hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
if (SUCCEEDED(hr))
{
memcpy(&statpsstg->fmtid, &This->fmtid, sizeof(statpsstg->fmtid));
memcpy(&statpsstg->clsid, &This->clsid, sizeof(statpsstg->clsid));
statpsstg->grfFlags = This->grfFlags;
memcpy(&statpsstg->mtime, &stat.mtime, sizeof(statpsstg->mtime));
memcpy(&statpsstg->ctime, &stat.ctime, sizeof(statpsstg->ctime));
memcpy(&statpsstg->atime, &stat.atime, sizeof(statpsstg->atime));
statpsstg->dwOSVersion = This->originatorOS;
}
return hr;
}
static int PropertyStorage_PropNameCompare(const void *a, const void *b,
void *extra)
{
PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
return strcmpW((LPCWSTR)a, (LPCWSTR)b);
else
return strcmpiW((LPCWSTR)a, (LPCWSTR)b);
}
static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
{
HeapFree(GetProcessHeap(), 0, k);
}
static int PropertyStorage_PropCompare(const void *a, const void *b,
void *extra)
{
return (PROPID)a - (PROPID)b;
}
static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
{
PropVariantClear((PROPVARIANT *)d);
HeapFree(GetProcessHeap(), 0, d);
}
/* Reads the dictionary from the memory buffer beginning at ptr. Interprets
* 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.
*/
static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
BYTE *ptr)
{
DWORD numEntries = *(DWORD *)ptr;
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;
for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
{
PROPID propid;
DWORD cbEntry;
int len;
StorageUtl_ReadDWord(ptr, 0, &propid);
ptr += sizeof(PROPID);
StorageUtl_ReadDWord(ptr, 0, &cbEntry);
ptr += sizeof(DWORD);
/* FIXME: if host is big-endian, this'll suck to convert */
len = MultiByteToWideChar(This->codePage, 0, ptr, cbEntry, NULL, 0);
if (!len)
hr = HRESULT_FROM_WIN32(GetLastError());
else
{
LPWSTR name = HeapAlloc(GetProcessHeap(), 0,
len * sizeof(WCHAR));
if (name)
{
MultiByteToWideChar(This->codePage, 0, ptr + sizeof(DWORD),
cbEntry, name, len);
dictionary_insert(This->name_to_propid, name,
(void *)propid);
dictionary_insert(This->propid_to_name, (void *)propid,
name);
TRACE("Property %s maps to id %ld\n", debugstr_w(name),
propid);
}
else
hr = STG_E_INSUFFICIENTMEMORY;
}
ptr += sizeof(DWORD) + cbEntry;
/* Unicode entries are padded to DWORD boundaries */
if (This->codePage == 1200 && cbEntry % sizeof(DWORD))
ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
}
}
else
{
/* one or the other failed, free the other */
if (This->name_to_propid)
{
dictionary_destroy(This->name_to_propid);
This->name_to_propid = NULL;
}
if (This->propid_to_name)
{
dictionary_destroy(This->propid_to_name);
This->propid_to_name = NULL;
}
hr = STG_E_INSUFFICIENTMEMORY;
}
return hr;
}
/* FIXME: there isn't any checking whether the read property extends past the
* end of the buffer.
*/
static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, DWORD type,
const BYTE *data)
{
HRESULT hr = S_OK;
prop->vt = VT_EMPTY;
switch (type)
{
case VT_NULL:
prop->vt = VT_NULL;
break;
case VT_I1:
prop->vt = VT_I1;
prop->u.cVal = *(const char *)data;
TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
break;
case VT_UI1:
prop->vt = VT_UI1;
prop->u.bVal = *(const UCHAR *)data;
TRACE("Read byte 0x%x\n", prop->u.bVal);
break;
case VT_I2:
prop->vt = VT_I2;
StorageUtl_ReadWord(data, 0, &prop->u.iVal);
TRACE("Read short %d\n", prop->u.iVal);
break;
case VT_UI2:
prop->vt = VT_UI2;
StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
TRACE("Read ushort %d\n", prop->u.uiVal);
break;
case VT_I4:
prop->vt = VT_I4;
StorageUtl_ReadDWord(data, 0, &prop->u.lVal);
TRACE("Read long %ld\n", prop->u.lVal);
break;
case VT_UI4:
prop->vt = VT_UI4;
StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
TRACE("Read ulong %ld\n", prop->u.ulVal);
break;
case VT_LPSTR:
{
DWORD count = *(const DWORD *)data;
prop->u.pszVal = CoTaskMemAlloc(count);
if (prop->u.pszVal)
{
/* FIXME: if the host is big-endian, this'll suck */
memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
prop->vt = VT_LPSTR;
/* FIXME: so far so good, but this may be Unicode or DBCS depending
* on This->codePage.
*/
TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
}
else
hr = STG_E_INSUFFICIENTMEMORY;
break;
}
case VT_LPWSTR:
{
DWORD count = *(DWORD *)data;
prop->u.pwszVal = CoTaskMemAlloc(count * sizeof(WCHAR));
if (prop->u.pwszVal)
{
/* FIXME: if the host is big-endian, gotta swap every char */
memcpy(prop->u.pwszVal, data + sizeof(DWORD),
count * sizeof(WCHAR));
prop->vt = VT_LPWSTR;
TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
}
else
hr = STG_E_INSUFFICIENTMEMORY;
break;
}
case VT_FILETIME:
/* FIXME: endianness */
prop->vt = VT_FILETIME;
memcpy(&prop->u.filetime, data, sizeof(FILETIME));
break;
default:
FIXME("unsupported type %ld\n", type);
hr = STG_E_INVALIDPARAMETER;
}
return hr;
}
static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
PROPERTYSETHEADER *hdr)
{
BYTE buf[sizeof(PROPERTYSETHEADER)];
ULONG count = 0;
HRESULT hr;
hr = IStream_Read(stm, buf, sizeof(buf), &count);
if (SUCCEEDED(hr))
{
if (count != sizeof(buf))
{
WARN("read %ld, expected %d\n", count, sizeof(buf));
hr = STG_E_INVALIDHEADER;
}
else
{
StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
&hdr->wByteOrder);
StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
&hdr->wFormat);
StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
&hdr->dwOSVer);
StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
&hdr->clsid);
StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
&hdr->reserved);
}
}
TRACE("returning 0x%08lx\n", hr);
return hr;
}
static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
FORMATIDOFFSET *fmt)
{
BYTE buf[sizeof(FORMATIDOFFSET)];
ULONG count = 0;
HRESULT hr;
hr = IStream_Read(stm, buf, sizeof(buf), &count);
if (SUCCEEDED(hr))
{
if (count != sizeof(buf))
{
WARN("read %ld, expected %d\n", count, sizeof(buf));
hr = STG_E_INVALIDHEADER;
}
else
{
StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
&fmt->fmtid);
StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
&fmt->dwOffset);
}
}
TRACE("returning 0x%08lx\n", hr);
return hr;
}
static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
PROPERTYSECTIONHEADER *hdr)
{
BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
ULONG count = 0;
HRESULT hr;
hr = IStream_Read(stm, buf, sizeof(buf), &count);
if (SUCCEEDED(hr))
{
if (count != sizeof(buf))
{
WARN("read %ld, expected %d\n", count, sizeof(buf));
hr = STG_E_INVALIDHEADER;
}
else
{
StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
cbSection), &hdr->cbSection);
StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
cProperties), &hdr->cProperties);
}
}
TRACE("returning 0x%08lx\n", hr);
return hr;
}
static HRESULT PropertyStorage_ReadFromStream(IPropertyStorage *iface)
{
PropertyStorage_impl *This = (PropertyStorage_impl *)iface;
PROPERTYSETHEADER hdr;
FORMATIDOFFSET fmtOffset;
PROPERTYSECTIONHEADER sectionHdr;
ULONG i;
STATSTG stat;
HRESULT hr;
BYTE *buf = NULL;
ULONG count = 0;
DWORD dictOffset = 0;
if (!This)
return E_INVALIDARG;
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;
hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
if (FAILED(hr))
goto end;
if (stat.cbSize.u.HighPart)
{
WARN("stream too big\n");
/* maximum size varies, but it can't be this big */
hr = STG_E_INVALIDHEADER;
goto end;
}
if (stat.cbSize.u.LowPart == 0)
{
/* empty stream is okay, we might be being called from Create */
hr = S_OK;
goto end;
}
else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
sizeof(FORMATIDOFFSET))
{
WARN("stream too small\n");
hr = STG_E_INVALIDHEADER;
goto end;
}
hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
/* I've only seen reserved == 1, but the article says I shouldn't disallow
* higher values.
*/
if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
{
WARN("bad magic in prop set header\n");
hr = STG_E_INVALIDHEADER;
goto end;
}
if (hdr.wFormat != 0 && hdr.wFormat != 1)
{
WARN("bad format version %d\n", hdr.wFormat);
hr = STG_E_INVALIDHEADER;
goto end;
}
memcpy(&This->clsid, &hdr.clsid, sizeof(This->clsid));
This->originatorOS = hdr.dwOSVer;
if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
WARN("File comes from a Mac, strings will probably be screwed up\n");
hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
if (FAILED(hr))
goto end;
if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
{
WARN("invalid offset %ld (stream length is %ld)\n", fmtOffset.dwOffset,
stat.cbSize.u.LowPart);
hr = STG_E_INVALIDHEADER;
goto end;
}
/* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
* follow not one, but two sections. The first is the standard properties
* for the document summary information, and the second is user-defined
* properties. This is the only case in which multiple sections are
* allowed.
* Reading the second stream isn't implemented yet.
*/
hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
if (FAILED(hr))
goto end;
/* The section size includes the section header, so check it */
if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
{
WARN("section header too small, got %ld, expected at least %d\n",
sectionHdr.cbSection, sizeof(PROPERTYSECTIONHEADER));
hr = STG_E_INVALIDHEADER;
goto end;
}
buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
sizeof(PROPERTYSECTIONHEADER));
if (!buf)
{
hr = STG_E_INSUFFICIENTMEMORY;
goto end;
}
hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
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 +
i * sizeof(PROPERTYIDOFFSET));
if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
idOffset->dwOffset >= sectionHdr.cbSection - sizeof(DWORD))
hr = STG_E_INVALIDPOINTER;
else
{
if (idOffset->propid >= PID_FIRST_USABLE &&
idOffset->propid < PID_MIN_READONLY && idOffset->propid >
This->highestProp)
This->highestProp = idOffset->propid;
if (idOffset->propid == 0)
{
/* Don't read the dictionary yet, its entries depend on the
* code page. Just store the offset so we know to read it
* later.
*/
dictOffset = idOffset->dwOffset;
}
else
{
DWORD type;
PROPVARIANT prop;
StorageUtl_ReadDWord(buf,
idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER), &type);
if (SUCCEEDED(PropertyStorage_ReadProperty(&prop, type,
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:
if (prop.vt == VT_I2)
This->codePage = (UINT)prop.u.iVal;
break;
case PID_LOCALE:
if (prop.vt == VT_I4)
This->locale = (LCID)prop.u.lVal;
break;
case PID_BEHAVIOR:
if (prop.vt == VT_I4 && prop.u.lVal)
This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
break;
default:
hr = PropertyStorage_StorePropWithId(This,
idOffset->propid, &prop);
}
}
}
}
}
if (!This->codePage)
{
/* default to Unicode unless told not to, as specified here:
* http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
*/
if (This->grfFlags & PROPSETFLAG_ANSI)
This->codePage = GetACP();
else
This->codePage = 1200;
}
if (!This->locale)
This->locale = LOCALE_SYSTEM_DEFAULT;
TRACE("Code page is %d, locale is %ld\n", This->codePage, This->locale);
if (dictOffset)
PropertyStorage_ReadDictionary(This,
buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
end:
HeapFree(GetProcessHeap(), 0, buf);
return hr;
}
static HRESULT PropertyStorage_WriteToStream(IPropertyStorage *iface)
{
FIXME("\n");
return E_NOTIMPL;
}
/***********************************************************************
* PropertyStorage_Contruct
* PropertyStorage_Construct
*/
static HRESULT PropertyStorage_Contruct(IStream *stm, IPropertyStorage** pps)
static HRESULT PropertyStorage_Construct(IStream *stm, REFFMTID rfmtid,
DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
{
PropertyStorage_impl *ps;
HRESULT hr;
ps = HeapAlloc(GetProcessHeap(), 0, sizeof *ps);
ps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *ps);
if (!ps)
return E_OUTOFMEMORY;
ps->vtbl = &IPropertyStorage_Vtbl;
ps->ref = 1;
InitializeCriticalSection(&ps->cs);
ps->stm = stm;
memcpy(&ps->fmtid, rfmtid, sizeof(ps->fmtid));
ps->grfFlags = grfFlags;
ps->grfMode = grfMode;
*pps = (IPropertyStorage*)ps;
TRACE("PropertyStorage %p constructed\n", ps);
return S_OK;
hr = PropertyStorage_ReadFromStream((IPropertyStorage *)ps);
if (SUCCEEDED(hr))
{
*pps = (IPropertyStorage *)ps;
TRACE("PropertyStorage %p constructed\n", ps);
hr = S_OK;
}
else
HeapFree(GetProcessHeap(), 0, ps);
return hr;
}
......@@ -288,15 +1176,68 @@ static HRESULT PropertyStorage_Contruct(IStream *stm, IPropertyStorage** pps)
* Implementation of IPropertySetStorage
*/
static LPCWSTR format_id_to_name(REFFMTID rfmtid)
#define BITS_PER_BYTE 8
#define CHARMASK 0x1f
#define BITS_IN_CHARMASK 5
/* Converts rfmtid to a string and returns the resulting string. If rfmtid
* is a well-known FMTID, it just returns a static string. Otherwise it
* creates the appropriate string name in str, which must be 27 characters
* in length, and returns str.
* Based on the algorithm described here:
* http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
*/
static LPCWSTR format_id_to_name(REFFMTID rfmtid, LPWSTR str)
{
static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
'I','n','f','o','r','m','a','t','i','o','n',0 };
static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',
0 };
LPCWSTR ret;
if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
return szSummaryInfo;
ERR("Unknown format id %s\n", debugstr_guid(rfmtid));
return NULL;
ret = szSummaryInfo;
else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
ret = szDocSummaryInfo;
else
{
BYTE *fmtptr;
WCHAR *pstr = str;
ULONG bitsRemaining = BITS_PER_BYTE;
*pstr++ = 5;
for (fmtptr = (BYTE *)rfmtid; fmtptr < (BYTE *)rfmtid + sizeof(FMTID); )
{
ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
if (bitsRemaining >= BITS_IN_CHARMASK)
{
*pstr = (WCHAR)(fmtMap[i & CHARMASK]);
if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
*pstr <= 'z')
*pstr += 'A' - 'a';
pstr++;
bitsRemaining -= BITS_IN_CHARMASK;
if (bitsRemaining == 0)
{
fmtptr++;
bitsRemaining = BITS_PER_BYTE;
}
}
else
{
if (++fmtptr < (BYTE *)rfmtid + sizeof(FMTID))
i |= *fmtptr << bitsRemaining;
*pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
}
}
*pstr = 0;
ret = str;
}
TRACE("returning %s\n", debugstr_w(ret));
return ret;
}
/************************************************************************
......@@ -349,28 +1290,48 @@ static HRESULT WINAPI IPropertySetStorage_fnCreate(
IPropertyStorage** ppprstg)
{
_ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
WCHAR nameBuf[27];
LPCWSTR name = NULL;
IStream *stm = NULL;
HRESULT r;
TRACE("%p %s %08lx %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
TRACE("%p %s %08lx %08lx %p\n", This, debugstr_guid(rfmtid), grfFlags,
grfMode, ppprstg);
/* be picky */
if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
return STG_E_INVALIDFLAG;
{
r = STG_E_INVALIDFLAG;
goto end;
}
if (!rfmtid)
return E_INVALIDARG;
{
r = E_INVALIDARG;
goto end;
}
name = format_id_to_name(rfmtid);
if (!name)
return STG_E_FILENOTFOUND;
/* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
* storage, not a stream. For now, disallow it.
*/
if (grfFlags & PROPSETFLAG_NONSIMPLE)
{
FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
r = STG_E_INVALIDFLAG;
goto end;
}
name = format_id_to_name(rfmtid, nameBuf);
r = IStorage_CreateStream( (IStorage*)This, name, grfMode, 0, 0, &stm );
if (FAILED(r))
return r;
goto end;
return PropertyStorage_Contruct(stm, ppprstg);
r = PropertyStorage_Construct(stm, rfmtid, grfFlags, grfMode, ppprstg);
end:
TRACE("returning 0x%08lx\n", r);
return r;
}
/************************************************************************
......@@ -384,6 +1345,7 @@ static HRESULT WINAPI IPropertySetStorage_fnOpen(
{
_ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
IStream *stm = NULL;
WCHAR nameBuf[27];
LPCWSTR name = NULL;
HRESULT r;
......@@ -392,20 +1354,29 @@ static HRESULT WINAPI IPropertySetStorage_fnOpen(
/* be picky */
if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
return STG_E_INVALIDFLAG;
{
r = STG_E_INVALIDFLAG;
goto end;
}
if (!rfmtid)
return E_INVALIDARG;
{
r = E_INVALIDARG;
goto end;
}
name = format_id_to_name(rfmtid);
if (!name)
return STG_E_FILENOTFOUND;
name = format_id_to_name(rfmtid, nameBuf);
r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm );
if (FAILED(r))
return r;
goto end;
r = PropertyStorage_Construct(stm, rfmtid, PROPSETFLAG_DEFAULT, grfMode,
ppprstg);
return PropertyStorage_Contruct(stm, ppprstg);
end:
TRACE("returning 0x%08lx\n", r);
return r;
}
/************************************************************************
......@@ -417,6 +1388,7 @@ static HRESULT WINAPI IPropertySetStorage_fnDelete(
{
_ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg);
IStorage *stg = NULL;
WCHAR nameBuf[27];
LPCWSTR name = NULL;
TRACE("%p %s\n", This, debugstr_guid(rfmtid));
......@@ -424,7 +1396,7 @@ static HRESULT WINAPI IPropertySetStorage_fnDelete(
if (!rfmtid)
return E_INVALIDARG;
name = format_id_to_name(rfmtid);
name = format_id_to_name(rfmtid, nameBuf);
if (!name)
return STG_E_FILENOTFOUND;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment