IStorage does not unlock after commit

224 views Asked by At

When I run the prog below, the result value of the stgOpenStorage is STG_E_SHAREVIOLATION. How should I close the IStorage to get it unlocked?

procedure TForm1.btnSaveClick(Sender: TObject);
var
  fileName : string;
  streamName : string;

  procedure storeTextIntoStorageStream( text_ : string );
  var
    documentStorage : IStorage;
    levelIStream : IStream;
    i, j : integer;
  begin
    if ( fileExists( fileName ) ) then
      deleteFile( fileName );
    stgCreateDocfile( @fileName[1], STGM_WRITE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT or STGM_CREATE, 0, documentStorage );
    try
      documentStorage.CreateStream( @streamName[1], STGM_WRITE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT, 0, 0, levelIStream );
      try
        i := length( text_ );
        levelIStream.write( @i, sizeOf( integer ), @j );
        levelIStream.write( @text_[1], i*sizeOf( char ), @j );
      finally
        levelIStream.Commit( 0 );
        levelIStream := NIL;
      end;
    finally
      documentStorage.Commit( 0 );
      documentStorage := NIL;
    end;
  end;

  function readTextFromStorageStream : string;
  var
    documentStorage : IStorage;
    levelIStream : IStream;
    i, j : integer;
  begin
    i := stgOpenStorage( @fileName[1], NIL, STGM_READ or STGM_SHARE_EXCLUSIVE or STGM_DIRECT, NIL, 0, documentStorage );
    try
      documentStorage.OpenStream( @streamName[1], NIL, STGM_READ or STGM_SHARE_EXCLUSIVE or STGM_DIRECT, 0, levelIStream );
      try
        levelIStream.read( @i, sizeOf( integer ), @j );
        setLength( result, i );
        levelIStream.read( @result[1], i*sizeOf( char ), @j );
      finally
        levelIStream := NIL;
      end;
    finally
      documentStorage := NIL;
    end;
  end;

begin
  fileName := 'c:\temp\test.stg';
  streamName := 'Stream-0';
  storeTextIntoStorageStream( memo1.Lines.DelimitedText );
  memo1.Lines.DelimitedText := readTextFromStorageStream;
end;

And how could I set the IStorage/IStream default size / size step? Because my test 1.6K byte content stored in 16K.

2

There are 2 answers

1
The Bitman On BEST ANSWER

There are two IStorage implementations in the Delphi source libraries. WinApi.OLE2 and WinApi.ActiveX. Which one do you use? In the WinApi.OLE2 unit, the IStorage and IStream are CLASSES, they are not INTERFACES. If you use this unit, the interface garbage collection and so the automatic closing does not work on the variables. If you use the WinApi.ActiveX unit, the example will work just fine.

8
Remy Lebeau On

The code looks fine 1, so I have a feeling that the problem is due to your use of STGM_SHARE_EXCLUSIVE. The file is in use at the time you are trying to open it, so I'm betting your OS/AV is the one keeping the file open (ie, to scan its content), not the interfaces in storeTextIntoStorageStream(), which are long gone by the time readTextFromStorageStream() is entered.

1: well, aside from the lack of adequate error handling. And the redundant nil'ing of interface variables. And, consider replacing your string indexes with PChar() casts instead.

In readTextFromStorageStream(), try replacing STGM_SHARE_EXCLUSIVE (which makes sense for a writer, but not a reader) with STGM_SHARE_DENY_WRITE instead and see if the error goes away:

procedure TForm1.btnSaveClick(Sender: TObject);
var
  fileName : string;
  streamName : string;

  procedure storeTextIntoStorageStream( const text_ : string );
  var
    documentStorage : IStorage;
    levelIStream : IStream;
    i, j : integer;
  begin
    if ( FileExists( fileName ) ) then
      DeleteFile( fileName );
    OleCheck( StgCreateDocFile( PChar(fileName), STGM_WRITE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT or STGM_CREATE, 0, documentStorage ));
    try
      OleCheck( documentStorage.CreateStream( PChar(streamName), STGM_WRITE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT, 0, 0, levelIStream ) );
      try
        i := Length( text_ );
        OleCheck( levelIStream.Write( @i, SizeOf( i ), @j ) );
        if ( i > 0 ) then
          OleCheck( levelIStream.Write( PChar(text_), i * SizeOf( Char ), @j ) );
      finally
        levelIStream.Commit( 0 );
      end;
    finally
      documentStorage.Commit( 0 );
    end;
  end;

  function readTextFromStorageStream : string;
  var
    documentStorage : IStorage;
    levelIStream : IStream;
    i, j : integer;
  begin
    Result := '';
    OleCheck( StgOpenStorage( PChar(fileName), nil, STGM_READ or STGM_SHARE_DENY_WRITE or STGM_DIRECT, NIL, 0, documentStorage ) );
    OleCheck( documentStorage.OpenStream( PChar(streamName), nil, STGM_READ or STGM_SHARE_DENY_WRITE or STGM_DIRECT, 0, levelIStream ) );
    OleCheck( levelIStream.Read( @i, SizeOf( i ), @j ) );
    if ( i > 0 ) then
    begin
      SetLength( Result, i );
      OleCheck( levelIStream.Read( PChar(Result), i * SizeOf( Char ), @j ) );
    end;
  end;

begin
  fileName := 'c:\temp\test.stg';
  streamName := 'Stream-0';
  storeTextIntoStorageStream( Memo1.Lines.DelimitedText );
  Memo1.Lines.DelimitedText := readTextFromStorageStream;
end;