# IPropertyStorage::WriteMultiple

Writes a specified group of properties to the current property set. If a property with a specified name already exists, it is replaced, even when the old and new types for the property value are different. If a property of a given name or property identifier does not exist, it is created.

HRESULT WriteMultiple(

ULONG cpspec, | //The number of properties being set.
PROPSPEC const rgpspec[], | //Property specifiers
PROPVARIANT const rgvar[], | //Array of PROPVARIANT values
PROPID propidNameFirst | //Minimum value for property identifiers when they must be allocated
); |

Parameters

cpspec
  [in] The number of properties being set. May legally be zero, though this is a no-op, writing no properties.
rgpspec[]
  [in] Array of the specifiers to which properties are to be set. These are in no particular order, and may legally contain duplicates (the last specified is to take effect). A mixture of property identifiers and string names is permitted.
rgvar[]
  [in] An array (of size cpspec) of PROPVARIANTs that contain the property values to be written. The array must be of the size specified by cpspec.
propidNameFirst
  [in] Specifies the minimum value for the property identifiers the method must assign if the rgpspec parameter specifies string-named properties for which no property identifiers currently exist. If all string-named properties specified already exist in this set, and thus already have property identifiers, this value is ignored. When not ignored, this value must be at least two (property identifiers 0and 1 are reserved for special uses) and less than 0x80000000 (property identifier values beyond that are reserved for special use).
HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION)
  There was a failed attempt to translate a Unicode string to or from Ansi.

Return Values

This method supports the standard return value E_UNEXPECTED, as well as the following:

S_OK
  All of the indicated properties were successfully written.
STG_E_ACCESSDENIED
  The requested access to the property storage object has been denied. No properties have been written. The property set was opened in STGM_READ mode.
STG_E_INSUFFICIENTMEMORY
  There is not sufficient memory to perform this operation. Some properties may or may not have been written.
STG_E_INVALIDPARAMETER
  At least one of the parameters is invalid. Some properties may not have been written. This error would be returned in several situations, for example: 1) rgvar may be NULL; 2) a stream- or storage-valued property is present in rgpspec but the property set was created without PROPSETFLAG_NONSIMPLE; 3) one or more property variant types may be invalid; 4) one of the PROPSPECs contains an illegal ulKind value.
STG_E_INVALIDPOINTER
  May be returned when at least one of the pointers passed in is invalid. Some properties may or may not have been written. More frequently, an invalid pointer will instead result in an access violation.
STG_E_WRITEFAULT
  Error writing the storage.
STG_E_REVERTED
  The property set was reverted. For example, if the property set is deleted while open (by using IPropertySetStorage::Delete) this status would be returned.
STG_E_MEDIUMFULL
  The disk is full. Some properties may or may not have been written.
STG_E_PROPSETMISMATCHED
  An attempt was made to write a non-simple (stream- or storage-valued) property to a simple property set.

Remarks

If a specified property already exists, it’s value is replaced with the new one, even when the old and new types for the property value are different. If you specify a property identifier that does not exist, that property is created. If a string name is supplied for a property which does not exist, the method will allocate a property identifier for that property, and the name will be added to the dictionary.

When allocating a property identifier, the implementation can choose any value not currently in use in the property set for a property identifier, as long as it is not 0 or 1 or greater than 0x80000000, all of which are reserved values. The propidNameFirst parameter establishes a minimum value for property identifiers within the set, and must be greater than 1 and less than 0x80000000.

If there is an attempt to write a property that already exists with an invalid parameter, the method should return STG_E_INVALIDPARAMETER; if the property does not exist, it should not be created. This behavior facilitates the use of a ReadMultiple update WriteMultiple sequence to update a group of properties without requiring that the calling code ensure that all the requested properties in the call to ReadMultiple were retrieved.

It is recommended that property sets be created as Unicode, by not setting the PROPSETFLAG_ANSI flag in the grfFlags parameter of IPropertySetStorage::Create. It is also recommended that you avoid using VT_LPSTR values, and use VT_LPWSTR values instead. When the property set code page is Unicode, VT_LPSTR string values are converted to Unicode when stored, and back to multibyte string values when retrieved. When the code page of the property set is not Unicode, property names, VT_BSTR strings, and non-simple property values are converted to multibyte strings when stored, and converted back to Unicode when retrieved, all using the current system ANSI code page.

To create stream or storage object as a property in a nonsimple property set, call IPropertyStorage::WriteMultiple. While you would also call this method to update simple properties, it is not an efficient way to update stream and storage objects in a property set. This is because updating one of these properties through a call to WriteMultiple creates in the property storage object a copy of the passed-in data, and the IStorage or IStream pointers are not retained beyond the duration of this call. It is usually more efficient to update stream or storage objects by first calling IPropertyStorage::ReadMultiple to get the interface pointer to the stream or storage, then writing data through the IStream or IStorage methods.

A stream or storage opened through a property is always opened in direct mode, so an additional level of nested transaction is not introduced. There is still likely to be a transaction on the property set as a whole. Further, a property-based stream or storage is opened in read-write mode, if possible, given the mode on the property set; otherwise, read mode is used.

When the copy is made, the underlying CopyTo operation on VT_STREAM properties operates on the current seek position of the source. The seek position is destroyed on failure, but on success it is at EOF.

If a stream or storage property does not exist, passing an IStream or IStorage pointer with a value of NULL creates an empty stream or storage property value. If a stream or storage property is already open from a call to ReadMultiple, a NULL value must cause the WriteMultiple operation to truncate it and return S_OK, placing the previously returned stream- and storage-valued pointers into the reverted state (as happens in the compound file implementation.)

Storage- and stream-valued properties always manifest themselves to downlevel clients as sibling streams or storages to the stream containing the main contents of the property set&emdash;they are never stored directly in-line in the property set. This allows smooth interoperability and control when down-level clients interact with up-level clients. Thus, from a downlevel perspective, property sets containing IStream or IStorage valued properties are always stored in a storage object, not a stream. The specific name of the sibling used is completely under the control of the IPropertyStorage implementation, as long as the name is from the non-reserved part of the IStorage name space. See Appendix C of the OLE Programmer’s Guide for a discussion of the serialized property set format for further details. As is described there, the string name is stored in the same format as a VT_BSTR. Refer also to the earlier discussion in this method of multibyte to Unicode conversions for property names.

If the WriteMultiple method returns an error when writing stream- or storage-valued properties (indirect properties), the amount of data actually written is undefined. If the caller requires consistency of the property set and its indirect properties when writing stream- and/or storage-valued properties, use of transacted mode is advised.

If an implicit deletion of a stream- or storage-valued property occurs while that property is open, (as, for example, when a VT_I4 is written over a VT_STREAM), the deletion will succeed and place the previously returned IStream pointer in the reverted state.

See Also

IPropertySetStorage::Create, IPropertyStorage::ReadMultiple