Click here to Skip to main content
Click here to Skip to main content

Structured Storage File Viewer Visual Studio Add-In

By , 3 Dec 2001
Rate this:
Please Sign up or sign in to vote.

Sample Image - SSFView.jpg

Introduction

A Structured Storage File is a binary file that is created and managed by the Microsoft OLE Structured Storage APIs. Structured Storage Files are also known as Compound Files. Essentially, we have a "file system within a file". Within such a compound file, we have two types of named objects: Storages and Streams.

A Storage Object acts like a directory of a typical file system. It manages other storages and streams but holds no data by itself. A Stream Object acts like a file in that it can hold information but not other storage objects. Examples of compound files include Microsoft Word (.doc) documents and Microsoft Excel (.xls) worksheet files.

SSFView is a Visual Studio add-in that allows a developer to view the contents of Structured Storage Files. An example view of a Microsoft Word document is shown in the screenshot above.

The motivation for the development of this add-in came about when my development team had to debug the implementation of a program data file which is stored using OLE's Structured Storage File format.

For debugging purposes, we had to compare stream outputs from existing storage files with live stream data being created. We had to dump out parts of stream data using trace statements and other file logging operations. It was messy and the use of these debugging constructs were short-termed.

We thus took the initiative to develop our own Structured Storage File Viewer which can be customized to meet our debugging needs. We thought it would be a good idea to share our code to readers out there who may be facing similar problems and would wish to have some startup code in order to develop a customized Structured Storage File Viewer.

Instead of developing a separate application, it was our unanimous choice to develop an add-in instead. The following are the advantages:

  1. Utilization of Visual Studio resources, especially the binary view windows.

    It would have taken much longer time for us to develop our own binary view window. Why not use the one supplied automatically by VS? Saving, searching capabilities are all provided.

  2. Integration with Visual Studio for debugging ease.

    Debugging is more intuitive when you can do everything in an integrated environment rather than having to step out into external applications every once in a while.

Installation

  1. Build the zipped SSFView.dsw project and copy SSFView.dll into Program Files\microsoft visual studio\common\msdev98\addins.
  2. Open Tools | Customize | Add-ins and Macro Files and tick "SSFView.DSAddIn.1".
  3. A new toolbar with an icon shown below will appear.

Usage

  1. Click on this icon and the usual Open dialog box will appear.

  2. Select any file known to be a compound file (e.g. a MS Word Doc file).
  3. The contents of the compound file will be displayed as a tree in the tree view window of the Viewer dialog box.

  4. Storages are displayed as folder icons.
  5. Streams are displayed as file icons.
  6. Clicking on either types of objects will cause the add-in to display statistics information about the selected object, on the edit box on the right.
  7. If you double-click on a Stream object, its full contents are displayed in a binary window in Visual Studio.
  8. You can opt to save the contents through Visual Studio in the usual way (via File | Save).

Limitations

Note that double-clicking the same stream item will cause its contents to be displayed even if its contents have already been displayed in a binary view window.

The contents of a stream object are first dumped by the add-in into a temporary file on your system. The contents of this file is displayed in the binary view window. Therefore the title of the window will be the name of the temp file and not the name of the stream object.

How It Works

The entire functionality of the add-in is encapsulated inside the CDlg_StructuredStorageFileViewer dialog box. This dialog box is designed to be a modeless dialog box. This dialog box will manage exactly one structured storage file and will do so until it is closed. This dialog box will start life when the add-in icon button is clicked at which time the following code will be executed:

STDMETHODIMP CCommands::StructuredStorageFileViewer()
{
      AFX_MANAGE_STATE(AfxGetStaticModuleState())

      // TODO: Add your implementation code here
        if (m_pDlg_StructuredStorageFileViewer == NULL)
      {
        LPTSTR lpszFileName = NULL;
        long lRetTemp = 0;

        lRetTemp = OpenAFile ((LPTSTR*)&lpszFileName);

        if ((lRetTemp == 0) && (lpszFileName))
        {
          m_pDlg_StructuredStorageFileViewer = 
            new CDlg_StructuredStorageFileViewer();
          m_pDlg_StructuredStorageFileViewer -> 
            SetFileToView((LPCTSTR)lpszFileName);
          m_pDlg_StructuredStorageFileViewer -> SetCommandsObject (this);
          m_pDlg_StructuredStorageFileViewer -> Display();
        }

        if (lpszFileName)
        {
          free (lpszFileName);
          lpszFileName = NULL;
        }
      }

      return S_OK;
}

A new instance of CDlg_StructuredStorageFileViewer is created, the file to view is set by the member SetFileToView() function. We also give this dialog box a pointer to the current CCommands object so that it can communicate with it. It is via the CCommands pointer that the dialog box instructs CCommands to display the contents of a stream object. We then instruct the dialog box to display itself.

The CDlg_StructuredStorageFileViewer dialog box responds to the TVN_SELCHANGED notification from the tree view control and the handler for this is CDlg_StructuredStorageFileViewer:: OnSelchangedTreeStructuredStorageFileContents(). The code for this function is shown below:

void 
  CDlg_StructuredStorageFileViewer::OnSelchangedTreeStructuredStorageFileContents
  (NMHDR* pNMHDR, LRESULT* pResult)
{
  NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
  // TODO: Add your control notification handler code here
  HWND hwndTree = pNMHDR -> hwndFrom;

  // If a selection in the Tree has changed, we investigate further.
  if (pNMHDR -> code == TVN_SELCHANGED)
  {
    // If this item is designated as a Storage item, 
    // we analyse it further.
    if ((pNMTreeView -> itemNew).lParam == STGTY_STORAGE)
    {
      // AnalyseStorage() will insert further Tree Items inside this item
      // if appropriate.
      AnalyseStorageItem ((HWND)hwndTree, 
        (pNMTreeView -> itemNew).hItem);
    }

    if ((pNMTreeView -> itemNew).lParam == STGTY_STREAM)
    {
      // AnalyseStreamItem() will display the
      // IStream information of this stream item.
      AnalyseStreamItem ((HWND)hwndTree, 
        (pNMTreeView -> itemNew).hItem);
    }
  }

  *pResult = 0;

  return;
}

