Storages and the IStorage Interface

If you have a storage object, you can manipulate it through the IStorage interface. Pay attention to these functions because Microsoft Foundation Class offers no support for storage access. Following are some of the important member functions and their significant parameters.

HRESULT Commit(…);

Commits all the changes to this storage and to all elements below it.

HRESULT CopyTo(…, IStorage**
pStgDest);

Copies a storage, with its name and all its substorages and streams (recursively), to another existing storage. Elements are merged into the target storage, replacing elements with matching names.

HRESULT CreateStorage(const WCHAR*
pName, …, DWORD mode, …, IStorage** ppStg);

Creates a new substorage under this storage object.

HRESULT CreateStream(const WCHAR*
pName, …, DWORD mode, …, IStream** ppStream);

Creates a new stream under this storage object.

HRESULT DestroyElement(const WCHAR* pName);

Destroys the named storage or stream that is under this storage object. A storage cannot destroy itself.

HRESULT EnumElements(…, IEnumSTATSTG** ppEnumStatstg);

Iterates through all the storages and streams under this storage object. The IEnumSTATSTG interface has Next, Skip, and Clone member functions, as do other COM enumerator interfaces.

HRESULT MoveElementTo(const WCHAR* pName,
IStorage* pStgDest, const LPWSTR* pNewName, DWORD flags);

Moves an element from this storage object to another storage object.

HRESULT OpenStream(const WCHAR*
pName, …, DWORD mode, …, IStorage** ppStg);

Opens an existing stream object, designated by name, under this storage object.

HRESULT OpenStorage(const WCHAR*
pName, …, DWORD mode, …, IStorage** ppStg);

Opens an existing substorage object, designated by name, under this storage object.

DWORD Release(void);

Decrements the reference count. If the storage is a root storage representing a disk file, Release closes the file when the reference count goes to 0.

HRESULT RenameElement(const
WCHAR* pOldName, const WCHAR* pNewName);

Assigns a new name to an existing storage or stream under this storage object.

HRESULT Revert(void);

Abandons a transaction, leaving the compound file unchanged.

HRESULT SetClass(CLSID& clsid);

Inserts a 128-bit class identifier into this storage object. This ID can then be retrieved with the Stat function.

HRESULT Stat(STATSTG* pStatstg, DWORD flag);

Fills in a STATSTG structure with useful information about the storage object, including its name and class ID.

Getting an IStorage Pointer

Where do you get the first IStorage pointer? COM gives you the global function StgCreateDocfile to create a new structured storage file on disk and the function StgOpenStorage to open an existing file. Both of these set a pointer to the file's root storage. Here's some code that opens an existing storage file named MyStore.stg and then creates a new substorage:

 IStorage* pStgRoot; IStorage* pSubStg; if (::StgCreateDocfile(L"MyStore.stg",     STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,     0, &pStgRoot) == S_OK) {     if (pStgRoot->CreateStorage(L"MySubstorageName",         STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,         0, 0, &pSubStg) == S_OK) {         // Do something with pSubStg         pSubStg->Release();     }     pStgRoot->Release(); } 

Freeing STATSTG Memory

When you call IStorage::Stat with a STATFLAG_DEFAULT value for the flag parameter, COM allocates memory for the element name. You must free this memory in a manner compatible with its allocation. COM has its own allocation system that uses an allocator object with an IMalloc interface. You must get an IMalloc pointer from COM, call IMalloc::Free for the string, and then release the allocator. The code below illustrates this.

If you want just the element size and type and not the name, you can call Stat with the STATFLAG_NONAME flag. In that case, no memory is allocated and you don't have to free it. This seems like an irritating detail, but if you don't follow the recipe, you'll have a memory leak.

Enumerating the Elements in a Storage Object

Following is some code that iterates through all the elements under a storage object, differentiating between substorages and streams. The elements are retrieved in a seemingly random sequence, independent of the sequence in which they were created; however, I've found that streams are always retrieved first. The IEnumSTATSTG::Next element fills in a STATSTG structure that tells you whether the element is a stream or a storage object.

 IEnumSTATSTG* pEnum; IMalloc* pMalloc; STATSTG statstg; extern IStorage* pStg;  // maybe from OpenStorage ::CoGetMalloc(MEMCTX_TASK, &pMalloc); // assumes AfxOleInit called VERIFY(pStg->EnumElements(0, NULL, 0, &pEnum) == S_OK) while (pEnum->Next(1, &statstg, NULL) == NOERROR) {     if (statstg.type == STGTY_STORAGE) {         if (pStg->OpenStorage(statstg.pwcsName, NULL,             STGM_READ | STGM_SHARE_EXCLUSIVE,             NULL, 0, &pSubStg) == S_OK) {             // Do something with the substorage         }         else if (statstg.type == STGTY_STREAM) {             // Process the stream         }         pMalloc->Free(statstg.pwcsName); // avoids memory leaks     }     pMalloc->Release(); } 

Sharing Storages Among Processes

If you pass an IStorage pointer to another process, the marshaling code ensures that the other process can access the corresponding storage element and everything below it. This is a convenient way of sharing part of a file. One of the standard data object media types of the TYMED enumeration is TYMED_ISTORAGE, and this means you can pass an IStorage pointer on the clipboard or through a drag-and-drop operation.



Programming Microsoft Visual C++
Programming Microsoft Visual C++
ISBN: 1572318570
EAN: 2147483647
Year: 1997
Pages: 332

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net