This site is moving to The Zen Source Library
Please Update your bookmarks


Item enumeration - DocFile Viewer

Enumerating all the elements in a DocFile can be very useful, to show how it is done I'll create a very simple DocFile viewer.
NB DocFiles are not optimized for enumeration, which means that enumerating a DocFiles elements is a relatively slow process. You should therefore use enumeration only when absolutely necessary. Later on in this tutorial I'll show a technique that you can use to eliminate enumeration and allow you to use storages and streams names of any length. For now I'll continue with enumeration.

IStorage.EnumElements is the function that is used to enumerate all the storages sub-items



   IStorage.EnumElements(  Must be 0,
                           Must be nil,
                           Must be 0,
                           Location for IEnumSTATSTG
                         ) : HResult;

EnumElements returns a HResult, S_OK indicates that the IEnumSTATSTG interface was returned.
Once you have the IEnumSTATSTG interface you can get each sub-element one at a time by using the "Next" method.


   IEnumSTATSTG.Next(  Number of items to retrieve,
                       returned StatSTG,
                       number fetched
                     ) : HResult;


The following function shows the names of every element in a given storage.


   procedure ShowName(  Stor : IStorage  );
   var
      Hr : HResult;
      Enum : IEnumSTATSTG;
      StatStg : TStatStg;
      NumFetched : integer;
   begin
         {Start enumeration}
      Hr := Stor.EnumElements(  0,  nil,  0,  Enum  );

         {Enum started Ok?}
      if(  Hr <> S_OK  ) then
         Exit;

      repeat
            {Get 1 STATSTG}
         Hr := Enum.Next(  1,  StatStg,  @NumFetched  );

            {Was a STATSTG retrieved?}
         if(  Hr <> S_OK  ) then
            continue;

            {Show the elements name}
         ShowMessage(  StatStg.pwcsName  );

         {Until an error occurs, or enum function indicates a stop}
      until (  Hr <> S_OK  );
   end;



The next step is to use this information to build a simple DocFile viewer.



Control Caption Name
TPanel   Panel1
TSpeedButton   sbut_Load
TOpenDialog   odlg_DocFile
TTreeView   tv_Enum

The DocFile viewer loads any DocFile and displays its contents in a TreeView. The tree shown in the picture above represents the hierarchy created by example 2 - "Sub-Storages". You can also load MS excel and word files and view their contents.

Starting with the sbut_LoadClick event I'll discuss each of the 3 methods in order


   procedure TForm1.sbut_LoadClick(Sender: TObject);
   var
      ws : WideString;
   begin
         {Select a DocFile to open}
      if(  not odlg_DocFile.Execute  ) then
         Exit;


         {Remove old file info}
      sbar_Simple.SimpleText := '';
      tv_Enum.Items.Clear;


         {Get UNICODE file name}
      ws := odlg_DocFile.FileName;

         {Check that this is a DocFile}
      if(   StgIsStorageFile(  PWideChar(ws)  ) <> S_OK   ) then
      begin
         MessageBeep(  MB_ICONEXCLAMATION  );
         ShowMessage(  'Not a doc file'  );
         Exit;
      end;

         {Load and parse}
      ProcessFile(  odlg_DocFile.FileName  );
   end;

In sbut_LoadClick.....

  1. User is asked to select a new file.
  2. If a file is selected then check if it is a DocFile
  3. If it is a DocFile Call ProcessFile



   procedure TForm1.ProcessFile(  sFileName : WideString  );
   var
      Hr : HResult;
      Root : IStorage;
   begin
         {open storage}
      Hr := StgOpenStorage(  PWideChar(sFileName),
                             nil,
                             STGM_READWRITE or STGM_DIRECT or
                             STGM_SHARE_EXCLUSIVE,
                             nil,
                             0,
                             Root
                           );

         {File opened?}
      if(   not SUCCEEDED(  Hr  )   ) then
      begin
         MessageBeep(  -1  );
         ShowMessage(  'Cant open file - ' + sFileName  );
         Exit;
      end;

         {Add Root}
      tv_Enum.Items.Add(  nil,  sFileName  );
         {Display name of open file}
      sbar_Simple.SimpleText := sFileName;

         {Start the enumeration}
      EnumInStorage(  Root,  tv_Enum.Items[ 0 ]  );

         {Show all}
      tv_Enum.FullExpand;
   end;

ProcessFile

  1. Try to open the given DocFile
  2. If the file cant be opened exit
  3. Add a root node to the TreeView, the node's caption = the files name
  4. Start the enumeration by calling EnumInStorage



   procedure TForm1.EnumInStorage(  Stor : IStorage;  Node : TTreeNode  );
   var
      Hr : HResult;
      Enum : IEnumSTATSTG;
      SubNode : TTreeNode;
      StatStg : TStatStg;
      SubStor : IStorage;
      HrSubStor : HResult;
      NumFetched : integer;
   begin
         {Start enumeration}
      Hr := Stor.EnumElements(  0,  nil,  0,  Enum  );
      OleCheck(  Hr  );

      repeat
            {Get 1 STATSTG}
         Hr := Enum.Next(  1,  StatStg,  @NumFetched  );

            {Was a STATSTG retrieved?}
         if(  Hr <> S_OK  ) then
            continue;

            {What type of element was returned?}
         case StatStg.dwType of

            STGTY_STORAGE : {Name of IStream element}
            begin
                  {Add node for the IStorage}
               SubNode := tv_Enum.Items.AddChild(  Node,  StatStg.pwcsName  );

                  {Open the sub-storage}
               HrSubStor := Stor.OpenStorage(  StatStg.pwcsName,
                                               nil,
                                               STGM_READWRITE or STGM_DIRECT or
                                               STGM_SHARE_EXCLUSIVE,
                                               nil,
                                               0,
                                               SubStor
                                             );

                  {If the storage was not opened}
               if(   SUCCEEDED(  HrSubStor  )   ) then
               begin
                     {Enum all elements in the sub-storage}
                  EnumInStorage(  SubStor,  SubNode  );
               end;
            end;


            STGTY_STREAM : {Name of IStream element}
            begin
                  {Add node for the stream}
               tv_Enum.Items.AddChild(  Node,  StatStg.pwcsName  );
            end;
         end;

         {Until an error occurs, or enum function indicates a stop}
      until (  Hr <> S_OK  );
   end;


EnumInStorage is the procedure in which all the enumeration is done. This is a recursive function, it is called recursively once for every sub-storage.

  1. Start the enumeration (Stor.EnumElements)

  2. Check for an error (OleCheck( Hr );)

  3. Repeat for every element in the IEnumSTATSTG

    1. Get next StatSTG element (Enum.Next( 1, StatStg, @NumFetched );)

    2. If the element is a storage (STGTY_STORAGE) then

      1. Add a node for this storage, caption = StatStg.pwcsName

      2. Open this sub-storage

      3. If the sub-storage was opened, call EnumInStorage (recursion)

    3. If the element is a stream (STGTY_STREAM) then
      1. Add a node for this stream, caption = StatStg.pwcsName






All information on these www pages is copyright (©) 1997 Andre .v.d. Merwe And may not be copied or mirrored without my permission.