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.....
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
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.
All information on these www pages is copyright (©) 1997 Andre .v.d. Merwe And may not be copied or mirrored without my permission.