Two functions stand out: AnalyseStorageItem() and AnalyseStreamItem(). AnalyseStorageItem() is called when a storage item is clicked on the tree view control. It will perform two things:

  1. It will expand this storage tree view item by examining it to see if it contains any storages and streams. If so, tree view children will be added to this item.
  2. It will also display the statistics information about this selected tree view item.

AnalyseStreamItem() will simply display statistics information about the stream object.

The CDlg_StructuredStorageFileViewer dialog box also responds to the NM_DBLCLK notification from the tree view control and the handler for this is CDlg_StructuredStorageFileViewer::OnDblclkTreeStructuredStorageFileContents(). The code for this function is shown below:

void 
  CDlg_StructuredStorageFileViewer::OnDblclkTreeStructuredStorageFileContents
  (NMHDR* pNMHDR, LRESULT* pResult) 
{
  // TODO: Add your control notification handler code here
  HWND hwndTree = pNMHDR -> hwndFrom;
  HTREEITEM hTreeItemCurrent = 
    TreeView_GetSelection(hwndTree);
  TV_ITEM tv_item_current;

  memset (&tv_item_current, 0, sizeof(TV_ITEM));
  tv_item_current.hItem = hTreeItemCurrent;
  tv_item_current.mask = TVIF_HANDLE | TVIF_PARAM;

  TreeView_GetItem (hwndTree, &tv_item_current);

  // If this item is designated as a Storage item, 
  // we analyse it further.
  if (tv_item_current.lParam == STGTY_STORAGE)
  {
    // Nothing needs to be done when a Storage Item is double-clicked.
    // It has already been analysed in TVN_SELCHANGED.
  }

  if (tv_item_current.lParam == STGTY_STREAM)
  {
    // When a Stream Item is double-clicked,
    // its contents will have to be displayed.
    DisplayStreamItem ((HWND)hwndTree, 
      (HTREEITEM)hTreeItemCurrent);
  }

  *pResult = 0;

  return;
}

Only when a Stream object is double-clicked will any meaningful thing be performed. We display the contents of this stream object when it is double-clicked. The main function to perform this is DisplayStreamItem().

Conclusion

A tree view provides a natural and intuitive layout for directories and files and this is the primary reason why we use it to display storages and streams. However, the emphasis of this add-in is to view and save data. Therefore nothing fancy is done with the tree view control.

This can be seen in the fact that although we used MFC's CTreeCtrl class to operate with the tree view, we did not fully use its MFC methods. Rather, we used TreeView_* macros. This is because we wanted to use several tried and tested code for working with tree views.

We also did not make extensive use of private item data (TV_INSERTSTRUCT.item.lParam) to point to structs. We simply used the lParam to indicate whether a tree view item is a Storage Item or a Stream Item.

HTREEITEM CDlg_StructuredStorageFileViewer::TreeItemInsert_Storage
  (HWND hwndTree, LPCTSTR lpszStorageName, HTREEITEM hTreeItemParent)
{
  TV_INSERTSTRUCT      tvis ;
      ...
      ...
      ...
>>tvis.item.lParam = STGTY_STORAGE;

  hTreeItemRet = TreeView_InsertItem(hwndTree, &tvis);

  return hTreeItemRet;
}

We plan to further enhance the statistics information display. The various date/time values are currently displayed as hex values. This is because we have yet to find out how to properly interpret FILETIME structs. If any reader knows how to interpret this, please contact us. Smile | :)

We also plan to extend our add-in to read memory storages and streams which can prove very useful as well.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Lim Bio Liong
Web Developer
Singapore Singapore
Lim Bio Liong is a Specialist at a leading Software House in Singapore.
 
Bio has been in software development for over 10 years. He specialises in C/C++ programming and Windows software development.
 
Bio has also done device-driver development and enjoys low-level programming. Bio has recently picked up C# programming and has been researching in this area.

Comments and Discussions

 
GeneralHelp ! how do I get the sub-storage count in SS file PinmemberPrasad_th2-Jul-04 6:21 
GeneralDeleting Storages and Stream PinmemberAamir Butt26-May-04 1:27 
GeneralRe: Deleting Storages and Stream PinmemberAamir Butt26-May-04 1:41 
GeneralStructure of Stg files PinmemberAndrew Phillips2-May-04 18:42 
GeneralDocFile Viewer Pinmemberton ot llet16-Dec-03 20:10 
GeneralHelp needed ,strip compound.... macro's PinmemberAtila29-May-02 1:47 
GeneralBrilliant! PinmemberPaulMdx5-Apr-02 3:59 
GeneralRe: Brilliant! PinmemberLim Bio Liong7-Apr-02 17:59 
GeneralVery, very cool code PinmemberDanYELL17-Jan-02 5:58 
I really like it. I works well. I always wanted to view the content of the OLE Structured files.
GeneralRe: Very, very cool code PinmemberLim Bio Liong17-Jan-02 18:49 
GeneralFILETIME PinmemberDale Stewart9-Jan-02 8:52 
GeneralRe: FILETIME PinmemberLim Bio Liong9-Jan-02 15:28 
GeneralRe: FILETIME PinsussAnonymous28-Oct-03 11:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140421.2 | Last Updated 4 Dec 2001
Article Copyright 2001 by Lim Bio Liong
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid