// MSITStg.h -- Interface declaration for IMSITStorage #ifndef __MSITSTG_H__ #define __MSITSTG_H__ // Class ID for the ITSS File System: DEFINE_GUID(CLSID_ITStorage, 0x5d02926a, 0x212e, 0x11d0, 0x9d, 0xf9, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); // Inteface ID for the IITStorage interface: DEFINE_GUID(IID_ITStorage, 0x88cc31de, 0x27ab, 0x11d0, 0x9d, 0xf9, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); // Class ID for the FSStorage wrapper for the Win32 file system: // {D54EEE56-AAAB-11d0-9E1D-00A0C922E6EC} DEFINE_GUID(CLSID_IFSStorage, 0xd54eee56, 0xaaab, 0x11d0, 0x9e, 0x1d, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); // Interface ID for the IFSStorage interface: // {8BB2438A-A70C-11d0-9E1C-00A0C922E6EC} DEFINE_GUID(IID_IFSStorage, 0x8bb2438a, 0xa70c, 0x11d0, 0x9e, 0x1c, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); // Interface ID for the extended IStream interface DEFINE_GUID(IID_IStreamITEx, 0xeb19b681, 0x9360, 0x11d0, 0x9e, 0x16, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); // Interface ID for the extended IStorage interface DEFINE_GUID(IID_IStorageITEx, 0xeb19b680, 0x9360, 0x11d0, 0x9e, 0x16, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); // Interface IDs for the Data Space Manager used within an ITStorage object: DEFINE_GUID(IID_IDataSpaceManager, 0x7c01fd0f, 0x7baa, 0x11d0, 0x9e, 0xc, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); DEFINE_GUID(IID_IDataSpace, 0x7c01fd0e, 0x7baa, 0x11d0, 0x9e, 0xc, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); DEFINE_GUID(IID_ITransformServices, 0xa55895fc, 0x89e1, 0x11d0, 0x9e, 0x14, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); DEFINE_GUID(IID_IKeyInstance, 0x96af35ce, 0x88ec, 0x11d0, 0x9e, 0x14, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); // Interface IDs for the plug-in data transforms: DEFINE_GUID(IID_ITransformFactory, 0x7c01fd0c, 0x7baa, 0x11d0, 0x9e, 0xc, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); DEFINE_GUID(IID_ITransformInstance, 0xeb19b67e, 0x9360, 0x11d0, 0x9e, 0x16, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); /* The IITStorage interface parallels the API's defined for creating and opening Docfiles. So if you have code that currently uses Docfiles for your storage mechanism, you can easily convert over to using ITS files instead. ITS files use a different on-disk structure to optimize them for very fast stream access and very low overhead. ITS files can manage thousands or millions of streams with very good access performance and very small directory space requirements. This makes ITS files ideal for CD-Roms and for data collections that you'll download across the Internet. To make the conversion to ITS files you'll need to call CoCreateInstance with the class-id CLSID_ITStorage and the interface-id IID_ITStorage. You'll get back an interface pointer, say pItStg. Then you'll need to adjust the places where your code creates or opens Docfiles. Instead of StgCreateDocfile you'll call pItStg->StgCreateDocfile, and instead of StgOpenStorage, you'll call pItStg->StgOpenStorage. In both cases you'll get back an IStorage interface pointer, say pIStg, which you can use just as you did before. That's it. In general the rest of your code shouldn't have to change. There are some functional difference between ITS files and Docfiles -- ITS files don't support STGM_TRANSACTED, for example. So if you have to have transacted file operations, you can't use ITS files -- at least for now. However in almost all other respects ITS files interfaces can directly replace Docfile interfaces. Converting your data is also easy. Just open one of your Docfiles using StgOpenStorage, create a new ITS file via pItStg->StgCreateDocfile, and then use the CopyTo interface to copy your data objects and their storage heirarchy over to the ITS file: pStgDocfile->CopyTo(0, NULL, NULL, pStgITS); In some cases you may want to exercise some control over the internal parameters kept in an ITS file. You do this by calling SetControlData to give the IITStorage interface a block of ITS control data. Then each subsequent call to StgCreateDocfile will use that control data. The ITS control data determines, among other things, the tradeoff between efficient random access to the stream data and minimizing the size of an ITS file. The actual structure and interpretation of ITS control data is documented below. (See the ITSFS_Control_Data data type). You can get default control data via the DefaultControlData fuction. Note that DefaultControlData allocates the control structure using IMalloc::Alloc as provided by CoGetMalloc and expects that your code will deallocate the structure using the IMalloc::Free. */ // IID_IStreamITEx interface declaration: DECLARE_INTERFACE_(IStreamITEx, IStream) { // IStreamITEx methods STDMETHOD(SetDataSpaceName)(const WCHAR * pwcsDataSpaceName) PURE; STDMETHOD(GetDataSpaceName)( WCHAR **ppwcsDataSpaceName) PURE; STDMETHOD(Flush)() = 0; }; // IID_IStorageITEx interface declaration: DECLARE_INTERFACE_(IStorageITEx, IStorage) { // IStorageITEx methods: STDMETHOD(GetCheckSum)(ULARGE_INTEGER *puli) PURE; STDMETHOD(CreateStreamITEx)(const WCHAR * pwcsName, const WCHAR *pwcsDataSpaceName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStreamITEx ** ppstm ) PURE; STDMETHOD(OpenStreamITEx)(const WCHAR * pwcsName, void * reserved1, DWORD grfMode, DWORD reserved2, IStreamITEx ** ppstm) PURE; }; #pragma warning( disable : 4200) // ITS_Control_Data is the generic structure of control data passed to the // IITStorage::SetControlData method or returned by the IITStorage::DefaultControlData // method. typedef struct _ITS_Control_Data { UINT cdwControlData; // Number of DWords to follow. UINT adwControlData[0]; // Actually this will be adwControlData[cdwControlData] } ITS_Control_Data, *PITS_Control_Data; // ITSFS_Control_Data is the actual prefix structure of control data for IITStorage. typedef struct _ITSFS_Control_Data { UINT cdwFollowing; // Must be 6 or 13 DWORD cdwITFS_Control; // Must be 5 DWORD dwMagic; // Must be MAGIC_ITSFS_CONTROL (see below) DWORD dwVersion; // Must be 1 DWORD cbDirectoryBlock;// Size in bytes of directory blocks (Default is 8192) DWORD cMinCacheEntries;// Least upper bound on the number of directory blocks // which we'll cache in memory. (Default is 20) DWORD fFlags; // Control bit flags (see below). // Default value is fDefaultIsCompression. } ITSFS_Control_Data, *PITSFS_Control_Data; // Signature value for ITSFS_Control_Data const DWORD MAGIC_ITSFS_CONTROL = 'I' | ('T' << 8) | ('S' << 16) | ('C' << 24); // Bit flag definitions for ITSFS_Control_Data::fFlags const DWORD fDefaultIsCompression = 0x00000001; const DWORD fDefaultIsUncompressed = 0x00000000; // Note all other fFlags bits positions are reserved for future releases and should be // set to zero. // When ITSFS_Control_Data::cdwFollowing is > 6, we assume that LZX_Control_Data follows // immediately after. (See the XformControlData type below) LZX_Control_Data defines // parameters which control the default compressed data space. // // If ITSFS_Control_Data::cdwFollowing is 6, we use default values for the LZX // control data. typedef struct _LZX_Control_Data { UINT cdwControlData; // Must be 6 DWORD dwLZXMagic; // Must be LZX_MAGIC (see below) DWORD dwVersion; // Must be 2 DWORD dwMulResetBlock;// Number of blocks between compression resets. (Default: 4) DWORD dwMulWindowSize;// Maximum number of blocks kept in data history (Default: 4) DWORD dwMulSecondPartition; // Granularity in blocks of sliding history(Default: 2) DWORD dwOptions; // Option flags (Default: fOptimizeCodeStreams) } LZX_Control_Data, *PLZX_Control_Data; // Note: The block size for LZX compression is 32768 bytes. const DWORD LZX_MAGIC = 'L' | ('Z' << 8 ) | ('X' << 16) | ('C' << 24); // Values for LZX_Control_Data::dwOptions const DWORD fOptimizeCodeStreams = 0x00000001; // Note that all other flag bit positions are reserved for future releases and should be // set to zero. // The second parameter for the IITStorage::Compact method below is an enueration // which defines the level of compaction to do. typedef enum ECompactionLev {COMPACT_DATA=0, COMPACT_DATA_AND_PATH} ; DECLARE_INTERFACE_(IITStorage, IUnknown) { // IITStorage methods STDMETHOD(StgCreateDocfile)(const WCHAR * pwcsName, DWORD grfMode, DWORD reserved, IStorage ** ppstgOpen ) PURE; STDMETHOD(StgCreateDocfileOnILockBytes)(ILockBytes * plkbyt, DWORD grfMode, DWORD reserved, IStorage ** ppstgOpen ) PURE; STDMETHOD(StgIsStorageFile)(const WCHAR * pwcsName) PURE; STDMETHOD(StgIsStorageILockBytes)(ILockBytes * plkbyt) PURE; STDMETHOD(StgOpenStorage)(const WCHAR * pwcsName, IStorage * pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage ** ppstgOpen ) PURE; STDMETHOD(StgOpenStorageOnILockBytes) (ILockBytes * plkbyt, IStorage * pStgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage ** ppstgOpen ) PURE; STDMETHOD(StgSetTimes)(WCHAR const * lpszName, FILETIME const * pctime, FILETIME const * patime, FILETIME const * pmtime ) PURE; STDMETHOD(SetControlData)(PITS_Control_Data pControlData) PURE; STDMETHOD(DefaultControlData)(PITS_Control_Data *ppControlData) PURE; STDMETHOD(Compact)(const WCHAR * pwcsName, ECompactionLev iLev) PURE; }; typedef IITStorage *PIITStorage; DECLARE_INTERFACE_(IFSStorage, IUnknown) { // IUnknown methods STDMETHOD(QueryInterface) (THIS_ REFIID, VOID **) PURE; STDMETHOD_(ULONG, AddRef) (THIS) PURE; STDMETHOD_(ULONG, Release) (THIS) PURE; // IFSStorage methods STDMETHOD(FSCreateStorage)(const WCHAR * pwcsName, DWORD grfMode, IStorage **ppstgOpen) PURE; STDMETHOD(FSOpenStorage)(const WCHAR * pwcsName, DWORD grfMode, IStorage **ppstgOpen) PURE; STDMETHOD(FSCreateStream)(const WCHAR *pwcsName, DWORD grfMode, IStream **ppStrm) PURE; STDMETHOD(FSCreateTemporaryStream)(IStream **ppStrm) PURE; STDMETHOD(FSOpenStream )(const WCHAR *pwcsName, DWORD grfMode, IStream **ppStrm) PURE; STDMETHOD(FSCreateLockBytes)(const WCHAR *pwcsName, DWORD grfMode, ILockBytes **ppLkb) PURE; STDMETHOD(FSCreateTemporaryLockBytes)(ILockBytes **ppLkb) PURE; STDMETHOD(FSOpenLockBytes )(const WCHAR *pwcsName, DWORD grfMode, ILockBytes **ppLkb) PURE; STDMETHOD(FSStgSetTimes)(WCHAR const * lpszName, FILETIME const * pctime, FILETIME const * patime, FILETIME const * pmtime ) PURE; }; typedef IFSStorage *PIFSStorage; /* ** Data Spaces -- What they are. Within an ITS file we store information in one or more data spaces. A data space is a container which holds the bits which represent a collection of streams. Each data space has a name and an associated collection of transforms. Those transforms take the raw data which you write to an ITS stream and map it into a representation stream. When you read from an ITS stream they do the reverse mapping to reconstruct your original data from the representation. When you first create an ITS file, it contains one data space named "Default_Space" which applies the LZX data compression transform. By default all of the streams you create will have their data representations stored in the default data space. If LZX compression meets your needs, and you're not concerned about data enciphering, you can skip over the following discussion. If, on the other hand, you want to create additional data spaces or transforms, read on. To create a data space, you must first get a pointer to the IDataSpaceManager interface. Just do a QueryInterface for IID_DataSpaceManager from any storage created by the IITStorage interface. Then you can call the CreateDataSpace function to define a new data space. When you're defining a collection of data spaces, be sure that their names are distinct. Defining two data spaces with the same name is an error. Data space names follow the rules for stream names. That is, they must be less than 260 characters long, and may not contain the characters '/'. '\', '|', ':', or any character less than 0x0020. Data spaces are kept in a separate name space. So you don't have to worry about colliding with a stream name or a storage name. As noted above, we have defined one special data space ("Default_Space") where all data resides if you take no action. You can redefine that default space simply by creating a new data space with the name "Default_Space". This is the one case where a name collision is allowed. If you do redefine the default data space, any data in the old space will automatically be transformed appropriately and moved into the new default data space. ** Importing Items If you have defined additional data spaces, the next step is to define which streams and storages you want to move into the new data spaces. You do that by means of the IDataSpace::Import function. For example suppose you've defined the dataspace *pMyDataSpace and you want to import the stream "Alpha" contained in the storage *pThatStorage: pMyDataSpace->Import(pThatStorage, "Alpha"); Similarly if you want to import the storage "HTML_Pages" from pThisStorage: pMyDataSpace->Import(pThisStorage, "HTML_Pages"); That will recursively import the "HTML_Pages" storage and all of the streams and storages contained within it. It also conditions those storages so that anything you create within them will be automatically imported into pMyDataSpace. Note that subsequent Import operations may alter that conditioning. If you later decide that you want to move "Alpha" back into the default data space: hr = pDataSpaceManager->OpenDataSpace(L"Default_Space", &pDefaultDataSpace); pDefaultDataSpace->Import(pThatStorage, "Alpha"); ** Data Space Transform Sets When you define a data space, you must specify a set of transforms to apply to the items you import into the space. A transform is an interface that converts data to some other representation. For example the LZX transform converts your imported data into a more compact, compressed representation. Other transforms might implement word or phrase based dictionary compression, or they might encipher your data, or they might just convert from one data format to another. You could, for example, construct a transform to store HTML data as a Rich Text stream. When you define a data space with more than one transform, they are applied in order. For example let's suppose that your transform set consists of these three: 1. A Dictionary compression transform 2. The LXZ transform 3. An data encryption transform Whenever you write data into this space, it will first be compressed using the dictionary compression methods, then LZX compression will be applied, and finally your information will be encrypted. When you read data the process is reversed so that the encryption transform supplies data to the LZX transform which in turn provides data for the dictionary compression transform. You define the transform set via a vector of class ids (paclsidXform). Each class id defines a location where an implementation of IID_Transform can be found. In addition you'll supply corresponding control data for each transform (papxfcd). The number of transforms is defined by the cXforms parameter. Note that it is legal to define a space with zero transforms. This is useful when you've got items which are already compressed and which won't benefit from an additional layer of compression overhead. The control data for a transform defines how it will operate in a particular data space. For example the control data for the LZX transform defines how aggressively it will pursue compression, and it controls the tradeoff between random access performance and the level of compression. The actual structure and content of the control data is documented above. (See the LZX_Control_Data data type.) ** Transform Factories -- How they are organized; What they do Transforms have two functional capabilities. They can return default control data (DefaultControlData), and they can create transform instances (CreateTransformInstance). When the ITSS code calls your CreateTransformInstance function, it will supply a storage medium (pXFSpan_Base) where transformed data is to be stored along with the control data for the instance and several other useful pieces of information. The CreateTransformInstance function has several parameters that you can use when you need to access global and/or instance data streams. They also support the construction of encryption transforms. You can ignore those parameters if your transform doesn't do encryption, uses only a single pass over the data, and doesn't rely on any data beyond the data in the stream being transformed. -- The rclsidXForm and pwszDataSpace parameters, respectively, tell you the Class ID by which your code was located, and the name of the data space in which your instance will be working. These values are used with the ITransformServices interface. -- The pXformServices parameter points to an instance of the ITranformServices interface. That interface gives you access to a couple of storages where you can keep global and instance data for your transform. It also gives you a way to contruct a temporary stream that is automatically deleted when you release it. That's very handy when you get a seek operation into the middle of the transformed data followed by a write operation. If you're implementing a multipass strategy, you can get access to those storages from code outside the transform by doing a QueryInterface from any ITS storage for the interface IID_ITransformServices. You identify the storage in question by the transform's class id and possibly the name of the data space instance. The per-transform-instance storage is also a convenient place to put the navigation data necessary to support fast seek operations. -- The pKeyManager parameter is an interface pointer used with encryption transforms. It supplies the read and write keys to use with your encryption transform. Those keys are set by the SetKeys interface of the ITransformServices interface. This allows you to separate your user interface code where people will enter passwords from the transform implementations. This can be useful when you want the same keys to be used for several different data spaces. ** Transform Instances -- How they are organized; What they do A Transform Instance is an object which simulates a data medium which can be suballocated. Suballocated items are managed as data spans (ITransformedSpan). You must supply a function to create a data span (CreateTransformedSpan), and a function to open a data span (OpenTransformedSpan). Both of those functions return an ITransformedSpan interface when they succeed. In addition you must supply the function SpaceSize to return the size of the entire untransformed data image. That is, SpaceSize returns the highest limit offset (offset + size) of any data span created within the instance. A span is identified by an ImageSpan structure which defines an offset and a size for the span. Both values are defined in untransformed space. For the Create function this is an output parameter, while it is an input parameter for the Open function. Note the interaction between the ImageSpan and the WriteAt member function of the ITransformedSpan interface. ** Transformed Data Spans -- How they are organized; What they do A transformed Data Span (ITransformedSpan) has two member functions -- ReadAt and WriteAt. Those functions are very similar to the ReadAt and WriteAt functions of the ILockBytes interface. The difference is that WriteAt includes an extra output parameter (pImageSpan) for recording the current span parameters. The ReadAt function doesn't include that parameter because read operations can never change the span's size or move it to a different offset. ** Implementation Strategies This section describes a few scenarios that you may encounter as you construct a transform along with strategies for those situations. This is an open ended list which will expand as we gain more experience with transforms. Many compression and encryption transforms are designed around sequential I/O. That is, they expect to get the raw data from a sequence of write opeations with no intervening seek operations. In many cases such transforms also write out the transformed data to the base stream in ascending order. Similarly they expect read requests to come to them with no intervening seek operations. The key issue for those transforms is how do you implement random access and interleaved read, write, and seek operations. Leaving aside write operations for the moment, let's consider a random sequence of reads interleaved with seek operations. One solution might be to construct a table to map from raw data offsets to transformed data offsets. You can store such a table in the instance storage for the current data space. One complication is that many compression transforms use the raw data as a dictionary. That is, you can only start reading from the beginning of the transformed data. You can deal with those transforms telling them to reset themselves periodically. That gives you a collection of starting points spread fairly evenly throught the transformed data. When you do this you'll need to supply a control data parameter to control the frequency of those reset points so that your clients can make an appropriate tradeoff between compression efficiency and random access performance. Now what about adding random write operations to the mix? The short answer here is that you can't do this in the middle of transformed data. One strategy would be to reconstruct the entire raw data into a temporary stream and do all your I/O to that stream until release time. Then at release time you can transform the revised data sequentially. A variation on that strategy is to keep track of which reset spans are modified and write the modified versions of those transformed spans to the end of the base stream. This leaves a certain amount of dead space in your transformed data, but it allows you to defer the sequential reconstruction work to a more convenient time. The down side is that it requires you to manage yet more navigation data in the instance storage for the data space. */ interface IDataSpaceManager; interface IDataSpace; interface ITransformServices; interface IKeyInstance; interface ITransformFactory; interface ITransformInstance; typedef struct _XformControlData { UINT cdwControlData; // Size of this structure in DWords UINT adwControlData[0]; // Actually this will be UINT adwData[cdwData]; } XformControlData, *PXformControlData; /* // {7C01FD0F-7BAA-11d0-9E0C-00A0C922E6EC} DEFINE_GUID(IID_IDataSpaceManager, 0x7c01fd0f, 0x7baa, 0x11d0, 0x9e, 0xc, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); */ interface IDataSpaceManager : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE CreateDataSpace (const WCHAR *pwszDataSpace, UINT cXforms, const CLSID *paclsidXform, PXformControlData paxfcd, IDataSpace *pITDataSpace ) = 0; virtual HRESULT STDMETHODCALLTYPE OpenDataSpace (const WCHAR *pwszDataSpace, IDataSpace *pITDataSpace ) = 0; virtual HRESULT STDMETHODCALLTYPE DiscardDataSpace (const WCHAR *pwszDataSpace) = 0; virtual HRESULT STDMETHODCALLTYPE EnumDataSpaces (IEnumSTATSTG ** ppenum) = 0; }; /* // {7C01FD0E-7BAA-11d0-9E0C-00A0C922E6EC} DEFINE_GUID(IID_IDataSpace, 0x7c01fd0e, 0x7baa, 0x11d0, 0x9e, 0xc, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); */ interface IDataSpace : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetTransformInfo (PUINT pcXforms, PUINT pcdwXformControlData, CLSID *paclsid, PXformControlData pxfcd ) = 0; virtual HRESULT STDMETHODCALLTYPE Import (IStorage *pStg, const WCHAR * pwszElementName) = 0; virtual HRESULT STDMETHODCALLTYPE ImportSpace(IStorage **ppStg) = 0; }; /* // {7C01FD0C-7BAA-11d0-9E0C-00A0C922E6EC} DEFINE_GUID(IID_ITransformFactory, 0x7c01fd0c, 0x7baa, 0x11d0, 0x9e, 0xc, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); */ interface ITransformFactory : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DefaultControlData (XformControlData **ppXFCD) = 0; virtual HRESULT STDMETHODCALLTYPE CreateTransformInstance (ITransformInstance *pXFormMedium, // Container data span for transformed data ULARGE_INTEGER cbUntransformedSize, // Untransformed size of data PXformControlData pXFCD, // Control data for this instance const CLSID *rclsidXForm, // Transform Class ID const WCHAR *pwszDataSpaceName, // Data space name for this instance ITransformServices *pXformServices, // Utility routines IKeyInstance *pKeyManager, // Interface to get enciphering keys ITransformInstance **ppTransformInstance // Out: Instance transform interface ) = 0; }; typedef struct _ImageSpan { ULARGE_INTEGER uliHandle; ULARGE_INTEGER uliSize; } ImageSpan; /* // {EB19B67E-9360-11d0-9E16-00A0C922E6EC} DEFINE_GUID(IID_ITransformInstance, 0xeb19b67e, 0x9360, 0x11d0, 0x9e, 0x16, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); */ interface ITransformInstance : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE ReadAt (ULARGE_INTEGER ulOffset, void *pv, ULONG cb, ULONG *pcbRead, ImageSpan *pSpan ) = 0; virtual HRESULT STDMETHODCALLTYPE WriteAt (ULARGE_INTEGER ulOffset, const void *pv, ULONG cb, ULONG *pcbWritten, ImageSpan *pSpan ) = 0; virtual HRESULT STDMETHODCALLTYPE Flush() = 0; virtual HRESULT STDMETHODCALLTYPE SpaceSize(ULARGE_INTEGER *puliSize) = 0; // Note: SpaceSize returns the high water mark for the space. That is, the largest // limit value (uliOffset + uliSize) for any transformed lockbytes created within // the base (*pXLKB). }; /* // {A55895FC-89E1-11d0-9E14-00A0C922E6EC} DEFINE_GUID(IID_ITransformServices, 0xa55895fc, 0x89e1, 0x11d0, 0x9e, 0x14, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); */ interface ITransformServices : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE PerTransformStorage (REFCLSID rclsidXForm, IStorage **ppStg) = 0; virtual HRESULT STDMETHODCALLTYPE PerTransformInstanceStorage (REFCLSID rclsidXForm, const WCHAR *pwszDataSpace, IStorage **ppStg) = 0; virtual HRESULT STDMETHODCALLTYPE SetKeys (REFCLSID rclsidXForm, const WCHAR *pwszDataSpace, PBYTE pbReadKey, UINT cbReadKey, PBYTE pbWriteKey, UINT cbWriteKey ) = 0; virtual HRESULT STDMETHODCALLTYPE CreateTemporaryStream(IStream **ppStrm) = 0; }; /* // {96AF35CE-88EC-11d0-9E14-00A0C922E6EC} DEFINE_GUID(IID_IKeyInstance, 0x96af35ce, 0x88ec, 0x11d0, 0x9e, 0x14, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec); */ interface IKeyInstance : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetKeys (PBYTE *pbReadKey, PUINT pcbReadKey, PBYTE *pbWriteKey, PUINT pcbWriteKey ) = 0; }; #endif // __MSITSTG_H__