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);
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;
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.