| /* |
| * Compound Storage (32 bit version) |
| * Storage implementation |
| * |
| * This file contains the compound file implementation |
| * of the storage interface. |
| * |
| * Copyright 1999 Francis Beaudet |
| * 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 |
| * 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 |
| * |
| * 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> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #define COBJMACROS |
| #define NONAMELESSUNION |
| #define NONAMELESSSTRUCT |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winnls.h" |
| #include "winuser.h" |
| #include "wine/unicode.h" |
| #include "wine/debug.h" |
| #include "dictionary.h" |
| #include "storage32.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(storage); |
| |
| #define _IPropertySetStorage_Offset ((int)(&(((StorageImpl*)0)->base.pssVtbl))) |
| #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; |
| |
| /*********************************************************************** |
| * Implementation of IPropertyStorage |
| */ |
| 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; |
| |
| /************************************************************************ |
| * IPropertyStorage_fnQueryInterface (IPropertyStorage) |
| */ |
| static HRESULT WINAPI IPropertyStorage_fnQueryInterface( |
| IPropertyStorage *iface, |
| REFIID riid, |
| void** ppvObject) |
| { |
| PropertyStorage_impl *This = (PropertyStorage_impl *)iface; |
| |
| if ( (This==0) || (ppvObject==0) ) |
| return E_INVALIDARG; |
| |
| *ppvObject = 0; |
| |
| if (IsEqualGUID(&IID_IUnknown, riid) || |
| IsEqualGUID(&IID_IPropertyStorage, riid)) |
| { |
| IPropertyStorage_AddRef(iface); |
| *ppvObject = (IPropertyStorage*)iface; |
| return S_OK; |
| } |
| |
| return E_NOINTERFACE; |
| } |
| |
| /************************************************************************ |
| * IPropertyStorage_fnAddRef (IPropertyStorage) |
| */ |
| static ULONG WINAPI IPropertyStorage_fnAddRef( |
| IPropertyStorage *iface) |
| { |
| PropertyStorage_impl *This = (PropertyStorage_impl *)iface; |
| return InterlockedIncrement(&This->ref); |
| } |
| |
| /************************************************************************ |
| * IPropertyStorage_fnRelease (IPropertyStorage) |
| */ |
| static ULONG WINAPI IPropertyStorage_fnRelease( |
| IPropertyStorage *iface) |
| { |
| PropertyStorage_impl *This = (PropertyStorage_impl *)iface; |
| ULONG ref; |
| |
| ref = InterlockedDecrement(&This->ref); |
| if (ref == 0) |
| { |
| 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) |
| */ |
| static HRESULT WINAPI IPropertyStorage_fnReadMultiple( |
| IPropertyStorage* iface, |
| ULONG cpspec, |
| const PROPSPEC rgpspec[], |
| PROPVARIANT rgpropvar[]) |
| { |
| 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; |
| } |
| |
| /************************************************************************ |
| * IPropertyStorage_fnWriteMultiple (IPropertyStorage) |
| */ |
| static HRESULT WINAPI IPropertyStorage_fnWriteMultiple( |
| IPropertyStorage* iface, |
| ULONG cpspec, |
| const PROPSPEC rgpspec[], |
| const PROPVARIANT rgpropvar[], |
| PROPID propidNameFirst) |
| { |
| 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; |
| } |
| |
| /************************************************************************ |
| * IPropertyStorage_fnDeleteMultiple (IPropertyStorage) |
| */ |
| static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple( |
| IPropertyStorage* iface, |
| ULONG cpspec, |
| const PROPSPEC rgpspec[]) |
| { |
| 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; |
| } |
| |
| /************************************************************************ |
| * IPropertyStorage_fnReadPropertyNames (IPropertyStorage) |
| */ |
| static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames( |
| IPropertyStorage* iface, |
| ULONG cpropid, |
| const PROPID rgpropid[], |
| LPOLESTR rglpwstrName[]) |
| { |
| 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; |
| } |
| |
| /************************************************************************ |
| * IPropertyStorage_fnWritePropertyNames (IPropertyStorage) |
| */ |
| static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames( |
| IPropertyStorage* iface, |
| ULONG cpropid, |
| const PROPID rgpropid[], |
| const LPOLESTR rglpwstrName[]) |
| { |
| 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; |
| } |
| |
| /************************************************************************ |
| * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage) |
| */ |
| static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames( |
| IPropertyStorage* iface, |
| ULONG cpropid, |
| const PROPID rgpropid[]) |
| { |
| 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; |
| } |
| |
| /************************************************************************ |
| * IPropertyStorage_fnCommit (IPropertyStorage) |
| */ |
| static HRESULT WINAPI IPropertyStorage_fnCommit( |
| IPropertyStorage* iface, |
| DWORD grfCommitFlags) |
| { |
| 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; |
| } |
| |
| /************************************************************************ |
| * IPropertyStorage_fnRevert (IPropertyStorage) |
| */ |
| static HRESULT WINAPI IPropertyStorage_fnRevert( |
| IPropertyStorage* iface) |
| { |
| 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; |
| } |
| |
| /************************************************************************ |
| * IPropertyStorage_fnEnum (IPropertyStorage) |
| */ |
| static HRESULT WINAPI IPropertyStorage_fnEnum( |
| IPropertyStorage* iface, |
| IEnumSTATPROPSTG** ppenum) |
| { |
| FIXME("\n"); |
| return E_NOTIMPL; |
| } |
| |
| /************************************************************************ |
| * IPropertyStorage_fnSetTimes (IPropertyStorage) |
| */ |
| static HRESULT WINAPI IPropertyStorage_fnSetTimes( |
| IPropertyStorage* iface, |
| const FILETIME* pctime, |
| const FILETIME* patime, |
| const FILETIME* pmtime) |
| { |
| FIXME("\n"); |
| return E_NOTIMPL; |
| } |
| |
| /************************************************************************ |
| * IPropertyStorage_fnSetClass (IPropertyStorage) |
| */ |
| static HRESULT WINAPI IPropertyStorage_fnSetClass( |
| IPropertyStorage* iface, |
| REFCLSID clsid) |
| { |
| 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; |
| } |
| |
| /************************************************************************ |
| * IPropertyStorage_fnStat (IPropertyStorage) |
| */ |
| 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, §ionHdr); |
| 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_Construct |
| */ |
| static HRESULT PropertyStorage_Construct(IStream *stm, REFFMTID rfmtid, |
| DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps) |
| { |
| PropertyStorage_impl *ps; |
| HRESULT hr; |
| |
| 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; |
| |
| 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; |
| } |
| |
| |
| /*********************************************************************** |
| * Implementation of IPropertySetStorage |
| */ |
| |
| #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)) |
| 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; |
| } |
| |
| /************************************************************************ |
| * IPropertySetStorage_fnQueryInterface (IUnknown) |
| * |
| * This method forwards to the common QueryInterface implementation |
| */ |
| static HRESULT WINAPI IPropertySetStorage_fnQueryInterface( |
| IPropertySetStorage *ppstg, |
| REFIID riid, |
| void** ppvObject) |
| { |
| _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg); |
| return StorageBaseImpl_QueryInterface( (IStorage*)This, riid, ppvObject ); |
| } |
| |
| /************************************************************************ |
| * IPropertySetStorage_fnAddRef (IUnknown) |
| * |
| * This method forwards to the common AddRef implementation |
| */ |
| static ULONG WINAPI IPropertySetStorage_fnAddRef( |
| IPropertySetStorage *ppstg) |
| { |
| _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg); |
| return StorageBaseImpl_AddRef( (IStorage*)This ); |
| } |
| |
| /************************************************************************ |
| * IPropertySetStorage_fnRelease (IUnknown) |
| * |
| * This method forwards to the common Release implementation |
| */ |
| static ULONG WINAPI IPropertySetStorage_fnRelease( |
| IPropertySetStorage *ppstg) |
| { |
| _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg); |
| return StorageBaseImpl_Release( (IStorage*)This ); |
| } |
| |
| /************************************************************************ |
| * IPropertySetStorage_fnCreate (IPropertySetStorage) |
| */ |
| static HRESULT WINAPI IPropertySetStorage_fnCreate( |
| IPropertySetStorage *ppstg, |
| REFFMTID rfmtid, |
| const CLSID* pclsid, |
| DWORD grfFlags, |
| DWORD grfMode, |
| IPropertyStorage** ppprstg) |
| { |
| _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg); |
| WCHAR nameBuf[27]; |
| LPCWSTR name = NULL; |
| IStream *stm = NULL; |
| HRESULT r; |
| |
| 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)) |
| { |
| r = STG_E_INVALIDFLAG; |
| goto end; |
| } |
| |
| if (!rfmtid) |
| { |
| r = E_INVALIDARG; |
| goto end; |
| } |
| |
| /* 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)) |
| goto end; |
| |
| r = PropertyStorage_Construct(stm, rfmtid, grfFlags, grfMode, ppprstg); |
| |
| end: |
| TRACE("returning 0x%08lx\n", r); |
| return r; |
| } |
| |
| /************************************************************************ |
| * IPropertySetStorage_fnOpen (IPropertySetStorage) |
| */ |
| static HRESULT WINAPI IPropertySetStorage_fnOpen( |
| IPropertySetStorage *ppstg, |
| REFFMTID rfmtid, |
| DWORD grfMode, |
| IPropertyStorage** ppprstg) |
| { |
| _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg); |
| IStream *stm = NULL; |
| WCHAR nameBuf[27]; |
| LPCWSTR name = NULL; |
| HRESULT r; |
| |
| TRACE("%p %s %08lx %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg); |
| |
| /* be picky */ |
| if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) && |
| grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE)) |
| { |
| r = STG_E_INVALIDFLAG; |
| goto end; |
| } |
| |
| if (!rfmtid) |
| { |
| r = E_INVALIDARG; |
| goto end; |
| } |
| |
| name = format_id_to_name(rfmtid, nameBuf); |
| |
| r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm ); |
| if (FAILED(r)) |
| goto end; |
| |
| r = PropertyStorage_Construct(stm, rfmtid, PROPSETFLAG_DEFAULT, grfMode, |
| ppprstg); |
| |
| end: |
| TRACE("returning 0x%08lx\n", r); |
| return r; |
| } |
| |
| /************************************************************************ |
| * IPropertySetStorage_fnDelete (IPropertySetStorage) |
| */ |
| static HRESULT WINAPI IPropertySetStorage_fnDelete( |
| IPropertySetStorage *ppstg, |
| REFFMTID rfmtid) |
| { |
| _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg); |
| IStorage *stg = NULL; |
| WCHAR nameBuf[27]; |
| LPCWSTR name = NULL; |
| |
| TRACE("%p %s\n", This, debugstr_guid(rfmtid)); |
| |
| if (!rfmtid) |
| return E_INVALIDARG; |
| |
| name = format_id_to_name(rfmtid, nameBuf); |
| if (!name) |
| return STG_E_FILENOTFOUND; |
| |
| stg = (IStorage*) This; |
| return IStorage_DestroyElement(stg, name); |
| } |
| |
| /************************************************************************ |
| * IPropertySetStorage_fnEnum (IPropertySetStorage) |
| */ |
| static HRESULT WINAPI IPropertySetStorage_fnEnum( |
| IPropertySetStorage *ppstg, |
| IEnumSTATPROPSETSTG** ppenum) |
| { |
| _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg); |
| FIXME("%p\n", This); |
| return E_NOTIMPL; |
| } |
| |
| |
| /*********************************************************************** |
| * vtables |
| */ |
| IPropertySetStorageVtbl IPropertySetStorage_Vtbl = |
| { |
| IPropertySetStorage_fnQueryInterface, |
| IPropertySetStorage_fnAddRef, |
| IPropertySetStorage_fnRelease, |
| IPropertySetStorage_fnCreate, |
| IPropertySetStorage_fnOpen, |
| IPropertySetStorage_fnDelete, |
| IPropertySetStorage_fnEnum |
| }; |
| |
| static IPropertyStorageVtbl IPropertyStorage_Vtbl = |
| { |
| IPropertyStorage_fnQueryInterface, |
| IPropertyStorage_fnAddRef, |
| IPropertyStorage_fnRelease, |
| IPropertyStorage_fnReadMultiple, |
| IPropertyStorage_fnWriteMultiple, |
| IPropertyStorage_fnDeleteMultiple, |
| IPropertyStorage_fnReadPropertyNames, |
| IPropertyStorage_fnWritePropertyNames, |
| IPropertyStorage_fnDeletePropertyNames, |
| IPropertyStorage_fnCommit, |
| IPropertyStorage_fnRevert, |
| IPropertyStorage_fnEnum, |
| IPropertyStorage_fnSetTimes, |
| IPropertyStorage_fnSetClass, |
| IPropertyStorage_fnStat, |
| }; |