Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Another OLE Doc Viewer but with editing facility

0.00/5 (No votes)
17 Jun 2004 1  
An article on how to put and get information to an OLE Structured Document.

Sample Image - Another_OLE_Doc_Viewer.jpg

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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here