Another OLE Doc Viewer but with editing facility






4.81/5 (9 votes)
An article on how to put and get information to an OLE Structured Document.
Introduction
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.
Background
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.
The Code
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; CString stgName; StgPointers * next; StgPointers * prev; HTREEITEM hTree; } Pointers;
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 htreeItem) { HTREEITEM hNewItem; if(!m_bRootInserted) { htreeItem = m_treeData.InsertItem(m_strFileName, 3 , 2);m_first->hTree = htreeItem; m_cur = m_prev = m_first; m_bRootInserted = true; } USES_CONVERSION; IEnumSTATSTG * ppenum = NULL; HRESULT hRes = pCurrentStorage->EnumElements(0,NULL, 0, &ppenum); CString addtoList; STATSTG arr[1]; while(hRes != S_FALSE) { hRes = ppenum->Next(1, arr, NULL); addtoList = W2A(arr[0].pwcsName); if(arr[0].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); //Adding in Linked List m_prev = m_cur; m_cur = new Pointers;m_cur->prev = m_prev;m_cur->parent = pCurrentStorage;m_cur->pointer = pNewStorage;m_cur->stgName = addtoList;m_cur->hTree = hNewItem;m_cur->next = NULL;m_prev->next = m_cur; //Added PopulateTree(pNewStorage, hNewItem); } if(arr[0].type == 2) { addtoList += " <STREAM>"; m_treeData.InsertItem(addtoList, htreeItem); } } ppenum->Release(); return true; }
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.
My Thanks
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.