How to use propvariant
How to use propvariant
PropVariantChangeType function (propvarutil.h)
Coerces a value stored as a PROPVARIANT structure to an equivalent value of a different variant type.
Syntax
Parameters
A pointer to a PROPVARIANT structure that, when this function returns successfully, receives the coerced value and its new type.
A reference to the source PROPVARIANT structure that contains the value expressed as its original type.
Reserved, must be 0.
Specifies the new type for the value. See the tables below for recognized type names.
Return value
Returns S_OK if successful, or a standard COM error value otherwise. If the requested coercion is not possible, an error is returned.
Remarks
Note that the source and destination PROPVARIANT structures must be separate structures. You cannot overwrite the source PROPVARIANT data with the new destination data; attempting to do so will result in an error.
PropVariantChangeType converts values between the following types as follows. Numbers refer to conditions explained after the tables.
VT_LPWSTR | VT_BSTR | VT_BOOL | VT_FILETIME | VT_DATE | VT_CLSID | |
---|---|---|---|---|---|---|
VT_LPWSTR | Yes | Yes | Yes | Yes (2) | Yes (2) | Yes |
VT_BSTR | Yes | Yes | Yes | Yes (2) | Yes (2) | Yes |
VT_BOOL | Yes | Yes | Yes | No | No | No |
VT_I2 | Yes | Yes | Yes | No | No | No |
VT_I4 | Yes | Yes | Yes | No | No | No |
VT_I8 | Yes | Yes | Yes | No | No | No |
VT_UI2 | Yes | Yes | Yes | No | No | No |
VT_UI4 | Yes | Yes | Yes | No | No | No |
VT_UI8 | Yes | Yes | Yes | No | No | No |
VT_R8 | Yes (3) | Yes (3) | Yes | No | No | No |
VT_FILETIME | Yes (2) | Yes (2) | No | Yes | Yes | No |
VT_DATE | Yes (2) | Yes (2) | No | Yes | Yes | No |
VT_CLSID | Yes | Yes | No | No | No | Yes |
В
VT_I2 | VT_I4 | VT_I8 | VT_UI2 | VT_UI4 | VT_UI8 | VT_R8 | |
---|---|---|---|---|---|---|---|
VT_LPWSTR | Yes | Yes | Yes | Yes | Yes | Yes | Yes (3) |
VT_BSTR | Yes | Yes | Yes | Yes | Yes | Yes | Yes (3) |
VT_BOOL | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
VT_I2 | Yes | Yes | Yes | Yes (1) | Yes (1) | Yes (1) | Yes (1) |
VT_I4 | Yes (1) | Yes | Yes | Yes (1) | Yes (1) | Yes (1) | Yes (1) |
VT_I8 | Yes (1) | Yes (1) | Yes | Yes (1) | Yes (1) | Yes (1) | Yes (1) |
VT_UI2 | Yes (1) | Yes | Yes | Yes | Yes | Yes | Yes (1) |
VT_UI4 | Yes (1) | Yes (1) | Yes | Yes (1) | Yes | Yes | Yes (1) |
VT_UI8 | Yes (1) | Yes (1) | Yes (1) | Yes (1) | Yes (1) | Yes | Yes (1) |
VT_R8 | Yes (1) | Yes (1) | Yes (1) | Yes (1) | Yes (1) | Yes (1) | Yes |
VT_FILETIME | No | No | No | No | No | No | No |
VT_DATE | No | No | No | No | No | No | No |
VT_CLSID | No | No | No | No | No | No | No |
В
Conditions
Coercion between types is performed without respect to property-specific information. Property-specific coercions should be performed using PSCoerceToCanonicalValue. Additionally, if the string form of a value is needed for UI purposes, PSFormatForDisplay should be used to format the value according to locale- and property-specific information rather than using PropVariantChangeType to coerce the value to a string.
Examples
The following code example, to be included as part of a larger program, demonstrates how to use PropVariantChangeType to initialize a VT_FILETIME value from a string.
PROPVARIANT structure (propidlbase.h)
The PROPVARIANT structure is used in the ReadMultiple and WriteMultiple methods of IPropertyStorage to define the type tag and the value of a property in a property set.
The PROPVARIANT structure is also used by the GetValue and SetValue methods of IPropertyStore, which replaces IPropertySetStorage as the primary way to program item properties in WindowsВ Vista. For more information, see Property Handlers.
There are five members. The first member, the value-type tag, and the last member, the value of the property, are significant. The middle three members are reserved for future use.
Syntax
Members
Remarks
The PROPVARIANT structure can also hold a value of VT_DECIMAL:
However, the value of the DECIMAL structure requires special handling. The DECIMAL structure is the same size as an entire PROPVARIANT structure and does not fit into the union that holds all other types of values. Instead, the value of the DECIMAL structure occupies the entire PROPVARIANT structure, including the reserved fields and the vt member. However, the first member of the DECIMAL structure is not used and is equal in size to the vt member of the PROPVARIANT structure. Therefore, the PROPVARIANT structure declaration in the Propidl.h header file of Win32 defines the decVal member in such a way that it corresponds to the beginning of the PROPVARIANT structure. Therefore, to put the value of the DECIMAL structure into a PROPVARIANT structure, the value must be loaded into the decVal member and the vt member is set to VT_DECIMAL, just as for any other value.
PROPVARIANT is the fundamental data type by which property values are read and written through the IPropertyStorage interface.
The data type PROPVARIANT is related to the data type VARIANT, defined as part of Automation in OLE2. Several definitions are reused from Automation, as follows:
In addition, some types are unique to the PROPVARIANT structure:
Among the unique PROPVARIANT types are several data types that define counted arrays of other data types. The data types of all counted arrays begin with the letters CA, for example CAUB, and have an OR operator vt value (the VarType of the element and an OR operator with VT_VECTOR). The counted array structure has the following form (where name is the specific name of the counted array).
The only significant difference between VT_BLOB_OBJECT and VT_STREAMED_OBJECT is that the former does not have the system-level storage overhead that the latter would have, and is therefore more suitable for scenarios involving numbers of small objects.
For example, a type indicator of VT_LPSTR|VT_VECTOR has a DWORD element count, followed by a pointer to an array of LPSTR elements.
VT_VECTOR can be combined by an OR operator with the following types: VT_I1, VT_UI1, VT_I2, VT_UI2, VT_BOOL, VT_I4, VT_UI4, VT_R4, VT_R8, VT_ERROR, VT_I8, VT_UI8, VT_CY, VT_DATE, VT_FILETIME, VT_CLSID, VT_CF, VT_BSTR, VT_LPSTR, VT_LPWSTR, and VT_VARIANT. VT_VECTOR can also be combined by an OR operation with VT_BSTR_BLOB, however it is for system use only.
VT_BYREF can use OR with the following types: VT_I1, VT_UI1, VT_I2, VT_UI2, VT_I4, VT_UI4, VT_INT, VT_UINT, VT_R4, VT_R8, VT_BOOL, VT_DECIMAL, VT_ERROR, VT_CY, VT_DATE, VT_BSTR, VT_UNKNOWN, VT_DISPATCH, VT_ARRAY, and VT_VARIANT.
В
Clipboard format identifiers, stored with the tag VT_CF, use one of five representations, identified in the ulClipFmt member of the CLIPDATA structure using the pClipData pointer to the particular data type.
ulClipFmt value | pClipData value |
---|---|
-1L | A DWORD that contains a built-in Windows clipboard format value. |
-2L | A DWORD that contains a Macintosh clipboard format value. |
-3L | A GUID that contains a format identifier (FMTID). This is rarely used. |
any positive value | A null-terminated string that contains a Windows clipboard format name, one suitable for passing to the RegisterClipboardFormat function. This function registers a new clipboard format. If a registered format with the specified name already exists, a new format is not registered and the return value identifies the existing format. This enables more than one application to copy and paste data using the same registered clipboard format. The format name comparison is case insensitive and is identified by values in the range from 0xC000 through 0xFFFF. The code page used for characters in the string is according to the code-page indicator. The «positive value» here is the string length, including the null byte at the end. When register clipboard formats are placed on or retrieved from the clipboard, they must be in the form of an HGLOBAL data-type value, which provides the handle to the object. |
0L | No data (rarely used). |
В
After the METAFILEPICT structure is the metafile data, suitable to be passed to the SetMetaFileBitsEx function. This function creates a memory-based, Windows-format metafile from the supplied data. This function is provided for compatibility with 16-bit versions of Windows. Win32-based applications should use the SetEnhMetaFileBits function. This function retrieves the contents of the specified enhanced-format metafile and copies them into a buffer. If the function succeeds and the buffer pointer is NULL, the return value is the size of the enhanced metafile in bytes. If the function succeeds and the buffer pointer is a valid pointer, the return value is the number of bytes copied to the buffer. If the function fails, the return value is zero.
When register clipboard formats are placed on or retrieved from the clipboard, they must be in the form of an HGLOBAL value.
Initializing Property Handlers
This topic explains how to create and register property handlers to work with the Windows property system.
This topic is organized as follows:
Property Handlers
Property handlers are a crucial part of the property system. They are invoked in-process by the indexer to read and index property values, and are also invoked by Windows Explorer in-process to read and write property values directly in the files. These handlers need to be carefully written and tested to prevent degraded performance or the loss of data in the affected files. For more information on indexer-specific considerations that affect property handler implementation, see Developing Property Handlers for Windows Search.
Before You Begin
Property handlers are COM objects that create the IPropertyStore abstraction for a specific file format. They read (parse) and write this file format in a manner that conforms to its specification. Some property handlers do their work based on APIs that abstract access to a specific file format. Before you develop a property handler for your file format, you need to understand how your file format stores properties, and how those properties (names and values) are mapped into the property store abstraction.
When planning your implementation, remember that property handlers are low-level components that are loaded in the context of processes like Windows Explorer, the Windows Search indexer, and third-party applications that use the Shell item programming model. As a result, property handlers cannot be implemented in managed code, and should be implemented in C++. If your handler uses any APIs or services to do its work, you must ensure that those services can function properly in the environment(s) in which your property handler is loaded.
Property handlers are always associated with specific file types; thus, if your file format contains properties that require a custom property handler, you should always register a unique file name extension for each file format.
Initializing Property Handlers
Before a property is used by the system, it is initialized by calling an implementation of IInitializeWithStream. The property handler should be initialized by having the system assign it a stream rather than leaving that assignment to the handler implementation. This method of initialization ensures the following things:
There are cases where initialization with streams is not possible. In those situations, there are two further interfaces that property handlers can implement: IInitializeWithFile and IInitializeWithItem. If a property handler does not implement the IInitializeWithStream, it must opt out of running in the isolated process into which the system indexer would place it by default if there were a change to the stream. To opt out of this feature, set the following registry value.
However, it is far better to implement IInitializeWithStream and do a stream-based initialization. Your property handler will be safer and more reliable as a result. Disabling process isolation is generally intended only for legacy property handlers and should be strenuously avoided by any new code.
To examine the implementation of a property handler in detail, look at the following code example, which is an implementation of IInitializeWithStream::Initialize. The handler is initialized by loading an XML-based recipe document through a pointer to that document’s associated IStream instance. The _spDocEle variable used near the end of the code example is defined earlier in the sample as an MSXML2::IXMLDOMElementPtr.
After the document itself is loaded, the properties to be displayed in Windows Explorer are loaded by calling the protected _LoadProperties method, as illustrated in the following code example. This process is examined in detail in the next section.
If the stream is read-only but the grfMode parameter contains the STGM_READWRITE flag, the initialization should fail and return STG_E_ACCESSDENIED. Without this check, Windows Explorer shows the property values as writable even though they are not, leading to a confusing end-user experience.
In-Memory Property Store
Before looking at the implementation of _LoadProperties, you should understand the PropertyMap array used in the sample to map properties in the XML document to existing properties in the property system through their PKEY values.
You should not expose every element and attribute in the XML file as a property. Instead, select only those that you believe will be useful to end users in the organization of their documents (in this case, recipes). This is an important concept to keep in mind as you develop your property handlers: the difference between information that is truly useful for organizational scenarios, and information that belongs to the details of your file and can be seen by opening the file itself. Properties are not intended to be a full duplication of an XML file.
Here is the full implementation of the _LoadProperties method called by IInitializeWithStream::Initialize.
The _LoadProperties method calls the Shell helper function PSCreateMemoryPropertyStore to create an in-memory property store (cache) for the handled properties. By using a cache, changes are tracked for you. This frees you from tracking whether a property value has been changed in the cache but not yet saved to persisted storage. It also frees you from needlessly persisting property values that have not changed.
The _LoadProperties method also calls _LoadProperty whose implementation is illustrated in the following code) once for each mapped property. _LoadProperty gets the value of the property as specified in the PropertyMap element in the XML stream and assigns it to the in-memory cache through a call to IPropertyStoreCache::SetValueAndState. The PSC_NORMAL flag in the call to IPropertyStoreCache::SetValueAndState indicates that the property value has not been altered since the time it entered the cache.
Dealing with PROPVARIANT-Based Values
In the implementation of _LoadProperty, a property value is provided in the form of a PROPVARIANT. A set of APIs in the software development kit (SDK) is provided to convert from primitive types such as PWSTR or int to or from PROPVARIANT types. These APIs are found in Propvarutil.h.
For example, to convert a PROPVARIANT to a string, you can use PropVariantToString as illustrated here.
To initialize a PROPVARIANT from a string, you can use InitPropVariantFromString.
As you can see in any of the recipe files included in the sample, there can be more than one keyword in each file. To account for this, the property system supports multi-valued strings represented as a vector of strings (for instance «VT_VECTOR | VT_LPWSTR»). The _LoadVectorProperty method in the example uses vector-based values.
If a value does not exist in the file, do not return an error. Instead, set the value to VT_EMPTY and return S_OK. VT_EMPTY indicates that the property value does not exist.
Supporting Open Metadata
This example uses an XML-based file format. Its schema can be extended to support properties that were not thought of during developmet, for example. This system is known as open metadata. This example extends the property system by creating a node under the Recipe element called ExtendedProperties, as illustrated in the following code example.
To load persisted extended properties during initialization, implement the _LoadExtendedProperties method, as illustrated in the following code example.
Serialization APIs declared in Propsys.h are used to serialize and deserialize PROPVARIANT types into blobs of data, and then Base64 encoding is used to serialize those blobs into strings that can be saved in the XML. These strings are stored in the EncodedValue attribute of the ExtendedProperties element. The following utility method, implemented in the sample’s Util.cpp file, performs the serialization. It begins with a call to the StgSerializePropVariant function to perform the binary serialization, as illustrated in the following code example.
Next, the CryptBinaryToString function, declared in Wincrypt.h, performs the Base64 conversion.
The DeserializePropVariantFromString function, also found in Util.cpp, reverses the operation, deserializing values from the XML file.
For information about support for open metadata, see «File Types that Support Open Metadata» in File Types.
Full-Text Contents
Property handlers can also facilitate a full-text search of the file contents, and they are an easy way to provide that functionality if the file format is not overly complicated. There is an alternative, more powerful way to provide the full text of the file through the IFilter interface implementation.
The following table summarizes the benefits of each approach using either IFilter or IPropertyStore.
Capability | IFilter | IPropertyStore |
---|---|---|
Allows write back to files? | No | Yes |
Provides mix of content and properties? | Yes | Yes |
Multilingual? | Yes | No |
MIME/Embedded? | Yes | No |
Text boundaries? | Sentence, paragraph, chapter | None |
Implementation supported for SPS/SQL Server? | Yes | No |
Implementation | Complex | Simple |
In the recipe handler sample, the recipe file format does not have any complex requirements, so only IPropertyStore has been implemented for full-text support. Full-text search is implemented for the XML nodes named in the following array.
The property system contains the System.Search.Contents (PKEY_Search_Contents) property, which was created to provide full-text content to the indexer. This property’s value is never displayed directly in the UI; the text from all of the XML nodes named in the array above are concatenated into a single string. That string is then provided to the indexer as the full-text content of the recipe file through a call to IPropertyStoreCache::SetValueAndState as illustrated in the following code example.
Providing Values for Properties
When used to read values, property handlers are usually invoked for one of the following reasons:
For enumeration, a property handler is asked to enumerate its properties either during indexing or when the properties dialog box asks for properties to display in the Other group. Indexing goes on constantly as a background operation. Whenever a file changes, the indexer is notified, and it re-indexes the file by asking the property handler to enumerate its properties. Therefore, it is critical that property handlers are implemented efficiently and return property values as quickly as possible. Enumerate all the properties for which you have values, just as you would for any collection, but do not enumerate properties that involve memory-intensive calculations or network requests that could make them slow to retrieve.
When writing a property handler, you usually need to consider the following two sets of properties.
Because the sample uses in-memory cache, implementing IPropertyStore methods is just a matter of delegating to that cache, as illustrated in the following code example.
If you choose not to delegate to the in-memory cache, you must implement your methods to provide> the following expected behavior:
Writing Back Values
When the property handler writes the value of a property using IPropertyStore::SetValue, it does not write the value to the file until IPropertyStore::Commit is called. The in-memory cache can be useful in implementing this scheme. In the sample code the IPropertyStore::SetValue implementation simply sets the new value in the in-memory cache and sets the state of that property to PSC_DIRTY.
One major advantage of using streams, as the sample, is reliability. Property handlers must always consider that they cannot leave a file in an inconsistent state in the case of a catastrophic failure. Corrupting a user’s files obviously should be avoided, and the best way to do this is with a «copy-on-write» mechanism. If your property handler uses a stream to access a file, you get this behavior automatically; the system writes any changes to the stream, replacing the file with the new copy only during the commit operation.
To override this behavior and control the file saving process manually, you can opt out of the safe save behavior by setting the ManualSafeSave value in your handler’s registry entry as illustrated here.
When a handler specifies the ManualSafeSave value, the stream with which it is initialized is not a transacted stream (STGM_TRANSACTED). The handler itself must implement the safe save function to ensure that the file is not corrupted if the save operation is interrupted. If the handler implements in-place writing, it will write to the stream that it is given. If the handler does not support this feature, then it must retrieve a stream with which to write the updated copy of the file using IDestinationStreamFactory::GetDestinationStream. After the handler is done writing, it should call IPropertyStore::Commit on the original stream to complete the operation, and replace the contents of the original stream with the new copy of the file.
ManualSafeSave is also the default situation if you do not initialize your handler with a stream. Without an original stream to receive the contents of the temporary stream, you must use ReplaceFile to perform an atomic replacement of the source file.
Large file formats that will be used in a way that produces files greater than 1 MB should implement support for in-place property writing; otherwise, the performance behavior does not meet the expectations of clients of the property system. In this scenario, the time required to write properties should not be affected by the file size.
For very large files, for example a video file of 1 GB or more, a different solution is required. If there is not enough space in the file to perform in-place writing, the handler may fail the property update if the amount of space reserved for in-place property writing has been exhausted. This failure occurs to avoid poor performance arising from 2 GB of IO (1 to read, 1 to write). Because of this potential failure, these file formats should reserve enough space for in-place property writing.
If the file has enough space in its header to write metadata, and if the writing of that metadata does not cause the file to grow or shrink, it might be safe to write in-place. We recommend 64 KB as a starting point. Writing in-place is equivalent to the handler asking for ManualSafeSave and calling IStream::Commit in the implementation of IPropertyStore::Commit, and has much better performance than copy-on-write. If the file size changes due to property value changes, writing in-place should not be attempted because of the potential for a corrupted file in the event of an abnormal termination.
For performance reasons we recommend that the ManualSafeSave option be used with property handlers working with files that are 100 KB or larger.
As illustrated in the following sample implementation of IPropertyStore::Commit, the handler for ManualSafeSave is registered to illustrate the manual safe save option. The _SaveCacheToDom method writes the property values stored in the in-memory cache to the XMLdocument object.
Next, commit the original stream, which writes the data back to the original file in a safe manner.
Then examine the _SaveCacheToDom implementation.
Next, get the number of properties stored in the in-memory cache.
Now iterate through the properties to determine whether the value of a property has been changed since it was loaded into memory.
The IPropertyStoreCache::GetState method gets the state of the property in the cache. The PSC_DIRTY flag, which was set in the IPropertyStore::SetValue implementation, marks a property as changed.
Map the property to the XML node as specified in the eg_rgPropertyMap array.
If a property is not in the map, it is a new property that was set by Windows Explorer. Because open metadata is supported, save the new property in the ExtendedProperties section of the XML.
Implementing IPropertyStoreCapabilities
IPropertyStoreCapabilities informs the Shell UI whether a particular property can be edited in the Shell UI. It is important to note that this relates only to the ability to edit the property in the UI, not whether you can successfully call IPropertyStore::SetValue on the property. A property that provokes a return value of S_FALSE from IPropertyStoreCapabilities::IsPropertyWritable might still be capable of being set through an application.
IsPropertyWritable returns S_OK to indicate that end users should be allowed to edit the property directly; S_FALSE indicates that they should not. S_FALSE can mean that applications are responsible for writing the property, not users. The Shell disables editing controls as appropriate based on the results of calls to this method. A handler that does not implement IPropertyStoreCapabilities is assumed to support open metadata through support for the writing of any property.
If you are building a handler that handles only read-only properties, then you should implement your Initialize method (IInitializeWithStream, IInitializeWithItem, or IInitializeWithFile) so that it returns STG_E_ACCESSDENIED when called with the STGM_READWRITE flag.
Some properties have their isInnate attribute set to true. Innate properties have the following characteristics:
Due to these characteristics, properties marked as IsInnate are provided to the user in the Shell UI only as read-only properties. If a property is marked as IsInnate, the property system does not store that property in the property handler. Therefore, property handlers do not need special code to account for these properties in their implementations. If the value of the IsInnate attribute is not explicitly stated for a particular property, the default value is false.
Registering and Distributing Property Handlers
With the property handler implemented, it must be registered and its file name extension associated with the handler. For more information, see Registering and Distributing Property Handlers.
PROPVARIANT structure (propidl.h)
The PROPVARIANT structure is used in the ReadMultiple and WriteMultiple methods of IPropertyStorage to define the type tag and the value of a property in a property set.
The PROPVARIANT structure is also used by the GetValue and SetValue methods of IPropertyStore, which replaces IPropertySetStorage as the primary way to program item properties in WindowsВ Vista. For more information, see Property Handlers.
There are five members. The first member, the value-type tag, and the last member, the value of the property, are significant. The middle three members are reserved for future use.
Syntax
Members
Reserved for future use.
Reserved for future use.
Reserved for future use.
VT_I1, Version 1
VT_INT, Version 1
VT_UINT, Version 1
VT_DISPATCH, Version 1
VT_ARRAY | VT_*, Version 1
VT_VECTOR | VT_I1, Version 1
VT_BYREF | VT_I1, Version 1
VT_BYREF | VT_UI1, Version 1
VT_BYREF | VT_I2, Version 1
VT_BYREF | VT_UI2, Version 1
VT_BYREF | VT_I4, Version 1
VT_BYREF | VT_UI4, Version 1
VT_BYREF | VT_INT, Version 1
VT_BYREF | VT_UINT, Version 1
VT_BYREF | VT_R4, Version 1
VT_BYREF | VT_R8, Version 1
VT_BYREF | VT_R8, Version 1
VT_BYREF | VT_DECIMAL, Version 1
VT_BYREF | VT_ERROR, Version 1
VT_BYREF | VT_CY, Version 1
VT_BYREF | VT_DATE, Version 1
VT_BYREF | VT_BSTR, Version 1
VT_BYREF | VT_UNKNOWN, Version 1
VT_BYREF | VT_DISPATCH, Version 1
VT_BYREF | VT_DISPATCH, Version 1
VT_BYREF | VT_VARIANT, Version 1
VT_BYREF | VT_DECIMAL, Version 1
Remarks
The PROPVARIANT structure can also hold a value of VT_DECIMAL:
However, the value of the DECIMAL structure requires special handling. The DECIMAL structure is the same size as an entire PROPVARIANT structure and does not fit into the union that holds all other types of values. Instead, the value of the DECIMAL structure occupies the entire PROPVARIANT structure, including the reserved fields and the vt member. However, the first member of the DECIMAL structure is not used and is equal in size to the vt member of the PROPVARIANT structure. Therefore, the PROPVARIANT structure declaration in the Propidl.h header file of Win32 defines the decVal member in such a way that it corresponds to the beginning of the PROPVARIANT structure. Therefore, to put the value of the DECIMAL structure into a PROPVARIANT structure, the value must be loaded into the decVal member and the vt member is set to VT_DECIMAL, just as for any other value.
PROPVARIANT is the fundamental data type by which property values are read and written through the IPropertyStorage interface.
The data type PROPVARIANT is related to the data type VARIANT, defined as part of Automation in OLE2. Several definitions are reused from Automation, as follows:
In addition, some types are unique to the PROPVARIANT structure:
Among the unique PROPVARIANT types are several data types that define counted arrays of other data types. The data types of all counted arrays begin with the letters CA, for example CAUB, and have an OR operator vt value (the VarType of the element and an OR operator with VT_VECTOR). The counted array structure has the following form (where name is the specific name of the counted array).
The only significant difference between VT_BLOB_OBJECT and VT_STREAMED_OBJECT is that the former does not have the system-level storage overhead that the latter would have, and is therefore more suitable for scenarios involving numbers of small objects.
For example, a type indicator of VT_LPSTR|VT_VECTOR has a DWORD element count, followed by a pointer to an array of LPSTR elements.
VT_VECTOR can be combined by an OR operator with the following types: VT_I1, VT_UI1, VT_I2, VT_UI2, VT_BOOL, VT_I4, VT_UI4, VT_R4, VT_R8, VT_ERROR, VT_I8, VT_UI8, VT_CY, VT_DATE, VT_FILETIME, VT_CLSID, VT_CF, VT_BSTR, VT_LPSTR, VT_LPWSTR, and VT_VARIANT. VT_VECTOR can also be combined by an OR operation with VT_BSTR_BLOB, however it is for system use only.
VT_BYREF can use OR with the following types: VT_I1, VT_UI1, VT_I2, VT_UI2, VT_I4, VT_UI4, VT_INT, VT_UINT, VT_R4, VT_R8, VT_BOOL, VT_DECIMAL, VT_ERROR, VT_CY, VT_DATE, VT_BSTR, VT_UNKNOWN, VT_DISPATCH, VT_ARRAY, and VT_VARIANT.
В
Clipboard format identifiers, stored with the tag VT_CF, use one of five representations, identified in the ulClipFmt member of the CLIPDATA structure using the pClipData pointer to the particular data type.
ulClipFmt value | pClipData value |
---|---|
-1L | A DWORD that contains a built-in Windows clipboard format value. |
-2L | A DWORD that contains a Macintosh clipboard format value. |
-3L | A GUID that contains a format identifier (FMTID). This is rarely used. |
any positive value | A null-terminated string that contains a Windows clipboard format name, one suitable for passing to the RegisterClipboardFormat function. This function registers a new clipboard format. If a registered format with the specified name already exists, a new format is not registered and the return value identifies the existing format. This enables more than one application to copy and paste data using the same registered clipboard format. The format name comparison is case insensitive and is identified by values in the range from 0xC000 through 0xFFFF. The code page used for characters in the string is according to the code-page indicator. The «positive value» here is the string length, including the null byte at the end. When register clipboard formats are placed on or retrieved from the clipboard, they must be in the form of an HGLOBAL data-type value, which provides the handle to the object. |
0L | No data (rarely used). |
В
After the METAFILEPICT structure is the metafile data, suitable to be passed to the SetMetaFileBitsEx function. This function creates a memory-based, Windows-format metafile from the supplied data. This function is provided for compatibility with 16-bit versions of Windows. Win32-based applications should use the SetEnhMetaFileBits function. This function retrieves the contents of the specified enhanced-format metafile and copies them into a buffer. If the function succeeds and the buffer pointer is NULL, the return value is the size of the enhanced metafile in bytes. If the function succeeds and the buffer pointer is a valid pointer, the return value is the number of bytes copied to the buffer. If the function fails, the return value is zero.
When register clipboard formats are placed on or retrieved from the clipboard, they must be in the form of an HGLOBAL value.
Инициализация обработчиков свойств
В этом разделе объясняется, как создавать и регистрировать обработчики свойств для работы с системой свойств Windows.
Этот раздел упорядочен следующим образом:
Обработчики свойств
Обработчики свойств являются важной частью системы свойств. Они вызываются индексатором для чтения и индексирования значений свойств, а также вызываются внутрипроцессным обозревателем Windows для чтения и записи значений свойств непосредственно в файлах. Эти обработчики должны быть тщательно написаны и протестированы, чтобы предотвратить снижение производительности или потерю данных в затронутых файлах. Дополнительные сведения об особенностях индексатора, влияющих на реализацию обработчика свойств, см. в статье «Разработка обработчиков свойств для поиска Windows».
Перед началом
Обработчики свойств — это COM-объекты, создающие абстракцию IPropertyStore для определенного формата файла. Они считывают (анализируют) и записывают этот формат файла таким образом, который соответствует его спецификации. Некоторые обработчики свойств выполняют свою работу на основе API, которые абстрагируют доступ к определенному формату файла. Перед разработкой обработчика свойств для формата файла необходимо понять, как формат файла хранит свойства и как эти свойства (имена и значения) сопоставляются с абстракцией хранилища свойств.
При планировании реализации помните, что обработчики свойств — это низкоуровневые компоненты, загруженные в контексте таких процессов, как Windows Explorer, индексатор поиска Windows и сторонние приложения, использующие модель программирования элементов оболочки. В результате обработчики свойств не могут быть реализованы в управляемом коде и должны быть реализованы в C++. Если обработчик использует любые API или службы для выполнения своей работы, необходимо убедиться, что эти службы могут правильно функционировать в средах, в которых загружается обработчик свойств.
Обработчики свойств всегда связаны с конкретными типами файлов; Таким образом, если формат файла содержит свойства, для которых требуется настраиваемый обработчик свойств, всегда следует зарегистрировать уникальное расширение имени файла для каждого формата файла.
Инициализация обработчиков свойств
Перед использованием свойства системой он инициализируется путем вызова реализации IInitializeWithStream. Обработчик свойств следует инициализировать, назначив системе поток, а не оставив это назначение реализации обработчика. Этот метод инициализации гарантирует следующее:
Существуют случаи, когда инициализация с потоками невозможна. В таких ситуациях существует два дополнительных интерфейса, которые могут реализовать обработчики свойств: IInitializeWithFile и IInitializeWithItem. Если обработчик свойств не реализует IInitializeWithStream, он должен отказаться от выполнения в изолированном процессе, в котором системный индексатор по умолчанию будет размещать его при изменении потока. Чтобы отказаться от этой функции, задайте следующее значение реестра.
Однако гораздо лучше реализовать IInitializeWithStream и выполнить инициализацию на основе потока. В результате обработчик свойств будет безопаснее и надежнее. Отключение изоляции процессов обычно предназначено только для устаревших обработчиков свойств и должно быть напряженно избегается любым новым кодом.
После загрузки самого документа свойства, отображаемые в Windows Explorer, загружаются путем вызова защищенного метода _LoadProperties, как показано в следующем примере кода. Этот процесс подробно рассматривается в следующем разделе.
Если поток доступен только для чтения, но параметр grfMode содержит флаг STGM_READWRITE, инициализация должна завершиться ошибкой и вернуть STG_E_ACCESSDENIED. Без этой проверки Windows Explorer отображает значения свойств как доступные для записи, даже если они не являются, что приводит к путанице взаимодействия с конечным пользователем.
хранилище свойств In-Memory
Не следует предоставлять каждый элемент и атрибут в XML-файле в качестве свойства. Вместо этого выберите только те, которые вы считаете полезными для конечных пользователей в организации своих документов (в данном случае рецепты). Это важная концепция, которая следует учитывать при разработке обработчиков свойств: разница между информацией, которая действительно полезна для сценариев организации, и информация, которая относится к деталям файла и может быть замечена путем открытия самого файла. Свойства не предназначены для полного дублирования XML-файла.
Работа со значениями PROPVARIANT-Based
Чтобы инициализировать PROPVARIANT из строки, можно использовать InitPropVariantFromString.
Как видно в любом из файлов рецептов, включенных в пример, в каждом файле может быть несколько ключевых слов. Для этого система свойств поддерживает многозначные строки, представленные вектором строк (например, «VT_VECTOR | VT_LPWSTR»). Метод _LoadVectorProperty в примере использует векторные значения.
Если значение не существует в файле, не возвращайте ошибку. Вместо этого задайте значение VT_EMPTY и верните S_OK. VT_EMPTY указывает, что значение свойства не существует.
Поддержка открытых метаданных
В этом примере используется формат файла на основе XML. Ее схему можно расширить для поддержки свойств, которые не считались во время разработки, например. Эта система называется открытыми метаданными. В этом примере система свойств расширяется путем создания узла в элементе Recipe с именем ExtendedProperties, как показано в следующем примере кода.
Затем функция CryptBinaryToStringÂ, объявленная в Wincrypt.h, выполняет преобразование Base64.
Сведения о поддержке открытых метаданных см. в разделе «Типы файлов, которые поддерживают открытые метаданные» в типах файлов.
содержимое Full-Text
В следующей таблице перечислены преимущества каждого подхода с помощью IFilter или IPropertyStore.
Функция | Ifilter | IPropertyStore |
---|---|---|
Позволяет выполнять обратную запись в файлы? | нет | Да |
Предоставляет сочетание содержимого и свойств? | Да | Да |
Многоязычных? | Да | нет |
MIME/Embedded? | Да | нет |
Границы текста? | Предложение, абзац, глава | None |
Реализация, поддерживаемая для SPS/SQL Server? | Да | нет |
Реализация | Complex | Простота |
Предоставление значений для свойств
При использовании для чтения значений обработчики свойств обычно вызываются по одной из следующих причин:
При написании обработчика свойств обычно необходимо учитывать следующие два набора свойств.
Так как в примере используется кэш в памяти, реализация методов IPropertyStore — это просто вопрос делегирования этому кэшу, как показано в следующем примере кода.
Если вы решили не делегировать кэшу в памяти, необходимо реализовать методы, чтобы обеспечить> следующее ожидаемое поведение:
Запись значений обратной копии
Одним из основных преимуществ использования потоков в качестве примера является надежность. Обработчики свойств всегда должны учитывать, что они не могут оставить файл в несогласованном состоянии в случае катастрофического сбоя. Очевидно, что следует избежать повреждения файлов пользователя, и лучше всего это сделать с помощью механизма копирования при записи. Если обработчик свойств использует поток для доступа к файлу, вы автоматически получаете это поведение; система записывает любые изменения в поток, заменив файл новой копией только во время операции фиксации.
Чтобы переопределить это поведение и управлять процессом сохранения файлов вручную, можно отказаться от безопасного поведения сохранения, задав значение ManualSafeSave в записи реестра обработчика, как показано здесь.
Если обработчик задает значение ManualSafeSave, поток, с которым он инициализируется, не является транзакционируемым потоком (STGM_TRANSACTED). Сам обработчик должен реализовать функцию безопасного сохранения, чтобы убедиться, что файл не поврежден, если операция сохранения прервана. Если обработчик реализует запись на месте, он будет записывать данные в предоставленный поток. Если обработчик не поддерживает эту функцию, он должен получить поток, с помощью которого необходимо записать обновленную копию файла с помощью IDestinationStreamFactory::GetDestinationStream. После записи обработчик должен вызвать IPropertyStore::Commit в исходном потоке, чтобы завершить операцию, и заменить содержимое исходного потока новой копией файла.
Большие форматы файлов, которые будут использоваться таким образом, чтобы создавать файлы размером более 1 МБ, следует реализовать поддержку записи свойств на месте; в противном случае поведение производительности не соответствует ожиданиям клиентов системы свойств. В этом сценарии время, необходимое для записи свойств, не должно влиять на размер файла.
Для очень больших файлов, например видеофайла размером 1 ГБ или более, требуется другое решение. Если в файле недостаточно места для записи на месте, обработчик может завершить обновление свойства ошибкой, если объем пространства, зарезервированного для записи свойств на месте, исчерпан. Эта ошибка возникает, чтобы избежать низкой производительности, связанной с 2 ГБ операций ввода-вывода (1 для чтения, 1 для записи). Из-за этого потенциального сбоя эти форматы файлов должны резервировать достаточно места для записи свойств на месте.
Если файл имеет достаточно места в заголовке для записи метаданных, а если запись этих метаданных не приводит к росту или уменьшению файла, возможно, будет безопасно выполнять запись на месте. Рекомендуется использовать 64 КБ в качестве отправной точки. Написание на месте эквивалентно обработчику запроса ManualSafeSave и вызову IStream::Commit в реализации IPropertyStore::Commit и имеет гораздо более высокую производительность, чем копирование при записи. Если размер файла изменяется из-за изменений значения свойства, запись на месте не должна выполняться из-за возможного повреждения файла в случае ненормального завершения.
По соображениям производительности рекомендуется использовать параметр ManualSafeSave с обработчиками свойств, работающими с файлами размером 100 КБ или больше.
Как показано в следующем примере реализации IPropertyStore::Commit, обработчик manualSafeSave регистрируется, чтобы проиллюстрировать параметр безопасного сохранения вручную. Метод _SaveCacheToDom записывает значения свойств, хранящиеся в кэше в памяти, в объект XMLdocument.
Затем зафиксируйте исходный поток, который записывает данные обратно в исходный файл безопасным способом.
Затем получите количество свойств, хранящихся в кэше в памяти.
Теперь выполните итерацию свойств, чтобы определить, было ли изменено значение свойства с момента загрузки в память.
Если свойство не находится на карте, это новое свойство, которое было задано Windows Explorer. Так как поддерживаются открытые метаданные, сохраните новое свойство в разделе ExtendedProperties XML.
Реализация IPropertyStoreCapabilities
Если вы создаете обработчик, обрабатывающий только свойства только для чтения, следует реализовать метод Initialize (IInitializeWithStream, IInitializeWithItem или IInitializeWithFile), чтобы он возвращал STG_E_ACCESSDENIED при вызове с флагом STGM_READWRITE.
Некоторые свойства имеют значение true для атрибута isInnate. Встроенные свойства имеют следующие характеристики:
Регистрация и распространение обработчиков свойств
После реализации обработчика свойств его необходимо зарегистрировать и его расширение имени файла, связанное с обработчиком. Дополнительные сведения см. в разделе «Регистрация и распространение обработчиков свойств».
Источники информации:
- http://docs.microsoft.com/en-us/windows/win32/api/propidlbase/ns-propidlbase-propvariant
- http://docs.microsoft.com/en-us/windows/win32/properties/building-property-handlers-property-handlers
- http://docs.microsoft.com/en-us/windows/win32/api/propidl/ns-propidl-propvariant
- http://docs.microsoft.com/ru-ru/windows/win32/properties/building-property-handlers-property-handlers