This is just another OLE Document / Structured Document Viewer. Just to add a little to it, I have added deletion and insertion facilities in it. I developed it coz it was needed for our internal office use. We were working a lot on Structured Storage files and needed a utility to not only view all the details of Storages and Streams within a file but also to be able to edit the file. So, this is the end-product.
You must be familiar with structured storage to have a complete grasp over this article. Just to introduce it to those who doesn't know about it, structured storage files are normally said to have a File System within a file. As with a normal file system in which we have folders and files, in a structured storage file, we have Storages and Streams. A storage can have streams as well as storages inside it, but a stream is like a simple binary file.
This is a small standalone exe. Now, some people would ask that Microsoft is itself providing an OLE Doc Viewer so what is the need for this. I think I have answered this before. I have added editing facilities which we needed for our office project and that was the only reason to develop it. Plus, I thought that it is something which should be shared. So, accept my apologies if somebody gets annoyed for repetition of work which I think isn't the case.
Since it is always considered best to represent a file system in a tree, so is done here. I have used a tree to show the full file system (so-called) within a storage file. By double-clicking or by selecting "Show Contents" from the right-click menu, you can see the value of the selected stream in the right side pane.
Well, coming to the code. I have stored all the Storages within a file in a linked list. There is a structure named
StgPointers. This is used for a doubly linked list, I used a doubly linked list just to ease out the movement within the linked list. I have only stored the Storages in the linked list not the Streams. This was done just because if we have got a Storage pointer, then it is not difficult to open the streams within that particular storage.
Now, using a linked list for a tree caused me some problems especially while deleting, because logically a tree should be stored in a tree but that was again difficult to handle. So, I opted for the first difficult option because I thought I was good at that :-). I know self-praise leads to isolation but sometimes ....
Anyway, here are some of the important code snippets:
First of all, the structure which is used for the linked list:
typedef struct StgPointers
IStorage * pointer;
IStorage * parent;
StgPointers * next;
StgPointers * prev;
This is the API used for opening a Storage file on the disk.
HRESULT hRes = StgOpenStorage(wcFName, NULL, m_dwMode, NULL, 0, &m_pRootStg);
Now, this is the recursive function which is used to populate the tree.
EnumElements is returning a pointer of type
IEnumSTATSTG which holds the enumerated elements within that storage. By calling its next function, we get its name in an array of type
STATSTG. Then it is being converted to a
CString and added to the tree.
bool COLEDOCViewerDlg::PopulateTree(IStorage * pCurrentStorage,
htreeItem = m_treeData.InsertItem(m_strFileName, 3 ,
2);m_first->hTree = htreeItem;
m_cur = m_prev = m_first;
m_bRootInserted = true;
IEnumSTATSTG * ppenum = NULL;
HRESULT hRes = pCurrentStorage->EnumElements(0,NULL, 0, &ppenum);
while(hRes != S_FALSE)
hRes = ppenum->Next(1, arr, NULL);
addtoList = W2A(arr.pwcsName);
if(arr.type == 1)
hNewItem = m_treeData.InsertItem(addtoList, 3, 2, htreeItem, NULL);
WCHAR * stgName = A2W(addtoList);
IStorage * pNewStorage = NULL;
hRes = pCurrentStorage->OpenStorage(stgName,
NULL, m_dwMode, NULL, 0, &pNewStorage);
m_prev = m_cur;
m_cur = new
NULL;m_prev->next = m_cur;
if(arr.type == 2)
addtoList += " <STREAM>";
The contents of a Stream are gotten like this. Here,
pStream is a pointer to the Stream whose value we are reading.
pStream->Read(readData, noofbytes, NULL);
The logic of
DeleteItem is a bit complex because as I mentioned earlier, I am storing a tree in a linked list which caused me a good amount of problem here. Further, inserting Streams / Storages is not that difficult. It would be quite clear from the code, so I don't think I should further explain it here.
One important thing is the mode in which all the objects are opened. These are handled by one variable
m_dwMode, and its value is set to:
m_dwMode = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT
STGM_READWRITE means that it is opened in both modes,
STGM_SHARE_EXCLUSIVE suggests that no one else can open this while it is opened, and
STGM_DIRECT means that we don't need to call Commit to ensure changes.
Remaining is quite simple.
I used a function
SnapLine which was from an external source. This was probably from some version of Borland C, but I don't remember exactly. Thanx to the developer who created this.